@mentra/bluetooth-sdk 0.1.13 → 0.1.14
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 +15 -3
- package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkModule.kt +27 -40
- package/android/src/main/java/com/mentra/bluetoothsdk/DeviceManager.kt +7 -13
- package/android/src/main/java/com/mentra/bluetoothsdk/DeviceStore.kt +0 -4
- package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothSdk.kt +19 -20
- package/android/src/main/java/com/mentra/bluetoothsdk/camera/CameraModels.kt +3 -5
- package/android/src/main/java/com/mentra/bluetoothsdk/controllers/ControllerManager.kt +1 -2
- package/android/src/main/java/com/mentra/bluetoothsdk/controllers/R1.kt +1 -2
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G1.kt +1 -5
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G2.kt +0 -5
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Mach1.kt +1 -5
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraLive.kt +1 -39
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraNex.kt +1 -2
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/SGCManager.kt +2 -4
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Simulated.kt +3 -7
- package/android/src/main/java/com/mentra/bluetoothsdk/status/DeviceStatus.kt +0 -6
- package/android/src/main/java/com/mentra/bluetoothsdk/streaming/StreamModels.kt +16 -33
- package/build/BluetoothSdk.types.d.ts +12 -11
- package/build/BluetoothSdk.types.d.ts.map +1 -1
- package/build/BluetoothSdk.types.js.map +1 -1
- package/build/_private/BluetoothSdkModule.d.ts +4 -6
- package/build/_private/BluetoothSdkModule.d.ts.map +1 -1
- package/build/_private/BluetoothSdkModule.js +0 -4
- package/build/_private/BluetoothSdkModule.js.map +1 -1
- package/build/_private/photoRequestPayload.d.ts.map +1 -1
- package/build/_private/photoRequestPayload.js +3 -1
- package/build/_private/photoRequestPayload.js.map +1 -1
- package/build/index.d.ts +1 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +3 -12
- package/build/index.js.map +1 -1
- package/ios/BluetoothSdkModule.swift +11 -16
- package/ios/Source/DeviceManager.swift +5 -11
- package/ios/Source/DeviceStore.swift +0 -4
- package/ios/Source/MentraBluetoothSDK.swift +24 -28
- package/ios/Source/camera/CameraModels.swift +7 -11
- package/ios/Source/controllers/ControllerManager.swift +1 -2
- package/ios/Source/controllers/R1.swift +1 -2
- package/ios/Source/sgcs/Frame.swift +0 -2
- package/ios/Source/sgcs/G1.swift +1 -3
- package/ios/Source/sgcs/G2.swift +1 -2
- package/ios/Source/sgcs/Mach1.swift +1 -3
- package/ios/Source/sgcs/MentraLive.swift +12 -39
- package/ios/Source/sgcs/MentraNex.swift +1 -3
- package/ios/Source/sgcs/SGCManager.swift +4 -5
- package/ios/Source/sgcs/Simulated.swift +2 -6
- package/ios/Source/status/DeviceStatus.swift +0 -8
- package/ios/Source/streaming/StreamModels.swift +7 -32
- package/package.json +1 -1
- package/src/BluetoothSdk.types.ts +12 -11
- package/src/_private/BluetoothSdkModule.ts +4 -11
- package/src/_private/photoRequestPayload.ts +3 -1
- package/src/index.ts +7 -15
package/README.md
CHANGED
|
@@ -405,8 +405,8 @@ await BluetoothSdk.startStream({
|
|
|
405
405
|
await BluetoothSdk.stopStream()
|
|
406
406
|
```
|
|
407
407
|
|
|
408
|
-
Use `rtmp://` or `rtmps://` for RTMP, `srt://` for SRT, and `http://` or `https://` for WHIP/WebRTC ingest. `startStream()` resolves with the correlated `stream_status` event once the glasses report `status: "streaming"`; `stopStream()` resolves when the glasses report `status: "stopped"` or confirms the stream was already stopped / not streaming. `stopStream()` returns a normalized stopped event for that already-stopped case. Stream starts reject if the glasses report an error before streaming; stream stops reject for real stop errors, send failure, another stop in flight, or timeout. The SDK sends stream keep-alives automatically while streaming and reports keep-alive failures through `stream_status`. The camera light is always enabled while streaming.
|
|
409
|
-
`stream_status` events may include `resolvedConfig`, which reports the effective transport, video, and audio settings after glasses defaults, clamps, and camera preflight.
|
|
408
|
+
Use `rtmp://` or `rtmps://` for RTMP, `srt://` for SRT, and `http://` or `https://` for WHIP/WebRTC ingest. `startStream()` resolves with the correlated `stream_status` event once the glasses report `status: "streaming"`; `stopStream()` resolves when the glasses report `status: "stopped"` or confirms the stream was already stopped / not streaming. `stopStream()` returns a normalized stopped event for that already-stopped case. Stream starts reject if the glasses report an error before streaming; stream stops reject for real stop errors, send failure, another stop in flight, or timeout. Stream video input fields are `width`, `height`, `bitrate`, and `fps`. The SDK sends stream keep-alives automatically while streaming and reports keep-alive failures through `stream_status`. The camera light is always enabled while streaming.
|
|
409
|
+
`stream_status` events may include `resolvedConfig`, which reports the effective transport, video, and audio settings after glasses defaults, clamps, and camera preflight. The resolved effective frame rate is reported as `resolvedConfig.video.fps`.
|
|
410
410
|
|
|
411
411
|
## Events
|
|
412
412
|
|
|
@@ -459,13 +459,25 @@ MENTRA_BLUETOOTH_SDK_PACKAGE_PATH=/path/to/MentraOS/mobile/modules/bluetooth-sdk
|
|
|
459
459
|
|
|
460
460
|
Use `bunx expo run:android` for Android. Keep local paths in your shell or CI environment, not in committed app config.
|
|
461
461
|
|
|
462
|
+
For local Android source compile checks inside this monorepo, run from the
|
|
463
|
+
MentraOS repo root:
|
|
464
|
+
|
|
465
|
+
```sh
|
|
466
|
+
./scripts/check-android-compile.sh bluetooth-sdk
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
The `android/` folder in this package is source for the generated Expo Android
|
|
470
|
+
project, not the local Gradle entrypoint. The check script prepares
|
|
471
|
+
`mobile/android` and uses its Gradle wrapper with `-PmentraPublicSdk=true`,
|
|
472
|
+
matching the CI release workflow's public SDK dependency mode.
|
|
473
|
+
|
|
462
474
|
For bare native iOS apps, use the public SwiftPM repository:
|
|
463
475
|
|
|
464
476
|
```text
|
|
465
477
|
https://github.com/Mentra-Community/mentra-bluetooth-sdk-ios.git
|
|
466
478
|
```
|
|
467
479
|
|
|
468
|
-
Select version `0.1.
|
|
480
|
+
Select version `0.1.14`, then add the `MentraBluetoothSDK` product to your app target.
|
|
469
481
|
|
|
470
482
|
For local SDK development, add this package folder directly in Xcode:
|
|
471
483
|
|
|
@@ -441,49 +441,16 @@ class BluetoothSdkModule : Module() {
|
|
|
441
441
|
sdk?.setVoiceActivityDetectionEnabled(enabled)
|
|
442
442
|
}
|
|
443
443
|
|
|
444
|
-
AsyncFunction("
|
|
445
|
-
|
|
446
|
-
val mfnr = params["mfnr"] as? Boolean
|
|
447
|
-
val zsl = params["zsl"] as? Boolean
|
|
448
|
-
val noiseReduction = params["noiseReduction"] as? Boolean
|
|
449
|
-
val edgeEnhancement = params["edgeEnhancement"] as? Boolean
|
|
450
|
-
val ispDigitalGain = (params["ispDigitalGain"] as? Number)?.toInt()
|
|
451
|
-
val ispAnalogGain = params["ispAnalogGain"] as? String
|
|
452
|
-
val aeExposureDivisor = (params["aeExposureDivisor"] as? Number)?.toInt()
|
|
453
|
-
val isoCap = (params["isoCap"] as? Number)?.toInt()
|
|
454
|
-
val compress = params["compress"] as? String
|
|
455
|
-
val sound = params["sound"] as? Boolean
|
|
456
|
-
val resetCaptureTuning = params["resetCaptureTuning"] as? Boolean == true
|
|
457
|
-
requireSdk()
|
|
458
|
-
.setButtonPhotoSettings(
|
|
459
|
-
ButtonPhotoSettings(
|
|
460
|
-
size = size,
|
|
461
|
-
mfnr = mfnr,
|
|
462
|
-
zsl = zsl,
|
|
463
|
-
noiseReduction = noiseReduction,
|
|
464
|
-
edgeEnhancement = edgeEnhancement,
|
|
465
|
-
ispDigitalGain = ispDigitalGain,
|
|
466
|
-
ispAnalogGain = ispAnalogGain,
|
|
467
|
-
aeExposureDivisor = aeExposureDivisor,
|
|
468
|
-
isoCap = isoCap,
|
|
469
|
-
compress = compress,
|
|
470
|
-
sound = sound,
|
|
471
|
-
resetCaptureTuning = resetCaptureTuning,
|
|
472
|
-
),
|
|
473
|
-
)
|
|
474
|
-
.values
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
AsyncFunction("setButtonVideoRecordingSettings") { width: Int, height: Int, fps: Int ->
|
|
478
|
-
requireSdk().setButtonVideoRecordingSettings(width, height, fps).values
|
|
444
|
+
AsyncFunction("setPhotoCaptureDefaults") { params: Map<String, Any?> ->
|
|
445
|
+
requireSdk().setPhotoCaptureDefaults(params.toPhotoCaptureDefaults()).values
|
|
479
446
|
}
|
|
480
447
|
|
|
481
|
-
AsyncFunction("
|
|
482
|
-
requireSdk().
|
|
448
|
+
AsyncFunction("setVideoRecordingDefaults") { width: Int, height: Int, fps: Int ->
|
|
449
|
+
requireSdk().setVideoRecordingDefaults(VideoRecordingDefaults(width, height, fps)).values
|
|
483
450
|
}
|
|
484
451
|
|
|
485
|
-
AsyncFunction("
|
|
486
|
-
requireSdk().
|
|
452
|
+
AsyncFunction("setMaxVideoRecordingDuration") { minutes: Int ->
|
|
453
|
+
requireSdk().setMaxVideoRecordingDuration(minutes).values
|
|
487
454
|
}
|
|
488
455
|
|
|
489
456
|
AsyncFunction("setCameraFov") { fov: Map<String, Any> ->
|
|
@@ -505,7 +472,7 @@ class BluetoothSdkModule : Module() {
|
|
|
505
472
|
}.toMap()
|
|
506
473
|
val req = PhotoRequest.fromMap(sanitized)
|
|
507
474
|
Bridge.log(
|
|
508
|
-
"NATIVE: PHOTO PIPELINE [3/6] BluetoothSdk.requestPhoto requestId=${req.requestId} appId=${req.appId} size=${req.size} compress=${req.compress}
|
|
475
|
+
"NATIVE: PHOTO PIPELINE [3/6] BluetoothSdk.requestPhoto requestId=${req.requestId} appId=${req.appId} size=${req.size} compress=${req.compress} sound=${req.sound} exposureTimeNs=${req.exposureTimeNs} iso=${req.iso}"
|
|
509
476
|
)
|
|
510
477
|
requireSdk().requestPhoto(req).values
|
|
511
478
|
}
|
|
@@ -586,6 +553,10 @@ class BluetoothSdkModule : Module() {
|
|
|
586
553
|
requireSdk().startStream(StreamRequest.fromMap(params)).values
|
|
587
554
|
}
|
|
588
555
|
|
|
556
|
+
AsyncFunction("startExternallyManagedStream") { params: Map<String, Any> ->
|
|
557
|
+
requireSdk().startExternallyManagedStream(StreamRequest.fromMap(params)).values
|
|
558
|
+
}
|
|
559
|
+
|
|
589
560
|
AsyncFunction("stopStream") { requireSdk().stopStream().values }
|
|
590
561
|
|
|
591
562
|
AsyncFunction("sendExternallyManagedStreamKeepAlive") { params: Map<String, Any> ->
|
|
@@ -775,6 +746,22 @@ private fun Map<String, Any>?.toMentraDevice(): Device? {
|
|
|
775
746
|
)
|
|
776
747
|
}
|
|
777
748
|
|
|
749
|
+
private fun Map<String, Any?>.toPhotoCaptureDefaults(): PhotoCaptureDefaults =
|
|
750
|
+
PhotoCaptureDefaults(
|
|
751
|
+
size = (this["size"] as? String)?.let { PhotoSize.fromValue(it) },
|
|
752
|
+
mfnr = this["mfnr"] as? Boolean,
|
|
753
|
+
zsl = this["zsl"] as? Boolean,
|
|
754
|
+
noiseReduction = this["noiseReduction"] as? Boolean,
|
|
755
|
+
edgeEnhancement = this["edgeEnhancement"] as? Boolean,
|
|
756
|
+
ispDigitalGain = (this["ispDigitalGain"] as? Number)?.toInt(),
|
|
757
|
+
ispAnalogGain = this["ispAnalogGain"] as? String,
|
|
758
|
+
aeExposureDivisor = (this["aeExposureDivisor"] as? Number)?.toInt(),
|
|
759
|
+
isoCap = (this["isoCap"] as? Number)?.toInt(),
|
|
760
|
+
compress = this["compress"] as? String,
|
|
761
|
+
sound = this["sound"] as? Boolean,
|
|
762
|
+
resetCaptureTuning = this["resetCaptureTuning"] as? Boolean == true,
|
|
763
|
+
)
|
|
764
|
+
|
|
778
765
|
private fun Map<String, Any>?.toMentraConnectOptions(): ConnectOptions {
|
|
779
766
|
val values = this ?: return ConnectOptions()
|
|
780
767
|
return ConnectOptions(
|
|
@@ -1365,7 +1365,6 @@ class DeviceManager {
|
|
|
1365
1365
|
|
|
1366
1366
|
fun startStream(message: MutableMap<String, Any>) {
|
|
1367
1367
|
Bridge.log("MAN: startStream")
|
|
1368
|
-
message["flash"] = true
|
|
1369
1368
|
sgc?.startStream(message)
|
|
1370
1369
|
}
|
|
1371
1370
|
|
|
@@ -1435,10 +1434,10 @@ class DeviceManager {
|
|
|
1435
1434
|
}
|
|
1436
1435
|
|
|
1437
1436
|
fun sendButtonPhotoSettings(requestId: String, size: String) {
|
|
1438
|
-
sendButtonPhotoSettings(requestId,
|
|
1437
|
+
sendButtonPhotoSettings(requestId, PhotoCaptureDefaults(PhotoSize.fromValue(size)))
|
|
1439
1438
|
}
|
|
1440
1439
|
|
|
1441
|
-
fun sendButtonPhotoSettings(requestId: String, settings:
|
|
1440
|
+
fun sendButtonPhotoSettings(requestId: String, settings: PhotoCaptureDefaults) {
|
|
1442
1441
|
val live = sgc as? MentraLive ?: throw IllegalStateException("unsupported_device")
|
|
1443
1442
|
live.sendButtonPhotoSettings(
|
|
1444
1443
|
requestId,
|
|
@@ -1462,11 +1461,6 @@ class DeviceManager {
|
|
|
1462
1461
|
live.sendButtonVideoRecordingSettings(requestId, width, height, fps)
|
|
1463
1462
|
}
|
|
1464
1463
|
|
|
1465
|
-
fun sendButtonCameraLedSetting(requestId: String, enabled: Boolean) {
|
|
1466
|
-
val live = sgc as? MentraLive ?: throw IllegalStateException("unsupported_device")
|
|
1467
|
-
live.sendButtonCameraLedSetting(requestId, enabled)
|
|
1468
|
-
}
|
|
1469
|
-
|
|
1470
1464
|
fun sendButtonMaxRecordingTime(requestId: String, minutes: Int) {
|
|
1471
1465
|
val live = sgc as? MentraLive ?: throw IllegalStateException("unsupported_device")
|
|
1472
1466
|
live.sendButtonMaxRecordingTime(requestId, minutes)
|
|
@@ -1565,11 +1559,11 @@ class DeviceManager {
|
|
|
1565
1559
|
maxRecordingTimeMinutes: Int = 0,
|
|
1566
1560
|
) {
|
|
1567
1561
|
Bridge.log(
|
|
1568
|
-
"MAN: onStartVideoRecording: requestId=$requestId, save=$save,
|
|
1562
|
+
"MAN: onStartVideoRecording: requestId=$requestId, save=$save, sound=$sound, " +
|
|
1569
1563
|
"resolution=${width}x${height}@${fps}fps, maxRecordingTimeMinutes=$maxRecordingTimeMinutes"
|
|
1570
1564
|
)
|
|
1571
1565
|
sgc?.startVideoRecording(
|
|
1572
|
-
requestId, save,
|
|
1566
|
+
requestId, save, sound, width, height, fps, maxRecordingTimeMinutes)
|
|
1573
1567
|
}
|
|
1574
1568
|
|
|
1575
1569
|
fun stopVideoRecording(requestId: String, webhookUrl: String?, authToken: String?) {
|
|
@@ -1606,11 +1600,11 @@ class DeviceManager {
|
|
|
1606
1600
|
val manualIso = if (exposureNs != null) request.iso?.takeIf { it > 0 } else null
|
|
1607
1601
|
val routed =
|
|
1608
1602
|
request.copy(
|
|
1609
|
-
|
|
1610
|
-
|
|
1603
|
+
exposureTimeNs = exposureNs?.toDouble(),
|
|
1604
|
+
iso = manualIso,
|
|
1611
1605
|
)
|
|
1612
1606
|
Bridge.log(
|
|
1613
|
-
"MAN: PHOTO PIPELINE [4/6] DeviceManager.requestPhoto requestId=${routed.requestId} appId=${routed.appId} size=${routed.size.value} compress=${routed.compress.value}
|
|
1607
|
+
"MAN: PHOTO PIPELINE [4/6] DeviceManager.requestPhoto requestId=${routed.requestId} appId=${routed.appId} size=${routed.size.value} compress=${routed.compress.value} save=${routed.save} sound=${routed.sound} exposureTimeNs=$exposureNs iso=${manualIso ?: "auto"} aeDivisor=${routed.aeExposureDivisor} isoCap=${routed.isoCap} sgc=${sgc?.javaClass?.simpleName ?: "null"}"
|
|
1614
1608
|
)
|
|
1615
1609
|
val activeSgc = sgc
|
|
1616
1610
|
if (activeSgc == null) {
|
|
@@ -127,7 +127,6 @@ object DeviceStore {
|
|
|
127
127
|
store.set("bluetooth", "voice_activity_detection_enabled", BluetoothSdkDefaults.VOICE_ACTIVITY_DETECTION_ENABLED)
|
|
128
128
|
store.set("bluetooth", "screen_disabled", false)
|
|
129
129
|
store.set("bluetooth", "button_photo_size", "max")
|
|
130
|
-
store.set("bluetooth", "button_camera_led", true)
|
|
131
130
|
store.set("bluetooth", "button_max_recording_time", 10)
|
|
132
131
|
store.set("bluetooth", "camera_fov", mapOf("fov" to 118, "roi_position" to 0))
|
|
133
132
|
store.set("bluetooth", "button_video_width", 1280)
|
|
@@ -287,9 +286,6 @@ object DeviceStore {
|
|
|
287
286
|
"bluetooth" to "button_photo_size" -> {
|
|
288
287
|
DeviceManager.getInstance().sgc?.sendButtonPhotoSettings()
|
|
289
288
|
}
|
|
290
|
-
"bluetooth" to "button_camera_led" -> {
|
|
291
|
-
DeviceManager.getInstance().sgc?.sendButtonCameraLedSetting()
|
|
292
|
-
}
|
|
293
289
|
"bluetooth" to "button_max_recording_time" -> {
|
|
294
290
|
DeviceManager.getInstance().sgc?.sendButtonMaxRecordingTime()
|
|
295
291
|
}
|
|
@@ -474,7 +474,7 @@ class MentraBluetoothSdk private constructor(
|
|
|
474
474
|
DeviceStore.apply(ObservableStore.BLUETOOTH_CATEGORY, "voice_activity_detection_enabled", enabled)
|
|
475
475
|
}
|
|
476
476
|
|
|
477
|
-
fun
|
|
477
|
+
fun setPhotoCaptureDefaults(settings: PhotoCaptureDefaults): SettingsAckEvent =
|
|
478
478
|
performSettingsCommand(
|
|
479
479
|
setting = "button_photo",
|
|
480
480
|
updateStore = { _ ->
|
|
@@ -527,30 +527,20 @@ class MentraBluetoothSdk private constructor(
|
|
|
527
527
|
send = { requestId -> deviceManager.sendButtonPhotoSettings(requestId, settings) },
|
|
528
528
|
)
|
|
529
529
|
|
|
530
|
-
fun
|
|
530
|
+
fun setVideoRecordingDefaults(defaults: VideoRecordingDefaults): SettingsAckEvent =
|
|
531
531
|
performSettingsCommand(
|
|
532
532
|
setting = "button_video_recording",
|
|
533
533
|
updateStore = { _ ->
|
|
534
|
-
DeviceStore.set(ObservableStore.BLUETOOTH_CATEGORY, "button_video_width", width)
|
|
535
|
-
DeviceStore.set(ObservableStore.BLUETOOTH_CATEGORY, "button_video_height", height)
|
|
536
|
-
DeviceStore.set(ObservableStore.BLUETOOTH_CATEGORY, "button_video_fps", fps)
|
|
534
|
+
DeviceStore.set(ObservableStore.BLUETOOTH_CATEGORY, "button_video_width", defaults.width)
|
|
535
|
+
DeviceStore.set(ObservableStore.BLUETOOTH_CATEGORY, "button_video_height", defaults.height)
|
|
536
|
+
DeviceStore.set(ObservableStore.BLUETOOTH_CATEGORY, "button_video_fps", defaults.fps)
|
|
537
537
|
},
|
|
538
538
|
send = { requestId ->
|
|
539
|
-
deviceManager.sendButtonVideoRecordingSettings(requestId, width, height, fps)
|
|
539
|
+
deviceManager.sendButtonVideoRecordingSettings(requestId, defaults.width, defaults.height, defaults.fps)
|
|
540
540
|
},
|
|
541
541
|
)
|
|
542
542
|
|
|
543
|
-
fun
|
|
544
|
-
setButtonVideoRecordingSettings(width = settings.width, height = settings.height, fps = settings.fps)
|
|
545
|
-
|
|
546
|
-
fun setButtonCameraLed(enabled: Boolean): SettingsAckEvent =
|
|
547
|
-
performSettingsCommand(
|
|
548
|
-
setting = "button_camera_led",
|
|
549
|
-
updateStore = { _ -> DeviceStore.set(ObservableStore.BLUETOOTH_CATEGORY, "button_camera_led", enabled) },
|
|
550
|
-
send = { requestId -> deviceManager.sendButtonCameraLedSetting(requestId, enabled) },
|
|
551
|
-
)
|
|
552
|
-
|
|
553
|
-
fun setButtonMaxRecordingTime(minutes: Int): SettingsAckEvent =
|
|
543
|
+
fun setMaxVideoRecordingDuration(minutes: Int): SettingsAckEvent =
|
|
554
544
|
performSettingsCommand(
|
|
555
545
|
setting = "button_max_recording_time",
|
|
556
546
|
updateStore = { _ ->
|
|
@@ -754,7 +744,16 @@ class MentraBluetoothSdk private constructor(
|
|
|
754
744
|
}
|
|
755
745
|
}
|
|
756
746
|
|
|
757
|
-
fun startStream(request: StreamRequest): StreamStatusEvent
|
|
747
|
+
fun startStream(request: StreamRequest): StreamStatusEvent =
|
|
748
|
+
startStream(request, startSdkKeepAlive = true)
|
|
749
|
+
|
|
750
|
+
internal fun startExternallyManagedStream(request: StreamRequest): StreamStatusEvent =
|
|
751
|
+
startStream(request, startSdkKeepAlive = false)
|
|
752
|
+
|
|
753
|
+
private fun startStream(
|
|
754
|
+
request: StreamRequest,
|
|
755
|
+
startSdkKeepAlive: Boolean,
|
|
756
|
+
): StreamStatusEvent {
|
|
758
757
|
val message = request.toMap().toMutableMap()
|
|
759
758
|
val streamId = (message["streamId"] as? String)?.takeIf { it.isNotBlank() }
|
|
760
759
|
?: "sdk-${UUID.randomUUID()}"
|
|
@@ -765,8 +764,8 @@ class MentraBluetoothSdk private constructor(
|
|
|
765
764
|
try {
|
|
766
765
|
deviceManager.startStream(message)
|
|
767
766
|
val event = pending.await(STREAM_START_TIMEOUT_MS)
|
|
768
|
-
if (
|
|
769
|
-
startStreamKeepAliveMonitor(streamId,
|
|
767
|
+
if (startSdkKeepAlive) {
|
|
768
|
+
startStreamKeepAliveMonitor(streamId, DEFAULT_STREAM_KEEP_ALIVE_INTERVAL_SECONDS)
|
|
770
769
|
}
|
|
771
770
|
return event
|
|
772
771
|
} finally {
|
|
@@ -56,8 +56,8 @@ enum class PhotoCompression(val value: String) {
|
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
data class
|
|
60
|
-
val size:
|
|
59
|
+
data class PhotoCaptureDefaults(
|
|
60
|
+
val size: PhotoSize? = null,
|
|
61
61
|
val mfnr: Boolean? = null,
|
|
62
62
|
val zsl: Boolean? = null,
|
|
63
63
|
val noiseReduction: Boolean? = null,
|
|
@@ -71,7 +71,7 @@ data class ButtonPhotoSettings(
|
|
|
71
71
|
val resetCaptureTuning: Boolean = false,
|
|
72
72
|
)
|
|
73
73
|
|
|
74
|
-
data class
|
|
74
|
+
data class VideoRecordingDefaults(
|
|
75
75
|
val width: Int,
|
|
76
76
|
val height: Int,
|
|
77
77
|
val fps: Int,
|
|
@@ -167,7 +167,6 @@ data class PhotoRequest @JvmOverloads constructor(
|
|
|
167
167
|
val webhookUrl: String,
|
|
168
168
|
val authToken: String? = null,
|
|
169
169
|
val compress: PhotoCompression = PhotoCompression.MEDIUM,
|
|
170
|
-
val flash: Boolean = true,
|
|
171
170
|
val save: Boolean = false,
|
|
172
171
|
val sound: Boolean = true,
|
|
173
172
|
/** Sensor exposure time for this capture only (ns), or null for auto exposure */
|
|
@@ -218,7 +217,6 @@ data class PhotoRequest @JvmOverloads constructor(
|
|
|
218
217
|
webhookUrl = stringValue(values, "webhookUrl", "webhook_url").orEmpty(),
|
|
219
218
|
authToken = stringValue(values, "authToken", "auth_token")?.takeIf { it.isNotBlank() },
|
|
220
219
|
compress = PhotoCompression.fromValue(stringValue(values, "compress") ?: "none"),
|
|
221
|
-
flash = boolValue(values, "flash") ?: true,
|
|
222
220
|
save = boolValue(values, "save", "saveToGallery") ?: false,
|
|
223
221
|
sound = boolValue(values, "sound") ?: true,
|
|
224
222
|
exposureTimeNs = exposureTimeNs,
|
|
@@ -20,14 +20,13 @@ abstract class ControllerManager {
|
|
|
20
20
|
abstract fun startStream(message: Map<String, Any>)
|
|
21
21
|
abstract fun stopStream()
|
|
22
22
|
abstract fun sendStreamKeepAlive(message: Map<String, Any>)
|
|
23
|
-
abstract fun startVideoRecording(requestId: String, save: Boolean,
|
|
23
|
+
abstract fun startVideoRecording(requestId: String, save: Boolean, sound: Boolean)
|
|
24
24
|
abstract fun stopVideoRecording(requestId: String)
|
|
25
25
|
|
|
26
26
|
// Button Settings
|
|
27
27
|
abstract fun sendButtonPhotoSettings()
|
|
28
28
|
abstract fun sendButtonVideoRecordingSettings()
|
|
29
29
|
abstract fun sendButtonMaxRecordingTime()
|
|
30
|
-
abstract fun sendButtonCameraLedSetting()
|
|
31
30
|
|
|
32
31
|
// Display Control
|
|
33
32
|
abstract fun setBrightness(level: Int, autoMode: Boolean)
|
|
@@ -863,7 +863,7 @@ class R1 : ControllerManager() {
|
|
|
863
863
|
override fun sortMicRanking(list: MutableList<String>): MutableList<String> = list
|
|
864
864
|
override fun sendJson(jsonOriginal: Map<String, Any>, wakeUp: Boolean, requireAck: Boolean) {}
|
|
865
865
|
override fun requestPhoto(request: PhotoRequest) {}
|
|
866
|
-
override fun startVideoRecording(requestId: String, save: Boolean,
|
|
866
|
+
override fun startVideoRecording(requestId: String, save: Boolean, sound: Boolean) {}
|
|
867
867
|
override fun stopVideoRecording(requestId: String) {}
|
|
868
868
|
override fun startStream(message: Map<String, Any>) {}
|
|
869
869
|
override fun stopStream() {}
|
|
@@ -871,7 +871,6 @@ class R1 : ControllerManager() {
|
|
|
871
871
|
override fun sendButtonPhotoSettings() {}
|
|
872
872
|
override fun sendButtonVideoRecordingSettings() {}
|
|
873
873
|
override fun sendButtonMaxRecordingTime() {}
|
|
874
|
-
override fun sendButtonCameraLedSetting() {}
|
|
875
874
|
override fun setBrightness(level: Int, autoMode: Boolean) {}
|
|
876
875
|
override fun clearDisplay() {}
|
|
877
876
|
override fun sendTextWall(text: String) {}
|
|
@@ -1584,7 +1584,7 @@ class G1 : SGCManager() {
|
|
|
1584
1584
|
|
|
1585
1585
|
}
|
|
1586
1586
|
|
|
1587
|
-
override fun startVideoRecording(requestId: String, save: Boolean,
|
|
1587
|
+
override fun startVideoRecording(requestId: String, save: Boolean, sound: Boolean) {
|
|
1588
1588
|
|
|
1589
1589
|
}
|
|
1590
1590
|
|
|
@@ -1600,10 +1600,6 @@ class G1 : SGCManager() {
|
|
|
1600
1600
|
|
|
1601
1601
|
}
|
|
1602
1602
|
|
|
1603
|
-
override fun sendButtonCameraLedSetting() {
|
|
1604
|
-
|
|
1605
|
-
}
|
|
1606
|
-
|
|
1607
1603
|
override fun sendCameraFovSetting() {
|
|
1608
1604
|
}
|
|
1609
1605
|
|
|
@@ -2660,7 +2660,6 @@ class G2 : SGCManager() {
|
|
|
2660
2660
|
override fun startVideoRecording(
|
|
2661
2661
|
requestId: String,
|
|
2662
2662
|
save: Boolean,
|
|
2663
|
-
flash: Boolean,
|
|
2664
2663
|
sound: Boolean
|
|
2665
2664
|
) {
|
|
2666
2665
|
Bridge.log("G2: startVideoRecording - not supported")
|
|
@@ -2683,10 +2682,6 @@ class G2 : SGCManager() {
|
|
|
2683
2682
|
Bridge.log("G2: sendButtonMaxRecordingTime")
|
|
2684
2683
|
}
|
|
2685
2684
|
|
|
2686
|
-
override fun sendButtonCameraLedSetting() {
|
|
2687
|
-
Bridge.log("G2: sendButtonCameraLedSetting")
|
|
2688
|
-
}
|
|
2689
|
-
|
|
2690
2685
|
override fun sendCameraFovSetting() {
|
|
2691
2686
|
Bridge.log("G2: sendCameraFovSetting")
|
|
2692
2687
|
}
|
|
@@ -183,7 +183,7 @@ class Mach1 : SGCManager() {
|
|
|
183
183
|
|
|
184
184
|
}
|
|
185
185
|
|
|
186
|
-
override fun startVideoRecording(requestId: String, save: Boolean,
|
|
186
|
+
override fun startVideoRecording(requestId: String, save: Boolean, sound: Boolean) {
|
|
187
187
|
|
|
188
188
|
}
|
|
189
189
|
|
|
@@ -203,10 +203,6 @@ class Mach1 : SGCManager() {
|
|
|
203
203
|
|
|
204
204
|
}
|
|
205
205
|
|
|
206
|
-
override fun sendButtonCameraLedSetting() {
|
|
207
|
-
|
|
208
|
-
}
|
|
209
|
-
|
|
210
206
|
override fun sendCameraFovSetting() {
|
|
211
207
|
}
|
|
212
208
|
|
|
@@ -5207,7 +5207,6 @@ class MentraLive : SGCManager() {
|
|
|
5207
5207
|
val webhookUrl = request.webhookUrl
|
|
5208
5208
|
val authToken = request.authToken
|
|
5209
5209
|
val compress = request.compress.value
|
|
5210
|
-
val flash = request.flash
|
|
5211
5210
|
val save = request.save
|
|
5212
5211
|
val sound = request.sound
|
|
5213
5212
|
val exposureTimeNs = request.exposureTimeNs
|
|
@@ -5226,8 +5225,6 @@ class MentraLive : SGCManager() {
|
|
|
5226
5225
|
(if (hasAuthToken) "***" else "none") +
|
|
5227
5226
|
", compress=" +
|
|
5228
5227
|
compress +
|
|
5229
|
-
", flash=" +
|
|
5230
|
-
flash +
|
|
5231
5228
|
", save=" +
|
|
5232
5229
|
save +
|
|
5233
5230
|
", sound=" +
|
|
@@ -5267,7 +5264,6 @@ class MentraLive : SGCManager() {
|
|
|
5267
5264
|
} else {
|
|
5268
5265
|
json.put("compress", "none")
|
|
5269
5266
|
}
|
|
5270
|
-
json.put("flash", flash)
|
|
5271
5267
|
json.put("save", save)
|
|
5272
5268
|
json.put("sound", sound)
|
|
5273
5269
|
if (exposureTimeNs != null && exposureTimeNs > 0L) {
|
|
@@ -6144,25 +6140,6 @@ class MentraLive : SGCManager() {
|
|
|
6144
6140
|
}
|
|
6145
6141
|
}
|
|
6146
6142
|
|
|
6147
|
-
fun sendButtonCameraLedSetting(enabled: Boolean) {
|
|
6148
|
-
sendButtonCameraLedSetting(null, enabled)
|
|
6149
|
-
}
|
|
6150
|
-
|
|
6151
|
-
fun sendButtonCameraLedSetting(requestId: String?, enabled: Boolean) {
|
|
6152
|
-
// Send LED setting to glasses
|
|
6153
|
-
val command = JSONObject()
|
|
6154
|
-
try {
|
|
6155
|
-
command.put("type", "button_camera_led")
|
|
6156
|
-
if (requestId != null && !requestId.isEmpty()) {
|
|
6157
|
-
command.put("request_id", requestId)
|
|
6158
|
-
}
|
|
6159
|
-
command.put("enabled", enabled)
|
|
6160
|
-
sendJson(command, true)
|
|
6161
|
-
} catch (e: Exception) {
|
|
6162
|
-
Log.e(TAG, "Error sending button camera LED setting", e)
|
|
6163
|
-
}
|
|
6164
|
-
}
|
|
6165
|
-
|
|
6166
6143
|
override fun sendText(text: String) {
|
|
6167
6144
|
Bridge.log("LIVE: [STUB] Device has no display. Text would show: " + text)
|
|
6168
6145
|
}
|
|
@@ -8073,9 +8050,6 @@ class MentraLive : SGCManager() {
|
|
|
8073
8050
|
// Send button photo settings
|
|
8074
8051
|
sendButtonPhotoSettings()
|
|
8075
8052
|
|
|
8076
|
-
// Send button camera LED setting
|
|
8077
|
-
sendButtonCameraLedSetting()
|
|
8078
|
-
|
|
8079
8053
|
// Send camera FOV setting (K900 / Mentra Live)
|
|
8080
8054
|
sendCameraFovSetting()
|
|
8081
8055
|
|
|
@@ -8144,12 +8118,6 @@ class MentraLive : SGCManager() {
|
|
|
8144
8118
|
)
|
|
8145
8119
|
}
|
|
8146
8120
|
|
|
8147
|
-
/** Send button camera LED setting to glasses */
|
|
8148
|
-
override fun sendButtonCameraLedSetting() {
|
|
8149
|
-
val enabled = DeviceStore.get("bluetooth", "button_camera_led") as Boolean
|
|
8150
|
-
sendButtonCameraLedSetting(null, enabled)
|
|
8151
|
-
}
|
|
8152
|
-
|
|
8153
8121
|
/** Send camera FOV setting to glasses (K900 / Mentra Live). */
|
|
8154
8122
|
override fun sendCameraFovSetting() {
|
|
8155
8123
|
var fov = 118
|
|
@@ -8228,17 +8196,15 @@ class MentraLive : SGCManager() {
|
|
|
8228
8196
|
override fun startVideoRecording(
|
|
8229
8197
|
requestId: String,
|
|
8230
8198
|
save: Boolean,
|
|
8231
|
-
flash: Boolean,
|
|
8232
8199
|
sound: Boolean
|
|
8233
8200
|
) {
|
|
8234
|
-
startVideoRecording(requestId, save,
|
|
8201
|
+
startVideoRecording(requestId, save, sound, 0, 0, 0, 0) // Use defaults
|
|
8235
8202
|
}
|
|
8236
8203
|
|
|
8237
8204
|
/**
|
|
8238
8205
|
* Start video recording with optional resolution settings
|
|
8239
8206
|
* @param requestId Request ID for tracking
|
|
8240
8207
|
* @param save Whether to save the video
|
|
8241
|
-
* @param flash Whether to enable privacy flash LED
|
|
8242
8208
|
* @param sound Whether to enable start/stop sounds
|
|
8243
8209
|
* @param width Video width (0 for default)
|
|
8244
8210
|
* @param height Video height (0 for default)
|
|
@@ -8248,7 +8214,6 @@ class MentraLive : SGCManager() {
|
|
|
8248
8214
|
override fun startVideoRecording(
|
|
8249
8215
|
requestId: String,
|
|
8250
8216
|
save: Boolean,
|
|
8251
|
-
flash: Boolean,
|
|
8252
8217
|
sound: Boolean,
|
|
8253
8218
|
width: Int,
|
|
8254
8219
|
height: Int,
|
|
@@ -8260,8 +8225,6 @@ class MentraLive : SGCManager() {
|
|
|
8260
8225
|
requestId +
|
|
8261
8226
|
", save=" +
|
|
8262
8227
|
save +
|
|
8263
|
-
", flash=" +
|
|
8264
|
-
flash +
|
|
8265
8228
|
", sound=" +
|
|
8266
8229
|
sound +
|
|
8267
8230
|
", resolution=" +
|
|
@@ -8285,7 +8248,6 @@ class MentraLive : SGCManager() {
|
|
|
8285
8248
|
json.put("type", "start_video_recording")
|
|
8286
8249
|
json.put("requestId", requestId)
|
|
8287
8250
|
json.put("save", save)
|
|
8288
|
-
json.put("flash", flash)
|
|
8289
8251
|
json.put("sound", sound)
|
|
8290
8252
|
|
|
8291
8253
|
// Auto-stop timer; only sent when set (> 0). 0 = record until stopped.
|
|
@@ -343,14 +343,13 @@ class MentraNex : SGCManager() {
|
|
|
343
343
|
override fun startStream(message: MutableMap<String, Any>) { Bridge.log("Nex: startStream operation not supported") }
|
|
344
344
|
override fun stopStream() { Bridge.log("Nex: stopStream operation not supported") }
|
|
345
345
|
override fun sendStreamKeepAlive(message: MutableMap<String, Any>) { Bridge.log("Nex: sendStreamKeepAlive operation not supported") }
|
|
346
|
-
override fun startVideoRecording(requestId: String, save: Boolean,
|
|
346
|
+
override fun startVideoRecording(requestId: String, save: Boolean, sound: Boolean) { Bridge.log("Nex: startVideoRecording operation not supported") }
|
|
347
347
|
override fun stopVideoRecording(requestId: String) { Bridge.log("Nex: stopVideoRecording operation not supported") }
|
|
348
348
|
|
|
349
349
|
// Button Settings: Not supported on Nex
|
|
350
350
|
override fun sendButtonPhotoSettings() { Bridge.log("Nex: sendButtonPhotoSettings operation not supported") }
|
|
351
351
|
override fun sendButtonVideoRecordingSettings() { Bridge.log("Nex: sendButtonVideoRecordingSettings operation not supported") }
|
|
352
352
|
override fun sendButtonMaxRecordingTime() { Bridge.log("Nex: sendButtonMaxRecordingTime operation not supported") }
|
|
353
|
-
override fun sendButtonCameraLedSetting() { Bridge.log("Nex: sendButtonCameraLedSetting operation not supported") }
|
|
354
353
|
|
|
355
354
|
override fun sendCameraFovSetting() { Bridge.log("Nex: sendCameraFovSetting operation not supported") }
|
|
356
355
|
|
|
@@ -20,7 +20,7 @@ abstract class SGCManager {
|
|
|
20
20
|
abstract fun startStream(message: MutableMap<String, Any>)
|
|
21
21
|
abstract fun stopStream()
|
|
22
22
|
abstract fun sendStreamKeepAlive(message: MutableMap<String, Any>)
|
|
23
|
-
abstract fun startVideoRecording(requestId: String, save: Boolean,
|
|
23
|
+
abstract fun startVideoRecording(requestId: String, save: Boolean, sound: Boolean)
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
26
|
* Start video recording with optional per-recording resolution/fps. A width,
|
|
@@ -32,14 +32,13 @@ abstract class SGCManager {
|
|
|
32
32
|
open fun startVideoRecording(
|
|
33
33
|
requestId: String,
|
|
34
34
|
save: Boolean,
|
|
35
|
-
flash: Boolean,
|
|
36
35
|
sound: Boolean,
|
|
37
36
|
width: Int,
|
|
38
37
|
height: Int,
|
|
39
38
|
fps: Int,
|
|
40
39
|
maxRecordingTimeMinutes: Int,
|
|
41
40
|
) {
|
|
42
|
-
startVideoRecording(requestId, save,
|
|
41
|
+
startVideoRecording(requestId, save, sound)
|
|
43
42
|
}
|
|
44
43
|
|
|
45
44
|
abstract fun stopVideoRecording(requestId: String)
|
|
@@ -59,7 +58,6 @@ abstract class SGCManager {
|
|
|
59
58
|
abstract fun sendButtonPhotoSettings()
|
|
60
59
|
abstract fun sendButtonVideoRecordingSettings()
|
|
61
60
|
abstract fun sendButtonMaxRecordingTime()
|
|
62
|
-
abstract fun sendButtonCameraLedSetting()
|
|
63
61
|
abstract fun sendCameraFovSetting()
|
|
64
62
|
|
|
65
63
|
// Display Control
|
|
@@ -34,7 +34,7 @@ class Simulated : SGCManager() {
|
|
|
34
34
|
|
|
35
35
|
// Camera & Media
|
|
36
36
|
override fun requestPhoto(request: PhotoRequest) {
|
|
37
|
-
Bridge.log("requestPhoto
|
|
37
|
+
Bridge.log("requestPhoto save=${request.save}, sound=${request.sound}")
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
override fun startStream(message: MutableMap<String, Any>) {
|
|
@@ -49,8 +49,8 @@ class Simulated : SGCManager() {
|
|
|
49
49
|
Bridge.log("sendStreamKeepAlive")
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
override fun startVideoRecording(requestId: String, save: Boolean,
|
|
53
|
-
Bridge.log("startVideoRecording
|
|
52
|
+
override fun startVideoRecording(requestId: String, save: Boolean, sound: Boolean) {
|
|
53
|
+
Bridge.log("startVideoRecording sound=$sound")
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
override fun stopVideoRecording(requestId: String) {
|
|
@@ -70,10 +70,6 @@ class Simulated : SGCManager() {
|
|
|
70
70
|
Bridge.log("sendButtonMaxRecordingTime")
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
override fun sendButtonCameraLedSetting() {
|
|
74
|
-
Bridge.log("sendButtonCameraLedSetting")
|
|
75
|
-
}
|
|
76
|
-
|
|
77
73
|
override fun sendCameraFovSetting() {
|
|
78
74
|
Bridge.log("sendCameraFovSetting")
|
|
79
75
|
}
|