1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 
17 package android.voicerecognition.cts;
18 
19 import android.app.Activity;
20 import android.content.ComponentName;
21 import android.content.Intent;
22 import android.os.Bundle;
23 import android.os.Handler;
24 import android.speech.ModelDownloadListener;
25 import android.speech.RecognitionListener;
26 import android.speech.RecognitionSupportCallback;
27 import android.speech.RecognizerIntent;
28 import android.speech.SpeechRecognizer;
29 
30 import androidx.annotation.NonNull;
31 
32 import java.util.ArrayList;
33 import java.util.List;
34 import java.util.concurrent.CountDownLatch;
35 import java.util.concurrent.Executors;
36 
37 /**
38  * An activity that uses SpeechRecognition APIs. SpeechRecognition will bind the RecognitionService
39  * to provide the voice recognition functions.
40  */
41 public class SpeechRecognitionActivity extends Activity {
42     private final String TAG = "SpeechRecognitionActivity";
43 
44     private List<RecognizerInfo> mRecognizerInfos;
45 
46     private Handler mHandler;
47 
48     @Override
onCreate(Bundle savedInstanceState)49     protected void onCreate(Bundle savedInstanceState) {
50         super.onCreate(savedInstanceState);
51         mHandler = new Handler(getMainLooper());
52         setContentView(R.layout.main);
53     }
54 
55     @Override
onDestroy()56     protected void onDestroy() {
57         destroyAllRecognizers();
58         super.onDestroy();
59     }
60 
startListeningDefault()61     public void startListeningDefault() {
62         startListening(/* index */ 0);
63     }
64 
startListening(int index)65     public void startListening(int index) {
66         final Intent recognizerIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
67         recognizerIntent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE, this.getPackageName());
68         startListening(recognizerIntent, index);
69     }
70 
startListening(Intent intent, int index)71     public void startListening(Intent intent, int index) {
72         mHandler.post(() -> mRecognizerInfos.get(index).mRecognizer.startListening(intent));
73     }
74 
stopListeningDefault()75     public void stopListeningDefault() {
76         stopListening(/* index */ 0);
77     }
78 
stopListening(int index)79     public void stopListening(int index) {
80         mHandler.post(mRecognizerInfos.get(index).mRecognizer::stopListening);
81     }
82 
cancelDefault()83     public void cancelDefault() {
84         cancel(/* index */ 0);
85     }
86 
cancel(int index)87     public void cancel(int index) {
88         mHandler.post(mRecognizerInfos.get(index).mRecognizer::cancel);
89     }
90 
destroyRecognizerDefault()91     public void destroyRecognizerDefault() {
92         destroyRecognizer(/* index */ 0);
93     }
94 
destroyRecognizer(int index)95     public void destroyRecognizer(int index) {
96         mHandler.post(mRecognizerInfos.get(index).mRecognizer::destroy);
97     }
98 
99     /**
100      * Destroy any / all recognizers used by this Activity.
101      */
destroyAllRecognizers()102     public void destroyAllRecognizers() {
103         mHandler.post(() -> {
104             if (mRecognizerInfos != null) {
105                 for (RecognizerInfo recognizerInfo : mRecognizerInfos) {
106                     if (recognizerInfo.mRecognizer != null) {
107                         recognizerInfo.mRecognizer.destroy();
108                     }
109                 }
110                 mRecognizerInfos.clear();
111             }
112         });
113     }
114 
checkRecognitionSupportDefault(Intent intent, RecognitionSupportCallback rsc)115     public void checkRecognitionSupportDefault(Intent intent, RecognitionSupportCallback rsc) {
116         checkRecognitionSupport(intent, rsc, /* index */ 0);
117     }
118 
checkRecognitionSupport(Intent intent, RecognitionSupportCallback rsc, int index)119     public void checkRecognitionSupport(Intent intent, RecognitionSupportCallback rsc, int index) {
120         mHandler.post(() -> mRecognizerInfos.get(index).mRecognizer.checkRecognitionSupport(
121                 intent, Executors.newSingleThreadExecutor(), rsc));
122     }
123 
triggerModelDownloadDefault(Intent intent)124     public void triggerModelDownloadDefault(Intent intent) {
125         triggerModelDownload(intent, /* index */ 0);
126     }
127 
triggerModelDownload(Intent intent, int index)128     public void triggerModelDownload(Intent intent, int index) {
129         mHandler.post(() -> mRecognizerInfos.get(index).mRecognizer.triggerModelDownload(intent));
130     }
131 
triggerModelDownloadWithListenerDefault( Intent intent, ModelDownloadListener listener)132     public void triggerModelDownloadWithListenerDefault(
133             Intent intent, ModelDownloadListener listener) {
134         triggerModelDownloadWithListener(intent, listener, /* index */ 0);
135     }
136 
triggerModelDownloadWithListener( Intent intent, ModelDownloadListener listener, int index)137     public void triggerModelDownloadWithListener(
138             Intent intent, ModelDownloadListener listener, int index) {
139         mHandler.post(() -> mRecognizerInfos.get(index)
140                 .mRecognizer.triggerModelDownload(
141                         intent,
142                         Executors.newSingleThreadExecutor(),
143                         listener));
144     }
145 
initDefault(boolean onDevice, String customRecognizerComponent)146     public void initDefault(boolean onDevice, String customRecognizerComponent) {
147         init(onDevice, customRecognizerComponent, /* recognizerCount */ 1);
148     }
149 
init(boolean onDevice, String customRecognizerComponent, int recognizerCount)150     public void init(boolean onDevice, String customRecognizerComponent, int recognizerCount) {
151         mRecognizerInfos = new ArrayList<>();
152 
153         for (int i = 0; i < recognizerCount; i++) {
154             mHandler.post(() -> {
155                 final SpeechRecognizer recognizer;
156                 if (onDevice) {
157                     recognizer = SpeechRecognizer.createOnDeviceTestingSpeechRecognizer(this);
158                 } else if (customRecognizerComponent != null) {
159                     recognizer = SpeechRecognizer.createSpeechRecognizer(this,
160                             ComponentName.unflattenFromString(customRecognizerComponent));
161                 } else {
162                     recognizer = SpeechRecognizer.createSpeechRecognizer(this);
163                 }
164                 mRecognizerInfos.add(new RecognizerInfo(recognizer));
165             });
166         }
167     }
168 
getRecognizerInfoDefault()169     RecognizerInfo getRecognizerInfoDefault() {
170         return getRecognizerInfo(/* index */ 0);
171     }
172 
getRecognizerInfo(int index)173     RecognizerInfo getRecognizerInfo(int index) {
174         return mRecognizerInfos.get(index);
175     }
176 
getRecognizerCount()177     int getRecognizerCount() {
178         return mRecognizerInfos.size();
179     }
180 
181     /**
182      * Data class containing information about a recognizer object used in the activity:
183      * <ul>
184      *   <li> {@link RecognizerInfo#mRecognizer} - the recognizer object;
185      *   <li> {@link RecognizerInfo#mCallbackMethodsInvoked} - list of {@link CallbackMethod}s
186      *   invoked on the recognizer's listener;
187      *   <li> {@link RecognizerInfo#mStartListeningCalled} - flag denoting
188      *   if the recognizer has been used;
189      *   <li> {@link RecognizerInfo#mCountDownLatch} - synchronization object used
190      *   to emulate waiting on the recognition result.
191      */
192     static class RecognizerInfo {
193         final SpeechRecognizer mRecognizer;
194         final List<CallbackMethod> mCallbackMethodsInvoked;
195         final List<Integer> mErrorCodesReceived;
196         public boolean mStartListeningCalled;
197         public CountDownLatch mCountDownLatch;
198 
RecognizerInfo(SpeechRecognizer recognizer)199         RecognizerInfo(SpeechRecognizer recognizer) {
200             mRecognizer = recognizer;
201             mCallbackMethodsInvoked = new ArrayList<>();
202             mErrorCodesReceived = new ArrayList<>();
203             mStartListeningCalled = false;
204             mCountDownLatch = new CountDownLatch(1);
205 
206             mRecognizer.setRecognitionListener(new SpeechRecognizerListener());
207         }
208 
209         class SpeechRecognizerListener implements RecognitionListener {
210             @Override
onReadyForSpeech(Bundle params)211             public void onReadyForSpeech(Bundle params) {
212                 mCallbackMethodsInvoked.add(CallbackMethod.CALLBACK_METHOD_READY_FOR_SPEECH);
213             }
214 
215             @Override
onBeginningOfSpeech()216             public void onBeginningOfSpeech() {
217                 mCallbackMethodsInvoked.add(CallbackMethod.CALLBACK_METHOD_BEGINNING_OF_SPEECH);
218             }
219 
220             @Override
onRmsChanged(float rmsdB)221             public void onRmsChanged(float rmsdB) {
222                 mCallbackMethodsInvoked.add(CallbackMethod.CALLBACK_METHOD_RMS_CHANGED);
223             }
224 
225             @Override
onBufferReceived(byte[] buffer)226             public void onBufferReceived(byte[] buffer) {
227                 mCallbackMethodsInvoked.add(CallbackMethod.CALLBACK_METHOD_BUFFER_RECEIVED);
228             }
229 
230             @Override
onEndOfSpeech()231             public void onEndOfSpeech() {
232                 mCallbackMethodsInvoked.add(CallbackMethod.CALLBACK_METHOD_END_OF_SPEECH);
233             }
234 
235             @Override
onError(int error)236             public void onError(int error) {
237                 mCallbackMethodsInvoked.add(CallbackMethod.CALLBACK_METHOD_ERROR);
238                 mErrorCodesReceived.add(error);
239             }
240 
241             @Override
onResults(Bundle results)242             public void onResults(Bundle results) {
243                 mCallbackMethodsInvoked.add(CallbackMethod.CALLBACK_METHOD_RESULTS);
244                 mStartListeningCalled = true;
245                 mCountDownLatch.countDown();
246             }
247 
248             @Override
onPartialResults(Bundle partialResults)249             public void onPartialResults(Bundle partialResults) {
250                 mCallbackMethodsInvoked.add(CallbackMethod.CALLBACK_METHOD_PARTIAL_RESULTS);
251             }
252 
253             @Override
onSegmentResults(@onNull Bundle segmentResults)254             public void onSegmentResults(@NonNull Bundle segmentResults) {
255                 mCallbackMethodsInvoked.add(CallbackMethod.CALLBACK_METHOD_SEGMENTS_RESULTS);
256             }
257 
258             @Override
onEndOfSegmentedSession()259             public void onEndOfSegmentedSession() {
260                 mCallbackMethodsInvoked.add(CallbackMethod.CALLBACK_METHOD_END_SEGMENTED_SESSION);
261             }
262 
263             @Override
onLanguageDetection(@onNull Bundle results)264             public void onLanguageDetection(@NonNull Bundle results) {
265                 mCallbackMethodsInvoked.add(CallbackMethod.CALLBACK_METHOD_LANGUAGE_DETECTION);
266             }
267 
268             @Override
onEvent(int eventType, Bundle params)269             public void onEvent(int eventType, Bundle params) {
270             }
271         }
272     }
273 }
274