ozone: evdev: Sync caps lock LED state to evdev
[chromium-blink-merge.git] / chrome / browser / ui / app_list / app_list_view_delegate.cc
blob1f268624aade0d713b2461e70bbc17bf49725270
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/profiler/scoped_tracker.h"
15 #include "base/stl_util.h"
16 #include "chrome/browser/apps/scoped_keep_alive.h"
17 #include "chrome/browser/browser_process.h"
18 #include "chrome/browser/chrome_notification_types.h"
19 #include "chrome/browser/profiles/profile_info_cache.h"
20 #include "chrome/browser/profiles/profile_manager.h"
21 #include "chrome/browser/search/hotword_service.h"
22 #include "chrome/browser/search/hotword_service_factory.h"
23 #include "chrome/browser/search_engines/template_url_service_factory.h"
24 #include "chrome/browser/ui/app_list/app_list_controller_delegate.h"
25 #include "chrome/browser/ui/app_list/app_list_service.h"
26 #include "chrome/browser/ui/app_list/app_list_syncable_service.h"
27 #include "chrome/browser/ui/app_list/app_list_syncable_service_factory.h"
28 #include "chrome/browser/ui/app_list/launcher_page_event_dispatcher.h"
29 #include "chrome/browser/ui/app_list/search/search_controller_factory.h"
30 #include "chrome/browser/ui/app_list/search/search_resource_manager.h"
31 #include "chrome/browser/ui/app_list/start_page_service.h"
32 #include "chrome/browser/ui/apps/chrome_app_delegate.h"
33 #include "chrome/browser/ui/browser_finder.h"
34 #include "chrome/browser/ui/chrome_pages.h"
35 #include "chrome/browser/ui/host_desktop.h"
36 #include "chrome/browser/ui/scoped_tabbed_browser_displayer.h"
37 #include "chrome/browser/web_applications/web_app.h"
38 #include "chrome/common/chrome_switches.h"
39 #include "chrome/common/extensions/extension_constants.h"
40 #include "chrome/common/url_constants.h"
41 #include "components/search_engines/template_url_prepopulate_data.h"
42 #include "components/signin/core/browser/signin_manager.h"
43 #include "content/public/browser/browser_thread.h"
44 #include "content/public/browser/notification_service.h"
45 #include "content/public/browser/page_navigator.h"
46 #include "content/public/browser/render_view_host.h"
47 #include "content/public/browser/render_widget_host_view.h"
48 #include "content/public/browser/speech_recognition_session_preamble.h"
49 #include "content/public/browser/user_metrics.h"
50 #include "content/public/browser/web_contents.h"
51 #include "extensions/browser/extension_registry.h"
52 #include "extensions/common/constants.h"
53 #include "extensions/common/extension_set.h"
54 #include "extensions/common/manifest_constants.h"
55 #include "extensions/common/manifest_handlers/launcher_page_info.h"
56 #include "grit/theme_resources.h"
57 #include "ui/app_list/app_list_switches.h"
58 #include "ui/app_list/app_list_view_delegate_observer.h"
59 #include "ui/app_list/search_box_model.h"
60 #include "ui/app_list/search_controller.h"
61 #include "ui/app_list/speech_ui_model.h"
62 #include "ui/base/resource/resource_bundle.h"
63 #include "ui/views/controls/webview/webview.h"
65 #if defined(TOOLKIT_VIEWS)
66 #include "ui/views/controls/webview/webview.h"
67 #endif
69 #if defined(USE_AURA)
70 #include "ui/keyboard/keyboard_util.h"
71 #endif
73 #if defined(USE_ASH)
74 #include "chrome/browser/ui/ash/app_list/app_sync_ui_state_watcher.h"
75 #endif
77 #if defined(OS_WIN)
78 #include "chrome/browser/web_applications/web_app_win.h"
79 #endif
82 namespace chrome {
83 const char kAppLauncherCategoryTag[] = "AppLauncher";
84 } // namespace chrome
86 namespace {
88 const int kAutoLaunchDefaultTimeoutMilliSec = 50;
90 #if defined(OS_WIN)
91 void CreateShortcutInWebAppDir(
92 const base::FilePath& app_data_dir,
93 base::Callback<void(const base::FilePath&)> callback,
94 const web_app::ShortcutInfo& info) {
95 content::BrowserThread::PostTaskAndReplyWithResult(
96 content::BrowserThread::FILE,
97 FROM_HERE,
98 base::Bind(web_app::CreateShortcutInWebAppDir, app_data_dir, info),
99 callback);
101 #endif
103 void PopulateUsers(const ProfileInfoCache& profile_info,
104 const base::FilePath& active_profile_path,
105 app_list::AppListViewDelegate::Users* users) {
106 users->clear();
107 const size_t count = profile_info.GetNumberOfProfiles();
108 for (size_t i = 0; i < count; ++i) {
109 app_list::AppListViewDelegate::User user;
110 user.name = profile_info.GetNameOfProfileAtIndex(i);
111 user.email = profile_info.GetUserNameOfProfileAtIndex(i);
112 user.profile_path = profile_info.GetPathOfProfileAtIndex(i);
113 user.active = active_profile_path == user.profile_path;
114 users->push_back(user);
118 // Gets a list of URLs of the custom launcher pages to show in the launcher.
119 // Returns a URL for each installed launcher page. If --custom-launcher-page is
120 // specified and valid, also includes that URL.
121 void GetCustomLauncherPageUrls(content::BrowserContext* browser_context,
122 std::vector<GURL>* urls) {
123 // First, check the command line.
124 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
125 if (app_list::switches::IsExperimentalAppListEnabled() &&
126 command_line->HasSwitch(app_list::switches::kCustomLauncherPage)) {
127 GURL custom_launcher_page_url(command_line->GetSwitchValueASCII(
128 app_list::switches::kCustomLauncherPage));
130 if (custom_launcher_page_url.SchemeIs(extensions::kExtensionScheme)) {
131 urls->push_back(custom_launcher_page_url);
132 } else {
133 LOG(ERROR) << "Invalid custom launcher page URL: "
134 << custom_launcher_page_url.possibly_invalid_spec();
138 // Search the list of installed extensions for ones with 'launcher_page'.
139 extensions::ExtensionRegistry* extension_registry =
140 extensions::ExtensionRegistry::Get(browser_context);
141 const extensions::ExtensionSet& enabled_extensions =
142 extension_registry->enabled_extensions();
143 for (extensions::ExtensionSet::const_iterator it = enabled_extensions.begin();
144 it != enabled_extensions.end();
145 ++it) {
146 const extensions::Extension* extension = it->get();
147 extensions::LauncherPageInfo* info =
148 extensions::LauncherPageHandler::GetInfo(extension);
149 if (!info)
150 continue;
152 urls->push_back(extension->GetResourceURL(info->page));
156 } // namespace
158 AppListViewDelegate::AppListViewDelegate(AppListControllerDelegate* controller)
159 : controller_(controller),
160 profile_(NULL),
161 model_(NULL),
162 is_voice_query_(false),
163 template_url_service_observer_(this),
164 scoped_observer_(this) {
165 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed.
166 tracked_objects::ScopedTracker tracking_profile(
167 FROM_HERE_WITH_EXPLICIT_FUNCTION(
168 "431326 AppListViewDelegate::AppListViewDelegate"));
170 CHECK(controller_);
171 // The SigninManagerFactor and the SigninManagers are observed to keep the
172 // profile switcher menu up to date, with the correct list of profiles and the
173 // correct email address (or none for signed out users) for each.
174 SigninManagerFactory::GetInstance()->AddObserver(this);
176 // Start observing all already-created SigninManagers.
177 ProfileManager* profile_manager = g_browser_process->profile_manager();
178 std::vector<Profile*> profiles = profile_manager->GetLoadedProfiles();
180 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed.
181 tracked_objects::ScopedTracker tracking_profile1(
182 FROM_HERE_WITH_EXPLICIT_FUNCTION(
183 "431326 AppListViewDelegate::AppListViewDelegate1"));
185 for (std::vector<Profile*>::iterator i = profiles.begin();
186 i != profiles.end();
187 ++i) {
188 SigninManagerBase* manager =
189 SigninManagerFactory::GetForProfileIfExists(*i);
190 if (manager) {
191 DCHECK(!scoped_observer_.IsObserving(manager));
192 scoped_observer_.Add(manager);
196 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed.
197 tracked_objects::ScopedTracker tracking_profile2(
198 FROM_HERE_WITH_EXPLICIT_FUNCTION(
199 "431326 AppListViewDelegate::AppListViewDelegate2"));
201 profile_manager->GetProfileInfoCache().AddObserver(this);
202 speech_ui_.reset(new app_list::SpeechUIModel);
204 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed.
205 tracked_objects::ScopedTracker tracking_profile3(
206 FROM_HERE_WITH_EXPLICIT_FUNCTION(
207 "431326 AppListViewDelegate::AppListViewDelegate3"));
209 #if defined(GOOGLE_CHROME_BUILD)
210 gfx::ImageSkia image =
211 *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
212 IDR_APP_LIST_GOOGLE_LOGO_VOICE_SEARCH);
214 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is
215 // fixed.
216 tracked_objects::ScopedTracker tracking_profile31(
217 FROM_HERE_WITH_EXPLICIT_FUNCTION(
218 "431326 AppListViewDelegate::AppListViewDelegate31"));
220 speech_ui_->set_logo(image);
221 #endif
223 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed.
224 tracked_objects::ScopedTracker tracking_profile32(
225 FROM_HERE_WITH_EXPLICIT_FUNCTION(
226 "431326 AppListViewDelegate::AppListViewDelegate32"));
228 registrar_.Add(this,
229 chrome::NOTIFICATION_APP_TERMINATING,
230 content::NotificationService::AllSources());
233 AppListViewDelegate::~AppListViewDelegate() {
234 // Note that the destructor is not always called. E.g. on Mac, this is owned
235 // by a leaky singleton. Essential shutdown work must be done by observing
236 // chrome::NOTIFICATION_APP_TERMINATING.
237 SetProfile(NULL);
238 g_browser_process->profile_manager()->GetProfileInfoCache().RemoveObserver(
239 this);
241 SigninManagerFactory* factory = SigninManagerFactory::GetInstance();
242 if (factory)
243 factory->RemoveObserver(this);
246 void AppListViewDelegate::SetProfile(Profile* new_profile) {
247 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed.
248 tracked_objects::ScopedTracker tracking_profile(
249 FROM_HERE_WITH_EXPLICIT_FUNCTION(
250 "431326 AppListViewDelegate::SetProfile"));
252 if (profile_ == new_profile)
253 return;
255 if (profile_) {
256 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed.
257 tracked_objects::ScopedTracker tracking_profile1(
258 FROM_HERE_WITH_EXPLICIT_FUNCTION(
259 "431326 AppListViewDelegate::SetProfile1"));
261 // Note: |search_resource_manager_| has a reference to |speech_ui_| so must
262 // be destroyed first.
263 search_resource_manager_.reset();
264 search_controller_.reset();
265 launcher_page_event_dispatcher_.reset();
266 custom_page_contents_.clear();
267 app_list::StartPageService* start_page_service =
268 app_list::StartPageService::Get(profile_);
269 if (start_page_service)
270 start_page_service->RemoveObserver(this);
271 #if defined(USE_ASH)
272 app_sync_ui_state_watcher_.reset();
273 #endif
274 model_ = NULL;
277 profile_ = new_profile;
278 if (!profile_) {
279 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed.
280 tracked_objects::ScopedTracker tracking_profile2(
281 FROM_HERE_WITH_EXPLICIT_FUNCTION(
282 "431326 AppListViewDelegate::SetProfile2"));
284 speech_ui_->SetSpeechRecognitionState(app_list::SPEECH_RECOGNITION_OFF);
285 return;
288 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed.
289 tracked_objects::ScopedTracker tracking_profile3(
290 FROM_HERE_WITH_EXPLICIT_FUNCTION(
291 "431326 AppListViewDelegate::SetProfile3"));
292 template_url_service_observer_.RemoveAll();
293 if (app_list::switches::IsExperimentalAppListEnabled()) {
294 TemplateURLService* template_url_service =
295 TemplateURLServiceFactory::GetForProfile(profile_);
296 template_url_service_observer_.Add(template_url_service);
299 model_ =
300 app_list::AppListSyncableServiceFactory::GetForProfile(profile_)->model();
302 #if defined(USE_ASH)
303 app_sync_ui_state_watcher_.reset(new AppSyncUIStateWatcher(profile_, model_));
304 #endif
306 SetUpSearchUI();
307 SetUpProfileSwitcher();
308 SetUpCustomLauncherPages();
309 OnTemplateURLServiceChanged();
311 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed.
312 tracked_objects::ScopedTracker tracking_profile4(
313 FROM_HERE_WITH_EXPLICIT_FUNCTION(
314 "431326 AppListViewDelegate::SetProfile4"));
316 // Clear search query.
317 model_->search_box()->SetText(base::string16());
320 void AppListViewDelegate::SetUpSearchUI() {
321 app_list::StartPageService* start_page_service =
322 app_list::StartPageService::Get(profile_);
323 if (start_page_service)
324 start_page_service->AddObserver(this);
326 speech_ui_->SetSpeechRecognitionState(start_page_service
327 ? start_page_service->state()
328 : app_list::SPEECH_RECOGNITION_OFF);
330 search_resource_manager_.reset(new app_list::SearchResourceManager(
331 profile_,
332 model_->search_box(),
333 speech_ui_.get()));
335 search_controller_ = CreateSearchController(profile_, model_, controller_);
338 void AppListViewDelegate::SetUpProfileSwitcher() {
339 // If a profile change is observed when there is no app list, there is nothing
340 // to update until SetProfile() calls this function again.
341 if (!profile_)
342 return;
344 // Don't populate the app list users if we are on the ash desktop.
345 chrome::HostDesktopType desktop = chrome::GetHostDesktopTypeForNativeWindow(
346 controller_->GetAppListWindow());
347 if (desktop == chrome::HOST_DESKTOP_TYPE_ASH)
348 return;
350 // Populate the app list users.
351 PopulateUsers(g_browser_process->profile_manager()->GetProfileInfoCache(),
352 profile_->GetPath(),
353 &users_);
355 FOR_EACH_OBSERVER(
356 app_list::AppListViewDelegateObserver, observers_, OnProfilesChanged());
359 void AppListViewDelegate::SetUpCustomLauncherPages() {
360 std::vector<GURL> custom_launcher_page_urls;
361 GetCustomLauncherPageUrls(profile_, &custom_launcher_page_urls);
362 if (custom_launcher_page_urls.empty())
363 return;
365 for (std::vector<GURL>::const_iterator it = custom_launcher_page_urls.begin();
366 it != custom_launcher_page_urls.end();
367 ++it) {
368 std::string extension_id = it->host();
369 apps::CustomLauncherPageContents* page_contents =
370 new apps::CustomLauncherPageContents(
371 scoped_ptr<extensions::AppDelegate>(
372 new ChromeAppDelegate(scoped_ptr<ScopedKeepAlive>())),
373 extension_id);
374 page_contents->Initialize(profile_, *it);
375 custom_page_contents_.push_back(page_contents);
378 std::string first_launcher_page_app_id = custom_launcher_page_urls[0].host();
379 const extensions::Extension* extension =
380 extensions::ExtensionRegistry::Get(profile_)
381 ->GetExtensionById(first_launcher_page_app_id,
382 extensions::ExtensionRegistry::EVERYTHING);
383 model_->set_custom_launcher_page_name(extension->name());
384 // Only the first custom launcher page gets events dispatched to it.
385 launcher_page_event_dispatcher_.reset(
386 new app_list::LauncherPageEventDispatcher(profile_,
387 first_launcher_page_app_id));
390 void AppListViewDelegate::OnHotwordStateChanged(bool started) {
391 if (started) {
392 if (speech_ui_->state() == app_list::SPEECH_RECOGNITION_READY) {
393 OnSpeechRecognitionStateChanged(
394 app_list::SPEECH_RECOGNITION_HOTWORD_LISTENING);
396 } else {
397 if (speech_ui_->state() == app_list::SPEECH_RECOGNITION_HOTWORD_LISTENING)
398 OnSpeechRecognitionStateChanged(app_list::SPEECH_RECOGNITION_READY);
402 void AppListViewDelegate::OnHotwordRecognized(
403 const scoped_refptr<content::SpeechRecognitionSessionPreamble>& preamble) {
404 DCHECK_EQ(app_list::SPEECH_RECOGNITION_HOTWORD_LISTENING,
405 speech_ui_->state());
406 ToggleSpeechRecognitionForHotword(preamble);
409 void AppListViewDelegate::SigninManagerCreated(SigninManagerBase* manager) {
410 scoped_observer_.Add(manager);
413 void AppListViewDelegate::SigninManagerShutdown(SigninManagerBase* manager) {
414 if (scoped_observer_.IsObserving(manager))
415 scoped_observer_.Remove(manager);
418 void AppListViewDelegate::GoogleSigninFailed(
419 const GoogleServiceAuthError& error) {
420 SetUpProfileSwitcher();
423 void AppListViewDelegate::GoogleSigninSucceeded(const std::string& account_id,
424 const std::string& username,
425 const std::string& password) {
426 SetUpProfileSwitcher();
429 void AppListViewDelegate::GoogleSignedOut(const std::string& account_id,
430 const std::string& username) {
431 SetUpProfileSwitcher();
434 void AppListViewDelegate::OnProfileAdded(const base::FilePath& profile_path) {
435 SetUpProfileSwitcher();
438 void AppListViewDelegate::OnProfileWasRemoved(
439 const base::FilePath& profile_path,
440 const base::string16& profile_name) {
441 SetUpProfileSwitcher();
444 void AppListViewDelegate::OnProfileNameChanged(
445 const base::FilePath& profile_path,
446 const base::string16& old_profile_name) {
447 SetUpProfileSwitcher();
450 bool AppListViewDelegate::ForceNativeDesktop() const {
451 return controller_->ForceNativeDesktop();
454 void AppListViewDelegate::SetProfileByPath(const base::FilePath& profile_path) {
455 DCHECK(model_);
456 // The profile must be loaded before this is called.
457 SetProfile(
458 g_browser_process->profile_manager()->GetProfileByPath(profile_path));
461 app_list::AppListModel* AppListViewDelegate::GetModel() {
462 return model_;
465 app_list::SpeechUIModel* AppListViewDelegate::GetSpeechUI() {
466 return speech_ui_.get();
469 void AppListViewDelegate::GetShortcutPathForApp(
470 const std::string& app_id,
471 const base::Callback<void(const base::FilePath&)>& callback) {
472 #if defined(OS_WIN)
473 const extensions::Extension* extension =
474 extensions::ExtensionRegistry::Get(profile_)->GetExtensionById(
475 app_id, extensions::ExtensionRegistry::EVERYTHING);
476 if (!extension) {
477 callback.Run(base::FilePath());
478 return;
481 base::FilePath app_data_dir(
482 web_app::GetWebAppDataDirectory(profile_->GetPath(),
483 extension->id(),
484 GURL()));
486 web_app::GetShortcutInfoForApp(
487 extension,
488 profile_,
489 base::Bind(CreateShortcutInWebAppDir, app_data_dir, callback));
490 #else
491 callback.Run(base::FilePath());
492 #endif
495 void AppListViewDelegate::StartSearch() {
496 if (search_controller_) {
497 search_controller_->Start(is_voice_query_);
498 controller_->OnSearchStarted();
502 void AppListViewDelegate::StopSearch() {
503 if (search_controller_)
504 search_controller_->Stop();
507 void AppListViewDelegate::OpenSearchResult(
508 app_list::SearchResult* result,
509 bool auto_launch,
510 int event_flags) {
511 if (auto_launch)
512 base::RecordAction(base::UserMetricsAction("AppList_AutoLaunched"));
513 search_controller_->OpenResult(result, event_flags);
514 is_voice_query_ = false;
517 void AppListViewDelegate::InvokeSearchResultAction(
518 app_list::SearchResult* result,
519 int action_index,
520 int event_flags) {
521 search_controller_->InvokeResultAction(result, action_index, event_flags);
524 base::TimeDelta AppListViewDelegate::GetAutoLaunchTimeout() {
525 return auto_launch_timeout_;
528 void AppListViewDelegate::AutoLaunchCanceled() {
529 if (is_voice_query_) {
530 base::RecordAction(base::UserMetricsAction("AppList_AutoLaunchCanceled"));
531 // Cancelling the auto launch means we are no longer in a voice query.
532 is_voice_query_ = false;
534 auto_launch_timeout_ = base::TimeDelta();
537 void AppListViewDelegate::ViewInitialized() {
538 app_list::StartPageService* service =
539 app_list::StartPageService::Get(profile_);
540 if (service) {
541 service->AppListShown();
542 if (service->HotwordEnabled()) {
543 HotwordService* hotword_service =
544 HotwordServiceFactory::GetForProfile(profile_);
545 if (hotword_service)
546 hotword_service->RequestHotwordSession(this);
548 OnHotwordStateChanged(service->HotwordEnabled());
552 void AppListViewDelegate::Dismiss() {
553 controller_->DismissView();
556 void AppListViewDelegate::ViewClosing() {
557 controller_->ViewClosing();
559 if (!profile_)
560 return;
562 app_list::StartPageService* service =
563 app_list::StartPageService::Get(profile_);
564 if (service) {
565 service->AppListHidden();
566 if (service->HotwordEnabled()) {
567 HotwordService* hotword_service =
568 HotwordServiceFactory::GetForProfile(profile_);
569 if (hotword_service) {
570 hotword_service->StopHotwordSession(this);
572 // If we're in always-on mode, we always want to restart hotwording
573 // after closing the launcher window. So, in always-on mode, hotwording
574 // is stopped, and then started again right away. Note that hotwording
575 // may already be stopped. The call to StopHotwordSession() above both
576 // explicitly stops hotwording, if it's running, and clears the
577 // association between the hotword service and |this|. When starting up
578 // hotwording, pass nullptr as the client so that hotword triggers cause
579 // the launcher to open.
580 // TODO(amistry): This only works on ChromeOS since Chrome hides the
581 // launcher instead of destroying it. Make this work on Chrome.
582 if (hotword_service->IsAlwaysOnEnabled())
583 hotword_service->RequestHotwordSession(nullptr);
589 gfx::ImageSkia AppListViewDelegate::GetWindowIcon() {
590 return controller_->GetWindowIcon();
593 void AppListViewDelegate::OpenSettings() {
594 const extensions::Extension* extension =
595 extensions::ExtensionRegistry::Get(profile_)->GetExtensionById(
596 extension_misc::kSettingsAppId,
597 extensions::ExtensionRegistry::EVERYTHING);
598 DCHECK(extension);
599 controller_->ActivateApp(profile_,
600 extension,
601 AppListControllerDelegate::LAUNCH_FROM_UNKNOWN,
605 void AppListViewDelegate::OpenHelp() {
606 chrome::HostDesktopType desktop = chrome::GetHostDesktopTypeForNativeWindow(
607 controller_->GetAppListWindow());
608 chrome::ScopedTabbedBrowserDisplayer displayer(profile_, desktop);
609 content::OpenURLParams params(GURL(chrome::kAppLauncherHelpURL),
610 content::Referrer(),
611 NEW_FOREGROUND_TAB,
612 ui::PAGE_TRANSITION_LINK,
613 false);
614 displayer.browser()->OpenURL(params);
617 void AppListViewDelegate::OpenFeedback() {
618 chrome::HostDesktopType desktop = chrome::GetHostDesktopTypeForNativeWindow(
619 controller_->GetAppListWindow());
620 Browser* browser = chrome::FindTabbedBrowser(profile_, false, desktop);
621 chrome::ShowFeedbackPage(browser, std::string(),
622 chrome::kAppLauncherCategoryTag);
625 void AppListViewDelegate::ToggleSpeechRecognition() {
626 ToggleSpeechRecognitionForHotword(nullptr);
629 void AppListViewDelegate::ToggleSpeechRecognitionForHotword(
630 const scoped_refptr<content::SpeechRecognitionSessionPreamble>& preamble) {
631 app_list::StartPageService* service =
632 app_list::StartPageService::Get(profile_);
633 if (service)
634 service->ToggleSpeechRecognition(preamble);
636 // With the new hotword extension, stop the hotword session. With the launcher
637 // and NTP, this is unnecessary since the hotwording is implicitly stopped.
638 // However, for always on, hotword triggering launches the launcher which
639 // starts a session and hence starts the hotword detector. This results in the
640 // hotword detector and the speech-to-text engine running in parallel, which
641 // will conflict with each other (i.e. saying 'Ok Google' twice in a row
642 // should cause a search to happen for 'Ok Google', not two hotword triggers).
643 // To get around this, always stop the session when switching to speech
644 // recognition.
645 if (HotwordService::IsExperimentalHotwordingEnabled() &&
646 service && service->HotwordEnabled()) {
647 HotwordService* hotword_service =
648 HotwordServiceFactory::GetForProfile(profile_);
649 if (hotword_service)
650 hotword_service->StopHotwordSession(this);
654 void AppListViewDelegate::ShowForProfileByPath(
655 const base::FilePath& profile_path) {
656 controller_->ShowForProfileByPath(profile_path);
659 void AppListViewDelegate::OnSpeechResult(const base::string16& result,
660 bool is_final) {
661 speech_ui_->SetSpeechResult(result, is_final);
662 if (is_final) {
663 auto_launch_timeout_ = base::TimeDelta::FromMilliseconds(
664 kAutoLaunchDefaultTimeoutMilliSec);
665 is_voice_query_ = true;
666 model_->search_box()->SetText(result);
670 void AppListViewDelegate::OnSpeechSoundLevelChanged(int16 level) {
671 speech_ui_->UpdateSoundLevel(level);
674 void AppListViewDelegate::OnSpeechRecognitionStateChanged(
675 app_list::SpeechRecognitionState new_state) {
676 speech_ui_->SetSpeechRecognitionState(new_state);
678 app_list::StartPageService* service =
679 app_list::StartPageService::Get(profile_);
680 // With the new hotword extension, we need to re-request hotwording after
681 // speech recognition has stopped. Do not request hotwording after the app
682 // list has already closed.
683 if (new_state == app_list::SPEECH_RECOGNITION_READY &&
684 HotwordService::IsExperimentalHotwordingEnabled() &&
685 service && service->HotwordEnabled() &&
686 controller_->GetAppListWindow()) {
687 HotwordService* hotword_service =
688 HotwordServiceFactory::GetForProfile(profile_);
689 if (hotword_service) {
690 hotword_service->RequestHotwordSession(this);
695 #if defined(TOOLKIT_VIEWS)
696 views::View* AppListViewDelegate::CreateStartPageWebView(
697 const gfx::Size& size) {
698 app_list::StartPageService* service =
699 app_list::StartPageService::Get(profile_);
700 if (!service)
701 return NULL;
703 service->LoadContentsIfNeeded();
705 content::WebContents* web_contents = service->GetStartPageContents();
706 if (!web_contents)
707 return NULL;
709 DCHECK_EQ(profile_, web_contents->GetBrowserContext());
710 views::WebView* web_view = new views::WebView(
711 web_contents->GetBrowserContext());
712 web_view->SetPreferredSize(size);
713 web_view->SetResizeBackgroundColor(SK_ColorTRANSPARENT);
714 web_view->SetWebContents(web_contents);
715 return web_view;
718 std::vector<views::View*> AppListViewDelegate::CreateCustomPageWebViews(
719 const gfx::Size& size) {
720 std::vector<views::View*> web_views;
722 for (ScopedVector<apps::CustomLauncherPageContents>::const_iterator it =
723 custom_page_contents_.begin();
724 it != custom_page_contents_.end();
725 ++it) {
726 content::WebContents* web_contents = (*it)->web_contents();
728 // The web contents should belong to the current profile.
729 DCHECK_EQ(profile_, web_contents->GetBrowserContext());
731 // Make the webview transparent.
732 web_contents->GetRenderViewHost()->GetView()->SetBackgroundColor(
733 SK_ColorTRANSPARENT);
735 views::WebView* web_view =
736 new views::WebView(web_contents->GetBrowserContext());
737 web_view->SetPreferredSize(size);
738 web_view->SetResizeBackgroundColor(SK_ColorTRANSPARENT);
739 web_view->SetWebContents(web_contents);
740 web_views.push_back(web_view);
743 return web_views;
746 void AppListViewDelegate::CustomLauncherPageAnimationChanged(double progress) {
747 if (launcher_page_event_dispatcher_)
748 launcher_page_event_dispatcher_->ProgressChanged(progress);
751 void AppListViewDelegate::CustomLauncherPagePopSubpage() {
752 if (launcher_page_event_dispatcher_)
753 launcher_page_event_dispatcher_->PopSubpage();
755 #endif
757 bool AppListViewDelegate::IsSpeechRecognitionEnabled() {
758 app_list::StartPageService* service =
759 app_list::StartPageService::Get(profile_);
760 return service && service->GetSpeechRecognitionContents();
763 const app_list::AppListViewDelegate::Users&
764 AppListViewDelegate::GetUsers() const {
765 return users_;
768 bool AppListViewDelegate::ShouldCenterWindow() const {
769 // Some ChromeOS devices (those that support TouchView mode) turn this flag on
770 // by default, which ensures that the app list is consistently centered on
771 // those devices. This avoids having the app list change shape and position as
772 // the user enters and exits TouchView mode.
773 if (app_list::switches::IsCenteredAppListEnabled())
774 return true;
776 // keyboard depends upon Aura.
777 #if defined(USE_AURA)
778 // If the virtual keyboard is enabled, use the new app list position. The old
779 // position is too tall, and doesn't fit in the left-over screen space.
780 if (keyboard::IsKeyboardEnabled())
781 return true;
782 #endif
784 return false;
787 void AppListViewDelegate::AddObserver(
788 app_list::AppListViewDelegateObserver* observer) {
789 observers_.AddObserver(observer);
792 void AppListViewDelegate::RemoveObserver(
793 app_list::AppListViewDelegateObserver* observer) {
794 observers_.RemoveObserver(observer);
797 void AppListViewDelegate::OnTemplateURLServiceChanged() {
798 if (!app_list::switches::IsExperimentalAppListEnabled())
799 return;
801 TemplateURLService* template_url_service =
802 TemplateURLServiceFactory::GetForProfile(profile_);
803 const TemplateURL* default_provider =
804 template_url_service->GetDefaultSearchProvider();
805 bool is_google =
806 TemplateURLPrepopulateData::GetEngineType(
807 *default_provider, template_url_service->search_terms_data()) ==
808 SEARCH_ENGINE_GOOGLE;
810 model_->SetSearchEngineIsGoogle(is_google);
812 app_list::StartPageService* start_page_service =
813 app_list::StartPageService::Get(profile_);
814 if (start_page_service)
815 start_page_service->set_search_engine_is_google(is_google);
818 void AppListViewDelegate::Observe(int type,
819 const content::NotificationSource& source,
820 const content::NotificationDetails& details) {
821 switch (type) {
822 case chrome::NOTIFICATION_APP_TERMINATING:
823 FOR_EACH_OBSERVER(
824 app_list::AppListViewDelegateObserver, observers_, OnShutdown());
826 SetProfile(NULL); // Ensures launcher page web contents are torn down.
828 // SigninManagerFactory is not a leaky singleton (unlike this class), and
829 // its destructor will check that it has no remaining observers.
830 scoped_observer_.RemoveAll();
831 SigninManagerFactory::GetInstance()->RemoveObserver(this);
832 break;
833 default:
834 NOTREACHED();