#include "ash/public/cpp/autotest_desks_api.h"
#include "ash/shell.h"
#include "ash/wm/desks/desk.h"
#include "ash/wm/desks/desk_animation_base.h"
#include "ash/wm/desks/desks_controller.h"
#include "ash/wm/desks/desks_histogram_enums.h"
#include "ash/wm/desks/root_window_desk_switch_animator.h"
#include "ash/wm/overview/overview_controller.h"
#include "base/check.h"
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/layer_animation_observer.h"
#include "ui/compositor/layer_animation_sequence.h"
#include "ui/compositor/layer_animator.h"
namespace ash {
namespace {
class DeskAnimationObserver : public DesksController::Observer {
public:
DeskAnimationObserver(base::OnceClosure on_desk_animation_complete)
: on_desk_animation_complete_(std::move(on_desk_animation_complete)) {
DesksController::Get()->AddObserver(this);
}
~DeskAnimationObserver() override {
DesksController::Get()->RemoveObserver(this);
}
DeskAnimationObserver(const DeskAnimationObserver& other) = delete;
DeskAnimationObserver& operator=(const DeskAnimationObserver& rhs) = delete;
void OnDeskSwitchAnimationFinished() override {
std::move(on_desk_animation_complete_).Run();
delete this;
}
private:
base::OnceClosure on_desk_animation_complete_;
};
class ChainedDeskAnimationObserver : public ui::LayerAnimationObserver,
public DesksController::Observer {
public:
ChainedDeskAnimationObserver(bool going_left,
int target_index,
base::OnceClosure on_desk_animation_complete)
: going_left_(going_left),
target_index_(target_index),
on_desk_animation_complete_(std::move(on_desk_animation_complete)) {
DesksController::Get()->AddObserver(this);
}
ChainedDeskAnimationObserver(const ChainedDeskAnimationObserver& other) =
delete;
ChainedDeskAnimationObserver& operator=(
const ChainedDeskAnimationObserver& rhs) = delete;
~ChainedDeskAnimationObserver() override {
DesksController::Get()->RemoveObserver(this);
}
void OnLayerAnimationEnded(ui::LayerAnimationSequence* sequence) override {}
void OnLayerAnimationAborted(ui::LayerAnimationSequence* sequence) override {}
void OnLayerAnimationScheduled(
ui::LayerAnimationSequence* sequence) override {
if ((sequence->properties() & ui::LayerAnimationElement::TRANSFORM) == 0)
return;
auto* controller = DesksController::Get();
const bool activated = controller->ActivateAdjacentDesk(
going_left_, DesksSwitchSource::kDeskSwitchShortcut);
DCHECK(activated);
if (controller->GetDeskIndex(controller->GetTargetActiveDesk()) ==
target_index_) {
animation_layer_->GetAnimator()->RemoveObserver(this);
}
}
void OnDeskActivationChanged(const Desk* activated,
const Desk* deactivated) override {
if (animation_layer_)
return;
auto* animation = DesksController::Get()->animation();
DCHECK(animation);
animation_layer_ = animation->GetDeskSwitchAnimatorAtIndexForTesting(0)
->GetAnimationLayerForTesting();
animation_layer_->GetAnimator()->AddObserver(this);
}
void OnDeskSwitchAnimationFinished() override {
std::move(on_desk_animation_complete_).Run();
delete this;
}
private:
const bool going_left_;
const int target_index_;
base::OnceClosure on_desk_animation_complete_;
raw_ptr<ui::Layer> animation_layer_ = nullptr;
};
}
AutotestDesksApi::AutotestDesksApi() = default;
AutotestDesksApi::~AutotestDesksApi() = default;
AutotestDesksApi::DesksInfo::DesksInfo() = default;
AutotestDesksApi::DesksInfo::DesksInfo(const DesksInfo&) = default;
AutotestDesksApi::DesksInfo::~DesksInfo() = default;
bool AutotestDesksApi::CreateNewDesk() {
if (!DesksController::Get()->CanCreateDesks())
return false;
DesksController::Get()->NewDesk(DesksCreationRemovalSource::kKeyboard);
return true;
}
bool AutotestDesksApi::ActivateDeskAtIndex(int index,
base::OnceClosure on_complete) {
DCHECK(!on_complete.is_null());
if (index < 0)
return false;
auto* controller = DesksController::Get();
if (index >= static_cast<int>(controller->desks().size()))
return false;
const Desk* target_desk = controller->desks()[index].get();
if (target_desk == controller->active_desk())
return false;
new DeskAnimationObserver(std::move(on_complete));
controller->ActivateDesk(target_desk, DesksSwitchSource::kMiniViewButton);
return true;
}
bool AutotestDesksApi::RemoveActiveDesk(base::OnceClosure on_complete) {
DCHECK(!on_complete.is_null());
auto* controller = DesksController::Get();
if (!controller->CanRemoveDesks())
return false;
if (!Shell::Get()->overview_controller()->InOverviewSession())
new DeskAnimationObserver(std::move(on_complete));
controller->RemoveDesk(controller->active_desk(),
DesksCreationRemovalSource::kButton,
DeskCloseType::kCombineDesks);
return true;
}
bool AutotestDesksApi::ActivateAdjacentDesksToTargetIndex(
int index,
base::OnceClosure on_complete) {
DCHECK(!on_complete.is_null());
if (index < 0)
return false;
auto* controller = DesksController::Get();
if (index >= static_cast<int>(controller->desks().size()))
return false;
const Desk* target_desk = controller->desks()[index].get();
if (target_desk == controller->active_desk())
return false;
int active_index = controller->GetDeskIndex(controller->active_desk());
const bool going_left = index < active_index;
new ChainedDeskAnimationObserver(going_left, index, std::move(on_complete));
const bool activated = controller->ActivateAdjacentDesk(
going_left, DesksSwitchSource::kDeskSwitchShortcut);
DCHECK(activated);
return true;
}
bool AutotestDesksApi::IsWindowInDesk(aura::Window* window, int desk_index) {
aura::Window* desk_container = DesksController::Get()->GetDeskContainer(
window->GetRootWindow(), desk_index);
return desk_container->Contains(window);
}
AutotestDesksApi::DesksInfo AutotestDesksApi::GetDesksInfo() const {
auto* controller = DesksController::Get();
DesksInfo info;
info.active_desk_index = controller->GetActiveDeskIndex();
info.num_desks = controller->desks().size();
info.is_animating = !!controller->animation();
aura::Window* root = Shell::GetPrimaryRootWindow();
for (const auto& desk : controller->desks()) {
aura::Window* container = desk->GetDeskContainerForRoot(root);
info.desk_containers.push_back(container->GetName());
}
return info;
}
}