diff --git a/.gitignore b/.gitignore index 401db12..8d35027 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,7 @@ project.xcworkspace # Android/IJ # +build/ .classpath .cxx .gradle diff --git a/README.md b/README.md index ad2fb37..5952ffe 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,51 @@ # expo-stt -Unofficial Speech To Text module for Expo which supported iOS and Android +- Unofficial Speech To Text module for Expo which supported iOS and Android +- Forked [anhtuank7c/expo-stt](https://github.com/anhtuank7c/expo-stt) +- Migrated [react-native-voice functionality](https://github.com/react-native-voice/voice) on [crossplatformkorea/expo-stt](https://github.com/crossplatformkorea/expo-stt), which is forked from [anhtuank7c/expo-stt](https://github.com/anhtuank7c/expo-stt) +- Currently, [anhtuank7c/expo-stt](https://github.com/anhtuank7c/expo-stt) has a separate Google voice recognition modal. Instead, I migrated the [react-native-voice](https://github.com/react-native-voice/voice) code onto [crossplatformkorea/expo-stt](https://github.com/crossplatformkorea/expo-stt), which was created with the [expo module](https://docs.expo.dev/modules/overview), to use the built-in microphone like [react-native-voice](https://github.com/react-native-voice/voice). -So sorry that I am unemployed and don't have much money to spend more time to make this module work also for web. +# Sequence Diagram -If you still want to support web platform, please follow this article https://developer.mozilla.org/en-US/docs/Web/API/Web_Speech_API/Using_the_Web_Speech_API +Below is a sequence diagram explaining how each module, including SpeechRecognizer, works. -![Demo speech to text](demo.png "Demo Speech To Text") +![Sequence Diagram](sequence-diagram.png) + +And below is the [mermaid](https://mermaid.js.org) code to create the above diagram. + +```mermaid + +sequenceDiagram + participant User + participant ExpoSttModule + participant SpeechRecognizer + participant ReactNative as React Native Module -# API documentation + User->>ExpoSttModule: startSpeech() + ExpoSttModule->>SpeechRecognizer: createSpeechRecognizer() + ExpoSttModule->>SpeechRecognizer: startListening() + SpeechRecognizer-->>ExpoSttModule: onReadyForSpeech -- [Documentation for the main branch](https://github.com/expo/expo/blob/main/docs/pages/versions/unversioned/sdk/stt.md) -- [Documentation for the latest stable release](https://docs.expo.dev/versions/latest/sdk/stt/) + User->>SpeechRecognizer: User starts speaking + SpeechRecognizer-->>ExpoSttModule: onBeginningOfSpeech + ExpoSttModule->>ReactNative: sendEvent(onSpeechStart) -# Installation in managed Expo projects + User->>SpeechRecognizer: User finishes speaking + SpeechRecognizer-->>ExpoSttModule: onEndOfSpeech + ExpoSttModule->>ReactNative: sendEvent(onSpeechEnd) -For [managed](https://docs.expo.dev/versions/latest/introduction/managed-vs-bare/) Expo projects, please follow the installation instructions in the [API documentation for the latest stable release](#api-documentation). If you follow the link and there is no documentation available then this library is not yet usable within managed projects — it is likely to be included in an upcoming Expo SDK release. + SpeechRecognizer-->>ExpoSttModule: onResults + ExpoSttModule->>ReactNative: sendEvent(onSpeechResult) -# Installation in bare React Native projects + alt SpeechRecognizer encounters an error + SpeechRecognizer-->>ExpoSttModule: onError + ExpoSttModule->>ReactNative: sendEvent(onSpeechError) + end +``` + +# Demo -For bare React Native projects, you must ensure that you have [installed and configured the `expo` package](https://docs.expo.dev/bare/installing-expo-modules/) before continuing. +![Demo speech to text](demo.png "Demo Speech To Text") ### Add the package to your npm dependencies @@ -39,6 +65,7 @@ npx expo prebuild --clean ### Configure for iOS (Bare React Native project only) Run `npx pod-install` after installing the npm package. + ## Add missing permissions for iOS Add following key to plugins of `app.json` in Expo project @@ -68,6 +95,7 @@ For Bare React Native project, you need to add these key to `Info.plist` in `ios ## Usage Register some listeners + ``` import * as ExpoStt from 'expo-stt'; @@ -82,10 +110,6 @@ Register some listeners setSpokenText(value.join()); }); - const onSpeechCancelled = ExpoStt.addOnSpeechCancelledListener(() => { - setRecognizing(false); - }); - const onSpeechError = ExpoStt.addOnSpeechErrorListener(({ cause }) => { setError(cause); setRecognizing(false); @@ -98,7 +122,6 @@ Register some listeners return () => { onSpeechStart.remove(); onSpeechResult.remove(); - onSpeechCancelled.remove(); onSpeechError.remove(); onSpeechEnd.remove(); }; @@ -107,21 +130,14 @@ Register some listeners There are some functions available to call such as: -* ExpoStt.startSpeech() -* ExpoStt.stopSpeech() -* ExpoStt.cancelSpeech() -* ExpoStt.destroySpeech() -* ExpoStt.requestRecognitionPermission() -* ExpoStt.checkRecognitionPermission() +- ExpoStt.startSpeech() +- ExpoStt.stopSpeech() +- ExpoStt.destroySpeech() +- ExpoStt.requestRecognitionPermission() +- ExpoStt.checkRecognitionPermission() Take a look into `example/App.tsx` for completed example # Contributing -Contributions are very welcome! Please refer to guidelines described in the [contributing guide]( https://github.com/expo/expo#contributing). - -## Author - -I am looking for a job as a React native developer, remote work is preferred. - -Check out my CV: https://anhtuank7c.github.io +Contributions are very welcome! Please refer to guidelines described in the [contributing guide](https://github.com/expo/expo#contributing). diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml index 2166eec..c70917b 100644 --- a/android/src/main/AndroidManifest.xml +++ b/android/src/main/AndroidManifest.xml @@ -1,2 +1,8 @@ - - + + + + + \ No newline at end of file diff --git a/android/src/main/java/expo/modules/stt/ExpoSttModule.kt b/android/src/main/java/expo/modules/stt/ExpoSttModule.kt index d886cda..3a119cb 100644 --- a/android/src/main/java/expo/modules/stt/ExpoSttModule.kt +++ b/android/src/main/java/expo/modules/stt/ExpoSttModule.kt @@ -1,41 +1,43 @@ package expo.modules.stt import android.util.Log -import expo.modules.kotlin.activityresult.AppContextActivityResultLauncher import expo.modules.kotlin.modules.Module import expo.modules.kotlin.modules.ModuleDefinition import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import java.util.* +import android.speech.SpeechRecognizer +import android.speech.RecognizerIntent +import android.content.Intent +import android.speech.RecognitionListener +import android.os.Bundle +import expo.modules.kotlin.exception.CodedException +import android.Manifest +import android.content.pm.PackageManager +import androidx.core.app.ActivityCompat +import androidx.core.content.ContextCompat -class ExpoSttModule : Module() { - private lateinit var voiceRecognizer: AppContextActivityResultLauncher +class ExpoSttModule : Module(), RecognitionListener { private var isRecognizing: Boolean = false + private var speech: SpeechRecognizer? = null companion object { - const val onSpeechResult = "onSpeechResult" - const val onSpeechError = "onSpeechError" - const val onSpeechCancelled = "onSpeechCancelled" const val onSpeechStart = "onSpeechStart" + const val onSpeechResult = "onSpeechResult" + const val onPartialResults = "onPartialResults" const val onSpeechEnd = "onSpeechEnd" + const val onSpeechError = "onSpeechError" const val TAG = "ExpoStt" } override fun definition() = ModuleDefinition { Name(TAG) - /** - * We don't need any permission for Recognizer on Android - * Just act like iOS one to unify the APIs - */ + Events(onSpeechStart, onSpeechResult, onPartialResults, onSpeechEnd, onSpeechError) + AsyncFunction("requestRecognitionPermission") { - return@AsyncFunction mapOf( - "status" to "granted", - "expires" to "never", - "granted" to true, - "canAskAgain" to true - ) + requestRecognitionPermission() } AsyncFunction("checkRecognitionPermission") { @@ -48,30 +50,28 @@ class ExpoSttModule : Module() { } Function("startSpeech") { + if (!isPermissionGranted()) { + requestRecognitionPermission() + return@Function false + } + if (isRecognizing) { sendEvent(onSpeechError, mapOf("cause" to "Speech recognition already started!")) return@Function false } - - isRecognizing = true - sendEvent(onSpeechStart) - val options = VoiceRecognizerContractOptions(Locale.getDefault()) + + if (speech != null) { + speech?.destroy() + speech = null + } + CoroutineScope(Dispatchers.Main).launch { - when (val result = voiceRecognizer.launch(options)) { - is VoiceRecognizerContractResult.Success -> { - isRecognizing = false - sendEvent(onSpeechResult, mapOf("value" to result.value)) - sendEvent(onSpeechEnd) - } - is VoiceRecognizerContractResult.Cancelled -> { - isRecognizing = false - sendEvent(onSpeechCancelled) - } - is VoiceRecognizerContractResult.Error -> { - isRecognizing = false - sendEvent(onSpeechError, mapOf("cause" to result.cause)) - } - } + speech = SpeechRecognizer.createSpeechRecognizer(appContext.reactContext) + speech?.setRecognitionListener(this@ExpoSttModule) + val intent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH) + intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, Locale.getDefault()) + + speech?.startListening(intent) } return@Function true } @@ -82,24 +82,112 @@ class ExpoSttModule : Module() { * A bit different compare to iOS APIs */ Function("stopSpeech") { + if (speech != null) { + speech?.stopListening() + } + isRecognizing = false Log.d(TAG, "Stop Voice Recognizer") - } - Function("cancelSpeech") { - Log.d(TAG, "Cancel Voice Recognizer") + return@Function null as Any } Function("destroySpeech") { + if (speech != null) { + speech?.destroy() + } + isRecognizing = false Log.d(TAG, "Destroy Voice Recognizer") + + return@Function null as Any + } + + } + + private fun requestRecognitionPermission() { + val currentActivity = appContext.currentActivity ?: throw CodedException("Activity is null") + val permission = Manifest.permission.RECORD_AUDIO + val isGranted = ContextCompat.checkSelfPermission(currentActivity, permission) == PackageManager.PERMISSION_GRANTED + + if (!isGranted) { + ActivityCompat.requestPermissions(currentActivity, arrayOf(permission), 1) + } + + mapOf( + "status" to if (isGranted) "granted" else "denied", + "expires" to "never", + "granted" to isGranted, + "canAskAgain" to true + ) + } + + private fun isPermissionGranted(): Boolean { + val permission = Manifest.permission.RECORD_AUDIO + val res = appContext.reactContext?.checkCallingOrSelfPermission(permission) + return res == PackageManager.PERMISSION_GRANTED + } + + override fun onReadyForSpeech(params: Bundle?) { + Log.d(TAG, "onReadyForSpeech") + } + + override fun onBeginningOfSpeech() { + isRecognizing = true + sendEvent(onSpeechStart) + Log.d(TAG, "onBeginningOfSpeech") + } + + override fun onResults(results: Bundle?) { + isRecognizing = false + val matches = results?.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION) + + if (matches != null) { + sendEvent(onSpeechResult, mapOf("results" to matches)) + } else { + sendEvent(onSpeechError, mapOf("errorMessage" to "No speech results")) } - RegisterActivityContracts { - voiceRecognizer = - registerForActivityResult(VoiceRecognizerContract()) { _, _ -> - Log.d(TAG, "handleResultUponActivityDestruction") - } + Log.d(TAG, "onResults $matches") + } + + override fun onPartialResults(partialResults: Bundle?) { + val matches = partialResults?.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION) + // Log.d(TAG, "onPartialResults $matches") + } + + override fun onEndOfSpeech() { + isRecognizing = false + sendEvent(onSpeechEnd) + Log.d(TAG, "onEndOfSpeech") + } + + override fun onError(error: Int) { + isRecognizing = false + val errorMessage = when (error) { + SpeechRecognizer.ERROR_AUDIO -> "Audio recording error" + SpeechRecognizer.ERROR_CLIENT -> "Client side error" + SpeechRecognizer.ERROR_INSUFFICIENT_PERMISSIONS -> "Insufficient permissions" + SpeechRecognizer.ERROR_NETWORK -> "Network error" + SpeechRecognizer.ERROR_NETWORK_TIMEOUT -> "Network timeout" + SpeechRecognizer.ERROR_NO_MATCH -> "No match" + SpeechRecognizer.ERROR_RECOGNIZER_BUSY -> "RecognitionService busy" + SpeechRecognizer.ERROR_SERVER -> "Error from server" + SpeechRecognizer.ERROR_SPEECH_TIMEOUT -> "No speech input" + else -> "Unknown error" } + + sendEvent(onSpeechError, mapOf("errorMessage" to errorMessage)) + Log.d(TAG, "onError: $error $errorMessage") + } + + override fun onBufferReceived(buffer: ByteArray?) { + Log.d(TAG, "onBufferReceived") + } + + override fun onRmsChanged(rmsdB: Float) { + // Log.d(TAG, "onRmsChanged: $rmsdB") + } - Events(onSpeechResult, onSpeechError, onSpeechEnd, onSpeechStart, onSpeechCancelled) + override fun onEvent(eventType: Int, params: Bundle?) { + Log.d(TAG, "onEvent: $eventType") } } diff --git a/build/ExpoStt.types.d.ts b/build/ExpoStt.types.d.ts deleted file mode 100644 index 8800f97..0000000 --- a/build/ExpoStt.types.d.ts +++ /dev/null @@ -1,14 +0,0 @@ -export type OnSpeechResultEventPayload = { - value: string[]; -}; -export type OnSpeechErrorEventPayload = { - cause: string; -}; -export declare enum ReactEvents { - onSpeechStart = "onSpeechStart", - onSpeechEnd = "onSpeechEnd", - onSpeechError = "onSpeechError", - onSpeechResult = "onSpeechResult", - onSpeechCancelled = "onSpeechCancelled" -} -//# sourceMappingURL=ExpoStt.types.d.ts.map \ No newline at end of file diff --git a/build/ExpoStt.types.d.ts.map b/build/ExpoStt.types.d.ts.map deleted file mode 100644 index 79a2a73..0000000 --- a/build/ExpoStt.types.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"ExpoStt.types.d.ts","sourceRoot":"","sources":["../src/ExpoStt.types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,0BAA0B,GAAG;IACvC,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB,CAAC;AACF,MAAM,MAAM,yBAAyB,GAAG;IACtC,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AACF,oBAAY,WAAW;IACrB,aAAa,kBAAkB;IAC/B,WAAW,gBAAgB;IAC3B,aAAa,kBAAkB;IAC/B,cAAc,mBAAmB;IACjC,iBAAiB,sBAAsB;CACxC"} \ No newline at end of file diff --git a/build/ExpoStt.types.js b/build/ExpoStt.types.js deleted file mode 100644 index fa5c309..0000000 --- a/build/ExpoStt.types.js +++ /dev/null @@ -1,9 +0,0 @@ -export var ReactEvents; -(function (ReactEvents) { - ReactEvents["onSpeechStart"] = "onSpeechStart"; - ReactEvents["onSpeechEnd"] = "onSpeechEnd"; - ReactEvents["onSpeechError"] = "onSpeechError"; - ReactEvents["onSpeechResult"] = "onSpeechResult"; - ReactEvents["onSpeechCancelled"] = "onSpeechCancelled"; -})(ReactEvents || (ReactEvents = {})); -//# sourceMappingURL=ExpoStt.types.js.map \ No newline at end of file diff --git a/build/ExpoStt.types.js.map b/build/ExpoStt.types.js.map deleted file mode 100644 index 3c943cb..0000000 --- a/build/ExpoStt.types.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"ExpoStt.types.js","sourceRoot":"","sources":["../src/ExpoStt.types.ts"],"names":[],"mappings":"AAMA,MAAM,CAAN,IAAY,WAMX;AAND,WAAY,WAAW;IACrB,8CAA+B,CAAA;IAC/B,0CAA2B,CAAA;IAC3B,8CAA+B,CAAA;IAC/B,gDAAiC,CAAA;IACjC,sDAAuC,CAAA;AACzC,CAAC,EANW,WAAW,KAAX,WAAW,QAMtB","sourcesContent":["export type OnSpeechResultEventPayload = {\n value: string[];\n};\nexport type OnSpeechErrorEventPayload = {\n cause: string;\n};\nexport enum ReactEvents {\n onSpeechStart = \"onSpeechStart\",\n onSpeechEnd = \"onSpeechEnd\",\n onSpeechError = \"onSpeechError\",\n onSpeechResult = \"onSpeechResult\",\n onSpeechCancelled = \"onSpeechCancelled\",\n}\n"]} \ No newline at end of file diff --git a/build/ExpoSttModule.d.ts b/build/ExpoSttModule.d.ts deleted file mode 100644 index 024cc3d..0000000 --- a/build/ExpoSttModule.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -declare const _default: any; -export default _default; -//# sourceMappingURL=ExpoSttModule.d.ts.map \ No newline at end of file diff --git a/build/ExpoSttModule.d.ts.map b/build/ExpoSttModule.d.ts.map deleted file mode 100644 index 916b38c..0000000 --- a/build/ExpoSttModule.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"ExpoSttModule.d.ts","sourceRoot":"","sources":["../src/ExpoSttModule.ts"],"names":[],"mappings":";AAIA,wBAA8C"} \ No newline at end of file diff --git a/build/ExpoSttModule.js b/build/ExpoSttModule.js deleted file mode 100644 index 58fdf32..0000000 --- a/build/ExpoSttModule.js +++ /dev/null @@ -1,5 +0,0 @@ -import { requireNativeModule } from "expo-modules-core"; -// It loads the native module object from the JSI or falls back to -// the bridge module (from NativeModulesProxy) if the remote debugger is on. -export default requireNativeModule("ExpoStt"); -//# sourceMappingURL=ExpoSttModule.js.map \ No newline at end of file diff --git a/build/ExpoSttModule.js.map b/build/ExpoSttModule.js.map deleted file mode 100644 index 98984e9..0000000 --- a/build/ExpoSttModule.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"ExpoSttModule.js","sourceRoot":"","sources":["../src/ExpoSttModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAExD,kEAAkE;AAClE,4EAA4E;AAC5E,eAAe,mBAAmB,CAAC,SAAS,CAAC,CAAC","sourcesContent":["import { requireNativeModule } from \"expo-modules-core\";\n\n// It loads the native module object from the JSI or falls back to\n// the bridge module (from NativeModulesProxy) if the remote debugger is on.\nexport default requireNativeModule(\"ExpoStt\");\n"]} \ No newline at end of file diff --git a/build/index.d.ts b/build/index.d.ts deleted file mode 100644 index 7d65514..0000000 --- a/build/index.d.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Subscription, PermissionResponse } from "expo-modules-core"; -import { OnSpeechResultEventPayload, OnSpeechErrorEventPayload, ReactEvents } from "./ExpoStt.types"; -export declare function startSpeech(): boolean; -export declare function stopSpeech(): void; -export declare function cancelSpeech(): void; -export declare function destroySpeech(): void; -export declare function requestRecognitionPermission(): Promise; -export declare function checkRecognitionPermission(): Promise; -export declare function addOnSpeechStartListener(listener: () => void): Subscription; -export declare function addOnSpeechEndListener(listener: () => void): Subscription; -export declare function addOnSpeechCancelledListener(listener: () => void): Subscription; -export declare function addOnSpeechResultListener(listener: (event: OnSpeechResultEventPayload) => void): Subscription; -export declare function addOnSpeechErrorListener(listener: (event: OnSpeechErrorEventPayload) => void): Subscription; -export { OnSpeechResultEventPayload, OnSpeechErrorEventPayload, ReactEvents }; -//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/build/index.d.ts.map b/build/index.d.ts.map deleted file mode 100644 index bc04c7e..0000000 --- a/build/index.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,YAAY,EACZ,kBAAkB,EACnB,MAAM,mBAAmB,CAAC;AAI3B,OAAO,EACL,0BAA0B,EAC1B,yBAAyB,EACzB,WAAW,EACZ,MAAM,iBAAiB,CAAC;AAGzB,wBAAgB,WAAW,IAAI,OAAO,CAErC;AACD,wBAAgB,UAAU,IAAI,IAAI,CAEjC;AACD,wBAAgB,YAAY,IAAI,IAAI,CAEnC;AACD,wBAAgB,aAAa,IAAI,IAAI,CAEpC;AACD,wBAAgB,4BAA4B,IAAI,OAAO,CAAC,kBAAkB,CAAC,CAE1E;AACD,wBAAgB,0BAA0B,IAAI,OAAO,CAAC,kBAAkB,CAAC,CAExE;AAID,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,YAAY,CAE3E;AACD,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,YAAY,CAEzE;AACD,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,MAAM,IAAI,GACnB,YAAY,CAEd;AACD,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,CAAC,KAAK,EAAE,0BAA0B,KAAK,IAAI,GACpD,YAAY,CAKd;AAED,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,CAAC,KAAK,EAAE,yBAAyB,KAAK,IAAI,GACnD,YAAY,CAKd;AAED,OAAO,EAAE,0BAA0B,EAAE,yBAAyB,EAAE,WAAW,EAAE,CAAC"} \ No newline at end of file diff --git a/build/index.js b/build/index.js deleted file mode 100644 index 6709a0e..0000000 --- a/build/index.js +++ /dev/null @@ -1,41 +0,0 @@ -import { NativeModulesProxy, EventEmitter, } from "expo-modules-core"; -// Import the native module. On web, it will be resolved to ExpoStt.web.ts -// and on native platforms to ExpoStt.ts -import { ReactEvents, } from "./ExpoStt.types"; -import ExpoSttModule from "./ExpoSttModule"; -export function startSpeech() { - return ExpoSttModule.startSpeech(); -} -export function stopSpeech() { - return ExpoSttModule.stopSpeech(); -} -export function cancelSpeech() { - return ExpoSttModule.cancelSpeech(); -} -export function destroySpeech() { - return ExpoSttModule.destroySpeech(); -} -export function requestRecognitionPermission() { - return ExpoSttModule.requestRecognitionPermission(); -} -export function checkRecognitionPermission() { - return ExpoSttModule.checkRecognitionPermission(); -} -const emitter = new EventEmitter(ExpoSttModule ?? NativeModulesProxy.ExpoStt); -export function addOnSpeechStartListener(listener) { - return emitter.addListener(ReactEvents.onSpeechStart, listener); -} -export function addOnSpeechEndListener(listener) { - return emitter.addListener(ReactEvents.onSpeechEnd, listener); -} -export function addOnSpeechCancelledListener(listener) { - return emitter.addListener(ReactEvents.onSpeechCancelled, listener); -} -export function addOnSpeechResultListener(listener) { - return emitter.addListener(ReactEvents.onSpeechResult, listener); -} -export function addOnSpeechErrorListener(listener) { - return emitter.addListener(ReactEvents.onSpeechError, listener); -} -export { ReactEvents }; -//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/build/index.js.map b/build/index.js.map deleted file mode 100644 index a0cd2e6..0000000 --- a/build/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,YAAY,GAGb,MAAM,mBAAmB,CAAC;AAE3B,0EAA0E;AAC1E,wCAAwC;AACxC,OAAO,EAGL,WAAW,GACZ,MAAM,iBAAiB,CAAC;AACzB,OAAO,aAAa,MAAM,iBAAiB,CAAC;AAE5C,MAAM,UAAU,WAAW;IACzB,OAAO,aAAa,CAAC,WAAW,EAAE,CAAC;AACrC,CAAC;AACD,MAAM,UAAU,UAAU;IACxB,OAAO,aAAa,CAAC,UAAU,EAAE,CAAC;AACpC,CAAC;AACD,MAAM,UAAU,YAAY;IAC1B,OAAO,aAAa,CAAC,YAAY,EAAE,CAAC;AACtC,CAAC;AACD,MAAM,UAAU,aAAa;IAC3B,OAAO,aAAa,CAAC,aAAa,EAAE,CAAC;AACvC,CAAC;AACD,MAAM,UAAU,4BAA4B;IAC1C,OAAO,aAAa,CAAC,4BAA4B,EAAE,CAAC;AACtD,CAAC;AACD,MAAM,UAAU,0BAA0B;IACxC,OAAO,aAAa,CAAC,0BAA0B,EAAE,CAAC;AACpD,CAAC;AAED,MAAM,OAAO,GAAG,IAAI,YAAY,CAAC,aAAa,IAAI,kBAAkB,CAAC,OAAO,CAAC,CAAC;AAE9E,MAAM,UAAU,wBAAwB,CAAC,QAAoB;IAC3D,OAAO,OAAO,CAAC,WAAW,CAAO,WAAW,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC;AACxE,CAAC;AACD,MAAM,UAAU,sBAAsB,CAAC,QAAoB;IACzD,OAAO,OAAO,CAAC,WAAW,CAAC,WAAW,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;AAChE,CAAC;AACD,MAAM,UAAU,4BAA4B,CAC1C,QAAoB;IAEpB,OAAO,OAAO,CAAC,WAAW,CAAO,WAAW,CAAC,iBAAiB,EAAE,QAAQ,CAAC,CAAC;AAC5E,CAAC;AACD,MAAM,UAAU,yBAAyB,CACvC,QAAqD;IAErD,OAAO,OAAO,CAAC,WAAW,CACxB,WAAW,CAAC,cAAc,EAC1B,QAAQ,CACT,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,wBAAwB,CACtC,QAAoD;IAEpD,OAAO,OAAO,CAAC,WAAW,CACxB,WAAW,CAAC,aAAa,EACzB,QAAQ,CACT,CAAC;AACJ,CAAC;AAED,OAAO,EAAyD,WAAW,EAAE,CAAC","sourcesContent":["import {\n NativeModulesProxy,\n EventEmitter,\n Subscription,\n PermissionResponse,\n} from \"expo-modules-core\";\n\n// Import the native module. On web, it will be resolved to ExpoStt.web.ts\n// and on native platforms to ExpoStt.ts\nimport {\n OnSpeechResultEventPayload,\n OnSpeechErrorEventPayload,\n ReactEvents,\n} from \"./ExpoStt.types\";\nimport ExpoSttModule from \"./ExpoSttModule\";\n\nexport function startSpeech(): boolean {\n return ExpoSttModule.startSpeech();\n}\nexport function stopSpeech(): void {\n return ExpoSttModule.stopSpeech();\n}\nexport function cancelSpeech(): void {\n return ExpoSttModule.cancelSpeech();\n}\nexport function destroySpeech(): void {\n return ExpoSttModule.destroySpeech();\n}\nexport function requestRecognitionPermission(): Promise {\n return ExpoSttModule.requestRecognitionPermission();\n}\nexport function checkRecognitionPermission(): Promise {\n return ExpoSttModule.checkRecognitionPermission();\n}\n\nconst emitter = new EventEmitter(ExpoSttModule ?? NativeModulesProxy.ExpoStt);\n\nexport function addOnSpeechStartListener(listener: () => void): Subscription {\n return emitter.addListener(ReactEvents.onSpeechStart, listener);\n}\nexport function addOnSpeechEndListener(listener: () => void): Subscription {\n return emitter.addListener(ReactEvents.onSpeechEnd, listener);\n}\nexport function addOnSpeechCancelledListener(\n listener: () => void\n): Subscription {\n return emitter.addListener(ReactEvents.onSpeechCancelled, listener);\n}\nexport function addOnSpeechResultListener(\n listener: (event: OnSpeechResultEventPayload) => void\n): Subscription {\n return emitter.addListener(\n ReactEvents.onSpeechResult,\n listener\n );\n}\n\nexport function addOnSpeechErrorListener(\n listener: (event: OnSpeechErrorEventPayload) => void\n): Subscription {\n return emitter.addListener(\n ReactEvents.onSpeechError,\n listener\n );\n}\n\nexport { OnSpeechResultEventPayload, OnSpeechErrorEventPayload, ReactEvents };\n"]} \ No newline at end of file diff --git a/bun.lockb b/bun.lockb new file mode 100755 index 0000000..bc53f88 Binary files /dev/null and b/bun.lockb differ diff --git a/example/App.tsx b/example/App.tsx index 4e5f953..49605f6 100644 --- a/example/App.tsx +++ b/example/App.tsx @@ -50,10 +50,6 @@ export default function App() { setSpokenText(value.join()); }); - const onSpeechCancelled = ExpoStt.addOnSpeechCancelledListener(() => { - setRecognizing(false); - }); - const onSpeechError = ExpoStt.addOnSpeechErrorListener(({ cause }) => { setError(cause); setRecognizing(false); @@ -66,7 +62,6 @@ export default function App() { return () => { onSpeechStart.remove(); onSpeechResult.remove(); - onSpeechCancelled.remove(); onSpeechError.remove(); onSpeechEnd.remove(); }; @@ -95,12 +90,6 @@ export default function App() { title="Stop speech" onPress={() => ExpoStt.stopSpeech()} /> -