@mentra/bluetooth-sdk 0.1.21-beta.0 → 0.1.21-beta.2
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 +22 -11
- package/android/build.gradle +3 -0
- package/android/src/main/AndroidManifest.xml +3 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkAnalytics.kt +34 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkDefaults.kt +1 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkModule.kt +43 -3
- package/android/src/main/java/com/mentra/bluetoothsdk/Bridge.kt +7 -2
- package/android/src/main/java/com/mentra/bluetoothsdk/DeviceManager.kt +82 -6
- package/android/src/main/java/com/mentra/bluetoothsdk/DeviceStore.kt +5 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothSdk.kt +32 -3
- package/android/src/main/java/com/mentra/bluetoothsdk/OtaManifest.kt +43 -3
- package/android/src/main/java/com/mentra/bluetoothsdk/camera/CameraModels.kt +17 -4
- package/android/src/main/java/com/mentra/bluetoothsdk/debug/BleTraceLogger.kt +1 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/services/Foreground.kt +53 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Ar99.java +2653 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraLive.kt +161 -14
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/SGCManager.kt +3 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/ar99/ota/Ar99OtaManager.java +566 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/ar99/ota/OtaByteUtils.java +18 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/ar99/ota/OtaCommandConstants.java +35 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/ar99/ota/OtaCrc32Util.java +13 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/ar99/ota/OtaGattTransport.java +11 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/ar99/ota/OtaProtocol.java +92 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/streaming/StreamModels.kt +41 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/types/DeviceModels.kt +7 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/BleJsonCompact.java +8 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/Constants.kt +2 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/PhoneAudioMonitor.kt +45 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/audio/Ar99OpusPcmDecoder.java +398 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/OtaManifestDowngradeTest.kt +86 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/StreamStatusEventTest.kt +29 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/camera/PhotoRequestTest.kt +30 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/Ar99AdvertisementMatcherTest.java +22 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/utils/BleJsonCompactTest.java +20 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/utils/PhoneAudioMonitorTest.kt +45 -0
- package/build/BluetoothSdk.types.d.ts +58 -25
- package/build/BluetoothSdk.types.d.ts.map +1 -1
- package/build/BluetoothSdk.types.js +2 -1
- package/build/BluetoothSdk.types.js.map +1 -1
- package/build/_internal.d.ts +1 -0
- package/build/_internal.d.ts.map +1 -1
- package/build/_internal.js +1 -0
- package/build/_internal.js.map +1 -1
- package/build/_private/BluetoothSdkModule.d.ts +8 -2
- package/build/_private/BluetoothSdkModule.d.ts.map +1 -1
- package/build/_private/BluetoothSdkModule.js +6 -3
- package/build/_private/BluetoothSdkModule.js.map +1 -1
- package/build/_private/cameraRequestPayload.d.ts +1 -1
- package/build/_private/cameraRequestPayload.d.ts.map +1 -1
- package/build/_private/cameraRequestPayload.js +6 -0
- package/build/_private/cameraRequestPayload.js.map +1 -1
- package/build/_private/photoRequestPayload.d.ts.map +1 -1
- package/build/_private/photoRequestPayload.js +10 -1
- package/build/_private/photoRequestPayload.js.map +1 -1
- package/build/_private/sdkOtaManifest.d.ts +20 -0
- package/build/_private/sdkOtaManifest.d.ts.map +1 -0
- package/build/_private/sdkOtaManifest.js +28 -0
- package/build/_private/sdkOtaManifest.js.map +1 -0
- package/build/index.d.ts +1 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +7 -1
- package/build/index.js.map +1 -1
- package/ios/BluetoothSdkModule.swift +41 -2
- package/ios/Source/BluetoothSdkDefaults.swift +2 -1
- package/ios/Source/Bridge.swift +15 -3
- package/ios/Source/DeviceManager.swift +67 -17
- package/ios/Source/DeviceStore.swift +5 -1
- package/ios/Source/MentraBluetoothSDK.swift +37 -8
- package/ios/Source/OtaManifest.swift +47 -4
- package/ios/Source/camera/CameraModels.swift +20 -5
- package/ios/Source/internal/BleTraceLogger.swift +1 -1
- package/ios/Source/internal/BluetoothSdkAnalytics.swift +39 -0
- package/ios/Source/sgcs/Ar99.swift +2451 -0
- package/ios/Source/sgcs/MentraLive.swift +174 -38
- package/ios/Source/sgcs/SGCManager.swift +8 -0
- package/ios/Source/sgcs/ar99/ota/Ar99OtaManager.swift +637 -0
- package/ios/Source/streaming/StreamModels.swift +71 -7
- package/ios/Source/types/DeviceModels.swift +13 -0
- package/ios/Source/utils/BleJsonCompact.swift +8 -0
- package/ios/Source/utils/Constants.swift +2 -0
- package/ios/Source/utils/PhoneAudioMonitor.swift +4 -0
- package/package.json +1 -1
- package/src/BluetoothSdk.types.ts +61 -28
- package/src/_internal.ts +1 -0
- package/src/_private/BluetoothSdkModule.ts +20 -6
- package/src/_private/cameraRequestPayload.ts +8 -2
- package/src/_private/photoRequestPayload.ts +12 -2
- package/src/_private/sdkOtaManifest.ts +31 -0
- package/src/index.ts +9 -1
|
@@ -95,6 +95,7 @@ class MentraLive : SGCManager() {
|
|
|
95
95
|
// LC3 frame size for Mentra Live
|
|
96
96
|
private const val LC3_FRAME_SIZE = 40
|
|
97
97
|
private const val VOICE_ACTIVITY_DETECTION_SWITCH_TYPE = 8
|
|
98
|
+
private const val LOUDNESS_GATE_SWITCH_TYPE = 10
|
|
98
99
|
private const val BES2700_MTU_LIMIT = 509
|
|
99
100
|
|
|
100
101
|
// L2CAP CoC fast path: new BES2700 firmware registers an LE L2CAP CoC server on this
|
|
@@ -233,6 +234,15 @@ class MentraLive : SGCManager() {
|
|
|
233
234
|
private var peerK900Le = false
|
|
234
235
|
private var peerWireCapsBinary = false
|
|
235
236
|
private var peerFilePayloadV2 = false
|
|
237
|
+
// Last observed glasses process session id (`sid` in glasses_ready / version_info_1).
|
|
238
|
+
// The BES keeps the BLE link alive across asg_client restarts, so transport state
|
|
239
|
+
// cannot signal a restart - a CHANGED (or newly appearing) sid is the restart signal.
|
|
240
|
+
// Null = no sid observed this BLE session (legacy glasses, or none seen yet).
|
|
241
|
+
private var glassesSessionId: String? = null
|
|
242
|
+
// True once a glasses_ready completed on THIS physical BLE session; resets only with
|
|
243
|
+
// the physical connection (never on heartbeat readiness flaps), so a first-seen sid
|
|
244
|
+
// after an upgrade OTA is always detected as a restart.
|
|
245
|
+
private var readinessCompletedThisBleSession = false
|
|
236
246
|
// Note: appVersion, buildNumber, deviceModel, androidVersion
|
|
237
247
|
// are inherited from SGCManager parent class
|
|
238
248
|
|
|
@@ -806,6 +816,15 @@ class MentraLive : SGCManager() {
|
|
|
806
816
|
return
|
|
807
817
|
}
|
|
808
818
|
|
|
819
|
+
if (state == ConnTypes.DISCONNECTED) {
|
|
820
|
+
// A manufacturing serial is session-bound. Clear it on disconnect so a previous
|
|
821
|
+
// pair's serial can never be associated with the next connection. Connect must NOT
|
|
822
|
+
// clear it: DeviceManager.disconnect already wipes it before any new connection, and
|
|
823
|
+
// clearing on CONNECTED would wipe a still-valid serial mid-session when a same-link
|
|
824
|
+
// glasses_ready (e.g. ASG restart) re-publishes CONNECTED.
|
|
825
|
+
DeviceStore.apply("glasses", "serialNumber", "")
|
|
826
|
+
}
|
|
827
|
+
|
|
809
828
|
// Actually update the connection state!
|
|
810
829
|
DeviceStore.apply("glasses", "connectionState", state)
|
|
811
830
|
|
|
@@ -821,6 +840,10 @@ class MentraLive : SGCManager() {
|
|
|
821
840
|
DeviceStore.apply("glasses", "appVersion", "")
|
|
822
841
|
DeviceStore.apply("glasses", "besFirmwareVersion", "")
|
|
823
842
|
DeviceStore.apply("glasses", "mtkFirmwareVersion", "")
|
|
843
|
+
// Modern ASG builds omit ota_version_url entirely (the phone owns manifest
|
|
844
|
+
// selection), and the chunked parser only writes present fields — clear it here so
|
|
845
|
+
// a URL reported by a previous build cannot leak into this session.
|
|
846
|
+
DeviceStore.apply("glasses", "otaVersionUrl", "")
|
|
824
847
|
Bridge.log("LIVE: Cleared cached version_info fields for fresh session")
|
|
825
848
|
}
|
|
826
849
|
|
|
@@ -1561,6 +1584,8 @@ class MentraLive : SGCManager() {
|
|
|
1561
1584
|
|
|
1562
1585
|
connectedDevice = null
|
|
1563
1586
|
glassesReady = false // Reset ready state on disconnect
|
|
1587
|
+
glassesSessionId = null // Fresh BLE session starts with no sid known
|
|
1588
|
+
readinessCompletedThisBleSession = false
|
|
1564
1589
|
|
|
1565
1590
|
// Reset audio pairing flags
|
|
1566
1591
|
glassesReadyReceived = false
|
|
@@ -1625,6 +1650,8 @@ class MentraLive : SGCManager() {
|
|
|
1625
1650
|
isConnected = false
|
|
1626
1651
|
isConnecting = false
|
|
1627
1652
|
glassesReady = false
|
|
1653
|
+
glassesSessionId = null
|
|
1654
|
+
readinessCompletedThisBleSession = false
|
|
1628
1655
|
glassesReadyReceived = false
|
|
1629
1656
|
audioConnected = false
|
|
1630
1657
|
|
|
@@ -3515,6 +3542,11 @@ class MentraLive : SGCManager() {
|
|
|
3515
3542
|
resetWireNegotiationState()
|
|
3516
3543
|
parsePeerWireCaps(json)
|
|
3517
3544
|
maybeSendWireHandshake()
|
|
3545
|
+
// Record the session id BEFORE marking ready: an unsolicited glasses_ready
|
|
3546
|
+
// already runs this full remote-reset flow, so recording (not re-triggering)
|
|
3547
|
+
// is correct here; version_info detection covers the restart case.
|
|
3548
|
+
json.optString("sid", "").takeIf { it.isNotEmpty() }?.let { glassesSessionId = it }
|
|
3549
|
+
readinessCompletedThisBleSession = true
|
|
3518
3550
|
|
|
3519
3551
|
// Set the ready flag to stop any future readiness checks
|
|
3520
3552
|
glassesReady = true
|
|
@@ -3674,7 +3706,7 @@ class MentraLive : SGCManager() {
|
|
|
3674
3706
|
|
|
3675
3707
|
"version_info" -> {
|
|
3676
3708
|
// Process version information from ASG client (legacy single-message format)
|
|
3677
|
-
Bridge.log("LIVE: Received version info from ASG client
|
|
3709
|
+
Bridge.log("LIVE: Received version info from ASG client")
|
|
3678
3710
|
|
|
3679
3711
|
// Extract version information
|
|
3680
3712
|
val appVersionLegacy = json.optString("app_version", "")
|
|
@@ -3684,6 +3716,7 @@ class MentraLive : SGCManager() {
|
|
|
3684
3716
|
val otaVersionUrlLegacy: String? = json.optString("ota_version_url", null)
|
|
3685
3717
|
val firmwareVersionLegacy = json.optString("firmware_version", "")
|
|
3686
3718
|
val btMacAddressLegacy = json.optString("bt_mac_address", "")
|
|
3719
|
+
val serialNumberLegacy = json.optString("serial_number", "")
|
|
3687
3720
|
|
|
3688
3721
|
// Update parent SGCManager fields
|
|
3689
3722
|
DeviceStore.apply("glasses", "appVersion", appVersionLegacy)
|
|
@@ -3697,6 +3730,9 @@ class MentraLive : SGCManager() {
|
|
|
3697
3730
|
)
|
|
3698
3731
|
DeviceStore.apply("glasses", "firmwareVersion", firmwareVersionLegacy)
|
|
3699
3732
|
DeviceStore.apply("glasses", "bluetoothMacAddress", btMacAddressLegacy)
|
|
3733
|
+
if (serialNumberLegacy.isNotBlank()) {
|
|
3734
|
+
DeviceStore.apply("glasses", "serialNumber", serialNumberLegacy)
|
|
3735
|
+
}
|
|
3700
3736
|
|
|
3701
3737
|
val versionInfoLegacy = HashMap<String, Any>()
|
|
3702
3738
|
versionInfoLegacy["appVersion"] = appVersionLegacy
|
|
@@ -3878,7 +3914,7 @@ class MentraLive : SGCManager() {
|
|
|
3878
3914
|
else -> {
|
|
3879
3915
|
// Flexible version_info parsing - handle any version_info* message
|
|
3880
3916
|
if (type.startsWith("version_info")) {
|
|
3881
|
-
Bridge.log("LIVE: Received " + type
|
|
3917
|
+
Bridge.log("LIVE: Received " + type)
|
|
3882
3918
|
|
|
3883
3919
|
// Extract all fields from JSON (except "type")
|
|
3884
3920
|
val fields = HashMap<String, Any>()
|
|
@@ -3965,6 +4001,10 @@ class MentraLive : SGCManager() {
|
|
|
3965
4001
|
fields["bt_mac_address"] as String
|
|
3966
4002
|
)
|
|
3967
4003
|
}
|
|
4004
|
+
val serialNumber = fields["serial_number"] as? String
|
|
4005
|
+
if (!serialNumber.isNullOrBlank()) {
|
|
4006
|
+
DeviceStore.apply("glasses", "serialNumber", serialNumber)
|
|
4007
|
+
}
|
|
3968
4008
|
if (fields.containsKey("system_time_ms")) {
|
|
3969
4009
|
val v = fields["system_time_ms"]
|
|
3970
4010
|
if (v is Number) {
|
|
@@ -3981,6 +4021,7 @@ class MentraLive : SGCManager() {
|
|
|
3981
4021
|
// running them per chunk is safe.
|
|
3982
4022
|
parsePeerWireCaps(json)
|
|
3983
4023
|
maybeSendWireHandshake()
|
|
4024
|
+
handleGlassesSessionId(json)
|
|
3984
4025
|
|
|
3985
4026
|
Bridge.log("LIVE: Processed version_info fields and sent to RN")
|
|
3986
4027
|
Bridge.sendVersionInfo(fields)
|
|
@@ -4332,15 +4373,7 @@ class MentraLive : SGCManager() {
|
|
|
4332
4373
|
// The glassesReady flag is reset on disconnect/reconnect, so this won't
|
|
4333
4374
|
// prevent proper reconnection
|
|
4334
4375
|
if (!glassesReady) {
|
|
4335
|
-
|
|
4336
|
-
"LIVE: 📱 Sending phone_ready to glasses - waiting for glasses_ready response"
|
|
4337
|
-
)
|
|
4338
|
-
val readyMsg = JSONObject()
|
|
4339
|
-
readyMsg.put("type", "phone_ready")
|
|
4340
|
-
readyMsg.put("timestamp", System.currentTimeMillis())
|
|
4341
|
-
|
|
4342
|
-
// Send it through our data channel
|
|
4343
|
-
sendJson(readyMsg, true)
|
|
4376
|
+
sendPhoneReady("SOC ready")
|
|
4344
4377
|
} else {
|
|
4345
4378
|
Bridge.log(
|
|
4346
4379
|
"LIVE: ✅ Glasses already marked as ready, skipping phone_ready"
|
|
@@ -4421,6 +4454,8 @@ class MentraLive : SGCManager() {
|
|
|
4421
4454
|
updateConnectionState(ConnTypes.DISCONNECTED)
|
|
4422
4455
|
glassesReady = false
|
|
4423
4456
|
glassesReadyReceived = false
|
|
4457
|
+
glassesSessionId = null
|
|
4458
|
+
readinessCompletedThisBleSession = false
|
|
4424
4459
|
}
|
|
4425
4460
|
"sr_adota" -> {
|
|
4426
4461
|
// BES chip OTA progress — K900 path (serial busy during BES flash). Emit ota_status
|
|
@@ -5542,6 +5577,8 @@ class MentraLive : SGCManager() {
|
|
|
5542
5577
|
mode: PhotoMode = PhotoMode.PHOTO,
|
|
5543
5578
|
exposureTimeNs: Long?,
|
|
5544
5579
|
durationMs: Int,
|
|
5580
|
+
zsl: Boolean? = null,
|
|
5581
|
+
mfnr: Boolean? = null,
|
|
5545
5582
|
) {
|
|
5546
5583
|
Bridge.log(
|
|
5547
5584
|
"LIVE: warmUpCamera() entry — requestId=" +
|
|
@@ -5564,6 +5601,12 @@ class MentraLive : SGCManager() {
|
|
|
5564
5601
|
if (exposureTimeNs != null && exposureTimeNs > 0L) {
|
|
5565
5602
|
json.put("exposureTimeNs", exposureTimeNs)
|
|
5566
5603
|
}
|
|
5604
|
+
if (mfnr != null) {
|
|
5605
|
+
json.put("mfnr", mfnr)
|
|
5606
|
+
}
|
|
5607
|
+
if (zsl != null) {
|
|
5608
|
+
json.put("zsl", zsl)
|
|
5609
|
+
}
|
|
5567
5610
|
json.put("durationMs", if (durationMs > 0) durationMs else 15000)
|
|
5568
5611
|
sendJson(json, true)
|
|
5569
5612
|
} catch (e: JSONException) {
|
|
@@ -7249,6 +7292,62 @@ class MentraLive : SGCManager() {
|
|
|
7249
7292
|
wireHandshakeSentGeneration = -1
|
|
7250
7293
|
}
|
|
7251
7294
|
|
|
7295
|
+
/** Sends phone_ready to (re)start the glasses-driven readiness flow. */
|
|
7296
|
+
private fun sendPhoneReady(reason: String) {
|
|
7297
|
+
Bridge.log("LIVE: 📱 Sending phone_ready to glasses ($reason) - waiting for glasses_ready response")
|
|
7298
|
+
val readyMsg = JSONObject()
|
|
7299
|
+
readyMsg.put("type", "phone_ready")
|
|
7300
|
+
readyMsg.put("timestamp", System.currentTimeMillis())
|
|
7301
|
+
sendJson(readyMsg, true)
|
|
7302
|
+
}
|
|
7303
|
+
|
|
7304
|
+
/**
|
|
7305
|
+
* Tracks the glasses process session id (`sid`) and re-runs the phone-driven readiness
|
|
7306
|
+
* flow when it changes under a live BLE link. The BES holds the link across asg_client
|
|
7307
|
+
* restarts (APK OTA, crash recovery), so a restarted glasses process is invisible to
|
|
7308
|
+
* transport state; the sid is the explicit protocol signal.
|
|
7309
|
+
*
|
|
7310
|
+
* Tri-state by design (retro-compat with pre-sid glasses builds):
|
|
7311
|
+
* - no `sid` key: legacy glasses - do nothing, today's behavior;
|
|
7312
|
+
* - sid appears where none was known on an ESTABLISHED session: the glasses just
|
|
7313
|
+
* updated from a pre-sid build and restarted - treat as a restart (this is the
|
|
7314
|
+
* upgrade OTA itself);
|
|
7315
|
+
* - sid differs from the recorded one: the glasses restarted - treat as a restart;
|
|
7316
|
+
* - same sid: same process, no action.
|
|
7317
|
+
*
|
|
7318
|
+
* A restart is handled as a LOGICAL session reset: send phone_ready immediately
|
|
7319
|
+
* (instead of waiting for the next sr_hrt heartbeat, and bypassing its stale
|
|
7320
|
+
* glassesReady suppression); the returning glasses_ready runs the full existing
|
|
7321
|
+
* remote-wire-reset flow. glassesReady/fullyBooted are deliberately left untouched -
|
|
7322
|
+
* the physical link never dropped, so the connection UI must not flap. The RN layer
|
|
7323
|
+
* is notified so the OTA coordinator can treat it as its reconnect edge.
|
|
7324
|
+
*/
|
|
7325
|
+
private fun handleGlassesSessionId(json: JSONObject) {
|
|
7326
|
+
val sid = json.optString("sid", "")
|
|
7327
|
+
if (sid.isEmpty()) return
|
|
7328
|
+
val previous = glassesSessionId
|
|
7329
|
+
if (sid == previous) return
|
|
7330
|
+
glassesSessionId = sid
|
|
7331
|
+
if (previous == null && !readinessCompletedThisBleSession) {
|
|
7332
|
+
// First sid of a fresh BLE session before readiness completes: the normal
|
|
7333
|
+
// pairing/readiness flow is already running - just record it.
|
|
7334
|
+
return
|
|
7335
|
+
}
|
|
7336
|
+
Bridge.log(
|
|
7337
|
+
"LIVE: 🔁 Glasses session changed (${previous ?: "<pre-sid build>"} -> $sid) - " +
|
|
7338
|
+
"asg restarted under a live link, re-running readiness"
|
|
7339
|
+
)
|
|
7340
|
+
sendPhoneReady("glasses session changed")
|
|
7341
|
+
try {
|
|
7342
|
+
val payload = HashMap<String, Any>()
|
|
7343
|
+
payload["previous_sid"] = previous ?: ""
|
|
7344
|
+
payload["sid"] = sid
|
|
7345
|
+
Bridge.sendTypedMessage("glasses_session_changed", payload)
|
|
7346
|
+
} catch (e: Exception) {
|
|
7347
|
+
Log.e(TAG, "Error emitting glasses_session_changed", e)
|
|
7348
|
+
}
|
|
7349
|
+
}
|
|
7350
|
+
|
|
7252
7351
|
private fun parsePeerWireCaps(json: JSONObject) {
|
|
7253
7352
|
val caps = json.optJSONObject("wire_caps") ?: return
|
|
7254
7353
|
if (caps.optBoolean("k900_le", false)) {
|
|
@@ -8887,6 +8986,9 @@ class MentraLive : SGCManager() {
|
|
|
8887
8986
|
|
|
8888
8987
|
// Send glasses-side Voice Activity Detection setting.
|
|
8889
8988
|
sendVoiceActivityDetectionSetting()
|
|
8989
|
+
|
|
8990
|
+
// Send glasses-side loudness / Barrier gate setting.
|
|
8991
|
+
sendLoudnessGateSetting()
|
|
8890
8992
|
}
|
|
8891
8993
|
|
|
8892
8994
|
override fun sendVoiceActivityDetectionSetting() {
|
|
@@ -8929,11 +9031,56 @@ class MentraLive : SGCManager() {
|
|
|
8929
9031
|
}
|
|
8930
9032
|
}
|
|
8931
9033
|
|
|
9034
|
+
override fun sendLoudnessGateSetting() {
|
|
9035
|
+
val value = DeviceStore.get("bluetooth", "loudness_gate_enabled")
|
|
9036
|
+
val enabled =
|
|
9037
|
+
if (value is Boolean) value
|
|
9038
|
+
else BluetoothSdkDefaults.LOUDNESS_GATE_ENABLED
|
|
9039
|
+
|
|
9040
|
+
Bridge.log("LIVE: 🎚️ Sending loudness/Barrier gate setting to glasses: " + enabled)
|
|
9041
|
+
|
|
9042
|
+
if (!isConnected) {
|
|
9043
|
+
Bridge.log("LIVE: Cannot send loudness gate setting - not connected")
|
|
9044
|
+
return
|
|
9045
|
+
}
|
|
9046
|
+
|
|
9047
|
+
try {
|
|
9048
|
+
val body = JSONObject()
|
|
9049
|
+
body.put("type", LOUDNESS_GATE_SWITCH_TYPE)
|
|
9050
|
+
body.put("switch", if (enabled) 1 else 0)
|
|
9051
|
+
|
|
9052
|
+
val cmdObject = JSONObject()
|
|
9053
|
+
cmdObject.put("C", "cs_swit")
|
|
9054
|
+
cmdObject.put("V", 1)
|
|
9055
|
+
cmdObject.put("B", body.toString())
|
|
9056
|
+
|
|
9057
|
+
val packedData =
|
|
9058
|
+
K900ProtocolUtils.packDataToK900(
|
|
9059
|
+
cmdObject.toString().toByteArray(StandardCharsets.UTF_8),
|
|
9060
|
+
K900ProtocolUtils.CMD_TYPE_STRING,
|
|
9061
|
+
k900LengthEndian()
|
|
9062
|
+
)
|
|
9063
|
+
if (packedData == null) {
|
|
9064
|
+
Bridge.log("LIVE: Failed to pack loudness gate setting command")
|
|
9065
|
+
return
|
|
9066
|
+
}
|
|
9067
|
+
queueData(packedData)
|
|
9068
|
+
} catch (e: JSONException) {
|
|
9069
|
+
Log.e(TAG, "Error creating loudness gate setting command", e)
|
|
9070
|
+
}
|
|
9071
|
+
}
|
|
9072
|
+
|
|
8932
9073
|
/** Send button photo settings to glasses, replaying all stored scan-tuning fields. */
|
|
8933
9074
|
override fun sendButtonPhotoSettings() {
|
|
9075
|
+
val legacyZslMfnr =
|
|
9076
|
+
DeviceStore.get(ObservableStore.BLUETOOTH_CATEGORY, "button_photo_zsl_mfnr") as Boolean?
|
|
8934
9077
|
val size = DeviceStore.get(ObservableStore.BLUETOOTH_CATEGORY, "button_photo_size") as String?
|
|
8935
|
-
val mfnr =
|
|
8936
|
-
|
|
9078
|
+
val mfnr =
|
|
9079
|
+
(DeviceStore.get(ObservableStore.BLUETOOTH_CATEGORY, "button_photo_mfnr") as Boolean?)
|
|
9080
|
+
?: legacyZslMfnr
|
|
9081
|
+
val zsl =
|
|
9082
|
+
(DeviceStore.get(ObservableStore.BLUETOOTH_CATEGORY, "button_photo_zsl") as Boolean?)
|
|
9083
|
+
?: legacyZslMfnr
|
|
8937
9084
|
val noiseReduction = DeviceStore.get(ObservableStore.BLUETOOTH_CATEGORY, "button_photo_noise_reduction") as Boolean?
|
|
8938
9085
|
val edgeEnhancement = DeviceStore.get(ObservableStore.BLUETOOTH_CATEGORY, "button_photo_edge_enhancement") as Boolean?
|
|
8939
9086
|
val ispDigitalGain = DeviceStore.get(ObservableStore.BLUETOOTH_CATEGORY, "button_photo_isp_digital_gain") as Int?
|
|
@@ -8950,7 +9097,7 @@ class MentraLive : SGCManager() {
|
|
|
8950
9097
|
|
|
8951
9098
|
/** Send camera FOV setting to glasses (K900 / Mentra Live). */
|
|
8952
9099
|
override fun sendCameraFovSetting() {
|
|
8953
|
-
var fov =
|
|
9100
|
+
var fov = 118
|
|
8954
9101
|
var roiPosition = 0
|
|
8955
9102
|
try {
|
|
8956
9103
|
val raw = DeviceStore.get("bluetooth", "camera_fov")
|
|
@@ -321,6 +321,9 @@ abstract class SGCManager {
|
|
|
321
321
|
// Voice Activity Detection
|
|
322
322
|
open fun sendVoiceActivityDetectionSetting() {}
|
|
323
323
|
|
|
324
|
+
// Mentra Live center-mic loudness / Barrier gate
|
|
325
|
+
open fun sendLoudnessGateSetting() {}
|
|
326
|
+
|
|
324
327
|
// Start/stop LC3 audio playback from glasses based on the nex_lc3_audio_playback flag.
|
|
325
328
|
open fun applyNexAudioPlaybackSetting() {}
|
|
326
329
|
|