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 "content/browser/speech/speech_recognizer_impl_android.h" 6 7 #include "base/android/jni_android.h" 8 #include "base/android/jni_array.h" 9 #include "base/android/jni_string.h" 10 #include "base/android/scoped_java_ref.h" 11 #include "base/bind.h" 12 #include "base/strings/utf_string_conversions.h" 13 #include "content/public/browser/browser_thread.h" 14 #include "content/public/browser/speech_recognition_event_listener.h" 15 #include "content/public/browser/speech_recognition_manager.h" 16 #include "content/public/browser/speech_recognition_session_config.h" 17 #include "content/public/common/speech_recognition_grammar.h" 18 #include "content/public/common/speech_recognition_result.h" 19 #include "jni/SpeechRecognition_jni.h" 20 21 using base::android::AppendJavaStringArrayToStringVector; 22 using base::android::AttachCurrentThread; 23 using base::android::ConvertUTF8ToJavaString; 24 using base::android::GetApplicationContext; 25 using base::android::JavaFloatArrayToFloatVector; 26 27 namespace content { 28 29 SpeechRecognizerImplAndroid::SpeechRecognizerImplAndroid( 30 SpeechRecognitionEventListener* listener, 31 int session_id) 32 : SpeechRecognizer(listener, session_id), 33 state_(STATE_IDLE) { 34 } 35 36 SpeechRecognizerImplAndroid::~SpeechRecognizerImplAndroid() { } 37 38 void SpeechRecognizerImplAndroid::StartRecognition( 39 const std::string& device_id) { 40 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 41 // TODO(xians): Open the correct device for speech on Android. 42 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind( 43 &SpeechRecognitionEventListener::OnRecognitionStart, 44 base::Unretained(listener()), 45 session_id())); 46 SpeechRecognitionSessionConfig config = 47 SpeechRecognitionManager::GetInstance()->GetSessionConfig(session_id()); 48 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind( 49 &content::SpeechRecognizerImplAndroid::StartRecognitionOnUIThread, this, 50 config.language, config.continuous, config.interim_results)); 51 } 52 53 void SpeechRecognizerImplAndroid::StartRecognitionOnUIThread( 54 std::string language, bool continuous, bool interim_results) { 55 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 56 JNIEnv* env = AttachCurrentThread(); 57 j_recognition_.Reset(Java_SpeechRecognition_createSpeechRecognition(env, 58 GetApplicationContext(), reinterpret_cast<intptr_t>(this))); 59 Java_SpeechRecognition_startRecognition(env, j_recognition_.obj(), 60 ConvertUTF8ToJavaString(env, language).obj(), continuous, 61 interim_results); 62 } 63 64 void SpeechRecognizerImplAndroid::AbortRecognition() { 65 if (BrowserThread::CurrentlyOn(BrowserThread::IO)) { 66 state_ = STATE_IDLE; 67 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind( 68 &content::SpeechRecognizerImplAndroid::AbortRecognition, this)); 69 return; 70 } 71 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 72 JNIEnv* env = AttachCurrentThread(); 73 if (!j_recognition_.is_null()) 74 Java_SpeechRecognition_abortRecognition(env, j_recognition_.obj()); 75 } 76 77 void SpeechRecognizerImplAndroid::StopAudioCapture() { 78 if (BrowserThread::CurrentlyOn(BrowserThread::IO)) { 79 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind( 80 &content::SpeechRecognizerImplAndroid::StopAudioCapture, this)); 81 return; 82 } 83 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 84 JNIEnv* env = AttachCurrentThread(); 85 if (!j_recognition_.is_null()) 86 Java_SpeechRecognition_stopRecognition(env, j_recognition_.obj()); 87 } 88 89 bool SpeechRecognizerImplAndroid::IsActive() const { 90 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 91 return state_ != STATE_IDLE; 92 } 93 94 bool SpeechRecognizerImplAndroid::IsCapturingAudio() const { 95 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 96 return state_ == STATE_CAPTURING_AUDIO; 97 } 98 99 void SpeechRecognizerImplAndroid::OnAudioStart(JNIEnv* env, jobject obj) { 100 if (BrowserThread::CurrentlyOn(BrowserThread::UI)) { 101 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind( 102 &SpeechRecognizerImplAndroid::OnAudioStart, this, 103 static_cast<JNIEnv*>(NULL), static_cast<jobject>(NULL))); 104 return; 105 } 106 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 107 state_ = STATE_CAPTURING_AUDIO; 108 listener()->OnAudioStart(session_id()); 109 } 110 111 void SpeechRecognizerImplAndroid::OnSoundStart(JNIEnv* env, jobject obj) { 112 if (BrowserThread::CurrentlyOn(BrowserThread::UI)) { 113 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind( 114 &SpeechRecognizerImplAndroid::OnSoundStart, this, 115 static_cast<JNIEnv*>(NULL), static_cast<jobject>(NULL))); 116 return; 117 } 118 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 119 listener()->OnSoundStart(session_id()); 120 } 121 122 void SpeechRecognizerImplAndroid::OnSoundEnd(JNIEnv* env, jobject obj) { 123 if (BrowserThread::CurrentlyOn(BrowserThread::UI)) { 124 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind( 125 &SpeechRecognizerImplAndroid::OnSoundEnd, this, 126 static_cast<JNIEnv*>(NULL), static_cast<jobject>(NULL))); 127 return; 128 } 129 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 130 listener()->OnSoundEnd(session_id()); 131 } 132 133 void SpeechRecognizerImplAndroid::OnAudioEnd(JNIEnv* env, jobject obj) { 134 if (BrowserThread::CurrentlyOn(BrowserThread::UI)) { 135 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind( 136 &SpeechRecognizerImplAndroid::OnAudioEnd, this, 137 static_cast<JNIEnv*>(NULL), static_cast<jobject>(NULL))); 138 return; 139 } 140 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 141 if (state_ == STATE_CAPTURING_AUDIO) 142 state_ = STATE_AWAITING_FINAL_RESULT; 143 listener()->OnAudioEnd(session_id()); 144 } 145 146 void SpeechRecognizerImplAndroid::OnRecognitionResults(JNIEnv* env, jobject obj, 147 jobjectArray strings, jfloatArray floats, jboolean provisional) { 148 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 149 std::vector<base::string16> options; 150 AppendJavaStringArrayToStringVector(env, strings, &options); 151 std::vector<float> scores(options.size(), 0.0); 152 if (floats != NULL) 153 JavaFloatArrayToFloatVector(env, floats, &scores); 154 SpeechRecognitionResults results; 155 results.push_back(SpeechRecognitionResult()); 156 SpeechRecognitionResult& result = results.back(); 157 CHECK_EQ(options.size(), scores.size()); 158 for (size_t i = 0; i < options.size(); ++i) { 159 result.hypotheses.push_back(SpeechRecognitionHypothesis( 160 options[i], static_cast<double>(scores[i]))); 161 } 162 result.is_provisional = provisional; 163 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind( 164 &SpeechRecognizerImplAndroid::OnRecognitionResultsOnIOThread, 165 this, results)); 166 } 167 168 void SpeechRecognizerImplAndroid::OnRecognitionResultsOnIOThread( 169 SpeechRecognitionResults const &results) { 170 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 171 listener()->OnRecognitionResults(session_id(), results); 172 } 173 174 void SpeechRecognizerImplAndroid::OnRecognitionError(JNIEnv* env, 175 jobject obj, jint error) { 176 if (BrowserThread::CurrentlyOn(BrowserThread::UI)) { 177 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind( 178 &SpeechRecognizerImplAndroid::OnRecognitionError, this, 179 static_cast<JNIEnv*>(NULL), static_cast<jobject>(NULL), error)); 180 return; 181 } 182 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 183 SpeechRecognitionErrorCode code = 184 static_cast<SpeechRecognitionErrorCode>(error); 185 listener()->OnRecognitionError(session_id(), SpeechRecognitionError(code)); 186 } 187 188 void SpeechRecognizerImplAndroid::OnRecognitionEnd(JNIEnv* env, 189 jobject obj) { 190 if (BrowserThread::CurrentlyOn(BrowserThread::UI)) { 191 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind( 192 &SpeechRecognizerImplAndroid::OnRecognitionEnd, this, 193 static_cast<JNIEnv*>(NULL), static_cast<jobject>(NULL))); 194 return; 195 } 196 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 197 state_ = STATE_IDLE; 198 listener()->OnRecognitionEnd(session_id()); 199 } 200 201 // static 202 bool SpeechRecognizerImplAndroid::RegisterSpeechRecognizer(JNIEnv* env) { 203 return RegisterNativesImpl(env); 204 } 205 206 } // namespace content 207