@glassly/bluetooth-sdk 0.1.0-dev.98 → 0.2.8-dev.444
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 +1075 -257
- 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 +1434 -504
- 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
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
public enum GlassesNotificationError: Error, LocalizedError {
|
|
4
|
+
case unsupported
|
|
5
|
+
case notConnected
|
|
6
|
+
case invalidConfig(String)
|
|
7
|
+
|
|
8
|
+
public var errorDescription: String? {
|
|
9
|
+
switch self {
|
|
10
|
+
case .unsupported:
|
|
11
|
+
"The connected glasses have no notification surface"
|
|
12
|
+
case .notConnected:
|
|
13
|
+
"No glasses connected"
|
|
14
|
+
case let .invalidConfig(reason):
|
|
15
|
+
reason
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/// Desired on-glass notification behaviour in glasses-neutral terms. Drivers map
|
|
21
|
+
/// it onto their protocol and snap values their firmware cannot take.
|
|
22
|
+
public struct GlassesNotificationConfig: Equatable, Sendable {
|
|
23
|
+
public struct AllowedApp: Equatable, Sendable {
|
|
24
|
+
public let id: String
|
|
25
|
+
public let name: String
|
|
26
|
+
|
|
27
|
+
public init(id: String, name: String) {
|
|
28
|
+
self.id = id
|
|
29
|
+
self.name = name
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/// Popup duration the firmware is allowed to be handed, in seconds.
|
|
34
|
+
static let popupSecondsRange = 0 ... 60
|
|
35
|
+
|
|
36
|
+
public let enabled: Bool
|
|
37
|
+
public let popups: Bool
|
|
38
|
+
/// 0 lets the firmware choose.
|
|
39
|
+
public let popupSeconds: Int
|
|
40
|
+
public let holdDuringApps: Bool
|
|
41
|
+
/// Android package names or iOS bundle ids.
|
|
42
|
+
public let blockedApps: [String]
|
|
43
|
+
/// Known ids minus blocked, for firmware whose filter is an allowlist.
|
|
44
|
+
public let allowedApps: [AllowedApp]
|
|
45
|
+
|
|
46
|
+
public init(
|
|
47
|
+
enabled: Bool,
|
|
48
|
+
popups: Bool,
|
|
49
|
+
popupSeconds: Int,
|
|
50
|
+
holdDuringApps: Bool,
|
|
51
|
+
blockedApps: [String],
|
|
52
|
+
allowedApps: [AllowedApp]
|
|
53
|
+
) {
|
|
54
|
+
self.enabled = enabled
|
|
55
|
+
self.popups = popups
|
|
56
|
+
self.popupSeconds = popupSeconds
|
|
57
|
+
self.holdDuringApps = holdDuringApps
|
|
58
|
+
self.blockedApps = blockedApps
|
|
59
|
+
self.allowedApps = allowedApps
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/// Numbers arrive as NSNumber across the Expo bridge; `as? Int` would drop a Double.
|
|
63
|
+
public init(dictionary: [String: Any]) throws {
|
|
64
|
+
enabled = dictionary["enabled"] as? Bool ?? true
|
|
65
|
+
popups = dictionary["popups"] as? Bool ?? true
|
|
66
|
+
holdDuringApps = dictionary["holdDuringApps"] as? Bool ?? true
|
|
67
|
+
|
|
68
|
+
let seconds = (dictionary["popupSeconds"] as? NSNumber)?.intValue ?? 0
|
|
69
|
+
guard Self.popupSecondsRange.contains(seconds) else {
|
|
70
|
+
throw GlassesNotificationError.invalidConfig(
|
|
71
|
+
"popupSeconds \(seconds) outside \(Self.popupSecondsRange.lowerBound)…\(Self.popupSecondsRange.upperBound)"
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
popupSeconds = seconds
|
|
75
|
+
|
|
76
|
+
blockedApps = (dictionary["blockedApps"] as? [String] ?? []).filter { !$0.isEmpty }
|
|
77
|
+
allowedApps = (dictionary["allowedApps"] as? [[String: Any]] ?? []).compactMap { entry in
|
|
78
|
+
guard let id = entry["id"] as? String, !id.isEmpty else { return nil }
|
|
79
|
+
return AllowedApp(id: id, name: entry["name"] as? String ?? id)
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
public var dictionary: [String: Any] {
|
|
84
|
+
[
|
|
85
|
+
"enabled": enabled,
|
|
86
|
+
"popups": popups,
|
|
87
|
+
"popupSeconds": popupSeconds,
|
|
88
|
+
"holdDuringApps": holdDuringApps,
|
|
89
|
+
"blockedApps": blockedApps,
|
|
90
|
+
"allowedApps": allowedApps.map { ["id": $0.id, "name": $0.name] },
|
|
91
|
+
]
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
public struct GlassesNotificationStatus: Sendable {
|
|
96
|
+
/// "phone_upload" | "ancs" | "none"
|
|
97
|
+
public let feed: String
|
|
98
|
+
/// "granted" | "denied" | "unknown" | "not_needed"
|
|
99
|
+
public let access: String
|
|
100
|
+
/// "idle" | "applying" | "applied" | "stale" | "error"
|
|
101
|
+
public let phase: String
|
|
102
|
+
|
|
103
|
+
public let supported: Bool
|
|
104
|
+
public let config: GlassesNotificationConfig?
|
|
105
|
+
public let error: String?
|
|
106
|
+
|
|
107
|
+
public init(
|
|
108
|
+
supported: Bool,
|
|
109
|
+
feed: String,
|
|
110
|
+
access: String,
|
|
111
|
+
phase: String,
|
|
112
|
+
config: GlassesNotificationConfig?,
|
|
113
|
+
error: String?
|
|
114
|
+
) {
|
|
115
|
+
self.supported = supported
|
|
116
|
+
self.feed = feed
|
|
117
|
+
self.access = access
|
|
118
|
+
self.phase = phase
|
|
119
|
+
self.config = config
|
|
120
|
+
self.error = error
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
public static let unsupported = GlassesNotificationStatus(
|
|
124
|
+
supported: false,
|
|
125
|
+
feed: "none",
|
|
126
|
+
access: "not_needed",
|
|
127
|
+
phase: "idle",
|
|
128
|
+
config: nil,
|
|
129
|
+
error: nil
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
public var dictionary: [String: Any] {
|
|
133
|
+
[
|
|
134
|
+
"supported": supported,
|
|
135
|
+
"feed": feed,
|
|
136
|
+
"access": access,
|
|
137
|
+
"phase": phase,
|
|
138
|
+
"config": config?.dictionary ?? NSNull(),
|
|
139
|
+
"error": error ?? NSNull(),
|
|
140
|
+
]
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/// One phone notification handed to glasses that present content themselves.
|
|
145
|
+
public struct PhoneNotificationUpload: Sendable {
|
|
146
|
+
public let notificationId: String
|
|
147
|
+
public let packageName: String
|
|
148
|
+
public let appName: String
|
|
149
|
+
public let title: String
|
|
150
|
+
public let subtitle: String
|
|
151
|
+
public let body: String
|
|
152
|
+
public let timestampMs: Int64
|
|
153
|
+
|
|
154
|
+
public init(
|
|
155
|
+
notificationId: String,
|
|
156
|
+
packageName: String,
|
|
157
|
+
appName: String,
|
|
158
|
+
title: String,
|
|
159
|
+
subtitle: String,
|
|
160
|
+
body: String,
|
|
161
|
+
timestampMs: Int64
|
|
162
|
+
) {
|
|
163
|
+
self.notificationId = notificationId
|
|
164
|
+
self.packageName = packageName
|
|
165
|
+
self.appName = appName
|
|
166
|
+
self.title = title
|
|
167
|
+
self.subtitle = subtitle
|
|
168
|
+
self.body = body
|
|
169
|
+
self.timestampMs = timestampMs
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
public init(dictionary: [String: Any]) {
|
|
173
|
+
notificationId = dictionary["notificationId"] as? String ?? ""
|
|
174
|
+
packageName = dictionary["packageName"] as? String ?? ""
|
|
175
|
+
appName = dictionary["appName"] as? String ?? ""
|
|
176
|
+
title = dictionary["title"] as? String ?? ""
|
|
177
|
+
subtitle = dictionary["subtitle"] as? String ?? ""
|
|
178
|
+
body = dictionary["body"] as? String ?? ""
|
|
179
|
+
timestampMs = (dictionary["timestampMs"] as? NSNumber)?.int64Value ?? 0
|
|
180
|
+
}
|
|
181
|
+
}
|
|
@@ -106,13 +106,25 @@ 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 — without this every apply re-fires the subscriber.
|
|
112
|
+
if let l = lhs as? [String: Any], let r = rhs as? [String: Any] {
|
|
113
|
+
return jsonEqual(l, r)
|
|
110
114
|
}
|
|
111
115
|
return false
|
|
112
116
|
}
|
|
113
117
|
|
|
118
|
+
/// Unserializable on either side is "changed" — nil == nil must not read as equal.
|
|
119
|
+
private func jsonEqual(_ lhs: Any, _ rhs: Any) -> Bool {
|
|
120
|
+
guard let l = toJson(lhs), let r = toJson(rhs) else { return false }
|
|
121
|
+
return l == r
|
|
122
|
+
}
|
|
123
|
+
|
|
114
124
|
private func toJson(_ value: Any) -> String? {
|
|
115
|
-
guard
|
|
125
|
+
guard JSONSerialization.isValidJSONObject(value),
|
|
126
|
+
let data = try? JSONSerialization.data(withJSONObject: value, options: [.sortedKeys])
|
|
127
|
+
else { return nil }
|
|
116
128
|
return String(data: data, encoding: .utf8)
|
|
117
129
|
}
|
|
118
130
|
}
|
|
@@ -3,8 +3,33 @@
|
|
|
3
3
|
The iOS target bundles the stock and Glassly CFW EVENOTA images stored in this
|
|
4
4
|
directory. They were copied from the sibling `g2flash` checkout:
|
|
5
5
|
|
|
6
|
-
- `g2_2.
|
|
7
|
-
- `g2_2.
|
|
6
|
+
- `g2_2.3.0.24.bin` -> `g2-stock-2.3.0.24.evenota.bin`
|
|
7
|
+
- `g2_2.3.0.24_cfw.bin` -> `g2-glassly-cfw-2.3.0.24.evenota.bin`
|
|
8
8
|
|
|
9
9
|
Run `./build_cfw.sh` in `g2flash` before manually replacing the CFW image. The
|
|
10
10
|
firmware binaries are committed so CI builds bundle them.
|
|
11
|
+
|
|
12
|
+
The bundled CFW is **GLASSLYCFW/37**, built in `tetramo-labs/g2flash` at
|
|
13
|
+
the commit named in its README, on stock G2 **2.3.0.24** (upstream
|
|
14
|
+
jimrandomh/g2flash's new base with its audited stock ABI table). Every stock
|
|
15
|
+
address was re-derived for 2.3.0.24; the fork keeps its own ANCS relay,
|
|
16
|
+
phone-selectable fast BLE link and four-microphone relay, and gains upstream's
|
|
17
|
+
ring touch-down and ring-report forwarding. Verified on hardware with the
|
|
18
|
+
g2flash shapes, vector and Bad Apple suites and the fast-link toggle.
|
|
19
|
+
Revision 38 adds the retained object cache (modes 37-41): objects and assets
|
|
20
|
+
with 32-bit ids and versions survive a hidden view, so reopening the dashboard
|
|
21
|
+
is one SHOW of references; the raw texture writes (mode 18) are gone. Verified
|
|
22
|
+
on hardware with the g2flash vector and shapes suites (43/43).
|
|
23
|
+
Field 100 is the bare revision string; clients gate on the number (the app
|
|
24
|
+
requires 39).
|
|
25
|
+
|
|
26
|
+
- Size: **4,661,963 bytes**
|
|
27
|
+
- SHA-256: `4c7bf9a552df1a42f26b3378425c7c2121ca4058fed7283ec863c3f8d2cd13fe`
|
|
28
|
+
|
|
29
|
+
Keep both platform copies and their `G2FirmwareCatalog` size/hash pins in sync.
|
|
30
|
+
|
|
31
|
+
Revision 39 adds the BLE keyboard bridge (`patches/keyboard.c`): the right
|
|
32
|
+
lens hosts one HID-over-GATT keyboard, driven by the phone over settings
|
|
33
|
+
fields 130 (control) and 131 (events). Verified on hardware from the g2flash
|
|
34
|
+
reference client (pairing, HOGP discovery and live key events); the phone
|
|
35
|
+
path (G2KeyboardBridge) is not yet hardware-validated. The app requires 39.
|
|
Binary file
|
|
Binary file
|
|
@@ -1,15 +1,34 @@
|
|
|
1
1
|
import CoreBluetooth
|
|
2
2
|
import Foundation
|
|
3
3
|
|
|
4
|
+
/// Phone-Bluetooth state watcher shared by every SDK command that needs the
|
|
5
|
+
/// radio. Creating a `CBCentralManager` is what makes iOS show the Bluetooth
|
|
6
|
+
/// permission alert, so the manager is created lazily on the first command
|
|
7
|
+
/// that needs Bluetooth, never on app launch. `BluetoothSDK` is built when
|
|
8
|
+
/// the Expo module is created, so an eager manager here prompts a fresh
|
|
9
|
+
/// install before onboarding.
|
|
4
10
|
final class BluetoothAvailability: NSObject, CBCentralManagerDelegate {
|
|
5
11
|
static let shared = BluetoothAvailability()
|
|
6
12
|
|
|
7
13
|
private var centralManager: CBCentralManager?
|
|
8
14
|
private var state: CBManagerState = .unknown
|
|
15
|
+
/// CoreBluetooth reports state asynchronously after the manager exists.
|
|
16
|
+
/// Until the first report, `.unknown` means "not asked yet", not "broken".
|
|
17
|
+
private var hasReportedState = false
|
|
9
18
|
private var listeners: [UUID: (CBManagerState) -> Void] = [:]
|
|
10
19
|
|
|
11
20
|
override private init() {
|
|
12
21
|
super.init()
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/// True once a central manager exists (the permission alert has been
|
|
25
|
+
/// shown, or authorization was already decided).
|
|
26
|
+
var isActivated: Bool {
|
|
27
|
+
centralManager != nil
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
private func activate() {
|
|
31
|
+
guard centralManager == nil else { return }
|
|
13
32
|
centralManager = CBCentralManager(
|
|
14
33
|
delegate: self,
|
|
15
34
|
queue: .main,
|
|
@@ -20,11 +39,15 @@ final class BluetoothAvailability: NSObject, CBCentralManagerDelegate {
|
|
|
20
39
|
|
|
21
40
|
func centralManagerDidUpdateState(_ central: CBCentralManager) {
|
|
22
41
|
state = central.state
|
|
42
|
+
hasReportedState = true
|
|
23
43
|
for listener in Array(listeners.values) {
|
|
24
44
|
listener(state)
|
|
25
45
|
}
|
|
26
46
|
}
|
|
27
47
|
|
|
48
|
+
/// Listeners registered before activation receive `.unknown` now and the
|
|
49
|
+
/// real state once a command activates the manager and CoreBluetooth
|
|
50
|
+
/// reports.
|
|
28
51
|
@discardableResult
|
|
29
52
|
func addStateListener(_ listener: @escaping (CBManagerState) -> Void) -> UUID {
|
|
30
53
|
// `listeners` is unsynchronized; it is only safe because the central
|
|
@@ -42,6 +65,7 @@ final class BluetoothAvailability: NSObject, CBCentralManagerDelegate {
|
|
|
42
65
|
}
|
|
43
66
|
|
|
44
67
|
func requirePoweredOn(operation: String) throws {
|
|
68
|
+
activate()
|
|
45
69
|
if let current = centralManager?.state {
|
|
46
70
|
state = current
|
|
47
71
|
}
|
|
@@ -63,6 +87,13 @@ final class BluetoothAvailability: NSObject, CBCentralManagerDelegate {
|
|
|
63
87
|
code: "bluetooth_unsupported",
|
|
64
88
|
message: "This phone does not support Bluetooth."
|
|
65
89
|
)
|
|
90
|
+
case .unknown where !hasReportedState:
|
|
91
|
+
// First command after lazy activation: CoreBluetooth has not
|
|
92
|
+
// reported yet (the permission alert may be on screen). Let the
|
|
93
|
+
// command proceed; the per-device controller owns its own
|
|
94
|
+
// central manager and handles the radio state itself, and this
|
|
95
|
+
// gate enforces from the first report onward.
|
|
96
|
+
return
|
|
66
97
|
case .resetting, .unknown:
|
|
67
98
|
throw BluetoothSdkError(
|
|
68
99
|
code: "bluetooth_not_ready",
|