@iternio/react-native-auto-play 0.4.5 → 0.4.7

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.
Files changed (80) hide show
  1. package/android/src/main/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/HybridAutoPlay.kt +89 -0
  2. package/android/src/main/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/VirtualRenderer.kt +12 -4
  3. package/android/src/main/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/VoiceInputManager.kt +20 -276
  4. package/android/src/main/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/utils/ThreadUtil.kt +13 -6
  5. package/ios/hybrid/HybridAutoPlay.swift +47 -2
  6. package/ios/utils/VoiceInputManager.swift +40 -141
  7. package/lib/index.d.ts +1 -3
  8. package/lib/index.js +1 -2
  9. package/lib/specs/AutoPlay.nitro.d.ts +29 -0
  10. package/lib/templates/MapTemplate.js +1 -6
  11. package/nitro.json +0 -10
  12. package/nitrogen/generated/android/ReactNativeAutoPlay+autolinking.cmake +0 -2
  13. package/nitrogen/generated/android/ReactNativeAutoPlayOnLoad.cpp +0 -18
  14. package/nitrogen/generated/android/c++/JHybridAutoPlaySpec.cpp +43 -0
  15. package/nitrogen/generated/android/c++/JHybridAutoPlaySpec.hpp +4 -0
  16. package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/HybridAutoPlaySpec.kt +17 -0
  17. package/nitrogen/generated/ios/ReactNativeAutoPlay-Swift-Cxx-Bridge.cpp +16 -41
  18. package/nitrogen/generated/ios/ReactNativeAutoPlay-Swift-Cxx-Bridge.hpp +126 -201
  19. package/nitrogen/generated/ios/ReactNativeAutoPlay-Swift-Cxx-Umbrella.hpp +0 -11
  20. package/nitrogen/generated/ios/ReactNativeAutoPlayAutolinking.mm +0 -8
  21. package/nitrogen/generated/ios/ReactNativeAutoPlayAutolinking.swift +0 -12
  22. package/nitrogen/generated/ios/c++/HybridAutoPlaySpecSwift.hpp +34 -0
  23. package/nitrogen/generated/ios/swift/Func_void_bool.swift +5 -5
  24. package/nitrogen/generated/ios/swift/{Func_void_VoiceInputResult.swift → Func_void_std__shared_ptr_ArrayBuffer_.swift} +10 -10
  25. package/nitrogen/generated/ios/swift/HybridAutoPlaySpec.swift +4 -0
  26. package/nitrogen/generated/ios/swift/HybridAutoPlaySpec_cxx.swift +82 -0
  27. package/nitrogen/generated/shared/c++/HybridAutoPlaySpec.cpp +4 -0
  28. package/nitrogen/generated/shared/c++/HybridAutoPlaySpec.hpp +5 -0
  29. package/package.json +1 -1
  30. package/src/index.ts +1 -3
  31. package/src/specs/AutoPlay.nitro.ts +37 -0
  32. package/src/templates/MapTemplate.ts +1 -6
  33. package/android/src/main/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/HybridVoice.kt +0 -95
  34. package/ios/hybrid/HybridVoice.swift +0 -63
  35. package/lib/HybridAutoPlay.d.ts +0 -2
  36. package/lib/HybridAutoPlay.js +0 -2
  37. package/lib/components/OnAppearedChildRenderer.d.ts +0 -10
  38. package/lib/components/OnAppearedChildRenderer.js +0 -26
  39. package/lib/hooks/useIsAutoPlayFocused.d.ts +0 -7
  40. package/lib/hooks/useIsAutoPlayFocused.js +0 -20
  41. package/lib/hybrid/HybridVoice.d.ts +0 -12
  42. package/lib/hybrid/HybridVoice.js +0 -13
  43. package/lib/hybrid.d.ts +0 -2
  44. package/lib/hybrid.js +0 -2
  45. package/lib/specs/AutomotivePermissionRequestTemplate.d.ts +0 -11
  46. package/lib/specs/AutomotivePermissionRequestTemplate.js +0 -1
  47. package/lib/specs/AutomotivePermissionRequestTemplate.nitro.d.ts +0 -11
  48. package/lib/specs/AutomotivePermissionRequestTemplate.nitro.js +0 -1
  49. package/lib/specs/Voice.nitro.d.ts +0 -51
  50. package/lib/specs/Voice.nitro.js +0 -1
  51. package/lib/templates/AutomotivePermissionRequestTemplate.d.ts +0 -23
  52. package/lib/templates/AutomotivePermissionRequestTemplate.js +0 -18
  53. package/lib/types/Glyphmap.d.ts +0 -4105
  54. package/lib/types/Glyphmap.js +0 -4105
  55. package/lib/types/Voice.d.ts +0 -15
  56. package/lib/types/Voice.js +0 -1
  57. package/nitrogen/generated/android/c++/JFunc_void_VoiceInputChunk.hpp +0 -81
  58. package/nitrogen/generated/android/c++/JHybridVoiceSpec.cpp +0 -104
  59. package/nitrogen/generated/android/c++/JHybridVoiceSpec.hpp +0 -66
  60. package/nitrogen/generated/android/c++/JVoiceInputChunk.hpp +0 -64
  61. package/nitrogen/generated/android/c++/JVoiceInputResult.hpp +0 -64
  62. package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/Func_void_VoiceInputChunk.kt +0 -80
  63. package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/HybridVoiceSpec.kt +0 -72
  64. package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/VoiceInputChunk.kt +0 -41
  65. package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/VoiceInputResult.kt +0 -41
  66. package/nitrogen/generated/ios/c++/HybridVoiceSpecSwift.cpp +0 -11
  67. package/nitrogen/generated/ios/c++/HybridVoiceSpecSwift.hpp +0 -116
  68. package/nitrogen/generated/ios/swift/Func_void_VoiceInputChunk.swift +0 -46
  69. package/nitrogen/generated/ios/swift/HybridVoiceSpec.swift +0 -58
  70. package/nitrogen/generated/ios/swift/HybridVoiceSpec_cxx.swift +0 -227
  71. package/nitrogen/generated/ios/swift/VoiceInputChunk.swift +0 -60
  72. package/nitrogen/generated/ios/swift/VoiceInputResult.swift +0 -60
  73. package/nitrogen/generated/shared/c++/HybridVoiceSpec.cpp +0 -24
  74. package/nitrogen/generated/shared/c++/HybridVoiceSpec.hpp +0 -73
  75. package/nitrogen/generated/shared/c++/VoiceInputChunk.hpp +0 -89
  76. package/nitrogen/generated/shared/c++/VoiceInputResult.hpp +0 -89
  77. package/src/components/OnAppearedChildRenderer.tsx +0 -37
  78. package/src/hybrid/HybridVoice.ts +0 -30
  79. package/src/specs/Voice.nitro.ts +0 -58
  80. package/src/types/Voice.ts +0 -17
@@ -1,47 +1,16 @@
1
1
  import AVFoundation
2
2
  import CarPlay
3
- import NitroModules
4
- import Speech
5
3
 
6
- /// Wraps CheckedContinuation so it can only be resumed once even when
7
- /// shared between a stop() call and an async recognition task callback.
8
- private final class ResultBox: @unchecked Sendable {
9
- private var continuation: CheckedContinuation<VoiceInputResult, Error>?
10
- private let lock = NSLock()
11
-
12
- init(_ continuation: CheckedContinuation<VoiceInputResult, Error>) {
13
- self.continuation = continuation
14
- }
15
-
16
- func resume(returning result: VoiceInputResult) {
17
- lock.lock()
18
- defer { lock.unlock() }
19
- continuation?.resume(returning: result)
20
- continuation = nil
21
- }
22
-
23
- func resume(throwing error: Error) {
24
- lock.lock()
25
- defer { lock.unlock() }
26
- continuation?.resume(throwing: error)
27
- continuation = nil
28
- }
29
- }
30
-
31
- /// Captures audio from the car microphone and buffers raw 16 kHz / 16-bit / mono PCM,
32
- /// or transcribes it via SFSpeechRecognizer when preferSpeechToText is true.
4
+ /// Captures audio from the car microphone and buffers raw 16 kHz / 16-bit / mono PCM.
5
+ /// Recording stops automatically when silence is detected or the max duration is reached.
33
6
  class VoiceInputManager {
34
7
  private var audioEngine: AVAudioEngine?
35
8
  private var voiceControlTemplate: CPVoiceControlTemplate?
36
- private var resultBox: ResultBox?
9
+ private var continuation: CheckedContinuation<[Int16], Error>?
37
10
  private var samples: [Int16] = []
38
11
  private var isStopping = false
39
12
  private let stopLock = NSLock()
40
13
 
41
- // STT
42
- private var recognitionRequest: SFSpeechAudioBufferRecognitionRequest?
43
- private var isSTTMode = false
44
-
45
14
  // Timing
46
15
  private var recordingStart: Date?
47
16
  private var silenceStart: Date?
@@ -64,33 +33,30 @@ class VoiceInputManager {
64
33
  interfaceController: AutoPlayInterfaceController?,
65
34
  silenceThresholdMs: Double,
66
35
  maxDurationMs: Double,
67
- listeningText: String,
68
- preferSpeechToText: Bool,
69
- onChunk: ((_ chunk: VoiceInputChunk) -> Void)?
70
- ) async throws -> VoiceInputResult {
71
- return try await withCheckedThrowingContinuation { cont in
72
- let box = ResultBox(cont)
73
- self.resultBox = box
36
+ listeningText: String
37
+ ) async throws -> Data {
38
+ let samples = try await withCheckedThrowingContinuation {
39
+ (cont: CheckedContinuation<[Int16], Error>) in
40
+ self.continuation = cont
74
41
  self.samples = []
75
42
  self.isStopping = false
76
- self.isSTTMode = preferSpeechToText
77
43
 
78
44
  do {
79
45
  try self.startCapture(
80
46
  interfaceController: interfaceController,
81
47
  silenceThresholdMs: silenceThresholdMs,
82
48
  maxDurationMs: maxDurationMs,
83
- listeningText: listeningText,
84
- preferSpeechToText: preferSpeechToText,
85
- onChunk: onChunk,
86
- box: box
49
+ listeningText: listeningText
87
50
  )
88
51
  }
89
52
  catch {
90
- self.cleanup(interfaceController: interfaceController)
91
- box.resume(throwing: error)
53
+ self.stopCapture(interfaceController: interfaceController)
54
+ self.continuation = nil
55
+ cont.resume(throwing: error)
92
56
  }
93
57
  }
58
+
59
+ return samplesAsData(samples)
94
60
  }
95
61
 
96
62
  func stop(interfaceController: AutoPlayInterfaceController? = nil) {
@@ -100,22 +66,14 @@ class VoiceInputManager {
100
66
  return
101
67
  }
102
68
  isStopping = true
103
- let wasSTTMode = isSTTMode
104
- let box = resultBox
69
+ let capturedContinuation = continuation
105
70
  let capturedSamples = samples
106
- resultBox = nil
71
+ continuation = nil
107
72
  samples = []
108
73
  stopLock.unlock()
109
74
 
110
- if wasSTTMode {
111
- // endAudio() causes the recognition task to fire its final result,
112
- // which resumes the box. Engine teardown happens there too.
113
- recognitionRequest?.endAudio()
114
- }
115
- else {
116
- cleanup(interfaceController: interfaceController)
117
- box?.resume(returning: makePCMResult(from: capturedSamples))
118
- }
75
+ stopCapture(interfaceController: interfaceController)
76
+ capturedContinuation?.resume(returning: capturedSamples)
119
77
  }
120
78
 
121
79
  // MARK: - Private
@@ -124,15 +82,13 @@ class VoiceInputManager {
124
82
  interfaceController: AutoPlayInterfaceController?,
125
83
  silenceThresholdMs: Double,
126
84
  maxDurationMs: Double,
127
- listeningText: String,
128
- preferSpeechToText: Bool,
129
- onChunk: ((_ chunk: VoiceInputChunk) -> Void)?,
130
- box: ResultBox
85
+ listeningText: String
131
86
  ) throws {
132
87
  guard AVAudioSession.sharedInstance().recordPermission == .granted else {
133
88
  throw VoiceInputError.microphonePermissionDenied
134
89
  }
135
90
 
91
+ // Activate the session first so inputNode reports the correct hardware format
136
92
  let session = AVAudioSession.sharedInstance()
137
93
  try session.setCategory(.playAndRecord, mode: .measurement, options: [])
138
94
  try session.setActive(true)
@@ -141,59 +97,12 @@ class VoiceInputManager {
141
97
  presentVoiceTemplate(interfaceController: interfaceController, listeningText: listeningText)
142
98
  }
143
99
 
144
- var activeRecognitionRequest: SFSpeechAudioBufferRecognitionRequest? = nil
145
-
146
- if preferSpeechToText, SFSpeechRecognizer.authorizationStatus() == .authorized,
147
- let recognizer = SFSpeechRecognizer(locale: Locale.current),
148
- recognizer.isAvailable
149
- {
150
- let request = SFSpeechAudioBufferRecognitionRequest()
151
- request.shouldReportPartialResults = true
152
- recognitionRequest = request
153
- activeRecognitionRequest = request
154
-
155
- recognizer.recognitionTask(with: request) { [weak self] result, error in
156
- guard let self else { return }
157
-
158
- if error != nil {
159
- // STT failed — fall back to whatever PCM was accumulated
160
- self.stopLock.lock()
161
- let capturedSamples = self.samples
162
- self.samples = []
163
- self.stopLock.unlock()
164
-
165
- self.cleanup(interfaceController: interfaceController)
166
- box.resume(returning: self.makePCMResult(from: capturedSamples))
167
- return
168
- }
169
-
170
- guard let result else { return }
171
-
172
- if result.isFinal {
173
- self.stopLock.lock()
174
- self.isStopping = true
175
- self.samples = []
176
- self.stopLock.unlock()
177
-
178
- self.cleanup(interfaceController: interfaceController)
179
- box.resume(
180
- returning: VoiceInputResult(
181
- transcription: result.bestTranscription.formattedString,
182
- audio: nil
183
- )
184
- )
185
- }
186
- else {
187
- onChunk?(VoiceInputChunk(partial: result.bestTranscription.formattedString, audio: nil))
188
- }
189
- }
190
- }
191
-
192
100
  let engine = AVAudioEngine()
193
101
  let inputNode = engine.inputNode
194
102
  let nativeFormat = inputNode.outputFormat(forBus: 0)
195
103
 
196
- guard let converter = AVAudioConverter(from: nativeFormat, to: VoiceInputManager.targetFormat) else {
104
+ let targetFormat = VoiceInputManager.targetFormat
105
+ guard let converter = AVAudioConverter(from: nativeFormat, to: targetFormat) else {
197
106
  throw VoiceInputError.converterUnavailable
198
107
  }
199
108
 
@@ -207,43 +116,36 @@ class VoiceInputManager {
207
116
  ) { [weak self] buffer, _ in
208
117
  guard let self, !self.isStopping else { return }
209
118
 
210
- // Feed STT if active
211
- activeRecognitionRequest?.append(buffer)
212
-
213
- // Convert to 16kHz int16 for accumulation and PCM chunks
214
119
  let outputFrameCapacity = AVAudioFrameCount(
215
- Double(buffer.frameLength) * VoiceInputManager.sampleRate / nativeFormat.sampleRate
120
+ Double(buffer.frameLength)
121
+ * VoiceInputManager.sampleRate
122
+ / nativeFormat.sampleRate
216
123
  )
124
+
217
125
  guard
218
126
  let outputBuffer = AVAudioPCMBuffer(
219
- pcmFormat: VoiceInputManager.targetFormat,
127
+ pcmFormat: targetFormat,
220
128
  frameCapacity: outputFrameCapacity
221
129
  )
222
130
  else { return }
223
131
 
224
132
  var conversionError: NSError?
225
- let status = converter.convert(to: outputBuffer, error: &conversionError) { _, outStatus in
133
+ let status = converter.convert(to: outputBuffer, error: &conversionError) {
134
+ _,
135
+ outStatus in
226
136
  outStatus.pointee = .haveData
227
137
  return buffer
228
138
  }
139
+
229
140
  guard status != .error, let int16Data = outputBuffer.int16ChannelData else { return }
230
141
 
231
142
  let frameCount = Int(outputBuffer.frameLength)
232
143
  let newSamples = Array(UnsafeBufferPointer(start: int16Data[0], count: frameCount))
233
144
  self.samples.append(contentsOf: newSamples)
234
145
 
235
- // PCM chunk callback
236
- if activeRecognitionRequest == nil, let onChunk {
237
- if let chunkBuffer = try? ArrayBuffer.copy(
238
- data: newSamples.withUnsafeBufferPointer { Data(buffer: $0) }
239
- ) {
240
- onChunk(VoiceInputChunk(partial: nil, audio: chunkBuffer))
241
- }
242
- }
243
-
244
146
  let now = Date()
245
147
 
246
- // Max duration — applies in both modes
148
+ // Max duration check
247
149
  if let start = self.recordingStart,
248
150
  now.timeIntervalSince(start) * 1000 >= maxDurationMs
249
151
  {
@@ -258,9 +160,7 @@ class VoiceInputManager {
258
160
  {
259
161
  let peak = newSamples.reduce(0) { max($0, abs(Int($1))) }
260
162
  if peak < VoiceInputManager.silenceAmplitudeThreshold {
261
- if self.silenceStart == nil {
262
- self.silenceStart = now
263
- }
163
+ if self.silenceStart == nil { self.silenceStart = now }
264
164
  if let silenceBegin = self.silenceStart,
265
165
  now.timeIntervalSince(silenceBegin) * 1000 >= silenceThresholdMs
266
166
  {
@@ -283,11 +183,10 @@ class VoiceInputManager {
283
183
  }
284
184
  }
285
185
 
286
- private func cleanup(interfaceController: AutoPlayInterfaceController?) {
186
+ private func stopCapture(interfaceController: AutoPlayInterfaceController?) {
287
187
  audioEngine?.inputNode.removeTap(onBus: 0)
288
188
  audioEngine?.stop()
289
189
  audioEngine = nil
290
- recognitionRequest = nil
291
190
  recordingStart = nil
292
191
  silenceStart = nil
293
192
  try? AVAudioSession.sharedInstance().setActive(false, options: .notifyOthersOnDeactivation)
@@ -296,12 +195,6 @@ class VoiceInputManager {
296
195
  }
297
196
  }
298
197
 
299
- private func makePCMResult(from samples: [Int16]) -> VoiceInputResult {
300
- let data = samples.withUnsafeBufferPointer { Data(buffer: $0) }
301
- let buffer = try? ArrayBuffer.copy(data: data)
302
- return VoiceInputResult(transcription: nil, audio: buffer)
303
- }
304
-
305
198
  private func presentVoiceTemplate(interfaceController: AutoPlayInterfaceController, listeningText: String) {
306
199
  let listeningState = CPVoiceControlState(
307
200
  identifier: "listening",
@@ -325,6 +218,12 @@ class VoiceInputManager {
325
218
  }
326
219
  voiceControlTemplate = nil
327
220
  }
221
+
222
+ private func samplesAsData(_ samples: [Int16]) -> Data {
223
+ samples.withUnsafeBufferPointer { ptr in
224
+ Data(buffer: ptr)
225
+ }
226
+ }
328
227
  }
329
228
 
330
229
  enum VoiceInputError: Error {
package/lib/index.d.ts CHANGED
@@ -1,8 +1,7 @@
1
1
  import { HybridAndroidAutoTelemetry } from './hybrid/HybridAndroidAutoTelemetry';
2
2
  import { HybridAutoPlay } from './hybrid/HybridAutoPlay';
3
- import { HybridVoice } from './hybrid/HybridVoice';
4
3
  import type { AndroidAutomotive } from './specs/AndroidAutomotive.nitro';
5
- export { HybridAndroidAutoTelemetry, HybridAutoPlay, HybridVoice };
4
+ export { HybridAndroidAutoTelemetry, HybridAutoPlay };
6
5
  export declare const HybridAndroidAutomotive: AndroidAutomotive | null;
7
6
  /**
8
7
  * These are the static module names for the app running on the mobile device, head unit screen and the CarPlay dashboard.
@@ -40,7 +39,6 @@ export * from './types/SignInMethod';
40
39
  export * from './types/Telemetry';
41
40
  export * from './types/Text';
42
41
  export * from './types/Trip';
43
- export type { VoiceInputChunk, VoiceInputOptions, VoiceInputResult } from './types/Voice';
44
42
  export type { AlertPriority, NavigationAlert as Alert, NavigationAlertAction as AlertAction, } from './utils/NitroAlert';
45
43
  export type { ThemedColor } from './utils/NitroColor';
46
44
  export type { GridButton } from './utils/NitroGrid';
package/lib/index.js CHANGED
@@ -3,9 +3,8 @@ import { NitroModules } from 'react-native-nitro-modules';
3
3
  import AutoPlayHeadlessJsTask from './AutoPlayHeadlessJsTask';
4
4
  import { HybridAndroidAutoTelemetry } from './hybrid/HybridAndroidAutoTelemetry';
5
5
  import { HybridAutoPlay } from './hybrid/HybridAutoPlay';
6
- import { HybridVoice } from './hybrid/HybridVoice';
7
6
  AutoPlayHeadlessJsTask.registerHeadlessTask(HybridAutoPlay);
8
- export { HybridAndroidAutoTelemetry, HybridAutoPlay, HybridVoice };
7
+ export { HybridAndroidAutoTelemetry, HybridAutoPlay };
9
8
  export const HybridAndroidAutomotive = Platform.OS === 'android'
10
9
  ? NitroModules.createHybridObject('AndroidAutomotive')
11
10
  : null;
@@ -31,6 +31,35 @@ export interface AutoPlay extends HybridObject<{
31
31
  * @namespace Android
32
32
  */
33
33
  addListenerVoiceInput(callback: (coordinates: Location | undefined, query: string | undefined) => void): CleanupCallback;
34
+ /**
35
+ * Returns true if microphone permission has already been granted.
36
+ */
37
+ hasVoiceInputPermission(): boolean;
38
+ /**
39
+ * Request microphone permission from the user.
40
+ * On Android: uses the car context when Android Auto is connected, otherwise
41
+ * falls back to the React Native application context.
42
+ * On iOS: uses AVAudioApplication (iOS 17+) or AVAudioSession (iOS 15–16).
43
+ * Returns true if permission was granted, false if denied.
44
+ */
45
+ requestVoiceInputPermission(): Promise<boolean>;
46
+ /**
47
+ * Start an in-app voice recording session.
48
+ * On Android: acquires audio focus and captures via CarAudioRecord when
49
+ * Android Auto is connected, otherwise uses standard AudioRecord.
50
+ * On iOS: presents CPVoiceControlTemplate (when a car is connected) and
51
+ * captures audio via AVAudioEngine.
52
+ * Resolves with the complete raw PCM buffer (16 kHz, 16-bit, mono) when
53
+ * silence is detected, the max duration is reached, or stopVoiceInput() is called.
54
+ * Rejects if microphone permission has not been granted or recording fails to start.
55
+ */
56
+ startVoiceInput(silenceThresholdMs?: number, maxDurationMs?: number, listeningText?: string): Promise<ArrayBuffer>;
57
+ /**
58
+ * Stop the active voice recording session early. Causes the Promise returned
59
+ * by startVoiceInput() to resolve with the audio captured so far.
60
+ * No-op if no recording is in progress.
61
+ */
62
+ stopVoiceInput(): void;
34
63
  /**
35
64
  * sets the specified template as root template, initializes a new stack
36
65
  * Promise might contain an error message in case setting root template failed
@@ -2,7 +2,6 @@ import React from 'react';
2
2
  import { AppRegistry, Platform } from 'react-native';
3
3
  import { NitroModules } from 'react-native-nitro-modules';
4
4
  import { MapTemplateProvider } from '../components/MapTemplateContext';
5
- import OnAppearedChildRenderer from '../components/OnAppearedChildRenderer';
6
5
  import { SafeAreaInsetsProvider } from '../components/SafeAreaInsetsContext';
7
6
  import { HybridAutoPlay } from '../hybrid/HybridAutoPlay';
8
7
  import { NitroActionUtil } from '../utils/NitroAction';
@@ -24,11 +23,7 @@ export class MapTemplate extends Template {
24
23
  children: React.createElement(SafeAreaInsetsProvider, {
25
24
  moduleName: this.id,
26
25
  // biome-ignore lint/correctness/noChildrenProp: there is no other way in a ts file
27
- children: React.createElement(OnAppearedChildRenderer, {
28
- // biome-ignore lint/correctness/noChildrenProp: there is no other way in a ts file
29
- children: React.createElement(component, props),
30
- moduleName: this.id,
31
- }),
26
+ children: React.createElement(component, props),
32
27
  }),
33
28
  }));
34
29
  const nitroConfig = {
package/nitro.json CHANGED
@@ -9,16 +9,6 @@
9
9
  "androidCxxLibName": "ReactNativeAutoPlay"
10
10
  },
11
11
  "autolinking": {
12
- "Voice": {
13
- "android": {
14
- "implementationClassName": "HybridVoice",
15
- "language": "kotlin"
16
- },
17
- "ios": {
18
- "implementationClassName": "HybridVoice",
19
- "language": "swift"
20
- }
21
- },
22
12
  "AutoPlay": {
23
13
  "android": {
24
14
  "implementationClassName": "HybridAutoPlay",
@@ -45,7 +45,6 @@ target_sources(
45
45
  ../nitrogen/generated/shared/c++/HybridMessageTemplateSpec.cpp
46
46
  ../nitrogen/generated/shared/c++/HybridSearchTemplateSpec.cpp
47
47
  ../nitrogen/generated/shared/c++/HybridSignInTemplateSpec.cpp
48
- ../nitrogen/generated/shared/c++/HybridVoiceSpec.cpp
49
48
  # Android-specific Nitrogen C++ sources
50
49
  ../nitrogen/generated/android/c++/JHybridAndroidAutomotiveSpec.cpp
51
50
  ../nitrogen/generated/android/c++/JHybridAndroidAutoTelemetrySpec.cpp
@@ -63,7 +62,6 @@ target_sources(
63
62
  ../nitrogen/generated/android/c++/JHybridSearchTemplateSpec.cpp
64
63
  ../nitrogen/generated/android/c++/JHybridSignInTemplateSpec.cpp
65
64
  ../nitrogen/generated/android/c++/JVariant_QrSignIn_PinSignIn_InputSignIn_GoogleSignIn.cpp
66
- ../nitrogen/generated/android/c++/JHybridVoiceSpec.cpp
67
65
  )
68
66
 
69
67
  # From node_modules/react-native/ReactAndroid/cmake-utils/folly-flags.cmake
@@ -46,8 +46,6 @@
46
46
  #include "JHybridSearchTemplateSpec.hpp"
47
47
  #include "JHybridSignInTemplateSpec.hpp"
48
48
  #include "JFunc_void_std__optional_std__string__std__optional_GoogleSignInAccount_.hpp"
49
- #include "JHybridVoiceSpec.hpp"
50
- #include "JFunc_void_VoiceInputChunk.hpp"
51
49
  #include <NitroModules/DefaultConstructableObject.hpp>
52
50
 
53
51
  namespace margelo::nitro::swe::iternio::reactnativeautoplay {
@@ -58,14 +56,6 @@ int initialize(JavaVM* vm) {
58
56
  });
59
57
  }
60
58
 
61
- struct JHybridVoiceSpecImpl: public jni::JavaClass<JHybridVoiceSpecImpl, JHybridVoiceSpec::JavaPart> {
62
- static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/swe/iternio/reactnativeautoplay/HybridVoice;";
63
- static std::shared_ptr<JHybridVoiceSpec> create() {
64
- static const auto constructorFn = javaClassStatic()->getConstructor<JHybridVoiceSpecImpl::javaobject()>();
65
- jni::local_ref<JHybridVoiceSpec::JavaPart> javaPart = javaClassStatic()->newObject(constructorFn);
66
- return javaPart->getJHybridVoiceSpec();
67
- }
68
- };
69
59
  struct JHybridAutoPlaySpecImpl: public jni::JavaClass<JHybridAutoPlaySpecImpl, JHybridAutoPlaySpec::JavaPart> {
70
60
  static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/swe/iternio/reactnativeautoplay/HybridAutoPlay;";
71
61
  static std::shared_ptr<JHybridAutoPlaySpec> create() {
@@ -191,16 +181,8 @@ void registerAllNatives() {
191
181
  margelo::nitro::swe::iternio::reactnativeautoplay::JHybridSearchTemplateSpec::CxxPart::registerNatives();
192
182
  margelo::nitro::swe::iternio::reactnativeautoplay::JHybridSignInTemplateSpec::CxxPart::registerNatives();
193
183
  margelo::nitro::swe::iternio::reactnativeautoplay::JFunc_void_std__optional_std__string__std__optional_GoogleSignInAccount__cxx::registerNatives();
194
- margelo::nitro::swe::iternio::reactnativeautoplay::JHybridVoiceSpec::CxxPart::registerNatives();
195
- margelo::nitro::swe::iternio::reactnativeautoplay::JFunc_void_VoiceInputChunk_cxx::registerNatives();
196
184
 
197
185
  // Register Nitro Hybrid Objects
198
- HybridObjectRegistry::registerHybridObjectConstructor(
199
- "Voice",
200
- []() -> std::shared_ptr<HybridObject> {
201
- return JHybridVoiceSpecImpl::create();
202
- }
203
- );
204
186
  HybridObjectRegistry::registerHybridObjectConstructor(
205
187
  "AutoPlay",
206
188
  []() -> std::shared_ptr<HybridObject> {
@@ -37,6 +37,8 @@ namespace margelo::nitro::swe::iternio::reactnativeautoplay { enum class NitroBu
37
37
  #include <NitroModules/JNICallable.hpp>
38
38
  #include <NitroModules/Promise.hpp>
39
39
  #include <NitroModules/JPromise.hpp>
40
+ #include <NitroModules/ArrayBuffer.hpp>
41
+ #include <NitroModules/JArrayBuffer.hpp>
40
42
  #include <NitroModules/JUnit.hpp>
41
43
  #include "EventName.hpp"
42
44
  #include "JEventName.hpp"
@@ -143,6 +145,47 @@ namespace margelo::nitro::swe::iternio::reactnativeautoplay {
143
145
  }
144
146
  }();
145
147
  }
148
+ bool JHybridAutoPlaySpec::hasVoiceInputPermission() {
149
+ static const auto method = _javaPart->javaClassStatic()->getMethod<jboolean()>("hasVoiceInputPermission");
150
+ auto __result = method(_javaPart);
151
+ return static_cast<bool>(__result);
152
+ }
153
+ std::shared_ptr<Promise<bool>> JHybridAutoPlaySpec::requestVoiceInputPermission() {
154
+ static const auto method = _javaPart->javaClassStatic()->getMethod<jni::local_ref<JPromise::javaobject>()>("requestVoiceInputPermission");
155
+ auto __result = method(_javaPart);
156
+ return [&]() {
157
+ auto __promise = Promise<bool>::create();
158
+ __result->cthis()->addOnResolvedListener([=](const jni::alias_ref<jni::JObject>& __boxedResult) {
159
+ auto __result = jni::static_ref_cast<jni::JBoolean>(__boxedResult);
160
+ __promise->resolve(static_cast<bool>(__result->value()));
161
+ });
162
+ __result->cthis()->addOnRejectedListener([=](const jni::alias_ref<jni::JThrowable>& __throwable) {
163
+ jni::JniException __jniError(__throwable);
164
+ __promise->reject(std::make_exception_ptr(__jniError));
165
+ });
166
+ return __promise;
167
+ }();
168
+ }
169
+ std::shared_ptr<Promise<std::shared_ptr<ArrayBuffer>>> JHybridAutoPlaySpec::startVoiceInput(std::optional<double> silenceThresholdMs, std::optional<double> maxDurationMs, const std::optional<std::string>& listeningText) {
170
+ static const auto method = _javaPart->javaClassStatic()->getMethod<jni::local_ref<JPromise::javaobject>(jni::alias_ref<jni::JDouble> /* silenceThresholdMs */, jni::alias_ref<jni::JDouble> /* maxDurationMs */, jni::alias_ref<jni::JString> /* listeningText */)>("startVoiceInput");
171
+ auto __result = method(_javaPart, silenceThresholdMs.has_value() ? jni::JDouble::valueOf(silenceThresholdMs.value()) : nullptr, maxDurationMs.has_value() ? jni::JDouble::valueOf(maxDurationMs.value()) : nullptr, listeningText.has_value() ? jni::make_jstring(listeningText.value()) : nullptr);
172
+ return [&]() {
173
+ auto __promise = Promise<std::shared_ptr<ArrayBuffer>>::create();
174
+ __result->cthis()->addOnResolvedListener([=](const jni::alias_ref<jni::JObject>& __boxedResult) {
175
+ auto __result = jni::static_ref_cast<JArrayBuffer::javaobject>(__boxedResult);
176
+ __promise->resolve(__result->cthis()->getArrayBuffer());
177
+ });
178
+ __result->cthis()->addOnRejectedListener([=](const jni::alias_ref<jni::JThrowable>& __throwable) {
179
+ jni::JniException __jniError(__throwable);
180
+ __promise->reject(std::make_exception_ptr(__jniError));
181
+ });
182
+ return __promise;
183
+ }();
184
+ }
185
+ void JHybridAutoPlaySpec::stopVoiceInput() {
186
+ static const auto method = _javaPart->javaClassStatic()->getMethod<void()>("stopVoiceInput");
187
+ method(_javaPart);
188
+ }
146
189
  std::shared_ptr<Promise<void>> JHybridAutoPlaySpec::setRootTemplate(const std::string& templateId) {
147
190
  static const auto method = _javaPart->javaClassStatic()->getMethod<jni::local_ref<JPromise::javaobject>(jni::alias_ref<jni::JString> /* templateId */)>("setRootTemplate");
148
191
  auto __result = method(_javaPart, jni::make_jstring(templateId));
@@ -57,6 +57,10 @@ namespace margelo::nitro::swe::iternio::reactnativeautoplay {
57
57
  std::function<void()> addListener(EventName eventType, const std::function<void()>& callback) override;
58
58
  std::function<void()> addListenerRenderState(const std::string& moduleName, const std::function<void(VisibilityState /* payload */)>& callback) override;
59
59
  std::function<void()> addListenerVoiceInput(const std::function<void(const std::optional<Location>& /* coordinates */, const std::optional<std::string>& /* query */)>& callback) override;
60
+ bool hasVoiceInputPermission() override;
61
+ std::shared_ptr<Promise<bool>> requestVoiceInputPermission() override;
62
+ std::shared_ptr<Promise<std::shared_ptr<ArrayBuffer>>> startVoiceInput(std::optional<double> silenceThresholdMs, std::optional<double> maxDurationMs, const std::optional<std::string>& listeningText) override;
63
+ void stopVoiceInput() override;
60
64
  std::shared_ptr<Promise<void>> setRootTemplate(const std::string& templateId) override;
61
65
  std::shared_ptr<Promise<void>> pushTemplate(const std::string& templateId) override;
62
66
  std::shared_ptr<Promise<void>> popTemplate(std::optional<bool> animate) override;
@@ -11,6 +11,7 @@ import androidx.annotation.Keep
11
11
  import com.facebook.jni.HybridData
12
12
  import com.facebook.proguard.annotations.DoNotStrip
13
13
  import com.margelo.nitro.core.Promise
14
+ import com.margelo.nitro.core.ArrayBuffer
14
15
  import com.margelo.nitro.core.HybridObject
15
16
 
16
17
  /**
@@ -56,6 +57,22 @@ abstract class HybridAutoPlaySpec: HybridObject() {
56
57
  return Func_void_java(__result)
57
58
  }
58
59
 
60
+ @DoNotStrip
61
+ @Keep
62
+ abstract fun hasVoiceInputPermission(): Boolean
63
+
64
+ @DoNotStrip
65
+ @Keep
66
+ abstract fun requestVoiceInputPermission(): Promise<Boolean>
67
+
68
+ @DoNotStrip
69
+ @Keep
70
+ abstract fun startVoiceInput(silenceThresholdMs: Double?, maxDurationMs: Double?, listeningText: String?): Promise<ArrayBuffer>
71
+
72
+ @DoNotStrip
73
+ @Keep
74
+ abstract fun stopVoiceInput(): Unit
75
+
59
76
  @DoNotStrip
60
77
  @Keep
61
78
  abstract fun setRootTemplate(templateId: String): Promise<Unit>
@@ -17,7 +17,6 @@
17
17
  #include "HybridMapTemplateSpecSwift.hpp"
18
18
  #include "HybridMessageTemplateSpecSwift.hpp"
19
19
  #include "HybridSearchTemplateSpecSwift.hpp"
20
- #include "HybridVoiceSpecSwift.hpp"
21
20
  #include "ReactNativeAutoPlay-Swift-Cxx-Umbrella.hpp"
22
21
  #include <NitroModules/NitroDefines.hpp>
23
22
 
@@ -47,6 +46,14 @@ namespace margelo::nitro::swe::iternio::reactnativeautoplay::bridge::swift {
47
46
  };
48
47
  }
49
48
 
49
+ // pragma MARK: std::function<void(bool /* result */)>
50
+ Func_void_bool create_Func_void_bool(void* NON_NULL swiftClosureWrapper) noexcept {
51
+ auto swiftClosure = ReactNativeAutoPlay::Func_void_bool::fromUnsafe(swiftClosureWrapper);
52
+ return [swiftClosure = std::move(swiftClosure)](bool result) mutable -> void {
53
+ swiftClosure.call(result);
54
+ };
55
+ }
56
+
50
57
  // pragma MARK: std::function<void(const std::exception_ptr& /* error */)>
51
58
  Func_void_std__exception_ptr create_Func_void_std__exception_ptr(void* NON_NULL swiftClosureWrapper) noexcept {
52
59
  auto swiftClosure = ReactNativeAutoPlay::Func_void_std__exception_ptr::fromUnsafe(swiftClosureWrapper);
@@ -55,6 +62,14 @@ namespace margelo::nitro::swe::iternio::reactnativeautoplay::bridge::swift {
55
62
  };
56
63
  }
57
64
 
65
+ // pragma MARK: std::function<void(const std::shared_ptr<ArrayBuffer>& /* result */)>
66
+ Func_void_std__shared_ptr_ArrayBuffer_ create_Func_void_std__shared_ptr_ArrayBuffer_(void* NON_NULL swiftClosureWrapper) noexcept {
67
+ auto swiftClosure = ReactNativeAutoPlay::Func_void_std__shared_ptr_ArrayBuffer_::fromUnsafe(swiftClosureWrapper);
68
+ return [swiftClosure = std::move(swiftClosure)](const std::shared_ptr<ArrayBuffer>& result) mutable -> void {
69
+ swiftClosure.call(ArrayBufferHolder(result));
70
+ };
71
+ }
72
+
58
73
  // pragma MARK: std::function<void(const SafeAreaInsets& /* insets */)>
59
74
  Func_void_SafeAreaInsets create_Func_void_SafeAreaInsets(void* NON_NULL swiftClosureWrapper) noexcept {
60
75
  auto swiftClosure = ReactNativeAutoPlay::Func_void_SafeAreaInsets::fromUnsafe(swiftClosureWrapper);
@@ -159,14 +174,6 @@ namespace margelo::nitro::swe::iternio::reactnativeautoplay::bridge::swift {
159
174
  };
160
175
  }
161
176
 
162
- // pragma MARK: std::function<void(bool /* isPanningInterfaceVisible */)>
163
- Func_void_bool create_Func_void_bool(void* NON_NULL swiftClosureWrapper) noexcept {
164
- auto swiftClosure = ReactNativeAutoPlay::Func_void_bool::fromUnsafe(swiftClosureWrapper);
165
- return [swiftClosure = std::move(swiftClosure)](bool isPanningInterfaceVisible) mutable -> void {
166
- swiftClosure.call(isPanningInterfaceVisible);
167
- };
168
- }
169
-
170
177
  // pragma MARK: std::shared_ptr<HybridGridTemplateSpec>
171
178
  std::shared_ptr<HybridGridTemplateSpec> create_std__shared_ptr_HybridGridTemplateSpec_(void* NON_NULL swiftUnsafePointer) noexcept {
172
179
  ReactNativeAutoPlay::HybridGridTemplateSpec_cxx swiftPart = ReactNativeAutoPlay::HybridGridTemplateSpec_cxx::fromUnsafe(swiftUnsafePointer);
@@ -302,37 +309,5 @@ namespace margelo::nitro::swe::iternio::reactnativeautoplay::bridge::swift {
302
309
  ReactNativeAutoPlay::HybridSearchTemplateSpec_cxx& swiftPart = swiftWrapper->getSwiftPart();
303
310
  return swiftPart.toUnsafe();
304
311
  }
305
-
306
- // pragma MARK: std::function<void(const VoiceInputResult& /* result */)>
307
- Func_void_VoiceInputResult create_Func_void_VoiceInputResult(void* NON_NULL swiftClosureWrapper) noexcept {
308
- auto swiftClosure = ReactNativeAutoPlay::Func_void_VoiceInputResult::fromUnsafe(swiftClosureWrapper);
309
- return [swiftClosure = std::move(swiftClosure)](const VoiceInputResult& result) mutable -> void {
310
- swiftClosure.call(result);
311
- };
312
- }
313
-
314
- // pragma MARK: std::function<void(const VoiceInputChunk& /* chunk */)>
315
- Func_void_VoiceInputChunk create_Func_void_VoiceInputChunk(void* NON_NULL swiftClosureWrapper) noexcept {
316
- auto swiftClosure = ReactNativeAutoPlay::Func_void_VoiceInputChunk::fromUnsafe(swiftClosureWrapper);
317
- return [swiftClosure = std::move(swiftClosure)](const VoiceInputChunk& chunk) mutable -> void {
318
- swiftClosure.call(chunk);
319
- };
320
- }
321
-
322
- // pragma MARK: std::shared_ptr<HybridVoiceSpec>
323
- std::shared_ptr<HybridVoiceSpec> create_std__shared_ptr_HybridVoiceSpec_(void* NON_NULL swiftUnsafePointer) noexcept {
324
- ReactNativeAutoPlay::HybridVoiceSpec_cxx swiftPart = ReactNativeAutoPlay::HybridVoiceSpec_cxx::fromUnsafe(swiftUnsafePointer);
325
- return std::make_shared<margelo::nitro::swe::iternio::reactnativeautoplay::HybridVoiceSpecSwift>(swiftPart);
326
- }
327
- void* NON_NULL get_std__shared_ptr_HybridVoiceSpec_(std__shared_ptr_HybridVoiceSpec_ cppType) {
328
- std::shared_ptr<margelo::nitro::swe::iternio::reactnativeautoplay::HybridVoiceSpecSwift> swiftWrapper = std::dynamic_pointer_cast<margelo::nitro::swe::iternio::reactnativeautoplay::HybridVoiceSpecSwift>(cppType);
329
- #ifdef NITRO_DEBUG
330
- if (swiftWrapper == nullptr) [[unlikely]] {
331
- throw std::runtime_error("Class \"HybridVoiceSpec\" is not implemented in Swift!");
332
- }
333
- #endif
334
- ReactNativeAutoPlay::HybridVoiceSpec_cxx& swiftPart = swiftWrapper->getSwiftPart();
335
- return swiftPart.toUnsafe();
336
- }
337
312
 
338
313
  } // namespace margelo::nitro::swe::iternio::reactnativeautoplay::bridge::swift