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