Add ENABLE_MEDIA_ROUTER define to builds other than Android and iOS.
[chromium-blink-merge.git] / chrome / browser / ui / app_list / app_list_view_delegate.cc
blobb2fae696b7ea251042dbd4f6b88fd9f270615271
1 // Copyright (c) 2012 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.
5 #include "chrome/browser/ui/app_list/app_list_view_delegate.h"
7 #include <vector>
9 #include "apps/custom_launcher_page_contents.h"
10 #include "base/callback.h"
11 #include "base/command_line.h"
12 #include "base/files/file_path.h"
13 #include "base/metrics/user_metrics.h"
14 #include "base/prefs/pref_service.h"
15 #include "base/profiler/scoped_tracker.h"
16 #include "base/stl_util.h"
17 #include "chrome/browser/apps/scoped_keep_alive.h"
18 #include "chrome/browser/browser_process.h"
19 #include "chrome/browser/chrome_notification_types.h"
20 #include "chrome/browser/profiles/profile_info_cache.h"
21 #include "chrome/browser/profiles/profile_manager.h"
22 #include "chrome/browser/search/hotword_service.h"
23 #include "chrome/browser/search/hotword_service_factory.h"
24 #include "chrome/browser/search_engines/template_url_service_factory.h"
25 #include "chrome/browser/ui/app_list/app_list_controller_delegate.h"
26 #include "chrome/browser/ui/app_list/app_list_service.h"
27 #include "chrome/browser/ui/app_list/app_list_syncable_service.h"
28 #include "chrome/browser/ui/app_list/app_list_syncable_service_factory.h"
29 #include "chrome/browser/ui/app_list/launcher_page_event_dispatcher.h"
30 #include "chrome/browser/ui/app_list/search/search_controller_factory.h"
31 #include "chrome/browser/ui/app_list/search/search_resource_manager.h"
32 #include "chrome/browser/ui/app_list/start_page_service.h"
33 #include "chrome/browser/ui/apps/chrome_app_delegate.h"
34 #include "chrome/browser/ui/browser_finder.h"
35 #include "chrome/browser/ui/chrome_pages.h"
36 #include "chrome/browser/ui/host_desktop.h"
37 #include "chrome/browser/ui/scoped_tabbed_browser_displayer.h"
38 #include "chrome/browser/web_applications/web_app.h"
39 #include "chrome/common/chrome_switches.h"
40 #include "chrome/common/extensions/extension_constants.h"
41 #include "chrome/common/pref_names.h"
42 #include "chrome/common/url_constants.h"
43 #include "components/search_engines/template_url_prepopulate_data.h"
44 #include "components/signin/core/browser/signin_manager.h"
45 #include "components/user_prefs/user_prefs.h"
46 #include "content/public/browser/browser_thread.h"
47 #include "content/public/browser/notification_service.h"
48 #include "content/public/browser/page_navigator.h"
49 #include "content/public/browser/render_view_host.h"
50 #include "content/public/browser/render_widget_host_view.h"
51 #include "content/public/browser/speech_recognition_session_preamble.h"
52 #include "content/public/browser/user_metrics.h"
53 #include "content/public/browser/web_contents.h"
54 #include "extensions/browser/extension_registry.h"
55 #include "extensions/common/constants.h"
56 #include "extensions/common/extension_set.h"
57 #include "extensions/common/manifest_constants.h"
58 #include "extensions/common/manifest_handlers/launcher_page_info.h"
59 #include "grit/theme_resources.h"
60 #include "ui/app_list/app_list_switches.h"
61 #include "ui/app_list/app_list_view_delegate_observer.h"
62 #include "ui/app_list/search_box_model.h"
63 #include "ui/app_list/search_controller.h"
64 #include "ui/app_list/speech_ui_model.h"
65 #include "ui/base/resource/resource_bundle.h"
66 #include "ui/views/controls/webview/webview.h"
68 #if defined(TOOLKIT_VIEWS)
69 #include "ui/views/controls/webview/webview.h"
70 #endif
72 #if defined(USE_AURA)
73 #include "ui/keyboard/keyboard_util.h"
74 #endif
76 #if defined(USE_ASH)
77 #include "chrome/browser/ui/ash/app_list/app_sync_ui_state_watcher.h"
78 #endif
80 #if defined(OS_WIN)
81 #include "chrome/browser/web_applications/web_app_win.h"
82 #endif
85 namespace chrome {
86 const char kAppLauncherCategoryTag[] = "AppLauncher";
87 } // namespace chrome
89 namespace {
91 const int kAutoLaunchDefaultTimeoutMilliSec = 50;
93 #if defined(OS_WIN)
94 void CreateShortcutInWebAppDir(
95 const base::FilePath& app_data_dir,
96 base::Callback<void(const base::FilePath&)> callback,
97 const web_app::ShortcutInfo& info) {
98 content::BrowserThread::PostTaskAndReplyWithResult(
99 content::BrowserThread::FILE,
100 FROM_HERE,
101 base::Bind(web_app::CreateShortcutInWebAppDir, app_data_dir, info),
102 callback);
104 #endif
106 void PopulateUsers(const ProfileInfoCache& profile_info,
107 const base::FilePath& active_profile_path,
108 app_list::AppListViewDelegate::Users* users) {
109 users->clear();
110 const size_t count = profile_info.GetNumberOfProfiles();
111 for (size_t i = 0; i < count; ++i) {
112 app_list::AppListViewDelegate::User user;
113 user.name = profile_info.GetNameOfProfileAtIndex(i);
114 user.email = profile_info.GetUserNameOfProfileAtIndex(i);
115 user.profile_path = profile_info.GetPathOfProfileAtIndex(i);
116 user.active = active_profile_path == user.profile_path;
117 users->push_back(user);
121 // Gets a list of URLs of the custom launcher pages to show in the launcher.
122 // Returns a URL for each installed launcher page. If --custom-launcher-page is
123 // specified and valid, also includes that URL.
124 void GetCustomLauncherPageUrls(content::BrowserContext* browser_context,
125 std::vector<GURL>* urls) {
126 // First, check the command line.
127 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
128 if (app_list::switches::IsExperimentalAppListEnabled() &&
129 command_line->HasSwitch(app_list::switches::kCustomLauncherPage)) {
130 GURL custom_launcher_page_url(command_line->GetSwitchValueASCII(
131 app_list::switches::kCustomLauncherPage));
133 if (custom_launcher_page_url.SchemeIs(extensions::kExtensionScheme)) {
134 urls->push_back(custom_launcher_page_url);
135 } else {
136 LOG(ERROR) << "Invalid custom launcher page URL: "
137 << custom_launcher_page_url.possibly_invalid_spec();
141 // Prevent launcher pages from loading unless the pref is enabled.
142 // (Command-line specified pages are exempt from this rule).
143 PrefService* profile_prefs = user_prefs::UserPrefs::Get(browser_context);
144 if (profile_prefs &&
145 profile_prefs->HasPrefPath(prefs::kGoogleNowLauncherEnabled) &&
146 !profile_prefs->GetBoolean(prefs::kGoogleNowLauncherEnabled)) {
147 return;
150 // Search the list of installed extensions for ones with 'launcher_page'.
151 extensions::ExtensionRegistry* extension_registry =
152 extensions::ExtensionRegistry::Get(browser_context);
153 const extensions::ExtensionSet& enabled_extensions =
154 extension_registry->enabled_extensions();
155 for (extensions::ExtensionSet::const_iterator it = enabled_extensions.begin();
156 it != enabled_extensions.end();
157 ++it) {
158 const extensions::Extension* extension = it->get();
159 extensions::LauncherPageInfo* info =
160 extensions::LauncherPageHandler::GetInfo(extension);
161 if (!info)
162 continue;
164 urls->push_back(extension->GetResourceURL(info->page));
168 } // namespace
170 AppListViewDelegate::AppListViewDelegate(AppListControllerDelegate* controller)
171 : controller_(controller),
172 profile_(NULL),
173 model_(NULL),
174 is_voice_query_(false),
175 template_url_service_observer_(this),
176 scoped_observer_(this) {
177 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed.
178 tracked_objects::ScopedTracker tracking_profile(
179 FROM_HERE_WITH_EXPLICIT_FUNCTION(
180 "431326 AppListViewDelegate::AppListViewDelegate"));
182 CHECK(controller_);
183 // The SigninManagerFactor and the SigninManagers are observed to keep the
184 // profile switcher menu up to date, with the correct list of profiles and the
185 // correct email address (or none for signed out users) for each.
186 SigninManagerFactory::GetInstance()->AddObserver(this);
188 // Start observing all already-created SigninManagers.
189 ProfileManager* profile_manager = g_browser_process->profile_manager();
190 std::vector<Profile*> profiles = profile_manager->GetLoadedProfiles();
192 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed.
193 tracked_objects::ScopedTracker tracking_profile1(
194 FROM_HERE_WITH_EXPLICIT_FUNCTION(
195 "431326 AppListViewDelegate::AppListViewDelegate1"));
197 for (std::vector<Profile*>::iterator i = profiles.begin();
198 i != profiles.end();
199 ++i) {
200 SigninManagerBase* manager =
201 SigninManagerFactory::GetForProfileIfExists(*i);
202 if (manager) {
203 DCHECK(!scoped_observer_.IsObserving(manager));
204 scoped_observer_.Add(manager);
208 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed.
209 tracked_objects::ScopedTracker tracking_profile2(
210 FROM_HERE_WITH_EXPLICIT_FUNCTION(
211 "431326 AppListViewDelegate::AppListViewDelegate2"));
213 profile_manager->GetProfileInfoCache().AddObserver(this);
214 speech_ui_.reset(new app_list::SpeechUIModel);
216 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed.
217 tracked_objects::ScopedTracker tracking_profile3(
218 FROM_HERE_WITH_EXPLICIT_FUNCTION(
219 "431326 AppListViewDelegate::AppListViewDelegate3"));
221 #if defined(GOOGLE_CHROME_BUILD)
222 gfx::ImageSkia image =
223 *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
224 IDR_APP_LIST_GOOGLE_LOGO_VOICE_SEARCH);
226 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is
227 // fixed.
228 tracked_objects::ScopedTracker tracking_profile31(
229 FROM_HERE_WITH_EXPLICIT_FUNCTION(
230 "431326 AppListViewDelegate::AppListViewDelegate31"));
232 speech_ui_->set_logo(image);
233 #endif
235 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed.
236 tracked_objects::ScopedTracker tracking_profile32(
237 FROM_HERE_WITH_EXPLICIT_FUNCTION(
238 "431326 AppListViewDelegate::AppListViewDelegate32"));
240 registrar_.Add(this,
241 chrome::NOTIFICATION_APP_TERMINATING,
242 content::NotificationService::AllSources());
245 AppListViewDelegate::~AppListViewDelegate() {
246 // Note that the destructor is not always called. E.g. on Mac, this is owned
247 // by a leaky singleton. Essential shutdown work must be done by observing
248 // chrome::NOTIFICATION_APP_TERMINATING.
249 SetProfile(NULL);
250 g_browser_process->profile_manager()->GetProfileInfoCache().RemoveObserver(
251 this);
253 SigninManagerFactory* factory = SigninManagerFactory::GetInstance();
254 if (factory)
255 factory->RemoveObserver(this);
258 void AppListViewDelegate::SetProfile(Profile* new_profile) {
259 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed.
260 tracked_objects::ScopedTracker tracking_profile(
261 FROM_HERE_WITH_EXPLICIT_FUNCTION(
262 "431326 AppListViewDelegate::SetProfile"));
264 if (profile_ == new_profile)
265 return;
267 if (profile_) {
268 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed.
269 tracked_objects::ScopedTracker tracking_profile1(
270 FROM_HERE_WITH_EXPLICIT_FUNCTION(
271 "431326 AppListViewDelegate::SetProfile1"));
273 // Note: |search_resource_manager_| has a reference to |speech_ui_| so must
274 // be destroyed first.
275 search_resource_manager_.reset();
276 search_controller_.reset();
277 launcher_page_event_dispatcher_.reset();
278 custom_page_contents_.clear();
279 app_list::StartPageService* start_page_service =
280 app_list::StartPageService::Get(profile_);
281 if (start_page_service)
282 start_page_service->RemoveObserver(this);
283 #if defined(USE_ASH)
284 app_sync_ui_state_watcher_.reset();
285 #endif
286 model_ = NULL;
289 profile_ = new_profile;
290 if (!profile_) {
291 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed.
292 tracked_objects::ScopedTracker tracking_profile2(
293 FROM_HERE_WITH_EXPLICIT_FUNCTION(
294 "431326 AppListViewDelegate::SetProfile2"));
296 speech_ui_->SetSpeechRecognitionState(app_list::SPEECH_RECOGNITION_OFF,
297 false);
298 return;
301 // If we are in guest mode, the new profile should be an incognito profile.
302 // Otherwise, this may later hit a check (same condition as this one) in
303 // Browser::Browser when opening links in a browser window (see
304 // http://crbug.com/460437).
305 DCHECK(!profile_->IsGuestSession() || profile_->IsOffTheRecord())
306 << "Guest mode must use incognito profile";
308 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed.
309 tracked_objects::ScopedTracker tracking_profile3(
310 FROM_HERE_WITH_EXPLICIT_FUNCTION(
311 "431326 AppListViewDelegate::SetProfile3"));
312 template_url_service_observer_.RemoveAll();
313 if (app_list::switches::IsExperimentalAppListEnabled()) {
314 TemplateURLService* template_url_service =
315 TemplateURLServiceFactory::GetForProfile(profile_);
316 template_url_service_observer_.Add(template_url_service);
319 model_ = app_list::AppListSyncableServiceFactory::GetForProfile(profile_)
320 ->GetModel();
322 #if defined(USE_ASH)
323 app_sync_ui_state_watcher_.reset(new AppSyncUIStateWatcher(profile_, model_));
324 #endif
326 SetUpSearchUI();
327 SetUpProfileSwitcher();
328 SetUpCustomLauncherPages();
329 OnTemplateURLServiceChanged();
331 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed.
332 tracked_objects::ScopedTracker tracking_profile4(
333 FROM_HERE_WITH_EXPLICIT_FUNCTION(
334 "431326 AppListViewDelegate::SetProfile4"));
336 // Clear search query.
337 model_->search_box()->SetText(base::string16());
340 void AppListViewDelegate::SetUpSearchUI() {
341 app_list::StartPageService* start_page_service =
342 app_list::StartPageService::Get(profile_);
343 if (start_page_service)
344 start_page_service->AddObserver(this);
346 speech_ui_->SetSpeechRecognitionState(start_page_service
347 ? start_page_service->state()
348 : app_list::SPEECH_RECOGNITION_OFF,
349 false);
351 search_resource_manager_.reset(new app_list::SearchResourceManager(
352 profile_,
353 model_->search_box(),
354 speech_ui_.get()));
356 search_controller_ = CreateSearchController(profile_, model_, controller_);
359 void AppListViewDelegate::SetUpProfileSwitcher() {
360 // If a profile change is observed when there is no app list, there is nothing
361 // to update until SetProfile() calls this function again.
362 if (!profile_)
363 return;
365 // Don't populate the app list users if we are on the ash desktop.
366 chrome::HostDesktopType desktop = chrome::GetHostDesktopTypeForNativeWindow(
367 controller_->GetAppListWindow());
368 if (desktop == chrome::HOST_DESKTOP_TYPE_ASH)
369 return;
371 // Populate the app list users.
372 PopulateUsers(g_browser_process->profile_manager()->GetProfileInfoCache(),
373 profile_->GetPath(),
374 &users_);
376 FOR_EACH_OBSERVER(
377 app_list::AppListViewDelegateObserver, observers_, OnProfilesChanged());
380 void AppListViewDelegate::SetUpCustomLauncherPages() {
381 std::vector<GURL> custom_launcher_page_urls;
382 GetCustomLauncherPageUrls(profile_, &custom_launcher_page_urls);
383 if (custom_launcher_page_urls.empty())
384 return;
386 for (std::vector<GURL>::const_iterator it = custom_launcher_page_urls.begin();
387 it != custom_launcher_page_urls.end();
388 ++it) {
389 std::string extension_id = it->host();
390 apps::CustomLauncherPageContents* page_contents =
391 new apps::CustomLauncherPageContents(
392 scoped_ptr<extensions::AppDelegate>(
393 new ChromeAppDelegate(scoped_ptr<ScopedKeepAlive>())),
394 extension_id);
395 page_contents->Initialize(profile_, *it);
396 custom_page_contents_.push_back(page_contents);
399 std::string first_launcher_page_app_id = custom_launcher_page_urls[0].host();
400 const extensions::Extension* extension =
401 extensions::ExtensionRegistry::Get(profile_)
402 ->GetExtensionById(first_launcher_page_app_id,
403 extensions::ExtensionRegistry::EVERYTHING);
404 model_->set_custom_launcher_page_name(extension->name());
405 // Only the first custom launcher page gets events dispatched to it.
406 launcher_page_event_dispatcher_.reset(
407 new app_list::LauncherPageEventDispatcher(profile_,
408 first_launcher_page_app_id));
411 void AppListViewDelegate::OnHotwordStateChanged(bool started) {
412 if (started) {
413 if (speech_ui_->state() == app_list::SPEECH_RECOGNITION_READY) {
414 OnSpeechRecognitionStateChanged(
415 app_list::SPEECH_RECOGNITION_HOTWORD_LISTENING);
417 } else {
418 if (speech_ui_->state() == app_list::SPEECH_RECOGNITION_HOTWORD_LISTENING)
419 OnSpeechRecognitionStateChanged(app_list::SPEECH_RECOGNITION_READY);
423 void AppListViewDelegate::OnHotwordRecognized(
424 const scoped_refptr<content::SpeechRecognitionSessionPreamble>& preamble) {
425 DCHECK_EQ(app_list::SPEECH_RECOGNITION_HOTWORD_LISTENING,
426 speech_ui_->state());
427 ToggleSpeechRecognitionForHotword(preamble);
430 void AppListViewDelegate::SigninManagerCreated(SigninManagerBase* manager) {
431 scoped_observer_.Add(manager);
434 void AppListViewDelegate::SigninManagerShutdown(SigninManagerBase* manager) {
435 if (scoped_observer_.IsObserving(manager))
436 scoped_observer_.Remove(manager);
439 void AppListViewDelegate::GoogleSigninFailed(
440 const GoogleServiceAuthError& error) {
441 SetUpProfileSwitcher();
444 void AppListViewDelegate::GoogleSigninSucceeded(const std::string& account_id,
445 const std::string& username,
446 const std::string& password) {
447 SetUpProfileSwitcher();
450 void AppListViewDelegate::GoogleSignedOut(const std::string& account_id,
451 const std::string& username) {
452 SetUpProfileSwitcher();
455 void AppListViewDelegate::OnProfileAdded(const base::FilePath& profile_path) {
456 SetUpProfileSwitcher();
459 void AppListViewDelegate::OnProfileWasRemoved(
460 const base::FilePath& profile_path,
461 const base::string16& profile_name) {
462 SetUpProfileSwitcher();
465 void AppListViewDelegate::OnProfileNameChanged(
466 const base::FilePath& profile_path,
467 const base::string16& old_profile_name) {
468 SetUpProfileSwitcher();
471 bool AppListViewDelegate::ForceNativeDesktop() const {
472 return controller_->ForceNativeDesktop();
475 void AppListViewDelegate::SetProfileByPath(const base::FilePath& profile_path) {
476 DCHECK(model_);
477 // The profile must be loaded before this is called.
478 SetProfile(
479 g_browser_process->profile_manager()->GetProfileByPath(profile_path));
482 app_list::AppListModel* AppListViewDelegate::GetModel() {
483 return model_;
486 app_list::SpeechUIModel* AppListViewDelegate::GetSpeechUI() {
487 return speech_ui_.get();
490 void AppListViewDelegate::GetShortcutPathForApp(
491 const std::string& app_id,
492 const base::Callback<void(const base::FilePath&)>& callback) {
493 #if defined(OS_WIN)
494 const extensions::Extension* extension =
495 extensions::ExtensionRegistry::Get(profile_)->GetExtensionById(
496 app_id, extensions::ExtensionRegistry::EVERYTHING);
497 if (!extension) {
498 callback.Run(base::FilePath());
499 return;
502 base::FilePath app_data_dir(
503 web_app::GetWebAppDataDirectory(profile_->GetPath(),
504 extension->id(),
505 GURL()));
507 web_app::GetShortcutInfoForApp(
508 extension,
509 profile_,
510 base::Bind(CreateShortcutInWebAppDir, app_data_dir, callback));
511 #else
512 callback.Run(base::FilePath());
513 #endif
516 void AppListViewDelegate::StartSearch() {
517 if (search_controller_) {
518 search_controller_->Start(is_voice_query_);
519 controller_->OnSearchStarted();
523 void AppListViewDelegate::StopSearch() {
524 if (search_controller_)
525 search_controller_->Stop();
528 void AppListViewDelegate::OpenSearchResult(
529 app_list::SearchResult* result,
530 bool auto_launch,
531 int event_flags) {
532 if (auto_launch)
533 base::RecordAction(base::UserMetricsAction("AppList_AutoLaunched"));
534 search_controller_->OpenResult(result, event_flags);
535 is_voice_query_ = false;
538 void AppListViewDelegate::InvokeSearchResultAction(
539 app_list::SearchResult* result,
540 int action_index,
541 int event_flags) {
542 search_controller_->InvokeResultAction(result, action_index, event_flags);
545 base::TimeDelta AppListViewDelegate::GetAutoLaunchTimeout() {
546 return auto_launch_timeout_;
549 void AppListViewDelegate::AutoLaunchCanceled() {
550 if (is_voice_query_) {
551 base::RecordAction(base::UserMetricsAction("AppList_AutoLaunchCanceled"));
552 // Cancelling the auto launch means we are no longer in a voice query.
553 is_voice_query_ = false;
555 auto_launch_timeout_ = base::TimeDelta();
558 void AppListViewDelegate::ViewInitialized() {
559 app_list::StartPageService* service =
560 app_list::StartPageService::Get(profile_);
561 if (service) {
562 service->AppListShown();
563 if (service->HotwordEnabled()) {
564 HotwordService* hotword_service =
565 HotwordServiceFactory::GetForProfile(profile_);
566 if (hotword_service)
567 hotword_service->RequestHotwordSession(this);
569 OnHotwordStateChanged(service->HotwordEnabled());
573 void AppListViewDelegate::Dismiss() {
574 controller_->DismissView();
577 void AppListViewDelegate::ViewClosing() {
578 controller_->ViewClosing();
580 if (!profile_)
581 return;
583 app_list::StartPageService* service =
584 app_list::StartPageService::Get(profile_);
585 if (service) {
586 service->AppListHidden();
587 if (service->HotwordEnabled()) {
588 HotwordService* hotword_service =
589 HotwordServiceFactory::GetForProfile(profile_);
590 if (hotword_service) {
591 hotword_service->StopHotwordSession(this);
593 // If we're in always-on mode, we always want to restart hotwording
594 // after closing the launcher window. So, in always-on mode, hotwording
595 // is stopped, and then started again right away. Note that hotwording
596 // may already be stopped. The call to StopHotwordSession() above both
597 // explicitly stops hotwording, if it's running, and clears the
598 // association between the hotword service and |this|. When starting up
599 // hotwording, pass nullptr as the client so that hotword triggers cause
600 // the launcher to open.
601 // TODO(amistry): This only works on ChromeOS since Chrome hides the
602 // launcher instead of destroying it. Make this work on Chrome.
603 if (hotword_service->IsAlwaysOnEnabled())
604 hotword_service->RequestHotwordSession(nullptr);
610 gfx::ImageSkia AppListViewDelegate::GetWindowIcon() {
611 return controller_->GetWindowIcon();
614 void AppListViewDelegate::OpenSettings() {
615 const extensions::Extension* extension =
616 extensions::ExtensionRegistry::Get(profile_)->GetExtensionById(
617 extension_misc::kSettingsAppId,
618 extensions::ExtensionRegistry::EVERYTHING);
619 DCHECK(extension);
620 controller_->ActivateApp(profile_,
621 extension,
622 AppListControllerDelegate::LAUNCH_FROM_UNKNOWN,
626 void AppListViewDelegate::OpenHelp() {
627 chrome::HostDesktopType desktop = chrome::GetHostDesktopTypeForNativeWindow(
628 controller_->GetAppListWindow());
629 chrome::ScopedTabbedBrowserDisplayer displayer(profile_, desktop);
630 content::OpenURLParams params(GURL(chrome::kAppLauncherHelpURL),
631 content::Referrer(),
632 NEW_FOREGROUND_TAB,
633 ui::PAGE_TRANSITION_LINK,
634 false);
635 displayer.browser()->OpenURL(params);
638 void AppListViewDelegate::OpenFeedback() {
639 chrome::HostDesktopType desktop = chrome::GetHostDesktopTypeForNativeWindow(
640 controller_->GetAppListWindow());
641 Browser* browser = chrome::FindTabbedBrowser(profile_, false, desktop);
642 chrome::ShowFeedbackPage(browser, std::string(),
643 chrome::kAppLauncherCategoryTag);
646 void AppListViewDelegate::ToggleSpeechRecognition() {
647 ToggleSpeechRecognitionForHotword(nullptr);
650 void AppListViewDelegate::ToggleSpeechRecognitionForHotword(
651 const scoped_refptr<content::SpeechRecognitionSessionPreamble>& preamble) {
652 app_list::StartPageService* service =
653 app_list::StartPageService::Get(profile_);
655 // Don't start the recognizer or stop the hotword session if there is a
656 // network error. Show the network error message instead.
657 if (service) {
658 if (service->state() == app_list::SPEECH_RECOGNITION_NETWORK_ERROR) {
659 speech_ui_->SetSpeechRecognitionState(
660 app_list::SPEECH_RECOGNITION_NETWORK_ERROR, true);
661 return;
663 service->ToggleSpeechRecognition(preamble);
666 // With the new hotword extension, stop the hotword session. With the launcher
667 // and NTP, this is unnecessary since the hotwording is implicitly stopped.
668 // However, for always on, hotword triggering launches the launcher which
669 // starts a session and hence starts the hotword detector. This results in the
670 // hotword detector and the speech-to-text engine running in parallel, which
671 // will conflict with each other (i.e. saying 'Ok Google' twice in a row
672 // should cause a search to happen for 'Ok Google', not two hotword triggers).
673 // To get around this, always stop the session when switching to speech
674 // recognition.
675 if (service && service->HotwordEnabled()) {
676 HotwordService* hotword_service =
677 HotwordServiceFactory::GetForProfile(profile_);
678 if (hotword_service)
679 hotword_service->StopHotwordSession(this);
683 void AppListViewDelegate::ShowForProfileByPath(
684 const base::FilePath& profile_path) {
685 controller_->ShowForProfileByPath(profile_path);
688 void AppListViewDelegate::OnSpeechResult(const base::string16& result,
689 bool is_final) {
690 speech_ui_->SetSpeechResult(result, is_final);
691 if (is_final) {
692 auto_launch_timeout_ = base::TimeDelta::FromMilliseconds(
693 kAutoLaunchDefaultTimeoutMilliSec);
694 is_voice_query_ = true;
695 model_->search_box()->SetText(result);
699 void AppListViewDelegate::OnSpeechSoundLevelChanged(int16 level) {
700 speech_ui_->UpdateSoundLevel(level);
703 void AppListViewDelegate::OnSpeechRecognitionStateChanged(
704 app_list::SpeechRecognitionState new_state) {
705 speech_ui_->SetSpeechRecognitionState(new_state, false);
707 app_list::StartPageService* service =
708 app_list::StartPageService::Get(profile_);
709 // With the new hotword extension, we need to re-request hotwording after
710 // speech recognition has stopped. Do not request hotwording after the app
711 // list has already closed.
712 if (new_state == app_list::SPEECH_RECOGNITION_READY &&
713 service && service->HotwordEnabled() &&
714 controller_->GetAppListWindow()) {
715 HotwordService* hotword_service =
716 HotwordServiceFactory::GetForProfile(profile_);
717 if (hotword_service) {
718 hotword_service->RequestHotwordSession(this);
723 #if defined(TOOLKIT_VIEWS)
724 views::View* AppListViewDelegate::CreateStartPageWebView(
725 const gfx::Size& size) {
726 app_list::StartPageService* service =
727 app_list::StartPageService::Get(profile_);
728 if (!service)
729 return NULL;
731 service->LoadContentsIfNeeded();
733 content::WebContents* web_contents = service->GetStartPageContents();
734 if (!web_contents)
735 return NULL;
737 DCHECK_EQ(profile_, web_contents->GetBrowserContext());
738 views::WebView* web_view = new views::WebView(
739 web_contents->GetBrowserContext());
740 web_view->SetPreferredSize(size);
741 web_view->SetResizeBackgroundColor(SK_ColorTRANSPARENT);
742 web_view->SetWebContents(web_contents);
743 return web_view;
746 std::vector<views::View*> AppListViewDelegate::CreateCustomPageWebViews(
747 const gfx::Size& size) {
748 std::vector<views::View*> web_views;
750 for (ScopedVector<apps::CustomLauncherPageContents>::const_iterator it =
751 custom_page_contents_.begin();
752 it != custom_page_contents_.end();
753 ++it) {
754 content::WebContents* web_contents = (*it)->web_contents();
756 // The web contents should belong to the current profile.
757 DCHECK_EQ(profile_, web_contents->GetBrowserContext());
759 // Make the webview transparent.
760 web_contents->GetRenderViewHost()->GetView()->SetBackgroundColor(
761 SK_ColorTRANSPARENT);
763 views::WebView* web_view =
764 new views::WebView(web_contents->GetBrowserContext());
765 web_view->SetPreferredSize(size);
766 web_view->SetResizeBackgroundColor(SK_ColorTRANSPARENT);
767 web_view->SetWebContents(web_contents);
768 web_views.push_back(web_view);
771 return web_views;
774 void AppListViewDelegate::CustomLauncherPageAnimationChanged(double progress) {
775 if (launcher_page_event_dispatcher_)
776 launcher_page_event_dispatcher_->ProgressChanged(progress);
779 void AppListViewDelegate::CustomLauncherPagePopSubpage() {
780 if (launcher_page_event_dispatcher_)
781 launcher_page_event_dispatcher_->PopSubpage();
783 #endif
785 bool AppListViewDelegate::IsSpeechRecognitionEnabled() {
786 app_list::StartPageService* service =
787 app_list::StartPageService::Get(profile_);
788 return service && service->GetSpeechRecognitionContents();
791 const app_list::AppListViewDelegate::Users&
792 AppListViewDelegate::GetUsers() const {
793 return users_;
796 bool AppListViewDelegate::ShouldCenterWindow() const {
797 // Some ChromeOS devices (those that support TouchView mode) turn this flag on
798 // by default, which ensures that the app list is consistently centered on
799 // those devices. This avoids having the app list change shape and position as
800 // the user enters and exits TouchView mode.
801 if (app_list::switches::IsCenteredAppListEnabled())
802 return true;
804 // keyboard depends upon Aura.
805 #if defined(USE_AURA)
806 // If the virtual keyboard is enabled, use the new app list position. The old
807 // position is too tall, and doesn't fit in the left-over screen space.
808 if (keyboard::IsKeyboardEnabled())
809 return true;
810 #endif
812 return false;
815 void AppListViewDelegate::AddObserver(
816 app_list::AppListViewDelegateObserver* observer) {
817 observers_.AddObserver(observer);
820 void AppListViewDelegate::RemoveObserver(
821 app_list::AppListViewDelegateObserver* observer) {
822 observers_.RemoveObserver(observer);
825 void AppListViewDelegate::OnTemplateURLServiceChanged() {
826 if (!app_list::switches::IsExperimentalAppListEnabled())
827 return;
829 TemplateURLService* template_url_service =
830 TemplateURLServiceFactory::GetForProfile(profile_);
831 const TemplateURL* default_provider =
832 template_url_service->GetDefaultSearchProvider();
833 bool is_google =
834 TemplateURLPrepopulateData::GetEngineType(
835 *default_provider, template_url_service->search_terms_data()) ==
836 SEARCH_ENGINE_GOOGLE;
838 model_->SetSearchEngineIsGoogle(is_google);
840 app_list::StartPageService* start_page_service =
841 app_list::StartPageService::Get(profile_);
842 if (start_page_service)
843 start_page_service->set_search_engine_is_google(is_google);
846 void AppListViewDelegate::Observe(int type,
847 const content::NotificationSource& source,
848 const content::NotificationDetails& details) {
849 switch (type) {
850 case chrome::NOTIFICATION_APP_TERMINATING:
851 FOR_EACH_OBSERVER(
852 app_list::AppListViewDelegateObserver, observers_, OnShutdown());
854 SetProfile(NULL); // Ensures launcher page web contents are torn down.
856 // SigninManagerFactory is not a leaky singleton (unlike this class), and
857 // its destructor will check that it has no remaining observers.
858 scoped_observer_.RemoveAll();
859 SigninManagerFactory::GetInstance()->RemoveObserver(this);
860 break;
861 default:
862 NOTREACHED();