1 /**
2  * Copyright (C) 2021 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 package com.android.car.voicecontrol;
17 
18 import android.content.Intent;
19 import android.os.Bundle;
20 import android.os.Handler;
21 import android.os.RemoteException;
22 import android.speech.SpeechRecognizer;
23 import android.util.Log;
24 
25 import androidx.annotation.Nullable;
26 
27 import java.util.ArrayList;
28 import java.util.Collections;
29 
30 /**
31  * Sample {@link android.speech.RecognitionService}. A real implementation would obtain access to
32  * the microphone and stream audio to either a backend service or an offline on-device voice
33  * recognition model. This sample code just pretends to have detected a set of pre-recorded phrases.
34  */
35 public class RecognitionService extends android.speech.RecognitionService {
36     private static final String TAG = "Mica.RecognitionService";
37     private Callback mCurrentListener;
38     private Handler mHandler = new Handler();
39     private Runnable mExecutionTask = this::onExecutionComplete;
40     private String[] mPrerecordedPhrases;
41     private int mPrerecordedPhraseIndex;
42 
43     @Override
onCreate()44     public void onCreate() {
45         super.onCreate();
46         mPrerecordedPhrases = getResources().getStringArray(R.array.recognizer_phrases);
47         mPrerecordedPhraseIndex = 0;
48     }
49 
50     @Override
onStartListening(Intent recognizerIntent, Callback listener)51     protected void onStartListening(Intent recognizerIntent, Callback listener) {
52         Log.d(TAG, "onStartListening(recognizerIntent: " + recognizerIntent
53                         + ", listener: " + listener);
54         if (mCurrentListener != null) {
55             mHandler.removeCallbacks(mExecutionTask);
56             sendEndOfSpeech();
57             sendResults(null);
58         }
59         mCurrentListener = listener;
60         sendBeginningOfSpeech();
61         mHandler.postDelayed(mExecutionTask, 1000);
62     }
63 
64     @Override
onCancel(Callback listener)65     protected void onCancel(Callback listener) {
66         Log.d(TAG, "onCancel(listener: " + listener);
67         if (mCurrentListener == null || listener != mCurrentListener) {
68             return;
69         }
70         mHandler.removeCallbacks(mExecutionTask);
71         mCurrentListener = null;
72     }
73 
74     @Override
onStopListening(Callback listener)75     protected void onStopListening(Callback listener) {
76         Log.d(TAG, "onStopListening(listener: " + listener);
77         if (mCurrentListener == null || listener != mCurrentListener) {
78             return;
79         }
80         mHandler.removeCallbacks(mExecutionTask);
81         sendEndOfSpeech();
82         sendResults(null);
83         mCurrentListener = null;
84     }
85 
onExecutionComplete()86     private void onExecutionComplete() {
87         if (mCurrentListener != null) {
88             sendEndOfSpeech();
89             sendResults(getNextResult());
90         }
91     }
92 
sendBeginningOfSpeech()93     private void sendBeginningOfSpeech() {
94         try {
95             mCurrentListener.beginningOfSpeech();
96             Log.d(TAG, "Sent beginningOfSpeech");
97         } catch (RemoteException e) {
98             Log.e(TAG, "Error initiating speech", e.getCause());
99         }
100     }
101 
sendEndOfSpeech()102     private void sendEndOfSpeech() {
103         try {
104             mCurrentListener.endOfSpeech();
105             Log.d(TAG, "Sent endOfSpeech");
106         } catch (RemoteException e) {
107             Log.e(TAG, "Error cancelling speech", e.getCause());
108         }
109     }
110 
getNextResult()111     private String getNextResult() {
112         String currentString = mPrerecordedPhrases[mPrerecordedPhraseIndex++];
113         mPrerecordedPhraseIndex %= mPrerecordedPhrases.length;
114         return currentString;
115     }
116 
sendResults(@ullable String result)117     private void sendResults(@Nullable String result) {
118         try {
119             mCurrentListener.endOfSpeech();
120             Bundle results = new Bundle();
121             results.putStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION, result != null
122                     ? new ArrayList<>(Collections.singletonList(result))
123                     : new ArrayList<>());
124             results.putFloatArray(SpeechRecognizer.CONFIDENCE_SCORES, result != null
125                     ? new float[]{1.0f}
126                     : new float[0]);
127             mCurrentListener.results(results);
128             Log.d(TAG, "Sent results: " + results);
129         } catch (RemoteException e) {
130             Log.e(TAG, "Error sending results", e.getCause());
131         }
132     }
133 }
134