@glassly/bluetooth-sdk 0.1.0-dev.99 → 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 +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
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ The package includes:
|
|
|
7
7
|
- A React Native / Expo module API exposed as `BluetoothSdk`.
|
|
8
8
|
- React hooks under `@glassly/bluetooth-sdk/react` for common scan,
|
|
9
9
|
connection, status, and event lifecycles.
|
|
10
|
-
- Native Android code published as `
|
|
10
|
+
- Native Android code published as `ai.glassly:bluetooth-sdk`.
|
|
11
11
|
- Native iOS code available as the `BluetoothSDK` Swift package.
|
|
12
12
|
- An Expo config plugin that wires the native dependencies into generated Android and iOS projects.
|
|
13
13
|
|
|
@@ -352,7 +352,7 @@ Android and iOS async APIs use `BluetoothSdkException` / `BluetoothSdkError` for
|
|
|
352
352
|
React Native apps should use `MentraLiveOtaFlow` or `useMentraLiveOta` from
|
|
353
353
|
`@glassly/engine/ota`. They provide the same tested Wi-Fi/hotspot,
|
|
354
354
|
APK/MTK/BES, restart, retry, and verification flow as the Glassly App. See
|
|
355
|
-
[Update Mentra Live](https://docs.glassly.
|
|
355
|
+
[Update Mentra Live](https://docs.glassly.ai/bluetooth-sdk/software-update).
|
|
356
356
|
|
|
357
357
|
The Bluetooth SDK exposes the lower-level commands and transport capabilities
|
|
358
358
|
that Glassly Engine and native apps build on. They are not a replacement for the
|
|
@@ -409,7 +409,7 @@ OTA requires Mentra Live glasses firmware that supports the ASG OTA protocol and
|
|
|
409
409
|
|
|
410
410
|
For release selection, bundle verification, analytics opt-out, internal server
|
|
411
411
|
requirements, and disconnected validation, see the
|
|
412
|
-
[air-gapped deployment guide](https://docs.glassly.
|
|
412
|
+
[air-gapped deployment guide](https://docs.glassly.ai/bluetooth-sdk/air-gapped-deployment).
|
|
413
413
|
|
|
414
414
|
Mentra Live also rejects `ota_start` before acknowledgement when its known battery level is below 5%, emitting a failed `ota_status` with `error_message: "battery_low"`. Unknown battery state remains fail-open, so apps should keep their own (typically stricter) user-facing battery policy.
|
|
415
415
|
|
|
@@ -458,6 +458,47 @@ await BluetoothSdk.stopStream()
|
|
|
458
458
|
Use `rtmp://` or `rtmps://` for RTMP, `srt://` for SRT, and `http://` or `https://` for WHIP/WebRTC ingest. `startStream()` resolves with the correlated `stream_status` event once the glasses report `status: "streaming"`; `stopStream()` resolves when the glasses report `status: "stopped"` or confirms the stream was already stopped / not streaming. `stopStream()` returns a normalized stopped event for that already-stopped case. Stream starts reject if the glasses report an error before streaming; stream stops reject for real stop errors, send failure, another stop in flight, or timeout. Stream video input fields are `width`, `height`, `bitrate`, and `fps`. The SDK sends stream keep-alives automatically while streaming and reports keep-alive failures through `stream_status`. The camera light is always enabled while streaming.
|
|
459
459
|
`stream_status` events may include `resolvedConfig`, which reports the effective transport, video, and audio settings after glasses defaults, clamps, and camera preflight. The resolved effective frame rate is reported as `resolvedConfig.video.fps`. While live, supported firmware emits periodic `streaming` events whose `stats` object contains `bitrate` (bits per second), `fps`, `droppedFrames`, `duration` (seconds), and `temperatureC` when the CPU sensor is available.
|
|
460
460
|
|
|
461
|
+
## Glasses Notifications
|
|
462
|
+
|
|
463
|
+
Some glasses present phone notifications themselves (popup + history) and take
|
|
464
|
+
a glasses-neutral config. The SDK describes what the wearer wants; each driver
|
|
465
|
+
maps it onto its own protocol and snaps values its firmware cannot take.
|
|
466
|
+
|
|
467
|
+
```ts
|
|
468
|
+
await BluetoothSdk.setGlassesNotificationConfig({
|
|
469
|
+
enabled: true,
|
|
470
|
+
popups: true, // false keeps history without popups
|
|
471
|
+
popupSeconds: 5, // 0 lets the firmware choose; drivers snap other values
|
|
472
|
+
holdDuringApps: true, // hold popups while navigation or a miniapp is on screen
|
|
473
|
+
blockedApps: [], // package names (Android) or bundle ids (iOS)
|
|
474
|
+
allowedApps: [], // known ids minus blocked, for firmware whose filter is an allowlist
|
|
475
|
+
})
|
|
476
|
+
const status = await BluetoothSdk.getGlassesNotificationStatus()
|
|
477
|
+
const subscription = BluetoothSdk.addListener('glasses_notification_status', handleStatus)
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
`setGlassesNotificationConfig` rejects when no glasses are connected or the
|
|
481
|
+
connected driver has no notification surface. The status reports `supported`,
|
|
482
|
+
the `feed` (`ancs` when the glasses read the phone's notifications themselves,
|
|
483
|
+
`phone_upload` when the phone hands them content, `none`), the wearer's
|
|
484
|
+
`access` consent where one applies, and a `phase`. `applied` means the config
|
|
485
|
+
was written to the link, not acknowledged by firmware; `stale` means a
|
|
486
|
+
reconnect is needed. Drivers re-apply their last config after every reconnect.
|
|
487
|
+
|
|
488
|
+
The G2 driver takes the config on both platforms. On iOS the glasses read the
|
|
489
|
+
phone's notifications directly and the config also drives the on-glass
|
|
490
|
+
allowlist file. On Android the phone uploads each notification as a small
|
|
491
|
+
file over the glasses' file-transfer services; uploads are queued (eight
|
|
492
|
+
pending, oldest dropped), serialized, and reported by id through
|
|
493
|
+
`notification_upload_result` (`sent`, `failed`, `cancelled`, `dropped`) with no
|
|
494
|
+
content attached. Disabling or disconnecting cancels queued uploads.
|
|
495
|
+
|
|
496
|
+
`notification_upload_result` is typed and emitted, but it is not yet in the
|
|
497
|
+
public listener allowlist, so `addListener("notification_upload_result", ...)`
|
|
498
|
+
throws today. Until that is fixed, treat per-notification outcomes as
|
|
499
|
+
diagnostic-only and drive UI from `getGlassesNotificationStatus()` and the
|
|
500
|
+
`glasses_notification_status` event.
|
|
501
|
+
|
|
461
502
|
## Events
|
|
462
503
|
|
|
463
504
|
React Native components should use `useBluetoothEvent()` for hardware events:
|
|
@@ -509,7 +550,7 @@ For normal app development, install the JavaScript package from npm. For SDK sou
|
|
|
509
550
|
|
|
510
551
|
```sh
|
|
511
552
|
bun add --no-save /path/to/Glassly/mobile/modules/bluetooth-sdk
|
|
512
|
-
|
|
553
|
+
BLUETOOTH_SDK_PACKAGE_PATH=/path/to/Glassly/mobile/modules/bluetooth-sdk bunx expo run:ios
|
|
513
554
|
```
|
|
514
555
|
|
|
515
556
|
Use `bunx expo run:android` for Android. Keep local paths in your shell or CI environment, not in committed app config.
|
|
@@ -523,7 +564,7 @@ Glassly repo root:
|
|
|
523
564
|
|
|
524
565
|
The `android/` folder in this package is source for the generated Expo Android
|
|
525
566
|
project, not the local Gradle entrypoint. The check script prepares
|
|
526
|
-
`mobile/android` and uses its Gradle wrapper with `-
|
|
567
|
+
`mobile/android` and uses its Gradle wrapper with `-PpublicSdk=true`,
|
|
527
568
|
matching the CI release workflow's public SDK dependency mode.
|
|
528
569
|
|
|
529
570
|
For bare native iOS apps, use the public SwiftPM repository:
|
|
@@ -551,7 +592,7 @@ Maintainers publishing the native Android artifacts to Maven Central should
|
|
|
551
592
|
follow [RELEASING_ANDROID_MAVEN.md](./RELEASING_ANDROID_MAVEN.md).
|
|
552
593
|
|
|
553
594
|
Public Maven publishing uses a public SDK mode that omits Glassly-only Android
|
|
554
|
-
integrations from the artifact metadata while normal Glassly
|
|
595
|
+
integrations from the artifact metadata while normal Glassly App builds keep
|
|
555
596
|
those integrations enabled.
|
|
556
597
|
|
|
557
598
|
Use `android/gradle.properties.example` as the template for Sonatype Central and
|
package/android/build.gradle
CHANGED
|
@@ -7,8 +7,8 @@ apply plugin: 'signing'
|
|
|
7
7
|
|
|
8
8
|
def packageJson = new JsonSlurper().parse(file("../package.json"))
|
|
9
9
|
def mavenGroupId = 'ai.glassly'
|
|
10
|
-
def mavenPublishVersion = (findProperty('
|
|
11
|
-
def publicMavenSdk = (findProperty('
|
|
10
|
+
def mavenPublishVersion = (findProperty('mavenVersion') ?: System.getenv('MAVEN_VERSION') ?: packageJson.version).toString()
|
|
11
|
+
def publicMavenSdk = (findProperty('publicSdk') ?: System.getenv('PUBLIC_SDK') ?: 'false').toString().toBoolean()
|
|
12
12
|
|
|
13
13
|
// Download Sherpa-ONNX Android AAR from the upstream GitHub release and stage
|
|
14
14
|
// it as a Maven artifact in a tiny on-disk repository under libs/maven/.
|
|
@@ -290,13 +290,13 @@ dependencies {
|
|
|
290
290
|
// through the public SDK API. Public Maven artifacts compile against them
|
|
291
291
|
// without publishing them as runtime transitives, so consumers only need
|
|
292
292
|
// Maven Central/Google repositories for normal SDK use.
|
|
293
|
-
def
|
|
293
|
+
def optionalIntegrationDependency = publicMavenSdk ? 'compileOnly' : 'implementation'
|
|
294
294
|
|
|
295
295
|
// Sherpa-ONNX for local transcription. Jitpack's v1.13.2 build failed
|
|
296
296
|
// upstream, so we stage the prebuilt AAR into libs/maven/ (see top of file)
|
|
297
297
|
// and consume it as a Maven coord — AGP rejects raw files() .aar deps in
|
|
298
298
|
// library modules.
|
|
299
|
-
dependencies.add(
|
|
299
|
+
dependencies.add(optionalIntegrationDependency, "${sherpaOnnxGroup}:${sherpaOnnxArtifact}:${sherpaOnnxVersion}@aar")
|
|
300
300
|
|
|
301
301
|
if (!publicMavenSdk) {
|
|
302
302
|
// Silero VAD — vendored source under silero/, wired as :silero
|
|
@@ -318,8 +318,8 @@ dependencies {
|
|
|
318
318
|
compileOnly 'com.squareup.picasso:picasso:2.8'
|
|
319
319
|
}
|
|
320
320
|
|
|
321
|
-
implementation 'com.google.protobuf:protobuf-java:4.
|
|
322
|
-
implementation 'com.google.protobuf:protobuf-java-util:4.
|
|
321
|
+
implementation 'com.google.protobuf:protobuf-java:4.36.1'
|
|
322
|
+
implementation 'com.google.protobuf:protobuf-java-util:4.36.1'
|
|
323
323
|
}
|
|
324
324
|
|
|
325
325
|
// Pin onnxruntime-android to 1.27.0 — the version sherpa-onnx 1.13.4 was built
|
|
@@ -386,7 +386,7 @@ afterEvaluate {
|
|
|
386
386
|
repositories {
|
|
387
387
|
maven {
|
|
388
388
|
name = 'sonatypeCentral'
|
|
389
|
-
def repositoryUrl = System.getenv('
|
|
389
|
+
def repositoryUrl = System.getenv('MAVEN_STAGING_REPOSITORY_URL') ?: 'https://ossrh-staging-api.central.sonatype.com/service/local/staging/deploy/maven2/'
|
|
390
390
|
url = uri(repositoryUrl)
|
|
391
391
|
if (repositoryUrl.startsWith('http')) {
|
|
392
392
|
credentials {
|
|
@@ -8,7 +8,7 @@ plugins {
|
|
|
8
8
|
|
|
9
9
|
def packageJson = new JsonSlurper().parse(file("../../package.json"))
|
|
10
10
|
def mavenGroupId = 'ai.glassly'
|
|
11
|
-
def mavenPublishVersion = (findProperty('
|
|
11
|
+
def mavenPublishVersion = (findProperty('mavenVersion') ?: System.getenv('MAVEN_VERSION') ?: packageJson.version).toString()
|
|
12
12
|
|
|
13
13
|
group = mavenGroupId
|
|
14
14
|
version = mavenPublishVersion
|
|
@@ -181,7 +181,7 @@ afterEvaluate {
|
|
|
181
181
|
repositories {
|
|
182
182
|
maven {
|
|
183
183
|
name = 'sonatypeCentral'
|
|
184
|
-
def repositoryUrl = System.getenv('
|
|
184
|
+
def repositoryUrl = System.getenv('MAVEN_STAGING_REPOSITORY_URL') ?: 'https://ossrh-staging-api.central.sonatype.com/service/local/staging/deploy/maven2/'
|
|
185
185
|
url = uri(repositoryUrl)
|
|
186
186
|
if (repositoryUrl.startsWith('http')) {
|
|
187
187
|
credentials {
|
|
@@ -3,9 +3,34 @@
|
|
|
3
3
|
The Android library bundles the stock and Glassly CFW EVENOTA images stored in
|
|
4
4
|
this directory. They are 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, and verified by size,
|
|
11
11
|
SHA-256, per-component CRC32C, and the MRAM guard before flashing.
|
|
12
|
+
|
|
13
|
+
The bundled CFW is **GLASSLYCFW/37**, built in `tetramo-labs/g2flash` at
|
|
14
|
+
the commit named in its README, on stock G2 **2.3.0.24** (upstream
|
|
15
|
+
jimrandomh/g2flash's new base with its audited stock ABI table). Every stock
|
|
16
|
+
address was re-derived for 2.3.0.24; the fork keeps its own ANCS relay,
|
|
17
|
+
phone-selectable fast BLE link and four-microphone relay, and gains upstream's
|
|
18
|
+
ring touch-down and ring-report forwarding. Verified on hardware with the
|
|
19
|
+
g2flash shapes, vector and Bad Apple suites and the fast-link toggle.
|
|
20
|
+
Revision 38 adds the retained object cache (modes 37-41): objects and assets
|
|
21
|
+
with 32-bit ids and versions survive a hidden view, so reopening the dashboard
|
|
22
|
+
is one SHOW of references; the raw texture writes (mode 18) are gone. Verified
|
|
23
|
+
on hardware with the g2flash vector and shapes suites (43/43).
|
|
24
|
+
Field 100 is the bare revision string; clients gate on the number (the app
|
|
25
|
+
requires 39).
|
|
26
|
+
|
|
27
|
+
- Size: **4,661,963 bytes**
|
|
28
|
+
- SHA-256: `4c7bf9a552df1a42f26b3378425c7c2121ca4058fed7283ec863c3f8d2cd13fe`
|
|
29
|
+
|
|
30
|
+
Keep both platform copies and their `G2FirmwareCatalog` size/hash pins in sync.
|
|
31
|
+
|
|
32
|
+
Revision 39 adds the BLE keyboard bridge (`patches/keyboard.c`): the right
|
|
33
|
+
lens hosts one HID-over-GATT keyboard, driven by the phone over settings
|
|
34
|
+
fields 130 (control) and 131 (events). Verified on hardware from the g2flash
|
|
35
|
+
reference client (pairing, HOGP discovery and live key events); the phone
|
|
36
|
+
path (G2KeyboardBridge) is not yet hardware-validated. The app requires 39.
|
|
Binary file
|
|
Binary file
|
|
@@ -505,8 +505,19 @@ class BluetoothSdk private constructor(
|
|
|
505
505
|
deviceManager.showNotificationsPanel()
|
|
506
506
|
}
|
|
507
507
|
|
|
508
|
+
/** Apply on-glass notification behaviour. Throws when nothing is connected. */
|
|
509
|
+
fun setGlassesNotificationConfig(config: GlassesNotificationConfig) {
|
|
510
|
+
deviceManager.setGlassesNotificationConfig(config)
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
fun getGlassesNotificationStatus(): GlassesNotificationStatus =
|
|
514
|
+
deviceManager.glassesNotificationStatus()
|
|
515
|
+
|
|
508
516
|
// ---------- Native navigation (glasses-rendered nav UI) ----------
|
|
509
517
|
|
|
518
|
+
suspend fun glassesSensorControl(sensor: String, operation: String, options: Map<String, Double>): Boolean =
|
|
519
|
+
deviceManager.glassesSensorControl(sensor, operation, options)
|
|
520
|
+
|
|
510
521
|
fun navigationModeStart() {
|
|
511
522
|
deviceManager.navigationModeStart()
|
|
512
523
|
}
|
|
@@ -1226,6 +1237,33 @@ class BluetoothSdk private constructor(
|
|
|
1226
1237
|
return deviceManager.startG2FirmwareFlash(target)
|
|
1227
1238
|
}
|
|
1228
1239
|
|
|
1240
|
+
// ---------- BLE keyboard bridge (G2 Glassly CFW revision 39+) ----------
|
|
1241
|
+
|
|
1242
|
+
suspend fun g2KeyboardScan(): List<Map<String, Any>> {
|
|
1243
|
+
requireGlassesConnected("scan for a keyboard")
|
|
1244
|
+
return deviceManager.g2KeyboardScan()
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
suspend fun g2KeyboardConnect(address: String, addressType: Int, name: String) {
|
|
1248
|
+
requireGlassesConnected("connect a keyboard")
|
|
1249
|
+
deviceManager.g2KeyboardConnect(address, addressType, name)
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
suspend fun g2KeyboardReconnect() {
|
|
1253
|
+
requireGlassesConnected("reconnect the keyboard")
|
|
1254
|
+
deviceManager.g2KeyboardReconnect()
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
suspend fun g2KeyboardDisconnect() = deviceManager.g2KeyboardDisconnect()
|
|
1258
|
+
|
|
1259
|
+
suspend fun g2KeyboardForget() = deviceManager.g2KeyboardForget()
|
|
1260
|
+
|
|
1261
|
+
suspend fun g2KeyboardPasskey(pin: Int) = deviceManager.g2KeyboardPasskey(pin)
|
|
1262
|
+
|
|
1263
|
+
suspend fun g2KeyboardCompare(accept: Boolean) = deviceManager.g2KeyboardCompare(accept)
|
|
1264
|
+
|
|
1265
|
+
suspend fun g2KeyboardStatus(): Map<String, Any> = deviceManager.g2KeyboardStatus()
|
|
1266
|
+
|
|
1229
1267
|
/** Select the HTTP(S) manifest used by subsequent OTA checks and installs. */
|
|
1230
1268
|
fun setOtaVersionUrl(otaVersionUrl: String) {
|
|
1231
1269
|
configuredOtaVersionUrl = OtaManifestChecker.normalizeHttpUrl(otaVersionUrl)
|
|
@@ -6,6 +6,7 @@ import androidx.lifecycle.Lifecycle
|
|
|
6
6
|
import androidx.lifecycle.LifecycleOwner
|
|
7
7
|
import com.glassly.bluetoothsdk.debug.BleTraceLogger
|
|
8
8
|
import com.glassly.bluetoothsdk.speakerid.SpeakerIdEngine
|
|
9
|
+
import com.glassly.bluetoothsdk.stt.STTTools
|
|
9
10
|
import com.glassly.bluetoothsdk.utils.DeviceTypes
|
|
10
11
|
import com.glassly.bluetoothsdk.utils.audio.PcmStreamManager
|
|
11
12
|
import expo.modules.kotlin.exception.CodedException
|
|
@@ -331,10 +332,12 @@ class BluetoothSdkModule : Module() {
|
|
|
331
332
|
"button_press",
|
|
332
333
|
"touch_event",
|
|
333
334
|
"accel_event",
|
|
335
|
+
"light_sensor",
|
|
334
336
|
"compass_heading",
|
|
335
337
|
"compass_calibration",
|
|
336
338
|
"navigation_mode_change",
|
|
337
339
|
"navigation_view_change",
|
|
340
|
+
"scene_frame_presented",
|
|
338
341
|
"head_up",
|
|
339
342
|
"voice_activity_detection_status",
|
|
340
343
|
"speaking_status",
|
|
@@ -374,6 +377,8 @@ class BluetoothSdkModule : Module() {
|
|
|
374
377
|
"speaker_enroll_result",
|
|
375
378
|
"phone_notification",
|
|
376
379
|
"phone_notification_dismissed",
|
|
380
|
+
"glasses_notification_status",
|
|
381
|
+
"notification_upload_result",
|
|
377
382
|
"ws_text",
|
|
378
383
|
"ws_bin",
|
|
379
384
|
"mic_pcm",
|
|
@@ -394,6 +399,9 @@ class BluetoothSdkModule : Module() {
|
|
|
394
399
|
"miniapp_selected",
|
|
395
400
|
"captions_tester_incident",
|
|
396
401
|
"extraction_progress",
|
|
402
|
+
"keyboard_key",
|
|
403
|
+
"keyboard_advert",
|
|
404
|
+
"keyboard_pairing",
|
|
397
405
|
)
|
|
398
406
|
|
|
399
407
|
OnCreate {
|
|
@@ -553,9 +561,27 @@ class BluetoothSdkModule : Module() {
|
|
|
553
561
|
|
|
554
562
|
SdkAsyncFunction("showNativeNotificationPanel") { -> sdk?.showNotificationsPanel() }
|
|
555
563
|
|
|
564
|
+
SdkAsyncFunction("setGlassesNotificationConfig") { config: Map<String, Any> ->
|
|
565
|
+
sdk?.setGlassesNotificationConfig(GlassesNotificationConfig.fromMap(config))
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
SdkAsyncFunction("getGlassesNotificationStatus") { ->
|
|
569
|
+
(sdk?.getGlassesNotificationStatus() ?: GlassesNotificationStatus.unsupported()).toMap()
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
AsyncFunction("uploadPhoneNotification") { upload: Map<String, Any> ->
|
|
573
|
+
val parsed = PhoneNotificationUpload.fromMap(upload)
|
|
574
|
+
Bridge.log("MOD: uploadPhoneNotification package=${parsed.packageName}")
|
|
575
|
+
deviceManager?.uploadPhoneNotification(parsed)
|
|
576
|
+
?: throw IllegalStateException("Glasses are not connected")
|
|
577
|
+
}
|
|
578
|
+
|
|
556
579
|
// Native navigation mode (glasses-rendered nav UI — G2 today). The JS layer
|
|
557
580
|
// feature-detects these by name (NativeNavigationService.isSupported), so the
|
|
558
581
|
// export names must match iOS exactly.
|
|
582
|
+
SdkCoroutineFunction("glassesSensorControl") { sensor: String, operation: String, options: Map<String, Double> ->
|
|
583
|
+
sdk?.glassesSensorControl(sensor, operation, options) ?: false
|
|
584
|
+
}
|
|
559
585
|
SdkAsyncFunction("navigationModeStart") { -> sdk?.navigationModeStart() }
|
|
560
586
|
SdkAsyncFunction("navigationModeStop") { arrived: Boolean -> sdk?.navigationModeStop(arrived) }
|
|
561
587
|
SdkAsyncFunction("navigationUpdateInfo") { params: Map<String, Any?> ->
|
|
@@ -570,14 +596,11 @@ class BluetoothSdkModule : Module() {
|
|
|
570
596
|
|
|
571
597
|
AsyncFunction("ping") { deviceManager?.ping() }
|
|
572
598
|
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
}
|
|
577
|
-
AsyncFunction("
|
|
578
|
-
deviceManager?.dbg2()
|
|
579
|
-
deviceManager?.sgc?.dbg2()
|
|
580
|
-
}
|
|
599
|
+
// DeviceManager forwards to the active SGC; calling sgc here too fired each hook twice.
|
|
600
|
+
AsyncFunction("dbg1") { deviceManager?.dbg1() }
|
|
601
|
+
AsyncFunction("dbg2") { deviceManager?.dbg2() }
|
|
602
|
+
AsyncFunction("dbg3") { deviceManager?.dbg3() }
|
|
603
|
+
AsyncFunction("dbg4") { deviceManager?.dbg4() }
|
|
581
604
|
|
|
582
605
|
// Stub on Android — iOS uses this for the jetsam stress test.
|
|
583
606
|
Function("getMemoryMB") { -> 0.0 }
|
|
@@ -762,6 +785,19 @@ class BluetoothSdkModule : Module() {
|
|
|
762
785
|
withContext(Dispatchers.IO) { requireSdk().startG2FirmwareFlash(target) }
|
|
763
786
|
}
|
|
764
787
|
|
|
788
|
+
// MARK: - BLE keyboard bridge (G2 Glassly CFW revision 39+)
|
|
789
|
+
|
|
790
|
+
SdkCoroutineFunction("g2KeyboardScan") { -> requireSdk().g2KeyboardScan() }
|
|
791
|
+
SdkCoroutineFunction("g2KeyboardConnect") { address: String, addressType: Int, name: String ->
|
|
792
|
+
requireSdk().g2KeyboardConnect(address, addressType, name)
|
|
793
|
+
}
|
|
794
|
+
SdkCoroutineFunction("g2KeyboardReconnect") { -> requireSdk().g2KeyboardReconnect() }
|
|
795
|
+
SdkCoroutineFunction("g2KeyboardDisconnect") { -> requireSdk().g2KeyboardDisconnect() }
|
|
796
|
+
SdkCoroutineFunction("g2KeyboardForget") { -> requireSdk().g2KeyboardForget() }
|
|
797
|
+
SdkCoroutineFunction("g2KeyboardPasskey") { pin: Int -> requireSdk().g2KeyboardPasskey(pin) }
|
|
798
|
+
SdkCoroutineFunction("g2KeyboardCompare") { accept: Boolean -> requireSdk().g2KeyboardCompare(accept) }
|
|
799
|
+
SdkCoroutineFunction("g2KeyboardStatus") { -> requireSdk().g2KeyboardStatus() }
|
|
800
|
+
|
|
765
801
|
// MARK: - Power Control Commands
|
|
766
802
|
|
|
767
803
|
SdkAsyncFunction("sendShutdown") { -> sdk?.sendShutdown() }
|
|
@@ -840,7 +876,7 @@ class BluetoothSdkModule : Module() {
|
|
|
840
876
|
// does a synchronous JNI model reload that would otherwise block every other
|
|
841
877
|
// native call in the app until it completes.
|
|
842
878
|
AsyncFunction("restartTranscriber") Coroutine { ->
|
|
843
|
-
withContext(Dispatchers.IO) { deviceManager
|
|
879
|
+
withContext(Dispatchers.IO) { checkNotNull(deviceManager) { "Bluetooth SDK is not initialized" }.restartTranscriber() }
|
|
844
880
|
}
|
|
845
881
|
|
|
846
882
|
// MARK: - Speaker Identification
|
|
@@ -948,7 +984,7 @@ class BluetoothSdkModule : Module() {
|
|
|
948
984
|
appContext.reactContext
|
|
949
985
|
?: appContext.currentActivity
|
|
950
986
|
?: throw IllegalStateException("No context available")
|
|
951
|
-
|
|
987
|
+
STTTools.setSttModelDetails(context, path, languageCode)
|
|
952
988
|
}
|
|
953
989
|
|
|
954
990
|
AsyncFunction("getSttModelPath") { ->
|
|
@@ -956,7 +992,7 @@ class BluetoothSdkModule : Module() {
|
|
|
956
992
|
appContext.reactContext
|
|
957
993
|
?: appContext.currentActivity
|
|
958
994
|
?: throw IllegalStateException("No context available")
|
|
959
|
-
|
|
995
|
+
STTTools.getSttModelPath(context)
|
|
960
996
|
}
|
|
961
997
|
|
|
962
998
|
AsyncFunction("checkSttModelAvailable") { ->
|
|
@@ -964,11 +1000,11 @@ class BluetoothSdkModule : Module() {
|
|
|
964
1000
|
appContext.reactContext
|
|
965
1001
|
?: appContext.currentActivity
|
|
966
1002
|
?: throw IllegalStateException("No context available")
|
|
967
|
-
|
|
1003
|
+
STTTools.checkSTTModelAvailable(context)
|
|
968
1004
|
}
|
|
969
1005
|
|
|
970
1006
|
AsyncFunction("validateSttModel") { path: String ->
|
|
971
|
-
|
|
1007
|
+
STTTools.validateSTTModel(path)
|
|
972
1008
|
}
|
|
973
1009
|
|
|
974
1010
|
// Runs on Dispatchers.IO, not the shared Expo AsyncFunctionQueue: bz2/tar
|
|
@@ -976,7 +1012,7 @@ class BluetoothSdkModule : Module() {
|
|
|
976
1012
|
// shared queue it froze every other native call in the app until it finished.
|
|
977
1013
|
AsyncFunction("extractTarBz2") Coroutine { sourcePath: String, destinationPath: String ->
|
|
978
1014
|
withContext(Dispatchers.IO) {
|
|
979
|
-
|
|
1015
|
+
STTTools.extractTarBz2(sourcePath, destinationPath)
|
|
980
1016
|
}
|
|
981
1017
|
}
|
|
982
1018
|
|
|
@@ -172,6 +172,24 @@ public class Bridge private constructor() {
|
|
|
172
172
|
sendTypedMessage("speaker_enroll_result", body as Map<String, Any>)
|
|
173
173
|
}
|
|
174
174
|
|
|
175
|
+
/** display.render() frame outcome; seq 0 frames are native-originated and never reported. */
|
|
176
|
+
@JvmStatic
|
|
177
|
+
fun sendSceneFramePresented(seq: Int, appId: String, presented: Boolean, reason: String?) {
|
|
178
|
+
if (seq <= 0) return
|
|
179
|
+
val body = HashMap<String, Any>()
|
|
180
|
+
body["seq"] = seq
|
|
181
|
+
body["appId"] = appId
|
|
182
|
+
body["presented"] = presented
|
|
183
|
+
if (reason != null) body["reason"] = reason
|
|
184
|
+
sendTypedMessage("scene_frame_presented", body as Map<String, Any>)
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** The glasses finished every animation a tagged scene commit started (tag = frame seq & 0xFFFF). */
|
|
188
|
+
@JvmStatic
|
|
189
|
+
fun sendSceneSettled(tag: Int) {
|
|
190
|
+
sendTypedMessage("scene_settled", mapOf("tag" to tag))
|
|
191
|
+
}
|
|
192
|
+
|
|
175
193
|
/** Send head position event */
|
|
176
194
|
@JvmStatic
|
|
177
195
|
fun sendHeadUp(isUp: Boolean) {
|