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_bubble_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 15using content::BrowserThread; 16using content::SpeechRecognitionManager; 17 18namespace { 19 20bool RequiresBubble(int session_id) { 21 return SpeechRecognitionManager::GetInstance()-> 22 GetSessionContext(session_id).requested_by_page_element; 23} 24 25} // namespace 26 27namespace speech { 28 29ChromeSpeechRecognitionManagerDelegateBubbleUI 30::ChromeSpeechRecognitionManagerDelegateBubbleUI() { 31} 32 33ChromeSpeechRecognitionManagerDelegateBubbleUI 34::~ChromeSpeechRecognitionManagerDelegateBubbleUI() { 35 if (bubble_controller_.get()) 36 bubble_controller_->CloseBubble(); 37} 38 39void 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 61void 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 81void 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 101void 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 111void 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 123void 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 165void 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 176void 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 191void ChromeSpeechRecognitionManagerDelegateBubbleUI::TabClosedCallback( 192 int render_process_id, int render_view_id) { 193 ChromeSpeechRecognitionManagerDelegate::TabClosedCallback( 194 render_process_id, render_view_id); 195 196 if (bubble_controller_.get() && 197 bubble_controller_->IsShowingBubbleForRenderView(render_process_id, 198 render_view_id)) { 199 bubble_controller_->CloseBubble(); 200 } 201} 202 203SpeechRecognitionBubbleController* 204ChromeSpeechRecognitionManagerDelegateBubbleUI::GetBubbleController() { 205 if (!bubble_controller_.get()) 206 bubble_controller_ = new SpeechRecognitionBubbleController(this); 207 return bubble_controller_.get(); 208} 209 210void ChromeSpeechRecognitionManagerDelegateBubbleUI::RestartLastSession() { 211 DCHECK(last_session_config_.get()); 212 SpeechRecognitionManager* manager = SpeechRecognitionManager::GetInstance(); 213 const int new_session_id = manager->CreateSession(*last_session_config_); 214 DCHECK_NE(SpeechRecognitionManager::kSessionIDInvalid, new_session_id); 215 last_session_config_.reset(); 216 manager->StartSession(new_session_id); 217} 218 219} // namespace speech 220