blob: 2ba29ceb49c3235df8249f6afc6bdb36e9271035 [file] [log] [blame]
// Copyright 2018 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/app_list/app_list_controller_impl.h"
#include <utility>
#include <vector>
#include "ash/app_list/app_list_controller_observer.h"
#include "ash/app_list/app_list_metrics.h"
#include "ash/app_list/app_list_presenter_delegate_impl.h"
#include "ash/app_list/model/app_list_folder_item.h"
#include "ash/app_list/model/app_list_item.h"
#include "ash/app_list/views/app_list_main_view.h"
#include "ash/app_list/views/app_list_view.h"
#include "ash/app_list/views/contents_view.h"
#include "ash/app_list/views/search_box_view.h"
#include "ash/assistant/assistant_controller.h"
#include "ash/assistant/assistant_ui_controller.h"
#include "ash/assistant/model/assistant_ui_model.h"
#include "ash/assistant/ui/assistant_ui_constants.h"
#include "ash/assistant/ui/assistant_view_delegate.h"
#include "ash/assistant/util/assistant_util.h"
#include "ash/assistant/util/deep_link_util.h"
#include "ash/home_screen/home_launcher_gesture_handler.h"
#include "ash/home_screen/home_screen_controller.h"
#include "ash/keyboard/ui/keyboard_controller.h"
#include "ash/public/cpp/app_list/app_list_client.h"
#include "ash/public/cpp/app_list/app_list_features.h"
#include "ash/public/cpp/app_list/app_list_metrics.h"
#include "ash/public/cpp/app_list/app_list_types.h"
#include "ash/public/cpp/ash_pref_names.h"
#include "ash/public/cpp/shell_window_ids.h"
#include "ash/public/cpp/voice_interaction_controller.h"
#include "ash/root_window_controller.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shelf/shelf_layout_manager.h"
#include "ash/shell.h"
#include "ash/wallpaper/wallpaper_controller_impl.h"
#include "ash/wm/mru_window_tracker.h"
#include "ash/wm/overview/overview_controller.h"
#include "ash/wm/splitview/split_view_controller.h"
#include "ash/wm/tablet_mode/tablet_mode_controller.h"
#include "ash/wm/window_state.h"
#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#include "base/metrics/user_metrics.h"
#include "base/strings/utf_string_conversions.h"
#include "chromeos/constants/chromeos_switches.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "extensions/common/constants.h"
#include "ui/base/ui_base_features.h"
#include "ui/display/manager/display_manager.h"
#include "ui/display/screen.h"
#include "ui/views/controls/textfield/textfield.h"
#include "ui/wm/public/activation_client.h"
namespace ash {
namespace {
bool IsHomeScreenAvailable() {
return Shell::Get()->home_screen_controller()->IsHomeScreenAvailable();
}
bool IsTabletMode() {
return Shell::Get()->tablet_mode_controller()->InTabletMode();
}
// Close current Assistant UI.
void CloseAssistantUi(AssistantExitPoint exit_point) {
if (app_list_features::IsEmbeddedAssistantUIEnabled())
Shell::Get()->assistant_controller()->ui_controller()->CloseUi(exit_point);
}
app_list::TabletModeAnimationTransition CalculateAnimationTransitionForMetrics(
HomeScreenDelegate::AnimationTrigger trigger,
bool launcher_should_show) {
switch (trigger) {
case HomeScreenDelegate::AnimationTrigger::kHideForWindow:
return app_list::TabletModeAnimationTransition::
kHideHomeLauncherForWindow;
case HomeScreenDelegate::AnimationTrigger::kLauncherButton:
return app_list::TabletModeAnimationTransition::kAppListButtonShow;
case HomeScreenDelegate::AnimationTrigger::kDragRelease:
return launcher_should_show
? app_list::TabletModeAnimationTransition::kDragReleaseShow
: app_list::TabletModeAnimationTransition::kDragReleaseHide;
case HomeScreenDelegate::AnimationTrigger::kOverviewMode:
return launcher_should_show
? app_list::TabletModeAnimationTransition::kExitOverviewMode
: app_list::TabletModeAnimationTransition::kEnterOverviewMode;
}
}
int GetAssistantPrivacyInfoShownCount() {
PrefService* prefs =
Shell::Get()->session_controller()->GetLastActiveUserPrefService();
return prefs->GetInteger(prefs::kAssistantPrivacyInfoShownInLauncher);
}
void SetAssistantPrivacyInfoShownCount(int count) {
PrefService* prefs =
Shell::Get()->session_controller()->GetLastActiveUserPrefService();
prefs->SetInteger(prefs::kAssistantPrivacyInfoShownInLauncher, count);
}
bool IsAssistantPrivacyInfoDismissed() {
PrefService* prefs =
Shell::Get()->session_controller()->GetLastActiveUserPrefService();
return prefs->GetBoolean(prefs::kAssistantPrivacyInfoDismissedInLauncher);
}
void SetAssistantPrivacyInfoDismissed() {
PrefService* prefs =
Shell::Get()->session_controller()->GetLastActiveUserPrefService();
prefs->SetBoolean(prefs::kAssistantPrivacyInfoDismissedInLauncher, true);
}
} // namespace
AppListControllerImpl::AppListControllerImpl()
: model_(std::make_unique<app_list::AppListModel>()),
presenter_(std::make_unique<AppListPresenterDelegateImpl>(this)) {
model_->AddObserver(this);
SessionControllerImpl* session_controller =
Shell::Get()->session_controller();
session_controller->AddObserver(this);
// In case of crash-and-restart case where session state starts with ACTIVE
// and does not change to trigger OnSessionStateChanged(), notify the current
// session state here to ensure that the app list is shown.
OnSessionStateChanged(session_controller->GetSessionState());
Shell* shell = Shell::Get();
shell->tablet_mode_controller()->AddObserver(this);
shell->wallpaper_controller()->AddObserver(this);
shell->AddShellObserver(this);
shell->overview_controller()->AddObserver(this);
keyboard::KeyboardController::Get()->AddObserver(this);
VoiceInteractionController::Get()->AddLocalObserver(this);
shell->window_tree_host_manager()->AddObserver(this);
shell->mru_window_tracker()->AddObserver(this);
if (app_list_features::IsEmbeddedAssistantUIEnabled()) {
shell->assistant_controller()->AddObserver(this);
shell->assistant_controller()->ui_controller()->AddModelObserver(this);
}
shell->home_screen_controller()->home_launcher_gesture_handler()->AddObserver(
this);
}
AppListControllerImpl::~AppListControllerImpl() {
// If this is being destroyed before the Shell starts shutting down, first
// remove this from objects it's observing.
if (!is_shutdown_)
Shutdown();
if (client_)
client_->OnAppListControllerDestroyed();
}
// static
void AppListControllerImpl::RegisterProfilePrefs(PrefRegistrySimple* registry) {
registry->RegisterIntegerPref(prefs::kAssistantPrivacyInfoShownInLauncher, 0);
registry->RegisterBooleanPref(
prefs::kAssistantPrivacyInfoDismissedInLauncher, false,
user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
}
void AppListControllerImpl::SetClient(app_list::AppListClient* client) {
client_ = client;
}
app_list::AppListClient* AppListControllerImpl::GetClient() {
DCHECK(client_);
return client_;
}
app_list::AppListModel* AppListControllerImpl::GetModel() {
return model_.get();
}
app_list::SearchModel* AppListControllerImpl::GetSearchModel() {
return &search_model_;
}
void AppListControllerImpl::AddItem(
std::unique_ptr<ash::AppListItemMetadata> item_data) {
const std::string folder_id = item_data->folder_id;
if (folder_id.empty())
model_->AddItem(CreateAppListItem(std::move(item_data)));
else
AddItemToFolder(std::move(item_data), folder_id);
}
void AppListControllerImpl::AddItemToFolder(
std::unique_ptr<ash::AppListItemMetadata> item_data,
const std::string& folder_id) {
// When we're setting a whole model of a profile, each item may have its
// folder id set properly. However, |AppListModel::AddItemToFolder| requires
// the item to add is not in the target folder yet, and sets its folder id
// later. So we should clear the folder id here to avoid breaking checks.
item_data->folder_id.clear();
model_->AddItemToFolder(CreateAppListItem(std::move(item_data)), folder_id);
}
void AppListControllerImpl::RemoveItem(const std::string& id) {
model_->DeleteItem(id);
}
void AppListControllerImpl::RemoveUninstalledItem(const std::string& id) {
model_->DeleteUninstalledItem(id);
}
void AppListControllerImpl::MoveItemToFolder(const std::string& id,
const std::string& folder_id) {
app_list::AppListItem* item = model_->FindItem(id);
model_->MoveItemToFolder(item, folder_id);
}
void AppListControllerImpl::SetStatus(ash::AppListModelStatus status) {
model_->SetStatus(status);
}
void AppListControllerImpl::SetState(ash::AppListState state) {
model_->SetState(state);
}
void AppListControllerImpl::HighlightItemInstalledFromUI(
const std::string& id) {
model_->top_level_item_list()->HighlightItemInstalledFromUI(id);
}
void AppListControllerImpl::SetSearchEngineIsGoogle(bool is_google) {
search_model_.SetSearchEngineIsGoogle(is_google);
}
void AppListControllerImpl::SetSearchTabletAndClamshellAccessibleName(
const base::string16& tablet_accessible_name,
const base::string16& clamshell_accessible_name) {
search_model_.search_box()->SetTabletAndClamshellAccessibleName(
tablet_accessible_name, clamshell_accessible_name);
}
void AppListControllerImpl::SetSearchHintText(const base::string16& hint_text) {
search_model_.search_box()->SetHintText(hint_text);
}
void AppListControllerImpl::UpdateSearchBox(const base::string16& text,
bool initiated_by_user) {
search_model_.search_box()->Update(text, initiated_by_user);
}
void AppListControllerImpl::PublishSearchResults(
std::vector<std::unique_ptr<ash::SearchResultMetadata>> results) {
std::vector<std::unique_ptr<app_list::SearchResult>> new_results;
for (auto& result_metadata : results) {
std::unique_ptr<app_list::SearchResult> result =
std::make_unique<app_list::SearchResult>();
result->SetMetadata(std::move(result_metadata));
new_results.push_back(std::move(result));
}
search_model_.PublishResults(std::move(new_results));
}
void AppListControllerImpl::SetItemMetadata(
const std::string& id,
std::unique_ptr<ash::AppListItemMetadata> data) {
app_list::AppListItem* item = model_->FindItem(id);
if (!item)
return;
// data may not contain valid position or icon. Preserve it in this case.
if (!data->position.IsValid())
data->position = item->position();
// Update the item's position and name based on the metadata.
if (!data->position.Equals(item->position()))
model_->SetItemPosition(item, data->position);
if (data->short_name.empty()) {
if (data->name != item->name()) {
model_->SetItemName(item, data->name);
}
} else {
if (data->name != item->name() || data->short_name != item->short_name()) {
model_->SetItemNameAndShortName(item, data->name, data->short_name);
}
}
// Folder icon is generated on ash side and chrome side passes a null
// icon here. Skip it.
if (data->icon.isNull())
data->icon = item->icon();
item->SetMetadata(std::move(data));
}
void AppListControllerImpl::SetItemIcon(const std::string& id,
const gfx::ImageSkia& icon) {
app_list::AppListItem* item = model_->FindItem(id);
if (item)
item->SetIcon(icon);
}
void AppListControllerImpl::SetItemIsInstalling(const std::string& id,
bool is_installing) {
app_list::AppListItem* item = model_->FindItem(id);
if (item)
item->SetIsInstalling(is_installing);
}
void AppListControllerImpl::SetItemPercentDownloaded(
const std::string& id,
int32_t percent_downloaded) {
app_list::AppListItem* item = model_->FindItem(id);
if (item)
item->SetPercentDownloaded(percent_downloaded);
}
void AppListControllerImpl::SetModelData(
int profile_id,
std::vector<std::unique_ptr<ash::AppListItemMetadata>> apps,
bool is_search_engine_google) {
// Clear old model data.
model_->DeleteAllItems();
search_model_.DeleteAllResults();
profile_id_ = profile_id;
// Populate new models. First populate folders and then other items to avoid
// automatically creating folder items in |AddItemToFolder|.
for (auto& app : apps) {
if (!app->is_folder)
continue;
DCHECK(app->folder_id.empty());
AddItem(std::move(app));
}
for (auto& app : apps) {
if (!app)
continue;
AddItem(std::move(app));
}
search_model_.SetSearchEngineIsGoogle(is_search_engine_google);
}
void AppListControllerImpl::SetSearchResultMetadata(
std::unique_ptr<ash::SearchResultMetadata> metadata) {
app_list::SearchResult* result = search_model_.FindSearchResult(metadata->id);
if (result)
result->SetMetadata(std::move(metadata));
}
void AppListControllerImpl::SetSearchResultIsInstalling(const std::string& id,
bool is_installing) {
app_list::SearchResult* result = search_model_.FindSearchResult(id);
if (result)
result->SetIsInstalling(is_installing);
}
void AppListControllerImpl::SetSearchResultPercentDownloaded(
const std::string& id,
int32_t percent_downloaded) {
app_list::SearchResult* result = search_model_.FindSearchResult(id);
if (result)
result->SetPercentDownloaded(percent_downloaded);
}
void AppListControllerImpl::NotifySearchResultItemInstalled(
const std::string& id) {
app_list::SearchResult* result = search_model_.FindSearchResult(id);
if (result)
result->NotifyItemInstalled();
}
void AppListControllerImpl::GetIdToAppListIndexMap(
GetIdToAppListIndexMapCallback callback) {
base::flat_map<std::string, uint16_t> id_to_app_list_index;
for (size_t i = 0; i < model_->top_level_item_list()->item_count(); ++i)
id_to_app_list_index[model_->top_level_item_list()->item_at(i)->id()] = i;
std::move(callback).Run(id_to_app_list_index);
}
void AppListControllerImpl::FindOrCreateOemFolder(
const std::string& oem_folder_name,
const syncer::StringOrdinal& preferred_oem_position,
FindOrCreateOemFolderCallback callback) {
app_list::AppListFolderItem* oem_folder =
model_->FindFolderItem(kOemFolderId);
if (!oem_folder) {
std::unique_ptr<app_list::AppListFolderItem> new_folder =
std::make_unique<app_list::AppListFolderItem>(kOemFolderId);
syncer::StringOrdinal oem_position = preferred_oem_position.IsValid()
? preferred_oem_position
: GetOemFolderPos();
// Do not create a sync item for the OEM folder here, do it in
// ResolveFolderPositions() when the item position is finalized.
oem_folder = static_cast<app_list::AppListFolderItem*>(
model_->AddItem(std::move(new_folder)));
model_->SetItemPosition(oem_folder, oem_position);
}
model_->SetItemName(oem_folder, oem_folder_name);
std::move(callback).Run();
}
void AppListControllerImpl::ResolveOemFolderPosition(
const syncer::StringOrdinal& preferred_oem_position,
ResolveOemFolderPositionCallback callback) {
// In ash:
app_list::AppListFolderItem* ash_oem_folder = FindFolderItem(kOemFolderId);
std::unique_ptr<ash::AppListItemMetadata> metadata;
if (ash_oem_folder) {
const syncer::StringOrdinal& oem_folder_pos =
preferred_oem_position.IsValid() ? preferred_oem_position
: GetOemFolderPos();
model_->SetItemPosition(ash_oem_folder, oem_folder_pos);
metadata = ash_oem_folder->CloneMetadata();
}
std::move(callback).Run(std::move(metadata));
}
void AppListControllerImpl::DismissAppList() {
presenter_.Dismiss(base::TimeTicks());
}
void AppListControllerImpl::GetAppInfoDialogBounds(
GetAppInfoDialogBoundsCallback callback) {
app_list::AppListView* app_list_view = presenter_.GetView();
gfx::Rect bounds = gfx::Rect();
if (app_list_view)
bounds = app_list_view->GetAppInfoDialogBounds();
std::move(callback).Run(bounds);
}
void AppListControllerImpl::ShowAppListAndSwitchToState(
ash::AppListState state) {
bool app_list_was_open = true;
if (!presenter_.IsVisible()) {
// TODO(calamity): This may cause the app list to show briefly before the
// state change. If this becomes an issue, add the ability to ash::Shell to
// load the app list without showing it.
ShowAppList();
app_list_was_open = false;
}
if (state == ash::AppListState::kInvalidState)
return;
app_list::ContentsView* contents_view =
presenter_.GetView()->app_list_main_view()->contents_view();
contents_view->SetActiveState(state, app_list_was_open /* animate */);
}
void AppListControllerImpl::ShowAppList() {
presenter_.Show(GetDisplayIdToShowAppListOn(), base::TimeTicks());
}
////////////////////////////////////////////////////////////////////////////////
// app_list::AppListModelObserver:
void AppListControllerImpl::OnAppListItemAdded(app_list::AppListItem* item) {
if (item->is_folder())
client_->OnFolderCreated(profile_id_, item->CloneMetadata());
else if (item->is_page_break())
client_->OnPageBreakItemAdded(profile_id_, item->id(), item->position());
}
void AppListControllerImpl::OnActiveUserPrefServiceChanged(
PrefService* /* pref_service */) {
if (!IsHomeScreenAvailable()) {
DismissAppList();
return;
}
// Show the app list after signing in in tablet mode.
Show(GetDisplayIdToShowAppListOn(), app_list::AppListShowSource::kTabletMode,
base::TimeTicks());
// The app list is not dismissed before switching user, suggestion chips will
// not be shown. So reset app list state and trigger an initial search here to
// update the suggestion results.
presenter_.GetView()->CloseOpenedPage();
presenter_.GetView()->search_box_view()->ClearSearch();
}
void AppListControllerImpl::OnAppListItemWillBeDeleted(
app_list::AppListItem* item) {
if (!client_)
return;
if (item->is_folder())
client_->OnFolderDeleted(profile_id_, item->CloneMetadata());
if (item->is_page_break())
client_->OnPageBreakItemDeleted(profile_id_, item->id());
}
void AppListControllerImpl::OnAppListItemUpdated(app_list::AppListItem* item) {
if (client_)
client_->OnItemUpdated(profile_id_, item->CloneMetadata());
}
void AppListControllerImpl::OnAppListStateChanged(ash::AppListState new_state,
ash::AppListState old_state) {
if (!app_list_features::IsEmbeddedAssistantUIEnabled())
return;
UpdateLauncherContainer();
if (new_state == ash::AppListState::kStateEmbeddedAssistant) {
// ShowUi will be no-op if the AssistantUiModel is already visible.
Shell::Get()->assistant_controller()->ui_controller()->ShowUi(
ash::AssistantEntryPoint::kUnspecified);
return;
}
if (old_state == ash::AppListState::kStateEmbeddedAssistant) {
// CloseUi will be no-op if the AssistantUiModel is already closed.
Shell::Get()->assistant_controller()->ui_controller()->CloseUi(
ash::AssistantExitPoint::kBackInLauncher);
}
}
////////////////////////////////////////////////////////////////////////////////
// Methods used in Ash
bool AppListControllerImpl::GetTargetVisibility() const {
return presenter_.GetTargetVisibility();
}
bool AppListControllerImpl::IsVisible() const {
return presenter_.IsVisible();
}
void AppListControllerImpl::Show(int64_t display_id,
app_list::AppListShowSource show_source,
base::TimeTicks event_time_stamp) {
UMA_HISTOGRAM_ENUMERATION(app_list::kAppListToggleMethodHistogram,
show_source);
presenter_.Show(display_id, event_time_stamp);
// AppListControllerImpl::Show is called in ash at the first time of showing
// app list view. So check whether the expand arrow view should be visible.
UpdateExpandArrowVisibility();
}
void AppListControllerImpl::UpdateYPositionAndOpacity(
int y_position_in_screen,
float background_opacity) {
// Avoid changing app list opacity and position when homecher is enabled.
if (IsHomeScreenAvailable())
return;
presenter_.UpdateYPositionAndOpacity(y_position_in_screen,
background_opacity);
}
void AppListControllerImpl::EndDragFromShelf(
ash::AppListViewState app_list_state) {
// Avoid dragging app list when homecher is enabled.
if (IsHomeScreenAvailable())
return;
presenter_.EndDragFromShelf(app_list_state);
}
void AppListControllerImpl::ProcessMouseWheelEvent(
const ui::MouseWheelEvent& event) {
presenter_.ProcessMouseWheelOffset(event.offset());
}
ash::ShelfAction AppListControllerImpl::ToggleAppList(
int64_t display_id,
app_list::AppListShowSource show_source,
base::TimeTicks event_time_stamp) {
ash::ShelfAction action =
presenter_.ToggleAppList(display_id, show_source, event_time_stamp);
UpdateExpandArrowVisibility();
if (action == SHELF_ACTION_APP_LIST_SHOWN) {
UMA_HISTOGRAM_ENUMERATION(app_list::kAppListToggleMethodHistogram,
show_source);
}
return action;
}
ash::AppListViewState AppListControllerImpl::GetAppListViewState() {
return model_->state_fullscreen();
}
void AppListControllerImpl::OnShellDestroying() {
// Stop observing at the beginning of ~Shell to avoid unnecessary work during
// Shell shutdown.
Shutdown();
}
void AppListControllerImpl::OnOverviewModeStarting() {
if (!IsHomeScreenAvailable())
DismissAppList();
}
void AppListControllerImpl::OnTabletModeStarted() {
presenter_.OnTabletModeChanged(true);
// Show the app list if the tablet mode starts.
Shell::Get()->home_screen_controller()->Show();
UpdateLauncherContainer();
}
void AppListControllerImpl::OnTabletModeEnded() {
aura::Window* window = presenter_.GetWindow();
base::AutoReset<bool> auto_reset(
&should_dismiss_immediately_,
window && RootWindowController::ForWindow(window)
->GetShelfLayoutManager()
->HasVisibleWindow());
presenter_.OnTabletModeChanged(false);
// Dismiss the app list if the tablet mode ends.
DismissAppList();
UpdateLauncherContainer();
}
void AppListControllerImpl::OnWallpaperColorsChanged() {
if (IsVisible())
presenter_.GetView()->OnWallpaperColorsChanged();
}
void AppListControllerImpl::OnKeyboardVisibilityChanged(const bool is_visible) {
onscreen_keyboard_shown_ = is_visible;
app_list::AppListView* app_list_view = presenter_.GetView();
if (app_list_view)
app_list_view->OnScreenKeyboardShown(is_visible);
}
void AppListControllerImpl::OnVoiceInteractionStatusChanged(
mojom::VoiceInteractionState state) {
UpdateAssistantVisibility();
}
void AppListControllerImpl::OnVoiceInteractionSettingsEnabled(bool enabled) {
UpdateAssistantVisibility();
}
void AppListControllerImpl::OnAssistantFeatureAllowedChanged(
mojom::AssistantAllowedState state) {
UpdateAssistantVisibility();
}
void AppListControllerImpl::OnDisplayConfigurationChanged() {
// Entering tablet mode triggers a display configuration change when we
// automatically switch to mirror mode. Switching to mirror mode happens
// asynchronously (see DisplayConfigurationObserver::OnTabletModeStarted()).
// This may result in the removal of a window tree host, as in the example of
// switching to tablet mode while Unified Desktop mode is on; the Unified host
// will be destroyed and the Home Launcher (which was created earlier when we
// entered tablet mode) will be dismissed.
// To avoid crashes, we must ensure that the Home Launcher shown status is as
// expected if it's enabled and we're still in tablet mode.
// https://crbug.com/900956.
const bool should_be_shown = IsTabletMode();
DCHECK_EQ(should_be_shown, IsHomeScreenAvailable());
if (should_be_shown == GetTargetVisibility())
return;
if (should_be_shown)
Shell::Get()->home_screen_controller()->Show();
}
void AppListControllerImpl::OnWindowUntracked(aura::Window* untracked_window) {
UpdateExpandArrowVisibility();
}
void AppListControllerImpl::OnAssistantReady() {
UpdateAssistantVisibility();
}
void AppListControllerImpl::OnUiVisibilityChanged(
AssistantVisibility new_visibility,
AssistantVisibility old_visibility,
base::Optional<AssistantEntryPoint> entry_point,
base::Optional<AssistantExitPoint> exit_point) {
switch (new_visibility) {
case AssistantVisibility::kVisible:
if (!IsVisible()) {
Show(GetDisplayIdToShowAppListOn(), app_list::kAssistantEntryPoint,
base::TimeTicks());
}
if (!IsShowingEmbeddedAssistantUI())
presenter_.ShowEmbeddedAssistantUI(true);
break;
case AssistantVisibility::kHidden:
NOTREACHED();
break;
case AssistantVisibility::kClosed:
if (!IsShowingEmbeddedAssistantUI())
break;
// When Launcher is closing, we do not want to call
// |ShowEmbeddedAssistantUI(false)|, which will show previous state page
// in Launcher and make the UI flash.
if (IsHomeScreenAvailable()) {
base::Optional<
app_list::ContentsView::ScopedSetActiveStateAnimationDisabler>
set_active_state_animation_disabler;
// When taking a screenshot by Assistant, we do not want to animate to
// the final state. Otherwise the screenshot may have tansient state
// during the animation. In tablet mode, we want to go back to
// kStateApps immediately, i.e. skipping the animation in
// |SetActiveStateInternal|, which are called from
// |ShowEmbeddedAssistantUI(false)| and
// |ClearSearchAndDeactivateSearchBox()|.
if (exit_point == AssistantExitPoint::kScreenshot) {
set_active_state_animation_disabler.emplace(
presenter_.GetView()->app_list_main_view()->contents_view());
}
presenter_.ShowEmbeddedAssistantUI(false);
if (exit_point != AssistantExitPoint::kBackInLauncher) {
presenter_.GetView()
->search_box_view()
->ClearSearchAndDeactivateSearchBox();
}
} else if (exit_point != AssistantExitPoint::kBackInLauncher) {
// Similarly, when taking a screenshot by Assistant in clamshell mode,
// we do not want to dismiss launcher with animation. Otherwise the
// screenshot may have tansient state during the animation.
base::AutoReset<bool> auto_reset(
&should_dismiss_immediately_,
exit_point == AssistantExitPoint::kScreenshot);
DismissAppList();
}
break;
}
}
void AppListControllerImpl::OnHomeLauncherAnimationComplete(
bool shown,
int64_t display_id) {
CloseAssistantUi(shown ? AssistantExitPoint::kLauncherOpen
: AssistantExitPoint::kLauncherClose);
}
void AppListControllerImpl::ShowHomeScreenView() {
DCHECK(IsTabletMode());
Show(GetDisplayIdToShowAppListOn(), app_list::kTabletMode, base::TimeTicks());
}
aura::Window* AppListControllerImpl::GetHomeScreenWindow() {
return presenter_.GetWindow();
}
void AppListControllerImpl::UpdateYPositionAndOpacityForHomeLauncher(
int y_position_in_screen,
float opacity,
UpdateAnimationSettingsCallback callback) {
presenter_.UpdateYPositionAndOpacityForHomeLauncher(
y_position_in_screen, opacity, std::move(callback));
}
void AppListControllerImpl::UpdateAfterHomeLauncherShown() {
// Show or hide the expand arrow view.
UpdateExpandArrowVisibility();
}
base::Optional<base::TimeDelta>
AppListControllerImpl::GetOptionalAnimationDuration() {
if (model_->state() == ash::AppListState::kStateEmbeddedAssistant) {
// If Assistant is shown, we don't want any delay in animation transitions
// since the launcher is already shown.
return base::TimeDelta::Min();
}
return base::nullopt;
}
bool AppListControllerImpl::ShouldShowShelfOnHomeScreen() const {
return true;
}
bool AppListControllerImpl::ShouldShowStatusAreaOnHomeScreen() const {
return true;
}
void AppListControllerImpl::Back() {
presenter_.GetView()->Back();
}
void AppListControllerImpl::SetKeyboardTraversalMode(bool engaged) {
if (keyboard_traversal_engaged_ == engaged)
return;
keyboard_traversal_engaged_ = engaged;
views::View* focused_view =
presenter_.GetView()->GetFocusManager()->GetFocusedView();
if (!focused_view)
return;
// When the search box has focus, it is actually the textfield that has focus.
// As such, the |SearchBoxView| must be told to repaint directly.
if (focused_view == presenter_.GetView()->search_box_view()->search_box())
presenter_.GetView()->search_box_view()->SchedulePaint();
else
focused_view->SchedulePaint();
}
ash::ShelfAction AppListControllerImpl::OnAppListButtonPressed(
int64_t display_id,
app_list::AppListShowSource show_source,
base::TimeTicks event_time_stamp) {
if (!IsHomeScreenAvailable())
return ToggleAppList(display_id, show_source, event_time_stamp);
bool handled = Shell::Get()->home_screen_controller()->GoHome(display_id);
// Perform the "back" action for the app list.
if (!handled)
Back();
return ash::SHELF_ACTION_APP_LIST_SHOWN;
}
bool AppListControllerImpl::IsShowingEmbeddedAssistantUI() const {
return presenter_.IsShowingEmbeddedAssistantUI();
}
void AppListControllerImpl::UpdateExpandArrowVisibility() {
bool should_show = false;
// Hide the expand arrow view when the home screen is available and there is
// no activatable window on the current active desk.
if (IsHomeScreenAvailable()) {
should_show = !ash::Shell::Get()
->mru_window_tracker()
->BuildWindowForCycleList(kActiveDesk)
.empty();
} else {
should_show = true;
}
presenter_.SetExpandArrowViewVisibility(should_show);
}
ash::AppListViewState AppListControllerImpl::CalculateStateAfterShelfDrag(
const ui::LocatedEvent& event_in_screen,
float launcher_above_shelf_bottom_amount) const {
if (presenter_.GetView())
return presenter_.GetView()->CalculateStateAfterShelfDrag(
event_in_screen, launcher_above_shelf_bottom_amount);
return ash::AppListViewState::kClosed;
}
void AppListControllerImpl::SetAppListModelForTest(
std::unique_ptr<app_list::AppListModel> model) {
model_->RemoveObserver(this);
model_ = std::move(model);
model_->AddObserver(this);
}
void AppListControllerImpl::SetStateTransitionAnimationCallback(
StateTransitionAnimationCallback callback) {
state_transition_animation_callback_ = std::move(callback);
}
void AppListControllerImpl::RecordShelfAppLaunched(
base::Optional<AppListViewState> recorded_app_list_view_state,
base::Optional<bool> recorded_home_launcher_shown) {
app_list::RecordAppListAppLaunched(
AppListLaunchedFrom::kLaunchedFromShelf,
recorded_app_list_view_state.value_or(GetAppListViewState()),
IsTabletMode(),
recorded_home_launcher_shown.value_or(presenter_.home_launcher_shown()));
}
////////////////////////////////////////////////////////////////////////////////
// Methods of |client_|:
void AppListControllerImpl::StartAssistant() {
if (app_list_features::IsEmbeddedAssistantUIEnabled()) {
ash::Shell::Get()->assistant_controller()->ui_controller()->ShowUi(
ash::AssistantEntryPoint::kLauncherSearchBoxMic);
return;
}
if (!IsHomeScreenAvailable())
DismissAppList();
ash::Shell::Get()->assistant_controller()->ui_controller()->ShowUi(
ash::AssistantEntryPoint::kLauncherSearchBox);
}
void AppListControllerImpl::StartSearch(const base::string16& raw_query) {
if (client_) {
base::string16 query;
base::TrimWhitespace(raw_query, base::TRIM_ALL, &query);
client_->StartSearch(query);
}
}
void AppListControllerImpl::OpenSearchResult(const std::string& result_id,
int event_flags,
AppListLaunchedFrom launched_from,
AppListLaunchType launch_type,
int suggestion_index) {
app_list::SearchResult* result = search_model_.FindSearchResult(result_id);
if (!result)
return;
if (launch_type == AppListLaunchType::kAppSearchResult) {
switch (launched_from) {
case AppListLaunchedFrom::kLaunchedFromSearchBox:
case AppListLaunchedFrom::kLaunchedFromSuggestionChip:
RecordAppLaunched(launched_from);
break;
case AppListLaunchedFrom::kLaunchedFromGrid:
case AppListLaunchedFrom::kLaunchedFromShelf:
break;
}
}
UMA_HISTOGRAM_ENUMERATION(app_list::kSearchResultOpenDisplayTypeHistogram,
result->display_type(),
ash::SearchResultDisplayType::kLast);
// Suggestion chips are not represented to the user as search results, so do
// not record search result metrics for them.
if (launched_from != AppListLaunchedFrom::kLaunchedFromSuggestionChip) {
base::RecordAction(base::UserMetricsAction("AppList_OpenSearchResult"));
UMA_HISTOGRAM_COUNTS_100(app_list::kSearchQueryLength,
GetLastQueryLength());
if (IsTabletMode()) {
UMA_HISTOGRAM_COUNTS_100(app_list::kSearchQueryLengthInTablet,
GetLastQueryLength());
} else {
UMA_HISTOGRAM_COUNTS_100(app_list::kSearchQueryLengthInClamshell,
GetLastQueryLength());
}
if (result->distance_from_origin() >= 0) {
UMA_HISTOGRAM_COUNTS_100(app_list::kSearchResultDistanceFromOrigin,
result->distance_from_origin());
}
}
if (presenter_.IsVisible() && result->is_omnibox_search() &&
IsAssistantAllowedAndEnabled() &&
app_list_features::IsEmbeddedAssistantUIEnabled()) {
// Record the assistant result. Other types of results are recorded in
// |client_| where there is richer data on SearchResultType.
DCHECK_EQ(AppListLaunchedFrom::kLaunchedFromSearchBox, launched_from)
<< "Only log search results which are represented to the user as "
"search results (ie. search results in the search result page) not "
"chips.";
app_list::RecordSearchResultOpenTypeHistogram(
launched_from, app_list::ASSISTANT_OMNIBOX_RESULT, IsTabletMode());
if (!GetLastQueryLength()) {
app_list::RecordZeroStateSuggestionOpenTypeHistogram(
app_list::ASSISTANT_OMNIBOX_RESULT);
}
Shell::Get()->assistant_controller()->ui_controller()->ShowUi(
AssistantEntryPoint::kLauncherSearchResult);
Shell::Get()->assistant_controller()->OpenUrl(
ash::assistant::util::CreateAssistantQueryDeepLink(
base::UTF16ToUTF8(result->title())));
} else {
if (client_)
client_->OpenSearchResult(result_id, event_flags, launched_from,
launch_type, suggestion_index);
}
ResetHomeLauncherIfShown();
}
void AppListControllerImpl::LogResultLaunchHistogram(
app_list::SearchResultLaunchLocation launch_location,
int suggestion_index) {
app_list::RecordSearchLaunchIndexAndQueryLength(
launch_location, GetLastQueryLength(), suggestion_index);
}
void AppListControllerImpl::LogSearchAbandonHistogram() {
app_list::RecordSearchAbandonWithQueryLengthHistogram(GetLastQueryLength());
}
void AppListControllerImpl::InvokeSearchResultAction(
const std::string& result_id,
int action_index,
int event_flags) {
if (client_)
client_->InvokeSearchResultAction(result_id, action_index, event_flags);
}
void AppListControllerImpl::GetSearchResultContextMenuModel(
const std::string& result_id,
GetContextMenuModelCallback callback) {
if (client_)
client_->GetSearchResultContextMenuModel(result_id, std::move(callback));
}
void AppListControllerImpl::ViewShown(int64_t display_id) {
if (app_list_features::IsEmbeddedAssistantUIEnabled() &&
GetAssistantViewDelegate()->GetUiModel()->ui_mode() !=
ash::AssistantUiMode::kLauncherEmbeddedUi) {
CloseAssistantUi(AssistantExitPoint::kLauncherOpen);
}
UpdateAssistantVisibility();
if (client_)
client_->ViewShown(display_id);
// Ensure search box starts fresh with no ring each time it opens.
keyboard_traversal_engaged_ = false;
}
void AppListControllerImpl::ViewClosing() {
if (presenter_.GetView()->search_box_view()->is_search_box_active()) {
// Close the virtual keyboard before the app list view is dismissed.
// Otherwise if the browser is behind the app list view, after the latter is
// closed, IME is updated because of the changed focus. Consequently,
// the virtual keyboard is hidden for the wrong IME instance, which may
// bring troubles when restoring the virtual keyboard (see
// https://crbug.com/944233).
keyboard::KeyboardController::Get()->HideKeyboardExplicitlyBySystem();
}
CloseAssistantUi(AssistantExitPoint::kLauncherClose);
if (client_)
client_->ViewClosing();
}
void AppListControllerImpl::ViewClosed() {
// Clear results to prevent initializing the next app list view with outdated
// results.
if (client_)
client_->StartSearch(base::string16());
}
const std::vector<SkColor>&
AppListControllerImpl::GetWallpaperProminentColors() {
return Shell::Get()->wallpaper_controller()->GetWallpaperColors();
}
void AppListControllerImpl::ActivateItem(const std::string& id,
int event_flags,
AppListLaunchedFrom launched_from) {
RecordAppLaunched(launched_from);
if (client_)
client_->ActivateItem(profile_id_, id, event_flags);
ResetHomeLauncherIfShown();
}
void AppListControllerImpl::GetContextMenuModel(
const std::string& id,
GetContextMenuModelCallback callback) {
if (client_)
client_->GetContextMenuModel(profile_id_, id, std::move(callback));
}
ui::ImplicitAnimationObserver* AppListControllerImpl::GetAnimationObserver(
ash::AppListViewState target_state) {
// |presenter_| observes the close animation only.
if (target_state == ash::AppListViewState::kClosed)
return &presenter_;
return nullptr;
}
void AppListControllerImpl::ShowWallpaperContextMenu(
const gfx::Point& onscreen_location,
ui::MenuSourceType source_type) {
Shell::Get()->ShowContextMenu(onscreen_location, source_type);
}
bool AppListControllerImpl::ProcessHomeLauncherGesture(
ui::GestureEvent* event,
const gfx::Point& screen_location) {
HomeLauncherGestureHandler* home_launcher_gesture_handler =
Shell::Get()->home_screen_controller()->home_launcher_gesture_handler();
switch (event->type()) {
case ui::ET_SCROLL_FLING_START:
case ui::ET_GESTURE_SCROLL_BEGIN:
return home_launcher_gesture_handler->OnPressEvent(
HomeLauncherGestureHandler::Mode::kSlideDownToHide, screen_location);
case ui::ET_GESTURE_SCROLL_UPDATE:
return home_launcher_gesture_handler->OnScrollEvent(
screen_location, event->details().scroll_y());
case ui::ET_GESTURE_END:
return home_launcher_gesture_handler->OnReleaseEvent(screen_location);
default:
break;
}
NOTREACHED();
return false;
}
bool AppListControllerImpl::KeyboardTraversalEngaged() {
return keyboard_traversal_engaged_;
}
bool AppListControllerImpl::CanProcessEventsOnApplistViews() {
// Do not allow processing events during overview or while overview is
// finished but still animating out. Note in clamshell mode, if overview and
// splitview is both active, we still allow the user to open app list and
// select an app. The app will be opened in snapped window state and overview
// will be ended after the app is opened.
OverviewController* overview_controller = Shell::Get()->overview_controller();
auto* split_view_controller = Shell::Get()->split_view_controller();
if ((overview_controller->InOverviewSession() &&
!split_view_controller->InClamshellSplitViewMode()) ||
overview_controller->IsCompletingShutdownAnimations()) {
return false;
}
HomeScreenController* home_screen_controller =
Shell::Get()->home_screen_controller();
return home_screen_controller &&
home_screen_controller->home_launcher_gesture_handler()->mode() !=
HomeLauncherGestureHandler::Mode::kSlideUpToShow;
}
bool AppListControllerImpl::ShouldDismissImmediately() {
if (should_dismiss_immediately_)
return true;
DCHECK(Shell::HasInstance());
const int ideal_shelf_y =
Shelf::ForWindow(presenter_.GetView()->GetWidget()->GetNativeView())
->GetIdealBounds()
.y();
const int current_y = presenter_.GetView()->GetBoundsInScreen().y();
return current_y > ideal_shelf_y;
}
void AppListControllerImpl::GetNavigableContentsFactory(
mojo::PendingReceiver<content::mojom::NavigableContentsFactory> receiver) {
if (client_)
client_->GetNavigableContentsFactory(std::move(receiver));
}
int AppListControllerImpl::GetTargetYForAppListHide(aura::Window* root_window) {
DCHECK(Shell::HasInstance());
return Shelf::ForWindow(root_window)->GetIdealBounds().y();
}
ash::AssistantViewDelegate* AppListControllerImpl::GetAssistantViewDelegate() {
return Shell::Get()->assistant_controller()->view_delegate();
}
void AppListControllerImpl::OnSearchResultVisibilityChanged(
const std::string& id,
bool visibility) {
if (client_)
client_->OnSearchResultVisibilityChanged(id, visibility);
}
bool AppListControllerImpl::IsAssistantAllowedAndEnabled() const {
if (!chromeos::switches::IsAssistantEnabled())
return false;
if (!Shell::Get()->assistant_controller()->IsAssistantReady())
return false;
auto* controller = VoiceInteractionController::Get();
return controller->settings_enabled().value_or(false) &&
controller->allowed_state() == mojom::AssistantAllowedState::ALLOWED &&
controller->voice_interaction_state().value_or(
mojom::VoiceInteractionState::NOT_READY) !=
mojom::VoiceInteractionState::NOT_READY;
}
bool AppListControllerImpl::ShouldShowAssistantPrivacyInfo() const {
if (!IsAssistantAllowedAndEnabled())
return false;
if (!app_list_features::IsEmbeddedAssistantUIEnabled())
return false;
const bool dismissed = IsAssistantPrivacyInfoDismissed();
if (dismissed)
return false;
const int count = GetAssistantPrivacyInfoShownCount();
constexpr int kThresholdToShow = 6;
return count >= 0 && count <= kThresholdToShow;
}
void AppListControllerImpl::MaybeIncreaseAssistantPrivacyInfoShownCount() {
const bool should_show = ShouldShowAssistantPrivacyInfo();
if (should_show) {
const int count = GetAssistantPrivacyInfoShownCount();
SetAssistantPrivacyInfoShownCount(count + 1);
}
}
void AppListControllerImpl::MarkAssistantPrivacyInfoDismissed() {
// User dismissed the privacy info view. Will not show the view again.
SetAssistantPrivacyInfoDismissed();
}
void AppListControllerImpl::OnStateTransitionAnimationCompleted(
ash::AppListViewState state) {
if (!state_transition_animation_callback_.is_null())
state_transition_animation_callback_.Run(state);
}
void AppListControllerImpl::GetAppLaunchedMetricParams(
app_list::AppLaunchedMetricParams* metric_params) {
metric_params->app_list_view_state = GetAppListViewState();
metric_params->is_tablet_mode = IsTabletMode();
metric_params->home_launcher_shown = presenter_.home_launcher_shown();
}
void AppListControllerImpl::RecordAppLaunched(
AppListLaunchedFrom launched_from) {
app_list::RecordAppListAppLaunched(launched_from, GetAppListViewState(),
IsTabletMode(),
presenter_.home_launcher_shown());
}
void AppListControllerImpl::AddObserver(AppListControllerObserver* observer) {
observers_.AddObserver(observer);
}
void AppListControllerImpl::RemoveObserver(
AppListControllerObserver* observer) {
observers_.RemoveObserver(observer);
}
void AppListControllerImpl::NotifyAppListVisibilityChanged(bool visible,
int64_t display_id) {
// Notify chrome of visibility changes.
if (client_)
client_->OnAppListVisibilityChanged(visible);
for (auto& observer : observers_)
observer.OnAppListVisibilityChanged(visible, display_id);
}
void AppListControllerImpl::NotifyAppListTargetVisibilityChanged(bool visible) {
// Notify chrome of target visibility changes.
if (client_)
client_->OnAppListTargetVisibilityChanged(visible);
}
////////////////////////////////////////////////////////////////////////////////
// Private used only:
void AppListControllerImpl::OnHomeLauncherDragStart() {
app_list::AppListView* app_list_view = presenter_.GetView();
DCHECK(app_list_view);
app_list_view->OnHomeLauncherDragStart();
}
void AppListControllerImpl::OnHomeLauncherDragInProgress() {
app_list::AppListView* app_list_view = presenter_.GetView();
DCHECK(app_list_view);
app_list_view->OnHomeLauncherDragInProgress();
}
void AppListControllerImpl::OnHomeLauncherDragEnd() {
app_list::AppListView* app_list_view = presenter_.GetView();
DCHECK(app_list_view);
app_list_view->OnHomeLauncherDragEnd();
}
syncer::StringOrdinal AppListControllerImpl::GetOemFolderPos() {
// Place the OEM folder just after the web store, which should always be
// followed by a pre-installed app (e.g. Search), so the poosition should be
// stable. TODO(stevenjb): consider explicitly setting the OEM folder location
// along with the name in ServicesCustomizationDocument::SetOemFolderName().
app_list::AppListItemList* item_list = model_->top_level_item_list();
if (!item_list->item_count()) {
LOG(ERROR) << "No top level item was found. "
<< "Placing OEM folder at the beginning.";
return syncer::StringOrdinal::CreateInitialOrdinal();
}
size_t web_store_app_index;
if (!item_list->FindItemIndex(extensions::kWebStoreAppId,
&web_store_app_index)) {
LOG(ERROR) << "Web store position is not found it top items. "
<< "Placing OEM folder at the end.";
return item_list->item_at(item_list->item_count() - 1)
->position()
.CreateAfter();
}
// Skip items with the same position.
const app_list::AppListItem* web_store_app_item =
item_list->item_at(web_store_app_index);
for (size_t j = web_store_app_index + 1; j < item_list->item_count(); ++j) {
const app_list::AppListItem* next_item = item_list->item_at(j);
DCHECK(next_item->position().IsValid());
if (!next_item->position().Equals(web_store_app_item->position())) {
const syncer::StringOrdinal oem_ordinal =
web_store_app_item->position().CreateBetween(next_item->position());
VLOG(1) << "Placing OEM Folder at: " << j
<< " position: " << oem_ordinal.ToDebugString();
return oem_ordinal;
}
}
const syncer::StringOrdinal oem_ordinal =
web_store_app_item->position().CreateAfter();
VLOG(1) << "Placing OEM Folder at: " << item_list->item_count()
<< " position: " << oem_ordinal.ToDebugString();
return oem_ordinal;
}
std::unique_ptr<app_list::AppListItem> AppListControllerImpl::CreateAppListItem(
std::unique_ptr<ash::AppListItemMetadata> metadata) {
std::unique_ptr<app_list::AppListItem> app_list_item =
metadata->is_folder
? std::make_unique<app_list::AppListFolderItem>(metadata->id)
: std::make_unique<app_list::AppListItem>(metadata->id);
app_list_item->SetMetadata(std::move(metadata));
return app_list_item;
}
app_list::AppListFolderItem* AppListControllerImpl::FindFolderItem(
const std::string& folder_id) {
return model_->FindFolderItem(folder_id);
}
void AppListControllerImpl::UpdateAssistantVisibility() {
GetSearchModel()->search_box()->SetShowAssistantButton(
IsAssistantAllowedAndEnabled());
}
int64_t AppListControllerImpl::GetDisplayIdToShowAppListOn() {
if (IsHomeScreenAvailable() &&
!Shell::Get()->display_manager()->IsInUnifiedMode()) {
return display::Display::HasInternalDisplay()
? display::Display::InternalDisplayId()
: display::Screen::GetScreen()->GetPrimaryDisplay().id();
}
return display::Screen::GetScreen()
->GetDisplayNearestWindow(ash::Shell::GetRootWindowForNewWindows())
.id();
}
void AppListControllerImpl::ResetHomeLauncherIfShown() {
if (!IsHomeScreenAvailable() || !presenter_.IsVisible())
return;
auto* const keyboard_controller = keyboard::KeyboardController::Get();
if (keyboard_controller->IsKeyboardVisible())
keyboard_controller->HideKeyboardByUser();
presenter_.GetView()->CloseOpenedPage();
// Refresh the suggestion chips with empty query.
StartSearch(base::string16());
}
void AppListControllerImpl::UpdateLauncherContainer(
base::Optional<int64_t> display_id) {
aura::Window* window = presenter_.GetWindow();
if (!window)
return;
aura::Window* parent_window = GetContainerForDisplayId(display_id);
if (parent_window && !parent_window->Contains(window)) {
parent_window->AddChild(window);
bool is_showing_app_window = false;
for (auto* app_window :
Shell::Get()->mru_window_tracker()->BuildWindowForCycleList(
kActiveDesk)) {
if (!parent_window->Contains(app_window) &&
!wm::GetWindowState(app_window)->IsMinimized()) {
is_showing_app_window = true;
break;
}
}
if (ShouldLauncherShowBehindApps() && is_showing_app_window) {
// When move launcher back to behind apps, and there is app window
// showing, we release focus.
Shell::Get()->activation_client()->DeactivateWindow(window);
}
}
}
int AppListControllerImpl::GetContainerId() const {
return ShouldLauncherShowBehindApps()
? ash::kShellWindowId_HomeScreenContainer
: ash::kShellWindowId_AppListContainer;
}
aura::Window* AppListControllerImpl::GetContainerForDisplayId(
base::Optional<int64_t> display_id) {
aura::Window* root_window =
display_id.has_value()
? Shell::GetRootWindowForDisplayId(display_id.value())
: presenter_.GetWindow()->GetRootWindow();
return root_window->GetChildById(GetContainerId());
}
bool AppListControllerImpl::ShouldLauncherShowBehindApps() const {
return IsHomeScreenAvailable() &&
model_->state() != ash::AppListState::kStateEmbeddedAssistant;
}
int AppListControllerImpl::GetLastQueryLength() {
return search_model_.search_box()->text().length();
}
void AppListControllerImpl::Shutdown() {
DCHECK(!is_shutdown_);
is_shutdown_ = true;
Shell* shell = Shell::Get();
shell->home_screen_controller()
->home_launcher_gesture_handler()
->RemoveObserver(this);
if (app_list_features::IsEmbeddedAssistantUIEnabled()) {
shell->assistant_controller()->RemoveObserver(this);
shell->assistant_controller()->ui_controller()->RemoveModelObserver(this);
}
shell->mru_window_tracker()->RemoveObserver(this);
shell->window_tree_host_manager()->RemoveObserver(this);
VoiceInteractionController::Get()->RemoveLocalObserver(this);
keyboard::KeyboardController::Get()->RemoveObserver(this);
shell->overview_controller()->RemoveObserver(this);
shell->RemoveShellObserver(this);
shell->wallpaper_controller()->RemoveObserver(this);
shell->tablet_mode_controller()->RemoveObserver(this);
shell->session_controller()->RemoveObserver(this);
model_->RemoveObserver(this);
}
void AppListControllerImpl::NotifyHomeLauncherAnimationTransition(
AnimationTrigger trigger,
bool launcher_will_show) {
// The AppListView may not exist if this is happening after tablet mode
// has started, but before the view is created.
if (!presenter_.GetView())
return;
presenter_.GetView()->OnTabletModeAnimationTransitionNotified(
CalculateAnimationTransitionForMetrics(trigger, launcher_will_show));
}
} // namespace ash