OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/ui/app_list/start_page_service.h" | 5 #include "chrome/browser/ui/app_list/start_page_service.h" |
6 | 6 |
7 #include <string> | 7 #include <string> |
8 | 8 |
9 #include "base/bind.h" | 9 #include "base/bind.h" |
10 #include "base/command_line.h" | 10 #include "base/command_line.h" |
(...skipping 18 matching lines...) Expand all Loading... |
29 #include "content/public/browser/notification_registrar.h" | 29 #include "content/public/browser/notification_registrar.h" |
30 #include "content/public/browser/notification_service.h" | 30 #include "content/public/browser/notification_service.h" |
31 #include "content/public/browser/notification_source.h" | 31 #include "content/public/browser/notification_source.h" |
32 #include "content/public/browser/web_contents.h" | 32 #include "content/public/browser/web_contents.h" |
33 #include "content/public/browser/web_contents_delegate.h" | 33 #include "content/public/browser/web_contents_delegate.h" |
34 #include "extensions/browser/extension_system_provider.h" | 34 #include "extensions/browser/extension_system_provider.h" |
35 #include "extensions/browser/extensions_browser_client.h" | 35 #include "extensions/browser/extensions_browser_client.h" |
36 #include "extensions/common/extension.h" | 36 #include "extensions/common/extension.h" |
37 #include "ui/app_list/app_list_switches.h" | 37 #include "ui/app_list/app_list_switches.h" |
38 | 38 |
| 39 #if defined(OS_CHROMEOS) |
| 40 #include "chromeos/audio/cras_audio_handler.h" |
| 41 #endif |
| 42 |
39 using base::RecordAction; | 43 using base::RecordAction; |
40 using base::UserMetricsAction; | 44 using base::UserMetricsAction; |
41 | 45 |
42 namespace app_list { | 46 namespace app_list { |
43 | 47 |
44 namespace { | 48 namespace { |
45 | 49 |
46 bool InSpeechRecognition(SpeechRecognitionState state) { | 50 bool InSpeechRecognition(SpeechRecognitionState state) { |
47 return state == SPEECH_RECOGNITION_RECOGNIZING || | 51 return state == SPEECH_RECOGNITION_RECOGNIZING || |
48 state == SPEECH_RECOGNITION_IN_SPEECH; | 52 state == SPEECH_RECOGNITION_IN_SPEECH; |
49 } | 53 } |
50 | 54 |
51 } | 55 } // namespace |
52 | 56 |
53 class StartPageService::ProfileDestroyObserver | 57 class StartPageService::ProfileDestroyObserver |
54 : public content::NotificationObserver { | 58 : public content::NotificationObserver { |
55 public: | 59 public: |
56 explicit ProfileDestroyObserver(StartPageService* service) | 60 explicit ProfileDestroyObserver(StartPageService* service) |
57 : service_(service) { | 61 : service_(service) { |
58 registrar_.Add(this, | 62 registrar_.Add(this, |
59 chrome::NOTIFICATION_PROFILE_DESTROYED, | 63 chrome::NOTIFICATION_PROFILE_DESTROYED, |
60 content::Source<Profile>(service_->profile())); | 64 content::Source<Profile>(service_->profile())); |
61 } | 65 } |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
95 const GURL& security_origin, | 99 const GURL& security_origin, |
96 content::MediaStreamType type) override { | 100 content::MediaStreamType type) override { |
97 return MediaCaptureDevicesDispatcher::GetInstance() | 101 return MediaCaptureDevicesDispatcher::GetInstance() |
98 ->CheckMediaAccessPermission(web_contents, security_origin, type); | 102 ->CheckMediaAccessPermission(web_contents, security_origin, type); |
99 } | 103 } |
100 | 104 |
101 private: | 105 private: |
102 DISALLOW_COPY_AND_ASSIGN(StartPageWebContentsDelegate); | 106 DISALLOW_COPY_AND_ASSIGN(StartPageWebContentsDelegate); |
103 }; | 107 }; |
104 | 108 |
| 109 #if defined(OS_CHROMEOS) |
| 110 |
| 111 class StartPageService::AudioStatus |
| 112 : public chromeos::CrasAudioHandler::AudioObserver { |
| 113 public: |
| 114 explicit AudioStatus(StartPageService* start_page_service) |
| 115 : start_page_service_(start_page_service) { |
| 116 chromeos::CrasAudioHandler::Get()->AddAudioObserver(this); |
| 117 CheckAndUpdate(); |
| 118 } |
| 119 |
| 120 ~AudioStatus() override { |
| 121 chromeos::CrasAudioHandler::Get()->RemoveAudioObserver(this); |
| 122 } |
| 123 |
| 124 bool CanListen() { |
| 125 chromeos::CrasAudioHandler* audio_handler = |
| 126 chromeos::CrasAudioHandler::Get(); |
| 127 return (audio_handler->GetPrimaryActiveInputNode() != 0) && |
| 128 !audio_handler->IsInputMuted(); |
| 129 } |
| 130 |
| 131 private: |
| 132 void CheckAndUpdate() { |
| 133 // TODO(mukai): If the system can listen, this should also restart the |
| 134 // hotword recognition. |
| 135 start_page_service_->OnSpeechRecognitionStateChanged( |
| 136 CanListen() ? SPEECH_RECOGNITION_READY : SPEECH_RECOGNITION_OFF); |
| 137 } |
| 138 |
| 139 // chromeos::CrasAudioHandler::AudioObserver: |
| 140 void OnInputMuteChanged() override { CheckAndUpdate(); } |
| 141 |
| 142 void OnActiveInputNodeChanged() override { CheckAndUpdate(); } |
| 143 |
| 144 StartPageService* start_page_service_; |
| 145 |
| 146 DISALLOW_COPY_AND_ASSIGN(AudioStatus); |
| 147 }; |
| 148 |
| 149 #endif // OS_CHROMEOS |
| 150 |
105 // static | 151 // static |
106 StartPageService* StartPageService::Get(Profile* profile) { | 152 StartPageService* StartPageService::Get(Profile* profile) { |
107 return StartPageServiceFactory::GetForProfile(profile); | 153 return StartPageServiceFactory::GetForProfile(profile); |
108 } | 154 } |
109 | 155 |
110 StartPageService::StartPageService(Profile* profile) | 156 StartPageService::StartPageService(Profile* profile) |
111 : profile_(profile), | 157 : profile_(profile), |
112 profile_destroy_observer_(new ProfileDestroyObserver(this)), | 158 profile_destroy_observer_(new ProfileDestroyObserver(this)), |
113 recommended_apps_(new RecommendedApps(profile)), | 159 recommended_apps_(new RecommendedApps(profile)), |
114 state_(app_list::SPEECH_RECOGNITION_OFF), | 160 state_(app_list::SPEECH_RECOGNITION_OFF), |
115 speech_button_toggled_manually_(false), | 161 speech_button_toggled_manually_(false), |
116 speech_result_obtained_(false), | 162 speech_result_obtained_(false), |
117 webui_finished_loading_(false), | 163 webui_finished_loading_(false), |
118 weak_factory_(this) { | 164 weak_factory_(this) { |
119 // If experimental hotwording is enabled, then we're always "ready". | 165 // If experimental hotwording is enabled, then we're always "ready". |
120 // Transitioning into the "hotword recognizing" state is handled by the | 166 // Transitioning into the "hotword recognizing" state is handled by the |
121 // hotword extension. | 167 // hotword extension. |
122 if (HotwordService::IsExperimentalHotwordingEnabled()) | 168 if (HotwordService::IsExperimentalHotwordingEnabled()) { |
123 state_ = app_list::SPEECH_RECOGNITION_READY; | 169 state_ = app_list::SPEECH_RECOGNITION_READY; |
| 170 } |
124 | 171 |
125 if (app_list::switches::IsExperimentalAppListEnabled()) | 172 if (app_list::switches::IsExperimentalAppListEnabled()) |
126 LoadContents(); | 173 LoadContents(); |
127 } | 174 } |
128 | 175 |
129 StartPageService::~StartPageService() {} | 176 StartPageService::~StartPageService() {} |
130 | 177 |
131 void StartPageService::AddObserver(StartPageObserver* observer) { | 178 void StartPageService::AddObserver(StartPageObserver* observer) { |
132 observers_.AddObserver(observer); | 179 observers_.AddObserver(observer); |
133 } | 180 } |
134 | 181 |
135 void StartPageService::RemoveObserver(StartPageObserver* observer) { | 182 void StartPageService::RemoveObserver(StartPageObserver* observer) { |
136 observers_.RemoveObserver(observer); | 183 observers_.RemoveObserver(observer); |
137 } | 184 } |
138 | 185 |
139 void StartPageService::AppListShown() { | 186 void StartPageService::AppListShown() { |
140 if (!contents_) { | 187 if (!contents_) { |
141 LoadContents(); | 188 LoadContents(); |
142 } else if (contents_->GetWebUI() && | 189 } else if (contents_->GetWebUI() && |
143 !HotwordService::IsExperimentalHotwordingEnabled()) { | 190 !HotwordService::IsExperimentalHotwordingEnabled()) { |
144 // If experimental hotwording is enabled, don't call onAppListShown. | 191 // If experimental hotwording is enabled, don't call onAppListShown. |
145 // onAppListShown() initializes the web speech API, which is not used with | 192 // onAppListShown() initializes the web speech API, which is not used with |
146 // experimental hotwording. | 193 // experimental hotwording. |
147 contents_->GetWebUI()->CallJavascriptFunction( | 194 contents_->GetWebUI()->CallJavascriptFunction( |
148 "appList.startPage.onAppListShown", | 195 "appList.startPage.onAppListShown", |
149 base::FundamentalValue(HotwordEnabled())); | 196 base::FundamentalValue(HotwordEnabled())); |
150 } | 197 } |
| 198 |
| 199 #if defined(OS_CHROMEOS) |
| 200 audio_status_.reset(new AudioStatus(this)); |
| 201 #endif |
151 } | 202 } |
152 | 203 |
153 void StartPageService::AppListHidden() { | 204 void StartPageService::AppListHidden() { |
154 if (contents_->GetWebUI()) { | 205 if (contents_->GetWebUI()) { |
155 contents_->GetWebUI()->CallJavascriptFunction( | 206 contents_->GetWebUI()->CallJavascriptFunction( |
156 "appList.startPage.onAppListHidden"); | 207 "appList.startPage.onAppListHidden"); |
157 } | 208 } |
158 if (!app_list::switches::IsExperimentalAppListEnabled()) | 209 if (!app_list::switches::IsExperimentalAppListEnabled()) |
159 UnloadContents(); | 210 UnloadContents(); |
160 | 211 |
161 if (HotwordService::IsExperimentalHotwordingEnabled() && | 212 if (HotwordService::IsExperimentalHotwordingEnabled() && |
162 speech_recognizer_) { | 213 speech_recognizer_) { |
163 speech_recognizer_->Stop(); | 214 speech_recognizer_->Stop(); |
164 } | 215 } |
| 216 |
| 217 #if defined(OS_CHROMEOS) |
| 218 audio_status_.reset(); |
| 219 #endif |
165 } | 220 } |
166 | 221 |
167 void StartPageService::ToggleSpeechRecognition() { | 222 void StartPageService::ToggleSpeechRecognition() { |
168 DCHECK(contents_); | 223 DCHECK(contents_); |
169 speech_button_toggled_manually_ = true; | 224 speech_button_toggled_manually_ = true; |
170 if (!contents_->GetWebUI()) | 225 if (!contents_->GetWebUI()) |
171 return; | 226 return; |
172 | 227 |
173 if (!webui_finished_loading_) { | 228 if (!webui_finished_loading_) { |
174 pending_webui_callbacks_.push_back( | 229 pending_webui_callbacks_.push_back( |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
244 } | 299 } |
245 | 300 |
246 void StartPageService::OnSpeechSoundLevelChanged(int16_t level) { | 301 void StartPageService::OnSpeechSoundLevelChanged(int16_t level) { |
247 FOR_EACH_OBSERVER(StartPageObserver, | 302 FOR_EACH_OBSERVER(StartPageObserver, |
248 observers_, | 303 observers_, |
249 OnSpeechSoundLevelChanged(level)); | 304 OnSpeechSoundLevelChanged(level)); |
250 } | 305 } |
251 | 306 |
252 void StartPageService::OnSpeechRecognitionStateChanged( | 307 void StartPageService::OnSpeechRecognitionStateChanged( |
253 SpeechRecognitionState new_state) { | 308 SpeechRecognitionState new_state) { |
| 309 #if defined(OS_CHROMEOS) |
| 310 // Sometimes this can be called even though there are no audio input devices. |
| 311 if (!audio_status_->CanListen()) |
| 312 new_state = SPEECH_RECOGNITION_OFF; |
| 313 #endif |
| 314 |
| 315 if (state_ == new_state) |
| 316 return; |
254 | 317 |
255 if (HotwordService::IsExperimentalHotwordingEnabled() && | 318 if (HotwordService::IsExperimentalHotwordingEnabled() && |
256 new_state == SPEECH_RECOGNITION_READY && | 319 new_state == SPEECH_RECOGNITION_READY && |
257 speech_recognizer_) { | 320 speech_recognizer_) { |
258 speech_recognizer_->Stop(); | 321 speech_recognizer_->Stop(); |
259 } | 322 } |
260 | 323 |
261 if (!InSpeechRecognition(state_) && InSpeechRecognition(new_state)) { | 324 if (!InSpeechRecognition(state_) && InSpeechRecognition(new_state)) { |
262 if (!speech_button_toggled_manually_ && | 325 if (!speech_button_toggled_manually_ && |
263 state_ == SPEECH_RECOGNITION_HOTWORD_LISTENING) { | 326 state_ == SPEECH_RECOGNITION_HOTWORD_LISTENING) { |
(...skipping 12 matching lines...) Expand all Loading... |
276 observers_, | 339 observers_, |
277 OnSpeechRecognitionStateChanged(new_state)); | 340 OnSpeechRecognitionStateChanged(new_state)); |
278 } | 341 } |
279 | 342 |
280 content::WebContents* StartPageService::GetSpeechContents() { | 343 content::WebContents* StartPageService::GetSpeechContents() { |
281 return GetSpeechRecognitionContents(); | 344 return GetSpeechRecognitionContents(); |
282 } | 345 } |
283 | 346 |
284 void StartPageService::Shutdown() { | 347 void StartPageService::Shutdown() { |
285 UnloadContents(); | 348 UnloadContents(); |
| 349 #if defined(OS_CHROMEOS) |
| 350 audio_status_.reset(); |
| 351 #endif |
286 } | 352 } |
287 | 353 |
288 void StartPageService::WebUILoaded() { | 354 void StartPageService::WebUILoaded() { |
289 // There's a race condition between the WebUI loading, and calling its JS | 355 // There's a race condition between the WebUI loading, and calling its JS |
290 // functions. Specifically, calling LoadContents() doesn't mean that the page | 356 // functions. Specifically, calling LoadContents() doesn't mean that the page |
291 // has loaded, but several code paths make this assumption. This function | 357 // has loaded, but several code paths make this assumption. This function |
292 // allows us to defer calling JS functions until after the page has finished | 358 // allows us to defer calling JS functions until after the page has finished |
293 // loading. | 359 // loading. |
294 webui_finished_loading_ = true; | 360 webui_finished_loading_ = true; |
295 for (const auto& cb : pending_webui_callbacks_) | 361 for (const auto& cb : pending_webui_callbacks_) |
(...skipping 20 matching lines...) Expand all Loading... |
316 ui::PAGE_TRANSITION_AUTO_TOPLEVEL, | 382 ui::PAGE_TRANSITION_AUTO_TOPLEVEL, |
317 std::string()); | 383 std::string()); |
318 } | 384 } |
319 | 385 |
320 void StartPageService::UnloadContents() { | 386 void StartPageService::UnloadContents() { |
321 contents_.reset(); | 387 contents_.reset(); |
322 webui_finished_loading_ = false; | 388 webui_finished_loading_ = false; |
323 } | 389 } |
324 | 390 |
325 } // namespace app_list | 391 } // namespace app_list |
OLD | NEW |