app_list_view_delegate.cc revision 010d83a9304c5a91596085d917d248abff47903a
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.
4
5#include "chrome/browser/ui/app_list/app_list_view_delegate.h"
6
7#include <vector>
8
9#include "base/callback.h"
10#include "base/files/file_path.h"
11#include "base/metrics/user_metrics.h"
12#include "base/stl_util.h"
13#include "chrome/browser/browser_process.h"
14#include "chrome/browser/chrome_notification_types.h"
15#include "chrome/browser/extensions/extension_service.h"
16#include "chrome/browser/profiles/profile_info_cache.h"
17#include "chrome/browser/profiles/profile_manager.h"
18#include "chrome/browser/search/hotword_service.h"
19#include "chrome/browser/search/hotword_service_factory.h"
20#include "chrome/browser/ui/app_list/app_list_controller_delegate.h"
21#include "chrome/browser/ui/app_list/app_list_service.h"
22#include "chrome/browser/ui/app_list/app_list_syncable_service.h"
23#include "chrome/browser/ui/app_list/app_list_syncable_service_factory.h"
24#include "chrome/browser/ui/app_list/search/search_controller.h"
25#include "chrome/browser/ui/app_list/start_page_service.h"
26#include "chrome/browser/ui/browser_finder.h"
27#include "chrome/browser/ui/chrome_pages.h"
28#include "chrome/browser/ui/host_desktop.h"
29#include "chrome/browser/ui/scoped_tabbed_browser_displayer.h"
30#include "chrome/browser/web_applications/web_app.h"
31#include "chrome/common/extensions/extension_constants.h"
32#include "chrome/common/url_constants.h"
33#include "components/signin/core/browser/signin_manager.h"
34#include "content/public/browser/browser_thread.h"
35#include "content/public/browser/page_navigator.h"
36#include "content/public/browser/user_metrics.h"
37#include "grit/theme_resources.h"
38#include "ui/app_list/app_list_switches.h"
39#include "ui/app_list/app_list_view_delegate_observer.h"
40#include "ui/app_list/search_box_model.h"
41#include "ui/app_list/speech_ui_model.h"
42#include "ui/base/resource/resource_bundle.h"
43
44#if defined(USE_AURA)
45#include "ui/keyboard/keyboard_util.h"
46#endif
47
48#if defined(USE_ASH)
49#include "chrome/browser/ui/ash/app_list/app_sync_ui_state_watcher.h"
50#endif
51
52#if defined(OS_WIN)
53#include "chrome/browser/web_applications/web_app_win.h"
54#endif
55
56
57namespace chrome {
58const char kAppLauncherCategoryTag[] = "AppLauncher";
59}  // namespace chrome
60
61namespace {
62
63const int kAutoLaunchDefaultTimeoutMilliSec = 50;
64
65#if defined(OS_WIN)
66void CreateShortcutInWebAppDir(
67    const base::FilePath& app_data_dir,
68    base::Callback<void(const base::FilePath&)> callback,
69    const web_app::ShortcutInfo& info) {
70  content::BrowserThread::PostTaskAndReplyWithResult(
71      content::BrowserThread::FILE,
72      FROM_HERE,
73      base::Bind(web_app::CreateShortcutInWebAppDir, app_data_dir, info),
74      callback);
75}
76#endif
77
78void PopulateUsers(const ProfileInfoCache& profile_info,
79                   const base::FilePath& active_profile_path,
80                   app_list::AppListViewDelegate::Users* users) {
81  users->clear();
82  const size_t count = profile_info.GetNumberOfProfiles();
83  for (size_t i = 0; i < count; ++i) {
84    // Don't display managed users.
85    if (profile_info.ProfileIsManagedAtIndex(i))
86      continue;
87
88    app_list::AppListViewDelegate::User user;
89    user.name = profile_info.GetNameOfProfileAtIndex(i);
90    user.email = profile_info.GetUserNameOfProfileAtIndex(i);
91    user.profile_path = profile_info.GetPathOfProfileAtIndex(i);
92    user.signin_required = profile_info.ProfileIsSigninRequiredAtIndex(i);
93    user.active = active_profile_path == user.profile_path;
94    users->push_back(user);
95  }
96}
97
98}  // namespace
99
100AppListViewDelegate::AppListViewDelegate(Profile* profile,
101                                         AppListControllerDelegate* controller)
102    : controller_(controller),
103      profile_(profile),
104      model_(NULL),
105      scoped_observer_(this) {
106  CHECK(controller_);
107  SigninManagerFactory::GetInstance()->AddObserver(this);
108
109  // Start observing all already-created SigninManagers.
110  ProfileManager* profile_manager = g_browser_process->profile_manager();
111  std::vector<Profile*> profiles = profile_manager->GetLoadedProfiles();
112  for (std::vector<Profile*>::iterator i = profiles.begin();
113       i != profiles.end();
114       ++i) {
115    SigninManagerBase* manager =
116        SigninManagerFactory::GetForProfileIfExists(*i);
117    if (manager) {
118      DCHECK(!scoped_observer_.IsObserving(manager));
119      scoped_observer_.Add(manager);
120    }
121  }
122
123  profile_manager->GetProfileInfoCache().AddObserver(this);
124
125  app_list::StartPageService* service =
126      app_list::StartPageService::Get(profile_);
127  speech_ui_.reset(new app_list::SpeechUIModel(
128      service ? service->state() : app_list::SPEECH_RECOGNITION_OFF));
129
130#if defined(GOOGLE_CHROME_BUILD)
131  speech_ui_->set_logo(
132      *ui::ResourceBundle::GetSharedInstance().
133      GetImageSkiaNamed(IDR_APP_LIST_GOOGLE_LOGO_VOICE_SEARCH));
134#endif
135
136  OnProfileChanged();  // sets model_
137  if (service)
138    service->AddObserver(this);
139}
140
141AppListViewDelegate::~AppListViewDelegate() {
142  app_list::StartPageService* service =
143      app_list::StartPageService::Get(profile_);
144  if (service)
145    service->RemoveObserver(this);
146  g_browser_process->
147      profile_manager()->GetProfileInfoCache().RemoveObserver(this);
148
149  SigninManagerFactory* factory = SigninManagerFactory::GetInstance();
150  if (factory)
151    factory->RemoveObserver(this);
152
153  // Ensure search controller is released prior to speech_ui_.
154  search_controller_.reset();
155}
156
157void AppListViewDelegate::OnHotwordStateChanged(bool started) {
158  if (started) {
159    if (speech_ui_->state() == app_list::SPEECH_RECOGNITION_READY) {
160      OnSpeechRecognitionStateChanged(
161          app_list::SPEECH_RECOGNITION_HOTWORD_LISTENING);
162    }
163  } else {
164    if (speech_ui_->state() == app_list::SPEECH_RECOGNITION_HOTWORD_LISTENING)
165      OnSpeechRecognitionStateChanged(app_list::SPEECH_RECOGNITION_READY);
166  }
167}
168
169void AppListViewDelegate::OnHotwordRecognized() {
170  DCHECK_EQ(app_list::SPEECH_RECOGNITION_HOTWORD_LISTENING,
171            speech_ui_->state());
172  ToggleSpeechRecognition();
173}
174
175void AppListViewDelegate::SigninManagerCreated(SigninManagerBase* manager) {
176  scoped_observer_.Add(manager);
177}
178
179void AppListViewDelegate::SigninManagerShutdown(SigninManagerBase* manager) {
180  if (scoped_observer_.IsObserving(manager))
181    scoped_observer_.Remove(manager);
182}
183
184void AppListViewDelegate::GoogleSigninFailed(
185    const GoogleServiceAuthError& error) {
186  OnProfileChanged();
187}
188
189void AppListViewDelegate::GoogleSigninSucceeded(const std::string& username,
190                                                const std::string& password) {
191  OnProfileChanged();
192}
193
194void AppListViewDelegate::GoogleSignedOut(const std::string& username) {
195  OnProfileChanged();
196}
197
198void AppListViewDelegate::OnProfileChanged() {
199  model_ = app_list::AppListSyncableServiceFactory::GetForProfile(
200      profile_)->model();
201
202  search_controller_.reset(new app_list::SearchController(
203      profile_, model_->search_box(), model_->results(),
204      speech_ui_.get(), controller_));
205
206  signin_delegate_.SetProfile(profile_);
207
208#if defined(USE_ASH)
209  app_sync_ui_state_watcher_.reset(new AppSyncUIStateWatcher(profile_, model_));
210#endif
211
212  // Don't populate the app list users if we are on the ash desktop.
213  chrome::HostDesktopType desktop = chrome::GetHostDesktopTypeForNativeWindow(
214      controller_->GetAppListWindow());
215  if (desktop == chrome::HOST_DESKTOP_TYPE_ASH)
216    return;
217
218  // Populate the app list users.
219  PopulateUsers(g_browser_process->profile_manager()->GetProfileInfoCache(),
220                profile_->GetPath(), &users_);
221
222  FOR_EACH_OBSERVER(app_list::AppListViewDelegateObserver,
223                    observers_,
224                    OnProfilesChanged());
225}
226
227bool AppListViewDelegate::ForceNativeDesktop() const {
228  return controller_->ForceNativeDesktop();
229}
230
231void AppListViewDelegate::SetProfileByPath(const base::FilePath& profile_path) {
232  DCHECK(model_);
233
234  // The profile must be loaded before this is called.
235  profile_ =
236      g_browser_process->profile_manager()->GetProfileByPath(profile_path);
237  DCHECK(profile_);
238
239  OnProfileChanged();
240
241  // Clear search query.
242  model_->search_box()->SetText(base::string16());
243}
244
245app_list::AppListModel* AppListViewDelegate::GetModel() {
246  return model_;
247}
248
249app_list::SigninDelegate* AppListViewDelegate::GetSigninDelegate() {
250  return &signin_delegate_;
251}
252
253app_list::SpeechUIModel* AppListViewDelegate::GetSpeechUI() {
254  return speech_ui_.get();
255}
256
257void AppListViewDelegate::GetShortcutPathForApp(
258    const std::string& app_id,
259    const base::Callback<void(const base::FilePath&)>& callback) {
260#if defined(OS_WIN)
261  ExtensionService* service = profile_->GetExtensionService();
262  DCHECK(service);
263  const extensions::Extension* extension =
264      service->GetInstalledExtension(app_id);
265  if (!extension) {
266    callback.Run(base::FilePath());
267    return;
268  }
269
270  base::FilePath app_data_dir(
271      web_app::GetWebAppDataDirectory(profile_->GetPath(),
272                                      extension->id(),
273                                      GURL()));
274
275  web_app::UpdateShortcutInfoAndIconForApp(
276      extension,
277      profile_,
278      base::Bind(CreateShortcutInWebAppDir, app_data_dir, callback));
279#else
280  callback.Run(base::FilePath());
281#endif
282}
283
284void AppListViewDelegate::StartSearch() {
285  if (search_controller_)
286    search_controller_->Start();
287}
288
289void AppListViewDelegate::StopSearch() {
290  if (search_controller_)
291    search_controller_->Stop();
292}
293
294void AppListViewDelegate::OpenSearchResult(
295    app_list::SearchResult* result,
296    bool auto_launch,
297    int event_flags) {
298  if (auto_launch)
299    base::RecordAction(base::UserMetricsAction("AppList_AutoLaunched"));
300  search_controller_->OpenResult(result, event_flags);
301}
302
303void AppListViewDelegate::InvokeSearchResultAction(
304    app_list::SearchResult* result,
305    int action_index,
306    int event_flags) {
307  search_controller_->InvokeResultAction(result, action_index, event_flags);
308}
309
310base::TimeDelta AppListViewDelegate::GetAutoLaunchTimeout() {
311  return auto_launch_timeout_;
312}
313
314void AppListViewDelegate::AutoLaunchCanceled() {
315  base::RecordAction(base::UserMetricsAction("AppList_AutoLaunchCanceled"));
316  auto_launch_timeout_ = base::TimeDelta();
317}
318
319void AppListViewDelegate::ViewInitialized() {
320  app_list::StartPageService* service =
321      app_list::StartPageService::Get(profile_);
322  if (service) {
323    service->AppListShown();
324    if (service->HotwordEnabled()) {
325      HotwordService* hotword_service =
326          HotwordServiceFactory::GetForProfile(profile_);
327      if (hotword_service)
328        hotword_service->RequestHotwordSession(this);
329    }
330  }
331}
332
333void AppListViewDelegate::Dismiss()  {
334  controller_->DismissView();
335}
336
337void AppListViewDelegate::ViewClosing() {
338  controller_->ViewClosing();
339
340  app_list::StartPageService* service =
341      app_list::StartPageService::Get(profile_);
342  if (service) {
343    service->AppListHidden();
344    if (service->HotwordEnabled()) {
345      HotwordService* hotword_service =
346          HotwordServiceFactory::GetForProfile(profile_);
347      if (hotword_service)
348        hotword_service->StopHotwordSession(this);
349    }
350  }
351}
352
353gfx::ImageSkia AppListViewDelegate::GetWindowIcon() {
354  return controller_->GetWindowIcon();
355}
356
357void AppListViewDelegate::OpenSettings() {
358  ExtensionService* service = profile_->GetExtensionService();
359  DCHECK(service);
360  const extensions::Extension* extension = service->GetInstalledExtension(
361      extension_misc::kSettingsAppId);
362  DCHECK(extension);
363  controller_->ActivateApp(profile_,
364                           extension,
365                           AppListControllerDelegate::LAUNCH_FROM_UNKNOWN,
366                           0);
367}
368
369void AppListViewDelegate::OpenHelp() {
370  chrome::HostDesktopType desktop = chrome::GetHostDesktopTypeForNativeWindow(
371      controller_->GetAppListWindow());
372  chrome::ScopedTabbedBrowserDisplayer displayer(profile_, desktop);
373  content::OpenURLParams params(GURL(chrome::kAppLauncherHelpURL),
374                                content::Referrer(),
375                                NEW_FOREGROUND_TAB,
376                                content::PAGE_TRANSITION_LINK,
377                                false);
378  displayer.browser()->OpenURL(params);
379}
380
381void AppListViewDelegate::OpenFeedback() {
382  chrome::HostDesktopType desktop = chrome::GetHostDesktopTypeForNativeWindow(
383      controller_->GetAppListWindow());
384  Browser* browser = chrome::FindTabbedBrowser(profile_, false, desktop);
385  chrome::ShowFeedbackPage(browser, std::string(),
386                           chrome::kAppLauncherCategoryTag);
387}
388
389void AppListViewDelegate::ToggleSpeechRecognition() {
390  app_list::StartPageService* service =
391      app_list::StartPageService::Get(profile_);
392  if (service)
393    service->ToggleSpeechRecognition();
394}
395
396void AppListViewDelegate::ShowForProfileByPath(
397    const base::FilePath& profile_path) {
398  controller_->ShowForProfileByPath(profile_path);
399}
400
401void AppListViewDelegate::OnSpeechResult(const base::string16& result,
402                                         bool is_final) {
403  speech_ui_->SetSpeechResult(result, is_final);
404  if (is_final) {
405    auto_launch_timeout_ = base::TimeDelta::FromMilliseconds(
406        kAutoLaunchDefaultTimeoutMilliSec);
407    model_->search_box()->SetText(result);
408  }
409}
410
411void AppListViewDelegate::OnSpeechSoundLevelChanged(int16 level) {
412  speech_ui_->UpdateSoundLevel(level);
413}
414
415void AppListViewDelegate::OnSpeechRecognitionStateChanged(
416    app_list::SpeechRecognitionState new_state) {
417  speech_ui_->SetSpeechRecognitionState(new_state);
418}
419
420void AppListViewDelegate::OnProfileAdded(const base::FilePath& profile_path) {
421  OnProfileChanged();
422}
423
424void AppListViewDelegate::OnProfileWasRemoved(
425    const base::FilePath& profile_path, const base::string16& profile_name) {
426  OnProfileChanged();
427}
428
429void AppListViewDelegate::OnProfileNameChanged(
430    const base::FilePath& profile_path,
431    const base::string16& old_profile_name) {
432  OnProfileChanged();
433}
434
435content::WebContents* AppListViewDelegate::GetStartPageContents() {
436  app_list::StartPageService* service =
437      app_list::StartPageService::Get(profile_);
438  if (!service)
439    return NULL;
440
441  return service->GetStartPageContents();
442}
443
444content::WebContents* AppListViewDelegate::GetSpeechRecognitionContents() {
445  app_list::StartPageService* service =
446      app_list::StartPageService::Get(profile_);
447  if (!service)
448    return NULL;
449
450  return service->GetSpeechRecognitionContents();
451}
452
453const app_list::AppListViewDelegate::Users&
454AppListViewDelegate::GetUsers() const {
455  return users_;
456}
457
458bool AppListViewDelegate::ShouldCenterWindow() const {
459  if (app_list::switches::IsCenteredAppListEnabled())
460    return true;
461
462  // keyboard depends upon Aura.
463#if defined(USE_AURA)
464  // If the virtual keyboard is enabled, use the new app list position. The old
465  // position is too tall, and doesn't fit in the left-over screen space.
466  if (keyboard::IsKeyboardEnabled())
467    return true;
468#endif
469
470  return false;
471}
472
473void AppListViewDelegate::AddObserver(
474    app_list::AppListViewDelegateObserver* observer) {
475  observers_.AddObserver(observer);
476}
477
478void AppListViewDelegate::RemoveObserver(
479    app_list::AppListViewDelegateObserver* observer) {
480  observers_.RemoveObserver(observer);
481}
482