@mentra/bluetooth-sdk 3.2.1-dev.314 → 3.3.0-beta.942
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 +72 -10
- package/android/build.gradle +15 -0
- package/android/lc3Lib/build.gradle +6 -1
- package/android/src/debug/java/com/mentra/bluetoothsdk/sgcs/NimoDiagnostics.kt +368 -0
- package/android/src/debug/java/com/mentra/bluetoothsdk/sgcs/NimoFramebufferProtocol.kt +156 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkAnalytics.kt +63 -14
- package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkAnalyticsTracker.kt +55 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkDefaults.kt +4 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkInstallId.kt +23 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkModule.kt +8 -2
- package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkUpdateCheck.kt +184 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkUsageTracker.kt +388 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/Bridge.kt +2 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/DeviceManager.kt +133 -26
- package/android/src/main/java/com/mentra/bluetoothsdk/DeviceStore.kt +37 -13
- package/android/src/main/java/com/mentra/bluetoothsdk/GeneratedChangelogCatalog.kt +3 -2
- package/android/src/main/java/com/mentra/bluetoothsdk/GeneratedReleaseMetadata.kt +6 -6
- package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothSdk.kt +227 -36
- package/android/src/main/java/com/mentra/bluetoothsdk/MentraLocalNetworkModule.kt +24 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/NetworkDiagnostics.kt +25 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/VersionInfoResponseAccumulator.kt +13 -2
- package/android/src/main/java/com/mentra/bluetoothsdk/debug/BleTraceLogger.kt +3 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/requests/DisplayRequests.kt +1 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/services/Foreground.kt +33 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/services/G2ConnectionRecovery.kt +47 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/ClassicAudioConnectionTracker.kt +4 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/ClassicAudioServiceDiscovery.kt +70 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G1.kt +2 -2
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G1TextSanitizer.kt +11 -32
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G2.kt +72 -21
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/LatinTextSanitizer.kt +39 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraLive.kt +194 -40
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraNex.kt +12 -8
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Nimo.kt +314 -395
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/NimoCanvasCodec.kt +247 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/NimoCanvasCoordinator.kt +238 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/NimoCanvasEncoder.kt +58 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/NimoCanvasSession.kt +180 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/SGCManager.kt +11 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/SelectedDeviceAddress.kt +17 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/SerializedGattCallback.kt +5 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/G2Text.kt +7 -0
- package/android/src/release/java/com/mentra/bluetoothsdk/sgcs/NimoDiagnostics.kt +20 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/BluetoothSdkAnalyticsTrackerTest.kt +58 -3
- package/android/src/test/java/com/mentra/bluetoothsdk/BluetoothSdkUpdateCheckTest.kt +124 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/BluetoothSdkUsageTrackerTest.kt +263 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/DashboardContentFormatterTest.kt +2 -2
- package/android/src/test/java/com/mentra/bluetoothsdk/DeviceIdentityPairingTest.kt +59 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/DeviceManagerSceneHandoffTest.kt +666 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/G2TextTest.kt +13 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/NetworkDiagnosticsTest.kt +55 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/ScanCompletionTest.kt +5 -1
- package/android/src/test/java/com/mentra/bluetoothsdk/VersionInfoResponseAccumulatorTest.kt +71 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/debug/BleTraceLoggerTest.kt +69 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/services/G2ConnectionRecoveryTest.kt +51 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/ClassicAudioServiceDiscoveryTest.kt +124 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/G1PairingTest.kt +105 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/G1TextSanitizerTest.kt +29 -4
- package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/G2DisplayThreadTest.kt +120 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/G2PairingTargetTest.kt +90 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/MentraLiveClassicDisconnectTest.kt +133 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/MentraLiveFileTransferSessionTest.kt +55 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/MentraLiveGattCallbackTest.kt +33 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/MentraLiveIncidentDispatchTest.kt +126 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/MentraLivePairingTargetTest.kt +65 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/MentraLiveWifiCredentialLogTest.kt +55 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/MentraNexPairingTest.kt +151 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/NimoBitmapAcceptanceTest.kt +105 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/NimoCanvasCodecTest.kt +233 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/NimoCanvasCoordinatorTest.kt +348 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/NimoCanvasSessionTest.kt +339 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/NimoMicReconnectTest.kt +93 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/NimoProtocolTest.kt +11 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/SelectedDeviceAddressTest.kt +27 -0
- package/android/src/testDebug/java/com/mentra/bluetoothsdk/sgcs/NimoDiagnosticsTest.kt +287 -0
- package/android/src/testDebug/java/com/mentra/bluetoothsdk/sgcs/NimoFramebufferProtocolTest.kt +127 -0
- package/android/src/testDebug/java/com/mentra/bluetoothsdk/sgcs/NimoLifecycleTest.kt +288 -0
- package/android/src/testDebug/java/com/mentra/bluetoothsdk/sgcs/NimoReadinessDriverTest.kt +205 -0
- package/build/BluetoothSdk.types.d.ts +16 -0
- package/build/BluetoothSdk.types.d.ts.map +1 -1
- package/build/BluetoothSdk.types.js +1 -0
- package/build/BluetoothSdk.types.js.map +1 -1
- package/build/_private/BluetoothSdkModule.d.ts +4 -0
- package/build/_private/BluetoothSdkModule.d.ts.map +1 -1
- package/build/_private/BluetoothSdkModule.js +5 -1
- package/build/_private/BluetoothSdkModule.js.map +1 -1
- package/build/_private/MentraLocalNetworkModule.d.ts +23 -0
- package/build/_private/MentraLocalNetworkModule.d.ts.map +1 -1
- package/build/_private/MentraLocalNetworkModule.js.map +1 -1
- package/build/_private/nativeLogConsole.d.ts +19 -0
- package/build/_private/nativeLogConsole.d.ts.map +1 -1
- package/build/_private/nativeLogConsole.js +12 -0
- package/build/_private/nativeLogConsole.js.map +1 -1
- package/build/generated/changelogCatalog.d.ts +5 -2
- package/build/generated/changelogCatalog.d.ts.map +1 -1
- package/build/generated/changelogCatalog.js +6 -2
- package/build/generated/changelogCatalog.js.map +1 -1
- package/build/generated/releaseMetadata.js +6 -6
- package/build/generated/releaseMetadata.js.map +1 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +1 -0
- package/build/index.js.map +1 -1
- package/build/ota-transport/index.d.ts +4 -0
- package/build/ota-transport/index.d.ts.map +1 -1
- package/build/ota-transport/index.js +2 -0
- package/build/ota-transport/index.js.map +1 -1
- package/ios/BluetoothSdkModule.swift +17 -4
- package/ios/MentraBluetoothSDK.podspec +3 -2
- package/ios/Source/BluetoothBackgroundMode.swift +32 -0
- package/ios/Source/BluetoothSdkDefaults.swift +7 -2
- package/ios/Source/Bridge.swift +15 -2
- package/ios/Source/DeviceManager.swift +102 -66
- package/ios/Source/DeviceStore.swift +26 -7
- package/ios/Source/GeneratedChangelogCatalog.swift +3 -2
- package/ios/Source/GeneratedReleaseMetadata.swift +6 -6
- package/ios/Source/MentraBluetoothSDK.swift +313 -54
- package/ios/Source/audio/GlassesMicWatchdog.swift +33 -0
- package/ios/Source/internal/BluetoothAvailability.swift +6 -0
- package/ios/Source/internal/BluetoothSdkAnalytics.swift +87 -13
- package/ios/Source/internal/BluetoothSdkAnalyticsTracker.swift +54 -1
- package/ios/Source/internal/BluetoothSdkInstallId.swift +21 -0
- package/ios/Source/internal/BluetoothSdkUpdateCheck.swift +223 -0
- package/ios/Source/internal/BluetoothSdkUsageTracker.swift +428 -0
- package/ios/Source/requests/DisplayRequests.swift +1 -1
- package/ios/Source/requests/VersionInfoResponseAccumulator.swift +11 -1
- package/ios/Source/sgcs/Ar99.swift +1 -2
- package/ios/Source/sgcs/G1.swift +68 -38
- package/ios/Source/sgcs/G1ConnectionTarget.swift +13 -0
- package/ios/Source/sgcs/G2.swift +261 -247
- package/ios/Source/sgcs/G2ConnectionSession.swift +101 -0
- package/ios/Source/sgcs/Mach1.swift +19 -7
- package/ios/Source/sgcs/MentraLive.swift +272 -197
- package/ios/Source/sgcs/MentraLiveFileTransferSession.swift +137 -0
- package/ios/Source/sgcs/MentraLivePairingAdvertisement.swift +47 -0
- package/ios/Source/sgcs/MentraNex.swift +8 -31
- package/ios/Source/sgcs/NexConnectionTarget.swift +15 -0
- package/ios/Source/sgcs/Nimo.swift +280 -408
- package/ios/Source/sgcs/NimoCanvasCodec.swift +272 -0
- package/ios/Source/sgcs/NimoCanvasCoordinator.swift +233 -0
- package/ios/Source/sgcs/NimoCanvasImage.swift +53 -0
- package/ios/Source/sgcs/NimoCanvasSession.swift +166 -0
- package/ios/Source/sgcs/NimoDiscovery.swift +49 -0
- package/ios/Source/sgcs/SGCManager.swift +25 -1
- package/ios/Source/sgcs/VuzixConnectionTarget.swift +15 -0
- package/ios/Source/status/DeviceStatus.swift +1 -0
- package/ios/Source/stt/SherpaOnnxTranscriber.swift +49 -189
- package/ios/Source/utils/AudioSessionMonitor.swift +44 -8
- package/ios/Source/utils/DemandDrivenAudioWorker.swift +150 -0
- package/ios/Source/utils/G1Text.swift +2 -24
- package/ios/Source/utils/G1TextSanitizer.swift +27 -0
- package/ios/Source/utils/G2Text.swift +11 -0
- package/ios/Source/utils/LatinTextSanitizer.swift +48 -0
- package/ios/Tests/BluetoothAudioRouteTests.swift +26 -0
- package/ios/Tests/BluetoothBackgroundModeTests.swift +32 -0
- package/ios/Tests/BluetoothSdkAnalyticsTrackerTests.swift +54 -3
- package/ios/Tests/BluetoothSdkUpdateCheckTests.swift +153 -0
- package/ios/Tests/BluetoothSdkUsageTrackerTests.swift +296 -0
- package/ios/Tests/DashboardContentFormatterTests.swift +2 -2
- package/ios/Tests/DashboardRenderingTests.swift +31 -0
- package/ios/Tests/DemandDrivenAudioWorkerTests.swift +157 -0
- package/ios/Tests/DeviceIdentityPairingTests.swift +49 -0
- package/ios/Tests/DeviceManagerReconnectTests.swift +362 -0
- package/ios/Tests/G1PairingTests.swift +162 -0
- package/ios/Tests/G1TextSanitizerTests.swift +32 -0
- package/ios/Tests/G2ConnectionSessionTests.swift +172 -0
- package/ios/Tests/G2TextTests.swift +10 -0
- package/ios/Tests/IncidentLogDispatchTests.swift +111 -0
- package/ios/Tests/MentraLiveFileTransferSessionTests.swift +47 -0
- package/ios/Tests/MentraLiveReconnectPolicyTests.swift +58 -0
- package/ios/Tests/MentraLiveSessionReadinessTests.swift +163 -0
- package/ios/Tests/NimoCanvasCodecTests.swift +217 -0
- package/ios/Tests/NimoCanvasSessionTests.swift +181 -0
- package/ios/Tests/NimoCanvasTransportTests.swift +186 -0
- package/ios/Tests/NimoDeviceCommandsTests.swift +16 -0
- package/ios/Tests/NimoDiscoveryTests.swift +79 -0
- package/ios/Tests/SGCConnectionTargetTests.swift +48 -0
- package/ios/Tests/VersionInfoCommandTests.swift +194 -0
- package/ios/Tests/VersionInfoResponseAccumulatorTests.swift +65 -0
- package/ios/Tests/VideoRecordingCommandTests.swift +81 -0
- package/ios/Tests/WifiCredentialLoggingTests.swift +42 -0
- package/package.json +2 -2
- package/plugin/build/withIos.d.ts +2 -0
- package/plugin/build/withIos.js +12 -1
- package/src/BluetoothSdk.types.ts +15 -0
- package/src/_private/BluetoothSdkModule.ts +10 -1
- package/src/_private/MentraLocalNetworkModule.ts +28 -0
- package/src/_private/nativeLogConsole.ts +19 -0
- package/src/generated/changelogCatalog.ts +6 -2
- package/src/generated/releaseMetadata.ts +6 -6
- package/src/index.ts +1 -0
- package/src/ota-transport/index.ts +10 -1
|
@@ -34,6 +34,7 @@ import android.os.SystemClock
|
|
|
34
34
|
import com.mentra.bluetoothsdk.utils.NativeLog as Log
|
|
35
35
|
import androidx.core.app.ActivityCompat
|
|
36
36
|
import com.mentra.bluetoothsdk.BluetoothSdkDefaults
|
|
37
|
+
import com.mentra.bluetoothsdk.BluetoothSdkException
|
|
37
38
|
import com.mentra.bluetoothsdk.Bridge
|
|
38
39
|
import com.mentra.bluetoothsdk.DeviceManager
|
|
39
40
|
import com.mentra.bluetoothsdk.PhotoRequest
|
|
@@ -141,6 +142,7 @@ class MentraLive : SGCManager() {
|
|
|
141
142
|
private const val LC3_FRAME_SIZE = 40
|
|
142
143
|
private const val VOICE_ACTIVITY_DETECTION_SWITCH_TYPE = 8
|
|
143
144
|
private const val LOUDNESS_GATE_SWITCH_TYPE = 10
|
|
145
|
+
private const val AUTO_POWER_OFF_SWITCH_TYPE = 11
|
|
144
146
|
// Mic tuning field names, matching the BES cs_mictun body.
|
|
145
147
|
private val MIC_TUNING_FIELDS =
|
|
146
148
|
listOf("gain", "open", "close", "attack", "hang", "sp_open", "sp_close", "sp_hold")
|
|
@@ -644,6 +646,10 @@ class MentraLive : SGCManager() {
|
|
|
644
646
|
private var a2dpProfile: BluetoothA2dp? = null
|
|
645
647
|
private var isA2dpProxyRegistered = false
|
|
646
648
|
private var a2dpConnectAttempts = 0
|
|
649
|
+
private val classicAudioServiceDiscovery =
|
|
650
|
+
ClassicAudioServiceDiscovery(handler, CONNECTION_TIMEOUT_MS.toLong()) {
|
|
651
|
+
Bridge.log("LIVE: Classic: $it")
|
|
652
|
+
}
|
|
647
653
|
|
|
648
654
|
private data class OutgoingBleCommandTraceInfo(
|
|
649
655
|
val commandType: String,
|
|
@@ -776,7 +782,8 @@ class MentraLive : SGCManager() {
|
|
|
776
782
|
}
|
|
777
783
|
|
|
778
784
|
// Inner class to track incoming file transfers
|
|
779
|
-
|
|
785
|
+
internal class FileTransferSession(var fileName: String, var fileSize: Int, flags: Int = 0) {
|
|
786
|
+
private val dynamicPayload = (flags and 0x0002) != 0
|
|
780
787
|
// NOTE: fileSize may be "fake" (inflated) due to BES firmware workaround
|
|
781
788
|
var actualPackSize =
|
|
782
789
|
0 // Actual pack size from first received packet (for BES lie detection)
|
|
@@ -805,9 +812,8 @@ class MentraLive : SGCManager() {
|
|
|
805
812
|
* Recalculate total packets based on actual pack size from received packet. Called when
|
|
806
813
|
* first packet is received to handle variable pack sizes.
|
|
807
814
|
*
|
|
808
|
-
*
|
|
809
|
-
*
|
|
810
|
-
* so, totalPackets = fileSize / 400, regardless of actual pack size.
|
|
815
|
+
* Dynamic-payload headers carry the true byte count. Only legacy small packs use
|
|
816
|
+
* the inflated-size workaround (multiples of BES_HARDCODED_PACK_SIZE).
|
|
811
817
|
*/
|
|
812
818
|
fun recalculateTotalPackets(actualPackSize: Int) {
|
|
813
819
|
if (actualPackSize <= 0 || actualPackSize > K900ProtocolUtils.FILE_PACK_SIZE) {
|
|
@@ -818,8 +824,8 @@ class MentraLive : SGCManager() {
|
|
|
818
824
|
|
|
819
825
|
// Detect BES lie: if fileSize is exact multiple of 400, glasses used the lie strategy
|
|
820
826
|
val isBesLie =
|
|
821
|
-
(fileSize % BES_HARDCODED_PACK_SIZE == 0) &&
|
|
822
|
-
(actualPackSize
|
|
827
|
+
!dynamicPayload && (fileSize % BES_HARDCODED_PACK_SIZE == 0) &&
|
|
828
|
+
(actualPackSize < BES_HARDCODED_PACK_SIZE)
|
|
823
829
|
|
|
824
830
|
val newTotalPackets: Int
|
|
825
831
|
if (isBesLie) {
|
|
@@ -857,6 +863,10 @@ class MentraLive : SGCManager() {
|
|
|
857
863
|
}
|
|
858
864
|
|
|
859
865
|
fun addPacket(index: Int, data: ByteArray): Boolean {
|
|
866
|
+
if (dynamicPayload && (index < 0 || index >= totalPackets || actualPackSize <= 0 ||
|
|
867
|
+
data.size != minOf(actualPackSize, fileSize - index * actualPackSize))) {
|
|
868
|
+
return false
|
|
869
|
+
}
|
|
860
870
|
if (index >= 0 && index < totalPackets && !receivedPackets.containsKey(index)) {
|
|
861
871
|
receivedPackets[index] = data
|
|
862
872
|
|
|
@@ -1703,6 +1713,7 @@ class MentraLive : SGCManager() {
|
|
|
1703
1713
|
readinessCompletedThisBleSession = false
|
|
1704
1714
|
glassesReadyReceived = false
|
|
1705
1715
|
ctkdInitiatedThisGattSession = false
|
|
1716
|
+
classicAudioServiceDiscovery.cancel()
|
|
1706
1717
|
audioConnected = false
|
|
1707
1718
|
notificationsEnabled = false
|
|
1708
1719
|
currentMtu = 23
|
|
@@ -1978,6 +1989,7 @@ class MentraLive : SGCManager() {
|
|
|
1978
1989
|
readinessCompletedThisBleSession = false
|
|
1979
1990
|
glassesReadyReceived = false
|
|
1980
1991
|
ctkdInitiatedThisGattSession = false
|
|
1992
|
+
classicAudioServiceDiscovery.cancel()
|
|
1981
1993
|
audioConnected = false
|
|
1982
1994
|
notificationsEnabled = false
|
|
1983
1995
|
processSendQueueRunnable?.let { handler.removeCallbacks(it) }
|
|
@@ -2147,7 +2159,7 @@ class MentraLive : SGCManager() {
|
|
|
2147
2159
|
// than scanning).
|
|
2148
2160
|
// Falls back to name-based scan if no address is saved.
|
|
2149
2161
|
val lastDeviceAddress =
|
|
2150
|
-
|
|
2162
|
+
selectedConnectionAddress()
|
|
2151
2163
|
if (lastDeviceAddress != null &&
|
|
2152
2164
|
!lastDeviceAddress.isEmpty() &&
|
|
2153
2165
|
bluetoothAdapter != null
|
|
@@ -2345,6 +2357,7 @@ class MentraLive : SGCManager() {
|
|
|
2345
2357
|
registerBondingReceiver()
|
|
2346
2358
|
bondingRetryCount = 0
|
|
2347
2359
|
ctkdInitiatedThisGattSession = false
|
|
2360
|
+
classicAudioServiceDiscovery.cancel()
|
|
2348
2361
|
Bridge.log(
|
|
2349
2362
|
"LIVE: CTKD: BLE GATT up - deferring Classic/A2DP until notifications ready"
|
|
2350
2363
|
)
|
|
@@ -2393,6 +2406,7 @@ class MentraLive : SGCManager() {
|
|
|
2393
2406
|
// Reset audio pairing flags
|
|
2394
2407
|
glassesReadyReceived = false
|
|
2395
2408
|
ctkdInitiatedThisGattSession = false
|
|
2409
|
+
classicAudioServiceDiscovery.cancel()
|
|
2396
2410
|
audioConnected = false
|
|
2397
2411
|
|
|
2398
2412
|
notificationsEnabled = false
|
|
@@ -2455,6 +2469,7 @@ class MentraLive : SGCManager() {
|
|
|
2455
2469
|
glassesReadyReceived = false
|
|
2456
2470
|
audioConnected = false
|
|
2457
2471
|
ctkdInitiatedThisGattSession = false
|
|
2472
|
+
classicAudioServiceDiscovery.cancel()
|
|
2458
2473
|
|
|
2459
2474
|
notificationsEnabled = false
|
|
2460
2475
|
|
|
@@ -3366,10 +3381,12 @@ class MentraLive : SGCManager() {
|
|
|
3366
3381
|
sendJson(json, wakeup, true)
|
|
3367
3382
|
}
|
|
3368
3383
|
|
|
3369
|
-
private fun sendJson(
|
|
3384
|
+
private fun sendJson(
|
|
3385
|
+
json: JSONObject?, wakeup: Boolean, bridgeLogging: Boolean,
|
|
3386
|
+
sessionGeneration: Long = bleSessionGeneration.get(),
|
|
3387
|
+
): Boolean {
|
|
3370
3388
|
if (json != null) {
|
|
3371
3389
|
try {
|
|
3372
|
-
val sessionGeneration = bleSessionGeneration.get()
|
|
3373
3390
|
if (buildNumberInt < 5) {
|
|
3374
3391
|
val jsonStr = json.toString()
|
|
3375
3392
|
// Bridge.log("LIVE: 📤 Sending JSON with esoteric message ID: " + jsonStr);
|
|
@@ -4137,7 +4154,9 @@ class MentraLive : SGCManager() {
|
|
|
4137
4154
|
"controllerId" to json.opt("controllerId"), "streamId" to json.opt("streamId"),
|
|
4138
4155
|
"probeId" to json.opt("probeId"))
|
|
4139
4156
|
com.mentra.bluetoothsdk.streaming.StreamControllerProbe.response(values)?.let {
|
|
4140
|
-
|
|
4157
|
+
// BES buffers non-waking commands when MTK enters standby, even while
|
|
4158
|
+
// its streaming CPU lease is held. The current probe needs a live reply.
|
|
4159
|
+
sendJson(JSONObject(it), true)
|
|
4141
4160
|
}
|
|
4142
4161
|
}
|
|
4143
4162
|
"pong" ->
|
|
@@ -5548,7 +5567,9 @@ class MentraLive : SGCManager() {
|
|
|
5548
5567
|
if (bodyObj != null) {
|
|
5549
5568
|
val type = bodyObj.optInt("type", -1)
|
|
5550
5569
|
val value = bodyObj.optInt("switch", -1)
|
|
5551
|
-
|
|
5570
|
+
// K900 replies carry result in "S"; 0 is RC_SUCCESS.
|
|
5571
|
+
val resultCode = json.optInt("S", -1)
|
|
5572
|
+
handleSwitchStatus(type, value, System.currentTimeMillis(), resultCode)
|
|
5552
5573
|
}
|
|
5553
5574
|
} catch (e: Exception) {
|
|
5554
5575
|
Log.e(TAG, "Error parsing sr_swit response", e)
|
|
@@ -5578,7 +5599,13 @@ class MentraLive : SGCManager() {
|
|
|
5578
5599
|
try {
|
|
5579
5600
|
val bodyObj = optK900Body(json)
|
|
5580
5601
|
if (bodyObj != null) {
|
|
5581
|
-
|
|
5602
|
+
val extras = HashMap<String, Any>()
|
|
5603
|
+
if (bodyObj.has("elapsed_ms")) extras["elapsedMs"] = bodyObj.optInt("elapsed_ms")
|
|
5604
|
+
if (bodyObj.has("timeout_ms")) extras["timeoutMs"] = bodyObj.optInt("timeout_ms")
|
|
5605
|
+
if (bodyObj.has("enabled")) extras["enabled"] = bodyObj.optInt("enabled") != 0
|
|
5606
|
+
if (bodyObj.has("armed")) extras["armed"] = bodyObj.optInt("armed") != 0
|
|
5607
|
+
if (bodyObj.has("inhibited")) extras["inhibited"] = bodyObj.optInt("inhibited") != 0
|
|
5608
|
+
Bridge.sendWearState(bodyObj.optInt("on", 0) != 0, extras)
|
|
5582
5609
|
}
|
|
5583
5610
|
} catch (e: Exception) {
|
|
5584
5611
|
Log.e(TAG, "Error parsing sr_wrst response", e)
|
|
@@ -5933,8 +5960,20 @@ class MentraLive : SGCManager() {
|
|
|
5933
5960
|
Bridge.sendWearTuningState(state)
|
|
5934
5961
|
}
|
|
5935
5962
|
|
|
5936
|
-
private fun handleSwitchStatus(
|
|
5963
|
+
private fun handleSwitchStatus(
|
|
5964
|
+
switchType: Int,
|
|
5965
|
+
switchValue: Int,
|
|
5966
|
+
timestamp: Long,
|
|
5967
|
+
resultCode: Int = -1
|
|
5968
|
+
) {
|
|
5937
5969
|
Bridge.sendSwitchStatus(switchType, switchValue, timestamp)
|
|
5970
|
+
if (switchType == AUTO_POWER_OFF_SWITCH_TYPE) {
|
|
5971
|
+
val ok = resultCode == 0
|
|
5972
|
+
Bridge.log(
|
|
5973
|
+
"LIVE: 🔋 auto power-off sr_swit reply type=$switchType switch=$switchValue" +
|
|
5974
|
+
" result=$resultCode ok=$ok"
|
|
5975
|
+
)
|
|
5976
|
+
}
|
|
5938
5977
|
if (switchType == VOICE_ACTIVITY_DETECTION_SWITCH_TYPE &&
|
|
5939
5978
|
(switchValue == 0 || switchValue == 1)
|
|
5940
5979
|
) {
|
|
@@ -6048,6 +6087,17 @@ class MentraLive : SGCManager() {
|
|
|
6048
6087
|
}
|
|
6049
6088
|
|
|
6050
6089
|
override fun sendIncidentId(incidentId: String, apiBaseUrl: String?) {
|
|
6090
|
+
if (!isConnected || bluetoothGatt == null || txCharacteristic == null) {
|
|
6091
|
+
throw BluetoothSdkException(
|
|
6092
|
+
"glasses_not_connected", "Cannot request incident logs because the glasses BLE link is not ready.",
|
|
6093
|
+
)
|
|
6094
|
+
}
|
|
6095
|
+
// The JS token sync may happen after glasses_ready. Use only its current native store value.
|
|
6096
|
+
val coreToken = DeviceStore.get("bluetooth", "core_token") as? String
|
|
6097
|
+
if (coreToken.isNullOrBlank()) {
|
|
6098
|
+
throw BluetoothSdkException("core_token_unavailable", "Cannot request incident logs without the current Core access token.")
|
|
6099
|
+
}
|
|
6100
|
+
val sessionGeneration = bleSessionGeneration.get()
|
|
6051
6101
|
try {
|
|
6052
6102
|
var base = if (apiBaseUrl != null) apiBaseUrl.trim() else ""
|
|
6053
6103
|
if (base.isEmpty()) {
|
|
@@ -6060,13 +6110,25 @@ class MentraLive : SGCManager() {
|
|
|
6060
6110
|
bleIncidentLogRelays[lKey] =
|
|
6061
6111
|
BleIncidentLogRelay(lKey, incidentId, base, BleIncidentLogKind.LOGCAT)
|
|
6062
6112
|
|
|
6113
|
+
val auth = JSONObject()
|
|
6114
|
+
auth.put("type", "auth_token")
|
|
6115
|
+
auth.put("coreToken", coreToken)
|
|
6116
|
+
auth.put("timestamp", System.currentTimeMillis())
|
|
6063
6117
|
val json = JSONObject()
|
|
6064
6118
|
json.put("type", "upload_incident_logs")
|
|
6065
6119
|
json.put("incidentId", incidentId)
|
|
6066
6120
|
json.put("apiBaseUrl", base)
|
|
6067
|
-
|
|
6121
|
+
// Wake ASG with its current token before the upload command enters the same FIFO.
|
|
6122
|
+
if (!sendJson(auth, true, true, sessionGeneration) ||
|
|
6123
|
+
sessionGeneration != bleSessionGeneration.get() ||
|
|
6124
|
+
!isConnected || bluetoothGatt == null || txCharacteristic == null ||
|
|
6125
|
+
!sendJson(json, true, true, sessionGeneration) || sessionGeneration != bleSessionGeneration.get()) {
|
|
6126
|
+
bleIncidentLogRelays.remove(bKey)
|
|
6127
|
+
bleIncidentLogRelays.remove(lKey)
|
|
6128
|
+
throw BluetoothSdkException("incident_dispatch_failed", "Could not queue the glasses incident-log request.")
|
|
6129
|
+
}
|
|
6068
6130
|
Bridge.log(
|
|
6069
|
-
"LIVE:
|
|
6131
|
+
"LIVE: Queued incidentId to glasses for log upload: " +
|
|
6070
6132
|
incidentId +
|
|
6071
6133
|
" (BLE relay keys " +
|
|
6072
6134
|
bKey +
|
|
@@ -6075,7 +6137,7 @@ class MentraLive : SGCManager() {
|
|
|
6075
6137
|
")"
|
|
6076
6138
|
)
|
|
6077
6139
|
} catch (e: JSONException) {
|
|
6078
|
-
|
|
6140
|
+
throw BluetoothSdkException("incident_dispatch_failed", "Could not create the glasses incident-log request.", e)
|
|
6079
6141
|
}
|
|
6080
6142
|
}
|
|
6081
6143
|
|
|
@@ -6140,7 +6202,8 @@ class MentraLive : SGCManager() {
|
|
|
6140
6202
|
val json = JSONObject()
|
|
6141
6203
|
json.put("type", "request_version")
|
|
6142
6204
|
requestId?.let { json.put("request_id", it) }
|
|
6143
|
-
|
|
6205
|
+
// Wake ASG so the version request and its response can finish after idle.
|
|
6206
|
+
sendJson(json, true)
|
|
6144
6207
|
Bridge.log("LIVE: 📱 Requesting version info from glasses")
|
|
6145
6208
|
} catch (e: JSONException) {
|
|
6146
6209
|
Log.e(TAG, "📱 Error creating request_version command", e)
|
|
@@ -6602,6 +6665,14 @@ class MentraLive : SGCManager() {
|
|
|
6602
6665
|
return false
|
|
6603
6666
|
}
|
|
6604
6667
|
|
|
6668
|
+
private fun selectedConnectionAddress(): String? = SelectedDeviceAddress.resolve(
|
|
6669
|
+
savedDeviceName,
|
|
6670
|
+
DeviceStore.get("bluetooth", "pending_device_name") as? String,
|
|
6671
|
+
DeviceStore.get("bluetooth", "pending_device_address") as? String,
|
|
6672
|
+
DeviceStore.get("bluetooth", "device_name") as? String,
|
|
6673
|
+
DeviceStore.get("bluetooth", "device_address") as? String,
|
|
6674
|
+
)
|
|
6675
|
+
|
|
6605
6676
|
fun connectToSmartGlasses() {
|
|
6606
6677
|
if (postGattLifecycle { connectToSmartGlasses() }) return
|
|
6607
6678
|
if (pairingYieldActive) {
|
|
@@ -6636,13 +6707,7 @@ class MentraLive : SGCManager() {
|
|
|
6636
6707
|
// var context = Bridge.getContext();
|
|
6637
6708
|
// SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
|
|
6638
6709
|
// String lastDeviceAddress = prefs.getString(PREF_DEVICE_NAME, null);
|
|
6639
|
-
val
|
|
6640
|
-
(DeviceStore.get("bluetooth", "pending_device_address") as String?)?.takeIf {
|
|
6641
|
-
it.isNotEmpty()
|
|
6642
|
-
}
|
|
6643
|
-
val lastDeviceAddress =
|
|
6644
|
-
pendingAddress
|
|
6645
|
-
?: (DeviceStore.get("bluetooth", "device_address") as String?)
|
|
6710
|
+
val lastDeviceAddress = selectedConnectionAddress()
|
|
6646
6711
|
|
|
6647
6712
|
if (lastDeviceAddress != null && lastDeviceAddress.length > 0) {
|
|
6648
6713
|
// Connect to last known device if available
|
|
@@ -7178,7 +7243,7 @@ class MentraLive : SGCManager() {
|
|
|
7178
7243
|
BluetoothDevice.BOND_BONDED ->
|
|
7179
7244
|
run {
|
|
7180
7245
|
Bridge.log(
|
|
7181
|
-
"LIVE: CTKD:
|
|
7246
|
+
"LIVE: CTKD: Successfully bonded - checking Classic audio services"
|
|
7182
7247
|
)
|
|
7183
7248
|
markPairingTiming(
|
|
7184
7249
|
"ctkd_bonded",
|
|
@@ -7197,7 +7262,7 @@ class MentraLive : SGCManager() {
|
|
|
7197
7262
|
// path does this at GATT connect).
|
|
7198
7263
|
// markAudioConnected / sendAudioConnected fire
|
|
7199
7264
|
// when the A2DP path completes.
|
|
7200
|
-
|
|
7265
|
+
prepareClassicAudio(device)
|
|
7201
7266
|
|
|
7202
7267
|
// Safety net: if glasses_ready somehow missed
|
|
7203
7268
|
// setting fullyBooted, catch up here.
|
|
@@ -7234,6 +7299,7 @@ class MentraLive : SGCManager() {
|
|
|
7234
7299
|
}
|
|
7235
7300
|
}
|
|
7236
7301
|
BluetoothDevice.BOND_NONE -> {
|
|
7302
|
+
classicAudioServiceDiscovery.cancel()
|
|
7237
7303
|
Bridge.log(
|
|
7238
7304
|
"LIVE: CTKD: ❌ Bonding failed or removed for device"
|
|
7239
7305
|
)
|
|
@@ -7355,6 +7421,10 @@ class MentraLive : SGCManager() {
|
|
|
7355
7421
|
classicAudioReceiver =
|
|
7356
7422
|
object : BroadcastReceiver() {
|
|
7357
7423
|
override fun onReceive(context: Context?, intent: Intent?) {
|
|
7424
|
+
if (intent?.action == BluetoothDevice.ACTION_UUID) {
|
|
7425
|
+
intent.bluetoothDeviceExtra()?.let { classicAudioServiceDiscovery.onUuids(it) }
|
|
7426
|
+
return
|
|
7427
|
+
}
|
|
7358
7428
|
if (intent?.action != BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED &&
|
|
7359
7429
|
intent?.action != BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED) return
|
|
7360
7430
|
val device = intent.bluetoothDeviceExtra() ?: return
|
|
@@ -7371,6 +7441,7 @@ class MentraLive : SGCManager() {
|
|
|
7371
7441
|
try {
|
|
7372
7442
|
val filter =
|
|
7373
7443
|
IntentFilter().apply {
|
|
7444
|
+
addAction(BluetoothDevice.ACTION_UUID)
|
|
7374
7445
|
addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED)
|
|
7375
7446
|
addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)
|
|
7376
7447
|
}
|
|
@@ -7495,10 +7566,10 @@ class MentraLive : SGCManager() {
|
|
|
7495
7566
|
when (device.bondState) {
|
|
7496
7567
|
BluetoothDevice.BOND_BONDED -> {
|
|
7497
7568
|
Bridge.log(
|
|
7498
|
-
"LIVE: CTKD: Device is already bonded -
|
|
7569
|
+
"LIVE: CTKD: Device is already bonded - checking Classic audio services"
|
|
7499
7570
|
)
|
|
7500
7571
|
markPairingTiming("ctkd_already_bonded")
|
|
7501
|
-
|
|
7572
|
+
prepareClassicAudio(device)
|
|
7502
7573
|
}
|
|
7503
7574
|
BluetoothDevice.BOND_BONDING -> {
|
|
7504
7575
|
// Secure pairing firmware may request SMP as soon as GATT connects.
|
|
@@ -7557,6 +7628,19 @@ class MentraLive : SGCManager() {
|
|
|
7557
7628
|
return classicAudioConnectionTracker.connected
|
|
7558
7629
|
}
|
|
7559
7630
|
|
|
7631
|
+
private fun prepareClassicAudio(device: BluetoothDevice) {
|
|
7632
|
+
if (!canConnectClassicAudio(device)) return
|
|
7633
|
+
val gatt = bluetoothGatt
|
|
7634
|
+
classicAudioServiceDiscovery.connectWhenDiscovered(device) {
|
|
7635
|
+
if (bluetoothGatt === gatt && canConnectClassicAudio(device)) connectA2dpProfile(device)
|
|
7636
|
+
}
|
|
7637
|
+
}
|
|
7638
|
+
|
|
7639
|
+
private fun canConnectClassicAudio(device: BluetoothDevice): Boolean =
|
|
7640
|
+
!isKilled && !pairingYieldActive && isConnected && notificationsEnabled &&
|
|
7641
|
+
ctkdInitiatedThisGattSession && connectedDevice?.address == device.address &&
|
|
7642
|
+
device.bondState == BluetoothDevice.BOND_BONDED
|
|
7643
|
+
|
|
7560
7644
|
/** A2DP profile service listener for connecting to already-bonded devices */
|
|
7561
7645
|
private val a2dpServiceListener: BluetoothProfile.ServiceListener =
|
|
7562
7646
|
object : BluetoothProfile.ServiceListener {
|
|
@@ -7605,7 +7689,9 @@ class MentraLive : SGCManager() {
|
|
|
7605
7689
|
Bridge.log("LIVE: A2DP: Skipping connectA2dpWithProxy — SGC destroyed")
|
|
7606
7690
|
return
|
|
7607
7691
|
}
|
|
7608
|
-
if (
|
|
7692
|
+
if (device == null || !canConnectClassicAudio(device) ||
|
|
7693
|
+
!ClassicAudioServiceDiscovery.hasAudioSink(device)) return
|
|
7694
|
+
if (a2dpProfile == null) {
|
|
7609
7695
|
Bridge.log("LIVE: A2DP: Cannot connect - proxy or device is null")
|
|
7610
7696
|
return
|
|
7611
7697
|
}
|
|
@@ -7655,14 +7741,11 @@ class MentraLive : SGCManager() {
|
|
|
7655
7741
|
"LIVE: A2DP: Retry $a2dpConnectAttempts/$A2DP_CONNECT_MAX_ATTEMPTS in " +
|
|
7656
7742
|
"${A2DP_CONNECT_RETRY_MS}ms ($reason)"
|
|
7657
7743
|
)
|
|
7744
|
+
val gatt = bluetoothGatt
|
|
7658
7745
|
handler.postDelayed(
|
|
7659
7746
|
Runnable {
|
|
7660
|
-
if (
|
|
7661
|
-
|
|
7662
|
-
}
|
|
7663
|
-
val target = connectedDevice ?: return@Runnable
|
|
7664
|
-
if (a2dpProfile != null) {
|
|
7665
|
-
connectA2dpWithProxy(target)
|
|
7747
|
+
if (bluetoothGatt === gatt && canConnectClassicAudio(device)) {
|
|
7748
|
+
connectA2dpWithProxy(device)
|
|
7666
7749
|
}
|
|
7667
7750
|
},
|
|
7668
7751
|
A2DP_CONNECT_RETRY_MS
|
|
@@ -7882,6 +7965,7 @@ class MentraLive : SGCManager() {
|
|
|
7882
7965
|
|
|
7883
7966
|
// Mark as killed to prevent reconnection attempts
|
|
7884
7967
|
isKilled = true
|
|
7968
|
+
classicAudioServiceDiscovery.cancel()
|
|
7885
7969
|
explicitConnectByName = false
|
|
7886
7970
|
pairingYieldActive = false
|
|
7887
7971
|
pairingYieldAwaitingReclaim = false
|
|
@@ -7903,7 +7987,11 @@ class MentraLive : SGCManager() {
|
|
|
7903
7987
|
|
|
7904
7988
|
// Tear down Classic (A2DP/HFP) before GATT close. Closing only the proxy left the
|
|
7905
7989
|
// phone's system Bluetooth UI showing Mentra Live still connected.
|
|
7906
|
-
|
|
7990
|
+
// BLE loss clears connectedDevice while the OS audio profiles can remain connected.
|
|
7991
|
+
// Use this session's existing Classic target before resetting the tracker below.
|
|
7992
|
+
val classicDevice = connectedDevice ?: classicAudioConnectionTracker.targetForDisconnect()?.let {
|
|
7993
|
+
bluetoothAdapter?.getRemoteDevice(it)
|
|
7994
|
+
}
|
|
7907
7995
|
disconnectClassicProfiles(classicDevice)
|
|
7908
7996
|
closeA2dpProxy()
|
|
7909
7997
|
classicAudioConnectionTracker.reset()
|
|
@@ -7980,6 +8068,7 @@ class MentraLive : SGCManager() {
|
|
|
7980
8068
|
glassesReadyReceived = false
|
|
7981
8069
|
audioConnected = false
|
|
7982
8070
|
ctkdInitiatedThisGattSession = false
|
|
8071
|
+
classicAudioServiceDiscovery.cancel()
|
|
7983
8072
|
DeviceStore.apply("glasses", "fullyBooted", false)
|
|
7984
8073
|
updateConnectionState(ConnTypes.DISCONNECTED)
|
|
7985
8074
|
|
|
@@ -9467,7 +9556,11 @@ class MentraLive : SGCManager() {
|
|
|
9467
9556
|
}
|
|
9468
9557
|
} else {
|
|
9469
9558
|
// Normal single message transmission
|
|
9470
|
-
transportLog(
|
|
9559
|
+
transportLog(
|
|
9560
|
+
"LIVE: Sending data to glasses: " +
|
|
9561
|
+
loggableOutgoingPayload(wireData, commandTraceInfo.commandType),
|
|
9562
|
+
bridgeLogging
|
|
9563
|
+
)
|
|
9471
9564
|
|
|
9472
9565
|
// Pack the data using the centralized utility with the negotiated endianness
|
|
9473
9566
|
val packedData =
|
|
@@ -9769,6 +9862,18 @@ class MentraLive : SGCManager() {
|
|
|
9769
9862
|
}
|
|
9770
9863
|
}
|
|
9771
9864
|
|
|
9865
|
+
/**
|
|
9866
|
+
* Credentials are sent unchanged but never logged; the BLE trace redacts their fields.
|
|
9867
|
+
*/
|
|
9868
|
+
private fun loggableOutgoingPayload(payload: String, commandType: String): String =
|
|
9869
|
+
try {
|
|
9870
|
+
val json = JSONObject(payload)
|
|
9871
|
+
if (json.has("password") || json.has("coreToken")) "<$commandType with credentials omitted>"
|
|
9872
|
+
else payload
|
|
9873
|
+
} catch (_: JSONException) {
|
|
9874
|
+
payload
|
|
9875
|
+
}
|
|
9876
|
+
|
|
9772
9877
|
private fun summarizeOutgoingMessage(payload: String?): String {
|
|
9773
9878
|
if (payload == null || payload.isEmpty()) {
|
|
9774
9879
|
return "type=unknown, requestId=none, appId=none, mode=none, transferMethod=none, bleImgId=none, exposureTimeNs=none, iso=none, mId=none"
|
|
@@ -10193,7 +10298,7 @@ class MentraLive : SGCManager() {
|
|
|
10193
10298
|
if (incidentRelay.session == null) {
|
|
10194
10299
|
activeFileTransfers.remove(packetInfo.fileName)
|
|
10195
10300
|
incidentRelay.session =
|
|
10196
|
-
FileTransferSession(packetInfo.fileName, packetInfo.fileSize)
|
|
10301
|
+
FileTransferSession(packetInfo.fileName, packetInfo.fileSize, packetInfo.flags)
|
|
10197
10302
|
incidentRelay.session!!.recalculateTotalPackets(packetInfo.packSize)
|
|
10198
10303
|
Bridge.log(
|
|
10199
10304
|
"LIVE: 📦 Started BLE incident log transfer: " +
|
|
@@ -10244,7 +10349,7 @@ class MentraLive : SGCManager() {
|
|
|
10244
10349
|
// Get or create session for this transfer
|
|
10245
10350
|
if (photoTransfer.session == null) {
|
|
10246
10351
|
photoTransfer.session =
|
|
10247
|
-
FileTransferSession(packetInfo.fileName, packetInfo.fileSize)
|
|
10352
|
+
FileTransferSession(packetInfo.fileName, packetInfo.fileSize, packetInfo.flags)
|
|
10248
10353
|
// Recalculate total packets based on actual pack size (handles variable MTU)
|
|
10249
10354
|
photoTransfer.session!!.recalculateTotalPackets(packetInfo.packSize)
|
|
10250
10355
|
Bridge.log(
|
|
@@ -10330,7 +10435,7 @@ class MentraLive : SGCManager() {
|
|
|
10330
10435
|
var session = activeFileTransfers[packetInfo.fileName]
|
|
10331
10436
|
if (session == null) {
|
|
10332
10437
|
// New file transfer
|
|
10333
|
-
session = FileTransferSession(packetInfo.fileName, packetInfo.fileSize)
|
|
10438
|
+
session = FileTransferSession(packetInfo.fileName, packetInfo.fileSize, packetInfo.flags)
|
|
10334
10439
|
// Recalculate total packets based on actual pack size (handles variable MTU)
|
|
10335
10440
|
session.recalculateTotalPackets(packetInfo.packSize)
|
|
10336
10441
|
activeFileTransfers[packetInfo.fileName] = session
|
|
@@ -10825,6 +10930,9 @@ class MentraLive : SGCManager() {
|
|
|
10825
10930
|
// Send glasses-side loudness / Barrier gate setting.
|
|
10826
10931
|
sendLoudnessGateSetting()
|
|
10827
10932
|
|
|
10933
|
+
// Send glasses-side auto power-off setting.
|
|
10934
|
+
sendAutoPowerOffSetting()
|
|
10935
|
+
|
|
10828
10936
|
// Send mic tuning. With nothing authorized this sends a reset, which is
|
|
10829
10937
|
// what returns a freshly connected pair of glasses to stock behaviour.
|
|
10830
10938
|
sendMicTuningSetting()
|
|
@@ -11108,6 +11216,52 @@ class MentraLive : SGCManager() {
|
|
|
11108
11216
|
}
|
|
11109
11217
|
}
|
|
11110
11218
|
|
|
11219
|
+
override fun sendAutoPowerOffSetting() {
|
|
11220
|
+
val value = DeviceStore.get("bluetooth", "auto_power_off_enabled")
|
|
11221
|
+
val enabled =
|
|
11222
|
+
if (value is Boolean) value
|
|
11223
|
+
else BluetoothSdkDefaults.AUTO_POWER_OFF_ENABLED
|
|
11224
|
+
|
|
11225
|
+
Bridge.log(
|
|
11226
|
+
"LIVE: 🔋 Sending auto power-off setting to glasses: enabled=$enabled" +
|
|
11227
|
+
" (cs_swit type=$AUTO_POWER_OFF_SWITCH_TYPE)"
|
|
11228
|
+
)
|
|
11229
|
+
|
|
11230
|
+
if (!isConnected) {
|
|
11231
|
+
Bridge.log("LIVE: Cannot send auto power-off setting - not connected")
|
|
11232
|
+
return
|
|
11233
|
+
}
|
|
11234
|
+
|
|
11235
|
+
try {
|
|
11236
|
+
val body = JSONObject()
|
|
11237
|
+
body.put("type", AUTO_POWER_OFF_SWITCH_TYPE)
|
|
11238
|
+
body.put("switch", if (enabled) 1 else 0)
|
|
11239
|
+
|
|
11240
|
+
val cmdObject = JSONObject()
|
|
11241
|
+
cmdObject.put("C", "cs_swit")
|
|
11242
|
+
cmdObject.put("V", 1)
|
|
11243
|
+
cmdObject.put("B", body.toString())
|
|
11244
|
+
|
|
11245
|
+
val packedData =
|
|
11246
|
+
K900ProtocolUtils.packDataToK900(
|
|
11247
|
+
cmdObject.toString().toByteArray(StandardCharsets.UTF_8),
|
|
11248
|
+
K900ProtocolUtils.CMD_TYPE_STRING,
|
|
11249
|
+
k900LengthEndian()
|
|
11250
|
+
)
|
|
11251
|
+
if (packedData == null) {
|
|
11252
|
+
Bridge.log("LIVE: Failed to pack auto power-off setting command")
|
|
11253
|
+
return
|
|
11254
|
+
}
|
|
11255
|
+
queueData(packedData)
|
|
11256
|
+
Bridge.log(
|
|
11257
|
+
"LIVE: 🔋 Queued auto power-off cs_swit type=$AUTO_POWER_OFF_SWITCH_TYPE" +
|
|
11258
|
+
" switch=${if (enabled) 1 else 0}"
|
|
11259
|
+
)
|
|
11260
|
+
} catch (e: JSONException) {
|
|
11261
|
+
Log.e(TAG, "Error creating auto power-off setting command", e)
|
|
11262
|
+
}
|
|
11263
|
+
}
|
|
11264
|
+
|
|
11111
11265
|
/** Send button photo settings to glasses, replaying all stored scan-tuning fields. */
|
|
11112
11266
|
override fun sendButtonPhotoSettings() {
|
|
11113
11267
|
val legacyZslMfnr =
|
|
@@ -208,7 +208,7 @@ class MentraNex : SGCManager() {
|
|
|
208
208
|
|
|
209
209
|
// If we already have saved device names for main...
|
|
210
210
|
if (name != null && preferredMainDeviceId != null) {
|
|
211
|
-
if (
|
|
211
|
+
if (name != preferredMainDeviceId) {
|
|
212
212
|
return // Not a matching device
|
|
213
213
|
}
|
|
214
214
|
}
|
|
@@ -230,7 +230,6 @@ class MentraNex : SGCManager() {
|
|
|
230
230
|
type = DeviceTypes.NEX
|
|
231
231
|
hasMic = true
|
|
232
232
|
DeviceStore.apply("glasses", "micEnabled", false)
|
|
233
|
-
preferredMainDeviceId = DeviceManager.getInstance().deviceName
|
|
234
233
|
|
|
235
234
|
// Initialize LC3 audio player
|
|
236
235
|
lc3AudioPlayer = Lc3Player(context)
|
|
@@ -1315,23 +1314,28 @@ class MentraNex : SGCManager() {
|
|
|
1315
1314
|
}
|
|
1316
1315
|
|
|
1317
1316
|
private fun connectToSmartGlasses() {
|
|
1318
|
-
val
|
|
1319
|
-
val deviceAddress =
|
|
1317
|
+
val target = preferredMainDeviceId?.takeIf { it.isNotBlank() } ?: return
|
|
1318
|
+
val deviceAddress = SelectedDeviceAddress.resolve(
|
|
1319
|
+
target,
|
|
1320
|
+
DeviceStore.get("bluetooth", "pending_device_name") as? String,
|
|
1321
|
+
DeviceStore.get("bluetooth", "pending_device_address") as? String,
|
|
1322
|
+
DeviceStore.get("bluetooth", "device_name") as? String,
|
|
1323
|
+
DeviceStore.get("bluetooth", "device_address") as? String,
|
|
1324
|
+
)
|
|
1320
1325
|
|
|
1321
1326
|
// Register bonding receiver
|
|
1322
1327
|
Bridge.log("connectToSmartGlasses start")
|
|
1323
|
-
Bridge.log("
|
|
1324
|
-
preferredMainDeviceId = DeviceManager.getInstance().deviceName
|
|
1328
|
+
Bridge.log("Connecting to selected Nex: $target address: $deviceAddress")
|
|
1325
1329
|
if (!bluetoothAdapter.isEnabled) {
|
|
1326
1330
|
return
|
|
1327
1331
|
}
|
|
1328
1332
|
when {
|
|
1329
|
-
|
|
1333
|
+
deviceAddress != null -> {
|
|
1330
1334
|
stopScan()
|
|
1331
1335
|
mainDevice = bluetoothAdapter.getRemoteDevice(deviceAddress)
|
|
1332
1336
|
mainTaskHandler?.sendEmptyMessageDelayed(MAIN_TASK_HANDLER_CODE_RECONNECT_DEVICE, 0)
|
|
1333
1337
|
}
|
|
1334
|
-
savedNexMainAddress != null -> {
|
|
1338
|
+
savedNexMainName == target && savedNexMainAddress != null -> {
|
|
1335
1339
|
mainDevice = bluetoothAdapter.getRemoteDevice(savedNexMainAddress)
|
|
1336
1340
|
mainTaskHandler?.sendEmptyMessageDelayed(MAIN_TASK_HANDLER_CODE_RECONNECT_DEVICE, 0)
|
|
1337
1341
|
}
|