| OLD | NEW |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 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/speech/speech_input_manager.h" | 5 #include "chrome/browser/speech/speech_input_manager.h" |
| 6 | 6 |
| 7 #include "app/l10n_util.h" | 7 #include "app/l10n_util.h" |
| 8 #include "base/ref_counted.h" | 8 #include "base/ref_counted.h" |
| 9 #include "base/singleton.h" | 9 #include "base/singleton.h" |
| 10 #include "chrome/browser/chrome_thread.h" | 10 #include "chrome/browser/chrome_thread.h" |
| (...skipping 24 matching lines...) Expand all Loading... |
| 35 // SpeechRecognizer::Delegate methods. | 35 // SpeechRecognizer::Delegate methods. |
| 36 virtual void SetRecognitionResult(int caller_id, bool error, | 36 virtual void SetRecognitionResult(int caller_id, bool error, |
| 37 const string16& value); | 37 const string16& value); |
| 38 virtual void DidCompleteRecording(int caller_id); | 38 virtual void DidCompleteRecording(int caller_id); |
| 39 virtual void DidCompleteRecognition(int caller_id); | 39 virtual void DidCompleteRecognition(int caller_id); |
| 40 virtual void OnRecognizerError(int caller_id, | 40 virtual void OnRecognizerError(int caller_id, |
| 41 SpeechRecognizer::ErrorCode error); | 41 SpeechRecognizer::ErrorCode error); |
| 42 virtual void DidCompleteEnvironmentEstimation(int caller_id); | 42 virtual void DidCompleteEnvironmentEstimation(int caller_id); |
| 43 | 43 |
| 44 // SpeechInputBubbleController::Delegate methods. | 44 // SpeechInputBubbleController::Delegate methods. |
| 45 virtual void RecognitionCancelled(int caller_id); | 45 virtual void InfoBubbleButtonClicked(int caller_id, |
| 46 virtual void SpeechInputFocusChanged(int caller_id); | 46 SpeechInputBubble::Button button); |
| 47 virtual void InfoBubbleFocusChanged(int caller_id); |
| 47 | 48 |
| 48 private: | 49 private: |
| 49 struct SpeechInputRequest { | 50 struct SpeechInputRequest { |
| 50 SpeechInputManagerDelegate* delegate; | 51 SpeechInputManagerDelegate* delegate; |
| 51 scoped_refptr<SpeechRecognizer> recognizer; | 52 scoped_refptr<SpeechRecognizer> recognizer; |
| 52 int render_process_id; | 53 bool is_active; // Set to true when recording or recognition is going on. |
| 53 int render_view_id; | |
| 54 gfx::Rect element_rect; | |
| 55 }; | 54 }; |
| 56 | 55 |
| 57 // Private constructor to enforce singleton. | 56 // Private constructor to enforce singleton. |
| 58 friend struct DefaultSingletonTraits<SpeechInputManagerImpl>; | 57 friend struct DefaultSingletonTraits<SpeechInputManagerImpl>; |
| 59 SpeechInputManagerImpl(); | 58 SpeechInputManagerImpl(); |
| 60 virtual ~SpeechInputManagerImpl(); | 59 virtual ~SpeechInputManagerImpl(); |
| 61 | 60 |
| 62 bool HasPendingRequest(int caller_id) const; | 61 bool HasPendingRequest(int caller_id) const; |
| 63 SpeechRecognizer* GetRecognizer(int caller_id) const; | |
| 64 SpeechInputManagerDelegate* GetDelegate(int caller_id) const; | 62 SpeechInputManagerDelegate* GetDelegate(int caller_id) const; |
| 65 | 63 |
| 66 void CancelRecognitionAndInformDelegate(int caller_id); | 64 void CancelRecognitionAndInformDelegate(int caller_id); |
| 67 | 65 |
| 68 // Shows an error message string as a simple alert info bar for the tab | 66 // Starts/restarts recognition for an existing request. |
| 69 // identified by |render_process_id| and |render_view_id|. | 67 void StartRecognitionForRequest(int caller_id); |
| 70 void ShowErrorMessage(int render_process_id, int render_view_id, | |
| 71 const string16& message); | |
| 72 static void ShowErrorMessageInUIThread( | |
| 73 int render_process_id, int render_view_id, | |
| 74 const string16& message); | |
| 75 | 68 |
| 76 SpeechInputManagerDelegate* delegate_; | 69 SpeechInputManagerDelegate* delegate_; |
| 77 typedef std::map<int, SpeechInputRequest> SpeechRecognizerMap; | 70 typedef std::map<int, SpeechInputRequest> SpeechRecognizerMap; |
| 78 SpeechRecognizerMap requests_; | 71 SpeechRecognizerMap requests_; |
| 79 int recording_caller_id_; | 72 int recording_caller_id_; |
| 80 scoped_refptr<SpeechInputBubbleController> bubble_controller_; | 73 scoped_refptr<SpeechInputBubbleController> bubble_controller_; |
| 81 }; | 74 }; |
| 82 | 75 |
| 83 SpeechInputManager* SpeechInputManager::Get() { | 76 SpeechInputManager* SpeechInputManager::Get() { |
| 84 return Singleton<SpeechInputManagerImpl>::get(); | 77 return Singleton<SpeechInputManagerImpl>::get(); |
| (...skipping 12 matching lines...) Expand all Loading... |
| 97 | 90 |
| 98 bool SpeechInputManagerImpl::HasPendingRequest(int caller_id) const { | 91 bool SpeechInputManagerImpl::HasPendingRequest(int caller_id) const { |
| 99 return requests_.find(caller_id) != requests_.end(); | 92 return requests_.find(caller_id) != requests_.end(); |
| 100 } | 93 } |
| 101 | 94 |
| 102 SpeechInputManagerDelegate* SpeechInputManagerImpl::GetDelegate( | 95 SpeechInputManagerDelegate* SpeechInputManagerImpl::GetDelegate( |
| 103 int caller_id) const { | 96 int caller_id) const { |
| 104 return requests_.find(caller_id)->second.delegate; | 97 return requests_.find(caller_id)->second.delegate; |
| 105 } | 98 } |
| 106 | 99 |
| 107 SpeechRecognizer* SpeechInputManagerImpl::GetRecognizer(int caller_id) const { | |
| 108 return requests_.find(caller_id)->second.recognizer; | |
| 109 } | |
| 110 | |
| 111 void SpeechInputManagerImpl::StartRecognition( | 100 void SpeechInputManagerImpl::StartRecognition( |
| 112 SpeechInputManagerDelegate* delegate, | 101 SpeechInputManagerDelegate* delegate, |
| 113 int caller_id, | 102 int caller_id, |
| 114 int render_process_id, | 103 int render_process_id, |
| 115 int render_view_id, | 104 int render_view_id, |
| 116 const gfx::Rect& element_rect) { | 105 const gfx::Rect& element_rect) { |
| 117 DCHECK(!HasPendingRequest(caller_id)); | 106 DCHECK(!HasPendingRequest(caller_id)); |
| 118 | 107 |
| 108 bubble_controller_->CreateBubble(caller_id, render_process_id, render_view_id, |
| 109 element_rect); |
| 110 |
| 111 SpeechInputRequest* request = &requests_[caller_id]; |
| 112 request->delegate = delegate; |
| 113 request->recognizer = new SpeechRecognizer(this, caller_id); |
| 114 request->is_active = false; |
| 115 |
| 116 StartRecognitionForRequest(caller_id); |
| 117 } |
| 118 |
| 119 void SpeechInputManagerImpl::StartRecognitionForRequest(int caller_id) { |
| 120 DCHECK(HasPendingRequest(caller_id)); |
| 121 |
| 119 // If we are currently recording audio for another caller, abort that cleanly. | 122 // If we are currently recording audio for another caller, abort that cleanly. |
| 120 if (recording_caller_id_) | 123 if (recording_caller_id_) |
| 121 CancelRecognitionAndInformDelegate(recording_caller_id_); | 124 CancelRecognitionAndInformDelegate(recording_caller_id_); |
| 122 | 125 |
| 123 if (!AudioManager::GetAudioManager()->HasAudioInputDevices()) { | 126 if (!AudioManager::GetAudioManager()->HasAudioInputDevices()) { |
| 124 ShowErrorMessage(render_process_id, render_view_id, | 127 bubble_controller_->SetBubbleMessage( |
| 125 l10n_util::GetStringUTF16(IDS_SPEECH_INPUT_NO_MIC)); | 128 caller_id, l10n_util::GetStringUTF16(IDS_SPEECH_INPUT_NO_MIC)); |
| 126 delegate->DidCompleteRecognition(caller_id); | 129 } else { |
| 127 return; | 130 recording_caller_id_ = caller_id; |
| 131 requests_[caller_id].is_active = true; |
| 132 requests_[caller_id].recognizer->StartRecording(); |
| 128 } | 133 } |
| 129 | |
| 130 recording_caller_id_ = caller_id; | |
| 131 SpeechInputRequest request; | |
| 132 request.delegate = delegate; | |
| 133 request.recognizer = new SpeechRecognizer(this, caller_id); | |
| 134 request.render_process_id = render_process_id; | |
| 135 request.render_view_id = render_view_id; | |
| 136 request.element_rect = element_rect; | |
| 137 requests_[caller_id] = request; | |
| 138 request.recognizer->StartRecording(); | |
| 139 } | 134 } |
| 140 | 135 |
| 141 void SpeechInputManagerImpl::CancelRecognition(int caller_id) { | 136 void SpeechInputManagerImpl::CancelRecognition(int caller_id) { |
| 142 DCHECK(HasPendingRequest(caller_id)); | 137 DCHECK(HasPendingRequest(caller_id)); |
| 143 GetRecognizer(caller_id)->CancelRecognition(); | 138 if (requests_[caller_id].is_active) |
| 139 requests_[caller_id].recognizer->CancelRecognition(); |
| 144 requests_.erase(caller_id); | 140 requests_.erase(caller_id); |
| 145 if (recording_caller_id_ == caller_id) | 141 if (recording_caller_id_ == caller_id) |
| 146 recording_caller_id_ = 0; | 142 recording_caller_id_ = 0; |
| 147 bubble_controller_->CloseBubble(caller_id); | 143 bubble_controller_->CloseBubble(caller_id); |
| 148 } | 144 } |
| 149 | 145 |
| 150 void SpeechInputManagerImpl::StopRecording(int caller_id) { | 146 void SpeechInputManagerImpl::StopRecording(int caller_id) { |
| 151 DCHECK(HasPendingRequest(caller_id)); | 147 DCHECK(HasPendingRequest(caller_id)); |
| 152 GetRecognizer(caller_id)->StopRecording(); | 148 requests_[caller_id].recognizer->StopRecording(); |
| 153 } | 149 } |
| 154 | 150 |
| 155 void SpeechInputManagerImpl::SetRecognitionResult(int caller_id, | 151 void SpeechInputManagerImpl::SetRecognitionResult(int caller_id, |
| 156 bool error, | 152 bool error, |
| 157 const string16& value) { | 153 const string16& value) { |
| 158 DCHECK(HasPendingRequest(caller_id)); | 154 DCHECK(HasPendingRequest(caller_id)); |
| 159 GetDelegate(caller_id)->SetRecognitionResult(caller_id, | 155 GetDelegate(caller_id)->SetRecognitionResult(caller_id, |
| 160 (error ? string16() : value)); | 156 (error ? string16() : value)); |
| 161 } | 157 } |
| 162 | 158 |
| 163 void SpeechInputManagerImpl::DidCompleteRecording(int caller_id) { | 159 void SpeechInputManagerImpl::DidCompleteRecording(int caller_id) { |
| 164 DCHECK(recording_caller_id_ == caller_id); | 160 DCHECK(recording_caller_id_ == caller_id); |
| 165 DCHECK(HasPendingRequest(caller_id)); | 161 DCHECK(HasPendingRequest(caller_id)); |
| 166 recording_caller_id_ = 0; | 162 recording_caller_id_ = 0; |
| 167 GetDelegate(caller_id)->DidCompleteRecording(caller_id); | 163 GetDelegate(caller_id)->DidCompleteRecording(caller_id); |
| 168 bubble_controller_->SetBubbleToRecognizingMode(caller_id); | 164 bubble_controller_->SetBubbleRecognizingMode(caller_id); |
| 169 } | 165 } |
| 170 | 166 |
| 171 void SpeechInputManagerImpl::DidCompleteRecognition(int caller_id) { | 167 void SpeechInputManagerImpl::DidCompleteRecognition(int caller_id) { |
| 172 GetDelegate(caller_id)->DidCompleteRecognition(caller_id); | 168 GetDelegate(caller_id)->DidCompleteRecognition(caller_id); |
| 173 requests_.erase(caller_id); | 169 requests_.erase(caller_id); |
| 174 bubble_controller_->CloseBubble(caller_id); | 170 bubble_controller_->CloseBubble(caller_id); |
| 175 } | 171 } |
| 176 | 172 |
| 177 void SpeechInputManagerImpl::OnRecognizerError( | 173 void SpeechInputManagerImpl::OnRecognizerError( |
| 178 int caller_id, SpeechRecognizer::ErrorCode error) { | 174 int caller_id, SpeechRecognizer::ErrorCode error) { |
| 179 // TODO(satish): Show specific messages for the various error codes and allow | 175 if (caller_id == recording_caller_id_) |
| 180 // user to retry without ending the recognition session if possible. | 176 recording_caller_id_ = 0; |
| 181 const SpeechInputRequest& request = requests_[caller_id]; | 177 |
| 182 ShowErrorMessage(request.render_process_id, request.render_view_id, | 178 requests_[caller_id].is_active = false; |
| 183 l10n_util::GetStringUTF16(IDS_SPEECH_INPUT_ERROR)); | 179 |
| 180 int message_id; |
| 181 switch (error) { |
| 182 case SpeechRecognizer::RECOGNIZER_ERROR_CAPTURE: |
| 183 message_id = IDS_SPEECH_INPUT_ERROR; |
| 184 break; |
| 185 case SpeechRecognizer::RECOGNIZER_ERROR_NO_SPEECH: |
| 186 message_id = IDS_SPEECH_INPUT_NO_SPEECH; |
| 187 break; |
| 188 case SpeechRecognizer::RECOGNIZER_ERROR_NO_RESULTS: |
| 189 message_id = IDS_SPEECH_INPUT_NO_RESULTS; |
| 190 break; |
| 191 default: |
| 192 NOTREACHED() << "unknown error " << error; |
| 193 return; |
| 194 } |
| 195 bubble_controller_->SetBubbleMessage(caller_id, |
| 196 l10n_util::GetStringUTF16(message_id)); |
| 184 } | 197 } |
| 185 | 198 |
| 186 void SpeechInputManagerImpl::DidCompleteEnvironmentEstimation(int caller_id) { | 199 void SpeechInputManagerImpl::DidCompleteEnvironmentEstimation(int caller_id) { |
| 187 DCHECK(HasPendingRequest(caller_id)); | 200 DCHECK(HasPendingRequest(caller_id)); |
| 188 DCHECK(recording_caller_id_ == caller_id); | 201 DCHECK(recording_caller_id_ == caller_id); |
| 189 | 202 |
| 190 // Speech recognizer has gathered enough background audio so we can ask the | 203 // Speech recognizer has gathered enough background audio so we can ask the |
| 191 // user to start speaking. | 204 // user to start speaking. |
| 192 const SpeechInputRequest& request = requests_[caller_id]; | 205 bubble_controller_->SetBubbleRecordingMode(caller_id); |
| 193 bubble_controller_->CreateBubble(caller_id, request.render_process_id, | |
| 194 request.render_view_id, | |
| 195 request.element_rect); | |
| 196 } | 206 } |
| 197 | 207 |
| 198 void SpeechInputManagerImpl::CancelRecognitionAndInformDelegate(int caller_id) { | 208 void SpeechInputManagerImpl::CancelRecognitionAndInformDelegate(int caller_id) { |
| 199 SpeechInputManagerDelegate* cur_delegate = GetDelegate(caller_id); | 209 SpeechInputManagerDelegate* cur_delegate = GetDelegate(caller_id); |
| 200 CancelRecognition(caller_id); | 210 CancelRecognition(caller_id); |
| 201 cur_delegate->DidCompleteRecording(caller_id); | 211 cur_delegate->DidCompleteRecording(caller_id); |
| 202 cur_delegate->DidCompleteRecognition(caller_id); | 212 cur_delegate->DidCompleteRecognition(caller_id); |
| 203 } | 213 } |
| 204 | 214 |
| 205 void SpeechInputManagerImpl::RecognitionCancelled(int caller_id) { | 215 void SpeechInputManagerImpl::InfoBubbleButtonClicked( |
| 216 int caller_id, SpeechInputBubble::Button button) { |
| 206 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); | 217 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); |
| 207 // Ignore if the caller id was not in our active recognizers list because the | 218 // Ignore if the caller id was not in our active recognizers list because the |
| 208 // user might have clicked more than once, or recognition could have been | 219 // user might have clicked more than once, or recognition could have been |
| 209 // cancelled due to other reasons before the user click was processed. | 220 // cancelled due to other reasons before the user click was processed. |
| 210 if (HasPendingRequest(caller_id)) | 221 if (!HasPendingRequest(caller_id)) |
| 222 return; |
| 223 |
| 224 if (button == SpeechInputBubble::BUTTON_CANCEL) { |
| 211 CancelRecognitionAndInformDelegate(caller_id); | 225 CancelRecognitionAndInformDelegate(caller_id); |
| 226 } else if (button == SpeechInputBubble::BUTTON_TRY_AGAIN) { |
| 227 StartRecognitionForRequest(caller_id); |
| 228 } |
| 212 } | 229 } |
| 213 | 230 |
| 214 void SpeechInputManagerImpl::SpeechInputFocusChanged(int caller_id) { | 231 void SpeechInputManagerImpl::InfoBubbleFocusChanged(int caller_id) { |
| 215 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); | 232 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); |
| 216 // Ignore if the caller id was not in our active recognizers list because the | 233 // Ignore if the caller id was not in our active recognizers list because the |
| 217 // user might have clicked more than once, or recognition could have been | 234 // user might have clicked more than once, or recognition could have been |
| 218 // ended due to other reasons before the user click was processed. | 235 // ended due to other reasons before the user click was processed. |
| 219 if (HasPendingRequest(caller_id)) { | 236 if (HasPendingRequest(caller_id)) { |
| 220 // If this is an ongoing recording, abort it since user has switched focus. | 237 // If this is an ongoing recording or if we were displaying an error message |
| 221 // Otherwise recognition has started and keep that going so user can start | 238 // to the user, abort it since user has switched focus. Otherwise |
| 222 // speaking to another element while this gets the results in parallel. | 239 // recognition has started and keep that going so user can start speaking to |
| 223 if (recording_caller_id_ == caller_id) | 240 // another element while this gets the results in parallel. |
| 241 if (recording_caller_id_ == caller_id || !requests_[caller_id].is_active) { |
| 224 CancelRecognitionAndInformDelegate(caller_id); | 242 CancelRecognitionAndInformDelegate(caller_id); |
| 243 } |
| 225 } | 244 } |
| 226 } | 245 } |
| 227 | 246 |
| 228 void SpeechInputManagerImpl::ShowErrorMessage( | |
| 229 int render_process_id, int render_view_id, | |
| 230 const string16& message) { | |
| 231 ChromeThread::PostTask( | |
| 232 ChromeThread::UI, FROM_HERE, | |
| 233 NewRunnableFunction(&SpeechInputManagerImpl::ShowErrorMessageInUIThread, | |
| 234 render_process_id, render_view_id, message)); | |
| 235 } | |
| 236 | |
| 237 void SpeechInputManagerImpl::ShowErrorMessageInUIThread( | |
| 238 int render_process_id, int render_view_id, | |
| 239 const string16& message) { | |
| 240 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); | |
| 241 TabContents* tab = tab_util::GetTabContentsByID(render_process_id, | |
| 242 render_view_id); | |
| 243 if (tab) // Check in case the tab was closed before we got this request. | |
| 244 tab->AddInfoBar(new SimpleAlertInfoBarDelegate(tab, message, NULL, true)); | |
| 245 } | |
| 246 | |
| 247 } // namespace speech_input | 247 } // namespace speech_input |
| OLD | NEW |