| 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 |