Skip to content

Commit 2b2a9ff

Browse files
committed
feat: android language detection
1 parent 522892e commit 2b2a9ff

File tree

4 files changed

+65
-23
lines changed

4 files changed

+65
-23
lines changed

README.md

+12-11
Original file line numberDiff line numberDiff line change
@@ -304,17 +304,18 @@ ExpoSpeechRecognitionModule.abort();
304304

305305
Events are largely based on the [Web Speech API](https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognition). The following events are supported:
306306

307-
| Event Name | Description | Notes |
308-
| ------------- | ------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
309-
| `audiostart` | Audio capturing has started | Includes the `uri` if `recordingOptions.persist` is enabled. |
310-
| `audioend` | Audio capturing has ended | Includes the `uri` if `recordingOptions.persist` is enabled. |
311-
| `end` | Speech recognition service has disconnected. | This should always be the last event dispatched, including after errors. |
312-
| `error` | Fired when a speech recognition error occurs. | You'll also receive an `error` event (with code "aborted") when calling `.abort()` |
313-
| `nomatch` | Speech recognition service returns a final result with no significant recognition. | You may have non-final results recognized. This may get emitted after cancellation. |
314-
| `result` | Speech recognition service returns a word or phrase has been positively recognized. | On Android, continous mode runs as a segmented session, meaning when a final result is reached, additional partial and final results will cover a new segment separate from the previous final result. On iOS, you should expect one final result before speech recognition has stopped. |
315-
| `speechstart` | Fired when any sound — recognizable speech or not — has been detected | On iOS, this will fire once in the session after a result has occurred |
316-
| `speechend` | Fired when speech recognized by the speech recognition service has stopped being detected. | Not supported yet on iOS |
317-
| `start` | Speech recognition has started | Use this event to indicate to the user when to speak. |
307+
| Event Name | Description | Notes |
308+
| ------------------- | ------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
309+
| `audiostart` | Audio capturing has started | Includes the `uri` if `recordingOptions.persist` is enabled. |
310+
| `audioend` | Audio capturing has ended | Includes the `uri` if `recordingOptions.persist` is enabled. |
311+
| `end` | Speech recognition service has disconnected. | This should always be the last event dispatched, including after errors. |
312+
| `error` | Fired when a speech recognition error occurs. | You'll also receive an `error` event (with code "aborted") when calling `.abort()` |
313+
| `nomatch` | Speech recognition service returns a final result with no significant recognition. | You may have non-final results recognized. This may get emitted after cancellation. |
314+
| `result` | Speech recognition service returns a word or phrase has been positively recognized. | On Android, continous mode runs as a segmented session, meaning when a final result is reached, additional partial and final results will cover a new segment separate from the previous final result. On iOS, you should expect one final result before speech recognition has stopped. |
315+
| `speechstart` | Fired when any sound — recognizable speech or not — has been detected | On iOS, this will fire once in the session after a result has occurred |
316+
| `speechend` | Fired when speech recognized by the speech recognition service has stopped being detected. | Not supported yet on iOS |
317+
| `start` | Speech recognition has started | Use this event to indicate to the user when to speak. |
318+
| `languagedetection` | Called when the language detection (and switching) results are available. | Android 14+ only. Enabled with `EXTRA_ENABLE_LANGUAGE_DETECTION` in the `androidIntent` option when starting. Also can be called multiple times by enabling `EXTRA_ENABLE_LANGUAGE_SWITCH`. |
318319

319320
## Handling Errors
320321

android/src/main/java/expo/modules/speechrecognition/ExpoSpeechRecognitionModule.kt

+18-12
Original file line numberDiff line numberDiff line change
@@ -325,26 +325,32 @@ class ExpoSpeechRecognitionModule : Module() {
325325
promise: Promise,
326326
) {
327327
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
328-
promise.resolve(mapOf(
329-
"locales" to mutableListOf<String>(),
330-
"installedLocales" to mutableListOf<String>(),
331-
))
328+
promise.resolve(
329+
mapOf(
330+
"locales" to mutableListOf<String>(),
331+
"installedLocales" to mutableListOf<String>(),
332+
),
333+
)
332334
return
333335
}
334336

335337
if (options.androidRecognitionServicePackage == null && !SpeechRecognizer.isOnDeviceRecognitionAvailable(appContext)) {
336-
promise.resolve(mapOf(
337-
"locales" to mutableListOf<String>(),
338-
"installedLocales" to mutableListOf<String>(),
339-
))
338+
promise.resolve(
339+
mapOf(
340+
"locales" to mutableListOf<String>(),
341+
"installedLocales" to mutableListOf<String>(),
342+
),
343+
)
340344
return
341345
}
342346

343347
if (options.androidRecognitionServicePackage != null && !SpeechRecognizer.isRecognitionAvailable(appContext)) {
344-
promise.resolve(mapOf(
345-
"locales" to mutableListOf<String>(),
346-
"installedLocales" to mutableListOf<String>(),
347-
))
348+
promise.resolve(
349+
mapOf(
350+
"locales" to mutableListOf<String>(),
351+
"installedLocales" to mutableListOf<String>(),
352+
),
353+
)
348354
return
349355
}
350356

android/src/main/java/expo/modules/speechrecognition/ExpoSpeechService.kt

+17
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,15 @@ class ExpoSpeechService(
561561
else -> 0.0f
562562
}
563563

564+
private fun languageDetectionConfidenceLevelToFloat(confidenceLevel: Int): Float =
565+
when (confidenceLevel) {
566+
SpeechRecognizer.LANGUAGE_DETECTION_CONFIDENCE_LEVEL_HIGHLY_CONFIDENT -> 1.0f
567+
SpeechRecognizer.LANGUAGE_DETECTION_CONFIDENCE_LEVEL_CONFIDENT -> 0.8f
568+
SpeechRecognizer.LANGUAGE_DETECTION_CONFIDENCE_LEVEL_NOT_CONFIDENT -> 0.5f
569+
SpeechRecognizer.LANGUAGE_DETECTION_CONFIDENCE_LEVEL_UNKNOWN -> 0f
570+
else -> 0.0f
571+
}
572+
564573
override fun onResults(results: Bundle?) {
565574
val resultsList = getResults(results)
566575

@@ -594,6 +603,14 @@ class ExpoSpeechService(
594603
}
595604
}
596605

606+
override fun onLanguageDetection(results: Bundle) {
607+
sendEvent("languagedetection", mapOf(
608+
"detectedLanguage" to results.getString(SpeechRecognizer.DETECTED_LANGUAGE),
609+
"confidence" to languageDetectionConfidenceLevelToFloat(results.getInt(SpeechRecognizer.LANGUAGE_DETECTION_CONFIDENCE_LEVEL)),
610+
"topLocaleAlternatives" to results.getStringArrayList(SpeechRecognizer.TOP_LOCALE_ALTERNATIVES)
611+
))
612+
}
613+
597614
/**
598615
* For API 33: Basically same as onResults but doesn't stop
599616
*/

src/ExpoSpeechRecognitionModule.types.ts

+18
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,23 @@ export type ExpoSpeechRecognitionErrorEvent = {
8888
message: string;
8989
};
9090

91+
export type LanguageDetectionEvent = {
92+
/** The language that was detected, in BCP-47 format. e.g. "en-US", "de-DE" */
93+
detectedLanguage: string;
94+
/** The confidence of the detected language. A value ranging between 0.0 and 1.0.
95+
*
96+
* Values range from:
97+
*
98+
* - 1.0 (highly confident)
99+
* - 0.8 (confident)
100+
* - 0.5 (not confident)
101+
* - 0.0 (unknown)
102+
*/
103+
confidence: number;
104+
/** The alternative locales for the same language, in BCP-47 format. e.g. ["en-US", "en-GB"] */
105+
topLocaleAlternatives: string[];
106+
};
107+
91108
/**
92109
* Events that are dispatched from the native side
93110
*/
@@ -127,6 +144,7 @@ export type ExpoSpeechRecognitionNativeEventMap = {
127144
end: null;
128145
soundstart: null;
129146
soundend: null;
147+
languagedetection: LanguageDetectionEvent;
130148
};
131149

132150
export type ExpoSpeechRecognitionOptions = {

0 commit comments

Comments
 (0)