blob: 20edf418dcbcdd1c5a5a889f34e84403290c486a [file] [log] [blame]
// 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