| // Copyright 2015 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/rotator/screen_rotation_animator.h" |
| |
| #include <memory> |
| #include <utility> |
| |
| #include "ash/public/cpp/metrics_util.h" |
| #include "ash/public/cpp/shell_window_ids.h" |
| #include "ash/rotator/screen_rotation_animation.h" |
| #include "ash/rotator/screen_rotation_animator_observer.h" |
| #include "ash/shell.h" |
| #include "ash/utility/layer_util.h" |
| #include "ash/utility/transformer_util.h" |
| #include "base/bind.h" |
| #include "base/command_line.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "base/threading/sequenced_task_runner_handle.h" |
| #include "base/time/time.h" |
| #include "components/viz/common/frame_sinks/copy_output_request.h" |
| #include "components/viz/common/frame_sinks/copy_output_result.h" |
| #include "ui/aura/window.h" |
| #include "ui/base/class_property.h" |
| #include "ui/compositor/animation_throughput_reporter.h" |
| #include "ui/compositor/callback_layer_animation_observer.h" |
| #include "ui/compositor/compositor.h" |
| #include "ui/compositor/layer.h" |
| #include "ui/compositor/layer_animation_element.h" |
| #include "ui/compositor/layer_animation_sequence.h" |
| #include "ui/compositor/layer_animator.h" |
| #include "ui/compositor/layer_owner.h" |
| #include "ui/compositor/layer_tree_owner.h" |
| #include "ui/compositor/scoped_animation_duration_scale_mode.h" |
| #include "ui/display/display.h" |
| #include "ui/display/manager/display_manager.h" |
| #include "ui/display/manager/managed_display_info.h" |
| #include "ui/display/screen.h" |
| #include "ui/gfx/animation/tween.h" |
| #include "ui/gfx/geometry/point.h" |
| #include "ui/gfx/geometry/rect.h" |
| #include "ui/gfx/geometry/size_f.h" |
| #include "ui/gfx/geometry/transform.h" |
| #include "ui/gfx/geometry/transform_util.h" |
| #include "ui/wm/core/window_util.h" |
| |
| DEFINE_UI_CLASS_PROPERTY_TYPE(ash::ScreenRotationAnimator*) |
| |
| namespace ash { |
| |
| namespace { |
| |
| // The number of degrees that the rotation animations animate through. |
| const int kRotationDegrees = 20; |
| |
| // The time it takes for the rotation animations to run. |
| const int kRotationDurationInMs = 250; |
| |
| // The rotation factors. |
| const int kCounterClockWiseRotationFactor = 1; |
| const int kClockWiseRotationFactor = -1; |
| |
| constexpr char kRotationAnimationSmoothness[] = |
| "Ash.Rotation.AnimationSmoothness"; |
| |
| // A property key to store the ScreenRotationAnimator of the window; Used for |
| // screen rotation. |
| DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(ScreenRotationAnimator, |
| kScreenRotationAnimatorKey, |
| nullptr) |
| |
| display::Display::Rotation GetCurrentScreenRotation(int64_t display_id) { |
| return Shell::Get() |
| ->display_manager() |
| ->GetDisplayInfo(display_id) |
| .GetActiveRotation(); |
| } |
| |
| // 180 degree rotations should animate clock-wise. |
| int GetRotationFactor(display::Display::Rotation initial_rotation, |
| display::Display::Rotation new_rotation) { |
| return (initial_rotation + 3) % 4 == new_rotation |
| ? kCounterClockWiseRotationFactor |
| : kClockWiseRotationFactor; |
| } |
| |
| aura::Window* GetScreenRotationContainer(aura::Window* root_window) { |
| return root_window->GetChildById(kShellWindowId_ScreenAnimationContainer); |
| } |
| |
| // Returns true if the rotation between |initial_rotation| and |new_rotation| is |
| // 180 degrees. |
| bool Is180DegreeFlip(display::Display::Rotation initial_rotation, |
| display::Display::Rotation new_rotation) { |
| return (initial_rotation + 2) % 4 == new_rotation; |
| } |
| |
| // Returns the initial degrees the old layer animation to begin with. |
| int GetInitialDegrees(display::Display::Rotation initial_rotation, |
| display::Display::Rotation new_rotation) { |
| return (Is180DegreeFlip(initial_rotation, new_rotation) ? 180 : 90); |
| } |
| |
| void AddLayerAtTopOfWindowLayers(aura::Window* root_window, ui::Layer* layer) { |
| // Add the cloned/copied layer tree into the root, so it will be rendered. |
| root_window->layer()->Add(layer); |
| root_window->layer()->StackAtTop(layer); |
| } |
| |
| void AddLayerBelowWindowLayer(aura::Window* root_window, |
| ui::Layer* top_layer, |
| ui::Layer* layer) { |
| // Add the cloned/copied layer tree into the root, so it will be rendered. |
| root_window->layer()->Add(layer); |
| root_window->layer()->StackBelow(layer, top_layer); |
| } |
| |
| // The Callback will be invoked when all animation sequences have |
| // finished. |observer| will be destroyed after invoking the Callback if it |
| // returns true. |
| bool AnimationEndedCallback( |
| base::WeakPtr<ScreenRotationAnimator> animator, |
| const ui::CallbackLayerAnimationObserver& observer) { |
| if (animator) |
| animator->ProcessAnimationQueue(); |
| return true; |
| } |
| |
| // Creates a Transform for the old layer in screen rotation animation. |
| gfx::Transform CreateScreenRotationOldLayerTransformForDisplay( |
| display::Display::Rotation old_rotation, |
| display::Display::Rotation new_rotation, |
| const display::Display& display) { |
| gfx::Transform inverse; |
| CHECK(CreateRotationTransform(old_rotation, new_rotation, |
| gfx::SizeF(display.size())) |
| .GetInverse(&inverse)); |
| return inverse; |
| } |
| |
| // The |request_id| changed since last copy request, which means a |
| // new rotation stated, we need to ignore this copy result. |
| bool IgnoreCopyResult(int64_t request_id, int64_t current_request_id) { |
| DCHECK(request_id <= current_request_id); |
| return request_id < current_request_id; |
| } |
| |
| bool RootWindowChangedForDisplayId(aura::Window* root_window, |
| int64_t display_id) { |
| return root_window != Shell::GetRootWindowForDisplayId(display_id); |
| } |
| |
| // Creates a mask layer and returns the |mask_layer_tree_owner|. |
| std::unique_ptr<ui::LayerTreeOwner> CreateMaskLayerTreeOwner( |
| const gfx::Rect& rect) { |
| std::unique_ptr<ui::Layer> mask_layer = |
| std::make_unique<ui::Layer>(ui::LAYER_SOLID_COLOR); |
| mask_layer->SetBounds(rect); |
| mask_layer->SetColor(SK_ColorBLACK); |
| return std::make_unique<ui::LayerTreeOwner>(std::move(mask_layer)); |
| } |
| |
| } // namespace |
| |
| // static |
| ScreenRotationAnimator* ScreenRotationAnimator::GetForRootWindow( |
| aura::Window* root_window) { |
| auto* animator = root_window->GetProperty(kScreenRotationAnimatorKey); |
| if (!animator) { |
| animator = new ScreenRotationAnimator(root_window); |
| root_window->SetProperty(kScreenRotationAnimatorKey, animator); |
| } |
| return animator; |
| } |
| |
| // static |
| void ScreenRotationAnimator::SetScreenRotationAnimatorForTest( |
| aura::Window* root_window, |
| std::unique_ptr<ScreenRotationAnimator> animator) { |
| root_window->SetProperty(kScreenRotationAnimatorKey, std::move(animator)); |
| } |
| |
| ScreenRotationAnimator::ScreenRotationAnimator(aura::Window* root_window) |
| : root_window_(root_window), |
| screen_rotation_state_(IDLE), |
| rotation_request_id_(0), |
| disable_animation_timers_for_test_(false) {} |
| |
| ScreenRotationAnimator::~ScreenRotationAnimator() { |
| // To prevent a call to |AnimationEndedCallback()| from calling a method on |
| // the |animator_|. |
| weak_factory_.InvalidateWeakPtrs(); |
| } |
| |
| void ScreenRotationAnimator::StartRotationAnimation( |
| std::unique_ptr<ScreenRotationRequest> rotation_request) { |
| const display::Display::Rotation current_rotation = |
| GetCurrentScreenRotation(rotation_request->display_id); |
| if (current_rotation == rotation_request->new_rotation) { |
| // We need to call |ProcessAnimationQueue()| to prepare for next rotation |
| // request. |
| ProcessAnimationQueue(); |
| return; |
| } |
| |
| rotation_request->old_rotation = current_rotation; |
| if (DisplayConfigurationController::ANIMATION_SYNC == |
| rotation_request->mode) { |
| StartSlowAnimation(std::move(rotation_request)); |
| } else { |
| current_async_rotation_request_ = ScreenRotationRequest(*rotation_request); |
| RequestCopyScreenRotationContainerLayer( |
| std::make_unique<viz::CopyOutputRequest>( |
| viz::CopyOutputRequest::ResultFormat::RGBA, |
| viz::CopyOutputRequest::ResultDestination::kNativeTextures, |
| CreateAfterCopyCallbackBeforeRotation( |
| std::move(rotation_request)))); |
| screen_rotation_state_ = COPY_REQUESTED; |
| } |
| } |
| |
| void ScreenRotationAnimator::StartSlowAnimation( |
| std::unique_ptr<ScreenRotationRequest> rotation_request) { |
| CreateOldLayerTreeForSlowAnimation(); |
| SetRotation(rotation_request->display_id, rotation_request->old_rotation, |
| rotation_request->new_rotation, rotation_request->source); |
| AnimateRotation(std::move(rotation_request)); |
| } |
| |
| void ScreenRotationAnimator::SetRotation( |
| int64_t display_id, |
| display::Display::Rotation old_rotation, |
| display::Display::Rotation new_rotation, |
| display::Display::RotationSource source) { |
| // Reset the current request because its rotation must be applied if any. |
| current_async_rotation_request_.reset(); |
| |
| // Allow compositor locks to extend timeout, so that screen rotation only |
| // takes output copy after contents are properlly resized, such as wallpaper |
| // and ARC apps. |
| ui::Compositor* compositor = root_window_->layer()->GetCompositor(); |
| compositor->SetAllowLocksToExtendTimeout(true); |
| Shell::Get()->display_manager()->SetDisplayRotation(display_id, new_rotation, |
| source); |
| compositor->SetAllowLocksToExtendTimeout(false); |
| const display::Display display = |
| Shell::Get()->display_manager()->GetDisplayForId(display_id); |
| old_layer_tree_owner_->root()->SetTransform( |
| CreateScreenRotationOldLayerTransformForDisplay(old_rotation, |
| new_rotation, display)); |
| } |
| |
| void ScreenRotationAnimator::RequestCopyScreenRotationContainerLayer( |
| std::unique_ptr<viz::CopyOutputRequest> copy_output_request) { |
| ui::Layer* screen_rotation_container_layer = |
| GetScreenRotationContainer(root_window_)->layer(); |
| copy_output_request->set_area( |
| gfx::Rect(screen_rotation_container_layer->size())); |
| copy_output_request->set_result_task_runner( |
| base::SequencedTaskRunnerHandle::Get()); |
| screen_rotation_container_layer->RequestCopyOfOutput( |
| std::move(copy_output_request)); |
| } |
| |
| ScreenRotationAnimator::CopyCallback |
| ScreenRotationAnimator::CreateAfterCopyCallbackBeforeRotation( |
| std::unique_ptr<ScreenRotationRequest> rotation_request) { |
| return base::BindOnce(&ScreenRotationAnimator:: |
| OnScreenRotationContainerLayerCopiedBeforeRotation, |
| weak_factory_.GetWeakPtr(), |
| std::move(rotation_request)); |
| } |
| |
| ScreenRotationAnimator::CopyCallback |
| ScreenRotationAnimator::CreateAfterCopyCallbackAfterRotation( |
| std::unique_ptr<ScreenRotationRequest> rotation_request) { |
| return base::BindOnce(&ScreenRotationAnimator:: |
| OnScreenRotationContainerLayerCopiedAfterRotation, |
| weak_factory_.GetWeakPtr(), |
| std::move(rotation_request)); |
| } |
| |
| void ScreenRotationAnimator::OnScreenRotationContainerLayerCopiedBeforeRotation( |
| std::unique_ptr<ScreenRotationRequest> rotation_request, |
| std::unique_ptr<viz::CopyOutputResult> result) { |
| if (IgnoreCopyResult(rotation_request->id, rotation_request_id_)) |
| return; |
| // Abort rotation and animation if the display was removed or the |
| // |root_window| was changed for |display_id|. |
| if (RootWindowChangedForDisplayId(root_window_, |
| rotation_request->display_id)) { |
| ProcessAnimationQueue(); |
| return; |
| } |
| // Abort animation and set the rotation to target rotation when the copy |
| // request has been canceled or failed. It would fail if, for examples: a) The |
| // layer is removed from the compositor and destroyed before committing the |
| // request to the compositor. b) The compositor is shutdown. |
| if (result->IsEmpty()) { |
| Shell::Get()->display_manager()->SetDisplayRotation( |
| rotation_request->display_id, rotation_request->new_rotation, |
| rotation_request->source); |
| ProcessAnimationQueue(); |
| return; |
| } |
| |
| old_layer_tree_owner_ = CopyLayerTree(std::move(result)); |
| AddLayerAtTopOfWindowLayers(root_window_, old_layer_tree_owner_->root()); |
| |
| // TODO(oshima): We need a better way to control animation and other |
| // activities during system wide animation. |
| animation_scale_mode_ = |
| std::make_unique<ui::ScopedAnimationDurationScaleMode>( |
| ui::ScopedAnimationDurationScaleMode::ZERO_DURATION); |
| |
| for (auto& observer : screen_rotation_animator_observers_) |
| observer.OnScreenCopiedBeforeRotation(); |
| |
| SetRotation(rotation_request->display_id, rotation_request->old_rotation, |
| rotation_request->new_rotation, rotation_request->source); |
| |
| RequestCopyScreenRotationContainerLayer( |
| std::make_unique<viz::CopyOutputRequest>( |
| viz::CopyOutputRequest::ResultFormat::RGBA, |
| viz::CopyOutputRequest::ResultDestination::kNativeTextures, |
| CreateAfterCopyCallbackAfterRotation(std::move(rotation_request)))); |
| } |
| |
| void ScreenRotationAnimator::OnScreenRotationContainerLayerCopiedAfterRotation( |
| std::unique_ptr<ScreenRotationRequest> rotation_request, |
| std::unique_ptr<viz::CopyOutputResult> result) { |
| animation_scale_mode_.reset(); |
| if (IgnoreCopyResult(rotation_request->id, rotation_request_id_)) { |
| NotifyAnimationFinished(/*canceled=*/true); |
| return; |
| } |
| // In the following cases, abort animation: |
| // 1) if the display was removed, |
| // 2) if the |root_window| was changed for |display_id|, |
| // 3) the copy request has been canceled or failed. It would fail if, |
| // for examples: a) The layer is removed from the compositor and destroyed |
| // before committing the request to the compositor. b) The compositor is |
| // shutdown. |
| if (RootWindowChangedForDisplayId(root_window_, |
| rotation_request->display_id) || |
| result->IsEmpty()) { |
| ProcessAnimationQueue(); |
| return; |
| } |
| |
| new_layer_tree_owner_ = CopyLayerTree(std::move(result)); |
| AddLayerBelowWindowLayer(root_window_, old_layer_tree_owner_->root(), |
| new_layer_tree_owner_->root()); |
| AnimateRotation(std::move(rotation_request)); |
| } |
| |
| void ScreenRotationAnimator::CreateOldLayerTreeForSlowAnimation() { |
| old_layer_tree_owner_ = ::wm::RecreateLayers(root_window_); |
| AddLayerAtTopOfWindowLayers(root_window_, old_layer_tree_owner_->root()); |
| } |
| |
| std::unique_ptr<ui::LayerTreeOwner> ScreenRotationAnimator::CopyLayerTree( |
| std::unique_ptr<viz::CopyOutputResult> result) { |
| gfx::Size layer_size = |
| GetScreenRotationContainer(root_window_)->layer()->size(); |
| std::unique_ptr<ui::Layer> copy_layer = |
| CreateLayerFromCopyOutputResult(std::move(result), layer_size); |
| DCHECK_EQ(copy_layer->size(), |
| GetScreenRotationContainer(root_window_)->layer()->size()); |
| |
| // TODO(crbug.com/1040279): This is a workaround and should be removed once |
| // the issue is fixed. |
| copy_layer->SetFillsBoundsOpaquely(false); |
| return std::make_unique<ui::LayerTreeOwner>(std::move(copy_layer)); |
| } |
| |
| void ScreenRotationAnimator::AnimateRotation( |
| std::unique_ptr<ScreenRotationRequest> rotation_request) { |
| screen_rotation_state_ = ROTATING; |
| const int rotation_factor = GetRotationFactor(rotation_request->old_rotation, |
| rotation_request->new_rotation); |
| const int old_layer_initial_rotation_degrees = GetInitialDegrees( |
| rotation_request->old_rotation, rotation_request->new_rotation); |
| const base::TimeDelta duration = base::Milliseconds(kRotationDurationInMs); |
| const gfx::Tween::Type tween_type = gfx::Tween::FAST_OUT_LINEAR_IN; |
| const gfx::Rect rotated_screen_bounds = root_window_->GetTargetBounds(); |
| const gfx::Point pivot = gfx::Point(rotated_screen_bounds.width() / 2, |
| rotated_screen_bounds.height() / 2); |
| |
| ui::Layer* screen_rotation_container_layer = |
| GetScreenRotationContainer(root_window_)->layer(); |
| ui::Layer* new_root_layer; |
| if (!new_layer_tree_owner_) { |
| new_root_layer = screen_rotation_container_layer; |
| } else { |
| new_root_layer = new_layer_tree_owner_->root(); |
| // Add a black mask layer on top of |screen_rotation_container_layer|. |
| mask_layer_tree_owner_ = CreateMaskLayerTreeOwner( |
| gfx::Rect(screen_rotation_container_layer->size())); |
| AddLayerBelowWindowLayer(root_window_, new_root_layer, |
| mask_layer_tree_owner_->root()); |
| } |
| |
| std::unique_ptr<ScreenRotationAnimation> new_layer_screen_rotation = |
| std::make_unique<ScreenRotationAnimation>( |
| new_root_layer, kRotationDegrees * rotation_factor, |
| 0 /* end_degrees */, new_root_layer->opacity(), |
| new_root_layer->opacity() /* target_opacity */, pivot, duration, |
| tween_type); |
| |
| ui::LayerAnimator* new_layer_animator = new_root_layer->GetAnimator(); |
| new_layer_animator->set_preemption_strategy( |
| ui::LayerAnimator::REPLACE_QUEUED_ANIMATIONS); |
| std::unique_ptr<ui::LayerAnimationSequence> new_layer_animation_sequence = |
| std::make_unique<ui::LayerAnimationSequence>( |
| std::move(new_layer_screen_rotation)); |
| |
| ui::Layer* old_root_layer = old_layer_tree_owner_->root(); |
| const gfx::Rect original_screen_bounds = old_root_layer->GetTargetBounds(); |
| // The old layer will also be transformed into the new orientation. We will |
| // translate it so that the old layer's center point aligns with the new |
| // orientation's center point and use that center point as the pivot for the |
| // rotation animation. |
| gfx::Transform translate_transform; |
| translate_transform.Translate( |
| (rotated_screen_bounds.width() - original_screen_bounds.width()) / 2, |
| (rotated_screen_bounds.height() - original_screen_bounds.height()) / 2); |
| old_root_layer->SetTransform(translate_transform); |
| |
| std::unique_ptr<ScreenRotationAnimation> old_layer_screen_rotation = |
| std::make_unique<ScreenRotationAnimation>( |
| old_root_layer, old_layer_initial_rotation_degrees * rotation_factor, |
| (old_layer_initial_rotation_degrees - kRotationDegrees) * |
| rotation_factor, |
| old_root_layer->opacity(), 0.0f /* target_opacity */, pivot, duration, |
| tween_type); |
| |
| ui::LayerAnimator* old_layer_animator = old_root_layer->GetAnimator(); |
| old_layer_animator->set_preemption_strategy( |
| ui::LayerAnimator::REPLACE_QUEUED_ANIMATIONS); |
| std::unique_ptr<ui::LayerAnimationSequence> old_layer_animation_sequence = |
| std::make_unique<ui::LayerAnimationSequence>( |
| std::move(old_layer_screen_rotation)); |
| |
| // In unit tests, we can use ash::ScreenRotationAnimatorTestApi to control the |
| // animation. |
| if (disable_animation_timers_for_test_) { |
| if (new_layer_tree_owner_) |
| new_layer_animator->set_disable_timer_for_test(true); |
| old_layer_animator->set_disable_timer_for_test(true); |
| } |
| ui::AnimationThroughputReporter reporter( |
| old_layer_animator, |
| metrics_util::ForSmoothness(base::BindRepeating([](int smoothness) { |
| UMA_HISTOGRAM_PERCENTAGE(kRotationAnimationSmoothness, smoothness); |
| }))); |
| |
| // Add an observer so that the cloned/copied layers can be cleaned up with the |
| // animation completes/aborts. |
| ui::CallbackLayerAnimationObserver* observer = |
| new ui::CallbackLayerAnimationObserver(base::BindRepeating( |
| &AnimationEndedCallback, weak_factory_.GetWeakPtr())); |
| if (new_layer_tree_owner_) |
| new_layer_animation_sequence->AddObserver(observer); |
| new_layer_animator->StartAnimation(new_layer_animation_sequence.release()); |
| old_layer_animation_sequence->AddObserver(observer); |
| old_layer_animator->StartAnimation(old_layer_animation_sequence.release()); |
| observer->SetActive(); |
| } |
| |
| void ScreenRotationAnimator::Rotate( |
| display::Display::Rotation new_rotation, |
| display::Display::RotationSource source, |
| DisplayConfigurationController::RotationAnimation mode) { |
| // |rotation_request_id_| is used to skip stale requests. Before the layer |
| // CopyOutputResult callback called, there could have new rotation request. |
| // Increases |rotation_request_id_| for each new request and in the callback, |
| // we compare the |rotation_request.id| and |rotation_request_id_| to |
| // determine the stale status. |
| rotation_request_id_++; |
| const int64_t display_id = |
| display::Screen::GetScreen()->GetDisplayNearestWindow(root_window_).id(); |
| std::unique_ptr<ScreenRotationRequest> rotation_request = |
| std::make_unique<ScreenRotationRequest>(rotation_request_id_, display_id, |
| new_rotation, source, mode); |
| target_rotation_ = new_rotation; |
| |
| if (mode == DisplayConfigurationController::ANIMATION_SYNC) |
| current_async_rotation_request_.reset(); |
| |
| switch (screen_rotation_state_) { |
| case IDLE: |
| DCHECK(!current_async_rotation_request_); |
| [[fallthrough]]; |
| case COPY_REQUESTED: |
| if (current_async_rotation_request_ && |
| !RootWindowChangedForDisplayId( |
| root_window_, current_async_rotation_request_->display_id)) { |
| Shell::Get()->display_manager()->SetDisplayRotation( |
| current_async_rotation_request_->display_id, |
| current_async_rotation_request_->new_rotation, |
| current_async_rotation_request_->source); |
| current_async_rotation_request_.reset(); |
| } |
| |
| StartRotationAnimation(std::move(rotation_request)); |
| break; |
| case ROTATING: |
| last_pending_request_ = std::move(rotation_request); |
| // The pending request will be processed when the |
| // |AnimationEndedCallback()| should be called after |StopAnimating()|. |
| StopAnimating(); |
| break; |
| } |
| } |
| |
| void ScreenRotationAnimator::AddObserver( |
| ScreenRotationAnimatorObserver* observer) { |
| screen_rotation_animator_observers_.AddObserver(observer); |
| } |
| |
| void ScreenRotationAnimator::RemoveObserver( |
| ScreenRotationAnimatorObserver* observer) { |
| screen_rotation_animator_observers_.RemoveObserver(observer); |
| } |
| |
| void ScreenRotationAnimator::ProcessAnimationQueue() { |
| screen_rotation_state_ = IDLE; |
| old_layer_tree_owner_.reset(); |
| new_layer_tree_owner_.reset(); |
| mask_layer_tree_owner_.reset(); |
| current_async_rotation_request_.reset(); |
| if (last_pending_request_ && |
| !RootWindowChangedForDisplayId(root_window_, |
| last_pending_request_->display_id)) { |
| StartRotationAnimation(std::move(last_pending_request_)); |
| return; |
| } |
| |
| NotifyAnimationFinished(/*canceled=*/false); |
| } |
| |
| bool ScreenRotationAnimator::IsRotating() const { |
| return screen_rotation_state_ != IDLE; |
| } |
| |
| display::Display::Rotation ScreenRotationAnimator::GetTargetRotation() const { |
| return target_rotation_; |
| } |
| |
| void ScreenRotationAnimator::StopAnimating() { |
| // |old_layer_tree_owner_| new_layer_tree_owner_| could be nullptr if another |
| // the rotation request comes before the copy request finished. |
| if (old_layer_tree_owner_) |
| old_layer_tree_owner_->root()->GetAnimator()->StopAnimating(); |
| if (new_layer_tree_owner_) |
| new_layer_tree_owner_->root()->GetAnimator()->StopAnimating(); |
| mask_layer_tree_owner_.reset(); |
| } |
| |
| void ScreenRotationAnimator::NotifyAnimationFinished(bool canceled) { |
| for (auto& observer : screen_rotation_animator_observers_) |
| observer.OnScreenRotationAnimationFinished(this, canceled); |
| } |
| |
| } // namespace ash |