@glassly/bluetooth-sdk 0.1.0-dev.99 → 0.2.8-dev.445
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 +47 -6
- package/android/build.gradle +7 -7
- package/android/lc3Lib/build.gradle +2 -2
- package/android/src/main/assets/G2Firmware/README.md +27 -2
- package/android/src/main/assets/G2Firmware/{g2-stock-2.2.9.22.evenota.bin → g2-glassly-cfw-2.3.0.24.evenota.bin} +0 -0
- package/android/src/main/assets/G2Firmware/{g2-glassly-cfw-2.2.9.22.evenota.bin → g2-stock-2.3.0.24.evenota.bin} +0 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/BluetoothSdk.kt +38 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/BluetoothSdkModule.kt +50 -14
- package/android/src/main/java/com/glassly/bluetoothsdk/Bridge.kt +18 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/DeviceManager.kt +139 -10
- package/android/src/main/java/com/glassly/bluetoothsdk/DeviceStore.kt +28 -1
- package/android/src/main/java/com/glassly/bluetoothsdk/DisplayPacer.kt +199 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/GeneratedChangelogCatalog.kt +10 -1
- package/android/src/main/java/com/glassly/bluetoothsdk/GeneratedReleaseMetadata.kt +4 -4
- package/android/src/main/java/com/glassly/bluetoothsdk/GlassesNotifications.kt +155 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2.kt +1065 -255
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2BleLink.kt +119 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2CfwClassification.kt +29 -28
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2CfwRenderer.kt +138 -33
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2CfwScene.kt +673 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2CfwTextureCache.kt +235 -162
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2CfwTransport.kt +402 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2DisplayDiagnostics.kt +84 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2FlashProtocol.kt +3 -3
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2Flasher.kt +9 -9
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2Keyboard.kt +689 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2KeyboardBridge.kt +452 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2KeyboardHid.kt +208 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2NotificationFile.kt +181 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2RingBattery.kt +20 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2Sensors.kt +67 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2TouchInput.kt +39 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/MentraLive.kt +5 -5
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/MentraNex.kt +14 -14
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/{GlasslyBle.java → NexProto.java} +6462 -7618
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/NotificationMessageIds.kt +58 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/SGCManager.kt +77 -2
- package/android/src/main/java/com/glassly/bluetoothsdk/speakerid/SpeakerIdEngine.kt +1 -1
- package/android/src/main/java/com/glassly/bluetoothsdk/status/DeviceStatus.kt +55 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/stt/STTTools.kt +1 -1
- package/android/src/main/java/com/glassly/bluetoothsdk/stt/SherpaOnnxTranscriber.kt +1 -1
- package/android/src/main/java/com/glassly/bluetoothsdk/tts/TTSTools.kt +1 -1
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/NexSGCUtils.kt +24 -149
- package/android/src/test/java/com/glassly/bluetoothsdk/DisplayPacerTest.kt +277 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/G2BleLinkTest.kt +70 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/G2CfwClassificationTest.kt +45 -56
- package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/G2CfwRendererTest.kt +78 -5
- package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/G2CfwSceneTest.kt +489 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/G2CfwTextureCacheTest.kt +100 -43
- package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/G2CfwTransportTest.kt +252 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/G2DisplayDiagnosticsTest.kt +64 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/G2KeyboardTest.kt +271 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/G2NotificationFileTest.kt +204 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/G2RingBatteryTest.kt +56 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/G2SensorsTest.kt +56 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/G2TouchInputTest.kt +60 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/NexProtoDescriptorTest.kt +32 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/NotificationMessageIdsTest.kt +58 -0
- package/build/BluetoothSdk.types.d.ts +209 -20
- package/build/BluetoothSdk.types.d.ts.map +1 -1
- package/build/BluetoothSdk.types.js.map +1 -1
- package/build/_internal.d.ts +1 -1
- package/build/_internal.d.ts.map +1 -1
- package/build/_internal.js.map +1 -1
- package/build/_private/BluetoothSdkModule.d.ts +30 -1
- package/build/_private/BluetoothSdkModule.d.ts.map +1 -1
- package/build/_private/BluetoothSdkModule.js.map +1 -1
- package/build/generated/changelogCatalog.d.ts +28 -1
- package/build/generated/changelogCatalog.d.ts.map +1 -1
- package/build/generated/changelogCatalog.js +37 -1
- package/build/generated/changelogCatalog.js.map +1 -1
- package/build/generated/releaseMetadata.js +4 -4
- package/build/generated/releaseMetadata.js.map +1 -1
- package/build/index.d.ts +1 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +14 -0
- package/build/index.js.map +1 -1
- package/ios/BluetoothSdkModule.swift +102 -13
- package/ios/GlasslyBluetoothSDK.podspec +1 -1
- package/ios/Packages/SherpaOnnx/SherpaOnnx.swift +8 -3
- package/ios/Source/BluetoothSDK.swift +53 -0
- package/ios/Source/BluetoothSdkDefaults.swift +1 -1
- package/ios/Source/Bridge.swift +13 -0
- package/ios/Source/DeviceManager.swift +151 -33
- package/ios/Source/DeviceStore.swift +28 -14
- package/ios/Source/DisplayPacer.swift +180 -0
- package/ios/Source/GeneratedChangelogCatalog.swift +10 -1
- package/ios/Source/GeneratedReleaseMetadata.swift +4 -4
- package/ios/Source/GlassesNotifications.swift +181 -0
- package/ios/Source/ObservableStore.swift +14 -2
- package/ios/Source/Resources/G2Firmware/README.md +27 -2
- package/ios/Source/Resources/G2Firmware/{g2-stock-2.2.9.22.evenota.bin → g2-glassly-cfw-2.3.0.24.evenota.bin} +0 -0
- package/ios/Source/Resources/G2Firmware/{g2-glassly-cfw-2.2.9.22.evenota.bin → g2-stock-2.3.0.24.evenota.bin} +0 -0
- package/ios/Source/controllers/R1.swift +0 -1
- package/ios/Source/internal/BluetoothAvailability.swift +31 -0
- package/ios/Source/sgcs/G2.swift +1420 -495
- package/ios/Source/sgcs/G2AncsRelay.swift +268 -0
- package/ios/Source/sgcs/G2BleLink.swift +102 -0
- package/ios/Source/sgcs/G2CfwClassification.swift +45 -27
- package/ios/Source/sgcs/G2CfwRenderer.swift +166 -33
- package/ios/Source/sgcs/G2CfwScene.swift +779 -0
- package/ios/Source/sgcs/G2CfwTextureCache.swift +244 -117
- package/ios/Source/sgcs/G2CfwTransport.swift +485 -0
- package/ios/Source/sgcs/G2DisplayDiagnostics.swift +154 -0
- package/ios/Source/sgcs/G2FlashProtocol.swift +2 -2
- package/ios/Source/sgcs/G2Flasher.swift +9 -9
- package/ios/Source/sgcs/G2Keyboard.swift +780 -0
- package/ios/Source/sgcs/G2KeyboardBridge.swift +438 -0
- package/ios/Source/sgcs/G2KeyboardHid.swift +290 -0
- package/ios/Source/sgcs/G2NotificationTuning.swift +24 -0
- package/ios/Source/sgcs/G2RingBattery.swift +22 -0
- package/ios/Source/sgcs/G2Sensors.swift +75 -0
- package/ios/Source/sgcs/G2TouchInput.swift +51 -0
- package/ios/Source/sgcs/G2WritePacing.swift +18 -0
- package/ios/Source/sgcs/MentraNex.swift +44 -44
- package/ios/Source/sgcs/SGCManager.swift +120 -13
- package/ios/Source/sgcs/{glassly_ble.pb.swift → nex_ble.pb.swift} +405 -465
- package/ios/Source/speakerid/SpeakerIdEngine.swift +2 -2
- package/ios/Source/status/DeviceStatus.swift +27 -0
- package/ios/Source/stt/SherpaOnnxTranscriber.swift +15 -14
- package/ios/Source/utils/Enums.swift +0 -1
- package/ios/Source/utils/G1Text.swift +0 -1
- package/ios/Source/utils/G2NotifyFile.swift +20 -10
- package/ios/Source/utils/JSCExperiment.swift +6 -6
- package/ios/Source/utils/Models.swift +0 -1
- package/ios/Tests/BluetoothSDKTests/BluetoothAvailabilityTests.swift +29 -0
- package/ios/Tests/BluetoothSDKTests/DisplayPacerTests.swift +255 -0
- package/ios/Tests/BluetoothSDKTests/G2AncsRelayTests.swift +186 -0
- package/ios/Tests/BluetoothSDKTests/G2BleLinkTests.swift +52 -0
- package/ios/Tests/BluetoothSDKTests/G2CfwClassificationTests.swift +46 -51
- package/ios/Tests/BluetoothSDKTests/G2CfwRendererTests.swift +164 -5
- package/ios/Tests/BluetoothSDKTests/G2CfwSceneTests.swift +497 -0
- package/ios/Tests/BluetoothSDKTests/G2CfwTextureCacheTests.swift +108 -44
- package/ios/Tests/BluetoothSDKTests/G2CfwTransportTests.swift +259 -0
- package/ios/Tests/BluetoothSDKTests/G2DisplayDiagnosticsTests.swift +89 -0
- package/ios/Tests/BluetoothSDKTests/G2FlashProtocolTests.swift +3 -2
- package/ios/Tests/BluetoothSDKTests/G2ImageAckTests.swift +104 -0
- package/ios/Tests/BluetoothSDKTests/G2KeyboardTests.swift +274 -0
- package/ios/Tests/BluetoothSDKTests/G2NotifyFileTests.swift +21 -5
- package/ios/Tests/BluetoothSDKTests/G2RingBatteryTests.swift +56 -0
- package/ios/Tests/BluetoothSDKTests/G2SensorsTests.swift +56 -0
- package/ios/Tests/BluetoothSDKTests/G2TouchInputTests.swift +71 -0
- package/ios/Tests/BluetoothSDKTests/G2WritePacingTests.swift +35 -0
- package/ios/Tests/BluetoothSDKTests/GlassesNotificationTests.swift +95 -0
- package/package.json +1 -1
- package/plugin/build/withAndroid.js +2 -2
- package/plugin/build/withIos.js +1 -1
- package/scripts/gen-nex-proto.sh +12 -0
- package/scripts/inject-ios-sdk-version.mjs +1 -1
- package/src/BluetoothSdk.types.ts +214 -19
- package/src/_internal.ts +1 -1
- package/src/_private/BluetoothSdkModule.ts +40 -0
- package/src/generated/changelogCatalog.ts +37 -1
- package/src/generated/releaseMetadata.ts +4 -4
- package/src/index.ts +24 -0
|
@@ -129,8 +129,8 @@ class MentraNexSGC: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate, SG
|
|
|
129
129
|
canvasElements.firstIndex(where: { $0.key == key })
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
-
private func sendCanvasCommand(_ configure: (inout
|
|
133
|
-
var msg =
|
|
132
|
+
private func sendCanvasCommand(_ configure: (inout NexPhoneToGlasses) -> Void) {
|
|
133
|
+
var msg = NexPhoneToGlasses()
|
|
134
134
|
configure(&msg)
|
|
135
135
|
guard let data = try? msg.serializedData() else { return }
|
|
136
136
|
queueDataWithOptimalChunking(data, packetType: PACKET_TYPE_PROTOBUF, waitTimeMs: 10)
|
|
@@ -735,7 +735,7 @@ class MentraNexSGC: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate, SG
|
|
|
735
735
|
guard packetData.count > 1 else { return }
|
|
736
736
|
let payload = packetData.subdata(in: 1 ..< packetData.count)
|
|
737
737
|
let commandName: String
|
|
738
|
-
if let phoneToGlasses = try?
|
|
738
|
+
if let phoneToGlasses = try? NexPhoneToGlasses(serializedData: payload) {
|
|
739
739
|
commandName = String(describing: phoneToGlasses.payload)
|
|
740
740
|
} else {
|
|
741
741
|
commandName = "UNKNOWN"
|
|
@@ -1257,7 +1257,7 @@ class MentraNexSGC: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate, SG
|
|
|
1257
1257
|
// during continuous speech) and each Bridge.log costs the JS thread.
|
|
1258
1258
|
// Bridge.log("NEX: Displaying text wall: '\(sanitizedText)'")
|
|
1259
1259
|
|
|
1260
|
-
let displayText =
|
|
1260
|
+
let displayText = NexDisplayText.with {
|
|
1261
1261
|
$0.text = sanitizedText
|
|
1262
1262
|
$0.size = 48
|
|
1263
1263
|
$0.x = 20
|
|
@@ -1265,7 +1265,7 @@ class MentraNexSGC: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate, SG
|
|
|
1265
1265
|
$0.color = 10000
|
|
1266
1266
|
}
|
|
1267
1267
|
|
|
1268
|
-
let phoneToGlasses =
|
|
1268
|
+
let phoneToGlasses = NexPhoneToGlasses.with {
|
|
1269
1269
|
$0.displayText = displayText
|
|
1270
1270
|
}
|
|
1271
1271
|
|
|
@@ -1354,7 +1354,7 @@ class MentraNexSGC: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate, SG
|
|
|
1354
1354
|
|
|
1355
1355
|
Bridge.log("NEX: Displaying scrolling text: '\(text)'")
|
|
1356
1356
|
|
|
1357
|
-
let displayScrollingText =
|
|
1357
|
+
let displayScrollingText = NexDisplayScrollingText.with {
|
|
1358
1358
|
$0.text = text
|
|
1359
1359
|
$0.size = 48
|
|
1360
1360
|
$0.x = 20
|
|
@@ -1368,7 +1368,7 @@ class MentraNexSGC: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate, SG
|
|
|
1368
1368
|
$0.lineSpacing = 2
|
|
1369
1369
|
}
|
|
1370
1370
|
|
|
1371
|
-
let phoneToGlasses =
|
|
1371
|
+
let phoneToGlasses = NexPhoneToGlasses.with {
|
|
1372
1372
|
$0.displayScrollingText = displayScrollingText
|
|
1373
1373
|
}
|
|
1374
1374
|
|
|
@@ -1412,7 +1412,7 @@ class MentraNexSGC: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate, SG
|
|
|
1412
1412
|
let totalChunks = Int(ceil(Double(bmpData.count) / Double(bmpChunkSize)))
|
|
1413
1413
|
|
|
1414
1414
|
// Send display image command first
|
|
1415
|
-
let displayImage =
|
|
1415
|
+
let displayImage = NexDisplayImage.with {
|
|
1416
1416
|
$0.streamID = streamId
|
|
1417
1417
|
$0.totalChunks = UInt32(totalChunks)
|
|
1418
1418
|
$0.x = 0
|
|
@@ -1422,7 +1422,7 @@ class MentraNexSGC: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate, SG
|
|
|
1422
1422
|
$0.encoding = "raw"
|
|
1423
1423
|
}
|
|
1424
1424
|
|
|
1425
|
-
let phoneToGlasses =
|
|
1425
|
+
let phoneToGlasses = NexPhoneToGlasses.with {
|
|
1426
1426
|
$0.msgID = "img_start_1"
|
|
1427
1427
|
$0.displayImage = displayImage
|
|
1428
1428
|
}
|
|
@@ -1615,7 +1615,7 @@ class MentraNexSGC: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate, SG
|
|
|
1615
1615
|
if !canvasElements.isEmpty || currentLayoutId != nil {
|
|
1616
1616
|
canvasElements.removeAll()
|
|
1617
1617
|
currentLayoutId = nil
|
|
1618
|
-
sendCanvasCommand { $0.canvasClear =
|
|
1618
|
+
sendCanvasCommand { $0.canvasClear = NexCanvasClear() }
|
|
1619
1619
|
}
|
|
1620
1620
|
|
|
1621
1621
|
// Drop any pending/resendable text wall so a stale caption can't
|
|
@@ -1626,9 +1626,9 @@ class MentraNexSGC: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate, SG
|
|
|
1626
1626
|
textWallResendsRemaining = 0
|
|
1627
1627
|
textWallLock.unlock()
|
|
1628
1628
|
|
|
1629
|
-
let clearDisplay =
|
|
1629
|
+
let clearDisplay = NexClearDisplay()
|
|
1630
1630
|
|
|
1631
|
-
let phoneToGlasses =
|
|
1631
|
+
let phoneToGlasses = NexPhoneToGlasses.with {
|
|
1632
1632
|
$0.msgID = "clear_disp_001"
|
|
1633
1633
|
$0.clearDisplay_p = clearDisplay
|
|
1634
1634
|
}
|
|
@@ -1675,11 +1675,11 @@ class MentraNexSGC: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate, SG
|
|
|
1675
1675
|
}
|
|
1676
1676
|
Bridge.log("NEX: Setting brightness to wire value \(validBrightness)")
|
|
1677
1677
|
|
|
1678
|
-
let brightnessConfig =
|
|
1678
|
+
let brightnessConfig = NexBrightnessConfig.with {
|
|
1679
1679
|
$0.value = UInt32(validBrightness)
|
|
1680
1680
|
}
|
|
1681
1681
|
|
|
1682
|
-
let phoneToGlasses =
|
|
1682
|
+
let phoneToGlasses = NexPhoneToGlasses.with {
|
|
1683
1683
|
$0.brightness = brightnessConfig
|
|
1684
1684
|
}
|
|
1685
1685
|
|
|
@@ -1695,11 +1695,11 @@ class MentraNexSGC: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate, SG
|
|
|
1695
1695
|
|
|
1696
1696
|
Bridge.log("NEX: Setting auto brightness to \(enabled)")
|
|
1697
1697
|
|
|
1698
|
-
let autoBrightnessConfig =
|
|
1698
|
+
let autoBrightnessConfig = NexAutoBrightnessConfig.with {
|
|
1699
1699
|
$0.enabled = enabled
|
|
1700
1700
|
}
|
|
1701
1701
|
|
|
1702
|
-
let phoneToGlasses =
|
|
1702
|
+
let phoneToGlasses = NexPhoneToGlasses.with {
|
|
1703
1703
|
$0.autoBrightness = autoBrightnessConfig
|
|
1704
1704
|
}
|
|
1705
1705
|
|
|
@@ -1717,11 +1717,11 @@ class MentraNexSGC: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate, SG
|
|
|
1717
1717
|
let validAngle = max(0, min(60, angle))
|
|
1718
1718
|
Bridge.log("NEX: Setting head-up angle to \(validAngle) degrees")
|
|
1719
1719
|
|
|
1720
|
-
let headUpAngleConfig =
|
|
1720
|
+
let headUpAngleConfig = NexHeadUpAngleConfig.with {
|
|
1721
1721
|
$0.angle = UInt32(validAngle)
|
|
1722
1722
|
}
|
|
1723
1723
|
|
|
1724
|
-
let phoneToGlasses =
|
|
1724
|
+
let phoneToGlasses = NexPhoneToGlasses.with {
|
|
1725
1725
|
$0.headUpAngle = headUpAngleConfig
|
|
1726
1726
|
}
|
|
1727
1727
|
|
|
@@ -1739,11 +1739,11 @@ class MentraNexSGC: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate, SG
|
|
|
1739
1739
|
let validHeight = max(0, min(8, height))
|
|
1740
1740
|
Bridge.log("NEX: Setting display height to \(validHeight)")
|
|
1741
1741
|
|
|
1742
|
-
let displayHeightConfig =
|
|
1742
|
+
let displayHeightConfig = NexDisplayHeightConfig.with {
|
|
1743
1743
|
$0.height = UInt32(validHeight)
|
|
1744
1744
|
}
|
|
1745
1745
|
|
|
1746
|
-
let phoneToGlasses =
|
|
1746
|
+
let phoneToGlasses = NexPhoneToGlasses.with {
|
|
1747
1747
|
$0.displayHeight = displayHeightConfig
|
|
1748
1748
|
}
|
|
1749
1749
|
|
|
@@ -1760,11 +1760,11 @@ class MentraNexSGC: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate, SG
|
|
|
1760
1760
|
let tier = NexDashboardDisplayWire.depthToWireTier(depth)
|
|
1761
1761
|
Bridge.log("NEX: Setting display distance tier \(tier) in distance_cm field (dashboard depth \(depth))")
|
|
1762
1762
|
|
|
1763
|
-
let displayDistanceConfig =
|
|
1763
|
+
let displayDistanceConfig = NexDisplayDistanceConfig.with {
|
|
1764
1764
|
$0.distanceCm = tier
|
|
1765
1765
|
}
|
|
1766
1766
|
|
|
1767
|
-
let phoneToGlasses =
|
|
1767
|
+
let phoneToGlasses = NexPhoneToGlasses.with {
|
|
1768
1768
|
$0.displayDistance = displayDistanceConfig
|
|
1769
1769
|
}
|
|
1770
1770
|
|
|
@@ -1782,11 +1782,11 @@ class MentraNexSGC: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate, SG
|
|
|
1782
1782
|
return
|
|
1783
1783
|
}
|
|
1784
1784
|
|
|
1785
|
-
let vadConfig =
|
|
1785
|
+
let vadConfig = NexVadEnabledConfig.with {
|
|
1786
1786
|
$0.enabled = enabled
|
|
1787
1787
|
}
|
|
1788
1788
|
|
|
1789
|
-
let phoneToGlasses =
|
|
1789
|
+
let phoneToGlasses = NexPhoneToGlasses.with {
|
|
1790
1790
|
$0.vadEnabled = vadConfig
|
|
1791
1791
|
}
|
|
1792
1792
|
|
|
@@ -1806,11 +1806,11 @@ class MentraNexSGC: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate, SG
|
|
|
1806
1806
|
|
|
1807
1807
|
Bridge.log("NEX: Setting microphone enabled: \(enabled)")
|
|
1808
1808
|
|
|
1809
|
-
let micStateConfig =
|
|
1809
|
+
let micStateConfig = NexMicStateConfig.with {
|
|
1810
1810
|
$0.enabled = enabled
|
|
1811
1811
|
}
|
|
1812
1812
|
|
|
1813
|
-
let phoneToGlasses =
|
|
1813
|
+
let phoneToGlasses = NexPhoneToGlasses.with {
|
|
1814
1814
|
$0.micState = micStateConfig
|
|
1815
1815
|
}
|
|
1816
1816
|
|
|
@@ -1844,9 +1844,9 @@ class MentraNexSGC: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate, SG
|
|
|
1844
1844
|
|
|
1845
1845
|
Bridge.log("NEX: Querying battery status")
|
|
1846
1846
|
|
|
1847
|
-
let batteryStateRequest =
|
|
1847
|
+
let batteryStateRequest = NexBatteryStateRequest()
|
|
1848
1848
|
|
|
1849
|
-
let phoneToGlasses =
|
|
1849
|
+
let phoneToGlasses = NexPhoneToGlasses.with {
|
|
1850
1850
|
$0.batteryState = batteryStateRequest
|
|
1851
1851
|
}
|
|
1852
1852
|
|
|
@@ -1862,9 +1862,9 @@ class MentraNexSGC: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate, SG
|
|
|
1862
1862
|
|
|
1863
1863
|
Bridge.log("NEX: Querying glasses information")
|
|
1864
1864
|
|
|
1865
|
-
let glassesInfoRequest =
|
|
1865
|
+
let glassesInfoRequest = NexGlassesInfoRequest()
|
|
1866
1866
|
|
|
1867
|
-
let phoneToGlasses =
|
|
1867
|
+
let phoneToGlasses = NexPhoneToGlasses.with {
|
|
1868
1868
|
$0.glassesInfo = glassesInfoRequest
|
|
1869
1869
|
}
|
|
1870
1870
|
|
|
@@ -1978,7 +1978,7 @@ class MentraNexSGC: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate, SG
|
|
|
1978
1978
|
|
|
1979
1979
|
private func processProtobufData(_ protobufData: Data) {
|
|
1980
1980
|
do {
|
|
1981
|
-
let glassesToPhone = try
|
|
1981
|
+
let glassesToPhone = try NexGlassesToPhone(serializedData: protobufData)
|
|
1982
1982
|
// No per-message log: String(describing: payload) stringifies the whole
|
|
1983
1983
|
// protobuf and every Bridge.log costs the JS thread.
|
|
1984
1984
|
// Bridge.log("NEX: Processing protobuf payload case: \(glassesToPhone.payload)")
|
|
@@ -2067,7 +2067,7 @@ class MentraNexSGC: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate, SG
|
|
|
2067
2067
|
|
|
2068
2068
|
// MARK: - Protobuf Event Handlers
|
|
2069
2069
|
|
|
2070
|
-
private func handleBatteryStatusProtobuf(_ batteryStatus:
|
|
2070
|
+
private func handleBatteryStatusProtobuf(_ batteryStatus: NexBatteryStatus) {
|
|
2071
2071
|
let level = Int(batteryStatus.level)
|
|
2072
2072
|
let isCharging = batteryStatus.charging
|
|
2073
2073
|
|
|
@@ -2078,7 +2078,7 @@ class MentraNexSGC: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate, SG
|
|
|
2078
2078
|
DeviceStore.shared.apply("glasses", "charging", isCharging)
|
|
2079
2079
|
}
|
|
2080
2080
|
|
|
2081
|
-
private func handleChargingStateProtobuf(_ chargingState:
|
|
2081
|
+
private func handleChargingStateProtobuf(_ chargingState: NexChargingState) {
|
|
2082
2082
|
let chargingState = chargingState.state == .charging
|
|
2083
2083
|
|
|
2084
2084
|
Bridge.log("NEX: 🔌 Charging State: \(chargingState ? "CHARGING" : "NOT_CHARGING")")
|
|
@@ -2087,7 +2087,7 @@ class MentraNexSGC: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate, SG
|
|
|
2087
2087
|
DeviceStore.shared.apply("glasses", "charging", chargingState)
|
|
2088
2088
|
}
|
|
2089
2089
|
|
|
2090
|
-
private func handleDeviceInfoProtobuf(_ deviceInfo:
|
|
2090
|
+
private func handleDeviceInfoProtobuf(_ deviceInfo: NexDeviceInfo) {
|
|
2091
2091
|
Bridge.log("NEX: 📱 Device Info: \(deviceInfo)")
|
|
2092
2092
|
|
|
2093
2093
|
// Update @Published properties (G1-compatible approach)
|
|
@@ -2095,7 +2095,7 @@ class MentraNexSGC: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate, SG
|
|
|
2095
2095
|
DeviceStore.shared.apply("glasses", "deviceHardwareModel", deviceInfo.hwModel)
|
|
2096
2096
|
}
|
|
2097
2097
|
|
|
2098
|
-
private func handleHeadPositionProtobuf(_ headPosition:
|
|
2098
|
+
private func handleHeadPositionProtobuf(_ headPosition: NexHeadPosition) {
|
|
2099
2099
|
let angle = Int(headPosition.angle)
|
|
2100
2100
|
|
|
2101
2101
|
Bridge.log("NEX: 📐 Head Position - Angle: \(angle)°")
|
|
@@ -2104,7 +2104,7 @@ class MentraNexSGC: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate, SG
|
|
|
2104
2104
|
headUpAngle = angle
|
|
2105
2105
|
}
|
|
2106
2106
|
|
|
2107
|
-
private func handleHeadUpAngleResponseProtobuf(_ response:
|
|
2107
|
+
private func handleHeadUpAngleResponseProtobuf(_ response: NexHeadUpAngleResponse) {
|
|
2108
2108
|
let success = response.success
|
|
2109
2109
|
|
|
2110
2110
|
Bridge.log("NEX: 📐 Head Up Angle Set Response - Success: \(success)")
|
|
@@ -2119,7 +2119,7 @@ class MentraNexSGC: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate, SG
|
|
|
2119
2119
|
emitEvent("HeadUpAngleResponseEvent", body: eventBody)
|
|
2120
2120
|
}
|
|
2121
2121
|
|
|
2122
|
-
private func handleVadEventProtobuf(_ vadEvent:
|
|
2122
|
+
private func handleVadEventProtobuf(_ vadEvent: NexVadEvent) {
|
|
2123
2123
|
let vadActiveState = vadEvent.state == .active
|
|
2124
2124
|
|
|
2125
2125
|
Bridge.log("NEX: 🎤 VAD Event - Voice Activity: \(vadActiveState)")
|
|
@@ -2130,7 +2130,7 @@ class MentraNexSGC: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate, SG
|
|
|
2130
2130
|
}
|
|
2131
2131
|
|
|
2132
2132
|
private func handleImageTransferCompleteProtobuf(
|
|
2133
|
-
_ transferComplete:
|
|
2133
|
+
_ transferComplete: NexImageTransferComplete
|
|
2134
2134
|
) {
|
|
2135
2135
|
let status = transferComplete.status
|
|
2136
2136
|
let missingChunks = transferComplete.missingChunks
|
|
@@ -2178,7 +2178,7 @@ class MentraNexSGC: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate, SG
|
|
|
2178
2178
|
}
|
|
2179
2179
|
}
|
|
2180
2180
|
|
|
2181
|
-
private func handleImuDataProtobuf(_ imuData:
|
|
2181
|
+
private func handleImuDataProtobuf(_ imuData: NexImuData) {
|
|
2182
2182
|
Bridge.log("NEX: 📊 IMU Data: \(imuData)")
|
|
2183
2183
|
|
|
2184
2184
|
// Update @Published properties (G1-compatible approach)
|
|
@@ -2187,7 +2187,7 @@ class MentraNexSGC: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate, SG
|
|
|
2187
2187
|
magnetometer = [imuData.mag.x, imuData.mag.y, imuData.mag.z]
|
|
2188
2188
|
}
|
|
2189
2189
|
|
|
2190
|
-
private func handleButtonEventProtobuf(_ buttonEvent:
|
|
2190
|
+
private func handleButtonEventProtobuf(_ buttonEvent: NexButtonEvent) {
|
|
2191
2191
|
let buttonNumber = Int(buttonEvent.button.rawValue)
|
|
2192
2192
|
let buttonState = buttonEvent.state
|
|
2193
2193
|
|
|
@@ -2198,7 +2198,7 @@ class MentraNexSGC: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate, SG
|
|
|
2198
2198
|
lastButtonState = "\(buttonState.rawValue)"
|
|
2199
2199
|
}
|
|
2200
2200
|
|
|
2201
|
-
private func handleHeadGestureProtobuf(_ headGesture:
|
|
2201
|
+
private func handleHeadGestureProtobuf(_ headGesture: NexHeadGesture) {
|
|
2202
2202
|
let gestureType = headGesture.gesture
|
|
2203
2203
|
|
|
2204
2204
|
Bridge.log("NEX: 👤 Head Gesture: \(gestureType)")
|
|
@@ -2450,7 +2450,7 @@ class MentraNexSGC: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate, SG
|
|
|
2450
2450
|
let eventBody: [String: Any] = [
|
|
2451
2451
|
"protobuf_schema_version": [
|
|
2452
2452
|
"schema_version": 1, // Default version
|
|
2453
|
-
"build_info": "Schema v1 |
|
|
2453
|
+
"build_info": "Schema v1 | nex_ble.proto",
|
|
2454
2454
|
"device_model": "Mentra Display",
|
|
2455
2455
|
],
|
|
2456
2456
|
]
|
|
@@ -2487,8 +2487,8 @@ class MentraNexSGC: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate, SG
|
|
|
2487
2487
|
}
|
|
2488
2488
|
|
|
2489
2489
|
private func sendDisconnectRequest() {
|
|
2490
|
-
let phoneToGlasses =
|
|
2491
|
-
$0.disconnect =
|
|
2490
|
+
let phoneToGlasses = NexPhoneToGlasses.with {
|
|
2491
|
+
$0.disconnect = NexDisconnectRequest()
|
|
2492
2492
|
}
|
|
2493
2493
|
guard let protobufData = try? phoneToGlasses.serializedData() else {
|
|
2494
2494
|
Bridge.log("NEX: ⚠️ Failed to serialize DisconnectRequest")
|
|
@@ -5,7 +5,7 @@ import Foundation
|
|
|
5
5
|
/// against the previous frame sent to this device.
|
|
6
6
|
struct SceneElement: Sendable {
|
|
7
7
|
let id: String
|
|
8
|
-
let type: String // "text" | "rect" | "image"
|
|
8
|
+
let type: String // "text" | "rect" | "image" | "circle" | "arc" | "pie" | "line" | "triangle" | "quad" | "bezier"
|
|
9
9
|
let x: Int32
|
|
10
10
|
let y: Int32
|
|
11
11
|
let w: Int32
|
|
@@ -20,11 +20,35 @@ struct SceneElement: Sendable {
|
|
|
20
20
|
/// height (grid text); anything else is the default proportional face.
|
|
21
21
|
let font: String
|
|
22
22
|
let fontSize: Int32
|
|
23
|
+
/// Shapes: solid fill instead of an outline.
|
|
24
|
+
let fill: Bool
|
|
25
|
+
/// Gray level 0..15, or -1 for the default (brightest).
|
|
26
|
+
let color: Int32
|
|
27
|
+
/// Point shapes: x0, y0, x1, y1, … in canvas pixels.
|
|
28
|
+
let points: [Int32]
|
|
29
|
+
let startDeg: Int32
|
|
30
|
+
let endDeg: Int32
|
|
31
|
+
/// Transition duration in ms (0 = none) and easing name.
|
|
32
|
+
let transitionMs: Int32
|
|
33
|
+
let easing: String
|
|
34
|
+
let path: [UInt8]
|
|
35
|
+
let pathScale: Int32
|
|
36
|
+
let fillRule: Int32
|
|
37
|
+
let rotationQ8: Int32
|
|
38
|
+
let pivotX: Int32?
|
|
39
|
+
let pivotY: Int32?
|
|
40
|
+
/// Stereo disparity in canvas px: drawn at x+depth on the left lens and
|
|
41
|
+
/// x-depth on the right, so positive values float in front of the frame.
|
|
42
|
+
let depth: Int32
|
|
23
43
|
|
|
24
44
|
init(
|
|
25
45
|
id: String, type: String, x: Int32, y: Int32, w: Int32, h: Int32, text: String?, data: String?,
|
|
26
46
|
border: Int32, radius: Int32, change: String, contentHash: String,
|
|
27
|
-
font: String = "default", fontSize: Int32 = 0
|
|
47
|
+
font: String = "default", fontSize: Int32 = 0,
|
|
48
|
+
fill: Bool = false, color: Int32 = -1, points: [Int32] = [], startDeg: Int32 = 0, endDeg: Int32 = 0,
|
|
49
|
+
transitionMs: Int32 = 0, easing: String = "ease",
|
|
50
|
+
path: [UInt8] = [], pathScale: Int32 = 256, fillRule: Int32 = 0,
|
|
51
|
+
rotationQ8: Int32 = 0, pivotX: Int32? = nil, pivotY: Int32? = nil, depth: Int32 = 0
|
|
28
52
|
) {
|
|
29
53
|
self.id = id
|
|
30
54
|
self.type = type
|
|
@@ -40,15 +64,45 @@ struct SceneElement: Sendable {
|
|
|
40
64
|
self.contentHash = contentHash
|
|
41
65
|
self.font = font
|
|
42
66
|
self.fontSize = fontSize
|
|
67
|
+
self.fill = fill
|
|
68
|
+
self.color = color
|
|
69
|
+
self.points = points
|
|
70
|
+
self.startDeg = startDeg
|
|
71
|
+
self.endDeg = endDeg
|
|
72
|
+
self.transitionMs = transitionMs
|
|
73
|
+
self.easing = easing
|
|
74
|
+
self.path = path
|
|
75
|
+
self.pathScale = pathScale
|
|
76
|
+
self.fillRule = fillRule
|
|
77
|
+
self.rotationQ8 = rotationQ8
|
|
78
|
+
self.pivotX = pivotX
|
|
79
|
+
self.pivotY = pivotY
|
|
80
|
+
self.depth = depth
|
|
43
81
|
}
|
|
44
82
|
|
|
45
83
|
func with(change: String) -> SceneElement {
|
|
46
84
|
SceneElement(
|
|
47
85
|
id: id, type: type, x: x, y: y, w: w, h: h, text: text, data: data,
|
|
48
86
|
border: border, radius: radius, change: change, contentHash: contentHash,
|
|
49
|
-
font: font, fontSize: fontSize
|
|
87
|
+
font: font, fontSize: fontSize, fill: fill, color: color, points: points,
|
|
88
|
+
startDeg: startDeg, endDeg: endDeg, transitionMs: transitionMs, easing: easing,
|
|
89
|
+
path: path, pathScale: pathScale, fillRule: fillRule, rotationQ8: rotationQ8, pivotX: pivotX, pivotY: pivotY,
|
|
90
|
+
depth: depth
|
|
50
91
|
)
|
|
51
92
|
}
|
|
93
|
+
|
|
94
|
+
/// The same element placed at the canvas origin (for standalone rasterization).
|
|
95
|
+
func atOrigin() -> SceneElement {
|
|
96
|
+
SceneElement(
|
|
97
|
+
id: id, type: type, x: 0, y: 0, w: w, h: h, text: text, data: data,
|
|
98
|
+
border: border, radius: radius, change: change, contentHash: contentHash,
|
|
99
|
+
font: font, fontSize: fontSize, fill: fill, color: color, points: points,
|
|
100
|
+
startDeg: startDeg, endDeg: endDeg, transitionMs: transitionMs, easing: easing,
|
|
101
|
+
path: path, pathScale: pathScale, fillRule: fillRule, rotationQ8: rotationQ8, pivotX: pivotX, pivotY: pivotY,
|
|
102
|
+
depth: depth
|
|
103
|
+
)
|
|
104
|
+
}
|
|
105
|
+
|
|
52
106
|
}
|
|
53
107
|
|
|
54
108
|
/// A whole scene from the host pipeline — the full frame plus per-element
|
|
@@ -67,11 +121,15 @@ struct SceneFrame: Sendable {
|
|
|
67
121
|
let removed: [String]
|
|
68
122
|
let canvasWidth: Int32
|
|
69
123
|
let canvasHeight: Int32
|
|
124
|
+
/// Host frame number, reported back in `scene_frame_presented` once the
|
|
125
|
+
/// device has the frame. 0 = native-originated (legacy sugar), never reported.
|
|
126
|
+
let seq: Int
|
|
70
127
|
|
|
71
128
|
init(
|
|
72
129
|
appId: String, epoch: Int, replay: Bool, elements: [SceneElement], removed: [String],
|
|
73
130
|
canvasWidth: Int32 = SceneFrame.defaultCanvasWidth,
|
|
74
|
-
canvasHeight: Int32 = SceneFrame.defaultCanvasHeight
|
|
131
|
+
canvasHeight: Int32 = SceneFrame.defaultCanvasHeight,
|
|
132
|
+
seq: Int = 0
|
|
75
133
|
) {
|
|
76
134
|
self.appId = appId
|
|
77
135
|
self.epoch = epoch
|
|
@@ -80,15 +138,35 @@ struct SceneFrame: Sendable {
|
|
|
80
138
|
self.removed = removed
|
|
81
139
|
self.canvasWidth = canvasWidth > 0 ? canvasWidth : SceneFrame.defaultCanvasWidth
|
|
82
140
|
self.canvasHeight = canvasHeight > 0 ? canvasHeight : SceneFrame.defaultCanvasHeight
|
|
141
|
+
self.seq = seq
|
|
83
142
|
}
|
|
84
143
|
|
|
85
144
|
func asReplay() -> SceneFrame {
|
|
86
145
|
SceneFrame(
|
|
87
146
|
appId: appId, epoch: epoch, replay: true,
|
|
88
147
|
elements: elements.map { $0.with(change: "created") }, removed: removed,
|
|
89
|
-
canvasWidth: canvasWidth, canvasHeight: canvasHeight
|
|
148
|
+
canvasWidth: canvasWidth, canvasHeight: canvasHeight, seq: seq
|
|
90
149
|
)
|
|
91
150
|
}
|
|
151
|
+
|
|
152
|
+
/// Tell the host this frame's content is on the glasses (or why it isn't).
|
|
153
|
+
func reportPresented(_ presented: Bool, reason: String? = nil) {
|
|
154
|
+
presentTag?.report(presented, reason: reason)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/// What a queued transfer must report once it lands; nil for native-originated frames.
|
|
158
|
+
var presentTag: PresentTag? {
|
|
159
|
+
seq > 0 ? PresentTag(seq: seq, appId: appId) : nil
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
struct PresentTag: Sendable {
|
|
163
|
+
let seq: Int
|
|
164
|
+
let appId: String
|
|
165
|
+
|
|
166
|
+
func report(_ presented: Bool, reason: String? = nil) {
|
|
167
|
+
Bridge.sendSceneFramePresented(seq: seq, appId: appId, presented: presented, reason: reason)
|
|
168
|
+
}
|
|
169
|
+
}
|
|
92
170
|
}
|
|
93
171
|
|
|
94
172
|
@MainActor
|
|
@@ -187,6 +265,9 @@ protocol SGCManager {
|
|
|
187
265
|
/// True while the glasses' own modal nav UI owns the display — DeviceManager
|
|
188
266
|
/// suppresses normal display traffic until exit.
|
|
189
267
|
var navigationModeActive: Bool { get }
|
|
268
|
+
/// True when the driver renders scenes itself with link-driven last-wins
|
|
269
|
+
/// coalescing; DeviceManager then skips the display pacer.
|
|
270
|
+
var supportsCfwSceneRendering: Bool { get }
|
|
190
271
|
func enterNavigationMode()
|
|
191
272
|
/// `arrived` shows the device's "navigation complete" screen before exiting.
|
|
192
273
|
func exitNavigationMode(arrived: Bool)
|
|
@@ -208,6 +289,9 @@ protocol SGCManager {
|
|
|
208
289
|
/// Live cfw_scene_renderer switch (only the G2 CFW has one): swap the
|
|
209
290
|
/// on-glass surface between the CFW carrier and a blank stock page.
|
|
210
291
|
func cfwSceneRendererToggled(_ enabled: Bool) async
|
|
292
|
+
/// Live cfw_fast_ble switch (G2 Glassly CFW revision 26+): ask the lenses
|
|
293
|
+
/// for the 7.5 ms fast connection profile, or return them to stock.
|
|
294
|
+
func cfwFastBleToggled(_ enabled: Bool) async
|
|
211
295
|
|
|
212
296
|
// MARK: - Dashboard Menu
|
|
213
297
|
|
|
@@ -221,12 +305,13 @@ protocol SGCManager {
|
|
|
221
305
|
|
|
222
306
|
func sendCalendarEvents(_ events: [[String: Any]])
|
|
223
307
|
|
|
224
|
-
// MARK: - Phone Notifications
|
|
308
|
+
// MARK: - Phone Notifications
|
|
225
309
|
|
|
226
|
-
///
|
|
227
|
-
func
|
|
228
|
-
|
|
229
|
-
|
|
310
|
+
/// Apply glasses-neutral notification behaviour to the connected firmware.
|
|
311
|
+
func setGlassesNotificationConfig(_ config: GlassesNotificationConfig) throws
|
|
312
|
+
func glassesNotificationStatus() -> GlassesNotificationStatus
|
|
313
|
+
/// Hand one notification to glasses that present content themselves.
|
|
314
|
+
func uploadPhoneNotification(_ upload: PhoneNotificationUpload) throws
|
|
230
315
|
|
|
231
316
|
// MARK: - Dashboard Display Settings
|
|
232
317
|
|
|
@@ -262,6 +347,8 @@ protocol SGCManager {
|
|
|
262
347
|
func ping()
|
|
263
348
|
func dbg1()
|
|
264
349
|
func dbg2()
|
|
350
|
+
func dbg3()
|
|
351
|
+
func dbg4()
|
|
265
352
|
|
|
266
353
|
// MARK: - Network Management
|
|
267
354
|
|
|
@@ -310,6 +397,10 @@ extension SGCManager {
|
|
|
310
397
|
/// Default: no-op. Only G2 distinguishes stock from Glassly CFW firmware.
|
|
311
398
|
func awaitFirmwareClassification() async {}
|
|
312
399
|
|
|
400
|
+
/// Default: no-op debug hooks; only G2 wires them.
|
|
401
|
+
func dbg3() {}
|
|
402
|
+
func dbg4() {}
|
|
403
|
+
|
|
313
404
|
/// Default: no-op. Only G2 renders positioned text containers; other glasses ignore it.
|
|
314
405
|
func sendPositionedText(
|
|
315
406
|
_: String, x _: Int32, y _: Int32, width _: Int32, height _: Int32,
|
|
@@ -404,6 +495,8 @@ extension SGCManager {
|
|
|
404
495
|
for id in frame.removed where !paintedIds.contains(id) {
|
|
405
496
|
await removeLayoutElement(id, layoutId: frame.appId)
|
|
406
497
|
}
|
|
498
|
+
// No device ack on this path: the awaited verbs above cover the sends.
|
|
499
|
+
frame.reportPresented(true)
|
|
407
500
|
}
|
|
408
501
|
|
|
409
502
|
// MARK: - Video recording (default: ignore custom settings, use saved defaults)
|
|
@@ -437,6 +530,12 @@ extension SGCManager {
|
|
|
437
530
|
|
|
438
531
|
func cfwSceneRendererToggled(_: Bool) async {}
|
|
439
532
|
|
|
533
|
+
func cfwFastBleToggled(_: Bool) async {}
|
|
534
|
+
|
|
535
|
+
var supportsCfwSceneRendering: Bool {
|
|
536
|
+
false
|
|
537
|
+
}
|
|
538
|
+
|
|
440
539
|
// MARK: - Native navigation defaults (only G2 has a glasses-side nav UI)
|
|
441
540
|
|
|
442
541
|
var navigationModeActive: Bool {
|
|
@@ -476,11 +575,19 @@ extension SGCManager {
|
|
|
476
575
|
|
|
477
576
|
func sendCalendarEvents(_: [[String: Any]]) {}
|
|
478
577
|
|
|
479
|
-
// MARK: - Phone Notifications (default
|
|
578
|
+
// MARK: - Phone Notifications (default unsupported — only G2 has a notification surface)
|
|
480
579
|
|
|
481
|
-
func
|
|
580
|
+
func setGlassesNotificationConfig(_: GlassesNotificationConfig) throws {
|
|
581
|
+
throw GlassesNotificationError.unsupported
|
|
582
|
+
}
|
|
482
583
|
|
|
483
|
-
func
|
|
584
|
+
func glassesNotificationStatus() -> GlassesNotificationStatus {
|
|
585
|
+
.unsupported
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
func uploadPhoneNotification(_: PhoneNotificationUpload) throws {
|
|
589
|
+
throw GlassesNotificationError.unsupported
|
|
590
|
+
}
|
|
484
591
|
|
|
485
592
|
// MARK: - Dashboard Display Settings (default no-op — only G2 supports this)
|
|
486
593
|
|