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