@capgo/capacitor-speech-recognition 7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,406 @@
1
+ package app.capgo.speechrecognition;
2
+
3
+ import android.Manifest;
4
+ import android.app.Activity;
5
+ import android.content.Intent;
6
+ import android.os.Build;
7
+ import android.os.Bundle;
8
+ import android.speech.RecognitionListener;
9
+ import android.speech.RecognizerIntent;
10
+ import android.speech.SpeechRecognizer;
11
+ import androidx.activity.result.ActivityResult;
12
+ import com.getcapacitor.JSArray;
13
+ import com.getcapacitor.JSObject;
14
+ import com.getcapacitor.Logger;
15
+ import com.getcapacitor.PermissionState;
16
+ import com.getcapacitor.Plugin;
17
+ import com.getcapacitor.PluginCall;
18
+ import com.getcapacitor.PluginMethod;
19
+ import com.getcapacitor.annotation.ActivityCallback;
20
+ import com.getcapacitor.annotation.CapacitorPlugin;
21
+ import com.getcapacitor.annotation.Permission;
22
+ import com.getcapacitor.annotation.PermissionCallback;
23
+ import java.util.ArrayList;
24
+ import java.util.List;
25
+ import java.util.Locale;
26
+ import java.util.concurrent.locks.ReentrantLock;
27
+ import org.json.JSONArray;
28
+
29
+ @CapacitorPlugin(
30
+ name = "SpeechRecognition",
31
+ permissions = { @Permission(strings = { Manifest.permission.RECORD_AUDIO }, alias = SpeechRecognitionPlugin.SPEECH_RECOGNITION) }
32
+ )
33
+ public class SpeechRecognitionPlugin extends Plugin implements Constants {
34
+
35
+ public static final String SPEECH_RECOGNITION = "speechRecognition";
36
+ private static final String TAG = "SpeechRecognition";
37
+
38
+ private Receiver languageReceiver;
39
+ private SpeechRecognizer speechRecognizer;
40
+ private final ReentrantLock lock = new ReentrantLock();
41
+ private boolean listening = false;
42
+ private JSONArray previousPartialResults = new JSONArray();
43
+
44
+ @Override
45
+ public void load() {
46
+ super.load();
47
+ bridge
48
+ .getWebView()
49
+ .post(() -> {
50
+ speechRecognizer = SpeechRecognizer.createSpeechRecognizer(bridge.getActivity());
51
+ SpeechRecognitionListener listener = new SpeechRecognitionListener();
52
+ speechRecognizer.setRecognitionListener(listener);
53
+ Logger.info(getLogTag(), "Instantiated SpeechRecognizer in load()");
54
+ });
55
+ }
56
+
57
+ @PluginMethod
58
+ public void available(PluginCall call) {
59
+ boolean val = SpeechRecognizer.isRecognitionAvailable(bridge.getContext());
60
+ call.resolve(new JSObject().put("available", val));
61
+ }
62
+
63
+ @PluginMethod
64
+ public void start(PluginCall call) {
65
+ if (!SpeechRecognizer.isRecognitionAvailable(bridge.getContext())) {
66
+ call.unavailable(NOT_AVAILABLE);
67
+ return;
68
+ }
69
+
70
+ if (getPermissionState(SPEECH_RECOGNITION) != PermissionState.GRANTED) {
71
+ call.reject(MISSING_PERMISSION);
72
+ return;
73
+ }
74
+
75
+ String language = call.getString("language", Locale.getDefault().toString());
76
+ int maxResults = call.getInt("maxResults", MAX_RESULTS);
77
+ String prompt = call.getString("prompt", null);
78
+ boolean partialResults = call.getBoolean("partialResults", false);
79
+ boolean popup = call.getBoolean("popup", false);
80
+ int allowForSilence = call.getInt("allowForSilence", 0);
81
+ beginListening(language, maxResults, prompt, partialResults, popup, call, allowForSilence);
82
+ }
83
+
84
+ @PluginMethod
85
+ public void stop(final PluginCall call) {
86
+ try {
87
+ stopListening();
88
+ } catch (Exception ex) {
89
+ call.reject(ex.getLocalizedMessage());
90
+ return;
91
+ }
92
+ call.resolve();
93
+ }
94
+
95
+ @PluginMethod
96
+ public void getSupportedLanguages(PluginCall call) {
97
+ if (languageReceiver == null) {
98
+ languageReceiver = new Receiver(call);
99
+ }
100
+
101
+ List<String> supportedLanguages = languageReceiver.getSupportedLanguages();
102
+ if (supportedLanguages != null) {
103
+ JSONArray languages = new JSONArray(supportedLanguages);
104
+ call.resolve(new JSObject().put("languages", languages));
105
+ return;
106
+ }
107
+
108
+ Intent detailsIntent = new Intent(RecognizerIntent.ACTION_GET_LANGUAGE_DETAILS);
109
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
110
+ detailsIntent.setPackage("com.google.android.googlequicksearchbox");
111
+ }
112
+ bridge.getActivity().sendOrderedBroadcast(detailsIntent, null, languageReceiver, null, Activity.RESULT_OK, null, null);
113
+ }
114
+
115
+ @PluginMethod
116
+ public void isListening(PluginCall call) {
117
+ call.resolve(new JSObject().put("listening", listening));
118
+ }
119
+
120
+ @PluginMethod
121
+ @Override
122
+ public void checkPermissions(PluginCall call) {
123
+ String state = permissionStateValue(getPermissionState(SPEECH_RECOGNITION));
124
+ call.resolve(new JSObject().put("speechRecognition", state));
125
+ }
126
+
127
+ @PluginMethod
128
+ @Override
129
+ public void requestPermissions(PluginCall call) {
130
+ requestPermissionForAlias(SPEECH_RECOGNITION, call, "permissionsCallback");
131
+ }
132
+
133
+ @PermissionCallback
134
+ private void permissionsCallback(PluginCall call) {
135
+ String state = permissionStateValue(getPermissionState(SPEECH_RECOGNITION));
136
+ call.resolve(new JSObject().put("speechRecognition", state));
137
+ }
138
+
139
+ @ActivityCallback
140
+ private void listeningResult(PluginCall call, ActivityResult result) {
141
+ if (call == null) {
142
+ return;
143
+ }
144
+
145
+ int resultCode = result.getResultCode();
146
+ if (resultCode == Activity.RESULT_OK) {
147
+ try {
148
+ ArrayList<String> matchesList = result.getData().getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
149
+ JSObject resultObj = new JSObject();
150
+ resultObj.put("matches", new JSArray(matchesList));
151
+ call.resolve(resultObj);
152
+ } catch (Exception ex) {
153
+ call.reject(ex.getMessage());
154
+ }
155
+ } else {
156
+ call.reject(Integer.toString(resultCode));
157
+ }
158
+
159
+ lock.lock();
160
+ listening(false);
161
+ lock.unlock();
162
+ }
163
+
164
+ private void beginListening(
165
+ String language,
166
+ int maxResults,
167
+ String prompt,
168
+ final boolean partialResults,
169
+ boolean showPopup,
170
+ PluginCall call,
171
+ int allowForSilence
172
+ ) {
173
+ Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
174
+ intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
175
+ intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, language);
176
+ intent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, maxResults);
177
+ intent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE, bridge.getActivity().getPackageName());
178
+ intent.putExtra(RecognizerIntent.EXTRA_PARTIAL_RESULTS, partialResults);
179
+ intent.putExtra("android.speech.extra.DICTATION_MODE", partialResults);
180
+
181
+ if (allowForSilence > 0) {
182
+ intent.putExtra(RecognizerIntent.EXTRA_SEGMENTED_SESSION, true);
183
+ intent.putExtra(RecognizerIntent.EXTRA_SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS, allowForSilence);
184
+ intent.putExtra(RecognizerIntent.EXTRA_SPEECH_INPUT_POSSIBLY_COMPLETE_SILENCE_LENGTH_MILLIS, allowForSilence);
185
+ }
186
+
187
+ if (prompt != null) {
188
+ intent.putExtra(RecognizerIntent.EXTRA_PROMPT, prompt);
189
+ }
190
+
191
+ if (showPopup) {
192
+ startActivityForResult(call, intent, "listeningResult");
193
+ } else {
194
+ bridge
195
+ .getWebView()
196
+ .post(() -> {
197
+ try {
198
+ lock.lock();
199
+
200
+ if (speechRecognizer != null) {
201
+ speechRecognizer.cancel();
202
+ speechRecognizer.destroy();
203
+ speechRecognizer = null;
204
+ }
205
+
206
+ speechRecognizer = SpeechRecognizer.createSpeechRecognizer(bridge.getActivity());
207
+ SpeechRecognitionListener listener = new SpeechRecognitionListener();
208
+ listener.setCall(call);
209
+ listener.setPartialResults(partialResults);
210
+ speechRecognizer.setRecognitionListener(listener);
211
+ speechRecognizer.startListening(intent);
212
+ listening(true);
213
+ if (partialResults) {
214
+ call.resolve();
215
+ }
216
+ } catch (Exception ex) {
217
+ call.reject(ex.getMessage());
218
+ } finally {
219
+ lock.unlock();
220
+ }
221
+ });
222
+ }
223
+ }
224
+
225
+ private void stopListening() {
226
+ bridge
227
+ .getWebView()
228
+ .post(() -> {
229
+ try {
230
+ lock.lock();
231
+ if (listening) {
232
+ speechRecognizer.stopListening();
233
+ listening(false);
234
+ }
235
+ } catch (Exception ex) {
236
+ throw ex;
237
+ } finally {
238
+ lock.unlock();
239
+ }
240
+ });
241
+ }
242
+
243
+ private void listening(boolean value) {
244
+ this.listening = value;
245
+ }
246
+
247
+ private String permissionStateValue(PermissionState state) {
248
+ switch (state) {
249
+ case GRANTED:
250
+ return "granted";
251
+ case DENIED:
252
+ return "denied";
253
+ case PROMPT:
254
+ case PROMPT_WITH_RATIONALE:
255
+ default:
256
+ return "prompt";
257
+ }
258
+ }
259
+
260
+ private class SpeechRecognitionListener implements RecognitionListener {
261
+
262
+ private PluginCall call;
263
+ private boolean partialResults;
264
+
265
+ public void setCall(PluginCall call) {
266
+ this.call = call;
267
+ }
268
+
269
+ public void setPartialResults(boolean partialResults) {
270
+ this.partialResults = partialResults;
271
+ }
272
+
273
+ @Override
274
+ public void onReadyForSpeech(Bundle params) {}
275
+
276
+ @Override
277
+ public void onBeginningOfSpeech() {
278
+ try {
279
+ lock.lock();
280
+ JSObject ret = new JSObject();
281
+ ret.put("status", "started");
282
+ notifyListeners(LISTENING_EVENT, ret);
283
+ } finally {
284
+ lock.unlock();
285
+ }
286
+ }
287
+
288
+ @Override
289
+ public void onRmsChanged(float rmsdB) {}
290
+
291
+ @Override
292
+ public void onBufferReceived(byte[] buffer) {}
293
+
294
+ @Override
295
+ public void onEndOfSpeech() {
296
+ bridge
297
+ .getWebView()
298
+ .post(() -> {
299
+ try {
300
+ lock.lock();
301
+ listening(false);
302
+
303
+ JSObject ret = new JSObject();
304
+ ret.put("status", "stopped");
305
+ notifyListeners(LISTENING_EVENT, ret);
306
+ } finally {
307
+ lock.unlock();
308
+ }
309
+ });
310
+ }
311
+
312
+ @Override
313
+ public void onError(int error) {
314
+ stopListening();
315
+ String errorMssg = getErrorText(error);
316
+
317
+ if (call != null) {
318
+ call.reject(errorMssg);
319
+ }
320
+ }
321
+
322
+ @Override
323
+ public void onResults(Bundle results) {
324
+ ArrayList<String> matches = results.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
325
+
326
+ try {
327
+ JSArray jsArray = new JSArray(matches);
328
+
329
+ if (call != null) {
330
+ if (!partialResults) {
331
+ call.resolve(new JSObject().put("status", "success").put("matches", jsArray));
332
+ } else {
333
+ JSObject ret = new JSObject();
334
+ ret.put("matches", jsArray);
335
+ notifyListeners(PARTIAL_RESULTS_EVENT, ret);
336
+ }
337
+ }
338
+ } catch (Exception ex) {
339
+ if (call != null) {
340
+ call.resolve(new JSObject().put("status", "error").put("message", ex.getMessage()));
341
+ }
342
+ }
343
+ }
344
+
345
+ @Override
346
+ public void onPartialResults(Bundle partialResultsBundle) {
347
+ ArrayList<String> matches = partialResultsBundle.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
348
+ JSArray matchesJSON = new JSArray(matches);
349
+
350
+ try {
351
+ if (matches != null && matches.size() > 0 && !previousPartialResults.equals(matchesJSON)) {
352
+ previousPartialResults = matchesJSON;
353
+ JSObject ret = new JSObject();
354
+ ret.put("matches", previousPartialResults);
355
+ notifyListeners(PARTIAL_RESULTS_EVENT, ret);
356
+ }
357
+ } catch (Exception ex) {}
358
+ }
359
+
360
+ @Override
361
+ public void onSegmentResults(Bundle results) {
362
+ ArrayList<String> matches = results.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
363
+ if (matches == null) {
364
+ return;
365
+ }
366
+ try {
367
+ JSObject ret = new JSObject();
368
+ ret.put("matches", new JSArray(matches));
369
+ notifyListeners(SEGMENT_RESULTS_EVENT, ret);
370
+ } catch (Exception ignored) {}
371
+ }
372
+
373
+ @Override
374
+ public void onEndOfSegmentedSession() {
375
+ notifyListeners(END_OF_SEGMENT_EVENT, new JSObject());
376
+ }
377
+
378
+ @Override
379
+ public void onEvent(int eventType, Bundle params) {}
380
+ }
381
+
382
+ private String getErrorText(int errorCode) {
383
+ switch (errorCode) {
384
+ case SpeechRecognizer.ERROR_AUDIO:
385
+ return "Audio recording error";
386
+ case SpeechRecognizer.ERROR_CLIENT:
387
+ return "Client side error";
388
+ case SpeechRecognizer.ERROR_INSUFFICIENT_PERMISSIONS:
389
+ return "Insufficient permissions";
390
+ case SpeechRecognizer.ERROR_NETWORK:
391
+ return "Network error";
392
+ case SpeechRecognizer.ERROR_NETWORK_TIMEOUT:
393
+ return "Network timeout";
394
+ case SpeechRecognizer.ERROR_NO_MATCH:
395
+ return "No match";
396
+ case SpeechRecognizer.ERROR_RECOGNIZER_BUSY:
397
+ return "RecognitionService busy";
398
+ case SpeechRecognizer.ERROR_SERVER:
399
+ return "Error from server";
400
+ case SpeechRecognizer.ERROR_SPEECH_TIMEOUT:
401
+ return "No speech input";
402
+ default:
403
+ return "Didn't understand, please try again.";
404
+ }
405
+ }
406
+ }
File without changes