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