@glassly/bluetooth-sdk 0.1.0-dev.102 → 0.1.0-dev.103
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/android/src/main/java/com/glassly/bluetoothsdk/GeneratedReleaseMetadata.kt +3 -3
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2.kt +26 -35
- package/build/generated/releaseMetadata.js +3 -3
- package/build/generated/releaseMetadata.js.map +1 -1
- package/ios/BluetoothSdkModule.swift +1 -0
- package/ios/Source/BluetoothSdkDefaults.swift +1 -1
- package/ios/Source/GeneratedReleaseMetadata.swift +3 -3
- package/ios/Source/ObservableStore.swift +15 -2
- package/ios/Source/sgcs/G2.swift +47 -39
- package/package.json +1 -1
- package/src/generated/releaseMetadata.ts +3 -3
|
@@ -3,9 +3,9 @@ package com.glassly.bluetoothsdk
|
|
|
3
3
|
/** Generated by release CI. Do not edit in a release checkout. */
|
|
4
4
|
internal object GeneratedReleaseMetadata {
|
|
5
5
|
const val FAMILY_BASE_VERSION: String = "0.1.0"
|
|
6
|
-
const val RELEASE_IDENTITY: String = "0.1.0-dev.
|
|
7
|
-
const val RELEASE_SET_ID: String = "glassly-0.1.0-dev.
|
|
8
|
-
const val SOURCE_COMMIT: String = "
|
|
6
|
+
const val RELEASE_IDENTITY: String = "0.1.0-dev.103"
|
|
7
|
+
const val RELEASE_SET_ID: String = "glassly-0.1.0-dev.103"
|
|
8
|
+
const val SOURCE_COMMIT: String = "ba3c3655d8255014fc9292896ef73dd34f12afff"
|
|
9
9
|
const val OTA_MANIFEST_URL: String = ""
|
|
10
10
|
const val OTA_MANIFEST_SHA256: String = ""
|
|
11
11
|
}
|
|
@@ -1734,9 +1734,9 @@ class G2 : SGCManager() {
|
|
|
1734
1734
|
private var recoveryInFlight = false
|
|
1735
1735
|
private var lastRecoveryRebuildMs: Long = 0
|
|
1736
1736
|
private val RECOVERY_DEBOUNCE_MS: Long = 1500
|
|
1737
|
-
//
|
|
1738
|
-
// down
|
|
1739
|
-
@Volatile private var
|
|
1737
|
+
// Stock notifications panel hold: page rebuilds, fbguard lease and CFW frames stay
|
|
1738
|
+
// down while the panel opens so EvenHub sends cannot snatch the screen back.
|
|
1739
|
+
@Volatile private var notificationsPanelHold = false
|
|
1740
1740
|
|
|
1741
1741
|
// Dashboard menu state
|
|
1742
1742
|
private var menuAppIdToPackageName: MutableMap<Int, String> = mutableMapOf()
|
|
@@ -2889,7 +2889,7 @@ class G2 : SGCManager() {
|
|
|
2889
2889
|
/** Install the single carrier and render the whole scene through CFW mode 3. */
|
|
2890
2890
|
private suspend fun applyCfwScene(frame: SceneFrame) {
|
|
2891
2891
|
// Held: retain the frame so the post-hold rebuild re-sends it, but paint nothing now.
|
|
2892
|
-
if (
|
|
2892
|
+
if (notificationsPanelHold) {
|
|
2893
2893
|
cfwLastFrame = frame
|
|
2894
2894
|
return
|
|
2895
2895
|
}
|
|
@@ -2974,7 +2974,7 @@ class G2 : SGCManager() {
|
|
|
2974
2974
|
* under a preserved direct frame.
|
|
2975
2975
|
*/
|
|
2976
2976
|
private fun startFbLease() {
|
|
2977
|
-
if (fbLeaseJob != null ||
|
|
2977
|
+
if (fbLeaseJob != null || notificationsPanelHold) return
|
|
2978
2978
|
sendFaceclawFbControl(acquire = true)
|
|
2979
2979
|
fbLeaseJob =
|
|
2980
2980
|
displayScope.launch {
|
|
@@ -3664,8 +3664,8 @@ class G2 : SGCManager() {
|
|
|
3664
3664
|
Bridge.log("G2: rebuildState() skipped — navigation mode owns the screen")
|
|
3665
3665
|
return
|
|
3666
3666
|
}
|
|
3667
|
-
if (
|
|
3668
|
-
Bridge.log("G2: rebuildState() skipped —
|
|
3667
|
+
if (notificationsPanelHold) {
|
|
3668
|
+
Bridge.log("G2: rebuildState() skipped — notifications panel hold")
|
|
3669
3669
|
return
|
|
3670
3670
|
}
|
|
3671
3671
|
Bridge.log("G2: rebuildState()")
|
|
@@ -4186,9 +4186,20 @@ class G2 : SGCManager() {
|
|
|
4186
4186
|
* 3. SKILL{skillId=NOTIFICATION, skillParam=show, ...} — dispatches the intent
|
|
4187
4187
|
* The SKILL step alone is ignored by firmware; the preceding ENTER+ASK
|
|
4188
4188
|
* supply the session context that lets the glasses act on the SKILL.
|
|
4189
|
+
* Our EvenHub page is torn down first and every path that would bring it
|
|
4190
|
+
* back (recovery rebuilds, fbguard lease, CFW frames, mic re-arm) is held
|
|
4191
|
+
* for 3 s, or the sends race the stock panel for the screen.
|
|
4189
4192
|
*/
|
|
4190
4193
|
override suspend fun showNotificationsPanel() {
|
|
4191
4194
|
Bridge.log("G2: showNotificationsPanel()")
|
|
4195
|
+
|
|
4196
|
+
notificationsPanelHold = true
|
|
4197
|
+
stopFbLease(sendRelease = true)
|
|
4198
|
+
sendEvenHubCommand(EvenHubProto.shutdownMessage())
|
|
4199
|
+
pageCreated = false
|
|
4200
|
+
// Page death takes the mic with it; intent is untouched so the post-hold rebuild re-arms.
|
|
4201
|
+
DeviceStore.apply("glasses", "micEnabled", false)
|
|
4202
|
+
|
|
4192
4203
|
val enterPayload = EvenAIProto.aiCtrl(
|
|
4193
4204
|
magicRandom = sendManager.nextMagicRandom(),
|
|
4194
4205
|
status = 2 // EVEN_AI_ENTER
|
|
@@ -4213,6 +4224,10 @@ class G2 : SGCManager() {
|
|
|
4213
4224
|
fTextEnd = 1
|
|
4214
4225
|
)
|
|
4215
4226
|
sendEvenAICommand(skillPayload)
|
|
4227
|
+
|
|
4228
|
+
delay(3_000)
|
|
4229
|
+
notificationsPanelHold = false
|
|
4230
|
+
Bridge.log("G2: notifications resurrect re-enabled")
|
|
4216
4231
|
}
|
|
4217
4232
|
|
|
4218
4233
|
override fun setBrightness(level: Int, autoMode: Boolean) {
|
|
@@ -4527,7 +4542,7 @@ class G2 : SGCManager() {
|
|
|
4527
4542
|
// off then back on (the firmware needs the off→on edge to re-arm).
|
|
4528
4543
|
evenHubMicIntent = true
|
|
4529
4544
|
// Held: intent is recorded; the post-hold rebuild re-arms the mic.
|
|
4530
|
-
if (
|
|
4545
|
+
if (notificationsPanelHold) return
|
|
4531
4546
|
DeviceStore.apply("glasses", "micEnabled", true)
|
|
4532
4547
|
val msg = EvenHubProto.audioControlMessage(false)
|
|
4533
4548
|
sendEvenHubCommand(msg)
|
|
@@ -4680,7 +4695,7 @@ class G2 : SGCManager() {
|
|
|
4680
4695
|
textContainers.clear()
|
|
4681
4696
|
dashboardShowing = 0
|
|
4682
4697
|
dashboardOpening = false
|
|
4683
|
-
|
|
4698
|
+
notificationsPanelHold = false
|
|
4684
4699
|
heartbeatCounter = 0
|
|
4685
4700
|
currentBitmapBase64 = ""
|
|
4686
4701
|
menuAppIdToPackageName.clear()
|
|
@@ -4727,33 +4742,9 @@ class G2 : SGCManager() {
|
|
|
4727
4742
|
sendEvenHubHeartbeat()
|
|
4728
4743
|
}
|
|
4729
4744
|
|
|
4730
|
-
|
|
4731
|
-
* Tear down the EvenHub page and hold every path that would bring it back
|
|
4732
|
-
* (recovery rebuilds, fbguard lease, CFW frames, mic re-arm) until [dbg2]
|
|
4733
|
-
* lifts the hold. Isolates the stock notifications UI from our sends.
|
|
4734
|
-
*/
|
|
4735
|
-
override fun dbg1() {
|
|
4736
|
-
Bridge.log("G2: dbg1() — EvenHub page down, resurrect held")
|
|
4737
|
-
debugPageHold = true
|
|
4738
|
-
stopFbLease(sendRelease = true)
|
|
4739
|
-
sendEvenHubCommand(EvenHubProto.shutdownMessage())
|
|
4740
|
-
pageCreated = false
|
|
4741
|
-
// Page death takes the mic with it; intent is untouched so the post-hold rebuild re-arms.
|
|
4742
|
-
DeviceStore.apply("glasses", "micEnabled", false)
|
|
4743
|
-
}
|
|
4745
|
+
override fun dbg1() {}
|
|
4744
4746
|
|
|
4745
|
-
|
|
4746
|
-
* Open the stock notifications panel, then lift the dbg1 hold 1 s later so
|
|
4747
|
-
* recovery resumes on the next firmware event / pending content.
|
|
4748
|
-
*/
|
|
4749
|
-
override fun dbg2() {
|
|
4750
|
-
displayScope.launch {
|
|
4751
|
-
showNotificationsPanel()
|
|
4752
|
-
delay(1_000)
|
|
4753
|
-
debugPageHold = false
|
|
4754
|
-
Bridge.log("G2: dbg2() — resurrect re-enabled")
|
|
4755
|
-
}
|
|
4756
|
-
}
|
|
4747
|
+
override fun dbg2() {}
|
|
4757
4748
|
|
|
4758
4749
|
/** Raw wake lease start — no durable intent; a reconnect drops it. */
|
|
4759
4750
|
override fun dbg3() {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
export const BLUETOOTH_SDK_RELEASE_METADATA = Object.freeze({
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"familyBaseVersion": "0.1.0",
|
|
4
|
-
"releaseIdentity": "0.1.0-dev.
|
|
5
|
-
"releaseSetId": "glassly-0.1.0-dev.
|
|
6
|
-
"sourceCommit": "
|
|
4
|
+
"releaseIdentity": "0.1.0-dev.103",
|
|
5
|
+
"releaseSetId": "glassly-0.1.0-dev.103",
|
|
6
|
+
"sourceCommit": "ba3c3655d8255014fc9292896ef73dd34f12afff",
|
|
7
7
|
"otaManifestUrl": null,
|
|
8
8
|
"otaManifestSha256": null
|
|
9
9
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"releaseMetadata.js","sourceRoot":"","sources":["../../src/generated/releaseMetadata.ts"],"names":[],"mappings":"AAWA,MAAM,CAAC,MAAM,8BAA8B,GAA0C,MAAM,CAAC,MAAM,CAAC;IACjG,eAAe,EAAE,CAAC;IAClB,mBAAmB,EAAE,OAAO;IAC5B,iBAAiB,EAAE,eAAe;IAClC,cAAc,EAAE,uBAAuB;IACvC,cAAc,EAAE,0CAA0C;IAC1D,gBAAgB,EAAE,IAAI;IACtB,mBAAmB,EAAE,IAAI;CAC1B,CAAC,CAAA","sourcesContent":["/** Generated by release CI. Do not edit in a release checkout. */\nexport interface BluetoothSdkReleaseMetadata {\n schemaVersion: 1\n familyBaseVersion: string | null\n releaseIdentity: string | null\n releaseSetId: string | null\n sourceCommit: string | null\n otaManifestUrl: string | null\n otaManifestSha256: string | null\n}\n\nexport const BLUETOOTH_SDK_RELEASE_METADATA: Readonly<BluetoothSdkReleaseMetadata> = Object.freeze({\n \"schemaVersion\": 1,\n \"familyBaseVersion\": \"0.1.0\",\n \"releaseIdentity\": \"0.1.0-dev.
|
|
1
|
+
{"version":3,"file":"releaseMetadata.js","sourceRoot":"","sources":["../../src/generated/releaseMetadata.ts"],"names":[],"mappings":"AAWA,MAAM,CAAC,MAAM,8BAA8B,GAA0C,MAAM,CAAC,MAAM,CAAC;IACjG,eAAe,EAAE,CAAC;IAClB,mBAAmB,EAAE,OAAO;IAC5B,iBAAiB,EAAE,eAAe;IAClC,cAAc,EAAE,uBAAuB;IACvC,cAAc,EAAE,0CAA0C;IAC1D,gBAAgB,EAAE,IAAI;IACtB,mBAAmB,EAAE,IAAI;CAC1B,CAAC,CAAA","sourcesContent":["/** Generated by release CI. Do not edit in a release checkout. */\nexport interface BluetoothSdkReleaseMetadata {\n schemaVersion: 1\n familyBaseVersion: string | null\n releaseIdentity: string | null\n releaseSetId: string | null\n sourceCommit: string | null\n otaManifestUrl: string | null\n otaManifestSha256: string | null\n}\n\nexport const BLUETOOTH_SDK_RELEASE_METADATA: Readonly<BluetoothSdkReleaseMetadata> = Object.freeze({\n \"schemaVersion\": 1,\n \"familyBaseVersion\": \"0.1.0\",\n \"releaseIdentity\": \"0.1.0-dev.103\",\n \"releaseSetId\": \"glassly-0.1.0-dev.103\",\n \"sourceCommit\": \"ba3c3655d8255014fc9292896ef73dd34f12afff\",\n \"otaManifestUrl\": null,\n \"otaManifestSha256\": null\n})\n"]}
|
|
@@ -11,7 +11,7 @@ enum BluetoothSdkDefaults {
|
|
|
11
11
|
static let voiceActivityDetectionEnabled = false
|
|
12
12
|
static let loudnessGateEnabled = true
|
|
13
13
|
private static let infoSdkVersionKey = "BluetoothSdkVersion"
|
|
14
|
-
private static let swiftPackageSdkVersion = "0.1.0-dev.
|
|
14
|
+
private static let swiftPackageSdkVersion = "0.1.0-dev.103"
|
|
15
15
|
private static let swiftPackageSdkVersionPlaceholder = "__GLASSLY" + "_BLUETOOTH_SDK_VERSION__"
|
|
16
16
|
|
|
17
17
|
private static func normalizedSdkVersion(_ value: String?) -> String? {
|
|
@@ -3,9 +3,9 @@ import Foundation
|
|
|
3
3
|
/// Generated by release CI. Do not edit in a release checkout.
|
|
4
4
|
enum GeneratedReleaseMetadata {
|
|
5
5
|
static let familyBaseVersion = "0.1.0"
|
|
6
|
-
static let releaseIdentity = "0.1.0-dev.
|
|
7
|
-
static let releaseSetId = "glassly-0.1.0-dev.
|
|
8
|
-
static let sourceCommit = "
|
|
6
|
+
static let releaseIdentity = "0.1.0-dev.103"
|
|
7
|
+
static let releaseSetId = "glassly-0.1.0-dev.103"
|
|
8
|
+
static let sourceCommit = "ba3c3655d8255014fc9292896ef73dd34f12afff"
|
|
9
9
|
static let otaManifestUrl = ""
|
|
10
10
|
static let otaManifestSha256 = ""
|
|
11
11
|
}
|
|
@@ -106,13 +106,26 @@ class ObservableStore {
|
|
|
106
106
|
if let l = lhs as? Double, let r = rhs as? Double { return l == r }
|
|
107
107
|
if let l = lhs as? [String], let r = rhs as? [String] { return l == r }
|
|
108
108
|
if let l = lhs as? [[String: Any]], let r = rhs as? [[String: Any]] {
|
|
109
|
-
return
|
|
109
|
+
return jsonEqual(l, r)
|
|
110
|
+
}
|
|
111
|
+
// Dict-valued settings (notification_ctrl / notification_filter) — without this every
|
|
112
|
+
// apply re-fires the subscriber, e.g. a full whitelist file push to both arms.
|
|
113
|
+
if let l = lhs as? [String: Any], let r = rhs as? [String: Any] {
|
|
114
|
+
return jsonEqual(l, r)
|
|
110
115
|
}
|
|
111
116
|
return false
|
|
112
117
|
}
|
|
113
118
|
|
|
119
|
+
/// Unserializable on either side is "changed" — nil == nil must not read as equal.
|
|
120
|
+
private func jsonEqual(_ lhs: Any, _ rhs: Any) -> Bool {
|
|
121
|
+
guard let l = toJson(lhs), let r = toJson(rhs) else { return false }
|
|
122
|
+
return l == r
|
|
123
|
+
}
|
|
124
|
+
|
|
114
125
|
private func toJson(_ value: Any) -> String? {
|
|
115
|
-
guard
|
|
126
|
+
guard JSONSerialization.isValidJSONObject(value),
|
|
127
|
+
let data = try? JSONSerialization.data(withJSONObject: value, options: [.sortedKeys])
|
|
128
|
+
else { return nil }
|
|
116
129
|
return String(data: data, encoding: .utf8)
|
|
117
130
|
}
|
|
118
131
|
}
|
package/ios/Source/sgcs/G2.swift
CHANGED
|
@@ -1841,9 +1841,9 @@ final class G2: NSObject, SGCManager {
|
|
|
1841
1841
|
private var recoveryInFlight = false
|
|
1842
1842
|
private var lastRecoveryRebuildMs: Int64 = 0
|
|
1843
1843
|
private let RECOVERY_DEBOUNCE_MS: Int64 = 1500
|
|
1844
|
-
//
|
|
1845
|
-
// down
|
|
1846
|
-
private var
|
|
1844
|
+
// Stock notifications panel hold: page rebuilds, fbguard lease and CFW frames stay
|
|
1845
|
+
// down while the panel opens so EvenHub sends cannot snatch the screen back.
|
|
1846
|
+
private var notificationsPanelHold = false
|
|
1847
1847
|
// A systemExit that arrives while the page acked a carrier fragment this
|
|
1848
1848
|
// recently is a phantom (the firmware emits exits even for healthy pages)
|
|
1849
1849
|
// and is dropped entirely — see the SysEvent handler.
|
|
@@ -2582,7 +2582,8 @@ final class G2: NSObject, SGCManager {
|
|
|
2582
2582
|
let ctrl = DeviceStore.shared.get("bluetooth", "notification_ctrl") as? [String: Any] ?? [:]
|
|
2583
2583
|
let enabled = ctrl["enabled"] as? Bool ?? true
|
|
2584
2584
|
let autoDisplay = ctrl["auto_display"] as? Bool ?? true
|
|
2585
|
-
|
|
2585
|
+
// Nested numbers cross the Expo bridge as Double; `as? Int` would silently fall back to 5.
|
|
2586
|
+
let dispTime = (ctrl["disp_time"] as? NSNumber)?.intValue ?? 5
|
|
2586
2587
|
let avoidDisturb = ctrl["avoid_disturb"] as? Bool ?? true
|
|
2587
2588
|
|
|
2588
2589
|
var ctrlW = ProtobufWriter()
|
|
@@ -2623,7 +2624,14 @@ final class G2: NSObject, SGCManager {
|
|
|
2623
2624
|
// The file is written even when filtering is off so a later enable
|
|
2624
2625
|
// toggles against the user's current list, not a stale one.
|
|
2625
2626
|
let json = G2NotifyFile.whitelistJson(filterEnabled: enabled, apps: entries)
|
|
2626
|
-
await sendNotifyFile(named: G2NotifyFile.whitelistFilename, content: json)
|
|
2627
|
+
let pushed = await sendNotifyFile(named: G2NotifyFile.whitelistFilename, content: json)
|
|
2628
|
+
// Enabling against a list that never landed (flash in progress, file characteristic not
|
|
2629
|
+
// yet discovered) would filter on whatever is on flash — absent means every notification
|
|
2630
|
+
// is dropped. Leave the firmware toggle alone; device-ready re-pushes the whole config.
|
|
2631
|
+
if enabled, !pushed {
|
|
2632
|
+
Bridge.log("G2: notification filter enable deferred — whitelist file not pushed")
|
|
2633
|
+
return
|
|
2634
|
+
}
|
|
2627
2635
|
|
|
2628
2636
|
var wlW = ProtobufWriter()
|
|
2629
2637
|
wlW.writeInt32Field(1, enabled ? 0 : 1) // whitelistDisable (disabled = fail-open)
|
|
@@ -2639,14 +2647,20 @@ final class G2: NSObject, SGCManager {
|
|
|
2639
2647
|
/// characteristic): FILE_CHECK header → START → DATA → END, per arm. The
|
|
2640
2648
|
/// glasses cache by checksum but still expect the data packets either way,
|
|
2641
2649
|
/// so no branching on the CACHE_HIT/MISS response is needed.
|
|
2642
|
-
|
|
2643
|
-
|
|
2650
|
+
/// Returns false when the file was skipped for any connected arm (flash in
|
|
2651
|
+
/// progress, file characteristic not discovered) so callers don't act on it.
|
|
2652
|
+
@discardableResult
|
|
2653
|
+
private func sendNotifyFile(named filename: String, content: Data) async -> Bool {
|
|
2654
|
+
guard !firmwareFlashLifecycleActive, !firmwareFlasher.isActive else { return false }
|
|
2644
2655
|
let header = G2NotifyFile.fileCheckHeader(filename: filename, content: content)
|
|
2645
2656
|
var pushedSides: [String] = []
|
|
2657
|
+
var skippedArm = false
|
|
2646
2658
|
for right in [true, false] {
|
|
2647
|
-
guard let peripheral = right ? rightPeripheral : leftPeripheral
|
|
2648
|
-
|
|
2649
|
-
|
|
2659
|
+
guard let peripheral = right ? rightPeripheral : leftPeripheral else { continue }
|
|
2660
|
+
guard let char = right ? rightFileWriteChar : leftFileWriteChar else {
|
|
2661
|
+
skippedArm = true
|
|
2662
|
+
continue
|
|
2663
|
+
}
|
|
2650
2664
|
let writeType: CBCharacteristicWriteType =
|
|
2651
2665
|
char.properties.contains(.writeWithoutResponse) ? .withoutResponse : .withResponse
|
|
2652
2666
|
|
|
@@ -2670,6 +2684,7 @@ final class G2: NSObject, SGCManager {
|
|
|
2670
2684
|
Bridge.log(
|
|
2671
2685
|
"G2: Pushed \(filename) (\(content.count) bytes) to [\(pushedSides.joined(separator: ","))]"
|
|
2672
2686
|
)
|
|
2687
|
+
return !pushedSides.isEmpty && !skippedArm
|
|
2673
2688
|
}
|
|
2674
2689
|
|
|
2675
2690
|
// MARK: - SGCManager: Display Control
|
|
@@ -2924,7 +2939,7 @@ final class G2: NSObject, SGCManager {
|
|
|
2924
2939
|
/// `cfwLastFrame` once the new page is up.
|
|
2925
2940
|
private func applyCfwScene(_ frame: SceneFrame) async {
|
|
2926
2941
|
// Held: retain the frame so the post-hold rebuild re-sends it, but paint nothing now.
|
|
2927
|
-
if
|
|
2942
|
+
if notificationsPanelHold {
|
|
2928
2943
|
cfwLastFrame = frame
|
|
2929
2944
|
return
|
|
2930
2945
|
}
|
|
@@ -3001,7 +3016,7 @@ final class G2: NSObject, SGCManager {
|
|
|
3001
3016
|
/// is inactive or the page went down, so a stock surface (dashboard, nav)
|
|
3002
3017
|
/// is never frozen under a preserved direct frame.
|
|
3003
3018
|
private func startFbLease() {
|
|
3004
|
-
guard fbLeaseTask == nil, !
|
|
3019
|
+
guard fbLeaseTask == nil, !notificationsPanelHold else { return }
|
|
3005
3020
|
sendFaceclawFbControl(acquire: true)
|
|
3006
3021
|
fbLeaseTask = Task { @MainActor [weak self] in
|
|
3007
3022
|
while !Task.isCancelled {
|
|
@@ -4085,8 +4100,8 @@ final class G2: NSObject, SGCManager {
|
|
|
4085
4100
|
Bridge.log("G2: rebuildState() skipped — navigation mode owns the screen")
|
|
4086
4101
|
return
|
|
4087
4102
|
}
|
|
4088
|
-
if
|
|
4089
|
-
Bridge.log("G2: rebuildState() skipped —
|
|
4103
|
+
if notificationsPanelHold {
|
|
4104
|
+
Bridge.log("G2: rebuildState() skipped — notifications panel hold")
|
|
4090
4105
|
return
|
|
4091
4106
|
}
|
|
4092
4107
|
Bridge.log("G2: rebuildState()")
|
|
@@ -4766,7 +4781,7 @@ final class G2: NSObject, SGCManager {
|
|
|
4766
4781
|
// toggle it off then back on (the firmware needs the off→on edge to re-arm).
|
|
4767
4782
|
evenHubMicIntent = true
|
|
4768
4783
|
// Held: intent is recorded; the post-hold rebuild re-arms the mic.
|
|
4769
|
-
if
|
|
4784
|
+
if notificationsPanelHold { return }
|
|
4770
4785
|
DeviceStore.shared.apply("glasses", "micEnabled", true)
|
|
4771
4786
|
let msg = EvenHubProto.audioControlMessage(enable: false)
|
|
4772
4787
|
sendEvenHubCommand(msg)
|
|
@@ -4890,7 +4905,7 @@ final class G2: NSObject, SGCManager {
|
|
|
4890
4905
|
stockImageStreamWarmedUp = false
|
|
4891
4906
|
dashboardShowing = 0
|
|
4892
4907
|
dashboardOpening = false
|
|
4893
|
-
|
|
4908
|
+
notificationsPanelHold = false
|
|
4894
4909
|
heartbeatCounter = 0
|
|
4895
4910
|
cfwCapabilityString = nil
|
|
4896
4911
|
cfwClassified = false
|
|
@@ -5031,9 +5046,19 @@ final class G2: NSObject, SGCManager {
|
|
|
5031
5046
|
/// 3. SKILL{skillId=NOTIFICATION, skillParam=show, ...} — dispatches the intent
|
|
5032
5047
|
/// The SKILL step alone is ignored by firmware; the preceding ENTER+ASK
|
|
5033
5048
|
/// supply the session context that lets the glasses act on the SKILL.
|
|
5049
|
+
/// Our EvenHub page is torn down first and every path that would bring it
|
|
5050
|
+
/// back (recovery rebuilds, fbguard lease, CFW frames, mic re-arm) is held
|
|
5051
|
+
/// for 3 s, or the sends race the stock panel for the screen.
|
|
5034
5052
|
func showNotificationsPanel() async {
|
|
5035
5053
|
Bridge.log("G2: showNotificationsPanel()")
|
|
5036
5054
|
|
|
5055
|
+
notificationsPanelHold = true
|
|
5056
|
+
stopFbLease(sendRelease: true)
|
|
5057
|
+
sendEvenHubCommand(EvenHubProto.shutdownMessage())
|
|
5058
|
+
pageCreated = false
|
|
5059
|
+
// Page death takes the mic with it; intent is untouched so the post-hold rebuild re-arms.
|
|
5060
|
+
DeviceStore.shared.apply("glasses", "micEnabled", false)
|
|
5061
|
+
|
|
5037
5062
|
let enterPayload = EvenAIProto.aiCtrl(
|
|
5038
5063
|
magicRandom: sendManager.nextMagicRandom(),
|
|
5039
5064
|
status: 2 // EVEN_AI_ENTER
|
|
@@ -5054,32 +5079,15 @@ final class G2: NSObject, SGCManager {
|
|
|
5054
5079
|
text: " ",
|
|
5055
5080
|
streamEnable: 1, fTextEnd: 1
|
|
5056
5081
|
)
|
|
5057
|
-
}
|
|
5058
5082
|
|
|
5059
|
-
|
|
5060
|
-
|
|
5061
|
-
|
|
5062
|
-
func dbg1() {
|
|
5063
|
-
Bridge.log("G2: dbg1() — EvenHub page down, resurrect held")
|
|
5064
|
-
debugPageHold = true
|
|
5065
|
-
stopFbLease(sendRelease: true)
|
|
5066
|
-
sendEvenHubCommand(EvenHubProto.shutdownMessage())
|
|
5067
|
-
pageCreated = false
|
|
5068
|
-
// Page death takes the mic with it; intent is untouched so the post-hold rebuild re-arms.
|
|
5069
|
-
DeviceStore.shared.apply("glasses", "micEnabled", false)
|
|
5083
|
+
try? await Task.sleep(nanoseconds: 3_000_000_000)
|
|
5084
|
+
notificationsPanelHold = false
|
|
5085
|
+
Bridge.log("G2: notifications resurrect re-enabled")
|
|
5070
5086
|
}
|
|
5071
5087
|
|
|
5072
|
-
|
|
5073
|
-
|
|
5074
|
-
func dbg2() {
|
|
5075
|
-
Task { [weak self] in
|
|
5076
|
-
await self?.showNotificationsPanel()
|
|
5077
|
-
try? await Task.sleep(nanoseconds: 1_000_000_000)
|
|
5078
|
-
guard let self else { return }
|
|
5079
|
-
self.debugPageHold = false
|
|
5080
|
-
Bridge.log("G2: dbg2() — resurrect re-enabled")
|
|
5081
|
-
}
|
|
5082
|
-
}
|
|
5088
|
+
func dbg1() {}
|
|
5089
|
+
|
|
5090
|
+
func dbg2() {}
|
|
5083
5091
|
|
|
5084
5092
|
/// Raw wake lease start — no durable intent; a reconnect drops it.
|
|
5085
5093
|
func dbg3() {
|
package/package.json
CHANGED
|
@@ -12,9 +12,9 @@ export interface BluetoothSdkReleaseMetadata {
|
|
|
12
12
|
export const BLUETOOTH_SDK_RELEASE_METADATA: Readonly<BluetoothSdkReleaseMetadata> = Object.freeze({
|
|
13
13
|
"schemaVersion": 1,
|
|
14
14
|
"familyBaseVersion": "0.1.0",
|
|
15
|
-
"releaseIdentity": "0.1.0-dev.
|
|
16
|
-
"releaseSetId": "glassly-0.1.0-dev.
|
|
17
|
-
"sourceCommit": "
|
|
15
|
+
"releaseIdentity": "0.1.0-dev.103",
|
|
16
|
+
"releaseSetId": "glassly-0.1.0-dev.103",
|
|
17
|
+
"sourceCommit": "ba3c3655d8255014fc9292896ef73dd34f12afff",
|
|
18
18
|
"otaManifestUrl": null,
|
|
19
19
|
"otaManifestSha256": null
|
|
20
20
|
})
|