start_page_service.cc revision 03b57e008b61dfcb1fbad3aea950ae0e001748b0
1// Copyright 2013 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/start_page_service.h" 6 7#include <string> 8 9#include "base/command_line.h" 10#include "base/memory/singleton.h" 11#include "base/metrics/user_metrics.h" 12#include "base/prefs/pref_service.h" 13#include "chrome/browser/chrome_notification_types.h" 14#include "chrome/browser/media/media_stream_infobar_delegate.h" 15#include "chrome/browser/profiles/profile.h" 16#include "chrome/browser/search/hotword_service.h" 17#include "chrome/browser/search/hotword_service_factory.h" 18#include "chrome/browser/ui/app_list/recommended_apps.h" 19#include "chrome/browser/ui/app_list/start_page_observer.h" 20#include "chrome/browser/ui/app_list/start_page_service_factory.h" 21#include "chrome/common/chrome_switches.h" 22#include "chrome/common/pref_names.h" 23#include "chrome/common/url_constants.h" 24#include "content/public/browser/notification_details.h" 25#include "content/public/browser/notification_observer.h" 26#include "content/public/browser/notification_registrar.h" 27#include "content/public/browser/notification_service.h" 28#include "content/public/browser/notification_source.h" 29#include "content/public/browser/web_contents.h" 30#include "content/public/browser/web_contents_delegate.h" 31#include "extensions/browser/extension_system_provider.h" 32#include "extensions/browser/extensions_browser_client.h" 33#include "extensions/common/extension.h" 34#include "ui/app_list/app_list_switches.h" 35 36using base::RecordAction; 37using base::UserMetricsAction; 38 39namespace app_list { 40 41namespace { 42 43bool InSpeechRecognition(SpeechRecognitionState state) { 44 return state == SPEECH_RECOGNITION_RECOGNIZING || 45 state == SPEECH_RECOGNITION_IN_SPEECH; 46} 47 48} 49 50class StartPageService::ProfileDestroyObserver 51 : public content::NotificationObserver { 52 public: 53 explicit ProfileDestroyObserver(StartPageService* service) 54 : service_(service) { 55 registrar_.Add(this, 56 chrome::NOTIFICATION_PROFILE_DESTROYED, 57 content::Source<Profile>(service_->profile())); 58 } 59 virtual ~ProfileDestroyObserver() {} 60 61 private: 62 // content::NotificationObserver 63 virtual void Observe(int type, 64 const content::NotificationSource& source, 65 const content::NotificationDetails& details) OVERRIDE { 66 DCHECK_EQ(chrome::NOTIFICATION_PROFILE_DESTROYED, type); 67 DCHECK_EQ(service_->profile(), content::Source<Profile>(source).ptr()); 68 service_->Shutdown(); 69 } 70 71 StartPageService* service_; // Owner of this class. 72 content::NotificationRegistrar registrar_; 73 74 DISALLOW_COPY_AND_ASSIGN(ProfileDestroyObserver); 75}; 76 77class StartPageService::StartPageWebContentsDelegate 78 : public content::WebContentsDelegate { 79 public: 80 StartPageWebContentsDelegate() {} 81 virtual ~StartPageWebContentsDelegate() {} 82 83 virtual void RequestMediaAccessPermission( 84 content::WebContents* web_contents, 85 const content::MediaStreamRequest& request, 86 const content::MediaResponseCallback& callback) OVERRIDE { 87 if (MediaStreamInfoBarDelegate::Create(web_contents, request, callback)) 88 NOTREACHED() << "Media stream not allowed for WebUI"; 89 } 90 91 private: 92 DISALLOW_COPY_AND_ASSIGN(StartPageWebContentsDelegate); 93}; 94 95// static 96StartPageService* StartPageService::Get(Profile* profile) { 97 return StartPageServiceFactory::GetForProfile(profile); 98} 99 100StartPageService::StartPageService(Profile* profile) 101 : profile_(profile), 102 profile_destroy_observer_(new ProfileDestroyObserver(this)), 103 recommended_apps_(new RecommendedApps(profile)), 104 state_(app_list::SPEECH_RECOGNITION_OFF), 105 speech_button_toggled_manually_(false), 106 speech_result_obtained_(false) { 107 // If experimental hotwording is enabled, then we're always "ready". 108 // Transitioning into the "hotword recognizing" state is handled by the 109 // hotword extension. 110 if (HotwordService::IsExperimentalHotwordingEnabled()) 111 state_ = app_list::SPEECH_RECOGNITION_READY; 112 113 if (app_list::switches::IsExperimentalAppListEnabled()) 114 LoadContents(); 115} 116 117StartPageService::~StartPageService() {} 118 119void StartPageService::AddObserver(StartPageObserver* observer) { 120 observers_.AddObserver(observer); 121} 122 123void StartPageService::RemoveObserver(StartPageObserver* observer) { 124 observers_.RemoveObserver(observer); 125} 126 127void StartPageService::AppListShown() { 128 if (!contents_) { 129 LoadContents(); 130 } else { 131 // If experimental hotwording is enabled, don't enable hotwording in the 132 // start page, since the hotword extension is taking care of this. 133 bool hotword_enabled = HotwordEnabled() && 134 !HotwordService::IsExperimentalHotwordingEnabled(); 135 contents_->GetWebUI()->CallJavascriptFunction( 136 "appList.startPage.onAppListShown", 137 base::FundamentalValue(hotword_enabled)); 138 } 139} 140 141void StartPageService::AppListHidden() { 142 contents_->GetWebUI()->CallJavascriptFunction( 143 "appList.startPage.onAppListHidden"); 144 if (!app_list::switches::IsExperimentalAppListEnabled()) 145 UnloadContents(); 146} 147 148void StartPageService::ToggleSpeechRecognition() { 149 speech_button_toggled_manually_ = true; 150 contents_->GetWebUI()->CallJavascriptFunction( 151 "appList.startPage.toggleSpeechRecognition"); 152} 153 154bool StartPageService::HotwordEnabled() { 155 if (HotwordService::IsExperimentalHotwordingEnabled()) { 156 return HotwordServiceFactory::IsServiceAvailable(profile_) && 157 profile_->GetPrefs()->GetBoolean(prefs::kHotwordSearchEnabled); 158 } 159#if defined(OS_CHROMEOS) 160 return HotwordServiceFactory::IsServiceAvailable(profile_) && 161 profile_->GetPrefs()->GetBoolean(prefs::kHotwordSearchEnabled); 162#else 163 return false; 164#endif 165} 166 167content::WebContents* StartPageService::GetStartPageContents() { 168 return app_list::switches::IsExperimentalAppListEnabled() ? contents_.get() 169 : NULL; 170} 171 172content::WebContents* StartPageService::GetSpeechRecognitionContents() { 173 if (app_list::switches::IsVoiceSearchEnabled()) { 174 if (!contents_) 175 LoadContents(); 176 return contents_.get(); 177 } 178 return NULL; 179} 180 181void StartPageService::OnSpeechResult( 182 const base::string16& query, bool is_final) { 183 if (is_final) { 184 speech_result_obtained_ = true; 185 RecordAction(UserMetricsAction("AppList_SearchedBySpeech")); 186 } 187 FOR_EACH_OBSERVER(StartPageObserver, 188 observers_, 189 OnSpeechResult(query, is_final)); 190} 191 192void StartPageService::OnSpeechSoundLevelChanged(int16 level) { 193 FOR_EACH_OBSERVER(StartPageObserver, 194 observers_, 195 OnSpeechSoundLevelChanged(level)); 196} 197 198void StartPageService::OnSpeechRecognitionStateChanged( 199 SpeechRecognitionState new_state) { 200 if (!InSpeechRecognition(state_) && InSpeechRecognition(new_state)) { 201 if (!speech_button_toggled_manually_ && 202 state_ == SPEECH_RECOGNITION_HOTWORD_LISTENING) { 203 RecordAction(UserMetricsAction("AppList_HotwordRecognized")); 204 } else { 205 RecordAction(UserMetricsAction("AppList_VoiceSearchStartedManually")); 206 } 207 } else if (InSpeechRecognition(state_) && !InSpeechRecognition(new_state) && 208 !speech_result_obtained_) { 209 RecordAction(UserMetricsAction("AppList_VoiceSearchCanceled")); 210 } 211 speech_button_toggled_manually_ = false; 212 speech_result_obtained_ = false; 213 state_ = new_state; 214 FOR_EACH_OBSERVER(StartPageObserver, 215 observers_, 216 OnSpeechRecognitionStateChanged(new_state)); 217} 218 219void StartPageService::Shutdown() { 220 UnloadContents(); 221} 222 223void StartPageService::LoadContents() { 224 contents_.reset(content::WebContents::Create( 225 content::WebContents::CreateParams(profile_))); 226 contents_delegate_.reset(new StartPageWebContentsDelegate()); 227 contents_->SetDelegate(contents_delegate_.get()); 228 229 GURL url(chrome::kChromeUIAppListStartPageURL); 230 CommandLine* command_line = CommandLine::ForCurrentProcess(); 231 if (command_line->HasSwitch(::switches::kAppListStartPageURL)) { 232 url = GURL( 233 command_line->GetSwitchValueASCII(::switches::kAppListStartPageURL)); 234 } 235 236 contents_->GetController().LoadURL( 237 url, 238 content::Referrer(), 239 content::PAGE_TRANSITION_AUTO_TOPLEVEL, 240 std::string()); 241} 242 243void StartPageService::UnloadContents() { 244 contents_.reset(); 245} 246 247} // namespace app_list 248