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"
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"
73 #include "ui/keyboard/keyboard_util.h"
77 #include "chrome/browser/ui/ash/app_list/app_sync_ui_state_watcher.h"
81 #include "chrome/browser/web_applications/web_app_win.h"
86 const char kAppLauncherCategoryTag
[] = "AppLauncher";
91 const int kAutoLaunchDefaultTimeoutMilliSec
= 50;
94 void CreateShortcutInWebAppDir(
95 const base::FilePath
& app_data_dir
,
96 base::Callback
<void(const base::FilePath
&)> callback
,
97 scoped_ptr
<web_app::ShortcutInfo
> info
) {
98 content::BrowserThread::PostTaskAndReplyWithResult(
99 content::BrowserThread::FILE, FROM_HERE
,
100 base::Bind(web_app::CreateShortcutInWebAppDir
, app_data_dir
,
101 base::Passed(&info
)),
106 void PopulateUsers(const ProfileInfoCache
& profile_info
,
107 const base::FilePath
& active_profile_path
,
108 app_list::AppListViewDelegate::Users
* users
) {
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
);
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
);
145 profile_prefs
->HasPrefPath(prefs::kGoogleNowLauncherEnabled
) &&
146 !profile_prefs
->GetBoolean(prefs::kGoogleNowLauncherEnabled
)) {
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();
158 const extensions::Extension
* extension
= it
->get();
159 extensions::LauncherPageInfo
* info
=
160 extensions::LauncherPageHandler::GetInfo(extension
);
164 urls
->push_back(extension
->GetResourceURL(info
->page
));
170 AppListViewDelegate::AppListViewDelegate(AppListControllerDelegate
* controller
)
171 : controller_(controller
),
174 is_voice_query_(false),
175 template_url_service_observer_(this),
176 scoped_observer_(this) {
178 // The SigninManagerFactor and the SigninManagers are observed to keep the
179 // profile switcher menu up to date, with the correct list of profiles and the
180 // correct email address (or none for signed out users) for each.
181 SigninManagerFactory::GetInstance()->AddObserver(this);
183 // Start observing all already-created SigninManagers.
184 ProfileManager
* profile_manager
= g_browser_process
->profile_manager();
185 std::vector
<Profile
*> profiles
= profile_manager
->GetLoadedProfiles();
187 for (std::vector
<Profile
*>::iterator i
= profiles
.begin();
190 SigninManagerBase
* manager
=
191 SigninManagerFactory::GetForProfileIfExists(*i
);
193 DCHECK(!scoped_observer_
.IsObserving(manager
));
194 scoped_observer_
.Add(manager
);
198 profile_manager
->GetProfileInfoCache().AddObserver(this);
199 speech_ui_
.reset(new app_list::SpeechUIModel
);
201 #if defined(GOOGLE_CHROME_BUILD)
202 gfx::ImageSkia
* image
;
204 // TODO(tapted): Remove ScopedTracker below once crbug.com/431326 is fixed.
205 tracked_objects::ScopedTracker
tracking_profile(
206 FROM_HERE_WITH_EXPLICIT_FUNCTION("431326 GetImageSkiaNamed()"));
207 image
= ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
208 IDR_APP_LIST_GOOGLE_LOGO_VOICE_SEARCH
);
211 speech_ui_
->set_logo(*image
);
215 chrome::NOTIFICATION_APP_TERMINATING
,
216 content::NotificationService::AllSources());
219 AppListViewDelegate::~AppListViewDelegate() {
220 // Note that the destructor is not always called. E.g. on Mac, this is owned
221 // by a leaky singleton. Essential shutdown work must be done by observing
222 // chrome::NOTIFICATION_APP_TERMINATING.
224 g_browser_process
->profile_manager()->GetProfileInfoCache().RemoveObserver(
227 SigninManagerFactory
* factory
= SigninManagerFactory::GetInstance();
229 factory
->RemoveObserver(this);
232 void AppListViewDelegate::SetProfile(Profile
* new_profile
) {
233 if (profile_
== new_profile
)
237 // Note: |search_resource_manager_| has a reference to |speech_ui_| so must
238 // be destroyed first.
239 search_resource_manager_
.reset();
240 search_controller_
.reset();
241 launcher_page_event_dispatcher_
.reset();
242 custom_page_contents_
.clear();
243 app_list::StartPageService
* start_page_service
=
244 app_list::StartPageService::Get(profile_
);
245 if (start_page_service
)
246 start_page_service
->RemoveObserver(this);
248 app_sync_ui_state_watcher_
.reset();
253 profile_
= new_profile
;
255 speech_ui_
->SetSpeechRecognitionState(app_list::SPEECH_RECOGNITION_OFF
,
260 // If we are in guest mode, the new profile should be an incognito profile.
261 // Otherwise, this may later hit a check (same condition as this one) in
262 // Browser::Browser when opening links in a browser window (see
263 // http://crbug.com/460437).
264 DCHECK(!profile_
->IsGuestSession() || profile_
->IsOffTheRecord())
265 << "Guest mode must use incognito profile";
268 // TODO(tapted): Remove ScopedTracker below once crbug.com/431326 is fixed.
269 tracked_objects::ScopedTracker
tracking_profile(
270 FROM_HERE_WITH_EXPLICIT_FUNCTION(
271 "431326 AppListViewDelegate TemplateURL etc."));
272 template_url_service_observer_
.RemoveAll();
273 if (app_list::switches::IsExperimentalAppListEnabled()) {
274 TemplateURLService
* template_url_service
=
275 TemplateURLServiceFactory::GetForProfile(profile_
);
276 template_url_service_observer_
.Add(template_url_service
);
279 model_
= app_list::AppListSyncableServiceFactory::GetForProfile(profile_
)
283 app_sync_ui_state_watcher_
.reset(
284 new AppSyncUIStateWatcher(profile_
, model_
));
288 SetUpProfileSwitcher();
289 SetUpCustomLauncherPages();
290 OnTemplateURLServiceChanged();
293 // Clear search query.
294 model_
->search_box()->SetText(base::string16());
297 void AppListViewDelegate::SetUpSearchUI() {
298 app_list::StartPageService
* start_page_service
=
299 app_list::StartPageService::Get(profile_
);
300 if (start_page_service
)
301 start_page_service
->AddObserver(this);
303 speech_ui_
->SetSpeechRecognitionState(start_page_service
304 ? start_page_service
->state()
305 : app_list::SPEECH_RECOGNITION_OFF
,
308 search_resource_manager_
.reset(new app_list::SearchResourceManager(
310 model_
->search_box(),
313 search_controller_
= CreateSearchController(profile_
, model_
, controller_
);
316 void AppListViewDelegate::SetUpProfileSwitcher() {
317 // If a profile change is observed when there is no app list, there is nothing
318 // to update until SetProfile() calls this function again.
322 // Don't populate the app list users if we are on the ash desktop.
323 chrome::HostDesktopType desktop
= chrome::GetHostDesktopTypeForNativeWindow(
324 controller_
->GetAppListWindow());
325 if (desktop
== chrome::HOST_DESKTOP_TYPE_ASH
)
328 // Populate the app list users.
329 PopulateUsers(g_browser_process
->profile_manager()->GetProfileInfoCache(),
334 app_list::AppListViewDelegateObserver
, observers_
, OnProfilesChanged());
337 void AppListViewDelegate::SetUpCustomLauncherPages() {
338 std::vector
<GURL
> custom_launcher_page_urls
;
339 GetCustomLauncherPageUrls(profile_
, &custom_launcher_page_urls
);
340 if (custom_launcher_page_urls
.empty())
343 for (std::vector
<GURL
>::const_iterator it
= custom_launcher_page_urls
.begin();
344 it
!= custom_launcher_page_urls
.end();
346 std::string extension_id
= it
->host();
347 apps::CustomLauncherPageContents
* page_contents
=
348 new apps::CustomLauncherPageContents(
349 scoped_ptr
<extensions::AppDelegate
>(
350 new ChromeAppDelegate(scoped_ptr
<ScopedKeepAlive
>())),
352 page_contents
->Initialize(profile_
, *it
);
353 custom_page_contents_
.push_back(page_contents
);
356 std::string first_launcher_page_app_id
= custom_launcher_page_urls
[0].host();
357 const extensions::Extension
* extension
=
358 extensions::ExtensionRegistry::Get(profile_
)
359 ->GetExtensionById(first_launcher_page_app_id
,
360 extensions::ExtensionRegistry::EVERYTHING
);
361 model_
->set_custom_launcher_page_name(extension
->name());
362 // Only the first custom launcher page gets events dispatched to it.
363 launcher_page_event_dispatcher_
.reset(
364 new app_list::LauncherPageEventDispatcher(profile_
,
365 first_launcher_page_app_id
));
368 void AppListViewDelegate::OnHotwordStateChanged(bool started
) {
370 if (speech_ui_
->state() == app_list::SPEECH_RECOGNITION_READY
) {
371 OnSpeechRecognitionStateChanged(
372 app_list::SPEECH_RECOGNITION_HOTWORD_LISTENING
);
375 if (speech_ui_
->state() == app_list::SPEECH_RECOGNITION_HOTWORD_LISTENING
)
376 OnSpeechRecognitionStateChanged(app_list::SPEECH_RECOGNITION_READY
);
380 void AppListViewDelegate::OnHotwordRecognized(
381 const scoped_refptr
<content::SpeechRecognitionSessionPreamble
>& preamble
) {
382 DCHECK_EQ(app_list::SPEECH_RECOGNITION_HOTWORD_LISTENING
,
383 speech_ui_
->state());
384 ToggleSpeechRecognitionForHotword(preamble
);
387 void AppListViewDelegate::SigninManagerCreated(SigninManagerBase
* manager
) {
388 scoped_observer_
.Add(manager
);
391 void AppListViewDelegate::SigninManagerShutdown(SigninManagerBase
* manager
) {
392 if (scoped_observer_
.IsObserving(manager
))
393 scoped_observer_
.Remove(manager
);
396 void AppListViewDelegate::GoogleSigninFailed(
397 const GoogleServiceAuthError
& error
) {
398 SetUpProfileSwitcher();
401 void AppListViewDelegate::GoogleSigninSucceeded(const std::string
& account_id
,
402 const std::string
& username
,
403 const std::string
& password
) {
404 SetUpProfileSwitcher();
407 void AppListViewDelegate::GoogleSignedOut(const std::string
& account_id
,
408 const std::string
& username
) {
409 SetUpProfileSwitcher();
412 void AppListViewDelegate::OnProfileAdded(const base::FilePath
& profile_path
) {
413 SetUpProfileSwitcher();
416 void AppListViewDelegate::OnProfileWasRemoved(
417 const base::FilePath
& profile_path
,
418 const base::string16
& profile_name
) {
419 SetUpProfileSwitcher();
422 void AppListViewDelegate::OnProfileNameChanged(
423 const base::FilePath
& profile_path
,
424 const base::string16
& old_profile_name
) {
425 SetUpProfileSwitcher();
428 bool AppListViewDelegate::ForceNativeDesktop() const {
429 return controller_
->ForceNativeDesktop();
432 void AppListViewDelegate::SetProfileByPath(const base::FilePath
& profile_path
) {
434 // The profile must be loaded before this is called.
436 g_browser_process
->profile_manager()->GetProfileByPath(profile_path
));
439 app_list::AppListModel
* AppListViewDelegate::GetModel() {
443 app_list::SpeechUIModel
* AppListViewDelegate::GetSpeechUI() {
444 return speech_ui_
.get();
447 void AppListViewDelegate::GetShortcutPathForApp(
448 const std::string
& app_id
,
449 const base::Callback
<void(const base::FilePath
&)>& callback
) {
451 const extensions::Extension
* extension
=
452 extensions::ExtensionRegistry::Get(profile_
)->GetExtensionById(
453 app_id
, extensions::ExtensionRegistry::EVERYTHING
);
455 callback
.Run(base::FilePath());
459 base::FilePath
app_data_dir(
460 web_app::GetWebAppDataDirectory(profile_
->GetPath(),
464 web_app::GetShortcutInfoForApp(
467 base::Bind(CreateShortcutInWebAppDir
, app_data_dir
, callback
));
469 callback
.Run(base::FilePath());
473 void AppListViewDelegate::StartSearch() {
474 if (search_controller_
) {
475 search_controller_
->Start(is_voice_query_
);
476 controller_
->OnSearchStarted();
480 void AppListViewDelegate::StopSearch() {
481 if (search_controller_
)
482 search_controller_
->Stop();
485 void AppListViewDelegate::OpenSearchResult(
486 app_list::SearchResult
* result
,
490 base::RecordAction(base::UserMetricsAction("AppList_AutoLaunched"));
491 search_controller_
->OpenResult(result
, event_flags
);
492 is_voice_query_
= false;
495 void AppListViewDelegate::InvokeSearchResultAction(
496 app_list::SearchResult
* result
,
499 search_controller_
->InvokeResultAction(result
, action_index
, event_flags
);
502 base::TimeDelta
AppListViewDelegate::GetAutoLaunchTimeout() {
503 return auto_launch_timeout_
;
506 void AppListViewDelegate::AutoLaunchCanceled() {
507 if (is_voice_query_
) {
508 base::RecordAction(base::UserMetricsAction("AppList_AutoLaunchCanceled"));
509 // Cancelling the auto launch means we are no longer in a voice query.
510 is_voice_query_
= false;
512 auto_launch_timeout_
= base::TimeDelta();
515 void AppListViewDelegate::ViewInitialized() {
516 app_list::StartPageService
* service
=
517 app_list::StartPageService::Get(profile_
);
519 service
->AppListShown();
520 if (service
->HotwordEnabled()) {
521 HotwordService
* hotword_service
=
522 HotwordServiceFactory::GetForProfile(profile_
);
524 hotword_service
->RequestHotwordSession(this);
526 OnHotwordStateChanged(service
->HotwordEnabled());
530 void AppListViewDelegate::Dismiss() {
531 controller_
->DismissView();
534 void AppListViewDelegate::ViewClosing() {
535 controller_
->ViewClosing();
540 app_list::StartPageService
* service
=
541 app_list::StartPageService::Get(profile_
);
543 service
->AppListHidden();
544 if (service
->HotwordEnabled()) {
545 HotwordService
* hotword_service
=
546 HotwordServiceFactory::GetForProfile(profile_
);
547 if (hotword_service
) {
548 hotword_service
->StopHotwordSession(this);
550 // If we're in always-on mode, we always want to restart hotwording
551 // after closing the launcher window. So, in always-on mode, hotwording
552 // is stopped, and then started again right away. Note that hotwording
553 // may already be stopped. The call to StopHotwordSession() above both
554 // explicitly stops hotwording, if it's running, and clears the
555 // association between the hotword service and |this|. When starting up
556 // hotwording, pass nullptr as the client so that hotword triggers cause
557 // the launcher to open.
558 // TODO(amistry): This only works on ChromeOS since Chrome hides the
559 // launcher instead of destroying it. Make this work on Chrome.
560 if (hotword_service
->IsAlwaysOnEnabled())
561 hotword_service
->RequestHotwordSession(nullptr);
567 gfx::ImageSkia
AppListViewDelegate::GetWindowIcon() {
568 return controller_
->GetWindowIcon();
571 void AppListViewDelegate::OpenSettings() {
572 const extensions::Extension
* extension
=
573 extensions::ExtensionRegistry::Get(profile_
)->GetExtensionById(
574 extension_misc::kSettingsAppId
,
575 extensions::ExtensionRegistry::EVERYTHING
);
577 controller_
->ActivateApp(profile_
,
579 AppListControllerDelegate::LAUNCH_FROM_UNKNOWN
,
583 void AppListViewDelegate::OpenHelp() {
584 chrome::HostDesktopType desktop
= chrome::GetHostDesktopTypeForNativeWindow(
585 controller_
->GetAppListWindow());
586 chrome::ScopedTabbedBrowserDisplayer
displayer(profile_
, desktop
);
587 content::OpenURLParams
params(GURL(chrome::kAppLauncherHelpURL
),
590 ui::PAGE_TRANSITION_LINK
,
592 displayer
.browser()->OpenURL(params
);
595 void AppListViewDelegate::OpenFeedback() {
596 chrome::HostDesktopType desktop
= chrome::GetHostDesktopTypeForNativeWindow(
597 controller_
->GetAppListWindow());
598 Browser
* browser
= chrome::FindTabbedBrowser(profile_
, false, desktop
);
599 chrome::ShowFeedbackPage(browser
, std::string(),
600 chrome::kAppLauncherCategoryTag
);
603 void AppListViewDelegate::ToggleSpeechRecognition() {
604 ToggleSpeechRecognitionForHotword(nullptr);
607 void AppListViewDelegate::ToggleSpeechRecognitionForHotword(
608 const scoped_refptr
<content::SpeechRecognitionSessionPreamble
>& preamble
) {
609 app_list::StartPageService
* service
=
610 app_list::StartPageService::Get(profile_
);
612 // Don't start the recognizer or stop the hotword session if there is a
613 // network error. Show the network error message instead.
615 if (service
->state() == app_list::SPEECH_RECOGNITION_NETWORK_ERROR
) {
616 speech_ui_
->SetSpeechRecognitionState(
617 app_list::SPEECH_RECOGNITION_NETWORK_ERROR
, true);
620 service
->ToggleSpeechRecognition(preamble
);
623 // With the new hotword extension, stop the hotword session. With the launcher
624 // and NTP, this is unnecessary since the hotwording is implicitly stopped.
625 // However, for always on, hotword triggering launches the launcher which
626 // starts a session and hence starts the hotword detector. This results in the
627 // hotword detector and the speech-to-text engine running in parallel, which
628 // will conflict with each other (i.e. saying 'Ok Google' twice in a row
629 // should cause a search to happen for 'Ok Google', not two hotword triggers).
630 // To get around this, always stop the session when switching to speech
632 if (service
&& service
->HotwordEnabled()) {
633 HotwordService
* hotword_service
=
634 HotwordServiceFactory::GetForProfile(profile_
);
636 hotword_service
->StopHotwordSession(this);
640 void AppListViewDelegate::ShowForProfileByPath(
641 const base::FilePath
& profile_path
) {
642 controller_
->ShowForProfileByPath(profile_path
);
645 void AppListViewDelegate::OnSpeechResult(const base::string16
& result
,
647 speech_ui_
->SetSpeechResult(result
, is_final
);
649 auto_launch_timeout_
= base::TimeDelta::FromMilliseconds(
650 kAutoLaunchDefaultTimeoutMilliSec
);
651 is_voice_query_
= true;
652 model_
->search_box()->SetText(result
);
656 void AppListViewDelegate::OnSpeechSoundLevelChanged(int16 level
) {
657 speech_ui_
->UpdateSoundLevel(level
);
660 void AppListViewDelegate::OnSpeechRecognitionStateChanged(
661 app_list::SpeechRecognitionState new_state
) {
662 speech_ui_
->SetSpeechRecognitionState(new_state
, false);
664 app_list::StartPageService
* service
=
665 app_list::StartPageService::Get(profile_
);
666 // With the new hotword extension, we need to re-request hotwording after
667 // speech recognition has stopped. Do not request hotwording after the app
668 // list has already closed.
669 if (new_state
== app_list::SPEECH_RECOGNITION_READY
&&
670 service
&& service
->HotwordEnabled() &&
671 controller_
->GetAppListWindow()) {
672 HotwordService
* hotword_service
=
673 HotwordServiceFactory::GetForProfile(profile_
);
674 if (hotword_service
) {
675 hotword_service
->RequestHotwordSession(this);
680 #if defined(TOOLKIT_VIEWS)
681 views::View
* AppListViewDelegate::CreateStartPageWebView(
682 const gfx::Size
& size
) {
683 app_list::StartPageService
* service
=
684 app_list::StartPageService::Get(profile_
);
688 service
->LoadContentsIfNeeded();
690 content::WebContents
* web_contents
= service
->GetStartPageContents();
694 DCHECK_EQ(profile_
, web_contents
->GetBrowserContext());
695 views::WebView
* web_view
= new views::WebView(
696 web_contents
->GetBrowserContext());
697 web_view
->SetPreferredSize(size
);
698 web_view
->SetResizeBackgroundColor(SK_ColorTRANSPARENT
);
699 web_view
->SetWebContents(web_contents
);
703 std::vector
<views::View
*> AppListViewDelegate::CreateCustomPageWebViews(
704 const gfx::Size
& size
) {
705 std::vector
<views::View
*> web_views
;
707 for (ScopedVector
<apps::CustomLauncherPageContents
>::const_iterator it
=
708 custom_page_contents_
.begin();
709 it
!= custom_page_contents_
.end();
711 content::WebContents
* web_contents
= (*it
)->web_contents();
713 // The web contents should belong to the current profile.
714 DCHECK_EQ(profile_
, web_contents
->GetBrowserContext());
716 // Make the webview transparent.
717 content::RenderWidgetHostView
* render_view_host_view
=
718 web_contents
->GetRenderViewHost()->GetView();
719 // The RenderWidgetHostView may be null if the renderer has crashed.
720 if (render_view_host_view
)
721 render_view_host_view
->SetBackgroundColor(SK_ColorTRANSPARENT
);
723 views::WebView
* web_view
=
724 new views::WebView(web_contents
->GetBrowserContext());
725 web_view
->SetPreferredSize(size
);
726 web_view
->SetResizeBackgroundColor(SK_ColorTRANSPARENT
);
727 web_view
->SetWebContents(web_contents
);
728 web_views
.push_back(web_view
);
734 void AppListViewDelegate::CustomLauncherPageAnimationChanged(double progress
) {
735 if (launcher_page_event_dispatcher_
)
736 launcher_page_event_dispatcher_
->ProgressChanged(progress
);
739 void AppListViewDelegate::CustomLauncherPagePopSubpage() {
740 if (launcher_page_event_dispatcher_
)
741 launcher_page_event_dispatcher_
->PopSubpage();
745 bool AppListViewDelegate::IsSpeechRecognitionEnabled() {
746 app_list::StartPageService
* service
=
747 app_list::StartPageService::Get(profile_
);
748 return service
&& service
->GetSpeechRecognitionContents();
751 const app_list::AppListViewDelegate::Users
&
752 AppListViewDelegate::GetUsers() const {
756 bool AppListViewDelegate::ShouldCenterWindow() const {
757 // Some ChromeOS devices (those that support TouchView mode) turn this flag on
758 // by default, which ensures that the app list is consistently centered on
759 // those devices. This avoids having the app list change shape and position as
760 // the user enters and exits TouchView mode.
761 if (app_list::switches::IsCenteredAppListEnabled())
764 // keyboard depends upon Aura.
765 #if defined(USE_AURA)
766 // If the virtual keyboard is enabled, use the new app list position. The old
767 // position is too tall, and doesn't fit in the left-over screen space.
768 if (keyboard::IsKeyboardEnabled())
775 void AppListViewDelegate::AddObserver(
776 app_list::AppListViewDelegateObserver
* observer
) {
777 observers_
.AddObserver(observer
);
780 void AppListViewDelegate::RemoveObserver(
781 app_list::AppListViewDelegateObserver
* observer
) {
782 observers_
.RemoveObserver(observer
);
785 void AppListViewDelegate::OnTemplateURLServiceChanged() {
786 if (!app_list::switches::IsExperimentalAppListEnabled())
789 TemplateURLService
* template_url_service
=
790 TemplateURLServiceFactory::GetForProfile(profile_
);
791 const TemplateURL
* default_provider
=
792 template_url_service
->GetDefaultSearchProvider();
794 TemplateURLPrepopulateData::GetEngineType(
795 *default_provider
, template_url_service
->search_terms_data()) ==
796 SEARCH_ENGINE_GOOGLE
;
798 model_
->SetSearchEngineIsGoogle(is_google
);
800 app_list::StartPageService
* start_page_service
=
801 app_list::StartPageService::Get(profile_
);
802 if (start_page_service
)
803 start_page_service
->set_search_engine_is_google(is_google
);
806 void AppListViewDelegate::Observe(int type
,
807 const content::NotificationSource
& source
,
808 const content::NotificationDetails
& details
) {
810 case chrome::NOTIFICATION_APP_TERMINATING
:
812 app_list::AppListViewDelegateObserver
, observers_
, OnShutdown());
814 SetProfile(NULL
); // Ensures launcher page web contents are torn down.
816 // SigninManagerFactory is not a leaky singleton (unlike this class), and
817 // its destructor will check that it has no remaining observers.
818 scoped_observer_
.RemoveAll();
819 SigninManagerFactory::GetInstance()->RemoveObserver(this);