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.
5 #include "chrome/browser/speech/chrome_speech_recognition_manager_delegate_bubble_ui.h"
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"
15 using content::BrowserThread
;
16 using content::SpeechRecognitionManager
;
20 bool RequiresBubble(int session_id
) {
21 return SpeechRecognitionManager::GetInstance()->
22 GetSessionContext(session_id
).requested_by_page_element
;
29 ChromeSpeechRecognitionManagerDelegateBubbleUI
30 ::ChromeSpeechRecognitionManagerDelegateBubbleUI() {
33 ChromeSpeechRecognitionManagerDelegateBubbleUI
34 ::~ChromeSpeechRecognitionManagerDelegateBubbleUI() {
35 if (bubble_controller_
.get())
36 bubble_controller_
->CloseBubble();
39 void ChromeSpeechRecognitionManagerDelegateBubbleUI::InfoBubbleButtonClicked(
40 int session_id
, SpeechRecognitionBubble::Button button
) {
41 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
43 // Note, the session might have been destroyed, therefore avoid calls to the
44 // manager which imply its existance (e.g., GetSessionContext()).
46 if (button
== SpeechRecognitionBubble::BUTTON_CANCEL
) {
47 GetBubbleController()->CloseBubble();
48 last_session_config_
.reset();
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();
61 void ChromeSpeechRecognitionManagerDelegateBubbleUI::InfoBubbleFocusChanged(
63 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
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
)
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();
77 // Clicking outside the bubble means we should abort.
78 SpeechRecognitionManager::GetInstance()->AbortSession(session_id
);
81 void ChromeSpeechRecognitionManagerDelegateBubbleUI::OnRecognitionStart(
83 ChromeSpeechRecognitionManagerDelegate::OnRecognitionStart(session_id
);
85 const content::SpeechRecognitionSessionContext
& context
=
86 SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id
);
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
)));
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
);
101 void ChromeSpeechRecognitionManagerDelegateBubbleUI::OnAudioStart(
103 ChromeSpeechRecognitionManagerDelegate::OnAudioStart(session_id
);
105 if (RequiresBubble(session_id
)) {
106 DCHECK_EQ(session_id
, GetBubbleController()->GetActiveSessionID());
107 GetBubbleController()->SetBubbleRecordingMode();
111 void ChromeSpeechRecognitionManagerDelegateBubbleUI::OnAudioEnd(
113 ChromeSpeechRecognitionManagerDelegate::OnAudioEnd(session_id
);
115 // OnAudioEnd can be also raised after an abort, when the bubble has already
117 if (GetBubbleController()->GetActiveSessionID() == session_id
) {
118 DCHECK(RequiresBubble(session_id
));
119 GetBubbleController()->SetBubbleRecognizingMode();
123 void ChromeSpeechRecognitionManagerDelegateBubbleUI::OnRecognitionError(
124 int session_id
, const content::SpeechRecognitionError
& error
) {
125 ChromeSpeechRecognitionManagerDelegate::OnRecognitionError(session_id
, error
);
127 // An error can be dispatched when the bubble is not visible anymore.
128 if (GetBubbleController()->GetActiveSessionID() != session_id
)
130 DCHECK(RequiresBubble(session_id
));
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
;
140 error_message_id
= IDS_SPEECH_INPUT_MIC_ERROR
;
144 case content::SPEECH_RECOGNITION_ERROR_ABORTED
:
145 error_message_id
= IDS_SPEECH_INPUT_ABORTED
;
147 case content::SPEECH_RECOGNITION_ERROR_NO_SPEECH
:
148 error_message_id
= IDS_SPEECH_INPUT_NO_SPEECH
;
150 case content::SPEECH_RECOGNITION_ERROR_NO_MATCH
:
151 error_message_id
= IDS_SPEECH_INPUT_NO_RESULTS
;
153 case content::SPEECH_RECOGNITION_ERROR_NETWORK
:
154 error_message_id
= IDS_SPEECH_INPUT_NET_ERROR
;
157 NOTREACHED() << "unknown error " << error
.code
;
160 GetBubbleController()->SetBubbleMessage(
161 l10n_util::GetStringUTF16(error_message_id
));
165 void ChromeSpeechRecognitionManagerDelegateBubbleUI::OnAudioLevelsChange(
166 int session_id
, float volume
, float noise_volume
) {
167 ChromeSpeechRecognitionManagerDelegate::OnAudioLevelsChange(
168 session_id
, volume
, noise_volume
);
170 if (GetBubbleController()->GetActiveSessionID() == session_id
) {
171 DCHECK(RequiresBubble(session_id
));
172 GetBubbleController()->SetBubbleInputVolume(volume
, noise_volume
);
176 void ChromeSpeechRecognitionManagerDelegateBubbleUI::OnRecognitionEnd(
178 ChromeSpeechRecognitionManagerDelegate::OnRecognitionEnd(session_id
);
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();
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
);
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
,
204 SpeechRecognitionBubbleController
*
205 ChromeSpeechRecognitionManagerDelegateBubbleUI::GetBubbleController() {
206 if (!bubble_controller_
.get())
207 bubble_controller_
= new SpeechRecognitionBubbleController(this);
208 return bubble_controller_
.get();
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
);
220 } // namespace speech