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