@mentra/bluetooth-sdk 0.1.8 → 0.1.10
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.
- package/README.md +116 -18
- package/android/build.gradle +289 -16
- package/android/gradle.properties.example +9 -0
- package/android/lc3Lib/build.gradle +95 -6
- package/android/settings.gradle +3 -0
- package/android/silero/LICENSE.md +17 -0
- package/android/silero/build.gradle +42 -0
- package/android/silero/proguard-rules.pro +24 -0
- package/android/silero/src/androidTest/assets/hello.wav +0 -0
- package/android/silero/src/androidTest/java/com/konovalov/vad/silero/VadSileroTest.kt +79 -0
- package/android/silero/src/main/assets/LICENSE +21 -0
- package/android/silero/src/main/assets/silero_vad.onnx +0 -0
- package/android/silero/src/main/java/com/konovalov/vad/silero/Vad.kt +160 -0
- package/android/silero/src/main/java/com/konovalov/vad/silero/VadSilero.kt +449 -0
- package/android/silero/src/main/java/com/konovalov/vad/silero/config/FrameSize.kt +15 -0
- package/android/silero/src/main/java/com/konovalov/vad/silero/config/Mode.kt +14 -0
- package/android/silero/src/main/java/com/konovalov/vad/silero/config/SampleRate.kt +12 -0
- package/android/silero/src/main/java/com/konovalov/vad/silero/utils/AudioUtils.kt +47 -0
- package/android/silero/src/main/java/com/konovalov/vad/silero/utils/TensorMap.kt +41 -0
- package/android/silero/src/test/java/com/konovalov/vad/silero/utils/AudioUtilsTest.kt +44 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkDefaults.kt +6 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkModule.kt +165 -27
- package/android/src/main/java/com/mentra/bluetoothsdk/Bridge.kt +89 -21
- package/android/src/main/java/com/mentra/bluetoothsdk/DeviceManager.kt +189 -16
- package/android/src/main/java/com/mentra/bluetoothsdk/DeviceStore.kt +22 -2
- package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothSdk.kt +787 -85
- package/android/src/main/java/com/mentra/bluetoothsdk/audio/AudioModels.kt +6 -2
- package/android/src/main/java/com/mentra/bluetoothsdk/camera/CameraModels.kt +123 -10
- package/android/src/main/java/com/mentra/bluetoothsdk/controllers/ControllerManager.kt +12 -2
- package/android/src/main/java/com/mentra/bluetoothsdk/controllers/R1.kt +8 -2
- package/android/src/main/java/com/mentra/bluetoothsdk/debug/BleTraceLogger.kt +33 -17
- package/android/src/main/java/com/mentra/bluetoothsdk/events/BluetoothEvents.kt +101 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/internal/MapParsing.kt +8 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/photoreceiver/LocalPhotoReceiverRegistry.java +108 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/photoreceiver/LocalPhotoUploadServer.kt +399 -74
- package/android/src/main/java/com/mentra/bluetoothsdk/photoreceiver/MentraPhotoReceiverModule.kt +100 -17
- package/android/src/main/java/com/mentra/bluetoothsdk/requests/DisplayRequests.kt +17 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G1.java +2 -2
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G2.kt +569 -220
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Mach1.java +2 -2
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraLive.java +271 -66
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraNex.kt +176 -104
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraosBle.java +12753 -27036
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/SGCManager.kt +54 -2
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Simulated.kt +16 -4
- package/android/src/main/java/com/mentra/bluetoothsdk/status/DeviceStatus.kt +60 -3
- package/android/src/main/java/com/mentra/bluetoothsdk/streaming/StreamModels.kt +12 -3
- package/android/src/main/java/com/mentra/bluetoothsdk/stt/STTTools.kt +87 -36
- package/android/src/main/java/com/mentra/bluetoothsdk/stt/SherpaOnnxTranscriber.kt +13 -4
- package/android/src/main/java/com/mentra/bluetoothsdk/stt/VadGateSpeechPolicy.kt +229 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/tts/TTSTools.kt +180 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/AvifExifStripper.java +573 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/BlePhotoUploadService.java +488 -50
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/HeifExifTagReader.java +199 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/MessageChunkReassembler.java +18 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/MessageChunker.java +64 -18
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/NexSGCUtils.kt +48 -39
- package/android/src/test/java/com/mentra/bluetoothsdk/camera/PhotoRequestTest.kt +23 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/photoreceiver/LocalPhotoReceiverRegistryTest.java +75 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/utils/AvifExifStripperTest.java +30 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/utils/BlePhotoUploadServiceTest.java +98 -0
- package/android/src/test/resources/avif_with_exif.avif +0 -0
- package/build/BluetoothSdk.types.d.ts +241 -43
- package/build/BluetoothSdk.types.d.ts.map +1 -1
- package/build/BluetoothSdk.types.js +11 -10
- package/build/BluetoothSdk.types.js.map +1 -1
- package/build/_private/BluetoothSdkModule.d.ts +36 -23
- package/build/_private/BluetoothSdkModule.d.ts.map +1 -1
- package/build/_private/BluetoothSdkModule.js +30 -52
- package/build/_private/BluetoothSdkModule.js.map +1 -1
- package/build/_private/photoRequestPayload.d.ts +4 -0
- package/build/_private/photoRequestPayload.d.ts.map +1 -0
- package/build/_private/photoRequestPayload.js +25 -0
- package/build/_private/photoRequestPayload.js.map +1 -0
- package/build/index.d.ts +1 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +23 -0
- package/build/index.js.map +1 -1
- package/build/react/useMentraBluetooth.d.ts +2 -2
- package/build/react/useMentraBluetooth.d.ts.map +1 -1
- package/build/react/useMentraBluetooth.js +3 -3
- package/build/react/useMentraBluetooth.js.map +1 -1
- package/ios/BluetoothSdkModule.swift +174 -70
- package/ios/LocalPhotoUploadServer.swift +370 -105
- package/ios/MentraBluetoothSDK.podspec +3 -1
- package/ios/MentraPhotoReceiverModule.swift +62 -10
- package/ios/Packages/CoreObjC/include/PcmConverter.h +22 -0
- package/ios/Packages/SherpaOnnx/SherpaOnnx.swift +668 -26
- package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/Info.plist +13 -9
- package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64/Headers/sherpa-onnx/c-api/c-api.h +4551 -0
- package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64/Headers/sherpa-onnx/c-api/cxx-api.h +1745 -0
- package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64/libsherpa-onnx.a +0 -0
- package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64_x86_64-simulator/Headers/sherpa-onnx/c-api/c-api.h +4551 -0
- package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64_x86_64-simulator/Headers/sherpa-onnx/c-api/cxx-api.h +1745 -0
- package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64_x86_64-simulator/libsherpa-onnx.a +0 -0
- package/ios/Source/BluetoothSdkDefaults.swift +6 -0
- package/ios/Source/Bridge.swift +58 -19
- package/ios/Source/DeviceManager.swift +149 -17
- package/ios/Source/DeviceStore.swift +16 -6
- package/ios/Source/MentraBluetoothSDK.swift +803 -47
- package/ios/Source/ObservableStore.swift +8 -2
- package/ios/Source/{Audio → audio}/AudioModels.swift +9 -7
- package/ios/Source/{Camera → camera}/CameraModels.swift +225 -16
- package/ios/Source/controllers/ControllerManager.swift +4 -3
- package/ios/Source/controllers/R1.swift +3 -3
- package/ios/Source/{Events → events}/BluetoothEvents.swift +183 -1
- package/ios/Source/{Internal → internal}/ValueParsing.swift +10 -1
- package/ios/Source/{Requests → requests}/DisplayRequests.swift +28 -4
- package/ios/Source/services/PhoneMic.swift +1 -0
- package/ios/Source/sgcs/G1.swift +2 -2
- package/ios/Source/sgcs/G2.swift +645 -403
- package/ios/Source/sgcs/Mach1.swift +2 -2
- package/ios/Source/sgcs/MentraLive.swift +812 -319
- package/ios/Source/sgcs/MentraNex.swift +412 -182
- package/ios/Source/sgcs/SGCManager.swift +57 -3
- package/ios/Source/sgcs/Simulated.swift +7 -3
- package/ios/Source/sgcs/mentraos_ble.pb.swift +3073 -3134
- package/ios/Source/status/DeviceStatus.swift +974 -0
- package/ios/Source/{Status → status}/WifiHotspotStatus.swift +4 -4
- package/ios/Source/{Streaming → streaming}/StreamModels.swift +24 -9
- package/ios/Source/stt/STTTools.swift +27 -12
- package/ios/Source/stt/SherpaOnnxTranscriber.swift +31 -8
- package/ios/Source/tts/TTSTools.swift +164 -0
- package/ios/Source/utils/JSCExperiment.swift +7 -7
- package/ios/Source/utils/MessageChunkReassembler.swift +20 -1
- package/ios/Source/utils/MessageChunker.swift +78 -21
- package/ios/Source/utils/TarBz2Extractor.swift +53 -2
- package/package.json +1 -1
- package/plugin/build/withAndroid.js +68 -4
- package/src/BluetoothSdk.types.ts +331 -59
- package/src/_private/BluetoothSdkModule.ts +113 -102
- package/src/_private/photoRequestPayload.ts +28 -0
- package/src/index.ts +58 -15
- package/src/react/useMentraBluetooth.ts +6 -4
- package/android/.project +0 -28
- package/android/lc3Lib/.project +0 -28
- package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/Headers/cargs.h +0 -162
- package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/Headers/sherpa-onnx/c-api/c-api.h +0 -1852
- package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/Headers/sherpa-onnx/c-api/cxx-api.h +0 -674
- package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64/sherpa-onnx.a +0 -0
- package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64_x86_64-simulator/sherpa-onnx.a +0 -0
- package/ios/Source/Status/DeviceStatus.swift +0 -471
- /package/ios/Source/{Connection → connection}/ScanSession.swift +0 -0
- /package/ios/Source/{Errors → errors}/BluetoothError.swift +0 -0
- /package/ios/Source/{Internal → internal}/BluetoothAvailability.swift +0 -0
- /package/ios/Source/{Status → status}/RuntimeState.swift +0 -0
- /package/ios/Source/{Types → types}/DeviceModels.swift +0 -0
|
@@ -113,6 +113,14 @@ class BluetoothSdkModule : Module() {
|
|
|
113
113
|
sendEvent("photo_status", event.values)
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
+
override fun onVideoRecordingStatus(event: VideoRecordingStatusEvent) {
|
|
117
|
+
sendEvent("video_recording_status", event.values)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
override fun onRgbLedControlResponse(event: RgbLedControlResponseEvent) {
|
|
121
|
+
sendEvent("rgb_led_control_response", event.values)
|
|
122
|
+
}
|
|
123
|
+
|
|
116
124
|
override fun onStreamStatus(event: StreamStatusEvent) {
|
|
117
125
|
sendEvent("stream_status", event.values)
|
|
118
126
|
}
|
|
@@ -121,6 +129,26 @@ class BluetoothSdkModule : Module() {
|
|
|
121
129
|
sendEvent("keep_alive_ack", event.values)
|
|
122
130
|
}
|
|
123
131
|
|
|
132
|
+
override fun onOtaUpdateAvailable(event: OtaUpdateAvailableEvent) {
|
|
133
|
+
sendEvent("ota_update_available", event.values)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
override fun onOtaStartAck(event: OtaStartAckEvent) {
|
|
137
|
+
sendEvent("ota_start_ack", event.values)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
override fun onOtaStatus(event: OtaStatusEvent) {
|
|
141
|
+
sendEvent("ota_status", event.values)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
override fun onSettingsAck(event: SettingsAckEvent) {
|
|
145
|
+
sendEvent("settings_ack", event.values)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
override fun onVersionInfo(event: VersionInfoResult) {
|
|
149
|
+
sendEvent("version_info", event.toMap() + mapOf("type" to "version_info"))
|
|
150
|
+
}
|
|
151
|
+
|
|
124
152
|
override fun onMicPcm(event: MicPcmEvent) {
|
|
125
153
|
sendEvent("mic_pcm", event.toMap())
|
|
126
154
|
}
|
|
@@ -144,7 +172,14 @@ class BluetoothSdkModule : Module() {
|
|
|
144
172
|
override fun onRawEvent(eventName: String, values: Map<String, Any>) {
|
|
145
173
|
sendEvent(eventName, values)
|
|
146
174
|
}
|
|
147
|
-
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
private fun requireSdk(): MentraBluetoothSdk =
|
|
178
|
+
sdk
|
|
179
|
+
?: throw BluetoothException(
|
|
180
|
+
"sdk_not_initialized",
|
|
181
|
+
"Bluetooth SDK is not initialized.",
|
|
182
|
+
)
|
|
148
183
|
|
|
149
184
|
override fun definition() = ModuleDefinition {
|
|
150
185
|
Name("BluetoothSdk")
|
|
@@ -166,10 +201,12 @@ class BluetoothSdkModule : Module() {
|
|
|
166
201
|
"battery_status",
|
|
167
202
|
"local_transcription",
|
|
168
203
|
"wifi_status_change",
|
|
204
|
+
"wifi_scan_result",
|
|
169
205
|
"hotspot_status_change",
|
|
170
206
|
"hotspot_error",
|
|
171
207
|
"photo_response",
|
|
172
208
|
"photo_status",
|
|
209
|
+
"video_recording_status",
|
|
173
210
|
"gallery_status",
|
|
174
211
|
"compatible_glasses_search_stop",
|
|
175
212
|
"heartbeat_sent",
|
|
@@ -179,6 +216,8 @@ class BluetoothSdkModule : Module() {
|
|
|
179
216
|
"swipe_volume_status",
|
|
180
217
|
"switch_status",
|
|
181
218
|
"rgb_led_control_response",
|
|
219
|
+
"settings_ack",
|
|
220
|
+
"version_info",
|
|
182
221
|
"pair_failure",
|
|
183
222
|
"audio_pairing_needed",
|
|
184
223
|
"audio_connected",
|
|
@@ -202,6 +241,7 @@ class BluetoothSdkModule : Module() {
|
|
|
202
241
|
"receive_command_from_ble",
|
|
203
242
|
"miniapp_selected",
|
|
204
243
|
"captions_tester_incident",
|
|
244
|
+
"extraction_progress",
|
|
205
245
|
)
|
|
206
246
|
|
|
207
247
|
OnCreate {
|
|
@@ -356,29 +396,58 @@ class BluetoothSdkModule : Module() {
|
|
|
356
396
|
|
|
357
397
|
// MARK: - WiFi Commands
|
|
358
398
|
|
|
359
|
-
AsyncFunction("requestWifiScan") {
|
|
399
|
+
AsyncFunction("requestWifiScan") { requireSdk().requestWifiScan().map { it.toMap() } }
|
|
360
400
|
|
|
361
401
|
AsyncFunction("sendWifiCredentials") { ssid: String, password: String ->
|
|
362
|
-
|
|
402
|
+
requireSdk().sendWifiCredentials(ssid, password).values
|
|
363
403
|
}
|
|
364
404
|
|
|
365
|
-
AsyncFunction("forgetWifiNetwork") { ssid: String ->
|
|
405
|
+
AsyncFunction("forgetWifiNetwork") { ssid: String -> requireSdk().forgetWifiNetwork(ssid).values }
|
|
366
406
|
|
|
367
407
|
AsyncFunction("setHotspotState") { enabled: Boolean ->
|
|
368
|
-
|
|
408
|
+
requireSdk().setHotspotState(enabled).values
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
AsyncFunction("setSystemTime") { timestampMs: Double ->
|
|
412
|
+
sdk?.setSystemTime(timestampMs.toLong())
|
|
369
413
|
}
|
|
370
414
|
|
|
371
415
|
// MARK: - Gallery Commands
|
|
372
416
|
|
|
373
417
|
AsyncFunction("setGalleryModeEnabled") { enabled: Boolean ->
|
|
374
|
-
|
|
418
|
+
requireSdk().setGalleryModeEnabled(enabled).values
|
|
375
419
|
}
|
|
376
420
|
|
|
377
421
|
AsyncFunction("setVoiceActivityDetectionEnabled") { enabled: Boolean ->
|
|
378
422
|
sdk?.setVoiceActivityDetectionEnabled(enabled)
|
|
379
423
|
}
|
|
380
424
|
|
|
381
|
-
AsyncFunction("
|
|
425
|
+
AsyncFunction("setButtonPhotoSettings") { size: String ->
|
|
426
|
+
requireSdk().setButtonPhotoSettings(ButtonPhotoSize.fromValue(size)).values
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
AsyncFunction("setButtonVideoRecordingSettings") { width: Int, height: Int, fps: Int ->
|
|
430
|
+
requireSdk().setButtonVideoRecordingSettings(width, height, fps).values
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
AsyncFunction("setButtonCameraLed") { enabled: Boolean ->
|
|
434
|
+
requireSdk().setButtonCameraLed(enabled).values
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
AsyncFunction("setButtonMaxRecordingTime") { minutes: Int ->
|
|
438
|
+
requireSdk().setButtonMaxRecordingTime(minutes).values
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
AsyncFunction("setCameraFov") { fov: Map<String, Any> ->
|
|
442
|
+
val value = (fov["fov"] as? Number)?.toInt() ?: CameraFov.DEFAULT_FOV
|
|
443
|
+
val roiPosition = CameraRoiPosition.fromValue(
|
|
444
|
+
(fov["roiPosition"] as? Number)?.toInt()
|
|
445
|
+
?: (fov["roi_position"] as? Number)?.toInt(),
|
|
446
|
+
)
|
|
447
|
+
requireSdk().setCameraFov(CameraFov(value, roiPosition)).values
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
AsyncFunction("queryGalleryStatus") { requireSdk().queryGalleryStatus().values }
|
|
382
451
|
|
|
383
452
|
AsyncFunction("requestPhoto") { params: Map<String, Any?> ->
|
|
384
453
|
// JS may pass null for optional fields; Map<String, Any> rejects null values at the bridge.
|
|
@@ -390,25 +459,20 @@ class BluetoothSdkModule : Module() {
|
|
|
390
459
|
Bridge.log(
|
|
391
460
|
"NATIVE: PHOTO PIPELINE [3/6] BluetoothSdk.requestPhoto requestId=${req.requestId} appId=${req.appId} size=${req.size} compress=${req.compress} flash=${req.flash} sound=${req.sound} exposureTimeNs=${req.exposureTimeNs} iso=${req.iso}"
|
|
392
461
|
)
|
|
393
|
-
|
|
394
|
-
if (activeSdk == null) {
|
|
395
|
-
Bridge.log(
|
|
396
|
-
"NATIVE: PHOTO PIPELINE — sdk is null; requestPhoto dropped requestId=${req.requestId}"
|
|
397
|
-
)
|
|
398
|
-
} else {
|
|
399
|
-
activeSdk.requestPhoto(req)
|
|
400
|
-
}
|
|
462
|
+
requireSdk().requestPhoto(req).values
|
|
401
463
|
}
|
|
402
464
|
|
|
403
465
|
// MARK: - OTA Commands
|
|
404
466
|
|
|
405
|
-
AsyncFunction("sendOtaStart") {
|
|
467
|
+
AsyncFunction("sendOtaStart") { requireSdk().sendOtaStart().values }
|
|
468
|
+
|
|
469
|
+
AsyncFunction("sendOtaQueryStatus") { requireSdk().sendOtaQueryStatus().values }
|
|
406
470
|
|
|
407
|
-
AsyncFunction("
|
|
471
|
+
AsyncFunction("retryOtaVersionCheck") { requireSdk().retryOtaVersionCheck().values }
|
|
408
472
|
|
|
409
473
|
// MARK: - Version Info Commands
|
|
410
474
|
|
|
411
|
-
AsyncFunction("requestVersionInfo") {
|
|
475
|
+
AsyncFunction("requestVersionInfo") { requireSdk().requestVersionInfo().toMap() }
|
|
412
476
|
|
|
413
477
|
// MARK: - Power Control Commands
|
|
414
478
|
|
|
@@ -418,24 +482,41 @@ class BluetoothSdkModule : Module() {
|
|
|
418
482
|
|
|
419
483
|
// MARK: - Video Recording Commands
|
|
420
484
|
|
|
421
|
-
AsyncFunction("startVideoRecording") {
|
|
422
|
-
|
|
485
|
+
AsyncFunction("startVideoRecording") {
|
|
486
|
+
requestId: String,
|
|
487
|
+
save: Boolean,
|
|
488
|
+
sound: Boolean,
|
|
489
|
+
settings: Map<String, Any?>? ->
|
|
490
|
+
// Optional per-recording {width,height,fps}. Absent fields stay 0, which
|
|
491
|
+
// the glasses treat as "use the saved button-video default". JS numbers
|
|
492
|
+
// arrive as Double across the bridge, so coerce to Int.
|
|
493
|
+
fun dim(key: String): Int = (settings?.get(key) as? Number)?.toInt() ?: 0
|
|
494
|
+
requireSdk().startVideoRecording(
|
|
495
|
+
VideoRecordingRequest(
|
|
496
|
+
requestId,
|
|
497
|
+
save,
|
|
498
|
+
sound,
|
|
499
|
+
dim("width"),
|
|
500
|
+
dim("height"),
|
|
501
|
+
dim("fps"),
|
|
502
|
+
)
|
|
503
|
+
).values
|
|
423
504
|
}
|
|
424
505
|
|
|
425
506
|
AsyncFunction("stopVideoRecording") { requestId: String ->
|
|
426
|
-
|
|
507
|
+
requireSdk().stopVideoRecording(requestId).values
|
|
427
508
|
}
|
|
428
509
|
|
|
429
510
|
// MARK: - Stream Commands
|
|
430
511
|
|
|
431
512
|
AsyncFunction("startStream") { params: Map<String, Any> ->
|
|
432
|
-
|
|
513
|
+
requireSdk().startStream(StreamRequest.fromMap(params)).values
|
|
433
514
|
}
|
|
434
515
|
|
|
435
|
-
AsyncFunction("stopStream") {
|
|
516
|
+
AsyncFunction("stopStream") { requireSdk().stopStream().values }
|
|
436
517
|
|
|
437
|
-
AsyncFunction("
|
|
438
|
-
sdk?.
|
|
518
|
+
AsyncFunction("sendExternallyManagedStreamKeepAlive") { params: Map<String, Any> ->
|
|
519
|
+
sdk?.sendExternallyManagedStreamKeepAlive(StreamKeepAliveRequest.fromMap(params))
|
|
439
520
|
}
|
|
440
521
|
|
|
441
522
|
// MARK: - Microphone Commands
|
|
@@ -481,7 +562,7 @@ class BluetoothSdkModule : Module() {
|
|
|
481
562
|
onDurationMs: Int,
|
|
482
563
|
offDurationMs: Int,
|
|
483
564
|
count: Int ->
|
|
484
|
-
|
|
565
|
+
requireSdk().rgbLedControl(
|
|
485
566
|
RgbLedRequest(
|
|
486
567
|
requestId = requestId,
|
|
487
568
|
packageName = packageName,
|
|
@@ -491,7 +572,7 @@ class BluetoothSdkModule : Module() {
|
|
|
491
572
|
offDurationMs = offDurationMs,
|
|
492
573
|
count = count,
|
|
493
574
|
)
|
|
494
|
-
)
|
|
575
|
+
).values
|
|
495
576
|
}
|
|
496
577
|
|
|
497
578
|
// MARK: - STT Commands
|
|
@@ -528,6 +609,63 @@ class BluetoothSdkModule : Module() {
|
|
|
528
609
|
com.mentra.bluetoothsdk.stt.STTTools.extractTarBz2(sourcePath, destinationPath)
|
|
529
610
|
}
|
|
530
611
|
|
|
612
|
+
// MARK: - TTS Commands
|
|
613
|
+
|
|
614
|
+
AsyncFunction("setTtsModelDetails") { path: String, languageCode: String ->
|
|
615
|
+
val context =
|
|
616
|
+
appContext.reactContext
|
|
617
|
+
?: appContext.currentActivity
|
|
618
|
+
?: throw IllegalStateException("No context available")
|
|
619
|
+
com.mentra.core.tts.TTSTools.setTtsModelDetails(context, path, languageCode)
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
AsyncFunction("getTtsModelPath") { ->
|
|
623
|
+
val context =
|
|
624
|
+
appContext.reactContext
|
|
625
|
+
?: appContext.currentActivity
|
|
626
|
+
?: throw IllegalStateException("No context available")
|
|
627
|
+
com.mentra.core.tts.TTSTools.getTtsModelPath(context)
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
AsyncFunction("getTtsModelLanguage") { ->
|
|
631
|
+
val context =
|
|
632
|
+
appContext.reactContext
|
|
633
|
+
?: appContext.currentActivity
|
|
634
|
+
?: throw IllegalStateException("No context available")
|
|
635
|
+
com.mentra.core.tts.TTSTools.getTtsModelLanguage(context)
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
AsyncFunction("checkTtsModelAvailable") { ->
|
|
639
|
+
val context =
|
|
640
|
+
appContext.reactContext
|
|
641
|
+
?: appContext.currentActivity
|
|
642
|
+
?: throw IllegalStateException("No context available")
|
|
643
|
+
com.mentra.core.tts.TTSTools.checkTTSModelAvailable(context)
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
AsyncFunction("validateTtsModel") { path: String ->
|
|
647
|
+
com.mentra.core.tts.TTSTools.validateTTSModel(path)
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
AsyncFunction("generateTtsAudio") {
|
|
651
|
+
text: String,
|
|
652
|
+
modelPath: String,
|
|
653
|
+
outputPath: String,
|
|
654
|
+
speakerId: Int,
|
|
655
|
+
speed: Double ->
|
|
656
|
+
val context =
|
|
657
|
+
appContext.reactContext
|
|
658
|
+
?: appContext.currentActivity
|
|
659
|
+
?: throw IllegalStateException("No context available")
|
|
660
|
+
com.mentra.core.tts.TTSTools.generateTtsAudio(
|
|
661
|
+
context,
|
|
662
|
+
text,
|
|
663
|
+
modelPath,
|
|
664
|
+
outputPath,
|
|
665
|
+
speakerId,
|
|
666
|
+
speed.toFloat()
|
|
667
|
+
)
|
|
668
|
+
}
|
|
531
669
|
}
|
|
532
670
|
}
|
|
533
671
|
|
|
@@ -107,6 +107,16 @@ public class Bridge private constructor() {
|
|
|
107
107
|
sendTypedMessage("log", data as Map<String, Any>)
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
+
/** Report tar.bz2 extraction progress to JavaScript. */
|
|
111
|
+
@JvmStatic
|
|
112
|
+
fun sendExtractionProgress(percentage: Int, bytesRead: Long, totalBytes: Long) {
|
|
113
|
+
val data = HashMap<String, Any>()
|
|
114
|
+
data["percentage"] = percentage
|
|
115
|
+
data["bytesRead"] = bytesRead
|
|
116
|
+
data["totalBytes"] = totalBytes
|
|
117
|
+
sendTypedMessage("extraction_progress", data as Map<String, Any>)
|
|
118
|
+
}
|
|
119
|
+
|
|
110
120
|
/** Send head position event */
|
|
111
121
|
@JvmStatic
|
|
112
122
|
fun sendHeadUp(isUp: Boolean) {
|
|
@@ -153,7 +163,7 @@ public class Bridge private constructor() {
|
|
|
153
163
|
private fun micPcmEventBody(data: ByteArray): HashMap<String, Any> {
|
|
154
164
|
val voiceActivityDetectionEnabled =
|
|
155
165
|
DeviceStore.get("glasses", "voiceActivityDetectionEnabled") as? Boolean
|
|
156
|
-
?:
|
|
166
|
+
?: BluetoothSdkDefaults.VOICE_ACTIVITY_DETECTION_ENABLED
|
|
157
167
|
val body = HashMap<String, Any>()
|
|
158
168
|
body["pcm"] = data
|
|
159
169
|
body["sampleRate"] = MIC_SAMPLE_RATE
|
|
@@ -167,7 +177,7 @@ public class Bridge private constructor() {
|
|
|
167
177
|
private fun micLc3EventBody(data: ByteArray): HashMap<String, Any> {
|
|
168
178
|
val voiceActivityDetectionEnabled =
|
|
169
179
|
DeviceStore.get("glasses", "voiceActivityDetectionEnabled") as? Boolean
|
|
170
|
-
?:
|
|
180
|
+
?: BluetoothSdkDefaults.VOICE_ACTIVITY_DETECTION_ENABLED
|
|
171
181
|
val frameSizeBytes =
|
|
172
182
|
(DeviceStore.store.get("bluetooth", "lc3_frame_size") as? Number)?.toInt()
|
|
173
183
|
?: DEFAULT_LC3_FRAME_SIZE_BYTES
|
|
@@ -357,15 +367,6 @@ public class Bridge private constructor() {
|
|
|
357
367
|
event["errorMessage"] = errorMessage
|
|
358
368
|
event["timestamp"] = timestamp
|
|
359
369
|
sendTypedMessage("photo_response", event as Map<String, Any>)
|
|
360
|
-
|
|
361
|
-
val status = HashMap<String, Any>()
|
|
362
|
-
status["type"] = "photo_status"
|
|
363
|
-
status["status"] = "failed"
|
|
364
|
-
status["requestId"] = requestId
|
|
365
|
-
status["errorCode"] = errorCode
|
|
366
|
-
status["errorMessage"] = errorMessage
|
|
367
|
-
status["timestamp"] = timestamp
|
|
368
|
-
sendTypedMessage("photo_status", status as Map<String, Any>)
|
|
369
370
|
}
|
|
370
371
|
|
|
371
372
|
@JvmStatic
|
|
@@ -373,6 +374,11 @@ public class Bridge private constructor() {
|
|
|
373
374
|
sendTypedMessage("photo_status", statusJson)
|
|
374
375
|
}
|
|
375
376
|
|
|
377
|
+
@JvmStatic
|
|
378
|
+
fun sendPhotoResponse(responseJson: Map<String, Any>) {
|
|
379
|
+
sendTypedMessage("photo_response", responseJson)
|
|
380
|
+
}
|
|
381
|
+
|
|
376
382
|
/** Send RGB LED control response */
|
|
377
383
|
@JvmStatic
|
|
378
384
|
fun sendRgbLedControlResponse(requestId: String, success: Boolean, error: String?) {
|
|
@@ -391,6 +397,45 @@ public class Bridge private constructor() {
|
|
|
391
397
|
}
|
|
392
398
|
}
|
|
393
399
|
|
|
400
|
+
@JvmStatic
|
|
401
|
+
fun sendSettingsAck(values: Map<String, Any>) {
|
|
402
|
+
val body = HashMap<String, Any>()
|
|
403
|
+
body["type"] = "settings_ack"
|
|
404
|
+
values.forEach { (key, value) ->
|
|
405
|
+
body[key] = value
|
|
406
|
+
}
|
|
407
|
+
sendTypedMessage("settings_ack", body)
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
@JvmStatic
|
|
411
|
+
fun sendVideoRecordingStatus(values: Map<String, Any>) {
|
|
412
|
+
val body = HashMap<String, Any>()
|
|
413
|
+
body["type"] = "video_recording_status"
|
|
414
|
+
values.forEach { (key, value) ->
|
|
415
|
+
body[key] = value
|
|
416
|
+
}
|
|
417
|
+
sendTypedMessage("video_recording_status", body)
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
@JvmStatic
|
|
421
|
+
fun sendVersionInfo(values: Map<String, Any>) {
|
|
422
|
+
fun stringField(vararg keys: String): String =
|
|
423
|
+
keys.firstNotNullOfOrNull { key -> values[key] as? String } ?: ""
|
|
424
|
+
val body = HashMap<String, Any>()
|
|
425
|
+
body["type"] = "version_info"
|
|
426
|
+
body["androidVersion"] = stringField("androidVersion", "android_version")
|
|
427
|
+
body["firmwareVersion"] = stringField("firmwareVersion", "firmware_version")
|
|
428
|
+
body["besFirmwareVersion"] = stringField("besFirmwareVersion", "bes_fw_version")
|
|
429
|
+
body["mtkFirmwareVersion"] = stringField("mtkFirmwareVersion", "mtk_fw_version")
|
|
430
|
+
body["buildNumber"] = stringField("buildNumber", "build_number")
|
|
431
|
+
(values["systemTimeMs"] as? Number ?: values["system_time_ms"] as? Number)?.let {
|
|
432
|
+
body["systemTimeMs"] = it.toLong()
|
|
433
|
+
}
|
|
434
|
+
body["otaVersionUrl"] = stringField("otaVersionUrl", "ota_version_url")
|
|
435
|
+
body["appVersion"] = stringField("appVersion", "app_version")
|
|
436
|
+
sendTypedMessage("version_info", body)
|
|
437
|
+
}
|
|
438
|
+
|
|
394
439
|
/**
|
|
395
440
|
* Send transcription result to server Used by AOSManager to send pre-formatted
|
|
396
441
|
* transcription results Matches the Swift structure exactly
|
|
@@ -457,7 +502,7 @@ public class Bridge private constructor() {
|
|
|
457
502
|
|
|
458
503
|
/** Send WiFi scan results */
|
|
459
504
|
@JvmStatic
|
|
460
|
-
fun updateWifiScanResults(networks: List<Map<String, Any
|
|
505
|
+
fun updateWifiScanResults(networks: List<Map<String, Any>>, scanComplete: Boolean) {
|
|
461
506
|
var storedNetworks: List<Map<String, Any>> =
|
|
462
507
|
DeviceStore.get("bluetooth", "wifiScanResults") as? List<Map<String, Any>>
|
|
463
508
|
?: emptyList()
|
|
@@ -469,6 +514,10 @@ public class Bridge private constructor() {
|
|
|
469
514
|
}
|
|
470
515
|
}
|
|
471
516
|
DeviceStore.apply("bluetooth", "wifiScanResults", updatedNetworks)
|
|
517
|
+
val body = HashMap<String, Any>()
|
|
518
|
+
body["networks"] = updatedNetworks
|
|
519
|
+
body["scanComplete"] = scanComplete
|
|
520
|
+
sendTypedMessage("wifi_scan_result", body)
|
|
472
521
|
}
|
|
473
522
|
|
|
474
523
|
/** Send gallery status - matches iOS MentraLive.swift handleGalleryStatus pattern */
|
|
@@ -478,14 +527,21 @@ public class Bridge private constructor() {
|
|
|
478
527
|
videoCount: Int,
|
|
479
528
|
totalCount: Int,
|
|
480
529
|
totalSize: Long,
|
|
481
|
-
hasContent: Boolean
|
|
530
|
+
hasContent: Boolean,
|
|
531
|
+
cameraBusy: Boolean,
|
|
532
|
+
cameraBusyReason: String?
|
|
482
533
|
) {
|
|
483
534
|
val galleryData = HashMap<String, Any>()
|
|
535
|
+
galleryData["type"] = "gallery_status"
|
|
484
536
|
galleryData["photos"] = photoCount
|
|
485
537
|
galleryData["videos"] = videoCount
|
|
486
538
|
galleryData["total"] = totalCount
|
|
487
539
|
galleryData["totalSize"] = totalSize
|
|
488
540
|
galleryData["hasContent"] = hasContent
|
|
541
|
+
galleryData["cameraBusy"] = cameraBusy
|
|
542
|
+
if (!cameraBusyReason.isNullOrBlank()) {
|
|
543
|
+
galleryData["cameraBusyReason"] = cameraBusyReason
|
|
544
|
+
}
|
|
489
545
|
|
|
490
546
|
sendTypedMessage("gallery_status", galleryData as Map<String, Any>)
|
|
491
547
|
}
|
|
@@ -550,6 +606,7 @@ public class Bridge private constructor() {
|
|
|
550
606
|
}
|
|
551
607
|
|
|
552
608
|
@JvmStatic
|
|
609
|
+
@JvmOverloads
|
|
553
610
|
fun sendOtaStatus(
|
|
554
611
|
sessionId: String,
|
|
555
612
|
totalSteps: Int,
|
|
@@ -559,7 +616,8 @@ public class Bridge private constructor() {
|
|
|
559
616
|
stepPercent: Int,
|
|
560
617
|
overallPercent: Int,
|
|
561
618
|
status: String,
|
|
562
|
-
errorMessage: String?
|
|
619
|
+
errorMessage: String? = null,
|
|
620
|
+
glassesTimeMs: Long? = null,
|
|
563
621
|
) {
|
|
564
622
|
val eventBody = HashMap<String, Any>()
|
|
565
623
|
eventBody["session_id"] = sessionId
|
|
@@ -571,6 +629,9 @@ public class Bridge private constructor() {
|
|
|
571
629
|
eventBody["overall_percent"] = overallPercent
|
|
572
630
|
eventBody["status"] = status
|
|
573
631
|
errorMessage?.let { eventBody["error_message"] = it }
|
|
632
|
+
if (glassesTimeMs != null && glassesTimeMs > 0) {
|
|
633
|
+
eventBody["glasses_time_ms"] = glassesTimeMs
|
|
634
|
+
}
|
|
574
635
|
|
|
575
636
|
Log.d(TAG, "Bridge: sendOtaStatus: $eventBody")
|
|
576
637
|
|
|
@@ -661,13 +722,17 @@ public class Bridge private constructor() {
|
|
|
661
722
|
return
|
|
662
723
|
}
|
|
663
724
|
|
|
664
|
-
if (type
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
725
|
+
if (shouldTraceTypedMessage(type)) {
|
|
726
|
+
try {
|
|
727
|
+
BleTraceLogger.logMap(
|
|
728
|
+
"phone_to_app",
|
|
729
|
+
"sdk_event_dispatch",
|
|
730
|
+
type,
|
|
731
|
+
mutableBody as Map<String, Any>,
|
|
732
|
+
)
|
|
733
|
+
} catch (e: Exception) {
|
|
734
|
+
Log.d(TAG, "BLE trace logging failed for typed message '$type'", e)
|
|
735
|
+
}
|
|
671
736
|
}
|
|
672
737
|
|
|
673
738
|
// Send directly using type as event name - no JSON serialization
|
|
@@ -687,6 +752,9 @@ public class Bridge private constructor() {
|
|
|
687
752
|
Log.e(TAG, "Error sending typed message of type '$type'", e)
|
|
688
753
|
}
|
|
689
754
|
}
|
|
755
|
+
|
|
756
|
+
private fun shouldTraceTypedMessage(type: String): Boolean =
|
|
757
|
+
type != "log" && type != "mic_pcm" && type != "mic_lc3"
|
|
690
758
|
}
|
|
691
759
|
|
|
692
760
|
init {
|