// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ASH_APP_LIST_APP_LIST_CONTROLLER_IMPL_H_
#define ASH_APP_LIST_APP_LIST_CONTROLLER_IMPL_H_
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "ash/app_list/app_list_metrics.h"
#include "ash/app_list/app_list_view_delegate.h"
#include "ash/app_list/home_launcher_animation_info.h"
#include "ash/app_list/model/search/search_model.h"
#include "ash/app_list/quick_app_access_model.h"
#include "ash/ash_export.h"
#include "ash/assistant/model/assistant_ui_model_observer.h"
#include "ash/public/cpp/app_list/app_list_client.h"
#include "ash/public/cpp/app_list/app_list_controller.h"
#include "ash/public/cpp/app_list/app_list_model_delegate.h"
#include "ash/public/cpp/assistant/controller/assistant_controller_observer.h"
#include "ash/public/cpp/feature_discovery_duration_reporter.h"
#include "ash/public/cpp/keyboard/keyboard_controller_observer.h"
#include "ash/public/cpp/session/session_observer.h"
#include "ash/public/cpp/shelf_types.h"
#include "ash/public/cpp/wallpaper/wallpaper_controller_observer.h"
#include "ash/shelf/shelf_layout_manager.h"
#include "ash/shell_observer.h"
#include "ash/wm/overview/overview_observer.h"
#include "ash/wm/overview/overview_types.h"
#include "ash/wm/splitview/split_view_observer.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/scoped_observation.h"
#include "base/time/time.h"
#include "chromeos/ash/services/assistant/public/cpp/assistant_enums.h"
#include "ui/aura/window_observer.h"
#include "ui/compositor/throughput_tracker.h"
#include "ui/display/display_observer.h"
#include "ui/display/manager/display_manager_observer.h"
#include "ui/display/types/display_constants.h"
class PrefRegistrySimple;
namespace display {
enum class TabletState;
} // namespace display
namespace ash {
class AppListBadgeController;
class AppListBubblePresenter;
class AppListControllerObserver;
class AppListItem;
struct AppListItemMetadata;
class AppListModel;
class AppListModelProvider;
class AppListPresenterImpl;
enum class AppListSortOrder;
class AppsCollectionsController;
// Ash's AppListController owns the AppListModel and implements interface
// functions that allow Chrome to modify and observe the Shelf and AppListModel
// state. It also controls the "home launcher", the tablet mode app list.
class ASH_EXPORT AppListControllerImpl
: public AppListController,
public SessionObserver,
public AppListViewDelegate,
public ShellObserver,
public OverviewObserver,
public SplitViewObserver,
public display::DisplayObserver,
public KeyboardControllerObserver,
public WallpaperControllerObserver,
public AssistantStateObserver,
public display::DisplayManagerObserver,
public aura::WindowObserver,
public AssistantControllerObserver,
public AssistantUiModelObserver,
public FeatureDiscoveryDurationReporter::ReporterObserver {
public:
AppListControllerImpl();
AppListControllerImpl(const AppListControllerImpl&) = delete;
AppListControllerImpl& operator=(const AppListControllerImpl&) = delete;
~AppListControllerImpl() override;
enum HomeLauncherTransitionState {
kFinished, // No drag or animation is in progress
kMostlyShown, // The home launcher occupies more than half of the screen
kMostlyHidden, // The home launcher occupies less than half of the screen
};
static void RegisterProfilePrefs(PrefRegistrySimple* registry);
AppListPresenterImpl* fullscreen_presenter() {
return fullscreen_presenter_.get();
}
// AppListController:
void SetClient(AppListClient* client) override;
AppListClient* GetClient() override;
void AddObserver(AppListControllerObserver* observer) override;
void RemoveObserver(AppListControllerObserver* obsever) override;
void SetActiveModel(int profile_id,
AppListModel* model,
SearchModel* search_model,
QuickAppAccessModel* quick_app_access_model) override;
void ClearActiveModel() override;
void DismissAppList() override;
void ShowAppList(AppListShowSource source) override;
AppListShowSource LastAppListShowSource() override;
aura::Window* GetWindow() override;
bool IsVisible(const std::optional<int64_t>& display_id) override;
bool IsVisible() override;
// SessionObserver:
void OnActiveUserPrefServiceChanged(PrefService* pref_service) override;
void OnSessionStateChanged(session_manager::SessionState state) override;
void OnUserSessionAdded(const AccountId& account_id) override;
// Methods used in ash:
bool GetTargetVisibility(const std::optional<int64_t>& display_id) const;
// 'should_record_metrics' is false when transitioning to tablet mode with a
// visible window which is shown over, and thus hides, the app list.
void Show(int64_t display_id,
AppListShowSource show_source,
base::TimeTicks event_time_stamp,
bool should_record_metrics);
void UpdateAppListWithNewTemporarySortOrder(
const std::optional<AppListSortOrder>& new_order,
bool animate,
base::OnceClosure update_position_closure) override;
// In tablet mode, takes the user to the home screen, either by ending
// Overview Mode/Split View Mode or by minimizing the other windows. Returns
// false if there was nothing to do because the given display was already
// "home". Illegal to call in clamshell mode.
bool GoHome(int64_t display_id);
// Toggles app list visibility. In tablet mode, this can only show the app
// list (by hiding any windows that might be shown over the homde launcher).
// |display_id| is the id of display where app list should toggle.
// |show_source| is the source of the event. |event_time_stamp| records the
// event timestamp.
ShelfAction ToggleAppList(int64_t display_id,
AppListShowSource show_source,
base::TimeTicks event_time_stamp);
// Returns whether the home launcher should be visible.
bool ShouldHomeLauncherBeVisible() const;
// AppListViewDelegate:
AppListNotifier* GetNotifier() override;
std::unique_ptr<ash::ScopedIphSession> CreateLauncherSearchIphSession()
override;
void StartAssistant(assistant::AssistantEntryPoint entry_point) override;
void EndAssistant(assistant::AssistantExitPoint exit_point) override;
std::vector<AppListSearchControlCategory> GetToggleableCategories()
const override;
void StartSearch(const std::u16string& raw_query) override;
void StartZeroStateSearch(base::OnceClosure callback,
base::TimeDelta timeout) override;
void OpenSearchResult(const std::string& result_id,
int event_flags,
AppListLaunchedFrom launched_from,
AppListLaunchType launch_type,
int suggestion_index,
bool launch_as_default) override;
void InvokeSearchResultAction(const std::string& result_id,
SearchResultActionType action) override;
using GetContextMenuModelCallback =
AppListViewDelegate::GetContextMenuModelCallback;
void ViewShown(int64_t display_id) override;
bool AppListTargetVisibility() const override;
void ViewClosing() override;
void ActivateItem(const std::string& id,
int event_flags,
AppListLaunchedFrom launched_from,
bool is_app_above_the_fold) override;
void GetContextMenuModel(const std::string& id,
AppListItemContext item_context,
GetContextMenuModelCallback callback) override;
void ShowWallpaperContextMenu(const gfx::Point& onscreen_location,
ui::MenuSourceType source_type) override;
bool KeyboardTraversalEngaged() override;
bool CanProcessEventsOnApplistViews() override;
bool ShouldDismissImmediately() override;
AssistantViewDelegate* GetAssistantViewDelegate() override;
void OnSearchResultVisibilityChanged(const std::string& id,
bool visibility) override;
bool IsAssistantAllowedAndEnabled() const override;
void OnStateTransitionAnimationCompleted(
AppListViewState state,
bool was_animation_interrupted) override;
void LoadIcon(const std::string& app_id) override;
bool HasValidProfile() const override;
bool ShouldHideContinueSection() const override;
void SetHideContinueSection(bool hide) override;
bool IsCategoryEnabled(AppListSearchControlCategory category) override;
void SetCategoryEnabled(AppListSearchControlCategory category,
bool enabled) override;
void GetAppLaunchedMetricParams(
AppLaunchedMetricParams* metric_params) override;
gfx::Rect SnapBoundsToDisplayEdge(const gfx::Rect& bounds) override;
AppListState GetCurrentAppListPage() const override;
void OnAppListPageChanged(AppListState page) override;
AppListViewState GetAppListViewState() const override;
void OnViewStateChanged(AppListViewState state) override;
int GetShelfSize() override;
int GetSystemShelfInsetsInTabletMode() override;
bool IsInTabletMode() const override;
void RecordAppsDefaultVisibility(
const std::vector<std::string>& apps_above_the_fold,
const std::vector<std::string>& apps_below_the_fold,
bool is_apps_collections_page) override;
// Notifies observers of AppList visibility changes.
void OnVisibilityChanged(bool visible, int64_t display_id);
void OnVisibilityWillChange(bool visible, int64_t display_id);
// ShellObserver:
void OnShelfAlignmentChanged(aura::Window* root_window,
ShelfAlignment old_alignment) override;
void OnShellDestroying() override;
// OverviewObserver:
void OnOverviewModeStarting() override;
void OnOverviewModeStartingAnimationComplete(bool canceled) override;
void OnOverviewModeEnding(OverviewSession* session) override;
void OnOverviewModeEnded() override;
void OnOverviewModeEndingAnimationComplete(bool canceled) override;
// SplitViewObserver:
void OnSplitViewStateChanged(SplitViewController::State previous_state,
SplitViewController::State state) override;
// display::DisplayObserver:
void OnDisplayTabletStateChanged(display::TabletState state) override;
// KeyboardControllerObserver:
void OnKeyboardVisibilityChanged(bool is_visible) override;
// WallpaperControllerObserver:
void OnWallpaperPreviewStarted() override;
void OnWallpaperPreviewEnded() override;
// AssistantStateObserver:
void OnAssistantStatusChanged(assistant::AssistantStatus status) override;
void OnAssistantSettingsEnabled(bool enabled) override;
void OnAssistantFeatureAllowedChanged(
assistant::AssistantAllowedState state) override;
// display::DisplayManagerObserver:
void OnDidApplyDisplayChanges() override;
// aura::WindowObserver:
void OnWindowVisibilityChanging(aura::Window* window, bool visible) override;
void OnWindowDestroyed(aura::Window* window) override;
// AssistantControllerObserver:
void OnAssistantReady() override;
// AssistantUiModelObserver:
void OnUiVisibilityChanged(
AssistantVisibility new_visibility,
AssistantVisibility old_visibility,
std::optional<AssistantEntryPoint> entry_point,
std::optional<AssistantExitPoint> exit_point) override;
// Gets the home screen window, if available, or null if the home screen
// window is being hidden for effects (e.g. when dragging windows or
// previewing the wallpaper).
aura::Window* GetHomeScreenWindow() const;
// Scales the home launcher view maintaining the view center point, and
// updates its opacity. If |callback| is non-null, the update should be
// animated, and the |callback| should be called with the animation settings.
// |animation_info| - Information about the transition trigger that will be
// used to report animation metrics. Should be set only if |callback| is
// not null (otherwise the transition will not be animated).
using UpdateAnimationSettingsCallback =
base::RepeatingCallback<void(ui::ScopedLayerAnimationSettings* settings)>;
void UpdateScaleAndOpacityForHomeLauncher(
float scale,
float opacity,
std::optional<HomeLauncherAnimationInfo> animation_info,
UpdateAnimationSettingsCallback callback);
// Called when the HomeLauncher positional animation has completed.
void OnHomeLauncherAnimationComplete(bool shown, int64_t display_id);
// Called when the HomeLauncher has changed its position on the screen,
// during either an animation or a drag.
void OnHomeLauncherPositionChanged(int percent_shown, int64_t display_id);
// True if home screen is visible.
bool IsHomeScreenVisible();
// Called when a window starts/ends dragging. If the home screen is shown, we
// should hide it during dragging a window and reshow it when the drag ends.
void OnWindowDragStarted();
// If |animate| is true, scale-in-to-show home screen if home screen should
// be shown after drag ends.
void OnWindowDragEnded(bool animate);
bool onscreen_keyboard_shown() const { return onscreen_keyboard_shown_; }
// Performs the 'back' action for the active page.
void Back();
void SetKeyboardTraversalMode(bool engaged);
// Returns whether the assistant page is showing (either in bubble app list or
// fullscreen app list).
bool IsShowingEmbeddedAssistantUI() const;
// Sets up `close_assistant_ui_runner_` to close the assistant.
void ScheduleCloseAssistant();
// Runs `close_assistant_ui_runner_` when it is non-null.
void MaybeCloseAssistant();
using StateTransitionAnimationCallback =
base::RepeatingCallback<void(AppListViewState)>;
void SetStateTransitionAnimationCallbackForTesting(
StateTransitionAnimationCallback callback);
using HomeLauncherAnimationCallback =
base::RepeatingCallback<void(bool shown)>;
void SetHomeLauncherAnimationCallbackForTesting(
HomeLauncherAnimationCallback callback);
AppListBubblePresenter* bubble_presenter_for_test() {
return bubble_presenter_.get();
}
void RecordShelfAppLaunched();
// Updates which container the fullscreen launcher window should be in.
void UpdateFullscreenLauncherContainer(
std::optional<int64_t> display_id = std::nullopt);
// Returns the parent window of the `AppListView` for a |display_id|.
aura::Window* GetFullscreenLauncherContainerForDisplayId(
std::optional<int64_t> display_id = std::nullopt);
// Methods for recording the state of the app list before it changes in order
// to record metrics.
void RecordAppListState();
AppListBadgeController* badge_controller_for_test() {
return badge_controller_.get();
}
// Returns the preferred width for the bubble launcher for the |root_window|.
int GetPreferredBubbleWidth(aura::Window* root_window) const;
// Set the launchable quick app button shown next to the home button. This app
// icon is shown next to the home button until the app is launched or the
// launcher is opened.
// Returns true when the quick app was changed to a valid `app_id` or reset
// using an empty `app_id`.
bool SetHomeButtonQuickApp(const std::string& app_id);
private:
// Convenience methods for getting models from `model_provider_`.
AppListModel* GetModel();
SearchModel* GetSearchModel();
std::unique_ptr<AppListItem> CreateAppListItem(
std::unique_ptr<AppListItemMetadata> metadata);
// Update the visibility of UIs controlled by `SearchBoxModel`.
void UpdateSearchBoxUiVisibilities();
int64_t GetDisplayIdToShowAppListOn();
void ResetHomeLauncherIfShown();
void ShowHomeScreen(AppListShowSource show_source);
// Updates the visibility of the home screen based on e.g. if the device is
// in overview mode.
void UpdateHomeScreenVisibility();
// Returns true if home screen should be shown based on the current
// configuration.
bool ShouldShowHomeScreen() const;
// Updates home launcher scale and opacity when the overview mode state
// changes. `show_home_launcher` - whether the home launcher should be shown.
// `animate` - whether the transition should be animated.
void UpdateForOverviewModeChange(bool show_home_launcher, bool animate);
// Shuts down the AppListControllerImpl, removing itself as an observer.
void Shutdown();
// Record the app launch for AppListAppLaunchedV2 metric.
void RecordAppLaunched(AppListLaunchedFrom launched_from);
// Updates the window that is tracked as |tracked_app_window_|.
void UpdateTrackedAppWindow();
// Responsible for starting or stopping |smoothness_tracker_|.
void StartTrackingAnimationSmoothness(int64_t display_id);
void RecordAnimationSmoothness();
// Called when all the window minimize animations triggered by a tablet mode
// "Go Home" have ended. |display_id| is the home screen display ID.
void OnGoHomeWindowAnimationsEnded(int64_t display_id);
// FeatureDiscoveryDurationReporter::ReporterObserver:
void OnReporterActivated() override;
// Called when display tablet state is changed to kInTabletMode or
// kInClamshellMode.
void OnChangedToInTabletMode();
void OnChangedToInClamshellMode();
// Gets the container which should contain the fullscreen launcher.
int GetFullscreenLauncherContainerId() const;
// Whether the home launcher is
// * being shown (either through an animation or a drag)
// * being hidden (either through an animation or a drag)
// * not animating nor being dragged.
// In the case where the home launcher is being dragged, the gesture can
// reverse direction at any point during the drag, in which case the only
// information given by "showing" versus "hiding" is the starting point of
// the drag and the assumed final state (which won't be accurate if the
// gesture is reversed).
HomeLauncherTransitionState home_launcher_transition_state_ = kFinished;
raw_ptr<AppListClient> client_ = nullptr;
// Tracks the most recent show source for the app list.
std::optional<AppListShowSource> last_open_source_;
// Tracks active app list and search models to app list UI stack. It can be
// accessed outside AppListModelControllerImpl using
// `AppListModelController::Get()`.
std::unique_ptr<AppListModelProvider> model_provider_;
// A callback that can be registered by a test to wait for the app list state
// transition animation to finish.
StateTransitionAnimationCallback state_transition_animation_callback_;
// Used for closing the Assistant ui in the asynchronous way.
base::ScopedClosureRunner close_assistant_ui_runner_;
// Manages the tablet mode home launcher. Destroying `AppListPresenterImpl`
// can reentrantly call back into `this` and use `model_provider_`,
// `state_transition_animation_callback_`, and `close_assistant_ui_runner_`,
// so `fullscreen_presenter_` must be ordered after all those fields.
std::unique_ptr<AppListPresenterImpl> fullscreen_presenter_;
// Manages the clamshell launcher bubble.
std::unique_ptr<AppListBubblePresenter> bubble_presenter_;
// Tracks the current page shown in the app list view (tracked for the
// fullscreen presenter).
AppListState app_list_page_ = AppListState::kInvalidState;
// Tracks the current state of `AppListView` (tracked for the fullscreen
// presenter)
AppListViewState app_list_view_state_ = AppListViewState::kClosed;
// True if the on-screen keyboard is shown.
bool onscreen_keyboard_shown_ = false;
// True if the most recent event handled by |presenter_| was a key event.
bool keyboard_traversal_engaged_ = false;
// True if Shutdown() has been called.
bool is_shutdown_ = false;
// Whether to immediately dismiss the AppListView.
bool should_dismiss_immediately_ = false;
// The last target visibility change and its display id.
bool last_target_visible_ = false;
int64_t last_target_visible_display_id_ = display::kInvalidDisplayId;
// The last visibility change and its display id.
bool last_visible_ = false;
int64_t last_visible_display_id_ = display::kInvalidDisplayId;
// Used in mojo callings to specify the profile whose app list data is
// read/written by Ash side through IPC. Notice that in multi-profile mode,
// each profile has its own AppListModelUpdater to manipulate app list items.
int profile_id_ = kAppListInvalidProfileID;
// Used when tablet mode is active to track the MRU window among the windows
// that were obscuring the home launcher when the home launcher visibility was
// last calculated.
// This window changing it's visibility to false is used as a signal that the
// home launcher visibility should be recalculated.
raw_ptr<aura::Window> tracked_app_window_ = nullptr;
// A callback that can be registered by a test to wait for the home launcher
// visibility animation to finish. Should only be used in tablet mode.
HomeLauncherAnimationCallback home_launcher_animation_callback_;
// The AppListViewState at the moment it was recorded, used to record app
// launching metrics. This allows an accurate AppListViewState to be recorded
// before AppListViewState changes.
std::optional<AppListViewState> recorded_app_list_view_state_;
// Whether the applist was shown at the moment it was recorded, used to record
// app launching metrics. This is recorded because AppList visibility can
// change before the metric is recorded.
std::optional<bool> recorded_app_list_visibility_;
// The last time the app list was shown.
std::optional<base::TimeTicks> last_show_timestamp_;
base::ObserverList<AppListControllerObserver> observers_;
// Sub-controller to handle app item badges. Must be constructed after
// `model_provider_`.
std::unique_ptr<AppListBadgeController> badge_controller_;
// Whether the wallpaper is being previewed. The home screen should be hidden
// during wallpaper preview.
bool in_wallpaper_preview_ = false;
// Whether we're currently in a window dragging process.
bool in_window_dragging_ = false;
// Whether a session was ever set ACTIVE for the app list.
bool has_session_started_ = false;
// The last overview mode exit type - cached when the overview exit starts, so
// it can be used to decide how to update home screen when overview mode exit
// animations are finished (at which point this information will not be
// available).
std::optional<OverviewEnterExitType> overview_exit_type_;
// Responsible for recording smoothness related UMA stats for home screen
// animations.
std::optional<ui::ThroughputTracker> smoothness_tracker_;
// Sub-controller to handle app collections page.
std::unique_ptr<AppsCollectionsController> apps_collections_controller_;
base::ScopedObservation<SplitViewController, SplitViewObserver>
split_view_observation_{this};
base::WeakPtrFactory<AppListControllerImpl> weak_ptr_factory_{this};
};
} // namespace ash
#endif // ASH_APP_LIST_APP_LIST_CONTROLLER_IMPL_H_