OLD | NEW |
| (Empty) |
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/speech/chrome_speech_recognition_manager_delegate_bubbl
e_ui.h" | |
6 | |
7 #include "base/strings/utf_string_conversions.h" | |
8 #include "content/public/browser/browser_thread.h" | |
9 #include "content/public/browser/speech_recognition_manager.h" | |
10 #include "content/public/browser/speech_recognition_session_context.h" | |
11 #include "content/public/common/speech_recognition_error.h" | |
12 #include "grit/generated_resources.h" | |
13 #include "ui/base/l10n/l10n_util.h" | |
14 | |
15 using content::BrowserThread; | |
16 using content::SpeechRecognitionManager; | |
17 | |
18 namespace { | |
19 | |
20 bool RequiresBubble(int session_id) { | |
21 return SpeechRecognitionManager::GetInstance()-> | |
22 GetSessionContext(session_id).requested_by_page_element; | |
23 } | |
24 | |
25 } // namespace | |
26 | |
27 namespace speech { | |
28 | |
29 ChromeSpeechRecognitionManagerDelegateBubbleUI | |
30 ::ChromeSpeechRecognitionManagerDelegateBubbleUI() { | |
31 } | |
32 | |
33 ChromeSpeechRecognitionManagerDelegateBubbleUI | |
34 ::~ChromeSpeechRecognitionManagerDelegateBubbleUI() { | |
35 if (bubble_controller_.get()) | |
36 bubble_controller_->CloseBubble(); | |
37 } | |
38 | |
39 void ChromeSpeechRecognitionManagerDelegateBubbleUI::InfoBubbleButtonClicked( | |
40 int session_id, SpeechRecognitionBubble::Button button) { | |
41 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
42 | |
43 // Note, the session might have been destroyed, therefore avoid calls to the | |
44 // manager which imply its existance (e.g., GetSessionContext()). | |
45 | |
46 if (button == SpeechRecognitionBubble::BUTTON_CANCEL) { | |
47 GetBubbleController()->CloseBubble(); | |
48 last_session_config_.reset(); | |
49 | |
50 // We can safely call AbortSession even if the session has already ended, | |
51 // the manager's public methods are reliable and will handle it properly. | |
52 SpeechRecognitionManager::GetInstance()->AbortSession(session_id); | |
53 } else if (button == SpeechRecognitionBubble::BUTTON_TRY_AGAIN) { | |
54 GetBubbleController()->CloseBubble(); | |
55 RestartLastSession(); | |
56 } else { | |
57 NOTREACHED(); | |
58 } | |
59 } | |
60 | |
61 void ChromeSpeechRecognitionManagerDelegateBubbleUI::InfoBubbleFocusChanged( | |
62 int session_id) { | |
63 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
64 | |
65 // This check is needed since on some systems (MacOS), in rare cases, if the | |
66 // user clicks repeatedly and fast on the input element, the FocusChanged | |
67 // event (corresponding to the old session that should be aborted) can be | |
68 // received after a new session (corresponding to the 2nd click) is started. | |
69 if (GetBubbleController()->GetActiveSessionID() != session_id) | |
70 return; | |
71 | |
72 // Note, the session might have been destroyed, therefore avoid calls to the | |
73 // manager which imply its existance (e.g., GetSessionContext()). | |
74 GetBubbleController()->CloseBubble(); | |
75 last_session_config_.reset(); | |
76 | |
77 // Clicking outside the bubble means we should abort. | |
78 SpeechRecognitionManager::GetInstance()->AbortSession(session_id); | |
79 } | |
80 | |
81 void ChromeSpeechRecognitionManagerDelegateBubbleUI::OnRecognitionStart( | |
82 int session_id) { | |
83 ChromeSpeechRecognitionManagerDelegate::OnRecognitionStart(session_id); | |
84 | |
85 const content::SpeechRecognitionSessionContext& context = | |
86 SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id); | |
87 | |
88 if (RequiresBubble(session_id)) { | |
89 // Copy the configuration of the session (for the "try again" button). | |
90 last_session_config_.reset(new content::SpeechRecognitionSessionConfig( | |
91 SpeechRecognitionManager::GetInstance()->GetSessionConfig(session_id))); | |
92 | |
93 // Create and show the bubble. It will be closed upon the OnEnd event. | |
94 GetBubbleController()->CreateBubble(session_id, | |
95 context.render_process_id, | |
96 context.render_view_id, | |
97 context.element_rect); | |
98 } | |
99 } | |
100 | |
101 void ChromeSpeechRecognitionManagerDelegateBubbleUI::OnAudioStart( | |
102 int session_id) { | |
103 ChromeSpeechRecognitionManagerDelegate::OnAudioStart(session_id); | |
104 | |
105 if (RequiresBubble(session_id)) { | |
106 DCHECK_EQ(session_id, GetBubbleController()->GetActiveSessionID()); | |
107 GetBubbleController()->SetBubbleRecordingMode(); | |
108 } | |
109 } | |
110 | |
111 void ChromeSpeechRecognitionManagerDelegateBubbleUI::OnAudioEnd( | |
112 int session_id) { | |
113 ChromeSpeechRecognitionManagerDelegate::OnAudioEnd(session_id); | |
114 | |
115 // OnAudioEnd can be also raised after an abort, when the bubble has already | |
116 // been closed. | |
117 if (GetBubbleController()->GetActiveSessionID() == session_id) { | |
118 DCHECK(RequiresBubble(session_id)); | |
119 GetBubbleController()->SetBubbleRecognizingMode(); | |
120 } | |
121 } | |
122 | |
123 void ChromeSpeechRecognitionManagerDelegateBubbleUI::OnRecognitionError( | |
124 int session_id, const content::SpeechRecognitionError& error) { | |
125 ChromeSpeechRecognitionManagerDelegate::OnRecognitionError(session_id, error); | |
126 | |
127 // An error can be dispatched when the bubble is not visible anymore. | |
128 if (GetBubbleController()->GetActiveSessionID() != session_id) | |
129 return; | |
130 DCHECK(RequiresBubble(session_id)); | |
131 | |
132 int error_message_id = 0; | |
133 switch (error.code) { | |
134 case content::SPEECH_RECOGNITION_ERROR_AUDIO: | |
135 switch (error.details) { | |
136 case content::SPEECH_AUDIO_ERROR_DETAILS_NO_MIC: | |
137 error_message_id = IDS_SPEECH_INPUT_NO_MIC; | |
138 break; | |
139 default: | |
140 error_message_id = IDS_SPEECH_INPUT_MIC_ERROR; | |
141 break; | |
142 } | |
143 break; | |
144 case content::SPEECH_RECOGNITION_ERROR_ABORTED: | |
145 error_message_id = IDS_SPEECH_INPUT_ABORTED; | |
146 break; | |
147 case content::SPEECH_RECOGNITION_ERROR_NO_SPEECH: | |
148 error_message_id = IDS_SPEECH_INPUT_NO_SPEECH; | |
149 break; | |
150 case content::SPEECH_RECOGNITION_ERROR_NO_MATCH: | |
151 error_message_id = IDS_SPEECH_INPUT_NO_RESULTS; | |
152 break; | |
153 case content::SPEECH_RECOGNITION_ERROR_NETWORK: | |
154 error_message_id = IDS_SPEECH_INPUT_NET_ERROR; | |
155 break; | |
156 default: | |
157 NOTREACHED() << "unknown error " << error.code; | |
158 return; | |
159 } | |
160 GetBubbleController()->SetBubbleMessage( | |
161 l10n_util::GetStringUTF16(error_message_id)); | |
162 | |
163 } | |
164 | |
165 void ChromeSpeechRecognitionManagerDelegateBubbleUI::OnAudioLevelsChange( | |
166 int session_id, float volume, float noise_volume) { | |
167 ChromeSpeechRecognitionManagerDelegate::OnAudioLevelsChange( | |
168 session_id, volume, noise_volume); | |
169 | |
170 if (GetBubbleController()->GetActiveSessionID() == session_id) { | |
171 DCHECK(RequiresBubble(session_id)); | |
172 GetBubbleController()->SetBubbleInputVolume(volume, noise_volume); | |
173 } | |
174 } | |
175 | |
176 void ChromeSpeechRecognitionManagerDelegateBubbleUI::OnRecognitionEnd( | |
177 int session_id) { | |
178 ChromeSpeechRecognitionManagerDelegate::OnRecognitionEnd(session_id); | |
179 | |
180 // The only case in which the OnRecognitionEnd should not close the bubble is | |
181 // when we are showing an error. In this case the bubble will be closed by | |
182 // the |InfoBubbleFocusChanged| method, when the users clicks either the | |
183 // "Cancel" button or outside of the bubble. | |
184 if (GetBubbleController()->GetActiveSessionID() == session_id && | |
185 !GetBubbleController()->IsShowingMessage()) { | |
186 DCHECK(RequiresBubble(session_id)); | |
187 GetBubbleController()->CloseBubble(); | |
188 } | |
189 } | |
190 | |
191 void ChromeSpeechRecognitionManagerDelegateBubbleUI::TabClosedCallback( | |
192 int render_process_id, int render_view_id) { | |
193 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
194 ChromeSpeechRecognitionManagerDelegate::TabClosedCallback( | |
195 render_process_id, render_view_id); | |
196 | |
197 // Avoid instantiating a bubble_controller_ if not needed. Thus, prefer a | |
198 // checked access to bubble_controller_ to GetBubbleController(). | |
199 if (bubble_controller_.get()) | |
200 bubble_controller_->CloseBubbleForRenderViewOnUIThread(render_process_id, | |
201 render_view_id); | |
202 } | |
203 | |
204 SpeechRecognitionBubbleController* | |
205 ChromeSpeechRecognitionManagerDelegateBubbleUI::GetBubbleController() { | |
206 if (!bubble_controller_.get()) | |
207 bubble_controller_ = new SpeechRecognitionBubbleController(this); | |
208 return bubble_controller_.get(); | |
209 } | |
210 | |
211 void ChromeSpeechRecognitionManagerDelegateBubbleUI::RestartLastSession() { | |
212 DCHECK(last_session_config_.get()); | |
213 SpeechRecognitionManager* manager = SpeechRecognitionManager::GetInstance(); | |
214 const int new_session_id = manager->CreateSession(*last_session_config_); | |
215 DCHECK_NE(SpeechRecognitionManager::kSessionIDInvalid, new_session_id); | |
216 last_session_config_.reset(); | |
217 manager->StartSession(new_session_id); | |
218 } | |
219 | |
220 } // namespace speech | |
OLD | NEW |