@mentra/bluetooth-sdk 0.1.13 → 0.1.16
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 +18 -8
- package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkModule.kt +48 -48
- package/android/src/main/java/com/mentra/bluetoothsdk/Bridge.kt +5 -20
- package/android/src/main/java/com/mentra/bluetoothsdk/DeviceManager.kt +360 -269
- package/android/src/main/java/com/mentra/bluetoothsdk/DeviceStore.kt +3 -7
- package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothSdk.kt +152 -72
- package/android/src/main/java/com/mentra/bluetoothsdk/OtaManifest.kt +12 -12
- package/android/src/main/java/com/mentra/bluetoothsdk/camera/CameraModels.kt +25 -11
- 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/events/BluetoothEvents.kt +1 -22
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G1.kt +51 -8
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G2.kt +1184 -800
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Mach1.kt +1 -5
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraLive.kt +423 -117
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraNex.kt +4 -6
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Nimo.kt +2532 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/SGCManager.kt +17 -5
- 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/android/src/main/java/com/mentra/bluetoothsdk/types/DeviceModels.kt +5 -3
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/Constants.kt +2 -1
- package/android/src/test/java/com/mentra/bluetoothsdk/camera/PhotoRequestTest.kt +60 -1
- package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/NimoProtocolTest.kt +333 -0
- package/build/BluetoothSdk.types.d.ts +50 -24
- 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/_internal.d.ts +4 -4
- package/build/_internal.js +4 -4
- package/build/_internal.js.map +1 -1
- package/build/_private/BluetoothSdkModule.d.ts +14 -8
- package/build/_private/BluetoothSdkModule.d.ts.map +1 -1
- package/build/_private/BluetoothSdkModule.js +4 -3
- package/build/_private/BluetoothSdkModule.js.map +1 -1
- package/build/_private/cameraRequestPayload.d.ts +4 -0
- package/build/_private/cameraRequestPayload.d.ts.map +1 -0
- package/build/_private/cameraRequestPayload.js +25 -0
- package/build/_private/cameraRequestPayload.js.map +1 -0
- package/build/_private/photoRequestPayload.d.ts.map +1 -1
- package/build/_private/photoRequestPayload.js +11 -3
- 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 +70 -61
- package/build/index.js.map +1 -1
- package/ios/BluetoothSdkModule.swift +51 -28
- package/ios/Source/Bridge.swift +4 -16
- package/ios/Source/DeviceManager.swift +76 -22
- package/ios/Source/DeviceStore.swift +0 -4
- package/ios/Source/MentraBluetoothSDK.swift +144 -78
- package/ios/Source/OtaManifest.swift +13 -13
- package/ios/Source/camera/CameraModels.swift +82 -20
- package/ios/Source/controllers/ControllerManager.swift +1 -2
- package/ios/Source/controllers/R1.swift +1 -2
- package/ios/Source/errors/{BluetoothError.swift → BluetoothSdkError.swift} +1 -1
- package/ios/Source/events/BluetoothEvents.swift +5 -27
- package/ios/Source/internal/BluetoothAvailability.swift +5 -5
- package/ios/Source/sgcs/Frame.swift +0 -2
- package/ios/Source/sgcs/G1.swift +52 -4
- package/ios/Source/sgcs/G2.swift +643 -204
- package/ios/Source/sgcs/Mach1.swift +1 -3
- package/ios/Source/sgcs/MentraLive.swift +64 -77
- package/ios/Source/sgcs/MentraNex.swift +1 -3
- package/ios/Source/sgcs/Nimo.swift +1928 -0
- package/ios/Source/sgcs/SGCManager.swift +16 -10
- 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/ios/Source/stt/STTTools.swift +1 -1
- package/ios/Source/types/DeviceModels.swift +5 -0
- package/ios/Source/utils/Constants.swift +2 -0
- package/package.json +6 -1
- package/src/BluetoothSdk.types.ts +54 -28
- package/src/_internal.ts +4 -4
- package/src/_private/BluetoothSdkModule.ts +20 -11
- package/src/_private/cameraRequestPayload.ts +29 -0
- package/src/_private/photoRequestPayload.ts +12 -3
- package/src/index.ts +77 -67
|
@@ -32,6 +32,7 @@ import com.mentra.bluetoothsdk.BluetoothSdkDefaults
|
|
|
32
32
|
import com.mentra.bluetoothsdk.Bridge
|
|
33
33
|
import com.mentra.bluetoothsdk.DeviceManager
|
|
34
34
|
import com.mentra.bluetoothsdk.PhotoRequest
|
|
35
|
+
import com.mentra.bluetoothsdk.PhotoSize
|
|
35
36
|
import com.mentra.bluetoothsdk.DeviceStore
|
|
36
37
|
import com.mentra.bluetoothsdk.ObservableStore
|
|
37
38
|
import com.mentra.bluetoothsdk.debug.BleTraceLogger
|
|
@@ -157,6 +158,8 @@ class MentraLive : SGCManager() {
|
|
|
157
158
|
|
|
158
159
|
// Rate limiting - minimum delay between BLE characteristic writes
|
|
159
160
|
private const val MIN_SEND_DELAY_MS = 160L // 160ms minimum delay (increased from 100ms)
|
|
161
|
+
private const val SIGNIFICANT_BLE_TRACE_DELAY_MS = 250L
|
|
162
|
+
private const val SIGNIFICANT_BLE_TRACE_QUEUE_SIZE = 5
|
|
160
163
|
|
|
161
164
|
// File transfer management
|
|
162
165
|
private const val FILE_SAVE_DIR = "MentraLive_Images"
|
|
@@ -237,7 +240,35 @@ class MentraLive : SGCManager() {
|
|
|
237
240
|
private var a2dpProfile: BluetoothA2dp? = null
|
|
238
241
|
private var isA2dpProxyRegistered = false
|
|
239
242
|
|
|
240
|
-
private
|
|
243
|
+
private data class OutgoingBleCommandTraceInfo(
|
|
244
|
+
val commandType: String,
|
|
245
|
+
val requestId: String?,
|
|
246
|
+
val appId: String?,
|
|
247
|
+
val messageId: Long?
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
private data class BleWriteTrace(
|
|
251
|
+
val sequence: Long,
|
|
252
|
+
val commandType: String,
|
|
253
|
+
val requestId: String?,
|
|
254
|
+
val appId: String?,
|
|
255
|
+
val messageId: Long?,
|
|
256
|
+
val chunkId: String?,
|
|
257
|
+
val chunkIndex: Int?,
|
|
258
|
+
val totalChunks: Int?,
|
|
259
|
+
val payloadBytes: Int?,
|
|
260
|
+
val packedBytes: Int,
|
|
261
|
+
val wakeup: Boolean,
|
|
262
|
+
val chunked: Boolean,
|
|
263
|
+
val queuedAtMs: Long
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
private data class QueuedBleWrite(val data: ByteArray, val trace: BleWriteTrace?)
|
|
267
|
+
|
|
268
|
+
private var sendQueue = ConcurrentLinkedQueue<QueuedBleWrite>()
|
|
269
|
+
private val bleWriteTraceSequence = AtomicLong(1)
|
|
270
|
+
private var inFlightBleWriteTrace: BleWriteTrace? = null
|
|
271
|
+
private var inFlightBleWriteStartedAtMs = 0L
|
|
241
272
|
// Queue for serializing BLE descriptor writes (only one GATT operation at a time)
|
|
242
273
|
private val pendingDescriptorWrites = ConcurrentLinkedQueue<BluetoothGattDescriptor>()
|
|
243
274
|
private var isDescriptorWriteInProgress = false
|
|
@@ -735,8 +766,7 @@ class MentraLive : SGCManager() {
|
|
|
735
766
|
}
|
|
736
767
|
// Drop cached version fields from the previous BLE session so the next version_info
|
|
737
768
|
// repopulates RN. Otherwise a stale build (e.g. 38) can remain while ASG is still 36,
|
|
738
|
-
// and the phone-side OTA check will
|
|
739
|
-
// ota_update_available.
|
|
769
|
+
// and the phone-side OTA manifest check will compare against the wrong build.
|
|
740
770
|
DeviceStore.apply("glasses", "buildNumber", "")
|
|
741
771
|
DeviceStore.apply("glasses", "appVersion", "")
|
|
742
772
|
DeviceStore.apply("glasses", "besFirmwareVersion", "")
|
|
@@ -1672,11 +1702,20 @@ class MentraLive : SGCManager() {
|
|
|
1672
1702
|
characteristic: BluetoothGattCharacteristic,
|
|
1673
1703
|
status: Int
|
|
1674
1704
|
) {
|
|
1705
|
+
val trace = inFlightBleWriteTrace
|
|
1706
|
+
val callbackAtMs = System.currentTimeMillis()
|
|
1707
|
+
val callbackDelayMs =
|
|
1708
|
+
if (inFlightBleWriteStartedAtMs > 0L)
|
|
1709
|
+
callbackAtMs - inFlightBleWriteStartedAtMs
|
|
1710
|
+
else null
|
|
1711
|
+
inFlightBleWriteTrace = null
|
|
1712
|
+
inFlightBleWriteStartedAtMs = 0L
|
|
1713
|
+
|
|
1675
1714
|
if (status == BluetoothGatt.GATT_SUCCESS) {
|
|
1676
1715
|
// Bridge.log("LIVE: Characteristic write successful");
|
|
1677
1716
|
|
|
1678
1717
|
// Calculate time since last send to enforce rate limiting
|
|
1679
|
-
val currentTimeMs =
|
|
1718
|
+
val currentTimeMs = callbackAtMs
|
|
1680
1719
|
val timeSinceLastSendMs = currentTimeMs - lastSendTimeMs
|
|
1681
1720
|
val nextProcessDelayMs: Long
|
|
1682
1721
|
|
|
@@ -1691,9 +1730,34 @@ class MentraLive : SGCManager() {
|
|
|
1691
1730
|
}
|
|
1692
1731
|
|
|
1693
1732
|
// Schedule the next queue processing with appropriate delay
|
|
1733
|
+
logBleWriteTrace(
|
|
1734
|
+
"write_callback",
|
|
1735
|
+
trace,
|
|
1736
|
+
mapOf(
|
|
1737
|
+
"status" to status,
|
|
1738
|
+
"success" to true,
|
|
1739
|
+
"callbackDelayMs" to callbackDelayMs,
|
|
1740
|
+
"timeSinceLastSendMs" to timeSinceLastSendMs,
|
|
1741
|
+
"nextProcessDelayMs" to nextProcessDelayMs,
|
|
1742
|
+
"queueSize" to sendQueue.size,
|
|
1743
|
+
"characteristicUuid" to characteristic.uuid.toString()
|
|
1744
|
+
)
|
|
1745
|
+
)
|
|
1694
1746
|
handler.postDelayed(processSendQueueRunnable!!, nextProcessDelayMs)
|
|
1695
1747
|
} else {
|
|
1696
1748
|
Log.e(TAG, "Characteristic write failed with status: " + status)
|
|
1749
|
+
logBleWriteTrace(
|
|
1750
|
+
"write_callback",
|
|
1751
|
+
trace,
|
|
1752
|
+
mapOf(
|
|
1753
|
+
"status" to status,
|
|
1754
|
+
"success" to false,
|
|
1755
|
+
"callbackDelayMs" to callbackDelayMs,
|
|
1756
|
+
"retryDelayMs" to 500L,
|
|
1757
|
+
"queueSize" to sendQueue.size,
|
|
1758
|
+
"characteristicUuid" to characteristic.uuid.toString()
|
|
1759
|
+
)
|
|
1760
|
+
)
|
|
1697
1761
|
// If write fails, try again with a longer delay
|
|
1698
1762
|
handler.postDelayed(processSendQueueRunnable!!, 500L)
|
|
1699
1763
|
}
|
|
@@ -2101,13 +2165,22 @@ class MentraLive : SGCManager() {
|
|
|
2101
2165
|
Bridge.log(
|
|
2102
2166
|
"LIVE: Rate limiting: Waiting " + remainingDelayMs + "ms before next BLE send"
|
|
2103
2167
|
)
|
|
2168
|
+
logBleWriteTrace(
|
|
2169
|
+
"rate_limited",
|
|
2170
|
+
sendQueue.peek()?.trace,
|
|
2171
|
+
mapOf(
|
|
2172
|
+
"remainingDelayMs" to remainingDelayMs,
|
|
2173
|
+
"timeSinceLastSendMs" to timeSinceLastSendMs,
|
|
2174
|
+
"queueSize" to sendQueue.size
|
|
2175
|
+
)
|
|
2176
|
+
)
|
|
2104
2177
|
handler.postDelayed(processSendQueueRunnable!!, remainingDelayMs)
|
|
2105
2178
|
return
|
|
2106
2179
|
}
|
|
2107
2180
|
|
|
2108
2181
|
// Send the next item from the queue
|
|
2109
|
-
val
|
|
2110
|
-
if (
|
|
2182
|
+
val queuedWrite = sendQueue.poll()
|
|
2183
|
+
if (queuedWrite != null) {
|
|
2111
2184
|
// Update last send time before sending
|
|
2112
2185
|
lastSendTimeMs = currentTimeMs
|
|
2113
2186
|
Bridge.log(
|
|
@@ -2117,28 +2190,78 @@ class MentraLive : SGCManager() {
|
|
|
2117
2190
|
timeSinceLastSendMs +
|
|
2118
2191
|
"ms"
|
|
2119
2192
|
)
|
|
2120
|
-
|
|
2193
|
+
logBleWriteTrace(
|
|
2194
|
+
"dequeued",
|
|
2195
|
+
queuedWrite.trace,
|
|
2196
|
+
mapOf(
|
|
2197
|
+
"queueDelayMs" to
|
|
2198
|
+
(queuedWrite.trace?.let { currentTimeMs - it.queuedAtMs }),
|
|
2199
|
+
"timeSinceLastSendMs" to timeSinceLastSendMs,
|
|
2200
|
+
"queueSizeAfterPoll" to sendQueue.size
|
|
2201
|
+
)
|
|
2202
|
+
)
|
|
2203
|
+
sendDataInternal(queuedWrite)
|
|
2121
2204
|
}
|
|
2122
2205
|
}
|
|
2123
2206
|
|
|
2124
2207
|
/** Send data through BLE */
|
|
2125
|
-
private fun sendDataInternal(
|
|
2126
|
-
if (!isConnected || bluetoothGatt == null || txCharacteristic == null ||
|
|
2208
|
+
private fun sendDataInternal(write: QueuedBleWrite?) {
|
|
2209
|
+
if (!isConnected || bluetoothGatt == null || txCharacteristic == null || write == null) {
|
|
2127
2210
|
return
|
|
2128
2211
|
}
|
|
2129
2212
|
|
|
2130
2213
|
try {
|
|
2131
|
-
|
|
2132
|
-
|
|
2214
|
+
val writeStartedAtMs = System.currentTimeMillis()
|
|
2215
|
+
txCharacteristic!!.value = write.data
|
|
2216
|
+
inFlightBleWriteTrace = write.trace
|
|
2217
|
+
inFlightBleWriteStartedAtMs = writeStartedAtMs
|
|
2218
|
+
val writeAccepted = bluetoothGatt!!.writeCharacteristic(txCharacteristic)
|
|
2219
|
+
logBleWriteTrace(
|
|
2220
|
+
"write_call",
|
|
2221
|
+
write.trace,
|
|
2222
|
+
mapOf(
|
|
2223
|
+
"writeAccepted" to writeAccepted,
|
|
2224
|
+
"currentMtu" to currentMtu,
|
|
2225
|
+
"writeType" to txCharacteristic!!.writeType,
|
|
2226
|
+
"queueSize" to sendQueue.size,
|
|
2227
|
+
"characteristicUuid" to txCharacteristic!!.uuid.toString()
|
|
2228
|
+
)
|
|
2229
|
+
)
|
|
2230
|
+
if (!writeAccepted) {
|
|
2231
|
+
inFlightBleWriteTrace = null
|
|
2232
|
+
inFlightBleWriteStartedAtMs = 0L
|
|
2233
|
+
}
|
|
2133
2234
|
} catch (e: Exception) {
|
|
2134
2235
|
Log.e(TAG, "Error sending data via BLE", e)
|
|
2236
|
+
logBleWriteTrace(
|
|
2237
|
+
"write_exception",
|
|
2238
|
+
write.trace,
|
|
2239
|
+
mapOf(
|
|
2240
|
+
"errorClass" to e.javaClass.simpleName,
|
|
2241
|
+
"errorMessage" to (e.message ?: "unknown")
|
|
2242
|
+
)
|
|
2243
|
+
)
|
|
2244
|
+
inFlightBleWriteTrace = null
|
|
2245
|
+
inFlightBleWriteStartedAtMs = 0L
|
|
2135
2246
|
}
|
|
2136
2247
|
}
|
|
2137
2248
|
|
|
2138
2249
|
/** Queue data to be sent */
|
|
2139
|
-
private fun queueData(data: ByteArray?) {
|
|
2250
|
+
private fun queueData(data: ByteArray?, trace: BleWriteTrace? = null) {
|
|
2140
2251
|
if (data != null) {
|
|
2141
|
-
|
|
2252
|
+
val queuedTrace =
|
|
2253
|
+
trace?.copy(
|
|
2254
|
+
queuedAtMs =
|
|
2255
|
+
if (trace.queuedAtMs > 0L)
|
|
2256
|
+
trace.queuedAtMs
|
|
2257
|
+
else System.currentTimeMillis()
|
|
2258
|
+
)
|
|
2259
|
+
sendQueue.add(QueuedBleWrite(data, queuedTrace))
|
|
2260
|
+
logBleChunkTrace(
|
|
2261
|
+
"queued",
|
|
2262
|
+
queuedTrace,
|
|
2263
|
+
mapOf("queueSizeAfterAdd" to sendQueue.size)
|
|
2264
|
+
)
|
|
2142
2265
|
// Bridge.log("LIVE: 📋 Added " + data.length + " to send queue - New queue size: " +
|
|
2143
2266
|
// sendQueue.size());
|
|
2144
2267
|
|
|
@@ -2836,6 +2959,12 @@ class MentraLive : SGCManager() {
|
|
|
2836
2959
|
} catch (e: JSONException) {
|
|
2837
2960
|
Log.e(TAG, "Error converting photo status to Map", e)
|
|
2838
2961
|
}
|
|
2962
|
+
"camera_status" ->
|
|
2963
|
+
try {
|
|
2964
|
+
Bridge.sendCameraStatus(jsonObjectToMap(json))
|
|
2965
|
+
} catch (e: JSONException) {
|
|
2966
|
+
Log.e(TAG, "Error converting camera status to Map", e)
|
|
2967
|
+
}
|
|
2839
2968
|
"stream_status" -> {
|
|
2840
2969
|
// Process streaming status update from ASG client
|
|
2841
2970
|
Bridge.log("LIVE: Received stream status update from glasses: " + json.toString())
|
|
@@ -3041,47 +3170,6 @@ class MentraLive : SGCManager() {
|
|
|
3041
3170
|
(if (success) "SUCCESS" else "FAILED")
|
|
3042
3171
|
)
|
|
3043
3172
|
}
|
|
3044
|
-
"ota_update_available" -> {
|
|
3045
|
-
// Process OTA update available notification from glasses (background mode)
|
|
3046
|
-
Bridge.log("LIVE: 📱 Received ota_update_available from glasses")
|
|
3047
|
-
Bridge.log("LIVE: 📱 OTA update available: " + json.toString())
|
|
3048
|
-
try {
|
|
3049
|
-
val otaVersionCode = json.optLong("version_code", 0)
|
|
3050
|
-
val otaVersionName = json.optString("version_name", "")
|
|
3051
|
-
val otaTotalSize = json.optLong("total_size", 0)
|
|
3052
|
-
|
|
3053
|
-
// Parse updates array
|
|
3054
|
-
val updates: MutableList<String> = ArrayList()
|
|
3055
|
-
if (json.has("updates")) {
|
|
3056
|
-
val updatesArray = json.getJSONArray("updates")
|
|
3057
|
-
for (i in 0 until updatesArray.length()) {
|
|
3058
|
-
updates.add(updatesArray.getString(i))
|
|
3059
|
-
}
|
|
3060
|
-
}
|
|
3061
|
-
|
|
3062
|
-
Bridge.log(
|
|
3063
|
-
"LIVE: 📱 OTA available - version: " +
|
|
3064
|
-
otaVersionName +
|
|
3065
|
-
" (" +
|
|
3066
|
-
otaVersionCode +
|
|
3067
|
-
"), updates: " +
|
|
3068
|
-
updates +
|
|
3069
|
-
", size: " +
|
|
3070
|
-
otaTotalSize +
|
|
3071
|
-
" bytes"
|
|
3072
|
-
)
|
|
3073
|
-
|
|
3074
|
-
// Send to React Native
|
|
3075
|
-
Bridge.sendOtaUpdateAvailable(
|
|
3076
|
-
otaVersionCode,
|
|
3077
|
-
otaVersionName,
|
|
3078
|
-
updates,
|
|
3079
|
-
otaTotalSize
|
|
3080
|
-
)
|
|
3081
|
-
} catch (e: JSONException) {
|
|
3082
|
-
Log.e(TAG, "Error parsing ota_update_available", e)
|
|
3083
|
-
}
|
|
3084
|
-
}
|
|
3085
3173
|
"ota_start_ack" -> {
|
|
3086
3174
|
// Glasses acknowledged receipt of ota_start — phone can cancel its retry timer
|
|
3087
3175
|
Bridge.log("LIVE: 📱 Received ota_start_ack from glasses")
|
|
@@ -4660,7 +4748,7 @@ class MentraLive : SGCManager() {
|
|
|
4660
4748
|
* When [otaVersionUrl] is non-null it is sent as the `ota_version_url` field so the glasses
|
|
4661
4749
|
* download from that manifest; asg_client's OtaCommandHandler reads and validates that field
|
|
4662
4750
|
* (it must be an http(s) URL). A null url omits the field, leaving the glasses to fall back to
|
|
4663
|
-
* their
|
|
4751
|
+
* their default version manifest.
|
|
4664
4752
|
*/
|
|
4665
4753
|
fun sendOtaStart(otaVersionUrl: String? = null) {
|
|
4666
4754
|
try {
|
|
@@ -4689,18 +4777,6 @@ class MentraLive : SGCManager() {
|
|
|
4689
4777
|
}
|
|
4690
4778
|
}
|
|
4691
4779
|
|
|
4692
|
-
fun sendOtaRetryVersionCheck() {
|
|
4693
|
-
try {
|
|
4694
|
-
val json = JSONObject()
|
|
4695
|
-
json.put("type", "ota_retry_version_check")
|
|
4696
|
-
json.put("timestamp", System.currentTimeMillis())
|
|
4697
|
-
sendJson(json, true)
|
|
4698
|
-
Bridge.log("LIVE: ⏰ Sending ota_retry_version_check command to glasses")
|
|
4699
|
-
} catch (e: JSONException) {
|
|
4700
|
-
Log.e(TAG, "⏰ Error creating ota_retry_version_check command", e)
|
|
4701
|
-
}
|
|
4702
|
-
}
|
|
4703
|
-
|
|
4704
4780
|
/**
|
|
4705
4781
|
* Request version info from glasses. Glasses will respond with version_info message containing
|
|
4706
4782
|
* build number, firmware version, etc.
|
|
@@ -5202,12 +5278,10 @@ class MentraLive : SGCManager() {
|
|
|
5202
5278
|
|
|
5203
5279
|
override fun requestPhoto(request: PhotoRequest) {
|
|
5204
5280
|
val requestId = request.requestId
|
|
5205
|
-
val appId = request.appId
|
|
5206
5281
|
val size = request.size.value
|
|
5207
5282
|
val webhookUrl = request.webhookUrl
|
|
5208
5283
|
val authToken = request.authToken
|
|
5209
5284
|
val compress = request.compress.value
|
|
5210
|
-
val flash = request.flash
|
|
5211
5285
|
val save = request.save
|
|
5212
5286
|
val sound = request.sound
|
|
5213
5287
|
val exposureTimeNs = request.exposureTimeNs
|
|
@@ -5216,8 +5290,6 @@ class MentraLive : SGCManager() {
|
|
|
5216
5290
|
Bridge.log(
|
|
5217
5291
|
"LIVE: Requesting photo: " +
|
|
5218
5292
|
requestId +
|
|
5219
|
-
" for app: " +
|
|
5220
|
-
appId +
|
|
5221
5293
|
" with size: " +
|
|
5222
5294
|
size +
|
|
5223
5295
|
", webhookUrl: " +
|
|
@@ -5226,8 +5298,6 @@ class MentraLive : SGCManager() {
|
|
|
5226
5298
|
(if (hasAuthToken) "***" else "none") +
|
|
5227
5299
|
", compress=" +
|
|
5228
5300
|
compress +
|
|
5229
|
-
", flash=" +
|
|
5230
|
-
flash +
|
|
5231
5301
|
", save=" +
|
|
5232
5302
|
save +
|
|
5233
5303
|
", sound=" +
|
|
@@ -5243,16 +5313,13 @@ class MentraLive : SGCManager() {
|
|
|
5243
5313
|
)
|
|
5244
5314
|
Bridge.log(
|
|
5245
5315
|
"LIVE: PHOTO PIPELINE [5/6] requestPhoto() entry — requestId=" +
|
|
5246
|
-
requestId
|
|
5247
|
-
", appId=" +
|
|
5248
|
-
appId
|
|
5316
|
+
requestId
|
|
5249
5317
|
)
|
|
5250
5318
|
|
|
5251
5319
|
try {
|
|
5252
5320
|
val json = JSONObject()
|
|
5253
5321
|
json.put("type", "take_photo")
|
|
5254
5322
|
json.put("requestId", requestId)
|
|
5255
|
-
json.put("appId", appId)
|
|
5256
5323
|
if (webhookUrl != null && !webhookUrl.isEmpty()) {
|
|
5257
5324
|
json.put("webhookUrl", webhookUrl)
|
|
5258
5325
|
}
|
|
@@ -5267,7 +5334,6 @@ class MentraLive : SGCManager() {
|
|
|
5267
5334
|
} else {
|
|
5268
5335
|
json.put("compress", "none")
|
|
5269
5336
|
}
|
|
5270
|
-
json.put("flash", flash)
|
|
5271
5337
|
json.put("save", save)
|
|
5272
5338
|
json.put("sound", sound)
|
|
5273
5339
|
if (exposureTimeNs != null && exposureTimeNs > 0L) {
|
|
@@ -5315,6 +5381,37 @@ class MentraLive : SGCManager() {
|
|
|
5315
5381
|
}
|
|
5316
5382
|
}
|
|
5317
5383
|
|
|
5384
|
+
fun warmUpCamera(
|
|
5385
|
+
requestId: String,
|
|
5386
|
+
size: PhotoSize,
|
|
5387
|
+
exposureTimeNs: Long?,
|
|
5388
|
+
durationMs: Int,
|
|
5389
|
+
) {
|
|
5390
|
+
Bridge.log(
|
|
5391
|
+
"LIVE: warmUpCamera() entry — requestId=" +
|
|
5392
|
+
requestId +
|
|
5393
|
+
", size=" +
|
|
5394
|
+
size.value +
|
|
5395
|
+
", durationMs=" +
|
|
5396
|
+
durationMs
|
|
5397
|
+
)
|
|
5398
|
+
|
|
5399
|
+
try {
|
|
5400
|
+
val json = JSONObject()
|
|
5401
|
+
json.put("type", "camera_warm_up")
|
|
5402
|
+
json.put("requestId", requestId)
|
|
5403
|
+
val sizeValue = size.value
|
|
5404
|
+
json.put("size", if (sizeValue.isNotEmpty()) sizeValue else "medium")
|
|
5405
|
+
if (exposureTimeNs != null && exposureTimeNs > 0L) {
|
|
5406
|
+
json.put("exposureTimeNs", exposureTimeNs)
|
|
5407
|
+
}
|
|
5408
|
+
json.put("durationMs", if (durationMs > 0) durationMs else 15000)
|
|
5409
|
+
sendJson(json, true)
|
|
5410
|
+
} catch (e: JSONException) {
|
|
5411
|
+
Log.e(TAG, "Error creating camera warm up JSON", e)
|
|
5412
|
+
}
|
|
5413
|
+
}
|
|
5414
|
+
|
|
5318
5415
|
override fun startStream(message: MutableMap<String, Any>) {
|
|
5319
5416
|
Bridge.log("LIVE: Starting RTMP stream")
|
|
5320
5417
|
|
|
@@ -6144,25 +6241,6 @@ class MentraLive : SGCManager() {
|
|
|
6144
6241
|
}
|
|
6145
6242
|
}
|
|
6146
6243
|
|
|
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
6244
|
override fun sendText(text: String) {
|
|
6167
6245
|
Bridge.log("LIVE: [STUB] Device has no display. Text would show: " + text)
|
|
6168
6246
|
}
|
|
@@ -6976,6 +7054,7 @@ class MentraLive : SGCManager() {
|
|
|
6976
7054
|
|
|
6977
7055
|
try {
|
|
6978
7056
|
val outgoingSummary = summarizeOutgoingMessage(data)
|
|
7057
|
+
val commandTraceInfo = parseOutgoingBleCommandTraceInfo(data)
|
|
6979
7058
|
val isPhotoRequest = outgoingSummary.contains("type=take_photo")
|
|
6980
7059
|
if (isPhotoRequest) {
|
|
6981
7060
|
Bridge.log(
|
|
@@ -7033,11 +7112,34 @@ class MentraLive : SGCManager() {
|
|
|
7033
7112
|
val packedData =
|
|
7034
7113
|
K900ProtocolUtils.packJsonToK900(
|
|
7035
7114
|
chunkStr,
|
|
7036
|
-
|
|
7037
|
-
|
|
7115
|
+
wakeup && i == 0
|
|
7116
|
+
) // Only wakeup on first chunk
|
|
7117
|
+
|
|
7118
|
+
val trace =
|
|
7119
|
+
createBleWriteTrace(
|
|
7120
|
+
commandTraceInfo,
|
|
7121
|
+
chunk.optString("id", "").takeIf { it.isNotBlank() },
|
|
7122
|
+
chunk.optInt("c", i),
|
|
7123
|
+
chunk.optInt("n", chunks.size),
|
|
7124
|
+
chunk.optString("d", "")
|
|
7125
|
+
.toByteArray(StandardCharsets.UTF_8)
|
|
7126
|
+
.size,
|
|
7127
|
+
packedData.size,
|
|
7128
|
+
wakeup && i == 0,
|
|
7129
|
+
true
|
|
7130
|
+
)
|
|
7131
|
+
logBleChunkTrace(
|
|
7132
|
+
"created",
|
|
7133
|
+
trace,
|
|
7134
|
+
mapOf(
|
|
7135
|
+
"chunkJsonBytes" to
|
|
7136
|
+
chunkStr.toByteArray(StandardCharsets.UTF_8).size,
|
|
7137
|
+
"messageBytes" to data.toByteArray(StandardCharsets.UTF_8).size
|
|
7138
|
+
)
|
|
7139
|
+
)
|
|
7038
7140
|
|
|
7039
7141
|
// Queue the chunk for sending
|
|
7040
|
-
queueData(packedData)
|
|
7142
|
+
queueData(packedData, trace)
|
|
7041
7143
|
|
|
7042
7144
|
// Add small delay between chunks to avoid overwhelming the connection
|
|
7043
7145
|
if (i < chunks.size - 1) {
|
|
@@ -7059,9 +7161,25 @@ class MentraLive : SGCManager() {
|
|
|
7059
7161
|
|
|
7060
7162
|
// Pack the data using the centralized utility
|
|
7061
7163
|
val packedData = K900ProtocolUtils.packJsonToK900(data, wakeup)
|
|
7164
|
+
val trace =
|
|
7165
|
+
createBleWriteTrace(
|
|
7166
|
+
commandTraceInfo,
|
|
7167
|
+
null,
|
|
7168
|
+
null,
|
|
7169
|
+
null,
|
|
7170
|
+
data.toByteArray(StandardCharsets.UTF_8).size,
|
|
7171
|
+
packedData.size,
|
|
7172
|
+
wakeup,
|
|
7173
|
+
false
|
|
7174
|
+
)
|
|
7175
|
+
logBleChunkTrace(
|
|
7176
|
+
"created",
|
|
7177
|
+
trace,
|
|
7178
|
+
mapOf("messageBytes" to data.toByteArray(StandardCharsets.UTF_8).size)
|
|
7179
|
+
)
|
|
7062
7180
|
|
|
7063
7181
|
// Queue the data for sending
|
|
7064
|
-
queueData(packedData)
|
|
7182
|
+
queueData(packedData, trace)
|
|
7065
7183
|
if (isPhotoRequest) {
|
|
7066
7184
|
Bridge.log(
|
|
7067
7185
|
"LIVE: PHOTO PIPELINE BLE handoff — packedLen=" +
|
|
@@ -7075,6 +7193,179 @@ class MentraLive : SGCManager() {
|
|
|
7075
7193
|
}
|
|
7076
7194
|
}
|
|
7077
7195
|
|
|
7196
|
+
private fun parseOutgoingBleCommandTraceInfo(payload: String): OutgoingBleCommandTraceInfo {
|
|
7197
|
+
return try {
|
|
7198
|
+
val obj = JSONObject(payload)
|
|
7199
|
+
OutgoingBleCommandTraceInfo(
|
|
7200
|
+
commandType = obj.optString("type", "unknown"),
|
|
7201
|
+
requestId = optNonBlankString(obj, "requestId"),
|
|
7202
|
+
appId = optNonBlankString(obj, "appId"),
|
|
7203
|
+
messageId = if (obj.has("mId")) obj.optLong("mId") else null
|
|
7204
|
+
)
|
|
7205
|
+
} catch (_: Exception) {
|
|
7206
|
+
OutgoingBleCommandTraceInfo(
|
|
7207
|
+
commandType = "unknown",
|
|
7208
|
+
requestId = null,
|
|
7209
|
+
appId = null,
|
|
7210
|
+
messageId = null
|
|
7211
|
+
)
|
|
7212
|
+
}
|
|
7213
|
+
}
|
|
7214
|
+
|
|
7215
|
+
private fun createBleWriteTrace(
|
|
7216
|
+
commandInfo: OutgoingBleCommandTraceInfo,
|
|
7217
|
+
chunkId: String?,
|
|
7218
|
+
chunkIndex: Int?,
|
|
7219
|
+
totalChunks: Int?,
|
|
7220
|
+
payloadBytes: Int?,
|
|
7221
|
+
packedBytes: Int,
|
|
7222
|
+
wakeup: Boolean,
|
|
7223
|
+
chunked: Boolean
|
|
7224
|
+
): BleWriteTrace {
|
|
7225
|
+
return BleWriteTrace(
|
|
7226
|
+
sequence = bleWriteTraceSequence.getAndIncrement(),
|
|
7227
|
+
commandType = commandInfo.commandType,
|
|
7228
|
+
requestId = commandInfo.requestId,
|
|
7229
|
+
appId = commandInfo.appId,
|
|
7230
|
+
messageId = commandInfo.messageId,
|
|
7231
|
+
chunkId = chunkId,
|
|
7232
|
+
chunkIndex = chunkIndex,
|
|
7233
|
+
totalChunks = totalChunks,
|
|
7234
|
+
payloadBytes = payloadBytes,
|
|
7235
|
+
packedBytes = packedBytes,
|
|
7236
|
+
wakeup = wakeup,
|
|
7237
|
+
chunked = chunked,
|
|
7238
|
+
queuedAtMs = System.currentTimeMillis()
|
|
7239
|
+
)
|
|
7240
|
+
}
|
|
7241
|
+
|
|
7242
|
+
private fun optNonBlankString(obj: JSONObject, key: String): String? {
|
|
7243
|
+
return obj.optString(key, "").takeIf { it.isNotBlank() }
|
|
7244
|
+
}
|
|
7245
|
+
|
|
7246
|
+
private fun logBleChunkTrace(
|
|
7247
|
+
stage: String,
|
|
7248
|
+
trace: BleWriteTrace?,
|
|
7249
|
+
extra: Map<String, Any?> = emptyMap()
|
|
7250
|
+
) {
|
|
7251
|
+
logBleTrace("sdk_ble_chunk", stage, trace, extra)
|
|
7252
|
+
}
|
|
7253
|
+
|
|
7254
|
+
private fun logBleWriteTrace(
|
|
7255
|
+
stage: String,
|
|
7256
|
+
trace: BleWriteTrace?,
|
|
7257
|
+
extra: Map<String, Any?> = emptyMap()
|
|
7258
|
+
) {
|
|
7259
|
+
logBleTrace("sdk_ble_write", stage, trace, extra)
|
|
7260
|
+
}
|
|
7261
|
+
|
|
7262
|
+
private fun logBleTrace(
|
|
7263
|
+
layer: String,
|
|
7264
|
+
stage: String,
|
|
7265
|
+
trace: BleWriteTrace?,
|
|
7266
|
+
extra: Map<String, Any?> = emptyMap()
|
|
7267
|
+
) {
|
|
7268
|
+
if (trace == null) {
|
|
7269
|
+
return
|
|
7270
|
+
}
|
|
7271
|
+
|
|
7272
|
+
val warningReason = bleTraceWarningReason(stage, extra)
|
|
7273
|
+
if (warningReason == null) {
|
|
7274
|
+
return
|
|
7275
|
+
}
|
|
7276
|
+
|
|
7277
|
+
try {
|
|
7278
|
+
val payload = mutableMapOf<String, Any>(
|
|
7279
|
+
"level" to "warning",
|
|
7280
|
+
"warningReason" to warningReason,
|
|
7281
|
+
"stage" to stage,
|
|
7282
|
+
"sequence" to trace.sequence,
|
|
7283
|
+
"commandType" to trace.commandType,
|
|
7284
|
+
"packedBytes" to trace.packedBytes,
|
|
7285
|
+
"wakeup" to trace.wakeup,
|
|
7286
|
+
"chunked" to trace.chunked,
|
|
7287
|
+
"queuedAtMs" to trace.queuedAtMs
|
|
7288
|
+
)
|
|
7289
|
+
trace.requestId?.let { payload["requestId"] = it }
|
|
7290
|
+
trace.appId?.let { payload["appId"] = it }
|
|
7291
|
+
trace.messageId?.let { payload["messageId"] = it }
|
|
7292
|
+
trace.chunkId?.let { payload["chunkId"] = it }
|
|
7293
|
+
trace.chunkIndex?.let {
|
|
7294
|
+
payload["chunkIndex"] = it
|
|
7295
|
+
payload["chunkNumber"] = it + 1
|
|
7296
|
+
}
|
|
7297
|
+
trace.totalChunks?.let { payload["totalChunks"] = it }
|
|
7298
|
+
trace.payloadBytes?.let { payload["payloadBytes"] = it }
|
|
7299
|
+
extra.forEach { (key, value) ->
|
|
7300
|
+
if (value != null) {
|
|
7301
|
+
payload[key] = value
|
|
7302
|
+
}
|
|
7303
|
+
}
|
|
7304
|
+
|
|
7305
|
+
BleTraceLogger.logMap("phone_to_glasses", layer, trace.commandType, payload)
|
|
7306
|
+
} catch (e: Exception) {
|
|
7307
|
+
Log.d(TAG, "BLE trace logging failed for $layer/$stage", e)
|
|
7308
|
+
}
|
|
7309
|
+
}
|
|
7310
|
+
|
|
7311
|
+
private fun bleTraceWarningReason(stage: String, extra: Map<String, Any?>): String? {
|
|
7312
|
+
if (extra["errorClass"] != null || extra["errorMessage"] != null) {
|
|
7313
|
+
return "ble_write_error"
|
|
7314
|
+
}
|
|
7315
|
+
if (extra["success"] == false) {
|
|
7316
|
+
return "ble_write_failed"
|
|
7317
|
+
}
|
|
7318
|
+
if (extra["writeAccepted"] == false) {
|
|
7319
|
+
return "ble_write_rejected"
|
|
7320
|
+
}
|
|
7321
|
+
|
|
7322
|
+
if (traceLongAtLeast(extra["queueDelayMs"], SIGNIFICANT_BLE_TRACE_DELAY_MS)) {
|
|
7323
|
+
return "queue_delay"
|
|
7324
|
+
}
|
|
7325
|
+
if (traceLongAtLeast(extra["callbackDelayMs"], SIGNIFICANT_BLE_TRACE_DELAY_MS)) {
|
|
7326
|
+
return "write_callback_delay"
|
|
7327
|
+
}
|
|
7328
|
+
if (traceLongAtLeast(extra["remainingDelayMs"], SIGNIFICANT_BLE_TRACE_DELAY_MS)) {
|
|
7329
|
+
return "rate_limit_delay"
|
|
7330
|
+
}
|
|
7331
|
+
if (traceLongAtLeast(extra["nextProcessDelayMs"], SIGNIFICANT_BLE_TRACE_DELAY_MS)) {
|
|
7332
|
+
return "next_process_delay"
|
|
7333
|
+
}
|
|
7334
|
+
if (extra["retryDelayMs"].asTraceLong() != null) {
|
|
7335
|
+
return "write_retry"
|
|
7336
|
+
}
|
|
7337
|
+
if (stage == "queued" &&
|
|
7338
|
+
traceIntAtLeast(extra["queueSizeAfterAdd"], SIGNIFICANT_BLE_TRACE_QUEUE_SIZE)
|
|
7339
|
+
) {
|
|
7340
|
+
return "queue_depth"
|
|
7341
|
+
}
|
|
7342
|
+
return null
|
|
7343
|
+
}
|
|
7344
|
+
|
|
7345
|
+
private fun traceLongAtLeast(value: Any?, threshold: Long): Boolean {
|
|
7346
|
+
return (value.asTraceLong() ?: Long.MIN_VALUE) >= threshold
|
|
7347
|
+
}
|
|
7348
|
+
|
|
7349
|
+
private fun traceIntAtLeast(value: Any?, threshold: Int): Boolean {
|
|
7350
|
+
return (value.asTraceInt() ?: Int.MIN_VALUE) >= threshold
|
|
7351
|
+
}
|
|
7352
|
+
|
|
7353
|
+
private fun Any?.asTraceLong(): Long? {
|
|
7354
|
+
return when (this) {
|
|
7355
|
+
is Number -> this.toLong()
|
|
7356
|
+
is String -> this.toLongOrNull()
|
|
7357
|
+
else -> null
|
|
7358
|
+
}
|
|
7359
|
+
}
|
|
7360
|
+
|
|
7361
|
+
private fun Any?.asTraceInt(): Int? {
|
|
7362
|
+
return when (this) {
|
|
7363
|
+
is Number -> this.toInt()
|
|
7364
|
+
is String -> this.toIntOrNull()
|
|
7365
|
+
else -> null
|
|
7366
|
+
}
|
|
7367
|
+
}
|
|
7368
|
+
|
|
7078
7369
|
private fun summarizeOutgoingMessage(payload: String?): String {
|
|
7079
7370
|
if (payload == null || payload.isEmpty()) {
|
|
7080
7371
|
return "type=unknown, requestId=none, appId=none, transferMethod=none, bleImgId=none, exposureTimeNs=none, iso=none, mId=none"
|
|
@@ -8073,9 +8364,6 @@ class MentraLive : SGCManager() {
|
|
|
8073
8364
|
// Send button photo settings
|
|
8074
8365
|
sendButtonPhotoSettings()
|
|
8075
8366
|
|
|
8076
|
-
// Send button camera LED setting
|
|
8077
|
-
sendButtonCameraLedSetting()
|
|
8078
|
-
|
|
8079
8367
|
// Send camera FOV setting (K900 / Mentra Live)
|
|
8080
8368
|
sendCameraFovSetting()
|
|
8081
8369
|
|
|
@@ -8144,12 +8432,6 @@ class MentraLive : SGCManager() {
|
|
|
8144
8432
|
)
|
|
8145
8433
|
}
|
|
8146
8434
|
|
|
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
8435
|
/** Send camera FOV setting to glasses (K900 / Mentra Live). */
|
|
8154
8436
|
override fun sendCameraFovSetting() {
|
|
8155
8437
|
var fov = 118
|
|
@@ -8194,6 +8476,36 @@ class MentraLive : SGCManager() {
|
|
|
8194
8476
|
}
|
|
8195
8477
|
}
|
|
8196
8478
|
|
|
8479
|
+
/**
|
|
8480
|
+
* Send camera tuning config (ANR / gain) to the glasses via the {@code camera_tuning_config}
|
|
8481
|
+
* command. The ASG client relays this as a {@code camconfig} broadcast to the camera HAL.
|
|
8482
|
+
*
|
|
8483
|
+
* @param requestId optional request ID echoed in the settings_ack response
|
|
8484
|
+
* @param anrOn {@code true} = ANR enabled, {@code false} = ANR disabled
|
|
8485
|
+
* @param gainOn {@code true} = stock gain params, {@code false} = pixsmart gain-off params
|
|
8486
|
+
*/
|
|
8487
|
+
fun sendCameraTuningConfig(requestId: String?, anrOn: Boolean, gainOn: Boolean) {
|
|
8488
|
+
Bridge.log("LIVE: Sending camera tuning config: anr=$anrOn, gain=$gainOn")
|
|
8489
|
+
|
|
8490
|
+
if (!isConnected) {
|
|
8491
|
+
Log.w(TAG, "Cannot send camera tuning config - not connected")
|
|
8492
|
+
return
|
|
8493
|
+
}
|
|
8494
|
+
|
|
8495
|
+
try {
|
|
8496
|
+
val json = JSONObject()
|
|
8497
|
+
json.put("type", "camera_tuning_config")
|
|
8498
|
+
if (!requestId.isNullOrEmpty()) {
|
|
8499
|
+
json.put("request_id", requestId)
|
|
8500
|
+
}
|
|
8501
|
+
json.put("anr", anrOn)
|
|
8502
|
+
json.put("gain", gainOn)
|
|
8503
|
+
sendJson(json, true)
|
|
8504
|
+
} catch (e: JSONException) {
|
|
8505
|
+
Log.e(TAG, "Error creating camera tuning config message", e)
|
|
8506
|
+
}
|
|
8507
|
+
}
|
|
8508
|
+
|
|
8197
8509
|
/**
|
|
8198
8510
|
* Send button max recording time to glasses Matches iOS MentraLive.swift
|
|
8199
8511
|
* sendButtonMaxRecordingTime pattern
|
|
@@ -8228,17 +8540,15 @@ class MentraLive : SGCManager() {
|
|
|
8228
8540
|
override fun startVideoRecording(
|
|
8229
8541
|
requestId: String,
|
|
8230
8542
|
save: Boolean,
|
|
8231
|
-
flash: Boolean,
|
|
8232
8543
|
sound: Boolean
|
|
8233
8544
|
) {
|
|
8234
|
-
startVideoRecording(requestId, save,
|
|
8545
|
+
startVideoRecording(requestId, save, sound, 0, 0, 0, 0) // Use defaults
|
|
8235
8546
|
}
|
|
8236
8547
|
|
|
8237
8548
|
/**
|
|
8238
8549
|
* Start video recording with optional resolution settings
|
|
8239
8550
|
* @param requestId Request ID for tracking
|
|
8240
8551
|
* @param save Whether to save the video
|
|
8241
|
-
* @param flash Whether to enable privacy flash LED
|
|
8242
8552
|
* @param sound Whether to enable start/stop sounds
|
|
8243
8553
|
* @param width Video width (0 for default)
|
|
8244
8554
|
* @param height Video height (0 for default)
|
|
@@ -8248,7 +8558,6 @@ class MentraLive : SGCManager() {
|
|
|
8248
8558
|
override fun startVideoRecording(
|
|
8249
8559
|
requestId: String,
|
|
8250
8560
|
save: Boolean,
|
|
8251
|
-
flash: Boolean,
|
|
8252
8561
|
sound: Boolean,
|
|
8253
8562
|
width: Int,
|
|
8254
8563
|
height: Int,
|
|
@@ -8260,8 +8569,6 @@ class MentraLive : SGCManager() {
|
|
|
8260
8569
|
requestId +
|
|
8261
8570
|
", save=" +
|
|
8262
8571
|
save +
|
|
8263
|
-
", flash=" +
|
|
8264
|
-
flash +
|
|
8265
8572
|
", sound=" +
|
|
8266
8573
|
sound +
|
|
8267
8574
|
", resolution=" +
|
|
@@ -8285,7 +8592,6 @@ class MentraLive : SGCManager() {
|
|
|
8285
8592
|
json.put("type", "start_video_recording")
|
|
8286
8593
|
json.put("requestId", requestId)
|
|
8287
8594
|
json.put("save", save)
|
|
8288
|
-
json.put("flash", flash)
|
|
8289
8595
|
json.put("sound", sound)
|
|
8290
8596
|
|
|
8291
8597
|
// Auto-stop timer; only sent when set (> 0). 0 = record until stopped.
|