blob: d8c25ece5cb3a64dedbdbc6ef220ed72f943d54d [file] [log] [blame]
// Copyright 2019 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/public/cpp/autotest_private_api_utils.h"
#include "ash/app_list/app_list_controller_impl.h"
#include "ash/frame/non_client_frame_view_ash.h"
#include "ash/home_screen/home_screen_controller.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/callback_helpers.h"
#include "base/optional.h"
#include "base/scoped_observation.h"
#include "ui/compositor/layer_animation_observer.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() {
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_;
DISALLOW_COPY_AND_ASSIGN(HomeLauncherStateWaiter);
};
// 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() {
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_;
DISALLOW_COPY_AND_ASSIGN(LauncherStateWaiter);
};
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=*/base::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()->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;
base::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()->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