@glassly/engine 0.1.0-dev.1 → 0.1.0-dev.101
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 +57 -19
- package/build/bluetooth-sdk/debug.d.ts +2 -0
- package/build/bluetooth-sdk/debug.d.ts.map +1 -0
- package/build/bluetooth-sdk/debug.js +2 -0
- package/build/bluetooth-sdk/debug.js.map +1 -0
- package/build/bluetooth-sdk/index.d.ts +3 -0
- package/build/bluetooth-sdk/index.d.ts.map +1 -0
- package/build/bluetooth-sdk/index.js +3 -0
- package/build/bluetooth-sdk/index.js.map +1 -0
- package/build/bluetooth-sdk/ota-transport.d.ts +2 -0
- package/build/bluetooth-sdk/ota-transport.d.ts.map +1 -0
- package/build/bluetooth-sdk/ota-transport.js +2 -0
- package/build/bluetooth-sdk/ota-transport.js.map +1 -0
- package/build/bluetooth-sdk/photo-receiver.d.ts +3 -0
- package/build/bluetooth-sdk/photo-receiver.d.ts.map +1 -0
- package/build/bluetooth-sdk/photo-receiver.js +3 -0
- package/build/bluetooth-sdk/photo-receiver.js.map +1 -0
- package/build/bluetooth-sdk/react.d.ts +2 -0
- package/build/bluetooth-sdk/react.d.ts.map +1 -0
- package/build/bluetooth-sdk/react.js +2 -0
- package/build/bluetooth-sdk/react.js.map +1 -0
- package/build/bluetooth-sdk/types.d.ts +2 -0
- package/build/bluetooth-sdk/types.d.ts.map +1 -0
- package/build/bluetooth-sdk/types.js +2 -0
- package/build/bluetooth-sdk/types.js.map +1 -0
- package/build/engine.d.ts +30 -18
- package/build/engine.d.ts.map +1 -1
- package/build/engine.js +12 -3
- package/build/engine.js.map +1 -1
- package/build/facades/dev.d.ts +1 -1
- package/build/facades/display.d.ts +11 -4
- package/build/facades/display.d.ts.map +1 -1
- package/build/facades/display.js +12 -2
- package/build/facades/display.js.map +1 -1
- package/build/facades/displayMirror.d.ts +2 -6
- package/build/facades/displayMirror.d.ts.map +1 -1
- package/build/facades/displayMirror.js +7 -14
- package/build/facades/displayMirror.js.map +1 -1
- package/build/facades/glasses.d.ts +6 -13
- package/build/facades/glasses.d.ts.map +1 -1
- package/build/facades/glasses.js +7 -3
- package/build/facades/glasses.js.map +1 -1
- package/build/facades/ota.d.ts +21 -21
- package/build/facades/ota.d.ts.map +1 -1
- package/build/facades/ota.js +26 -10
- package/build/facades/ota.js.map +1 -1
- package/build/facades/pairing.d.ts +11 -5
- package/build/facades/pairing.d.ts.map +1 -1
- package/build/facades/pairing.js +93 -17
- package/build/facades/pairing.js.map +1 -1
- package/build/facades/phoneNotifications.d.ts +12 -2
- package/build/facades/phoneNotifications.d.ts.map +1 -1
- package/build/facades/phoneNotifications.js +30 -10
- package/build/facades/phoneNotifications.js.map +1 -1
- package/build/facades/speakers.d.ts +1 -1
- package/build/facades/speakers.d.ts.map +1 -1
- package/build/facades/speakers.js.map +1 -1
- package/build/facades/speech.d.ts +7 -6
- package/build/facades/speech.d.ts.map +1 -1
- package/build/facades/speech.js +1 -1
- package/build/facades/speech.js.map +1 -1
- package/build/generated/releaseMetadata.d.ts +12 -0
- package/build/generated/releaseMetadata.d.ts.map +1 -0
- package/build/generated/releaseMetadata.js +10 -0
- package/build/generated/releaseMetadata.js.map +1 -0
- package/build/index.d.ts +9 -6
- package/build/index.d.ts.map +1 -1
- package/build/index.js +7 -5
- package/build/index.js.map +1 -1
- package/build/react/MentraLiveOtaFlow.d.ts +44 -0
- package/build/react/MentraLiveOtaFlow.d.ts.map +1 -0
- package/build/react/MentraLiveOtaFlow.js +322 -0
- package/build/react/MentraLiveOtaFlow.js.map +1 -0
- package/build/react/index.d.ts +3 -0
- package/build/react/index.d.ts.map +1 -0
- package/build/react/index.js +3 -0
- package/build/react/index.js.map +1 -0
- package/build/react/useEngineSnapshot.d.ts +2 -0
- package/build/react/useEngineSnapshot.d.ts.map +1 -0
- package/build/react/useEngineSnapshot.js +14 -0
- package/build/react/useEngineSnapshot.js.map +1 -0
- package/build/react/useMentraLiveOta.d.ts +84 -0
- package/build/react/useMentraLiveOta.d.ts.map +1 -0
- package/build/react/useMentraLiveOta.js +627 -0
- package/build/react/useMentraLiveOta.js.map +1 -0
- package/build/services/AppRegistry.d.ts +8 -0
- package/build/services/AppRegistry.d.ts.map +1 -1
- package/build/services/AppRegistry.js +15 -0
- package/build/services/AppRegistry.js.map +1 -1
- package/build/services/BlobStore.d.ts +2 -0
- package/build/services/BlobStore.d.ts.map +1 -1
- package/build/services/BlobStore.js +63 -10
- package/build/services/BlobStore.js.map +1 -1
- package/build/services/CustomDashboardModel.d.ts +48 -0
- package/build/services/CustomDashboardModel.d.ts.map +1 -0
- package/build/services/CustomDashboardModel.js +73 -0
- package/build/services/CustomDashboardModel.js.map +1 -0
- package/build/services/CustomDashboardService.d.ts +55 -0
- package/build/services/CustomDashboardService.d.ts.map +1 -0
- package/build/services/CustomDashboardService.js +520 -0
- package/build/services/CustomDashboardService.js.map +1 -0
- package/build/services/DashboardCoordinator.d.ts +26 -0
- package/build/services/DashboardCoordinator.d.ts.map +1 -0
- package/build/services/DashboardCoordinator.js +125 -0
- package/build/services/DashboardCoordinator.js.map +1 -0
- package/build/services/DeviceEventRouter.d.ts.map +1 -1
- package/build/services/DeviceEventRouter.js +45 -6
- package/build/services/DeviceEventRouter.js.map +1 -1
- package/build/services/DisplayProcessor.d.ts +3 -3
- package/build/services/DisplayProcessor.js +3 -3
- package/build/services/DisplayProcessor.js.map +1 -1
- package/build/services/GestureDotsService.d.ts +40 -0
- package/build/services/GestureDotsService.d.ts.map +1 -0
- package/build/services/GestureDotsService.js +167 -0
- package/build/services/GestureDotsService.js.map +1 -0
- package/build/services/GlassesStatusProjection.d.ts +15 -1
- package/build/services/GlassesStatusProjection.d.ts.map +1 -1
- package/build/services/GlassesStatusProjection.js +18 -11
- package/build/services/GlassesStatusProjection.js.map +1 -1
- package/build/services/HotspotOtaTransport.d.ts.map +1 -1
- package/build/services/HotspotOtaTransport.js +8 -7
- package/build/services/HotspotOtaTransport.js.map +1 -1
- package/build/services/LocalDisplayManager.d.ts +55 -3
- package/build/services/LocalDisplayManager.d.ts.map +1 -1
- package/build/services/LocalDisplayManager.js +196 -16
- package/build/services/LocalDisplayManager.js.map +1 -1
- package/build/services/LocalMiniappRuntime.d.ts +13 -0
- package/build/services/LocalMiniappRuntime.d.ts.map +1 -1
- package/build/services/LocalMiniappRuntime.js +32 -0
- package/build/services/LocalMiniappRuntime.js.map +1 -1
- package/build/services/MiniappExitGesture.d.ts +33 -0
- package/build/services/MiniappExitGesture.d.ts.map +1 -0
- package/build/services/MiniappExitGesture.js +54 -0
- package/build/services/MiniappExitGesture.js.map +1 -0
- package/build/services/OtaAutoChain.d.ts +41 -0
- package/build/services/OtaAutoChain.d.ts.map +1 -0
- package/build/services/OtaAutoChain.js +91 -0
- package/build/services/OtaAutoChain.js.map +1 -0
- package/build/services/OtaErrorMapping.d.ts +18 -0
- package/build/services/OtaErrorMapping.d.ts.map +1 -0
- package/build/services/OtaErrorMapping.js +63 -0
- package/build/services/OtaErrorMapping.js.map +1 -0
- package/build/services/OtaInstallCoordinator.d.ts +26 -3
- package/build/services/OtaInstallCoordinator.d.ts.map +1 -1
- package/build/services/OtaInstallCoordinator.js +38 -13
- package/build/services/OtaInstallCoordinator.js.map +1 -1
- package/build/services/OtaService.d.ts.map +1 -1
- package/build/services/OtaService.js +1 -1
- package/build/services/OtaService.js.map +1 -1
- package/build/services/OtaUpdateCheckService.d.ts +4 -1
- package/build/services/OtaUpdateCheckService.d.ts.map +1 -1
- package/build/services/OtaUpdateCheckService.js +28 -16
- package/build/services/OtaUpdateCheckService.js.map +1 -1
- package/build/services/PhoneNotificationsSync.d.ts +13 -0
- package/build/services/PhoneNotificationsSync.d.ts.map +1 -1
- package/build/services/PhoneNotificationsSync.js +101 -9
- package/build/services/PhoneNotificationsSync.js.map +1 -1
- package/build/services/PhonePhotoCoordinator.js +1 -1
- package/build/services/PhonePhotoCoordinator.js.map +1 -1
- package/build/services/PhoneStreamCoordinator.d.ts.map +1 -1
- package/build/services/PhoneStreamCoordinator.js +9 -6
- package/build/services/PhoneStreamCoordinator.js.map +1 -1
- package/build/services/SceneRenderer.d.ts.map +1 -1
- package/build/services/SceneRenderer.js +5 -0
- package/build/services/SceneRenderer.js.map +1 -1
- package/build/services/StreamLifecycleController.d.ts +4 -0
- package/build/services/StreamLifecycleController.d.ts.map +1 -1
- package/build/services/StreamLifecycleController.js +4 -0
- package/build/services/StreamLifecycleController.js.map +1 -1
- package/build/services/asg/localNetworkTransport.d.ts.map +1 -1
- package/build/services/asg/localNetworkTransport.js +13 -12
- package/build/services/asg/localNetworkTransport.js.map +1 -1
- package/build/services/effectiveCapabilities.d.ts +1 -1
- package/build/services/effectiveCapabilities.d.ts.map +1 -1
- package/build/services/effectiveCapabilities.js +26 -7
- package/build/services/effectiveCapabilities.js.map +1 -1
- package/build/services/gestureClaims.d.ts +18 -0
- package/build/services/gestureClaims.d.ts.map +1 -0
- package/build/services/gestureClaims.js +24 -0
- package/build/services/gestureClaims.js.map +1 -0
- package/build/services/gestureDotGlyphs.d.ts +8 -0
- package/build/services/gestureDotGlyphs.d.ts.map +1 -0
- package/build/services/gestureDotGlyphs.js +13 -0
- package/build/services/gestureDotGlyphs.js.map +1 -0
- package/build/services/glassesClockSync.d.ts.map +1 -1
- package/build/services/glassesClockSync.js +5 -1
- package/build/services/glassesClockSync.js.map +1 -1
- package/build/services/iosNotificationAppCatalog.d.ts +13 -0
- package/build/services/iosNotificationAppCatalog.d.ts.map +1 -0
- package/build/services/iosNotificationAppCatalog.js +28 -0
- package/build/services/iosNotificationAppCatalog.js.map +1 -0
- package/build/services/otaDisplayState.d.ts +1 -1
- package/build/services/otaDisplayState.d.ts.map +1 -1
- package/build/services/otaDisplayState.js.map +1 -1
- package/build/services/otaInstallPolicy.d.ts +5 -1
- package/build/services/otaInstallPolicy.d.ts.map +1 -1
- package/build/services/otaInstallPolicy.js +4 -0
- package/build/services/otaInstallPolicy.js.map +1 -1
- package/build/services/otaLegacyMapping.d.ts +1 -1
- package/build/services/otaLegacyMapping.d.ts.map +1 -1
- package/build/services/otaLegacyMapping.js.map +1 -1
- package/build/services/otaManifestPolicy.d.ts +14 -0
- package/build/services/otaManifestPolicy.d.ts.map +1 -0
- package/build/services/otaManifestPolicy.js +20 -0
- package/build/services/otaManifestPolicy.js.map +1 -0
- package/build/services/otaManifestUrl.d.ts +2 -1
- package/build/services/otaManifestUrl.d.ts.map +1 -1
- package/build/services/otaManifestUrl.js +24 -32
- package/build/services/otaManifestUrl.js.map +1 -1
- package/build/services/otaReleaseVersion.d.ts +10 -0
- package/build/services/otaReleaseVersion.d.ts.map +1 -0
- package/build/services/otaReleaseVersion.js +14 -0
- package/build/services/otaReleaseVersion.js.map +1 -0
- package/build/stores/appVisibility.d.ts +3 -0
- package/build/stores/appVisibility.d.ts.map +1 -0
- package/build/stores/appVisibility.js +3 -0
- package/build/stores/appVisibility.js.map +1 -0
- package/build/stores/apps.d.ts.map +1 -1
- package/build/stores/apps.js +7 -1
- package/build/stores/apps.js.map +1 -1
- package/build/stores/core.d.ts +3 -3
- package/build/stores/core.d.ts.map +1 -1
- package/build/stores/core.js.map +1 -1
- package/build/stores/display.d.ts +4 -4
- package/build/stores/display.d.ts.map +1 -1
- package/build/stores/display.js +12 -36
- package/build/stores/display.js.map +1 -1
- package/build/stores/glasses.d.ts +10 -4
- package/build/stores/glasses.d.ts.map +1 -1
- package/build/stores/glasses.js +2 -0
- package/build/stores/glasses.js.map +1 -1
- package/build/stores/settings.d.ts.map +1 -1
- package/build/stores/settings.js +140 -5
- package/build/stores/settings.js.map +1 -1
- package/build/types/applet.d.ts +3 -1
- package/build/types/applet.d.ts.map +1 -1
- package/build/types/applet.js +0 -3
- package/build/types/applet.js.map +1 -1
- package/build/types/capabilities/even-realities-g2.d.ts.map +1 -1
- package/build/types/capabilities/even-realities-g2.js +2 -2
- package/build/types/capabilities/even-realities-g2.js.map +1 -1
- package/build/types/enums.d.ts +1 -5
- package/build/types/enums.d.ts.map +1 -1
- package/build/types/enums.js +1 -8
- package/build/types/enums.js.map +1 -1
- package/build/types/hardware.d.ts +1 -1
- package/build/types/hardware.d.ts.map +1 -1
- package/build/types/hardware.js +1 -0
- package/build/types/hardware.js.map +1 -1
- package/build/types/index.d.ts +3 -7
- package/build/types/index.d.ts.map +1 -1
- package/build/types/index.js +3 -7
- package/build/types/index.js.map +1 -1
- package/build/utils/display/composer/ColumnComposer.d.ts +1 -1
- package/build/utils/display/composer/ColumnComposer.js +1 -1
- package/build/utils/display/composer/ColumnComposer.js.map +1 -1
- package/build/utils/display/scene/process.d.ts.map +1 -1
- package/build/utils/display/scene/process.js +4 -3
- package/build/utils/display/scene/process.js.map +1 -1
- package/build/utils/display/scene/types.d.ts +6 -2
- package/build/utils/display/scene/types.d.ts.map +1 -1
- package/build/utils/display/scene/types.js.map +1 -1
- package/package.json +69 -35
- package/src/bluetooth-sdk/debug.ts +1 -0
- package/src/bluetooth-sdk/index.ts +2 -0
- package/src/bluetooth-sdk/ota-transport.ts +1 -0
- package/src/bluetooth-sdk/photo-receiver.ts +2 -0
- package/src/bluetooth-sdk/react.ts +1 -0
- package/src/bluetooth-sdk/types.ts +1 -0
- package/src/engine.ts +12 -3
- package/src/facades/display.ts +12 -2
- package/src/facades/displayMirror.ts +7 -15
- package/src/facades/glasses.ts +8 -4
- package/src/facades/ota.ts +36 -11
- package/src/facades/pairing.ts +103 -23
- package/src/facades/phoneNotifications.ts +32 -10
- package/src/facades/speakers.ts +1 -1
- package/src/facades/speech.ts +21 -7
- package/src/generated/releaseMetadata.ts +20 -0
- package/src/index.ts +31 -5
- package/src/react/MentraLiveOtaFlow.tsx +754 -0
- package/src/react/index.ts +21 -0
- package/src/react/useEngineSnapshot.ts +16 -0
- package/src/react/useMentraLiveOta.ts +793 -0
- package/src/services/AppRegistry.ts +16 -0
- package/src/services/BlobStore.ts +57 -10
- package/src/services/CustomDashboardModel.ts +83 -0
- package/src/services/CustomDashboardService.ts +544 -0
- package/src/services/DashboardCoordinator.ts +121 -0
- package/src/services/DeviceEventRouter.ts +48 -8
- package/src/services/DisplayProcessor.ts +3 -3
- package/src/services/GestureDotsService.ts +164 -0
- package/src/services/GlassesStatusProjection.ts +20 -11
- package/src/services/HotspotOtaTransport.ts +8 -7
- package/src/services/LocalDisplayManager.ts +198 -18
- package/src/services/LocalMiniappRuntime.ts +37 -0
- package/src/services/MiniappExitGesture.ts +76 -0
- package/src/services/OtaAutoChain.ts +130 -0
- package/src/services/OtaErrorMapping.ts +80 -0
- package/src/services/OtaInstallCoordinator.ts +40 -13
- package/src/services/OtaService.ts +1 -2
- package/src/services/OtaUpdateCheckService.ts +33 -19
- package/src/services/PhoneNotificationsSync.ts +125 -9
- package/src/services/PhonePhotoCoordinator.ts +1 -1
- package/src/services/PhoneStreamCoordinator.ts +10 -6
- package/src/services/SceneRenderer.ts +4 -0
- package/src/services/StreamLifecycleController.ts +4 -0
- package/src/services/asg/localNetworkTransport.ts +20 -19
- package/src/services/effectiveCapabilities.ts +27 -7
- package/src/services/gestureClaims.ts +28 -0
- package/src/services/gestureDotGlyphs.ts +15 -0
- package/src/services/glassesClockSync.ts +5 -1
- package/src/services/iosNotificationAppCatalog.ts +39 -0
- package/src/services/otaDisplayState.ts +1 -1
- package/src/services/otaInstallPolicy.ts +5 -1
- package/src/services/otaLegacyMapping.ts +1 -1
- package/src/services/otaManifestPolicy.ts +43 -0
- package/src/services/otaManifestUrl.ts +27 -36
- package/src/services/otaReleaseVersion.ts +22 -0
- package/src/stores/appVisibility.ts +3 -0
- package/src/stores/apps.ts +7 -1
- package/src/stores/core.ts +4 -4
- package/src/stores/display.ts +16 -48
- package/src/stores/glasses.ts +15 -6
- package/src/stores/settings.ts +140 -5
- package/src/types/applet.ts +3 -1
- package/src/types/capabilities/even-realities-g2.ts +3 -2
- package/src/types/enums.ts +1 -9
- package/src/types/hardware.ts +2 -1
- package/src/types/index.ts +3 -7
- package/src/utils/display/composer/ColumnComposer.ts +1 -1
- package/src/utils/display/scene/process.ts +4 -3
- package/src/utils/display/scene/types.ts +0 -0
- package/build/devtools.d.ts +0 -11
- package/build/devtools.d.ts.map +0 -1
- package/build/devtools.js +0 -13
- package/build/devtools.js.map +0 -1
- package/build/internal.d.ts +0 -67
- package/build/internal.d.ts.map +0 -1
- package/build/internal.js +0 -90
- package/build/internal.js.map +0 -1
- package/src/devtools.ts +0 -13
- package/src/internal.ts +0 -155
|
@@ -17,8 +17,7 @@
|
|
|
17
17
|
* state written mid-pass queues a follow-up pass — the same way a setState
|
|
18
18
|
* during React effects scheduled a re-render after the current effect batch.
|
|
19
19
|
*/
|
|
20
|
-
import BluetoothSdk from "@glassly/bluetooth-sdk
|
|
21
|
-
import type {OtaProgress, OtaStatus} from "@glassly/bluetooth-sdk/internal"
|
|
20
|
+
import BluetoothSdk, {type OtaProgress, type OtaStatus} from "@glassly/bluetooth-sdk"
|
|
22
21
|
import GlobalEventEmitter from "../utils/GlobalEventEmitter"
|
|
23
22
|
import {isGlassesConnected, useGlassesStore} from "../stores/glasses"
|
|
24
23
|
import {resolveOtaManifestUrl} from "./otaManifestUrl"
|
|
@@ -27,10 +26,12 @@ import type {OtaCheckCurrentGlassesResult} from "./OtaUpdateCheckService"
|
|
|
27
26
|
import {deriveDisplayState, type DisplayState} from "./otaDisplayState"
|
|
28
27
|
import {
|
|
29
28
|
BES_CONTINUE_LOCKOUT_MS,
|
|
29
|
+
BES_RESTART_TIMEOUT_MS,
|
|
30
30
|
DOWNLOAD_STUCK_TIMEOUT_MS,
|
|
31
31
|
GLOBAL_OTA_TIMEOUT_MS,
|
|
32
32
|
LEGACY_APK_COMPLETION_SETTLE_MS,
|
|
33
33
|
LEGACY_BES_CONTINUE_LOCKOUT_MS,
|
|
34
|
+
LEGACY_BES_RESTART_TIMEOUT_MS,
|
|
34
35
|
LEGACY_DOWNLOAD_STUCK_TIMEOUT_MS,
|
|
35
36
|
LEGACY_GLOBAL_OTA_TIMEOUT_MS,
|
|
36
37
|
LEGACY_MTK_INSTALL_TIMEOUT_MS,
|
|
@@ -170,6 +171,8 @@ export interface OtaInstallSnapshot {
|
|
|
170
171
|
/** Pre-ota_start phone staging/join state for a hotspot attempt. */
|
|
171
172
|
hotspotPhase: HotspotOtaPhase
|
|
172
173
|
hotspotArtifactPercent: number | null
|
|
174
|
+
/** Transport selected from the checked glasses capabilities and Wi-Fi state. */
|
|
175
|
+
transport: "wifi" | "hotspot"
|
|
173
176
|
}
|
|
174
177
|
|
|
175
178
|
type OtaStartOutcome = "pending" | "acknowledged" | "rejected"
|
|
@@ -244,6 +247,7 @@ class OtaInstallCoordinator {
|
|
|
244
247
|
private pingInterval: ReturnType<typeof setInterval> | null = null
|
|
245
248
|
private queryReplyTimeout: ReturnType<typeof setTimeout> | null = null
|
|
246
249
|
private continueLockoutTimer: ReturnType<typeof setTimeout> | null = null
|
|
250
|
+
private restartRecoveryTimeout: ReturnType<typeof setTimeout> | null = null
|
|
247
251
|
private legacyApkSettleTimer: ReturnType<typeof setTimeout> | null = null
|
|
248
252
|
private mtkStallDetectTimer: ReturnType<typeof setTimeout> | null = null
|
|
249
253
|
private mtkSimTickTimer: ReturnType<typeof setInterval> | null = null
|
|
@@ -407,7 +411,7 @@ class OtaInstallCoordinator {
|
|
|
407
411
|
if (this.otaStartOwnership?.outcome === "pending") {
|
|
408
412
|
void this.sendOtaStartWithWatchdogs()
|
|
409
413
|
} else {
|
|
410
|
-
void BluetoothSdk.
|
|
414
|
+
void BluetoothSdk.queryOtaStatus().catch(() => {})
|
|
411
415
|
this.armQueryReplyFallback("retry")
|
|
412
416
|
}
|
|
413
417
|
}
|
|
@@ -449,16 +453,17 @@ class OtaInstallCoordinator {
|
|
|
449
453
|
* stays in the screen): clear the selected update, and after an APK step
|
|
450
454
|
* clear the stale build number so the next check re-reads version_info.
|
|
451
455
|
*/
|
|
452
|
-
finish(): void {
|
|
456
|
+
async finish(): Promise<void> {
|
|
453
457
|
if (this.otaStartOwnership?.outcome !== "pending") {
|
|
454
458
|
this.otaStartOwnership = null
|
|
455
459
|
}
|
|
456
460
|
useGlassesStore.getState().setOtaUpdateAvailable(null)
|
|
457
461
|
if (this.apkStepSeen) {
|
|
458
|
-
BluetoothSdk.updateGlasses({buildNumber: ""})
|
|
459
462
|
useGlassesStore.getState().setGlassesInfo({buildNumber: ""})
|
|
460
463
|
}
|
|
461
|
-
|
|
464
|
+
// The post-install check needs internet again. Do not let callers leave the
|
|
465
|
+
// terminal screen while Android still owns the no-internet hotspot route.
|
|
466
|
+
await this.teardownHotspotTransport()
|
|
462
467
|
}
|
|
463
468
|
|
|
464
469
|
/**
|
|
@@ -467,8 +472,8 @@ class OtaInstallCoordinator {
|
|
|
467
472
|
* deriveDisplayState doesn't resurrect the stale install when the host
|
|
468
473
|
* navigates away and back through /ota routes.
|
|
469
474
|
*/
|
|
470
|
-
discard(): void {
|
|
471
|
-
this.finish()
|
|
475
|
+
async discard(): Promise<void> {
|
|
476
|
+
await this.finish()
|
|
472
477
|
const store = useGlassesStore.getState()
|
|
473
478
|
store.setOtaStatus(null)
|
|
474
479
|
store.setOtaProgress(null)
|
|
@@ -505,6 +510,7 @@ class OtaInstallCoordinator {
|
|
|
505
510
|
versionChangePhase: this.deriveVersionChangePhase(connected),
|
|
506
511
|
hotspotPhase: this.hotspotPhase,
|
|
507
512
|
hotspotArtifactPercent: this.hotspotArtifactPercent,
|
|
513
|
+
transport: this.selectedTransport,
|
|
508
514
|
}
|
|
509
515
|
}
|
|
510
516
|
|
|
@@ -997,12 +1003,22 @@ class OtaInstallCoordinator {
|
|
|
997
1003
|
// (15s unified, 35s for legacy-shaped sessions — WP 8C-f).
|
|
998
1004
|
if (displayStateChanged && displayState === "restarting") {
|
|
999
1005
|
this.clearContinueLockoutTimer()
|
|
1006
|
+
this.clearRestartRecoveryTimeout()
|
|
1000
1007
|
const lockoutMs = legacySession ? LEGACY_BES_CONTINUE_LOCKOUT_MS : BES_CONTINUE_LOCKOUT_MS
|
|
1008
|
+
const restartTimeoutMs = legacySession ? LEGACY_BES_RESTART_TIMEOUT_MS : BES_RESTART_TIMEOUT_MS
|
|
1001
1009
|
this.setContinueButtonDisabled(true)
|
|
1002
1010
|
this.continueLockoutTimer = setTimeout(() => {
|
|
1003
1011
|
this.continueLockoutTimer = null
|
|
1004
1012
|
this.setContinueButtonDisabled(false)
|
|
1005
1013
|
}, lockoutMs)
|
|
1014
|
+
this.restartRecoveryTimeout = setTimeout(() => {
|
|
1015
|
+
this.restartRecoveryTimeout = null
|
|
1016
|
+
if (this.computeDisplayStateNow() !== "restarting") return
|
|
1017
|
+
console.log(`[OTA_PROGRESS] watchdog: glasses did not finish restarting in ${restartTimeoutMs}ms`)
|
|
1018
|
+
this.setErrorMsg(OtaProgressMessages.restartTimeout)
|
|
1019
|
+
}, restartTimeoutMs)
|
|
1020
|
+
} else if (displayStateChanged) {
|
|
1021
|
+
this.clearRestartRecoveryTimeout()
|
|
1006
1022
|
}
|
|
1007
1023
|
|
|
1008
1024
|
// Legacy MTK install stall simulation (WP 8C-e): display-only. The simulation
|
|
@@ -1065,7 +1081,7 @@ class OtaInstallCoordinator {
|
|
|
1065
1081
|
if (this.otaStartOwnership?.outcome === "pending") {
|
|
1066
1082
|
void this.sendOtaStartWithWatchdogs()
|
|
1067
1083
|
}
|
|
1068
|
-
void BluetoothSdk.
|
|
1084
|
+
void BluetoothSdk.queryOtaStatus().catch(() => {})
|
|
1069
1085
|
this.armQueryReplyFallback("reconnect")
|
|
1070
1086
|
return true
|
|
1071
1087
|
}
|
|
@@ -1109,7 +1125,7 @@ class OtaInstallCoordinator {
|
|
|
1109
1125
|
if (noSessionYet) {
|
|
1110
1126
|
if (!isIdleStatus && this.otaStartOwnership?.outcome === "acknowledged") {
|
|
1111
1127
|
console.log("[OTA_PROGRESS] initial mount, acknowledged session has no cached status — reconciling")
|
|
1112
|
-
void BluetoothSdk.
|
|
1128
|
+
void BluetoothSdk.queryOtaStatus().catch(() => {})
|
|
1113
1129
|
this.armQueryReplyFallback("initial-mount")
|
|
1114
1130
|
return
|
|
1115
1131
|
}
|
|
@@ -1125,7 +1141,7 @@ class OtaInstallCoordinator {
|
|
|
1125
1141
|
void this.sendOtaStartWithWatchdogs()
|
|
1126
1142
|
} else {
|
|
1127
1143
|
console.log("[OTA_PROGRESS] initial mount, session exists, sending ota_query_status")
|
|
1128
|
-
void BluetoothSdk.
|
|
1144
|
+
void BluetoothSdk.queryOtaStatus().catch(() => {})
|
|
1129
1145
|
this.armQueryReplyFallback("initial-mount")
|
|
1130
1146
|
}
|
|
1131
1147
|
}
|
|
@@ -1154,7 +1170,7 @@ class OtaInstallCoordinator {
|
|
|
1154
1170
|
this.clearProgressTimeout()
|
|
1155
1171
|
this.onFirstActivity()
|
|
1156
1172
|
this.onFirstNonZeroProgress()
|
|
1157
|
-
void BluetoothSdk.
|
|
1173
|
+
void BluetoothSdk.queryOtaStatus().catch(() => {})
|
|
1158
1174
|
useGlassesStore.getState().setMtkUpdatedThisSession(true)
|
|
1159
1175
|
}
|
|
1160
1176
|
|
|
@@ -1345,7 +1361,7 @@ class OtaInstallCoordinator {
|
|
|
1345
1361
|
const s = useGlassesStore.getState().otaStatus
|
|
1346
1362
|
if (s?.stepType === "apk" && s.phase === "install" && s.status === "in_progress") {
|
|
1347
1363
|
this.apkInstallPollInFlight = true
|
|
1348
|
-
void BluetoothSdk.
|
|
1364
|
+
void BluetoothSdk.queryOtaStatus()
|
|
1349
1365
|
.catch(() => {})
|
|
1350
1366
|
.finally(() => {
|
|
1351
1367
|
this.apkInstallPollInFlight = false
|
|
@@ -1492,6 +1508,9 @@ class OtaInstallCoordinator {
|
|
|
1492
1508
|
otaVersionUrl = this.hotspotManifestUrl
|
|
1493
1509
|
this.maybeArmStuckWatchdog()
|
|
1494
1510
|
}
|
|
1511
|
+
if (!otaVersionUrl) {
|
|
1512
|
+
throw new Error("OTA is disabled because this build has no immutable manifest pin")
|
|
1513
|
+
}
|
|
1495
1514
|
console.log(`[OTA_PROGRESS] sending ota_start with ${this.selectedTransport} manifest URL: ${otaVersionUrl}`)
|
|
1496
1515
|
nativeStartAttempted = true
|
|
1497
1516
|
await BluetoothSdk.startOtaUpdate(otaVersionUrl)
|
|
@@ -1662,6 +1681,13 @@ class OtaInstallCoordinator {
|
|
|
1662
1681
|
}
|
|
1663
1682
|
}
|
|
1664
1683
|
|
|
1684
|
+
private clearRestartRecoveryTimeout(): void {
|
|
1685
|
+
if (this.restartRecoveryTimeout) {
|
|
1686
|
+
clearTimeout(this.restartRecoveryTimeout)
|
|
1687
|
+
this.restartRecoveryTimeout = null
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
|
|
1665
1691
|
private clearLegacyApkSettleTimer(): void {
|
|
1666
1692
|
if (this.legacyApkSettleTimer) {
|
|
1667
1693
|
clearTimeout(this.legacyApkSettleTimer)
|
|
@@ -1700,6 +1726,7 @@ class OtaInstallCoordinator {
|
|
|
1700
1726
|
this.clearPingInterval()
|
|
1701
1727
|
this.clearLegacyApkSettleTimer()
|
|
1702
1728
|
this.clearMtkSimulationTimers()
|
|
1729
|
+
this.clearRestartRecoveryTimeout()
|
|
1703
1730
|
}
|
|
1704
1731
|
}
|
|
1705
1732
|
|
|
@@ -10,8 +10,7 @@
|
|
|
10
10
|
*
|
|
11
11
|
* Started by `engine.start()`. Idempotent.
|
|
12
12
|
*/
|
|
13
|
-
import BluetoothSdk from "@glassly/bluetooth-sdk
|
|
14
|
-
import type {OtaStatus} from "@glassly/bluetooth-sdk/internal"
|
|
13
|
+
import BluetoothSdk, {type OtaStatus} from "@glassly/bluetooth-sdk"
|
|
15
14
|
import GlobalEventEmitter from "../utils/GlobalEventEmitter"
|
|
16
15
|
import {useGlassesStore} from "../stores/glasses"
|
|
17
16
|
import {handleOtaClockSkewFromGlasses} from "./glassesClockSync"
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import BluetoothSdk from "@glassly/bluetooth-sdk
|
|
2
|
-
import
|
|
1
|
+
import BluetoothSdk, {type OtaUpdateInfo} from "@glassly/bluetooth-sdk"
|
|
2
|
+
import {ENGINE_RELEASE_METADATA} from "../generated/releaseMetadata"
|
|
3
3
|
import {getGlassesSystemTimeMs, isGlassesConnected, useGlassesStore, waitForGlassesState} from "../stores/glasses"
|
|
4
|
-
import {SETTINGS, useSettingsStore} from "../stores/settings"
|
|
5
4
|
import {maybeFixGlassesClockFromVersionInfo} from "./glassesClockSync"
|
|
6
|
-
import {resolveOtaManifestUrl} from "./otaManifestUrl"
|
|
5
|
+
import {hasConfiguredModernOtaManifestPin, resolveOtaManifestUrl} from "./otaManifestUrl"
|
|
6
|
+
import {resolveOtaReleaseVersion} from "./otaReleaseVersion"
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Phone-side mirror of ASG's `DOWNGRADE_FLOOR_VERSION_CODE`. Set to the versionCode of the first
|
|
@@ -39,6 +39,7 @@ export interface BesFirmware {
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
export interface VersionJson {
|
|
42
|
+
releaseVersion?: string
|
|
42
43
|
apps?: {
|
|
43
44
|
[packageName: string]: VersionInfo
|
|
44
45
|
}
|
|
@@ -67,6 +68,8 @@ export interface OtaCheckResult {
|
|
|
67
68
|
* instead of racing it with a second fetch of the same URL.
|
|
68
69
|
*/
|
|
69
70
|
manifestBody: string | null
|
|
71
|
+
/** Coordinated release identity for the selected OTA pin. */
|
|
72
|
+
releaseVersion: string | null
|
|
70
73
|
/**
|
|
71
74
|
* Why the check failed, when it did. "network" is retryable (connection/server
|
|
72
75
|
* trouble); "pin_unavailable" is permanent for this app build (manifest 404/gone,
|
|
@@ -107,6 +110,7 @@ function emptyCheckResult(skippedReason?: OtaCheckSkippedReason): OtaCheckCurren
|
|
|
107
110
|
besVersion: null,
|
|
108
111
|
isApkDowngrade: false,
|
|
109
112
|
manifestBody: null,
|
|
113
|
+
releaseVersion: null,
|
|
110
114
|
updateInfo: null,
|
|
111
115
|
isRequired: true,
|
|
112
116
|
skippedReason,
|
|
@@ -123,7 +127,7 @@ async function fetchVersionInfoDetailed(url: string): Promise<ManifestFetchResul
|
|
|
123
127
|
try {
|
|
124
128
|
const response = await fetch(url)
|
|
125
129
|
if (!response.ok) {
|
|
126
|
-
console.
|
|
130
|
+
console.warn("Failed to fetch version info:", response.status)
|
|
127
131
|
// 4xx: the manifest does not exist (or is gone) — permanent for this app
|
|
128
132
|
// build, retrying cannot help. Everything else is transient server/network
|
|
129
133
|
// trouble and retryable.
|
|
@@ -135,11 +139,11 @@ async function fetchVersionInfoDetailed(url: string): Promise<ManifestFetchResul
|
|
|
135
139
|
try {
|
|
136
140
|
return {json: await response.json()}
|
|
137
141
|
} catch (parseError) {
|
|
138
|
-
console.
|
|
142
|
+
console.warn("OTA: manifest is not valid JSON:", parseError)
|
|
139
143
|
return {json: null, failureReason: "pin_unavailable"}
|
|
140
144
|
}
|
|
141
145
|
} catch (error) {
|
|
142
|
-
console.
|
|
146
|
+
console.warn("OTA: Error fetching version info:", error)
|
|
143
147
|
return {json: null, failureReason: "network"}
|
|
144
148
|
}
|
|
145
149
|
}
|
|
@@ -299,6 +303,7 @@ export async function checkForOtaUpdate(
|
|
|
299
303
|
besVersion: null,
|
|
300
304
|
isApkDowngrade: false,
|
|
301
305
|
manifestBody: null,
|
|
306
|
+
releaseVersion: null,
|
|
302
307
|
checkFailureReason: fetched.failureReason ?? "network",
|
|
303
308
|
}
|
|
304
309
|
}
|
|
@@ -330,6 +335,7 @@ export async function checkForOtaUpdate(
|
|
|
330
335
|
besVersion: null,
|
|
331
336
|
isApkDowngrade: false,
|
|
332
337
|
manifestBody: null,
|
|
338
|
+
releaseVersion: null,
|
|
333
339
|
checkFailureReason: "pin_unavailable",
|
|
334
340
|
}
|
|
335
341
|
}
|
|
@@ -368,6 +374,12 @@ export async function checkForOtaUpdate(
|
|
|
368
374
|
besVersion: versionJson?.bes_firmware?.version || null,
|
|
369
375
|
isApkDowngrade: apkDirection === "downgrade",
|
|
370
376
|
manifestBody: versionJson ? JSON.stringify(versionJson) : null,
|
|
377
|
+
releaseVersion: resolveOtaReleaseVersion({
|
|
378
|
+
manifestReleaseVersion: versionJson.releaseVersion,
|
|
379
|
+
manifestUrl: otaVersionUrl,
|
|
380
|
+
packagedManifestUrl: ENGINE_RELEASE_METADATA.otaManifestUrl,
|
|
381
|
+
packagedReleaseIdentity: ENGINE_RELEASE_METADATA.releaseIdentity,
|
|
382
|
+
}),
|
|
371
383
|
}
|
|
372
384
|
} catch (error) {
|
|
373
385
|
console.error("Error checking for OTA update:", error)
|
|
@@ -380,6 +392,7 @@ export async function checkForOtaUpdate(
|
|
|
380
392
|
besVersion: null,
|
|
381
393
|
isApkDowngrade: false,
|
|
382
394
|
manifestBody: null,
|
|
395
|
+
releaseVersion: null,
|
|
383
396
|
checkFailureReason: "network",
|
|
384
397
|
}
|
|
385
398
|
}
|
|
@@ -401,22 +414,19 @@ export async function checkCurrentGlassesForUpdate(
|
|
|
401
414
|
return emptyCheckResult("disconnected")
|
|
402
415
|
}
|
|
403
416
|
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
const overrideUrl = useSettingsStore.getState().getSetting(SETTINGS.ota_version_url.key)
|
|
407
|
-
const overrideActive = superMode && typeof overrideUrl === "string" && overrideUrl.trim() !== ""
|
|
408
|
-
if (!embeddedManifestUrl && !overrideActive) {
|
|
409
|
-
console.log("OTA: check skipped - mobile app has no embedded OTA manifest pin")
|
|
417
|
+
if (!hasConfiguredModernOtaManifestPin()) {
|
|
418
|
+
console.log("OTA: check skipped - this app or Engine package has no embedded OTA manifest pin")
|
|
410
419
|
return emptyCheckResult("dev_build")
|
|
411
420
|
}
|
|
412
|
-
if (!embeddedManifestUrl) {
|
|
413
|
-
console.log("OTA: no embedded manifest pin, but super-mode manifest override is active - checking anyway")
|
|
414
|
-
}
|
|
415
421
|
|
|
416
422
|
if (refreshVersionInfo) {
|
|
417
|
-
void BluetoothSdk.requestVersionInfo()
|
|
418
|
-
|
|
419
|
-
|
|
423
|
+
void BluetoothSdk.requestVersionInfo()
|
|
424
|
+
.then((versionInfo) => {
|
|
425
|
+
useGlassesStore.getState().setGlassesInfo(versionInfo)
|
|
426
|
+
})
|
|
427
|
+
.catch((error) => {
|
|
428
|
+
console.warn("OTA: Failed to request version_info from glasses:", error)
|
|
429
|
+
})
|
|
420
430
|
}
|
|
421
431
|
|
|
422
432
|
let buildNumber = useGlassesStore.getState().buildNumber
|
|
@@ -470,6 +480,10 @@ export async function checkCurrentGlassesForUpdate(
|
|
|
470
480
|
}
|
|
471
481
|
|
|
472
482
|
const manifestUrl = resolveOtaManifestUrl(useGlassesStore.getState().otaVersionUrl, buildNumber)
|
|
483
|
+
if (!manifestUrl) {
|
|
484
|
+
console.log("OTA: check skipped - no OTA manifest pin resolved")
|
|
485
|
+
return emptyCheckResult("dev_build")
|
|
486
|
+
}
|
|
473
487
|
const result = await checkForOtaUpdate(
|
|
474
488
|
manifestUrl,
|
|
475
489
|
buildNumber,
|
|
@@ -1,24 +1,50 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Phone-notifications sync — engine-owned.
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
2
|
+
* Phone-notifications sync — engine-owned. Started by `engine.start()`.
|
|
3
|
+
*
|
|
4
|
+
* Android: pushes the notification-forwarding operational kill switch + config
|
|
5
|
+
* (`android_notification_listener_enabled` and `notifications_blocklist`) to
|
|
6
|
+
* the native NotificationListener via `CrustModule.setNotificationConfig`, so
|
|
6
7
|
* the blocklist reaches the listener for ANY host — not just the Glassly App,
|
|
7
8
|
* where this used to live in MantleManager. The Android service is desired by
|
|
8
9
|
* default now that its cold start is isolated from React Native. Native code
|
|
9
10
|
* keeps the component discoverable in Android's notification-access Settings,
|
|
10
11
|
* but only binds the service after access is granted. The debug setting remains
|
|
11
12
|
* a hidden emergency kill switch; Notify's running state gates presentation,
|
|
12
|
-
* not capture.
|
|
13
|
+
* not capture.
|
|
14
|
+
*
|
|
15
|
+
* iOS: the G2 consumes notifications on-firmware via ANCS, so the phone's job
|
|
16
|
+
* is config, not capture. This sync (a) records the apps the glasses report
|
|
17
|
+
* posting notifications (`notification_app_seen` → `ios_notification_seen_apps`)
|
|
18
|
+
* and (b) maps the shared `notifications_blocklist` + the G2 display settings
|
|
19
|
+
* onto the native `notification_filter` / `notification_ctrl` DeviceStore keys,
|
|
20
|
+
* which the bluetooth-sdk pushes to the glasses (and replays on reconnect).
|
|
21
|
+
* The firmware filter is allow-based and fail-open: an empty blocklist disables
|
|
22
|
+
* whitelisting entirely; any block enables it with allowlist = (catalog ∪ seen)
|
|
23
|
+
* minus blocked.
|
|
13
24
|
*/
|
|
25
|
+
import {Platform} from "react-native"
|
|
14
26
|
import {shallow} from "zustand/shallow"
|
|
15
27
|
import CrustModule from "@glassly/crust"
|
|
28
|
+
import BluetoothSdk from "@glassly/bluetooth-sdk/internal"
|
|
29
|
+
import type {
|
|
30
|
+
G2NotificationCtrlSetting,
|
|
31
|
+
G2NotificationFilterSetting,
|
|
32
|
+
NotificationAppSeenEvent,
|
|
33
|
+
} from "@glassly/bluetooth-sdk/internal"
|
|
16
34
|
|
|
17
35
|
import {useSettingsStore, SETTINGS} from "../stores/settings"
|
|
36
|
+
import {IOS_NOTIFICATION_APP_CATALOG} from "./iosNotificationAppCatalog"
|
|
37
|
+
|
|
38
|
+
export interface SeenNotificationApp {
|
|
39
|
+
appId: string
|
|
40
|
+
displayName: string
|
|
41
|
+
lastSeen: number
|
|
42
|
+
}
|
|
18
43
|
|
|
19
44
|
let unsubscribe: (() => void) | null = null
|
|
45
|
+
let seenAppsSubscription: {remove(): void} | null = null
|
|
20
46
|
|
|
21
|
-
function
|
|
47
|
+
function pushAndroidConfig(): void {
|
|
22
48
|
const s = useSettingsStore.getState()
|
|
23
49
|
const listenerEnabled = Boolean(s.getSetting(SETTINGS.android_notification_listener_enabled.key))
|
|
24
50
|
const blocklist = s.getSetting(SETTINGS.notifications_blocklist.key)
|
|
@@ -27,20 +53,110 @@ function pushConfig(): void {
|
|
|
27
53
|
)
|
|
28
54
|
}
|
|
29
55
|
|
|
56
|
+
export function getSeenNotificationApps(): SeenNotificationApp[] {
|
|
57
|
+
const seen = useSettingsStore.getState().getSetting(SETTINGS.ios_notification_seen_apps.key)
|
|
58
|
+
return Array.isArray(seen) ? (seen as SeenNotificationApp[]) : []
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Catalog ∪ seen apps, seen display names winning. Sorted by name. */
|
|
62
|
+
export function getKnownNotificationApps(): Array<{appId: string; displayName: string}> {
|
|
63
|
+
const merged = new Map<string, string>()
|
|
64
|
+
for (const app of IOS_NOTIFICATION_APP_CATALOG) {
|
|
65
|
+
merged.set(app.appId, app.displayName)
|
|
66
|
+
}
|
|
67
|
+
for (const app of getSeenNotificationApps()) {
|
|
68
|
+
if (app.appId) merged.set(app.appId, app.displayName || app.appId)
|
|
69
|
+
}
|
|
70
|
+
return [...merged.entries()]
|
|
71
|
+
.map(([appId, displayName]) => ({appId, displayName}))
|
|
72
|
+
.sort((a, b) => a.displayName.localeCompare(b.displayName))
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function computeG2NotificationFilter(): G2NotificationFilterSetting {
|
|
76
|
+
const blocklistValue = useSettingsStore.getState().getSetting(SETTINGS.notifications_blocklist.key)
|
|
77
|
+
const blocklist = Array.isArray(blocklistValue) ? (blocklistValue as string[]) : []
|
|
78
|
+
const blocked = new Set(blocklist)
|
|
79
|
+
// The list is included even while filtering is off, so the on-glass file
|
|
80
|
+
// already holds the user's current list when a first block enables it.
|
|
81
|
+
const apps = getKnownNotificationApps()
|
|
82
|
+
.filter((app) => !blocked.has(app.appId))
|
|
83
|
+
.map((app) => ({id: app.appId, name: app.displayName}))
|
|
84
|
+
return {enabled: blocked.size > 0, apps}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function computeG2NotificationCtrl(): G2NotificationCtrlSetting {
|
|
88
|
+
const s = useSettingsStore.getState()
|
|
89
|
+
return {
|
|
90
|
+
enabled: Boolean(s.getSetting(SETTINGS.g2_notifications_enabled.key)),
|
|
91
|
+
auto_display: Boolean(s.getSetting(SETTINGS.g2_notification_auto_display.key)),
|
|
92
|
+
disp_time: Number(s.getSetting(SETTINGS.g2_notification_disp_time.key)) || 0,
|
|
93
|
+
avoid_disturb: Boolean(s.getSetting(SETTINGS.g2_notification_avoid_disturb.key)),
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function pushIosConfig(): void {
|
|
98
|
+
// The native DeviceStore dedupes unchanged values and replays these to the
|
|
99
|
+
// glasses on every reconnect, so pushing eagerly is cheap.
|
|
100
|
+
void Promise.resolve(
|
|
101
|
+
BluetoothSdk.updateBluetoothSettings({
|
|
102
|
+
notification_ctrl: computeG2NotificationCtrl(),
|
|
103
|
+
notification_filter: computeG2NotificationFilter(),
|
|
104
|
+
}),
|
|
105
|
+
).catch((err: unknown) =>
|
|
106
|
+
console.warn(`PhoneNotificationsSync: notification config push failed: ${(err as Error)?.message ?? err}`),
|
|
107
|
+
)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** Record a glasses-reported source app. Writes only on new app / renamed app. */
|
|
111
|
+
function recordSeenApp(event: NotificationAppSeenEvent): void {
|
|
112
|
+
if (!event.appId) return
|
|
113
|
+
const seen = getSeenNotificationApps()
|
|
114
|
+
const existing = seen.find((app) => app.appId === event.appId)
|
|
115
|
+
const displayName = event.displayName || existing?.displayName || event.appId
|
|
116
|
+
if (existing && existing.displayName === displayName) return
|
|
117
|
+
const next = seen
|
|
118
|
+
.filter((app) => app.appId !== event.appId)
|
|
119
|
+
.concat([{appId: event.appId, displayName, lastSeen: event.timestamp || Date.now()}])
|
|
120
|
+
useSettingsStore.getState().setSetting(SETTINGS.ios_notification_seen_apps.key, next)
|
|
121
|
+
}
|
|
122
|
+
|
|
30
123
|
export function startPhoneNotificationsSync(): void {
|
|
31
124
|
if (unsubscribe) return
|
|
32
|
-
|
|
125
|
+
|
|
126
|
+
if (Platform.OS === "android") {
|
|
127
|
+
pushAndroidConfig() // initial sync so the listener has the current config
|
|
128
|
+
unsubscribe = useSettingsStore.subscribe(
|
|
129
|
+
(state) => ({
|
|
130
|
+
listenerEnabled: state.getSetting(SETTINGS.android_notification_listener_enabled.key),
|
|
131
|
+
blocklist: state.getSetting(SETTINGS.notifications_blocklist.key),
|
|
132
|
+
}),
|
|
133
|
+
() => pushAndroidConfig(),
|
|
134
|
+
{equalityFn: shallow},
|
|
135
|
+
)
|
|
136
|
+
return
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (Platform.OS !== "ios") return
|
|
140
|
+
|
|
141
|
+
pushIosConfig()
|
|
33
142
|
unsubscribe = useSettingsStore.subscribe(
|
|
34
143
|
(state) => ({
|
|
35
|
-
listenerEnabled: state.getSetting(SETTINGS.android_notification_listener_enabled.key),
|
|
36
144
|
blocklist: state.getSetting(SETTINGS.notifications_blocklist.key),
|
|
145
|
+
seenApps: state.getSetting(SETTINGS.ios_notification_seen_apps.key),
|
|
146
|
+
enabled: state.getSetting(SETTINGS.g2_notifications_enabled.key),
|
|
147
|
+
autoDisplay: state.getSetting(SETTINGS.g2_notification_auto_display.key),
|
|
148
|
+
dispTime: state.getSetting(SETTINGS.g2_notification_disp_time.key),
|
|
149
|
+
avoidDisturb: state.getSetting(SETTINGS.g2_notification_avoid_disturb.key),
|
|
37
150
|
}),
|
|
38
|
-
() =>
|
|
151
|
+
() => pushIosConfig(),
|
|
39
152
|
{equalityFn: shallow},
|
|
40
153
|
)
|
|
154
|
+
seenAppsSubscription = BluetoothSdk.addListener("notification_app_seen", recordSeenApp)
|
|
41
155
|
}
|
|
42
156
|
|
|
43
157
|
export function stopPhoneNotificationsSync(): void {
|
|
44
158
|
unsubscribe?.()
|
|
45
159
|
unsubscribe = null
|
|
160
|
+
seenAppsSubscription?.remove()
|
|
161
|
+
seenAppsSubscription = null
|
|
46
162
|
}
|
|
@@ -166,7 +166,7 @@ export class PhonePhotoCoordinator {
|
|
|
166
166
|
// to time out. Fail fast with a typed error.
|
|
167
167
|
//
|
|
168
168
|
// We DON'T pre-check `hasCamera` here — the canonical capability data
|
|
169
|
-
// lives in `getModelCapabilities(deviceModel)`
|
|
169
|
+
// lives in `getModelCapabilities(deviceModel)` from @glassly/engine and
|
|
170
170
|
// pulling that into this file would add a cross-package import. If a
|
|
171
171
|
// cameraless device receives the BLE photo command, the glasses-side
|
|
172
172
|
// handler will return a photo_response error within ~1s and the gated
|
|
@@ -796,12 +796,6 @@ export class PhoneStreamCoordinator {
|
|
|
796
796
|
for (const reject of entry.hlsReadyRejecters) reject(pendingErr)
|
|
797
797
|
entry.hlsReadyResolvers = []
|
|
798
798
|
entry.hlsReadyRejecters = []
|
|
799
|
-
// Cloudflare DELETE is fire-and-forget — failing it doesn't undo the
|
|
800
|
-
// BLE stop we're about to do, and Cloudflare's idle-input scavenger
|
|
801
|
-
// will eventually clean up leaked inputs.
|
|
802
|
-
teardownManagedStream(entry.liveInputId).catch((err) => {
|
|
803
|
-
console.warn("[STREAM] teardownManagedStream failed:", err)
|
|
804
|
-
})
|
|
805
799
|
}
|
|
806
800
|
|
|
807
801
|
try {
|
|
@@ -816,6 +810,16 @@ export class PhoneStreamCoordinator {
|
|
|
816
810
|
// Only clear if we're still the active entry (defensive — runExclusive
|
|
817
811
|
// serializes us, so this should always be true).
|
|
818
812
|
if (this.current === entry) this.current = null
|
|
813
|
+
|
|
814
|
+
if (entry.kind === "managed") {
|
|
815
|
+
// Start remote cleanup only after the publisher has stopped, but do not
|
|
816
|
+
// hold the local transition lock on an unbounded network request. The
|
|
817
|
+
// runtime's durable cleanup queue owns retries if this request fails or
|
|
818
|
+
// the app exits before Cloudflare finishes finalizing the recording.
|
|
819
|
+
void teardownManagedStream(entry.liveInputId).catch((err) => {
|
|
820
|
+
console.warn("[STREAM] teardownManagedStream failed:", err)
|
|
821
|
+
})
|
|
822
|
+
}
|
|
819
823
|
}
|
|
820
824
|
}
|
|
821
825
|
}
|
|
@@ -273,6 +273,10 @@ class SceneRenderer {
|
|
|
273
273
|
}
|
|
274
274
|
|
|
275
275
|
private sendFrame(frame: SceneFrame): void {
|
|
276
|
+
// Single native view: only the visible surface's frames go out. Suppressed
|
|
277
|
+
// main frames stay committed; setDashboardActive(false) replays them.
|
|
278
|
+
const dashboardActive = useDisplayStore.getState().dashboardActive
|
|
279
|
+
if (dashboardActive !== (frame.view === "dashboard")) return
|
|
276
280
|
try {
|
|
277
281
|
// Frames carry the canvas they were laid out against; only a frame from
|
|
278
282
|
// a path that predates the tag falls back to the current capabilities.
|
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
* Drives RTMP stream liveliness checks via a keep-alive heartbeat:
|
|
3
3
|
* `setActive(true)` starts the timer; each tick sends a keep-alive with a
|
|
4
4
|
* fresh ackId; `maxMissedAcks` consecutive timeouts fire `onTimeout`.
|
|
5
|
+
*
|
|
6
|
+
* Phone-owned successor to the retired cloud stream lifecycle controller.
|
|
7
|
+
* It uses the local `LifecycleLogger` interface because pino is not a phone
|
|
8
|
+
* dependency.
|
|
5
9
|
*/
|
|
6
10
|
|
|
7
11
|
export interface LifecycleLogger {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as RNFS from "@dr.pogodin/react-native-fs"
|
|
2
|
-
import {
|
|
2
|
+
import {otaLocalNetwork, type OtaLocalNetworkDownloadProgress} from "@glassly/bluetooth-sdk/ota-transport"
|
|
3
3
|
import {Buffer} from "buffer"
|
|
4
4
|
import {Platform} from "react-native"
|
|
5
5
|
import WifiManager from "react-native-wifi-reborn"
|
|
@@ -17,7 +17,7 @@ export function shouldUseScopedLocalNetwork(os: string, moduleAvailable: boolean
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
function supportsScopedConnection(): boolean {
|
|
20
|
-
return shouldUseScopedLocalNetwork(Platform.OS,
|
|
20
|
+
return shouldUseScopedLocalNetwork(Platform.OS, otaLocalNetwork.isAvailable())
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
function normalizeHeaders(headers?: HeadersInit): Record<string, string> {
|
|
@@ -48,25 +48,25 @@ export const localNetworkTransport = {
|
|
|
48
48
|
await WifiManager.connectToProtectedSSID(ssid, password, false, false)
|
|
49
49
|
return undefined
|
|
50
50
|
}
|
|
51
|
-
const result = await
|
|
51
|
+
const result = await otaLocalNetwork.connect(ssid, password)
|
|
52
52
|
scopedConnectionActive = true
|
|
53
53
|
return result.localAddress
|
|
54
54
|
},
|
|
55
55
|
|
|
56
56
|
async disconnect(): Promise<void> {
|
|
57
57
|
if (supportsScopedConnection()) {
|
|
58
|
-
await
|
|
58
|
+
await otaLocalNetwork.disconnect().catch(() => {})
|
|
59
59
|
}
|
|
60
60
|
scopedConnectionActive = false
|
|
61
61
|
nativeJobs.clear()
|
|
62
62
|
},
|
|
63
63
|
|
|
64
64
|
async fetch(url: string | URL | Request, init?: RequestInit, timeoutMs = 30_000): Promise<Response> {
|
|
65
|
-
if (!scopedConnectionActive || !
|
|
65
|
+
if (!scopedConnectionActive || !otaLocalNetwork.isAvailable() || typeof url !== "string") {
|
|
66
66
|
return globalThis.fetch(url, init)
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
const nativeModule =
|
|
69
|
+
const nativeModule = otaLocalNetwork
|
|
70
70
|
const id = requestId("request")
|
|
71
71
|
const signal = init?.signal
|
|
72
72
|
const cancel = () => void nativeModule.cancel(id)
|
|
@@ -92,13 +92,13 @@ export const localNetworkTransport = {
|
|
|
92
92
|
},
|
|
93
93
|
|
|
94
94
|
downloadFile(options: DownloadOptions): DownloadHandle {
|
|
95
|
-
if (!scopedConnectionActive || !
|
|
95
|
+
if (!scopedConnectionActive || !otaLocalNetwork.isAvailable()) return RNFS.downloadFile(options)
|
|
96
96
|
|
|
97
97
|
const jobId = nextJobId++
|
|
98
98
|
const id = requestId("download")
|
|
99
99
|
let began = false
|
|
100
100
|
nativeJobs.set(jobId, id)
|
|
101
|
-
const subscription =
|
|
101
|
+
const subscription = otaLocalNetwork.onDownloadProgress((event: OtaLocalNetworkDownloadProgress) => {
|
|
102
102
|
if (event.requestId !== id) return
|
|
103
103
|
if (!began) {
|
|
104
104
|
began = true
|
|
@@ -116,14 +116,15 @@ export const localNetworkTransport = {
|
|
|
116
116
|
})
|
|
117
117
|
})
|
|
118
118
|
|
|
119
|
-
const promise =
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
119
|
+
const promise = otaLocalNetwork
|
|
120
|
+
.download(
|
|
121
|
+
id,
|
|
122
|
+
options.fromUrl,
|
|
123
|
+
options.toFile,
|
|
124
|
+
options.headers || {},
|
|
125
|
+
options.connectionTimeout || 30_000,
|
|
126
|
+
options.readTimeout || 30_000,
|
|
127
|
+
)
|
|
127
128
|
.then((result) => {
|
|
128
129
|
if (!began) {
|
|
129
130
|
options.begin?.({
|
|
@@ -154,16 +155,16 @@ export const localNetworkTransport = {
|
|
|
154
155
|
|
|
155
156
|
stopDownload(jobId: number): void {
|
|
156
157
|
const nativeId = nativeJobs.get(jobId)
|
|
157
|
-
if (nativeId &&
|
|
158
|
+
if (nativeId && otaLocalNetwork.isAvailable()) {
|
|
158
159
|
cancelledNativeJobs.add(jobId)
|
|
159
|
-
void
|
|
160
|
+
void otaLocalNetwork.cancel(nativeId)
|
|
160
161
|
return
|
|
161
162
|
}
|
|
162
163
|
RNFS.stopDownload(jobId)
|
|
163
164
|
},
|
|
164
165
|
}
|
|
165
166
|
|
|
166
|
-
|
|
167
|
+
otaLocalNetwork.onNetworkLost(() => {
|
|
167
168
|
scopedConnectionActive = false
|
|
168
169
|
nativeJobs.clear()
|
|
169
170
|
})
|