@iternio/react-native-auto-play 0.4.12 → 0.5.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.
@@ -1,6 +1,8 @@
1
1
  package com.margelo.nitro.swe.iternio.reactnativeautoplay
2
2
 
3
+ import android.Manifest
3
4
  import android.annotation.SuppressLint
5
+ import android.content.Context
4
6
  import android.content.Intent
5
7
  import android.content.pm.PackageManager
6
8
  import android.media.AudioAttributes
@@ -91,7 +93,7 @@ class VoiceInputManager(
91
93
  // MARK: - STT path (SpeechRecognizer owns the mic)
92
94
 
93
95
  private suspend fun startSTT(
94
- context: android.content.Context,
96
+ context: Context,
95
97
  onChunk: ((chunk: VoiceInputChunk) -> Unit)?,
96
98
  language: String?
97
99
  ): VoiceInputResult = suspendCancellableCoroutine { cont ->
@@ -110,8 +112,7 @@ class VoiceInputManager(
110
112
  override fun onError(error: Int) {
111
113
  activeSpeechRecognizer = null
112
114
  recognizer.destroy()
113
- // Return empty transcription — caller sees null audio and null transcription
114
- cont.resume(VoiceInputResult(transcription = null, audio = null))
115
+ cont.resumeWithException(RuntimeException("SpeechRecognizer error $error"))
115
116
  }
116
117
 
117
118
  override fun onPartialResults(partialResults: Bundle?) {
@@ -173,12 +174,14 @@ class VoiceInputManager(
173
174
  }.getOrThrow()
174
175
  }
175
176
 
177
+ var pcmBytes: ByteArray
176
178
  try {
177
- recordPCM(silenceThresholdMs, maxDurationMs) { chunk ->
179
+ pcmBytes = recordPCM(silenceThresholdMs, maxDurationMs) { chunk ->
178
180
  chunk.audio?.let { ab ->
179
181
  try {
180
182
  pipeOut.write(ab.toByteArray())
181
183
  } catch (_: Exception) {
184
+ isRecording = false
182
185
  }
183
186
  }
184
187
  }
@@ -193,12 +196,17 @@ class VoiceInputManager(
193
196
  }
194
197
  }
195
198
 
196
- return sttDeferred.await()
199
+ return try {
200
+ sttDeferred.await()
201
+ } catch (_: Exception) {
202
+ val directBuffer = ByteBuffer.allocateDirect(pcmBytes.size).put(pcmBytes).rewind() as ByteBuffer
203
+ VoiceInputResult(transcription = null, audio = ArrayBuffer.wrap(directBuffer))
204
+ }
197
205
  }
198
206
 
199
207
  @RequiresApi(Build.VERSION_CODES.TIRAMISU)
200
208
  private suspend fun startSTTWithSource(
201
- context: android.content.Context,
209
+ context: Context,
202
210
  audioSource: ParcelFileDescriptor,
203
211
  silenceThresholdMs: Long,
204
212
  onChunk: ((chunk: VoiceInputChunk) -> Unit)?,
@@ -223,7 +231,7 @@ class VoiceInputManager(
223
231
  override fun onError(error: Int) {
224
232
  activeSpeechRecognizer = null
225
233
  recognizer.destroy()
226
- cont.resume(VoiceInputResult(transcription = lastPartial, audio = null))
234
+ cont.resumeWithException(RuntimeException("SpeechRecognizer error $error"))
227
235
  }
228
236
 
229
237
  override fun onPartialResults(partialResults: Bundle?) {
@@ -473,7 +481,7 @@ class VoiceInputManager(
473
481
  fun hasVoiceInputPermission(): Boolean {
474
482
  val context = NitroModules.applicationContext ?: return false
475
483
  return ContextCompat.checkSelfPermission(
476
- context, android.Manifest.permission.RECORD_AUDIO
484
+ context, Manifest.permission.RECORD_AUDIO
477
485
  ) == PackageManager.PERMISSION_GRANTED
478
486
  }
479
487
  }
@@ -103,6 +103,7 @@ class VoiceInputManager {
103
103
  }
104
104
  isStopping = true
105
105
  let wasSTTMode = isSTTMode
106
+ let capturedRequest = recognitionRequest
106
107
  let box = resultBox
107
108
  let capturedSamples = samples
108
109
  resultBox = nil
@@ -112,7 +113,7 @@ class VoiceInputManager {
112
113
  if wasSTTMode {
113
114
  // endAudio() causes the recognition task to fire its final result,
114
115
  // which resumes the box. Engine teardown happens there too.
115
- recognitionRequest?.endAudio()
116
+ capturedRequest?.endAudio()
116
117
  }
117
118
  else {
118
119
  cleanup(interfaceController: interfaceController)
@@ -161,6 +162,7 @@ class VoiceInputManager {
161
162
  if error != nil {
162
163
  // STT failed — fall back to whatever PCM was accumulated
163
164
  self.stopLock.lock()
165
+ self.isStopping = true
164
166
  let capturedSamples = self.samples
165
167
  self.samples = []
166
168
  self.stopLock.unlock()
@@ -233,7 +235,11 @@ class VoiceInputManager {
233
235
 
234
236
  let frameCount = Int(outputBuffer.frameLength)
235
237
  let newSamples = Array(UnsafeBufferPointer(start: int16Data[0], count: frameCount))
236
- self.samples.append(contentsOf: newSamples)
238
+ self.stopLock.lock()
239
+ if !self.isStopping {
240
+ self.samples.append(contentsOf: newSamples)
241
+ }
242
+ self.stopLock.unlock()
237
243
 
238
244
  // PCM chunk callback
239
245
  if activeRecognitionRequest == nil, let onChunk {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iternio/react-native-auto-play",
3
- "version": "0.4.12",
3
+ "version": "0.5.0",
4
4
  "description": "Android Auto and Apple CarPlay for react-native",
5
5
  "main": "lib/index",
6
6
  "module": "lib/index",
@@ -1,2 +0,0 @@
1
- import type { AutoPlay } from './specs/AutoPlay.nitro';
2
- export declare const HybridAutoPlay: AutoPlay;
@@ -1,2 +0,0 @@
1
- import { NitroModules } from 'react-native-nitro-modules';
2
- export const HybridAutoPlay = NitroModules.createHybridObject('AutoPlay');
@@ -1,10 +0,0 @@
1
- type Props = {
2
- children: React.ReactNode;
3
- moduleName: string;
4
- };
5
- /**
6
- * renders the passed children when the specified scene/screen appeared
7
- * this makes sure child hooks are executed only when the map template is ready
8
- */
9
- export default function OnAppearedChildRenderer({ children, moduleName }: Props): import("react").ReactNode;
10
- export {};
@@ -1,26 +0,0 @@
1
- import { useEffect, useState } from 'react';
2
- import { HybridAutoPlay } from '../hybrid/HybridAutoPlay';
3
- /**
4
- * renders the passed children when the specified scene/screen appeared
5
- * this makes sure child hooks are executed only when the map template is ready
6
- */
7
- export default function OnAppearedChildRenderer({ children, moduleName }) {
8
- const [didAppear, setDidAppear] = useState(false);
9
- useEffect(() => {
10
- let remove = HybridAutoPlay.addListenerRenderState(moduleName, (renderState) => {
11
- if (renderState === 'didAppear') {
12
- remove?.();
13
- remove = null;
14
- setDidAppear(true);
15
- }
16
- });
17
- return () => {
18
- remove?.();
19
- remove = null;
20
- };
21
- }, [moduleName]);
22
- if (didAppear) {
23
- return children;
24
- }
25
- return null;
26
- }
@@ -1,7 +0,0 @@
1
- /**
2
- * A hook to determine if the CarPlay/Android Auto screen is currently focused (visible).
3
- *
4
- * @param moduleName The name of the module to listen to.
5
- * @returns `true` if the screen is focused, `false` otherwise.
6
- */
7
- export declare function useIsAutoPlayFocused(moduleName: string): boolean;
@@ -1,20 +0,0 @@
1
- import { useEffect, useState } from 'react';
2
- import { HybridAutoPlay } from '..';
3
- /**
4
- * A hook to determine if the CarPlay/Android Auto screen is currently focused (visible).
5
- *
6
- * @param moduleName The name of the module to listen to.
7
- * @returns `true` if the screen is focused, `false` otherwise.
8
- */
9
- export function useIsAutoPlayFocused(moduleName) {
10
- const [isFocused, setIsFocused] = useState(false);
11
- useEffect(() => {
12
- const remove = HybridAutoPlay.addListenerRenderState(moduleName, (state) => {
13
- setIsFocused(state === 'didAppear');
14
- });
15
- return () => {
16
- remove();
17
- };
18
- }, [moduleName]);
19
- return isFocused;
20
- }
package/lib/hybrid.d.ts DELETED
@@ -1,2 +0,0 @@
1
- import type { AutoPlay as NitroAutoPlay } from './specs/AutoPlay.nitro';
2
- export declare const HybridAutoPlay: NitroAutoPlay;
package/lib/hybrid.js DELETED
@@ -1,2 +0,0 @@
1
- import { NitroModules } from 'react-native-nitro-modules';
2
- export const HybridAutoPlay = NitroModules.createHybridObject('AutoPlay');
@@ -1,11 +0,0 @@
1
- import type { HybridObject } from 'react-native-nitro-modules';
2
- import type { NitroAutomotivePermissionRequestTemplateConfig } from '../templates/AutomotivePermissionRequestTemplate';
3
- import type { NitroTemplateConfig } from './AutoPlay.nitro';
4
- interface AutomotivePermissionRequestTemplateConfig extends NitroTemplateConfig, NitroAutomotivePermissionRequestTemplateConfig {
5
- }
6
- export interface AutomotivePermissionRequestTemplate extends HybridObject<{
7
- android: 'kotlin';
8
- }> {
9
- createAutomotivePermissionRequestTemplate(config: AutomotivePermissionRequestTemplateConfig): void;
10
- }
11
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1,11 +0,0 @@
1
- import type { HybridObject } from 'react-native-nitro-modules';
2
- import type { NitroAutomotivePermissionRequestTemplateConfig } from '../templates/AutomotivePermissionRequestTemplate';
3
- import type { NitroTemplateConfig } from './AutoPlay.nitro';
4
- interface AutomotivePermissionRequestTemplateConfig extends NitroTemplateConfig, NitroAutomotivePermissionRequestTemplateConfig {
5
- }
6
- export interface AutomotivePermissionRequestTemplate extends HybridObject<{
7
- android: 'kotlin';
8
- }> {
9
- createAutomotivePermissionRequestTemplate(config: AutomotivePermissionRequestTemplateConfig): void;
10
- }
11
- export {};
@@ -1,23 +0,0 @@
1
- import type { CustomActionButtonAndroid } from '../types/Button';
2
- import type { AutoText } from '../types/Text';
3
- import { type NitroAction } from '../utils/NitroAction';
4
- import { type HeaderActions, Template, type TemplateConfig } from './Template';
5
- export interface NitroAutomotivePermissionRequestTemplateConfig extends TemplateConfig {
6
- headerActions?: Array<NitroAction>;
7
- title: AutoText;
8
- actions: Array<NitroAction>;
9
- }
10
- export type AutomotivePermissionRequestTemplateConfig = Omit<NitroAutomotivePermissionRequestTemplateConfig, 'headerActions' | 'buttons'> & {
11
- /**
12
- * action buttons, usually at the the top right on Android and a top bar on iOS
13
- */
14
- headerActions?: HeaderActions<AutomotivePermissionRequestTemplate>;
15
- actions: [CustomActionButtonAndroid<AutomotivePermissionRequestTemplate>] | [
16
- CustomActionButtonAndroid<AutomotivePermissionRequestTemplate>,
17
- CustomActionButtonAndroid<AutomotivePermissionRequestTemplate>
18
- ];
19
- };
20
- export declare class AutomotivePermissionRequestTemplate extends Template<AutomotivePermissionRequestTemplateConfig, HeaderActions<AutomotivePermissionRequestTemplate>> {
21
- private template;
22
- constructor(config: AutomotivePermissionRequestTemplateConfig);
23
- }
@@ -1,18 +0,0 @@
1
- import { NitroModules } from 'react-native-nitro-modules';
2
- import { NitroActionUtil } from '../utils/NitroAction';
3
- import { Template, } from './Template';
4
- const HybridAutomotivePermissionRequestTemplate = NitroModules.createHybridObject('AutomotivePermissionRequestTemplate');
5
- export class AutomotivePermissionRequestTemplate extends Template {
6
- template = this;
7
- constructor(config) {
8
- super(config);
9
- const { headerActions, actions, ...rest } = config;
10
- const nitroConfig = {
11
- ...rest,
12
- id: this.id,
13
- headerActions: NitroActionUtil.convert(this.template, headerActions),
14
- actions: NitroActionUtil.convert(this.template, actions),
15
- };
16
- HybridAutomotivePermissionRequestTemplate.createAutomotivePermissionRequestTemplate(nitroConfig);
17
- }
18
- }