Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(65)

Side by Side Diff: chrome/browser/speech/speech_input_manager.cc

Issue 3352018: Show error messages in speech bubble allowing user to retry as well. (Closed)
Patch Set: Address joth's comments. Created 10 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698