1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "ash/app_list/app_list_controller_impl.h"
6 
7 #include <utility>
8 #include <vector>
9 
10 #include "ash/app_list/app_list_metrics.h"
11 #include "ash/app_list/app_list_presenter_delegate_impl.h"
12 #include "ash/app_list/model/app_list_folder_item.h"
13 #include "ash/app_list/model/app_list_item.h"
14 #include "ash/app_list/views/app_list_main_view.h"
15 #include "ash/app_list/views/app_list_view.h"
16 #include "ash/app_list/views/apps_container_view.h"
17 #include "ash/app_list/views/apps_grid_view.h"
18 #include "ash/app_list/views/contents_view.h"
19 #include "ash/app_list/views/search_box_view.h"
20 #include "ash/assistant/assistant_controller_impl.h"
21 #include "ash/assistant/ui/assistant_ui_constants.h"
22 #include "ash/assistant/ui/assistant_view_delegate.h"
23 #include "ash/assistant/util/assistant_util.h"
24 #include "ash/assistant/util/deep_link_util.h"
25 #include "ash/home_screen/home_launcher_gesture_handler.h"
26 #include "ash/home_screen/home_screen_controller.h"
27 #include "ash/keyboard/ui/keyboard_ui_controller.h"
28 #include "ash/public/cpp/app_list/app_list_client.h"
29 #include "ash/public/cpp/app_list/app_list_config.h"
30 #include "ash/public/cpp/app_list/app_list_controller_observer.h"
31 #include "ash/public/cpp/app_list/app_list_features.h"
32 #include "ash/public/cpp/app_list/app_list_metrics.h"
33 #include "ash/public/cpp/app_list/app_list_notifier.h"
34 #include "ash/public/cpp/app_list/app_list_types.h"
35 #include "ash/public/cpp/ash_features.h"
36 #include "ash/public/cpp/ash_pref_names.h"
37 #include "ash/public/cpp/assistant/controller/assistant_controller.h"
38 #include "ash/public/cpp/assistant/controller/assistant_ui_controller.h"
39 #include "ash/public/cpp/shelf_config.h"
40 #include "ash/public/cpp/shelf_types.h"
41 #include "ash/public/cpp/shell_window_ids.h"
42 #include "ash/public/cpp/window_properties.h"
43 #include "ash/root_window_controller.h"
44 #include "ash/screen_util.h"
45 #include "ash/session/session_controller_impl.h"
46 #include "ash/shell.h"
47 #include "ash/wallpaper/wallpaper_controller_impl.h"
48 #include "ash/wm/mru_window_tracker.h"
49 #include "ash/wm/overview/overview_controller.h"
50 #include "ash/wm/splitview/split_view_controller.h"
51 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
52 #include "ash/wm/window_state.h"
53 #include "base/callback_helpers.h"
54 #include "base/logging.h"
55 #include "base/metrics/histogram_macros.h"
56 #include "base/metrics/user_metrics.h"
57 #include "base/strings/utf_string_conversions.h"
58 #include "chromeos/constants/chromeos_features.h"
59 #include "chromeos/constants/chromeos_pref_names.h"
60 #include "components/pref_registry/pref_registry_syncable.h"
61 #include "components/prefs/pref_change_registrar.h"
62 #include "components/prefs/pref_registry_simple.h"
63 #include "components/prefs/pref_service.h"
64 #include "components/services/app_service/public/cpp/app_registry_cache_wrapper.h"
65 #include "extensions/common/constants.h"
66 #include "ui/base/ui_base_features.h"
67 #include "ui/display/manager/display_manager.h"
68 #include "ui/display/screen.h"
69 #include "ui/message_center/message_center.h"
70 #include "ui/views/controls/textfield/textfield.h"
71 #include "ui/wm/core/coordinate_conversion.h"
72 #include "ui/wm/public/activation_client.h"
73 
74 namespace ash {
75 
76 using chromeos::assistant::AssistantEntryPoint;
77 using chromeos::assistant::AssistantExitPoint;
78 
79 namespace {
80 
IsTabletMode()81 bool IsTabletMode() {
82   return Shell::Get()->tablet_mode_controller()->InTabletMode();
83 }
84 
CalculateAnimationTransitionForMetrics(HomeScreenDelegate::AnimationTrigger trigger,bool launcher_should_show)85 TabletModeAnimationTransition CalculateAnimationTransitionForMetrics(
86     HomeScreenDelegate::AnimationTrigger trigger,
87     bool launcher_should_show) {
88   switch (trigger) {
89     case HomeScreenDelegate::AnimationTrigger::kHideForWindow:
90       return TabletModeAnimationTransition::kHideHomeLauncherForWindow;
91     case HomeScreenDelegate::AnimationTrigger::kLauncherButton:
92       return TabletModeAnimationTransition::kHomeButtonShow;
93     case HomeScreenDelegate::AnimationTrigger::kDragRelease:
94       return launcher_should_show
95                  ? TabletModeAnimationTransition::kDragReleaseShow
96                  : TabletModeAnimationTransition::kDragReleaseHide;
97     case HomeScreenDelegate::AnimationTrigger::kOverviewModeFade:
98       return launcher_should_show
99                  ? TabletModeAnimationTransition::kFadeOutOverview
100                  : TabletModeAnimationTransition::kFadeInOverview;
101   }
102 }
103 
GetAssistantPrivacyInfoShownCount()104 int GetAssistantPrivacyInfoShownCount() {
105   PrefService* prefs =
106       Shell::Get()->session_controller()->GetLastActiveUserPrefService();
107   return prefs->GetInteger(prefs::kAssistantPrivacyInfoShownInLauncher);
108 }
109 
SetAssistantPrivacyInfoShownCount(int count)110 void SetAssistantPrivacyInfoShownCount(int count) {
111   PrefService* prefs =
112       Shell::Get()->session_controller()->GetLastActiveUserPrefService();
113   prefs->SetInteger(prefs::kAssistantPrivacyInfoShownInLauncher, count);
114 }
115 
SetAssistantPrivacyInfoDismissed()116 void SetAssistantPrivacyInfoDismissed() {
117   PrefService* prefs =
118       Shell::Get()->session_controller()->GetLastActiveUserPrefService();
119   prefs->SetBoolean(prefs::kAssistantPrivacyInfoDismissedInLauncher, true);
120 }
121 
GetSuggestedContentInfoShownCount()122 int GetSuggestedContentInfoShownCount() {
123   PrefService* prefs =
124       Shell::Get()->session_controller()->GetLastActiveUserPrefService();
125   return prefs->GetInteger(prefs::kSuggestedContentInfoShownInLauncher);
126 }
127 
SetSuggestedContentInfoShownCount(int count)128 void SetSuggestedContentInfoShownCount(int count) {
129   PrefService* prefs =
130       Shell::Get()->session_controller()->GetLastActiveUserPrefService();
131   prefs->SetInteger(prefs::kSuggestedContentInfoShownInLauncher, count);
132 }
133 
IsSuggestedContentInfoDismissed()134 bool IsSuggestedContentInfoDismissed() {
135   PrefService* prefs =
136       Shell::Get()->session_controller()->GetLastActiveUserPrefService();
137   return prefs->GetBoolean(prefs::kSuggestedContentInfoDismissedInLauncher);
138 }
139 
SetSuggestedContentInfoDismissed()140 void SetSuggestedContentInfoDismissed() {
141   PrefService* prefs =
142       Shell::Get()->session_controller()->GetLastActiveUserPrefService();
143   prefs->SetBoolean(prefs::kSuggestedContentInfoDismissedInLauncher, true);
144 }
145 
IsSuggestedContentEnabled()146 bool IsSuggestedContentEnabled() {
147   PrefService* prefs =
148       Shell::Get()->session_controller()->GetLastActiveUserPrefService();
149   return prefs->GetBoolean(chromeos::prefs::kSuggestedContentEnabled);
150 }
151 
152 // Gets the MRU window shown over the applist when in tablet mode.
153 // Returns nullptr if no windows are shown over the applist.
GetTopVisibleWindow()154 aura::Window* GetTopVisibleWindow() {
155   std::vector<aura::Window*> window_list =
156       Shell::Get()->mru_window_tracker()->BuildWindowListIgnoreModal(
157           DesksMruType::kActiveDesk);
158   for (auto* window : window_list) {
159     if (window->TargetVisibility() && !WindowState::Get(window)->IsMinimized())
160       return window;
161   }
162   return nullptr;
163 }
164 
LogAppListShowSource(AppListShowSource show_source)165 void LogAppListShowSource(AppListShowSource show_source) {
166   UMA_HISTOGRAM_ENUMERATION(kAppListToggleMethodHistogram, show_source);
167 }
168 
169 base::Optional<TabletModeAnimationTransition>
GetTransitionFromMetricsAnimationInfo(base::Optional<HomeScreenDelegate::AnimationInfo> animation_info)170 GetTransitionFromMetricsAnimationInfo(
171     base::Optional<HomeScreenDelegate::AnimationInfo> animation_info) {
172   if (!animation_info.has_value())
173     return base::nullopt;
174 
175   return CalculateAnimationTransitionForMetrics(animation_info->trigger,
176                                                 animation_info->showing);
177 }
178 
179 }  // namespace
180 
AppListControllerImpl()181 AppListControllerImpl::AppListControllerImpl()
182     : model_(std::make_unique<AppListModel>()),
183       color_provider_(AppListColorProviderImpl()),
184       presenter_(std::make_unique<AppListPresenterDelegateImpl>(this)),
185       is_notification_indicator_enabled_(
186           ::features::IsNotificationIndicatorEnabled()) {
187   model_->AddObserver(this);
188   SessionControllerImpl* session_controller =
189       Shell::Get()->session_controller();
190   session_controller->AddObserver(this);
191 
192   // In case of crash-and-restart case where session state starts with ACTIVE
193   // and does not change to trigger OnSessionStateChanged(), notify the current
194   // session state here to ensure that the app list is shown.
195   OnSessionStateChanged(session_controller->GetSessionState());
196 
197   Shell* shell = Shell::Get();
198   shell->tablet_mode_controller()->AddObserver(this);
199   shell->wallpaper_controller()->AddObserver(this);
200   shell->AddShellObserver(this);
201   shell->overview_controller()->AddObserver(this);
202   keyboard::KeyboardUIController::Get()->AddObserver(this);
203   AssistantState::Get()->AddObserver(this);
204   shell->window_tree_host_manager()->AddObserver(this);
205   shell->mru_window_tracker()->AddObserver(this);
206   AssistantController::Get()->AddObserver(this);
207   AssistantUiController::Get()->GetModel()->AddObserver(this);
208   message_center::MessageCenter::Get()->AddObserver(this);
209 }
210 
~AppListControllerImpl()211 AppListControllerImpl::~AppListControllerImpl() {
212   if (tracked_app_window_) {
213     tracked_app_window_->RemoveObserver(this);
214     tracked_app_window_ = nullptr;
215   }
216 
217   // If this is being destroyed before the Shell starts shutting down, first
218   // remove this from objects it's observing.
219   if (!is_shutdown_)
220     Shutdown();
221 
222   if (client_)
223     client_->OnAppListControllerDestroyed();
224 }
225 
226 // static
RegisterProfilePrefs(PrefRegistrySimple * registry)227 void AppListControllerImpl::RegisterProfilePrefs(PrefRegistrySimple* registry) {
228   registry->RegisterIntegerPref(prefs::kAssistantPrivacyInfoShownInLauncher, 0);
229   registry->RegisterBooleanPref(
230       prefs::kAssistantPrivacyInfoDismissedInLauncher, false,
231       user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
232   registry->RegisterIntegerPref(
233       prefs::kSuggestedContentInfoShownInLauncher, 0,
234       user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
235   registry->RegisterBooleanPref(
236       prefs::kSuggestedContentInfoDismissedInLauncher, false,
237       user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
238 }
239 
SetClient(AppListClient * client)240 void AppListControllerImpl::SetClient(AppListClient* client) {
241   client_ = client;
242 }
243 
GetClient()244 AppListClient* AppListControllerImpl::GetClient() {
245   DCHECK(client_);
246   return client_;
247 }
248 
GetModel()249 AppListModel* AppListControllerImpl::GetModel() {
250   return model_.get();
251 }
252 
GetSearchModel()253 SearchModel* AppListControllerImpl::GetSearchModel() {
254   return &search_model_;
255 }
256 
GetNotifier()257 AppListNotifier* AppListControllerImpl::GetNotifier() {
258   if (!client_)
259     return nullptr;
260   return client_->GetNotifier();
261 }
262 
AddItem(std::unique_ptr<AppListItemMetadata> item_data)263 void AppListControllerImpl::AddItem(
264     std::unique_ptr<AppListItemMetadata> item_data) {
265   const std::string folder_id = item_data->folder_id;
266   if (folder_id.empty())
267     model_->AddItem(CreateAppListItem(std::move(item_data)));
268   else
269     AddItemToFolder(std::move(item_data), folder_id);
270 }
271 
AddItemToFolder(std::unique_ptr<AppListItemMetadata> item_data,const std::string & folder_id)272 void AppListControllerImpl::AddItemToFolder(
273     std::unique_ptr<AppListItemMetadata> item_data,
274     const std::string& folder_id) {
275   // When we're setting a whole model of a profile, each item may have its
276   // folder id set properly. However, |AppListModel::AddItemToFolder| requires
277   // the item to add is not in the target folder yet, and sets its folder id
278   // later. So we should clear the folder id here to avoid breaking checks.
279   item_data->folder_id.clear();
280   model_->AddItemToFolder(CreateAppListItem(std::move(item_data)), folder_id);
281 }
282 
RemoveItem(const std::string & id)283 void AppListControllerImpl::RemoveItem(const std::string& id) {
284   model_->DeleteItem(id);
285 }
286 
RemoveUninstalledItem(const std::string & id)287 void AppListControllerImpl::RemoveUninstalledItem(const std::string& id) {
288   model_->DeleteUninstalledItem(id);
289 }
290 
MoveItemToFolder(const std::string & id,const std::string & folder_id)291 void AppListControllerImpl::MoveItemToFolder(const std::string& id,
292                                              const std::string& folder_id) {
293   AppListItem* item = model_->FindItem(id);
294   model_->MoveItemToFolder(item, folder_id);
295 }
296 
SetStatus(AppListModelStatus status)297 void AppListControllerImpl::SetStatus(AppListModelStatus status) {
298   model_->SetStatus(status);
299 }
300 
SetSearchEngineIsGoogle(bool is_google)301 void AppListControllerImpl::SetSearchEngineIsGoogle(bool is_google) {
302   search_model_.SetSearchEngineIsGoogle(is_google);
303 }
304 
UpdateSearchBox(const base::string16 & text,bool initiated_by_user)305 void AppListControllerImpl::UpdateSearchBox(const base::string16& text,
306                                             bool initiated_by_user) {
307   search_model_.search_box()->Update(text, initiated_by_user);
308 }
309 
PublishSearchResults(std::vector<std::unique_ptr<SearchResultMetadata>> results)310 void AppListControllerImpl::PublishSearchResults(
311     std::vector<std::unique_ptr<SearchResultMetadata>> results) {
312   std::vector<std::unique_ptr<SearchResult>> new_results;
313   for (auto& result_metadata : results) {
314     std::unique_ptr<SearchResult> result = std::make_unique<SearchResult>();
315     result->SetMetadata(std::move(result_metadata));
316     new_results.push_back(std::move(result));
317   }
318   search_model_.PublishResults(std::move(new_results));
319 }
320 
SetItemMetadata(const std::string & id,std::unique_ptr<AppListItemMetadata> data)321 void AppListControllerImpl::SetItemMetadata(
322     const std::string& id,
323     std::unique_ptr<AppListItemMetadata> data) {
324   AppListItem* item = model_->FindItem(id);
325   if (!item)
326     return;
327 
328   // data may not contain valid position or icon. Preserve it in this case.
329   if (!data->position.IsValid())
330     data->position = item->position();
331 
332   // Update the item's position and name based on the metadata.
333   if (!data->position.Equals(item->position()))
334     model_->SetItemPosition(item, data->position);
335 
336   if (data->short_name.empty()) {
337     if (data->name != item->name()) {
338       model_->SetItemName(item, data->name);
339     }
340   } else {
341     if (data->name != item->name() || data->short_name != item->short_name()) {
342       model_->SetItemNameAndShortName(item, data->name, data->short_name);
343     }
344   }
345 
346   // Folder icon is generated on ash side and chrome side passes a null
347   // icon here. Skip it.
348   if (data->icon.isNull())
349     data->icon = item->GetIcon(AppListConfigType::kShared);
350 
351   item->SetMetadata(std::move(data));
352 }
353 
SetItemIcon(const std::string & id,const gfx::ImageSkia & icon)354 void AppListControllerImpl::SetItemIcon(const std::string& id,
355                                         const gfx::ImageSkia& icon) {
356   AppListItem* item = model_->FindItem(id);
357   if (item)
358     item->SetIcon(AppListConfigType::kShared, icon);
359 }
360 
SetModelData(int profile_id,std::vector<std::unique_ptr<AppListItemMetadata>> apps,bool is_search_engine_google)361 void AppListControllerImpl::SetModelData(
362     int profile_id,
363     std::vector<std::unique_ptr<AppListItemMetadata>> apps,
364     bool is_search_engine_google) {
365   // Clear old model data.
366   model_->DeleteAllItems();
367   search_model_.DeleteAllResults();
368 
369   profile_id_ = profile_id;
370 
371   // Populate new models. First populate folders and then other items to avoid
372   // automatically creating folder items in |AddItemToFolder|.
373   for (auto& app : apps) {
374     if (!app->is_folder)
375       continue;
376     DCHECK(app->folder_id.empty());
377     AddItem(std::move(app));
378   }
379   for (auto& app : apps) {
380     if (!app)
381       continue;
382     AddItem(std::move(app));
383   }
384   search_model_.SetSearchEngineIsGoogle(is_search_engine_google);
385 }
386 
SetSearchResultMetadata(std::unique_ptr<SearchResultMetadata> metadata)387 void AppListControllerImpl::SetSearchResultMetadata(
388     std::unique_ptr<SearchResultMetadata> metadata) {
389   SearchResult* result = search_model_.FindSearchResult(metadata->id);
390   if (result)
391     result->SetMetadata(std::move(metadata));
392 }
393 
GetIdToAppListIndexMap(GetIdToAppListIndexMapCallback callback)394 void AppListControllerImpl::GetIdToAppListIndexMap(
395     GetIdToAppListIndexMapCallback callback) {
396   base::flat_map<std::string, uint16_t> id_to_app_list_index;
397   for (size_t i = 0; i < model_->top_level_item_list()->item_count(); ++i)
398     id_to_app_list_index[model_->top_level_item_list()->item_at(i)->id()] = i;
399   std::move(callback).Run(id_to_app_list_index);
400 }
401 
FindOrCreateOemFolder(const std::string & oem_folder_name,const syncer::StringOrdinal & preferred_oem_position,FindOrCreateOemFolderCallback callback)402 void AppListControllerImpl::FindOrCreateOemFolder(
403     const std::string& oem_folder_name,
404     const syncer::StringOrdinal& preferred_oem_position,
405     FindOrCreateOemFolderCallback callback) {
406   AppListFolderItem* oem_folder = model_->FindFolderItem(kOemFolderId);
407   if (!oem_folder) {
408     std::unique_ptr<AppListFolderItem> new_folder =
409         std::make_unique<AppListFolderItem>(kOemFolderId);
410     syncer::StringOrdinal oem_position = preferred_oem_position.IsValid()
411                                              ? preferred_oem_position
412                                              : GetOemFolderPos();
413     // Do not create a sync item for the OEM folder here, do it in
414     // ResolveFolderPositions() when the item position is finalized.
415     oem_folder =
416         static_cast<AppListFolderItem*>(model_->AddItem(std::move(new_folder)));
417     model_->SetItemPosition(oem_folder, oem_position);
418   }
419   model_->SetItemName(oem_folder, oem_folder_name);
420   std::move(callback).Run();
421 }
422 
ResolveOemFolderPosition(const syncer::StringOrdinal & preferred_oem_position,ResolveOemFolderPositionCallback callback)423 void AppListControllerImpl::ResolveOemFolderPosition(
424     const syncer::StringOrdinal& preferred_oem_position,
425     ResolveOemFolderPositionCallback callback) {
426   // In ash:
427   AppListFolderItem* ash_oem_folder = FindFolderItem(kOemFolderId);
428   std::unique_ptr<AppListItemMetadata> metadata;
429   if (ash_oem_folder) {
430     const syncer::StringOrdinal& oem_folder_pos =
431         preferred_oem_position.IsValid() ? preferred_oem_position
432                                          : GetOemFolderPos();
433     model_->SetItemPosition(ash_oem_folder, oem_folder_pos);
434     metadata = ash_oem_folder->CloneMetadata();
435   }
436   std::move(callback).Run(std::move(metadata));
437 }
438 
NotifyProcessSyncChangesFinished()439 void AppListControllerImpl::NotifyProcessSyncChangesFinished() {
440   // When there are incompatible apps on different devices under the same
441   // user account, it is possible that moving or adding an app on an empty
442   // spot on a page of a different type of device (e.g. Device 1) may cause app
443   // overflow on another device (e.g. Device 2) since it may have more apps on
444   // the same page. See details in http://crbug.com/1098174.
445   // When the change is synced to the Device 2, paged view structure may load
446   // meta data and detect a full page of apps without a page break item
447   // at the end of the overflowed page. Therefore, after the sync service has
448   // finished processing sync change, SaveToMetaData should be called to insert
449   // page break items if there are any missing at the end of full pages.
450   AppListView* const app_list_view = presenter_.GetView();
451   if (app_list_view) {
452     app_list_view->app_list_main_view()
453         ->contents_view()
454         ->apps_container_view()
455         ->apps_grid_view()
456         ->UpdatePagedViewStructure();
457   }
458 }
459 
DismissAppList()460 void AppListControllerImpl::DismissAppList() {
461   if (tracked_app_window_) {
462     tracked_app_window_->RemoveObserver(this);
463     tracked_app_window_ = nullptr;
464   }
465 
466   presenter_.Dismiss(base::TimeTicks());
467 }
468 
GetAppInfoDialogBounds(GetAppInfoDialogBoundsCallback callback)469 void AppListControllerImpl::GetAppInfoDialogBounds(
470     GetAppInfoDialogBoundsCallback callback) {
471   AppListView* app_list_view = presenter_.GetView();
472   gfx::Rect bounds = gfx::Rect();
473   if (app_list_view)
474     bounds = app_list_view->GetAppInfoDialogBounds();
475   std::move(callback).Run(bounds);
476 }
477 
ShowAppList()478 void AppListControllerImpl::ShowAppList() {
479   presenter_.Show(GetDisplayIdToShowAppListOn(), base::TimeTicks());
480 }
481 
GetWindow()482 aura::Window* AppListControllerImpl::GetWindow() {
483   return presenter_.GetWindow();
484 }
485 
IsVisible(const base::Optional<int64_t> & display_id)486 bool AppListControllerImpl::IsVisible(
487     const base::Optional<int64_t>& display_id) {
488   return last_visible_ && (!display_id.has_value() ||
489                            display_id.value() == last_visible_display_id_);
490 }
491 
492 ////////////////////////////////////////////////////////////////////////////////
493 // AppListModelObserver:
494 
OnAppListItemAdded(AppListItem * item)495 void AppListControllerImpl::OnAppListItemAdded(AppListItem* item) {
496   client_->OnItemAdded(profile_id_, item->CloneMetadata());
497 
498   if (is_notification_indicator_enabled_ && cache_ &&
499       notification_badging_pref_enabled_.value_or(false)) {
500     // Update the notification badge indicator for the newly added app list
501     // item.
502     cache_->ForOneApp(item->id(), [item](const apps::AppUpdate& update) {
503       item->UpdateBadge(update.HasBadge() == apps::mojom::OptionalBool::kTrue);
504     });
505   }
506 }
507 
OnActiveUserPrefServiceChanged(PrefService * pref_service)508 void AppListControllerImpl::OnActiveUserPrefServiceChanged(
509     PrefService* pref_service) {
510   if (is_notification_indicator_enabled_) {
511     pref_change_registrar_ = std::make_unique<PrefChangeRegistrar>();
512     pref_change_registrar_->Init(pref_service);
513 
514     pref_change_registrar_->Add(
515         prefs::kAppNotificationBadgingEnabled,
516         base::BindRepeating(&AppListControllerImpl::UpdateAppBadging,
517                             base::Unretained(this)));
518 
519     // Observe AppRegistryCache for the current active account to get
520     // notification updates.
521     AccountId account_id =
522         Shell::Get()->session_controller()->GetActiveAccountId();
523     cache_ =
524         apps::AppRegistryCacheWrapper::Get().GetAppRegistryCache(account_id);
525     Observe(cache_);
526 
527     // Resetting the recorded pref forces the next call to UpdateAppBadging()
528     // to update notification badging for every app item.
529     notification_badging_pref_enabled_.reset();
530     UpdateAppBadging();
531   }
532 
533   if (!IsTabletMode()) {
534     DismissAppList();
535     return;
536   }
537 
538   // The app list is not dismissed before switching user, suggestion chips will
539   // not be shown. So reset app list state and trigger an initial search here to
540   // update the suggestion results.
541   if (presenter_.GetView()) {
542     presenter_.GetView()->CloseOpenedPage();
543     presenter_.GetView()->search_box_view()->ClearSearch();
544   }
545 }
546 
OnSessionStateChanged(session_manager::SessionState state)547 void AppListControllerImpl::OnSessionStateChanged(
548     session_manager::SessionState state) {
549   if (!IsTabletMode())
550     return;
551 
552   if (state != session_manager::SessionState::ACTIVE)
553     return;
554 
555   // Show the app list after signing in in tablet mode. For metrics, the app
556   // list is not considered shown since the browser window is shown over app
557   // list upon login.
558   if (!presenter_.GetTargetVisibility())
559     Shell::Get()->home_screen_controller()->Show();
560 
561   // Hide app list UI initially to prevent app list from flashing in background
562   // while the initial app window is being shown.
563   if (!last_target_visible_ && !ShouldHomeLauncherBeVisible())
564     presenter_.SetViewVisibility(false);
565   else
566     OnVisibilityChanged(true, last_visible_display_id_);
567 }
568 
OnAppListItemWillBeDeleted(AppListItem * item)569 void AppListControllerImpl::OnAppListItemWillBeDeleted(AppListItem* item) {
570   if (!client_)
571     return;
572 
573   if (item->is_folder())
574     client_->OnFolderDeleted(profile_id_, item->CloneMetadata());
575 
576   if (item->is_page_break())
577     client_->OnPageBreakItemDeleted(profile_id_, item->id());
578 }
579 
OnAppListItemUpdated(AppListItem * item)580 void AppListControllerImpl::OnAppListItemUpdated(AppListItem* item) {
581   if (client_)
582     client_->OnItemUpdated(profile_id_, item->CloneMetadata());
583 }
584 
OnAppListStateChanged(AppListState new_state,AppListState old_state)585 void AppListControllerImpl::OnAppListStateChanged(AppListState new_state,
586                                                   AppListState old_state) {
587   UpdateLauncherContainer();
588 
589   if (new_state == AppListState::kStateEmbeddedAssistant) {
590     // ShowUi() will be no-op if the Assistant UI is already visible.
591     AssistantUiController::Get()->ShowUi(AssistantEntryPoint::kUnspecified);
592     return;
593   }
594 
595   if (old_state == AppListState::kStateEmbeddedAssistant) {
596     // CloseUi() will be no-op if the Assistant UI is already closed.
597     AssistantUiController::Get()->CloseUi(AssistantExitPoint::kBackInLauncher);
598   }
599 }
600 
601 ////////////////////////////////////////////////////////////////////////////////
602 // Methods used in Ash
603 
GetTargetVisibility(const base::Optional<int64_t> & display_id) const604 bool AppListControllerImpl::GetTargetVisibility(
605     const base::Optional<int64_t>& display_id) const {
606   return last_target_visible_ &&
607          (!display_id.has_value() ||
608           display_id.value() == last_visible_display_id_);
609 }
610 
Show(int64_t display_id,base::Optional<AppListShowSource> show_source,base::TimeTicks event_time_stamp)611 void AppListControllerImpl::Show(int64_t display_id,
612                                  base::Optional<AppListShowSource> show_source,
613                                  base::TimeTicks event_time_stamp) {
614   if (show_source.has_value())
615     LogAppListShowSource(show_source.value());
616 
617   presenter_.Show(display_id, event_time_stamp);
618 
619   // AppListControllerImpl::Show is called in ash at the first time of showing
620   // app list view. So check whether the expand arrow view should be visible.
621   UpdateExpandArrowVisibility();
622 }
623 
UpdateYPositionAndOpacity(int y_position_in_screen,float background_opacity)624 void AppListControllerImpl::UpdateYPositionAndOpacity(
625     int y_position_in_screen,
626     float background_opacity) {
627   // Avoid changing app list opacity and position when homecher is enabled.
628   if (IsTabletMode())
629     return;
630   presenter_.UpdateYPositionAndOpacity(y_position_in_screen,
631                                        background_opacity);
632 }
633 
EndDragFromShelf(AppListViewState app_list_state)634 void AppListControllerImpl::EndDragFromShelf(AppListViewState app_list_state) {
635   // Avoid dragging app list when homecher is enabled.
636   if (IsTabletMode())
637     return;
638   presenter_.EndDragFromShelf(app_list_state);
639 }
640 
ProcessMouseWheelEvent(const ui::MouseWheelEvent & event)641 void AppListControllerImpl::ProcessMouseWheelEvent(
642     const ui::MouseWheelEvent& event) {
643   presenter_.ProcessMouseWheelOffset(event.offset());
644 }
645 
ToggleAppList(int64_t display_id,AppListShowSource show_source,base::TimeTicks event_time_stamp)646 ShelfAction AppListControllerImpl::ToggleAppList(
647     int64_t display_id,
648     AppListShowSource show_source,
649     base::TimeTicks event_time_stamp) {
650   if (IsTabletMode()) {
651     bool handled = Shell::Get()->home_screen_controller()->GoHome(display_id);
652 
653     // Perform the "back" action for the app list.
654     if (!handled) {
655       Back();
656       return SHELF_ACTION_APP_LIST_BACK;
657     }
658 
659     LogAppListShowSource(show_source);
660     return SHELF_ACTION_APP_LIST_SHOWN;
661   }
662 
663   base::AutoReset<bool> auto_reset(&should_dismiss_immediately_,
664                                    display_id != last_visible_display_id_);
665   ShelfAction action =
666       presenter_.ToggleAppList(display_id, show_source, event_time_stamp);
667   UpdateExpandArrowVisibility();
668   if (action == SHELF_ACTION_APP_LIST_SHOWN)
669     LogAppListShowSource(show_source);
670   return action;
671 }
672 
GetAppListViewState()673 AppListViewState AppListControllerImpl::GetAppListViewState() {
674   return model_->state_fullscreen();
675 }
676 
ShouldHomeLauncherBeVisible() const677 bool AppListControllerImpl::ShouldHomeLauncherBeVisible() const {
678   if (!IsTabletMode())
679     return false;
680 
681   if (home_launcher_transition_state_ ==
682       HomeLauncherTransitionState::kMostlyShown)
683     return true;
684 
685   return !Shell::Get()->overview_controller()->InOverviewSession() &&
686          !GetTopVisibleWindow();
687 }
688 
OnShelfAlignmentChanged(aura::Window * root_window,ShelfAlignment old_alignment)689 void AppListControllerImpl::OnShelfAlignmentChanged(
690     aura::Window* root_window,
691     ShelfAlignment old_alignment) {
692   if (!IsTabletMode())
693     DismissAppList();
694 }
695 
OnShellDestroying()696 void AppListControllerImpl::OnShellDestroying() {
697   // Stop observing at the beginning of ~Shell to avoid unnecessary work during
698   // Shell shutdown.
699   Shutdown();
700 }
701 
OnOverviewModeStarting()702 void AppListControllerImpl::OnOverviewModeStarting() {
703   if (IsTabletMode()) {
704     const int64_t display_id = last_visible_display_id_;
705     OnVisibilityWillChange(false /*shown*/, display_id);
706   } else {
707     DismissAppList();
708   }
709 }
710 
OnOverviewModeStartingAnimationComplete(bool canceled)711 void AppListControllerImpl::OnOverviewModeStartingAnimationComplete(
712     bool canceled) {
713   if (!IsTabletMode())
714     return;
715 
716   // If overview start was canceled, overview end animations are about to start.
717   // Preemptively update the target app list visibility.
718   if (canceled) {
719     OnVisibilityWillChange(!GetTopVisibleWindow(), last_visible_display_id_);
720     return;
721   }
722 
723   OnVisibilityChanged(false /* shown */, last_visible_display_id_);
724 }
725 
OnOverviewModeEnding(OverviewSession * session)726 void AppListControllerImpl::OnOverviewModeEnding(OverviewSession* session) {
727   if (!IsTabletMode())
728     return;
729 
730   // Overview state might end during home launcher transition - if that is the
731   // case, respect the final state set by in-progress home launcher transition.
732   if (home_launcher_transition_state_ != HomeLauncherTransitionState::kFinished)
733     return;
734 
735   OnVisibilityWillChange(!GetTopVisibleWindow() /*shown*/,
736                          last_visible_display_id_);
737 }
738 
OnOverviewModeEnded()739 void AppListControllerImpl::OnOverviewModeEnded() {
740   if (!IsTabletMode())
741     return;
742   // Overview state might end during home launcher transition - if that is the
743   // case, respect the final state set by in-progress home launcher transition.
744   if (home_launcher_transition_state_ != HomeLauncherTransitionState::kFinished)
745     return;
746   OnVisibilityChanged(!GetTopVisibleWindow(), last_visible_display_id_);
747 }
748 
OnTabletModeStarted()749 void AppListControllerImpl::OnTabletModeStarted() {
750   const AppListView* app_list_view = presenter_.GetView();
751   // In tablet mode shelf orientation is always "bottom". Dismiss app list if
752   // switching to tablet mode from side shelf app list, to ensure the app list
753   // is re-shown and laid out with correct "side shelf" value.
754   if (app_list_view && app_list_view->is_side_shelf())
755     DismissAppList();
756 
757   presenter_.OnTabletModeChanged(true);
758 
759   // Show the app list if the tablet mode starts.
760   if (Shell::Get()->session_controller()->GetSessionState() ==
761       session_manager::SessionState::ACTIVE) {
762     Shell::Get()->home_screen_controller()->Show();
763   }
764   UpdateLauncherContainer();
765 
766   // If the app list is visible before the transition to tablet mode,
767   // AppListPresenter relies on the active window change to detect the app list
768   // view got hidden behind a window. Though, app list UI moving behind an app
769   // window does not always cause an active window change:
770   // *   If the app list is still being shown - given that app list takes focus
771   //     from the top window only when it's fully shown, the focus will remain
772   //     within the app window throughout the tablet mode transition.
773   // *   If the assistant UI is visible before the tablet mode transition - the
774   //     assistant will keep the focus during transition, even though the app
775   //     window will be shown over the app list view.
776   // Ensure the app list visibility is properly updated if the app list is
777   // hidden behind a window at this point.
778   if (last_target_visible_ && !ShouldHomeLauncherBeVisible())
779     OnVisibilityChanged(false, last_visible_display_id_);
780 }
781 
OnTabletModeEnded()782 void AppListControllerImpl::OnTabletModeEnded() {
783   aura::Window* window = presenter_.GetWindow();
784   base::AutoReset<bool> auto_reset(
785       &should_dismiss_immediately_,
786       window && RootWindowController::ForWindow(window)
787                     ->GetShelfLayoutManager()
788                     ->HasVisibleWindow());
789   presenter_.OnTabletModeChanged(false);
790   UpdateLauncherContainer();
791 
792   // Dismiss the app list if the tablet mode ends.
793   DismissAppList();
794 }
795 
OnWallpaperColorsChanged()796 void AppListControllerImpl::OnWallpaperColorsChanged() {
797   if (IsVisible(last_visible_display_id_))
798     presenter_.GetView()->OnWallpaperColorsChanged();
799 }
800 
OnKeyboardVisibilityChanged(const bool is_visible)801 void AppListControllerImpl::OnKeyboardVisibilityChanged(const bool is_visible) {
802   onscreen_keyboard_shown_ = is_visible;
803   AppListView* app_list_view = presenter_.GetView();
804   if (app_list_view)
805     app_list_view->OnScreenKeyboardShown(is_visible);
806 }
807 
OnAssistantStatusChanged(chromeos::assistant::AssistantStatus status)808 void AppListControllerImpl::OnAssistantStatusChanged(
809     chromeos::assistant::AssistantStatus status) {
810   UpdateAssistantVisibility();
811 }
812 
OnAssistantSettingsEnabled(bool enabled)813 void AppListControllerImpl::OnAssistantSettingsEnabled(bool enabled) {
814   UpdateAssistantVisibility();
815 }
816 
OnAssistantFeatureAllowedChanged(chromeos::assistant::AssistantAllowedState state)817 void AppListControllerImpl::OnAssistantFeatureAllowedChanged(
818     chromeos::assistant::AssistantAllowedState state) {
819   UpdateAssistantVisibility();
820 }
821 
OnDisplayConfigurationChanged()822 void AppListControllerImpl::OnDisplayConfigurationChanged() {
823   // Entering tablet mode triggers a display configuration change when we
824   // automatically switch to mirror mode. Switching to mirror mode happens
825   // asynchronously (see DisplayConfigurationObserver::OnTabletModeStarted()).
826   // This may result in the removal of a window tree host, as in the example of
827   // switching to tablet mode while Unified Desktop mode is on; the Unified host
828   // will be destroyed and the Home Launcher (which was created earlier when we
829   // entered tablet mode) will be dismissed.
830   // To avoid crashes, we must ensure that the Home Launcher shown status is as
831   // expected if it's enabled and we're still in tablet mode.
832   // https://crbug.com/900956.
833   const bool should_be_shown =
834       IsTabletMode() && Shell::Get()->session_controller()->GetSessionState() ==
835                             session_manager::SessionState::ACTIVE;
836 
837   if (!should_be_shown || should_be_shown == presenter_.GetTargetVisibility())
838     return;
839 
840   Shell::Get()->home_screen_controller()->Show();
841 }
842 
OnWindowUntracked(aura::Window * untracked_window)843 void AppListControllerImpl::OnWindowUntracked(aura::Window* untracked_window) {
844   UpdateExpandArrowVisibility();
845 }
846 
OnAssistantReady()847 void AppListControllerImpl::OnAssistantReady() {
848   UpdateAssistantVisibility();
849 }
850 
OnUiVisibilityChanged(AssistantVisibility new_visibility,AssistantVisibility old_visibility,base::Optional<AssistantEntryPoint> entry_point,base::Optional<AssistantExitPoint> exit_point)851 void AppListControllerImpl::OnUiVisibilityChanged(
852     AssistantVisibility new_visibility,
853     AssistantVisibility old_visibility,
854     base::Optional<AssistantEntryPoint> entry_point,
855     base::Optional<AssistantExitPoint> exit_point) {
856   switch (new_visibility) {
857     case AssistantVisibility::kVisible:
858       if (!IsVisible(base::nullopt)) {
859         Show(GetDisplayIdToShowAppListOn(), kAssistantEntryPoint,
860              base::TimeTicks());
861       }
862 
863       if (!IsShowingEmbeddedAssistantUI())
864         presenter_.ShowEmbeddedAssistantUI(true);
865 
866       // Make sure that app list views are visible - they might get hidden
867       // during session startup, and the app list visibility might not have yet
868       // changed to visible by this point.
869       presenter_.SetViewVisibility(true);
870       break;
871     case AssistantVisibility::kClosed:
872       if (!IsShowingEmbeddedAssistantUI())
873         break;
874 
875       // When Launcher is closing, we do not want to call
876       // |ShowEmbeddedAssistantUI(false)|, which will show previous state page
877       // in Launcher and make the UI flash.
878       if (IsTabletMode()) {
879         base::Optional<ContentsView::ScopedSetActiveStateAnimationDisabler>
880             set_active_state_animation_disabler;
881         // When taking a screenshot by Assistant, we do not want to animate to
882         // the final state. Otherwise the screenshot may have tansient state
883         // during the animation. In tablet mode, we want to go back to
884         // kStateApps immediately, i.e. skipping the animation in
885         // |SetActiveStateInternal|, which are called from
886         // |ShowEmbeddedAssistantUI(false)| and
887         // |ClearSearchAndDeactivateSearchBox()|.
888         if (exit_point == AssistantExitPoint::kScreenshot) {
889           set_active_state_animation_disabler.emplace(
890               presenter_.GetView()->app_list_main_view()->contents_view());
891         }
892 
893         presenter_.ShowEmbeddedAssistantUI(false);
894 
895         if (exit_point != AssistantExitPoint::kBackInLauncher) {
896           presenter_.GetView()
897               ->search_box_view()
898               ->ClearSearchAndDeactivateSearchBox();
899         }
900       } else if (exit_point != AssistantExitPoint::kBackInLauncher) {
901         // Similarly, when taking a screenshot by Assistant in clamshell mode,
902         // we do not want to dismiss launcher with animation. Otherwise the
903         // screenshot may have tansient state during the animation.
904         base::AutoReset<bool> auto_reset(
905             &should_dismiss_immediately_,
906             exit_point == AssistantExitPoint::kScreenshot);
907         DismissAppList();
908       }
909 
910       break;
911   }
912 }
913 
914 base::ScopedClosureRunner
DisableHomeScreenBackgroundBlur()915 AppListControllerImpl::DisableHomeScreenBackgroundBlur() {
916   AppListView* const app_list_view = presenter_.GetView();
917   if (!app_list_view)
918     return base::ScopedClosureRunner(base::DoNothing());
919   return app_list_view->app_list_main_view()
920       ->contents_view()
921       ->apps_container_view()
922       ->DisableSuggestionChipsBlur();
923 }
924 
OnHomeLauncherAnimationComplete(bool shown,int64_t display_id)925 void AppListControllerImpl::OnHomeLauncherAnimationComplete(
926     bool shown,
927     int64_t display_id) {
928   // Stop disabling background blur in home screen when the home screen
929   // transition ends.
930   home_screen_blur_disabler_.reset();
931 
932   home_launcher_transition_state_ = HomeLauncherTransitionState::kFinished;
933 
934   AssistantUiController::Get()->CloseUi(
935       shown ? AssistantExitPoint::kLauncherOpen
936             : AssistantExitPoint::kLauncherClose);
937 
938   // Animations can be reversed (e.g. in a drag). Let's ensure the target
939   // visibility is correct first.
940   OnVisibilityChanged(shown, display_id);
941 }
942 
OnHomeLauncherPositionChanged(int percent_shown,int64_t display_id)943 void AppListControllerImpl::OnHomeLauncherPositionChanged(int percent_shown,
944                                                           int64_t display_id) {
945   // Disable home screen background blur if the home launcher transition is
946   // staring - the blur disabler will be reset when the transition ends (in
947   // OnHomeLauncherAnimationComplete()).
948   if (home_launcher_transition_state_ == HomeLauncherTransitionState::kFinished)
949     home_screen_blur_disabler_ = DisableHomeScreenBackgroundBlur();
950 
951   const bool mostly_shown = percent_shown >= 50;
952   home_launcher_transition_state_ =
953       mostly_shown ? HomeLauncherTransitionState::kMostlyShown
954                    : HomeLauncherTransitionState::kMostlyHidden;
955   OnVisibilityWillChange(mostly_shown, display_id);
956 }
957 
ShowHomeScreenView()958 void AppListControllerImpl::ShowHomeScreenView() {
959   DCHECK(IsTabletMode());
960 
961   // App list is only considered shown for metrics if there are currently no
962   // other visible windows shown over the app list after the tablet transition.
963   base::Optional<AppListShowSource> show_source;
964   if (!GetTopVisibleWindow())
965     show_source = kTabletMode;
966 
967   Show(GetDisplayIdToShowAppListOn(), show_source, base::TimeTicks());
968 }
969 
GetHomeScreenWindow()970 aura::Window* AppListControllerImpl::GetHomeScreenWindow() {
971   return presenter_.GetWindow();
972 }
973 
UpdateScaleAndOpacityForHomeLauncher(float scale,float opacity,base::Optional<AnimationInfo> animation_info,UpdateAnimationSettingsCallback callback)974 void AppListControllerImpl::UpdateScaleAndOpacityForHomeLauncher(
975     float scale,
976     float opacity,
977     base::Optional<AnimationInfo> animation_info,
978     UpdateAnimationSettingsCallback callback) {
979   DCHECK(!animation_info.has_value() || !callback.is_null());
980 
981   presenter_.UpdateScaleAndOpacityForHomeLauncher(
982       scale, opacity,
983       GetTransitionFromMetricsAnimationInfo(std::move(animation_info)),
984       std::move(callback));
985 }
986 
Back()987 void AppListControllerImpl::Back() {
988   presenter_.GetView()->Back();
989 }
990 
SetKeyboardTraversalMode(bool engaged)991 void AppListControllerImpl::SetKeyboardTraversalMode(bool engaged) {
992   if (keyboard_traversal_engaged_ == engaged)
993     return;
994 
995   keyboard_traversal_engaged_ = engaged;
996 
997   views::View* focused_view =
998       presenter_.GetView()->GetFocusManager()->GetFocusedView();
999 
1000   if (!focused_view)
1001     return;
1002 
1003   // When the search box has focus, it is actually the textfield that has focus.
1004   // As such, the |SearchBoxView| must be told to repaint directly.
1005   if (focused_view == presenter_.GetView()->search_box_view()->search_box())
1006     presenter_.GetView()->search_box_view()->SchedulePaint();
1007   else
1008     focused_view->SchedulePaint();
1009 }
1010 
IsShowingEmbeddedAssistantUI() const1011 bool AppListControllerImpl::IsShowingEmbeddedAssistantUI() const {
1012   return presenter_.IsShowingEmbeddedAssistantUI();
1013 }
1014 
UpdateExpandArrowVisibility()1015 void AppListControllerImpl::UpdateExpandArrowVisibility() {
1016   presenter_.SetExpandArrowViewVisibility(!IsTabletMode());
1017 }
1018 
CalculateStateAfterShelfDrag(const ui::LocatedEvent & event_in_screen,float launcher_above_shelf_bottom_amount) const1019 AppListViewState AppListControllerImpl::CalculateStateAfterShelfDrag(
1020     const ui::LocatedEvent& event_in_screen,
1021     float launcher_above_shelf_bottom_amount) const {
1022   if (presenter_.GetView())
1023     return presenter_.GetView()->CalculateStateAfterShelfDrag(
1024         event_in_screen, launcher_above_shelf_bottom_amount);
1025   return AppListViewState::kClosed;
1026 }
1027 
SetAppListModelForTest(std::unique_ptr<AppListModel> model)1028 void AppListControllerImpl::SetAppListModelForTest(
1029     std::unique_ptr<AppListModel> model) {
1030   model_->RemoveObserver(this);
1031   model_ = std::move(model);
1032   model_->AddObserver(this);
1033 }
1034 
SetStateTransitionAnimationCallbackForTesting(StateTransitionAnimationCallback callback)1035 void AppListControllerImpl::SetStateTransitionAnimationCallbackForTesting(
1036     StateTransitionAnimationCallback callback) {
1037   state_transition_animation_callback_ = std::move(callback);
1038 }
1039 
SetHomeLauncherAnimationCallbackForTesting(HomeLauncherAnimationCallback callback)1040 void AppListControllerImpl::SetHomeLauncherAnimationCallbackForTesting(
1041     HomeLauncherAnimationCallback callback) {
1042   home_launcher_animation_callback_ = std::move(callback);
1043 }
1044 
RecordShelfAppLaunched()1045 void AppListControllerImpl::RecordShelfAppLaunched() {
1046   RecordAppListAppLaunched(
1047       AppListLaunchedFrom::kLaunchedFromShelf,
1048       recorded_app_list_view_state_.value_or(GetAppListViewState()),
1049       IsTabletMode(), recorded_app_list_visibility_.value_or(last_visible_));
1050   recorded_app_list_view_state_ = base::nullopt;
1051   recorded_app_list_visibility_ = base::nullopt;
1052 }
1053 
1054 ////////////////////////////////////////////////////////////////////////////////
1055 // Methods of |client_|:
1056 
StartAssistant()1057 void AppListControllerImpl::StartAssistant() {
1058   AssistantUiController::Get()->ShowUi(
1059       AssistantEntryPoint::kLauncherSearchBoxIcon);
1060 }
1061 
StartSearch(const base::string16 & raw_query)1062 void AppListControllerImpl::StartSearch(const base::string16& raw_query) {
1063   if (client_) {
1064     base::string16 query;
1065     base::TrimWhitespace(raw_query, base::TRIM_ALL, &query);
1066     client_->StartSearch(query);
1067     auto* notifier = GetNotifier();
1068     if (notifier)
1069       notifier->NotifySearchQueryChanged(raw_query);
1070   }
1071 }
1072 
OpenSearchResult(const std::string & result_id,int event_flags,AppListLaunchedFrom launched_from,AppListLaunchType launch_type,int suggestion_index,bool launch_as_default)1073 void AppListControllerImpl::OpenSearchResult(const std::string& result_id,
1074                                              int event_flags,
1075                                              AppListLaunchedFrom launched_from,
1076                                              AppListLaunchType launch_type,
1077                                              int suggestion_index,
1078                                              bool launch_as_default) {
1079   SearchResult* result = search_model_.FindSearchResult(result_id);
1080   if (!result)
1081     return;
1082 
1083   if (launch_type == AppListLaunchType::kAppSearchResult) {
1084     switch (launched_from) {
1085       case AppListLaunchedFrom::kLaunchedFromSearchBox:
1086       case AppListLaunchedFrom::kLaunchedFromSuggestionChip:
1087         RecordAppLaunched(launched_from);
1088         break;
1089       case AppListLaunchedFrom::kLaunchedFromGrid:
1090       case AppListLaunchedFrom::kLaunchedFromShelf:
1091         break;
1092     }
1093   }
1094 
1095   UMA_HISTOGRAM_ENUMERATION(kSearchResultOpenDisplayTypeHistogram,
1096                             result->display_type(),
1097                             SearchResultDisplayType::kLast);
1098 
1099   // Suggestion chips are not represented to the user as search results, so do
1100   // not record search result metrics for them.
1101   if (launched_from != AppListLaunchedFrom::kLaunchedFromSuggestionChip) {
1102     base::RecordAction(base::UserMetricsAction("AppList_OpenSearchResult"));
1103 
1104     UMA_HISTOGRAM_COUNTS_100(kSearchQueryLength, GetLastQueryLength());
1105     if (IsTabletMode()) {
1106       UMA_HISTOGRAM_COUNTS_100(kSearchQueryLengthInTablet,
1107                                GetLastQueryLength());
1108     } else {
1109       UMA_HISTOGRAM_COUNTS_100(kSearchQueryLengthInClamshell,
1110                                GetLastQueryLength());
1111     }
1112   }
1113 
1114   auto* notifier = GetNotifier();
1115   if (notifier) {
1116     // Special-case chip results, because the display type of app results
1117     // doesn't account for whether it's being displayed in the suggestion chips
1118     // or app tiles.
1119     AppListNotifier::Result notifier_result(result->id(),
1120                                             result->metrics_type());
1121     if (launched_from == AppListLaunchedFrom::kLaunchedFromSuggestionChip) {
1122       notifier->NotifyLaunched(SearchResultDisplayType::kChip, notifier_result);
1123     } else {
1124       notifier->NotifyLaunched(result->display_type(), notifier_result);
1125     }
1126   }
1127 
1128   if (client_) {
1129     client_->OpenSearchResult(result_id, event_flags, launched_from,
1130                               launch_type, suggestion_index, launch_as_default);
1131   }
1132 
1133   ResetHomeLauncherIfShown();
1134 }
1135 
LogResultLaunchHistogram(SearchResultLaunchLocation launch_location,int suggestion_index)1136 void AppListControllerImpl::LogResultLaunchHistogram(
1137     SearchResultLaunchLocation launch_location,
1138     int suggestion_index) {
1139   RecordSearchLaunchIndexAndQueryLength(launch_location, GetLastQueryLength(),
1140                                         suggestion_index);
1141 }
1142 
LogSearchAbandonHistogram()1143 void AppListControllerImpl::LogSearchAbandonHistogram() {
1144   RecordSearchAbandonWithQueryLengthHistogram(GetLastQueryLength());
1145 }
1146 
InvokeSearchResultAction(const std::string & result_id,int action_index)1147 void AppListControllerImpl::InvokeSearchResultAction(
1148     const std::string& result_id,
1149     int action_index) {
1150   if (client_)
1151     client_->InvokeSearchResultAction(result_id, action_index);
1152 }
1153 
GetSearchResultContextMenuModel(const std::string & result_id,GetContextMenuModelCallback callback)1154 void AppListControllerImpl::GetSearchResultContextMenuModel(
1155     const std::string& result_id,
1156     GetContextMenuModelCallback callback) {
1157   if (client_)
1158     client_->GetSearchResultContextMenuModel(result_id, std::move(callback));
1159 }
1160 
ViewShown(int64_t display_id)1161 void AppListControllerImpl::ViewShown(int64_t display_id) {
1162   UpdateAssistantVisibility();
1163 
1164   if (client_)
1165     client_->ViewShown(display_id);
1166 
1167   Shell::Get()->home_screen_controller()->OnAppListViewShown();
1168 
1169   // Ensure search box starts fresh with no ring each time it opens.
1170   keyboard_traversal_engaged_ = false;
1171 }
1172 
AppListTargetVisibility() const1173 bool AppListControllerImpl::AppListTargetVisibility() const {
1174   return last_target_visible_;
1175 }
1176 
ViewClosing()1177 void AppListControllerImpl::ViewClosing() {
1178   if (presenter_.GetView()->search_box_view()->is_search_box_active()) {
1179     // Close the virtual keyboard before the app list view is dismissed.
1180     // Otherwise if the browser is behind the app list view, after the latter is
1181     // closed, IME is updated because of the changed focus. Consequently,
1182     // the virtual keyboard is hidden for the wrong IME instance, which may
1183     // bring troubles when restoring the virtual keyboard (see
1184     // https://crbug.com/944233).
1185     keyboard::KeyboardUIController::Get()->HideKeyboardExplicitlyBySystem();
1186   }
1187 
1188   AssistantUiController::Get()->CloseUi(AssistantExitPoint::kLauncherClose);
1189 
1190   if (client_)
1191     client_->ViewClosing();
1192 
1193   if (Shell::Get()->home_screen_controller())
1194     Shell::Get()->home_screen_controller()->OnAppListViewClosing();
1195 }
1196 
1197 const std::vector<SkColor>&
GetWallpaperProminentColors()1198 AppListControllerImpl::GetWallpaperProminentColors() {
1199   return Shell::Get()->wallpaper_controller()->GetWallpaperColors();
1200 }
1201 
ActivateItem(const std::string & id,int event_flags,AppListLaunchedFrom launched_from)1202 void AppListControllerImpl::ActivateItem(const std::string& id,
1203                                          int event_flags,
1204                                          AppListLaunchedFrom launched_from) {
1205   RecordAppLaunched(launched_from);
1206 
1207   if (client_)
1208     client_->ActivateItem(profile_id_, id, event_flags);
1209 
1210   ResetHomeLauncherIfShown();
1211 }
1212 
GetContextMenuModel(const std::string & id,GetContextMenuModelCallback callback)1213 void AppListControllerImpl::GetContextMenuModel(
1214     const std::string& id,
1215     GetContextMenuModelCallback callback) {
1216   if (client_)
1217     client_->GetContextMenuModel(profile_id_, id, std::move(callback));
1218 }
1219 
GetAnimationObserver(AppListViewState target_state)1220 ui::ImplicitAnimationObserver* AppListControllerImpl::GetAnimationObserver(
1221     AppListViewState target_state) {
1222   // |presenter_| observes the close animation only.
1223   if (target_state == AppListViewState::kClosed)
1224     return &presenter_;
1225   return nullptr;
1226 }
1227 
ShowWallpaperContextMenu(const gfx::Point & onscreen_location,ui::MenuSourceType source_type)1228 void AppListControllerImpl::ShowWallpaperContextMenu(
1229     const gfx::Point& onscreen_location,
1230     ui::MenuSourceType source_type) {
1231   Shell::Get()->ShowContextMenu(onscreen_location, source_type);
1232 }
1233 
KeyboardTraversalEngaged()1234 bool AppListControllerImpl::KeyboardTraversalEngaged() {
1235   return keyboard_traversal_engaged_;
1236 }
1237 
CanProcessEventsOnApplistViews()1238 bool AppListControllerImpl::CanProcessEventsOnApplistViews() {
1239   // Do not allow processing events during overview or while overview is
1240   // finished but still animating out.
1241   OverviewController* overview_controller = Shell::Get()->overview_controller();
1242   if (overview_controller->InOverviewSession() ||
1243       overview_controller->IsCompletingShutdownAnimations()) {
1244     return false;
1245   }
1246 
1247   return true;
1248 }
1249 
ShouldDismissImmediately()1250 bool AppListControllerImpl::ShouldDismissImmediately() {
1251   if (should_dismiss_immediately_)
1252     return true;
1253 
1254   DCHECK(Shell::HasInstance());
1255   const int ideal_shelf_y =
1256       Shelf::ForWindow(presenter_.GetView()->GetWidget()->GetNativeView())
1257           ->GetIdealBounds()
1258           .y();
1259 
1260   const int current_y =
1261       presenter_.GetView()->GetWidget()->GetNativeWindow()->bounds().y();
1262   return current_y > ideal_shelf_y;
1263 }
1264 
GetTargetYForAppListHide(aura::Window * root_window)1265 int AppListControllerImpl::GetTargetYForAppListHide(aura::Window* root_window) {
1266   DCHECK(Shell::HasInstance());
1267   gfx::Point top_center =
1268       Shelf::ForWindow(root_window)->GetShelfBoundsInScreen().top_center();
1269   wm::ConvertPointFromScreen(root_window, &top_center);
1270   return top_center.y();
1271 }
1272 
GetAssistantViewDelegate()1273 AssistantViewDelegate* AppListControllerImpl::GetAssistantViewDelegate() {
1274   return Shell::Get()->assistant_controller()->view_delegate();
1275 }
1276 
OnSearchResultVisibilityChanged(const std::string & id,bool visibility)1277 void AppListControllerImpl::OnSearchResultVisibilityChanged(
1278     const std::string& id,
1279     bool visibility) {
1280   if (client_)
1281     client_->OnSearchResultVisibilityChanged(id, visibility);
1282 }
1283 
NotifySearchResultsForLogging(const base::string16 & raw_query,const SearchResultIdWithPositionIndices & results,int position_index)1284 void AppListControllerImpl::NotifySearchResultsForLogging(
1285     const base::string16& raw_query,
1286     const SearchResultIdWithPositionIndices& results,
1287     int position_index) {
1288   if (client_) {
1289     base::string16 query;
1290     base::TrimWhitespace(raw_query, base::TRIM_ALL, &query);
1291     client_->NotifySearchResultsForLogging(query, results, position_index);
1292   }
1293 }
1294 
MaybeIncreasePrivacyInfoShownCounts()1295 void AppListControllerImpl::MaybeIncreasePrivacyInfoShownCounts() {
1296   if (ShouldShowAssistantPrivacyInfo()) {
1297     const int count = GetAssistantPrivacyInfoShownCount();
1298     SetAssistantPrivacyInfoShownCount(count + 1);
1299   } else if (ShouldShowSuggestedContentInfo()) {
1300     const int count = GetSuggestedContentInfoShownCount();
1301     SetSuggestedContentInfoShownCount(count + 1);
1302   }
1303 }
1304 
IsAssistantAllowedAndEnabled() const1305 bool AppListControllerImpl::IsAssistantAllowedAndEnabled() const {
1306   if (!Shell::Get()->assistant_controller()->IsAssistantReady())
1307     return false;
1308 
1309   auto* state = AssistantState::Get();
1310   return state->settings_enabled().value_or(false) &&
1311          state->allowed_state() ==
1312              chromeos::assistant::AssistantAllowedState::ALLOWED &&
1313          state->assistant_status() !=
1314              chromeos::assistant::AssistantStatus::NOT_READY;
1315 }
1316 
ShouldShowAssistantPrivacyInfo() const1317 bool AppListControllerImpl::ShouldShowAssistantPrivacyInfo() const {
1318   // TODO(b/174506130) completely remove Assistant privacy info in followup.
1319   return false;
1320 }
1321 
MarkAssistantPrivacyInfoDismissed()1322 void AppListControllerImpl::MarkAssistantPrivacyInfoDismissed() {
1323   // User dismissed the privacy info view. Will not show the view again.
1324   SetAssistantPrivacyInfoDismissed();
1325 }
1326 
ShouldShowSuggestedContentInfo() const1327 bool AppListControllerImpl::ShouldShowSuggestedContentInfo() const {
1328   if (!base::FeatureList::IsEnabled(
1329           chromeos::features::kSuggestedContentToggle)) {
1330     return false;
1331   }
1332 
1333   if (!IsSuggestedContentEnabled()) {
1334     // Don't show if user has interacted with the setting already.
1335     SetSuggestedContentInfoDismissed();
1336     return false;
1337   }
1338 
1339   if (IsSuggestedContentInfoDismissed()) {
1340     return false;
1341   }
1342 
1343   const int count = GetSuggestedContentInfoShownCount();
1344   constexpr int kThresholdToShow = 3;
1345   return count >= 0 && count <= kThresholdToShow;
1346 }
1347 
MarkSuggestedContentInfoDismissed()1348 void AppListControllerImpl::MarkSuggestedContentInfoDismissed() {
1349   // User dismissed the privacy info view. Will not show the view again.
1350   SetSuggestedContentInfoDismissed();
1351 }
1352 
OnStateTransitionAnimationCompleted(AppListViewState state)1353 void AppListControllerImpl::OnStateTransitionAnimationCompleted(
1354     AppListViewState state) {
1355   if (!state_transition_animation_callback_.is_null())
1356     state_transition_animation_callback_.Run(state);
1357 }
1358 
OnViewStateChanged(AppListViewState state)1359 void AppListControllerImpl::OnViewStateChanged(AppListViewState state) {
1360   auto* notifier = GetNotifier();
1361   if (notifier)
1362     notifier->NotifyUIStateChanged(state);
1363 }
1364 
GetAppLaunchedMetricParams(AppLaunchedMetricParams * metric_params)1365 void AppListControllerImpl::GetAppLaunchedMetricParams(
1366     AppLaunchedMetricParams* metric_params) {
1367   metric_params->app_list_view_state = GetAppListViewState();
1368   metric_params->is_tablet_mode = IsTabletMode();
1369   metric_params->home_launcher_shown = last_visible_;
1370 }
1371 
SnapBoundsToDisplayEdge(const gfx::Rect & bounds)1372 gfx::Rect AppListControllerImpl::SnapBoundsToDisplayEdge(
1373     const gfx::Rect& bounds) {
1374   AppListView* app_list_view = presenter_.GetView();
1375   DCHECK(app_list_view && app_list_view->GetWidget());
1376   aura::Window* window = app_list_view->GetWidget()->GetNativeView();
1377   return screen_util::SnapBoundsToDisplayEdge(bounds, window);
1378 }
1379 
GetShelfSize()1380 int AppListControllerImpl::GetShelfSize() {
1381   return ShelfConfig::Get()->system_shelf_size();
1382 }
1383 
IsInTabletMode()1384 bool AppListControllerImpl::IsInTabletMode() {
1385   return Shell::Get()->tablet_mode_controller()->InTabletMode();
1386 }
1387 
GetColorProvider()1388 AppListColorProviderImpl* AppListControllerImpl::GetColorProvider() {
1389   return &color_provider_;
1390 }
1391 
RecordAppLaunched(AppListLaunchedFrom launched_from)1392 void AppListControllerImpl::RecordAppLaunched(
1393     AppListLaunchedFrom launched_from) {
1394   RecordAppListAppLaunched(launched_from, GetAppListViewState(), IsTabletMode(),
1395                            last_visible_);
1396 }
1397 
AddObserver(AppListControllerObserver * observer)1398 void AppListControllerImpl::AddObserver(AppListControllerObserver* observer) {
1399   observers_.AddObserver(observer);
1400 }
1401 
RemoveObserver(AppListControllerObserver * observer)1402 void AppListControllerImpl::RemoveObserver(
1403     AppListControllerObserver* observer) {
1404   observers_.RemoveObserver(observer);
1405 }
1406 
OnVisibilityChanged(bool visible,int64_t display_id)1407 void AppListControllerImpl::OnVisibilityChanged(bool visible,
1408                                                 int64_t display_id) {
1409   // Focus and app visibility changes while finishing home launcher state
1410   // animation may cause OnVisibilityChanged() to be called before the home
1411   // launcher state transition finished - delay the visibility change until the
1412   // home launcher stops animating, so observers do not miss the animation state
1413   // update.
1414   if (home_launcher_transition_state_ !=
1415       HomeLauncherTransitionState::kFinished) {
1416     OnVisibilityWillChange(visible, display_id);
1417     return;
1418   }
1419 
1420   bool real_visibility = visible;
1421   // HomeLauncher is only visible when no other app windows are visible,
1422   // unless we are in the process of animating to (or dragging) the home
1423   // launcher.
1424   if (IsTabletMode()) {
1425     UpdateTrackedAppWindow();
1426 
1427     if (tracked_app_window_)
1428       real_visibility = false;
1429   }
1430 
1431   aura::Window* app_list_window = GetWindow();
1432   real_visibility &= app_list_window && app_list_window->TargetVisibility();
1433 
1434   OnVisibilityWillChange(real_visibility, display_id);
1435 
1436   // Skip adjacent same changes.
1437   if (last_visible_ == real_visibility &&
1438       last_visible_display_id_ == display_id) {
1439     return;
1440   }
1441 
1442   last_visible_display_id_ = display_id;
1443 
1444   AppListView* const app_list_view = presenter_.GetView();
1445   app_list_view->UpdatePageResetTimer(real_visibility);
1446 
1447   if (!real_visibility) {
1448     app_list_view->search_box_view()->ClearSearchAndDeactivateSearchBox();
1449     // Reset the app list contents state, so the app list is in initial state
1450     // when the app list visibility changes again.
1451     app_list_view->app_list_main_view()->contents_view()->ResetForShow();
1452   }
1453 
1454   // Notify chrome of visibility changes.
1455   if (last_visible_ != real_visibility) {
1456     // When showing the launcher with the virtual keyboard enabled, one feature
1457     // called "transient blur" (which means that if focus was lost but regained
1458     // a few seconds later, we would show the virtual keyboard again) may show
1459     // the virtual keyboard, which is not what we want. So hide the virtual
1460     // keyboard explicitly when the launcher shows.
1461     if (real_visibility)
1462       keyboard::KeyboardUIController::Get()->HideKeyboardExplicitlyBySystem();
1463 
1464     if (client_)
1465       client_->OnAppListVisibilityChanged(real_visibility);
1466 
1467     last_visible_ = real_visibility;
1468 
1469     // We could make Assistant sub-controllers an AppListControllerObserver, but
1470     // we do not want to introduce new dependency of AppListController to
1471     // Assistant.
1472     GetAssistantViewDelegate()->OnHostViewVisibilityChanged(real_visibility);
1473     for (auto& observer : observers_)
1474       observer.OnAppListVisibilityChanged(real_visibility, display_id);
1475 
1476     if (!home_launcher_animation_callback_.is_null())
1477       home_launcher_animation_callback_.Run(real_visibility);
1478   }
1479 }
1480 
OnWindowVisibilityChanging(aura::Window * window,bool visible)1481 void AppListControllerImpl::OnWindowVisibilityChanging(aura::Window* window,
1482                                                        bool visible) {
1483   if (visible || window != tracked_app_window_)
1484     return;
1485 
1486   UpdateTrackedAppWindow();
1487 
1488   if (!tracked_app_window_ && ShouldHomeLauncherBeVisible())
1489     OnVisibilityChanged(true, last_visible_display_id_);
1490 }
1491 
OnWindowDestroyed(aura::Window * window)1492 void AppListControllerImpl::OnWindowDestroyed(aura::Window* window) {
1493   if (window != tracked_app_window_)
1494     return;
1495 
1496   tracked_app_window_ = nullptr;
1497 }
1498 
OnVisibilityWillChange(bool visible,int64_t display_id)1499 void AppListControllerImpl::OnVisibilityWillChange(bool visible,
1500                                                    int64_t display_id) {
1501   bool real_target_visibility = visible;
1502   // HomeLauncher is only visible when no other app windows are visible,
1503   // unless we are in the process of animating to (or dragging) the home
1504   // launcher.
1505   if (IsTabletMode() && home_launcher_transition_state_ ==
1506                             HomeLauncherTransitionState::kFinished) {
1507     real_target_visibility &= !GetTopVisibleWindow();
1508   }
1509 
1510   // Skip adjacent same changes.
1511   if (last_target_visible_ == real_target_visibility &&
1512       last_target_visible_display_id_ == display_id) {
1513     return;
1514   }
1515 
1516   // Notify chrome of target visibility changes.
1517   if (last_target_visible_ != real_target_visibility) {
1518     last_target_visible_ = real_target_visibility;
1519     last_target_visible_display_id_ = display_id;
1520 
1521     if (real_target_visibility && IsTabletMode()) {
1522       // Update the arrow visibility when starting to show the home screen
1523       // (presumably, the visibility has already been updated if home is being
1524       // hidden).
1525       UpdateExpandArrowVisibility();
1526     }
1527 
1528     if (real_target_visibility && presenter_.GetView())
1529       presenter_.SetViewVisibility(true);
1530 
1531     if (client_)
1532       client_->OnAppListVisibilityWillChange(real_target_visibility);
1533 
1534     for (auto& observer : observers_) {
1535       observer.OnAppListVisibilityWillChange(real_target_visibility,
1536                                              display_id);
1537     }
1538   }
1539 }
1540 
1541 ////////////////////////////////////////////////////////////////////////////////
1542 // Private used only:
1543 
GetOemFolderPos()1544 syncer::StringOrdinal AppListControllerImpl::GetOemFolderPos() {
1545   // Place the OEM folder just after the web store, which should always be
1546   // followed by a pre-installed app (e.g. Search), so the poosition should be
1547   // stable. TODO(stevenjb): consider explicitly setting the OEM folder location
1548   // along with the name in ServicesCustomizationDocument::SetOemFolderName().
1549   AppListItemList* item_list = model_->top_level_item_list();
1550   if (!item_list->item_count()) {
1551     LOG(ERROR) << "No top level item was found. "
1552                << "Placing OEM folder at the beginning.";
1553     return syncer::StringOrdinal::CreateInitialOrdinal();
1554   }
1555 
1556   size_t web_store_app_index;
1557   if (!item_list->FindItemIndex(extensions::kWebStoreAppId,
1558                                 &web_store_app_index)) {
1559     LOG(ERROR) << "Web store position is not found it top items. "
1560                << "Placing OEM folder at the end.";
1561     return item_list->item_at(item_list->item_count() - 1)
1562         ->position()
1563         .CreateAfter();
1564   }
1565 
1566   // Skip items with the same position.
1567   const AppListItem* web_store_app_item =
1568       item_list->item_at(web_store_app_index);
1569   for (size_t j = web_store_app_index + 1; j < item_list->item_count(); ++j) {
1570     const AppListItem* next_item = item_list->item_at(j);
1571     DCHECK(next_item->position().IsValid());
1572     if (!next_item->position().Equals(web_store_app_item->position())) {
1573       const syncer::StringOrdinal oem_ordinal =
1574           web_store_app_item->position().CreateBetween(next_item->position());
1575       VLOG(1) << "Placing OEM Folder at: " << j
1576               << " position: " << oem_ordinal.ToDebugString();
1577       return oem_ordinal;
1578     }
1579   }
1580 
1581   const syncer::StringOrdinal oem_ordinal =
1582       web_store_app_item->position().CreateAfter();
1583   VLOG(1) << "Placing OEM Folder at: " << item_list->item_count()
1584           << " position: " << oem_ordinal.ToDebugString();
1585   return oem_ordinal;
1586 }
1587 
CreateAppListItem(std::unique_ptr<AppListItemMetadata> metadata)1588 std::unique_ptr<AppListItem> AppListControllerImpl::CreateAppListItem(
1589     std::unique_ptr<AppListItemMetadata> metadata) {
1590   std::unique_ptr<AppListItem> app_list_item =
1591       metadata->is_folder ? std::make_unique<AppListFolderItem>(metadata->id)
1592                           : std::make_unique<AppListItem>(metadata->id);
1593   app_list_item->SetMetadata(std::move(metadata));
1594   return app_list_item;
1595 }
1596 
FindFolderItem(const std::string & folder_id)1597 AppListFolderItem* AppListControllerImpl::FindFolderItem(
1598     const std::string& folder_id) {
1599   return model_->FindFolderItem(folder_id);
1600 }
1601 
UpdateAssistantVisibility()1602 void AppListControllerImpl::UpdateAssistantVisibility() {
1603   GetSearchModel()->search_box()->SetShowAssistantButton(
1604       IsAssistantAllowedAndEnabled());
1605 }
1606 
GetDisplayIdToShowAppListOn()1607 int64_t AppListControllerImpl::GetDisplayIdToShowAppListOn() {
1608   if (IsTabletMode() && !Shell::Get()->display_manager()->IsInUnifiedMode()) {
1609     return display::Display::HasInternalDisplay()
1610                ? display::Display::InternalDisplayId()
1611                : display::Screen::GetScreen()->GetPrimaryDisplay().id();
1612   }
1613 
1614   return display::Screen::GetScreen()
1615       ->GetDisplayNearestWindow(Shell::GetRootWindowForNewWindows())
1616       .id();
1617 }
1618 
ResetHomeLauncherIfShown()1619 void AppListControllerImpl::ResetHomeLauncherIfShown() {
1620   if (!IsTabletMode() || !presenter_.IsVisibleDeprecated())
1621     return;
1622 
1623   auto* const keyboard_controller = keyboard::KeyboardUIController::Get();
1624   if (keyboard_controller->IsKeyboardVisible())
1625     keyboard_controller->HideKeyboardByUser();
1626   presenter_.GetView()->CloseOpenedPage();
1627 
1628   // Refresh the suggestion chips with empty query.
1629   StartSearch(base::string16());
1630 }
1631 
UpdateLauncherContainer(base::Optional<int64_t> display_id)1632 void AppListControllerImpl::UpdateLauncherContainer(
1633     base::Optional<int64_t> display_id) {
1634   aura::Window* window = presenter_.GetWindow();
1635   if (!window)
1636     return;
1637 
1638   aura::Window* parent_window = GetContainerForDisplayId(display_id);
1639   if (parent_window && !parent_window->Contains(window)) {
1640     parent_window->AddChild(window);
1641     // Release focus if the launcher is moving behind apps, and there is app
1642     // window showing. Note that the app list can be shown behind apps in tablet
1643     // mode only.
1644     if (IsTabletMode() && !ShouldHomeLauncherBeVisible()) {
1645       WindowState* const window_state = WindowState::Get(window);
1646       if (window_state->IsActive())
1647         window_state->Deactivate();
1648     }
1649   }
1650 }
1651 
GetContainerId() const1652 int AppListControllerImpl::GetContainerId() const {
1653   return ShouldLauncherShowBehindApps() ? kShellWindowId_HomeScreenContainer
1654                                         : kShellWindowId_AppListContainer;
1655 }
1656 
GetContainerForDisplayId(base::Optional<int64_t> display_id)1657 aura::Window* AppListControllerImpl::GetContainerForDisplayId(
1658     base::Optional<int64_t> display_id) {
1659   aura::Window* root_window = nullptr;
1660   if (display_id.has_value()) {
1661     root_window = Shell::GetRootWindowForDisplayId(display_id.value());
1662   } else if (presenter_.GetWindow()) {
1663     root_window = presenter_.GetWindow()->GetRootWindow();
1664   }
1665 
1666   return root_window ? root_window->GetChildById(GetContainerId()) : nullptr;
1667 }
1668 
ShouldLauncherShowBehindApps() const1669 bool AppListControllerImpl::ShouldLauncherShowBehindApps() const {
1670   return IsTabletMode() &&
1671          model_->state() != AppListState::kStateEmbeddedAssistant;
1672 }
1673 
GetLastQueryLength()1674 int AppListControllerImpl::GetLastQueryLength() {
1675   base::string16 query;
1676   base::TrimWhitespace(search_model_.search_box()->text(), base::TRIM_ALL,
1677                        &query);
1678   return query.length();
1679 }
1680 
Shutdown()1681 void AppListControllerImpl::Shutdown() {
1682   DCHECK(!is_shutdown_);
1683   is_shutdown_ = true;
1684 
1685   Shell* shell = Shell::Get();
1686   message_center::MessageCenter::Get()->RemoveObserver(this);
1687   AssistantController::Get()->RemoveObserver(this);
1688   AssistantUiController::Get()->GetModel()->RemoveObserver(this);
1689   shell->mru_window_tracker()->RemoveObserver(this);
1690   shell->window_tree_host_manager()->RemoveObserver(this);
1691   AssistantState::Get()->RemoveObserver(this);
1692   keyboard::KeyboardUIController::Get()->RemoveObserver(this);
1693   shell->overview_controller()->RemoveObserver(this);
1694   shell->RemoveShellObserver(this);
1695   shell->wallpaper_controller()->RemoveObserver(this);
1696   shell->tablet_mode_controller()->RemoveObserver(this);
1697   shell->session_controller()->RemoveObserver(this);
1698   model_->RemoveObserver(this);
1699 }
1700 
IsHomeScreenVisible()1701 bool AppListControllerImpl::IsHomeScreenVisible() {
1702   return IsTabletMode() && IsVisible(base::nullopt);
1703 }
1704 
GetInitialAppListItemScreenBoundsForWindow(aura::Window * window)1705 gfx::Rect AppListControllerImpl::GetInitialAppListItemScreenBoundsForWindow(
1706     aura::Window* window) {
1707   if (!presenter_.GetView())
1708     return gfx::Rect();
1709   std::string* app_id = window->GetProperty(kAppIDKey);
1710   return presenter_.GetView()->GetItemScreenBoundsInFirstGridPage(
1711       app_id ? *app_id : std::string());
1712 }
1713 
OnAppUpdate(const apps::AppUpdate & update)1714 void AppListControllerImpl::OnAppUpdate(const apps::AppUpdate& update) {
1715   if (update.HasBadgeChanged() &&
1716       notification_badging_pref_enabled_.value_or(false) &&
1717       !quiet_mode_enabled_.value_or(false)) {
1718     UpdateItemNotificationBadge(update.AppId(), update.HasBadge());
1719   }
1720 }
1721 
OnAppRegistryCacheWillBeDestroyed(apps::AppRegistryCache * cache)1722 void AppListControllerImpl::OnAppRegistryCacheWillBeDestroyed(
1723     apps::AppRegistryCache* cache) {
1724   Observe(nullptr);
1725 }
1726 
OnQuietModeChanged(bool in_quiet_mode)1727 void AppListControllerImpl::OnQuietModeChanged(bool in_quiet_mode) {
1728   UpdateAppBadging();
1729 }
1730 
UpdateTrackedAppWindow()1731 void AppListControllerImpl::UpdateTrackedAppWindow() {
1732   aura::Window* top_window = GetTopVisibleWindow();
1733   if (tracked_app_window_ == top_window)
1734     return;
1735 
1736   if (tracked_app_window_)
1737     tracked_app_window_->RemoveObserver(this);
1738   tracked_app_window_ = top_window;
1739   if (tracked_app_window_)
1740     tracked_app_window_->AddObserver(this);
1741 }
1742 
RecordAppListState()1743 void AppListControllerImpl::RecordAppListState() {
1744   recorded_app_list_view_state_ = GetAppListViewState();
1745   recorded_app_list_visibility_ = last_visible_;
1746 }
1747 
UpdateItemNotificationBadge(const std::string & app_id,apps::mojom::OptionalBool has_badge)1748 void AppListControllerImpl::UpdateItemNotificationBadge(
1749     const std::string& app_id,
1750     apps::mojom::OptionalBool has_badge) {
1751   AppListItem* item = model_->FindItem(app_id);
1752   if (item)
1753     item->UpdateBadge(has_badge == apps::mojom::OptionalBool::kTrue);
1754 }
1755 
UpdateAppBadging()1756 void AppListControllerImpl::UpdateAppBadging() {
1757   bool new_badging_enabled = pref_change_registrar_
1758                                  ? pref_change_registrar_->prefs()->GetBoolean(
1759                                        prefs::kAppNotificationBadgingEnabled)
1760                                  : false;
1761   bool new_quiet_mode_enabled =
1762       message_center::MessageCenter::Get()->IsQuietMode();
1763 
1764   if (notification_badging_pref_enabled_.has_value() &&
1765       notification_badging_pref_enabled_.value() == new_badging_enabled &&
1766       quiet_mode_enabled_.has_value() &&
1767       quiet_mode_enabled_.value() == new_quiet_mode_enabled) {
1768     return;
1769   }
1770   notification_badging_pref_enabled_ = new_badging_enabled;
1771   quiet_mode_enabled_ = new_quiet_mode_enabled;
1772 
1773   if (cache_) {
1774     cache_->ForEachApp([this](const apps::AppUpdate& update) {
1775       // Set the app notification badge hidden when the pref is disabled.
1776       apps::mojom::OptionalBool has_badge =
1777           notification_badging_pref_enabled_.value() &&
1778                   !quiet_mode_enabled_.value() &&
1779                   (update.HasBadge() == apps::mojom::OptionalBool::kTrue)
1780               ? apps::mojom::OptionalBool::kTrue
1781               : apps::mojom::OptionalBool::kFalse;
1782       UpdateItemNotificationBadge(update.AppId(), has_badge);
1783     });
1784   }
1785 }
1786 
1787 }  // namespace ash
1788