blob: f6a4bb3894479c10f6c95961602c693017fa430e [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/shelf/hotseat_transition_animator.h"
#include "ash/public/cpp/shelf_config.h"
#include "ash/shelf/shelf_widget.h"
#include "ash/shell.h"
#include "ash/wm/tablet_mode/tablet_mode_controller.h"
#include "base/metrics/histogram_macros.h"
#include "ui/compositor/animation_metrics_reporter.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/layer_animator.h"
#include "ui/compositor/scoped_layer_animation_settings.h"
#include "ui/gfx/animation/tween.h"
#include "ui/gfx/geometry/rect.h"
namespace ash {
class HotseatTransitionAnimator::TransitionAnimationMetricsReporter
: public ui::AnimationMetricsReporter {
public:
TransitionAnimationMetricsReporter() = default;
~TransitionAnimationMetricsReporter() override = default;
void set_new_state(HotseatState new_state) { new_state_ = new_state; }
// ui::AnimationMetricsReporter:
void Report(int value) override {
switch (new_state_) {
case HotseatState::kShown:
UMA_HISTOGRAM_PERCENTAGE(
"Ash.HotseatTransition.AnimationSmoothness."
"TransitionToShownHotseat",
value);
break;
case HotseatState::kExtended:
UMA_HISTOGRAM_PERCENTAGE(
"Ash.HotseatTransition.AnimationSmoothness."
"TransitionToExtendedHotseat",
value);
break;
case HotseatState::kHidden:
UMA_HISTOGRAM_PERCENTAGE(
"Ash.HotseatTransition.AnimationSmoothness."
"TransitionToHiddenHotseat",
value);
break;
default:
NOTREACHED();
}
}
private:
// The state to which the animation is transitioning.
HotseatState new_state_;
};
HotseatTransitionAnimator::HotseatTransitionAnimator(ShelfWidget* shelf_widget)
: shelf_widget_(shelf_widget),
animation_metrics_reporter_(
std::make_unique<TransitionAnimationMetricsReporter>()) {
Shell::Get()->tablet_mode_controller()->AddObserver(this);
}
HotseatTransitionAnimator::~HotseatTransitionAnimator() {
StopObservingImplicitAnimations();
if (Shell::Get()->tablet_mode_controller())
Shell::Get()->tablet_mode_controller()->RemoveObserver(this);
}
void HotseatTransitionAnimator::OnHotseatStateChanged(HotseatState old_state,
HotseatState new_state) {
DoAnimation(old_state, new_state);
}
void HotseatTransitionAnimator::AddObserver(Observer* observer) {
observers_.AddObserver(observer);
}
void HotseatTransitionAnimator::RemoveObserver(Observer* observer) {
observers_.RemoveObserver(observer);
}
void HotseatTransitionAnimator::OnImplicitAnimationsCompleted() {
std::move(animation_complete_callback_).Run();
if (test_observer_)
test_observer_->OnTransitionTestAnimationEnded();
}
void HotseatTransitionAnimator::OnTabletModeStarting() {
tablet_mode_transitioning_ = true;
}
void HotseatTransitionAnimator::OnTabletModeStarted() {
tablet_mode_transitioning_ = false;
}
void HotseatTransitionAnimator::OnTabletModeEnding() {
tablet_mode_transitioning_ = true;
}
void HotseatTransitionAnimator::OnTabletModeEnded() {
tablet_mode_transitioning_ = false;
}
void HotseatTransitionAnimator::SetAnimationsEnabledInSessionState(
bool enabled) {
animations_enabled_for_current_session_state_ = enabled;
ui::Layer* animating_background = shelf_widget_->GetAnimatingBackground();
if (!enabled && animating_background->GetAnimator()->is_animating())
animating_background->GetAnimator()->StopAnimating();
}
void HotseatTransitionAnimator::SetTestObserver(TestObserver* test_observer) {
test_observer_ = test_observer;
}
void HotseatTransitionAnimator::DoAnimation(HotseatState old_state,
HotseatState new_state) {
const bool animating_to_shown_background = new_state != HotseatState::kShown;
gfx::Transform transform;
if (animating_to_shown_background)
transform.Translate(0, -ShelfConfig::Get()->in_app_shelf_size());
if (!ShouldDoAnimation(old_state, new_state)) {
shelf_widget_->GetAnimatingBackground()->SetTransform(transform);
return;
}
StopObservingImplicitAnimations();
shelf_widget_->GetAnimatingBackground()->SetColor(
ShelfConfig::Get()->GetMaximizedShelfColor());
shelf_widget_->GetAnimatingDragHandle()->SetBounds(
shelf_widget_->GetDragHandle()->bounds());
animation_metrics_reporter_->set_new_state(new_state);
for (auto& observer : observers_)
observer.OnHotseatTransitionAnimationStarted(old_state, new_state);
{
ui::ScopedLayerAnimationSettings shelf_bg_animation_setter(
shelf_widget_->GetAnimatingBackground()->GetAnimator());
shelf_bg_animation_setter.SetTransitionDuration(
ShelfConfig::Get()->hotseat_background_animation_duration());
shelf_bg_animation_setter.SetTweenType(gfx::Tween::EASE_OUT);
shelf_bg_animation_setter.SetPreemptionStrategy(
ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
shelf_bg_animation_setter.SetAnimationMetricsReporter(
animation_metrics_reporter_.get());
animation_complete_callback_ = base::BindOnce(
&HotseatTransitionAnimator::NotifyHotseatTransitionAnimationEnded,
weak_ptr_factory_.GetWeakPtr(), old_state, new_state);
shelf_bg_animation_setter.AddObserver(this);
shelf_widget_->GetAnimatingBackground()->SetTransform(transform);
}
}
bool HotseatTransitionAnimator::ShouldDoAnimation(HotseatState old_state,
HotseatState new_state) {
// The first HotseatState change when going to tablet mode should not be
// animated.
if (tablet_mode_transitioning_)
return false;
if (!animations_enabled_for_current_session_state_)
return false;
return (new_state == HotseatState::kShown ||
old_state == HotseatState::kShown) &&
Shell::Get()->tablet_mode_controller()->InTabletMode();
}
void HotseatTransitionAnimator::NotifyHotseatTransitionAnimationEnded(
HotseatState old_state,
HotseatState new_state) {
for (auto& observer : observers_)
observer.OnHotseatTransitionAnimationEnded(old_state, new_state);
}
} // namespace ash