1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
|
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/public/cpp/autotest_private_api_utils.h"
#include "ash/app_list/app_list_controller_impl.h"
#include "ash/app_list/app_list_presenter_impl.h"
#include "ash/frame/non_client_frame_view_ash.h"
#include "ash/shell.h"
#include "ash/wm/mru_window_tracker.h"
#include "ash/wm/tablet_mode/scoped_skip_user_session_blocked_check.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/scoped_observation.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/layer_animation_observer.h"
#include "ui/compositor/layer_animator.h"
namespace ash {
namespace {
class HomeLauncherStateWaiter {
public:
HomeLauncherStateWaiter(bool target_shown, base::OnceClosure closure)
: target_shown_(target_shown), closure_(std::move(closure)) {
Shell::Get()
->app_list_controller()
->SetHomeLauncherAnimationCallbackForTesting(base::BindRepeating(
&HomeLauncherStateWaiter::OnHomeLauncherAnimationCompleted,
base::Unretained(this)));
}
HomeLauncherStateWaiter(const HomeLauncherStateWaiter&) = delete;
HomeLauncherStateWaiter& operator=(const HomeLauncherStateWaiter&) = delete;
~HomeLauncherStateWaiter() {
Shell::Get()
->app_list_controller()
->SetHomeLauncherAnimationCallbackForTesting(base::NullCallback());
}
private:
// Passed to AppListControllerImpl as a callback to run when home launcher
// transition animation is complete.
void OnHomeLauncherAnimationCompleted(bool shown) {
if (shown == target_shown_) {
std::move(closure_).Run();
delete this;
}
}
bool target_shown_;
base::OnceClosure closure_;
};
// A waiter that waits until the animation ended with the target state, and
// execute the callback. This self destruction upon completion.
class LauncherStateWaiter {
public:
LauncherStateWaiter(ash::AppListViewState state, base::OnceClosure closure)
: target_state_(state), closure_(std::move(closure)) {
Shell::Get()
->app_list_controller()
->SetStateTransitionAnimationCallbackForTesting(base::BindRepeating(
&LauncherStateWaiter::OnStateChanged, base::Unretained(this)));
}
LauncherStateWaiter(const LauncherStateWaiter&) = delete;
LauncherStateWaiter& operator=(const LauncherStateWaiter&) = delete;
~LauncherStateWaiter() {
Shell::Get()
->app_list_controller()
->SetStateTransitionAnimationCallbackForTesting(base::NullCallback());
}
void OnStateChanged(ash::AppListViewState state) {
if (target_state_ == state) {
std::move(closure_).Run();
delete this;
}
}
private:
ash::AppListViewState target_state_;
base::OnceClosure closure_;
};
class LauncherAnimationWaiter : public ui::LayerAnimationObserver {
public:
LauncherAnimationWaiter(AppListView* view, base::OnceClosure closure)
: closure_(std::move(closure)) {
observation_.Observe(view->GetWidget()->GetLayer()->GetAnimator());
}
~LauncherAnimationWaiter() override = default;
LauncherAnimationWaiter(const LauncherAnimationWaiter&) = delete;
LauncherAnimationWaiter& operator=(const LauncherAnimationWaiter&) = delete;
private:
// ui::LayerAnimationObserver:
void OnLayerAnimationEnded(ui::LayerAnimationSequence* sequence) override {
std::move(closure_).Run();
delete this;
}
void OnLayerAnimationAborted(ui::LayerAnimationSequence* sequence) override {
OnLayerAnimationEnded(sequence);
}
void OnLayerAnimationScheduled(
ui::LayerAnimationSequence* sequence) override {}
base::OnceClosure closure_;
base::ScopedObservation<ui::LayerAnimator, ui::LayerAnimationObserver>
observation_{this};
};
bool WaitForHomeLauncherState(bool target_visible, base::OnceClosure closure) {
if (Shell::Get()->app_list_controller()->IsVisible(
/*display_id=*/absl::nullopt) == target_visible) {
std::move(closure).Run();
return true;
}
new HomeLauncherStateWaiter(target_visible, std::move(closure));
return false;
}
bool WaitForLauncherAnimation(base::OnceClosure closure) {
auto* app_list_view =
Shell::Get()->app_list_controller()->fullscreen_presenter()->GetView();
if (!app_list_view) {
std::move(closure).Run();
return true;
}
bool animating =
app_list_view->GetWidget()->GetLayer()->GetAnimator()->is_animating();
if (!animating) {
std::move(closure).Run();
return true;
}
new LauncherAnimationWaiter(app_list_view, std::move(closure));
return false;
}
} // namespace
std::vector<aura::Window*> GetAppWindowList() {
ScopedSkipUserSessionBlockedCheck skip_session_blocked;
return Shell::Get()->mru_window_tracker()->BuildAppWindowList(kAllDesks);
}
bool WaitForLauncherState(AppListViewState target_state,
base::OnceClosure closure) {
const bool in_tablet_mode =
Shell::Get()->tablet_mode_controller()->InTabletMode();
if (in_tablet_mode) {
// App-list can't enter kPeeking or kHalf state in tablet mode. Thus
// |target_state| should be either kClosed, kFullscreenAllApps or
// kFullscreenSearch.
DCHECK(target_state == AppListViewState::kClosed ||
target_state == AppListViewState::kFullscreenAllApps ||
target_state == AppListViewState::kFullscreenSearch);
}
// In the tablet mode, home launcher visibility state needs special handling,
// as app list view visibility does not match home launcher visibility. The
// app list view is always visible, but the home launcher may be obscured by
// app windows. The waiter interprets waits for kClosed state as waits
// "home launcher not visible" state - note that the app list view
// is actually expected to be in a visible state.
AppListViewState effective_target_state =
in_tablet_mode && target_state == AppListViewState::kClosed
? AppListViewState::kFullscreenAllApps
: target_state;
absl::optional<bool> target_home_launcher_visibility;
if (in_tablet_mode)
target_home_launcher_visibility = target_state != AppListViewState::kClosed;
// Don't wait if the launcher is already in the target state and not
// animating.
auto* app_list_view =
Shell::Get()->app_list_controller()->fullscreen_presenter()->GetView();
bool animating =
app_list_view &&
app_list_view->GetWidget()->GetLayer()->GetAnimator()->is_animating();
bool at_target_state =
(!app_list_view && effective_target_state == AppListViewState::kClosed) ||
(app_list_view &&
app_list_view->app_list_state() == effective_target_state);
if (at_target_state && !animating) {
// In tablet mode, ensure that the home launcher is in the expected state.
if (target_home_launcher_visibility.has_value()) {
return WaitForHomeLauncherState(*target_home_launcher_visibility,
std::move(closure));
}
std::move(closure).Run();
return true;
}
// In tablet mode, ensure that the home launcher is in the expected state.
base::OnceClosure callback =
target_home_launcher_visibility.has_value()
? base::BindOnce(base::IgnoreResult(&WaitForHomeLauncherState),
*target_home_launcher_visibility, std::move(closure))
: std::move(closure);
if (at_target_state)
return WaitForLauncherAnimation(std::move(callback));
new LauncherStateWaiter(
target_state,
base::BindOnce(base::IgnoreResult(&WaitForLauncherAnimation),
std::move(callback)));
return false;
}
} // namespace ash
|