app_list_view_delegate.cc revision a02191e04bc25c4935f804f2c080ae28663d096d
114852f27e792470232287371c5ffd4cee1d5b943Chris Lattner// Copyright (c) 2012 The Chromium Authors. All rights reserved.
214852f27e792470232287371c5ffd4cee1d5b943Chris Lattner// Use of this source code is governed by a BSD-style license that can be
314852f27e792470232287371c5ffd4cee1d5b943Chris Lattner// found in the LICENSE file.
414852f27e792470232287371c5ffd4cee1d5b943Chris Lattner
514852f27e792470232287371c5ffd4cee1d5b943Chris Lattner#include "chrome/browser/ui/app_list/app_list_view_delegate.h"
614852f27e792470232287371c5ffd4cee1d5b943Chris Lattner
714852f27e792470232287371c5ffd4cee1d5b943Chris Lattner#include <vector>
814852f27e792470232287371c5ffd4cee1d5b943Chris Lattner
914852f27e792470232287371c5ffd4cee1d5b943Chris Lattner#include "base/callback.h"
1014852f27e792470232287371c5ffd4cee1d5b943Chris Lattner#include "base/files/file_path.h"
1114852f27e792470232287371c5ffd4cee1d5b943Chris Lattner#include "base/metrics/user_metrics.h"
1214852f27e792470232287371c5ffd4cee1d5b943Chris Lattner#include "base/stl_util.h"
1314852f27e792470232287371c5ffd4cee1d5b943Chris Lattner#include "chrome/browser/browser_process.h"
1414852f27e792470232287371c5ffd4cee1d5b943Chris Lattner#include "chrome/browser/chrome_notification_types.h"
1514852f27e792470232287371c5ffd4cee1d5b943Chris Lattner#include "chrome/browser/extensions/extension_service.h"
1614852f27e792470232287371c5ffd4cee1d5b943Chris Lattner#include "chrome/browser/feedback/feedback_util.h"
1714852f27e792470232287371c5ffd4cee1d5b943Chris Lattner#include "chrome/browser/profiles/profile_info_cache.h"
180b8c9a80f20772c3793201ab5b251d3520b9cea3Chandler Carruth#include "chrome/browser/profiles/profile_manager.h"
1914852f27e792470232287371c5ffd4cee1d5b943Chris Lattner#include "chrome/browser/ui/app_list/app_list_controller_delegate.h"
2014852f27e792470232287371c5ffd4cee1d5b943Chris Lattner#include "chrome/browser/ui/app_list/app_list_service.h"
2114852f27e792470232287371c5ffd4cee1d5b943Chris Lattner#include "chrome/browser/ui/app_list/app_list_syncable_service.h"
2214852f27e792470232287371c5ffd4cee1d5b943Chris Lattner#include "chrome/browser/ui/app_list/app_list_syncable_service_factory.h"
2314852f27e792470232287371c5ffd4cee1d5b943Chris Lattner#include "chrome/browser/ui/app_list/search/search_controller.h"
2414852f27e792470232287371c5ffd4cee1d5b943Chris Lattner#include "chrome/browser/ui/app_list/start_page_service.h"
2514852f27e792470232287371c5ffd4cee1d5b943Chris Lattner#include "chrome/browser/ui/browser_finder.h"
2614852f27e792470232287371c5ffd4cee1d5b943Chris Lattner#include "chrome/browser/ui/chrome_pages.h"
2714852f27e792470232287371c5ffd4cee1d5b943Chris Lattner#include "chrome/browser/ui/host_desktop.h"
2814852f27e792470232287371c5ffd4cee1d5b943Chris Lattner#include "chrome/browser/ui/scoped_tabbed_browser_displayer.h"
2914852f27e792470232287371c5ffd4cee1d5b943Chris Lattner#include "chrome/browser/web_applications/web_app.h"
3014852f27e792470232287371c5ffd4cee1d5b943Chris Lattner#include "chrome/common/extensions/extension_constants.h"
3114852f27e792470232287371c5ffd4cee1d5b943Chris Lattner#include "chrome/common/url_constants.h"
3214852f27e792470232287371c5ffd4cee1d5b943Chris Lattner#include "components/signin/core/browser/signin_manager.h"
3314852f27e792470232287371c5ffd4cee1d5b943Chris Lattner#include "content/public/browser/browser_thread.h"
3414852f27e792470232287371c5ffd4cee1d5b943Chris Lattner#include "content/public/browser/page_navigator.h"
3514852f27e792470232287371c5ffd4cee1d5b943Chris Lattner#include "content/public/browser/user_metrics.h"
3614852f27e792470232287371c5ffd4cee1d5b943Chris Lattner#include "grit/theme_resources.h"
3714852f27e792470232287371c5ffd4cee1d5b943Chris Lattner#include "ui/app_list/app_list_view_delegate_observer.h"
3814852f27e792470232287371c5ffd4cee1d5b943Chris Lattner#include "ui/app_list/search_box_model.h"
3914852f27e792470232287371c5ffd4cee1d5b943Chris Lattner#include "ui/app_list/speech_ui_model.h"
4014852f27e792470232287371c5ffd4cee1d5b943Chris Lattner#include "ui/base/resource/resource_bundle.h"
4114852f27e792470232287371c5ffd4cee1d5b943Chris Lattner
4214852f27e792470232287371c5ffd4cee1d5b943Chris Lattner#if defined(USE_ASH)
4379fca6fea87be7221843031870bbf2c9ae1fc555Dan Gohman#include "chrome/browser/ui/ash/app_list/app_sync_ui_state_watcher.h"
4479fca6fea87be7221843031870bbf2c9ae1fc555Dan Gohman#endif
4514852f27e792470232287371c5ffd4cee1d5b943Chris Lattner
4614852f27e792470232287371c5ffd4cee1d5b943Chris Lattner#if defined(OS_WIN)
4714852f27e792470232287371c5ffd4cee1d5b943Chris Lattner#include "chrome/browser/web_applications/web_app_win.h"
4814852f27e792470232287371c5ffd4cee1d5b943Chris Lattner#endif
49dce4a407a24b04eebc6a376f8e62b41aaa7b071fStephen Hines
5014852f27e792470232287371c5ffd4cee1d5b943Chris Lattnernamespace {
5114852f27e792470232287371c5ffd4cee1d5b943Chris Lattner
5214852f27e792470232287371c5ffd4cee1d5b943Chris Lattnerconst int kAutoLaunchDefaultTimeoutMilliSec = 50;
53dce4a407a24b04eebc6a376f8e62b41aaa7b071fStephen Hines
5414852f27e792470232287371c5ffd4cee1d5b943Chris Lattner#if defined(OS_WIN)
5514852f27e792470232287371c5ffd4cee1d5b943Chris Lattnervoid CreateShortcutInWebAppDir(
5614852f27e792470232287371c5ffd4cee1d5b943Chris Lattner    const base::FilePath& app_data_dir,
5714852f27e792470232287371c5ffd4cee1d5b943Chris Lattner    base::Callback<void(const base::FilePath&)> callback,
5814852f27e792470232287371c5ffd4cee1d5b943Chris Lattner    const web_app::ShortcutInfo& info) {
5914852f27e792470232287371c5ffd4cee1d5b943Chris Lattner  content::BrowserThread::PostTaskAndReplyWithResult(
6014852f27e792470232287371c5ffd4cee1d5b943Chris Lattner      content::BrowserThread::FILE,
61f0443c1eb44d737d9bd78962932fc80f74c6113cDaniel Dunbar      FROM_HERE,
6214852f27e792470232287371c5ffd4cee1d5b943Chris Lattner      base::Bind(web_app::CreateShortcutInWebAppDir, app_data_dir, info),
6314852f27e792470232287371c5ffd4cee1d5b943Chris Lattner      callback);
64}
65#endif
66
67void PopulateUsers(const ProfileInfoCache& profile_info,
68                   const base::FilePath& active_profile_path,
69                   app_list::AppListViewDelegate::Users* users) {
70  users->clear();
71  const size_t count = profile_info.GetNumberOfProfiles();
72  for (size_t i = 0; i < count; ++i) {
73    // Don't display managed users.
74    if (profile_info.ProfileIsManagedAtIndex(i))
75      continue;
76
77    app_list::AppListViewDelegate::User user;
78    user.name = profile_info.GetNameOfProfileAtIndex(i);
79    user.email = profile_info.GetUserNameOfProfileAtIndex(i);
80    user.profile_path = profile_info.GetPathOfProfileAtIndex(i);
81    user.signin_required = profile_info.ProfileIsSigninRequiredAtIndex(i);
82    user.active = active_profile_path == user.profile_path;
83    users->push_back(user);
84  }
85}
86
87}  // namespace
88
89AppListViewDelegate::AppListViewDelegate(Profile* profile,
90                                         AppListControllerDelegate* controller)
91    : controller_(controller),
92      profile_(profile),
93      model_(NULL),
94      scoped_observer_(this) {
95  CHECK(controller_);
96  SigninManagerFactory::GetInstance()->AddObserver(this);
97
98  // Start observing all already-created SigninManagers.
99  ProfileManager* profile_manager = g_browser_process->profile_manager();
100  std::vector<Profile*> profiles = profile_manager->GetLoadedProfiles();
101  for (std::vector<Profile*>::iterator i = profiles.begin();
102       i != profiles.end();
103       ++i) {
104    SigninManagerBase* manager =
105        SigninManagerFactory::GetForProfileIfExists(*i);
106    if (manager) {
107      DCHECK(!scoped_observer_.IsObserving(manager));
108      scoped_observer_.Add(manager);
109    }
110  }
111
112  profile_manager->GetProfileInfoCache().AddObserver(this);
113
114  app_list::StartPageService* service =
115      app_list::StartPageService::Get(profile_);
116  speech_ui_.reset(new app_list::SpeechUIModel(
117      service ? service->state() : app_list::SPEECH_RECOGNITION_OFF));
118
119#if defined(GOOGLE_CHROME_BUILD)
120  speech_ui_->set_logo(
121      *ui::ResourceBundle::GetSharedInstance().
122      GetImageSkiaNamed(IDR_APP_LIST_GOOGLE_LOGO_VOICE_SEARCH));
123#endif
124
125  OnProfileChanged();  // sets model_
126  if (service)
127    service->AddObserver(this);
128}
129
130AppListViewDelegate::~AppListViewDelegate() {
131  app_list::StartPageService* service =
132      app_list::StartPageService::Get(profile_);
133  if (service)
134    service->RemoveObserver(this);
135  g_browser_process->
136      profile_manager()->GetProfileInfoCache().RemoveObserver(this);
137
138  SigninManagerFactory* factory = SigninManagerFactory::GetInstance();
139  if (factory)
140    factory->RemoveObserver(this);
141
142  // Ensure search controller is released prior to speech_ui_.
143  search_controller_.reset();
144}
145
146void AppListViewDelegate::SigninManagerCreated(SigninManagerBase* manager) {
147  scoped_observer_.Add(manager);
148}
149
150void AppListViewDelegate::SigninManagerShutdown(SigninManagerBase* manager) {
151  if (scoped_observer_.IsObserving(manager))
152    scoped_observer_.Remove(manager);
153}
154
155void AppListViewDelegate::GoogleSigninFailed(
156    const GoogleServiceAuthError& error) {
157  OnProfileChanged();
158}
159
160void AppListViewDelegate::GoogleSigninSucceeded(const std::string& username,
161                                                const std::string& password) {
162  OnProfileChanged();
163}
164
165void AppListViewDelegate::GoogleSignedOut(const std::string& username) {
166  OnProfileChanged();
167}
168
169void AppListViewDelegate::OnProfileChanged() {
170  model_ = app_list::AppListSyncableServiceFactory::GetForProfile(
171      profile_)->model();
172
173  search_controller_.reset(new app_list::SearchController(
174      profile_, model_->search_box(), model_->results(),
175      speech_ui_.get(), controller_));
176
177  signin_delegate_.SetProfile(profile_);
178
179#if defined(USE_ASH)
180  app_sync_ui_state_watcher_.reset(new AppSyncUIStateWatcher(profile_, model_));
181#endif
182
183  // Don't populate the app list users if we are on the ash desktop.
184  chrome::HostDesktopType desktop = chrome::GetHostDesktopTypeForNativeWindow(
185      controller_->GetAppListWindow());
186  if (desktop == chrome::HOST_DESKTOP_TYPE_ASH)
187    return;
188
189  // Populate the app list users.
190  PopulateUsers(g_browser_process->profile_manager()->GetProfileInfoCache(),
191                profile_->GetPath(), &users_);
192
193  FOR_EACH_OBSERVER(app_list::AppListViewDelegateObserver,
194                    observers_,
195                    OnProfilesChanged());
196}
197
198bool AppListViewDelegate::ForceNativeDesktop() const {
199  return controller_->ForceNativeDesktop();
200}
201
202void AppListViewDelegate::SetProfileByPath(const base::FilePath& profile_path) {
203  DCHECK(model_);
204
205  // The profile must be loaded before this is called.
206  profile_ =
207      g_browser_process->profile_manager()->GetProfileByPath(profile_path);
208  DCHECK(profile_);
209
210  OnProfileChanged();
211
212  // Clear search query.
213  model_->search_box()->SetText(base::string16());
214}
215
216app_list::AppListModel* AppListViewDelegate::GetModel() {
217  return model_;
218}
219
220app_list::SigninDelegate* AppListViewDelegate::GetSigninDelegate() {
221  return &signin_delegate_;
222}
223
224app_list::SpeechUIModel* AppListViewDelegate::GetSpeechUI() {
225  return speech_ui_.get();
226}
227
228void AppListViewDelegate::GetShortcutPathForApp(
229    const std::string& app_id,
230    const base::Callback<void(const base::FilePath&)>& callback) {
231#if defined(OS_WIN)
232  ExtensionService* service = profile_->GetExtensionService();
233  DCHECK(service);
234  const extensions::Extension* extension =
235      service->GetInstalledExtension(app_id);
236  if (!extension) {
237    callback.Run(base::FilePath());
238    return;
239  }
240
241  base::FilePath app_data_dir(
242      web_app::GetWebAppDataDirectory(profile_->GetPath(),
243                                      extension->id(),
244                                      GURL()));
245
246  web_app::UpdateShortcutInfoAndIconForApp(
247      extension,
248      profile_,
249      base::Bind(CreateShortcutInWebAppDir, app_data_dir, callback));
250#else
251  callback.Run(base::FilePath());
252#endif
253}
254
255void AppListViewDelegate::StartSearch() {
256  if (search_controller_)
257    search_controller_->Start();
258}
259
260void AppListViewDelegate::StopSearch() {
261  if (search_controller_)
262    search_controller_->Stop();
263}
264
265void AppListViewDelegate::OpenSearchResult(
266    app_list::SearchResult* result,
267    bool auto_launch,
268    int event_flags) {
269  if (auto_launch)
270    base::RecordAction(base::UserMetricsAction("AppList_AutoLaunched"));
271  search_controller_->OpenResult(result, event_flags);
272}
273
274void AppListViewDelegate::InvokeSearchResultAction(
275    app_list::SearchResult* result,
276    int action_index,
277    int event_flags) {
278  search_controller_->InvokeResultAction(result, action_index, event_flags);
279}
280
281base::TimeDelta AppListViewDelegate::GetAutoLaunchTimeout() {
282  return auto_launch_timeout_;
283}
284
285void AppListViewDelegate::AutoLaunchCanceled() {
286  base::RecordAction(base::UserMetricsAction("AppList_AutoLaunchCanceled"));
287  auto_launch_timeout_ = base::TimeDelta();
288}
289
290void AppListViewDelegate::ViewInitialized() {
291  app_list::StartPageService* service =
292      app_list::StartPageService::Get(profile_);
293  if (service)
294    service->AppListShown();
295}
296
297void AppListViewDelegate::Dismiss()  {
298  controller_->DismissView();
299}
300
301void AppListViewDelegate::ViewClosing() {
302  controller_->ViewClosing();
303
304  app_list::StartPageService* service =
305      app_list::StartPageService::Get(profile_);
306  if (service)
307    service->AppListHidden();
308}
309
310gfx::ImageSkia AppListViewDelegate::GetWindowIcon() {
311  return controller_->GetWindowIcon();
312}
313
314void AppListViewDelegate::OpenSettings() {
315  ExtensionService* service = profile_->GetExtensionService();
316  DCHECK(service);
317  const extensions::Extension* extension = service->GetInstalledExtension(
318      extension_misc::kSettingsAppId);
319  DCHECK(extension);
320  controller_->ActivateApp(profile_,
321                           extension,
322                           AppListControllerDelegate::LAUNCH_FROM_UNKNOWN,
323                           0);
324}
325
326void AppListViewDelegate::OpenHelp() {
327  chrome::HostDesktopType desktop = chrome::GetHostDesktopTypeForNativeWindow(
328      controller_->GetAppListWindow());
329  chrome::ScopedTabbedBrowserDisplayer displayer(profile_, desktop);
330  content::OpenURLParams params(GURL(chrome::kAppLauncherHelpURL),
331                                content::Referrer(),
332                                NEW_FOREGROUND_TAB,
333                                content::PAGE_TRANSITION_LINK,
334                                false);
335  displayer.browser()->OpenURL(params);
336}
337
338void AppListViewDelegate::OpenFeedback() {
339  chrome::HostDesktopType desktop = chrome::GetHostDesktopTypeForNativeWindow(
340      controller_->GetAppListWindow());
341  Browser* browser = chrome::FindTabbedBrowser(profile_, false, desktop);
342  chrome::ShowFeedbackPage(browser, std::string(),
343                           chrome::kAppLauncherCategoryTag);
344}
345
346void AppListViewDelegate::ToggleSpeechRecognition() {
347  app_list::StartPageService* service =
348      app_list::StartPageService::Get(profile_);
349  if (service)
350    service->ToggleSpeechRecognition();
351}
352
353void AppListViewDelegate::ShowForProfileByPath(
354    const base::FilePath& profile_path) {
355  controller_->ShowForProfileByPath(profile_path);
356}
357
358void AppListViewDelegate::OnSpeechResult(const base::string16& result,
359                                         bool is_final) {
360  speech_ui_->SetSpeechResult(result, is_final);
361  if (is_final) {
362    auto_launch_timeout_ = base::TimeDelta::FromMilliseconds(
363        kAutoLaunchDefaultTimeoutMilliSec);
364    model_->search_box()->SetText(result);
365  }
366}
367
368void AppListViewDelegate::OnSpeechSoundLevelChanged(int16 level) {
369  speech_ui_->UpdateSoundLevel(level);
370}
371
372void AppListViewDelegate::OnSpeechRecognitionStateChanged(
373    app_list::SpeechRecognitionState new_state) {
374  speech_ui_->SetSpeechRecognitionState(new_state);
375}
376
377void AppListViewDelegate::OnProfileAdded(const base::FilePath& profile_path) {
378  OnProfileChanged();
379}
380
381void AppListViewDelegate::OnProfileWasRemoved(
382    const base::FilePath& profile_path, const base::string16& profile_name) {
383  OnProfileChanged();
384}
385
386void AppListViewDelegate::OnProfileNameChanged(
387    const base::FilePath& profile_path,
388    const base::string16& old_profile_name) {
389  OnProfileChanged();
390}
391
392content::WebContents* AppListViewDelegate::GetStartPageContents() {
393  app_list::StartPageService* service =
394      app_list::StartPageService::Get(profile_);
395  if (!service)
396    return NULL;
397
398  return service->GetStartPageContents();
399}
400
401content::WebContents* AppListViewDelegate::GetSpeechRecognitionContents() {
402  app_list::StartPageService* service =
403      app_list::StartPageService::Get(profile_);
404  if (!service)
405    return NULL;
406
407  return service->GetSpeechRecognitionContents();
408}
409
410const app_list::AppListViewDelegate::Users&
411AppListViewDelegate::GetUsers() const {
412  return users_;
413}
414
415void AppListViewDelegate::AddObserver(
416    app_list::AppListViewDelegateObserver* observer) {
417  observers_.AddObserver(observer);
418}
419
420void AppListViewDelegate::RemoveObserver(
421    app_list::AppListViewDelegateObserver* observer) {
422  observers_.RemoveObserver(observer);
423}
424