| // Copyright 2014 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/wm/test_session_state_animator.h" |
| |
| #include <utility> |
| #include <vector> |
| |
| #include "base/barrier_closure.h" |
| #include "base/bind.h" |
| #include "base/stl_util.h" |
| |
| namespace ash { |
| |
| namespace { |
| // A no-op callback that can be used when managing an animation that didn't |
| // actually have a callback given. |
| void DummyCallback() {} |
| } // namespace |
| |
| const SessionStateAnimator::Container |
| TestSessionStateAnimator::kAllContainers[] = { |
| SessionStateAnimator::WALLPAPER, |
| SessionStateAnimator::SHELF, |
| SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS, |
| SessionStateAnimator::LOCK_SCREEN_WALLPAPER, |
| SessionStateAnimator::LOCK_SCREEN_CONTAINERS, |
| SessionStateAnimator::LOCK_SCREEN_RELATED_CONTAINERS, |
| SessionStateAnimator::ROOT_CONTAINER}; |
| |
| // A simple SessionStateAnimator::AnimationSequence that tracks the number of |
| // attached sequences. The callback will be invoked if all animations complete |
| // successfully. |
| class TestSessionStateAnimator::AnimationSequence |
| : public SessionStateAnimator::AnimationSequence { |
| public: |
| AnimationSequence(base::OnceClosure callback, |
| TestSessionStateAnimator* animator) |
| : SessionStateAnimator::AnimationSequence(std::move(callback)), |
| sequence_count_(0), |
| sequence_aborted_(false), |
| animator_(animator) {} |
| |
| ~AnimationSequence() override = default; |
| |
| virtual void SequenceAttached() { ++sequence_count_; } |
| |
| // Notify the sequence that is has completed. |
| virtual void SequenceFinished(bool successfully) { |
| DCHECK_GT(sequence_count_, 0); |
| --sequence_count_; |
| sequence_aborted_ |= !successfully; |
| if (sequence_count_ == 0) { |
| if (sequence_aborted_) |
| OnAnimationAborted(); |
| else |
| OnAnimationCompleted(); |
| } |
| } |
| |
| // ash::SessionStateAnimator::AnimationSequence: |
| void StartAnimation(int container_mask, |
| AnimationType type, |
| AnimationSpeed speed) override { |
| animator_->StartAnimationInSequence(container_mask, type, speed, this); |
| } |
| |
| private: |
| // Tracks the number of contained animations. |
| int sequence_count_; |
| |
| // True if the sequence was aborted. |
| bool sequence_aborted_; |
| |
| // The TestSessionAnimator that created this. Not owned. |
| TestSessionStateAnimator* animator_; |
| |
| DISALLOW_COPY_AND_ASSIGN(AnimationSequence); |
| }; |
| |
| TestSessionStateAnimator::ActiveAnimation::ActiveAnimation( |
| int animation_epoch, |
| base::TimeDelta duration, |
| SessionStateAnimator::Container container, |
| AnimationType type, |
| AnimationSpeed speed, |
| base::Closure success_callback, |
| base::Closure failed_callback) |
| : animation_epoch(animation_epoch), |
| remaining_duration(duration), |
| container(container), |
| type(type), |
| speed(speed), |
| success_callback(success_callback), |
| failed_callback(failed_callback) {} |
| |
| TestSessionStateAnimator::ActiveAnimation::ActiveAnimation( |
| const ActiveAnimation& other) = default; |
| |
| TestSessionStateAnimator::ActiveAnimation::~ActiveAnimation() = default; |
| |
| TestSessionStateAnimator::TestSessionStateAnimator() |
| : last_animation_epoch_(0), is_wallpaper_hidden_(false) {} |
| |
| TestSessionStateAnimator::~TestSessionStateAnimator() { |
| CompleteAllAnimations(false); |
| } |
| |
| void TestSessionStateAnimator::ResetAnimationEpoch() { |
| CompleteAllAnimations(false); |
| last_animation_epoch_ = 0; |
| } |
| |
| void TestSessionStateAnimator::Advance(const base::TimeDelta& duration) { |
| for (ActiveAnimationsMap::iterator container_iter = |
| active_animations_.begin(); |
| container_iter != active_animations_.end(); ++container_iter) { |
| AnimationList::iterator animation_iter = (*container_iter).second.begin(); |
| while (animation_iter != (*container_iter).second.end()) { |
| ActiveAnimation& active_animation = *animation_iter; |
| active_animation.remaining_duration -= duration; |
| if (active_animation.remaining_duration <= base::TimeDelta()) { |
| active_animation.success_callback.Run(); |
| animation_iter = (*container_iter).second.erase(animation_iter); |
| } else { |
| ++animation_iter; |
| } |
| } |
| } |
| } |
| |
| void TestSessionStateAnimator::CompleteAnimations(int animation_epoch, |
| bool completed_successfully) { |
| for (ActiveAnimationsMap::iterator container_iter = |
| active_animations_.begin(); |
| container_iter != active_animations_.end(); ++container_iter) { |
| AnimationList::iterator animation_iter = (*container_iter).second.begin(); |
| while (animation_iter != (*container_iter).second.end()) { |
| ActiveAnimation active_animation = *animation_iter; |
| if (active_animation.animation_epoch <= animation_epoch) { |
| if (completed_successfully) |
| active_animation.success_callback.Run(); |
| else |
| active_animation.failed_callback.Run(); |
| animation_iter = (*container_iter).second.erase(animation_iter); |
| } else { |
| ++animation_iter; |
| } |
| } |
| } |
| } |
| |
| void TestSessionStateAnimator::CompleteAllAnimations( |
| bool completed_successfully) { |
| CompleteAnimations(last_animation_epoch_, completed_successfully); |
| } |
| |
| bool TestSessionStateAnimator::IsContainerAnimated( |
| SessionStateAnimator::Container container, |
| SessionStateAnimator::AnimationType type) const { |
| ActiveAnimationsMap::const_iterator container_iter = |
| active_animations_.find(container); |
| if (container_iter != active_animations_.end()) { |
| for (AnimationList::const_iterator animation_iter = |
| (*container_iter).second.begin(); |
| animation_iter != (*container_iter).second.end(); ++animation_iter) { |
| const ActiveAnimation& active_animation = *animation_iter; |
| if (active_animation.type == type) |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| bool TestSessionStateAnimator::AreContainersAnimated( |
| int container_mask, |
| SessionStateAnimator::AnimationType type) const { |
| for (size_t i = 0; i < base::size(kAllContainers); ++i) { |
| if (container_mask & kAllContainers[i] && |
| !IsContainerAnimated(kAllContainers[i], type)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| size_t TestSessionStateAnimator::GetAnimationCount() const { |
| size_t count = 0; |
| for (ActiveAnimationsMap::const_iterator container_iter = |
| active_animations_.begin(); |
| container_iter != active_animations_.end(); ++container_iter) { |
| count += (*container_iter).second.size(); |
| } |
| return count; |
| } |
| |
| void TestSessionStateAnimator::StartAnimation(int container_mask, |
| AnimationType type, |
| AnimationSpeed speed) { |
| ++last_animation_epoch_; |
| for (size_t i = 0; i < base::size(kAllContainers); ++i) { |
| if (container_mask & kAllContainers[i]) { |
| // Use a dummy no-op callback because one isn't required by the client |
| // but one is required when completing or aborting animations. |
| base::Closure callback = base::Bind(&DummyCallback); |
| AddAnimation(kAllContainers[i], type, speed, callback, callback); |
| } |
| } |
| } |
| |
| void TestSessionStateAnimator::StartAnimationWithCallback( |
| int container_mask, |
| AnimationType type, |
| AnimationSpeed speed, |
| base::OnceClosure callback) { |
| ++last_animation_epoch_; |
| |
| int container_count = 0; |
| for (size_t i = 0; i < base::size(kAllContainers); ++i) { |
| if (container_mask & kAllContainers[i]) |
| ++container_count; |
| } |
| |
| base::RepeatingClosure completion_callback = |
| base::BarrierClosure(container_count, std::move(callback)); |
| for (size_t i = 0; i < base::size(kAllContainers); ++i) { |
| if (container_mask & kAllContainers[i]) { |
| // ash::SessionStateAnimatorImpl invokes the callback whether or not the |
| // animation was completed successfully or not. |
| AddAnimation(kAllContainers[i], type, speed, completion_callback, |
| completion_callback); |
| } |
| } |
| } |
| |
| ash::SessionStateAnimator::AnimationSequence* |
| TestSessionStateAnimator::BeginAnimationSequence(base::OnceClosure callback) { |
| return new AnimationSequence(std::move(callback), this); |
| } |
| |
| bool TestSessionStateAnimator::IsWallpaperHidden() const { |
| return is_wallpaper_hidden_; |
| } |
| |
| void TestSessionStateAnimator::ShowWallpaper() { |
| is_wallpaper_hidden_ = false; |
| } |
| |
| void TestSessionStateAnimator::HideWallpaper() { |
| is_wallpaper_hidden_ = true; |
| } |
| |
| void TestSessionStateAnimator::StartAnimationInSequence( |
| int container_mask, |
| AnimationType type, |
| AnimationSpeed speed, |
| AnimationSequence* animation_sequence) { |
| ++last_animation_epoch_; |
| for (size_t i = 0; i < base::size(kAllContainers); ++i) { |
| if (container_mask & kAllContainers[i]) { |
| base::Closure success_callback = |
| base::Bind(&AnimationSequence::SequenceFinished, |
| base::Unretained(animation_sequence), true); |
| base::Closure failed_callback = |
| base::Bind(&AnimationSequence::SequenceFinished, |
| base::Unretained(animation_sequence), false); |
| animation_sequence->SequenceAttached(); |
| AddAnimation(kAllContainers[i], type, speed, success_callback, |
| failed_callback); |
| } |
| } |
| } |
| |
| void TestSessionStateAnimator::AddAnimation( |
| SessionStateAnimator::Container container, |
| AnimationType type, |
| AnimationSpeed speed, |
| base::Closure success_callback, |
| base::Closure failed_callback) { |
| base::TimeDelta duration = GetDuration(speed); |
| ActiveAnimation active_animation(last_animation_epoch_, duration, container, |
| type, speed, success_callback, |
| failed_callback); |
| // This test double is limited to only have one animation active for a given |
| // container at a time. |
| AbortAnimation(container); |
| active_animations_[container].push_back(active_animation); |
| } |
| |
| void TestSessionStateAnimator::AbortAnimation( |
| SessionStateAnimator::Container container) { |
| ActiveAnimationsMap::iterator container_iter = |
| active_animations_.find(container); |
| if (container_iter != active_animations_.end()) { |
| AnimationList::iterator animation_iter = (*container_iter).second.begin(); |
| while (animation_iter != (*container_iter).second.end()) { |
| ActiveAnimation active_animation = *animation_iter; |
| active_animation.failed_callback.Run(); |
| animation_iter = (*container_iter).second.erase(animation_iter); |
| } |
| } |
| } |
| |
| } // namespace ash |