@glassly/engine 0.1.0-dev.1 → 0.1.0-dev.26
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 +53 -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 +23 -16
- package/build/engine.d.ts.map +1 -1
- package/build/engine.js +9 -3
- package/build/engine.js.map +1 -1
- package/build/facades/dev.d.ts +1 -1
- package/build/facades/display.d.ts +1 -1
- package/build/facades/display.d.ts.map +1 -1
- package/build/facades/display.js +1 -1
- package/build/facades/display.js.map +1 -1
- package/build/facades/glasses.d.ts +5 -13
- package/build/facades/glasses.d.ts.map +1 -1
- package/build/facades/glasses.js +5 -3
- package/build/facades/glasses.js.map +1 -1
- package/build/facades/ota.d.ts +19 -21
- package/build/facades/ota.d.ts.map +1 -1
- package/build/facades/ota.js +23 -9
- 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/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 +7 -4
- package/build/index.d.ts.map +1 -1
- package/build/index.js +5 -3
- package/build/index.js.map +1 -1
- package/build/react/MentraLiveOtaFlow.d.ts +39 -0
- package/build/react/MentraLiveOtaFlow.d.ts.map +1 -0
- package/build/react/MentraLiveOtaFlow.js +200 -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 +80 -0
- package/build/react/useMentraLiveOta.d.ts.map +1 -0
- package/build/react/useMentraLiveOta.js +550 -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 +61 -0
- package/build/services/CustomDashboardService.d.ts.map +1 -0
- package/build/services/CustomDashboardService.js +552 -0
- package/build/services/CustomDashboardService.js.map +1 -0
- package/build/services/DeviceEventRouter.d.ts.map +1 -1
- package/build/services/DeviceEventRouter.js +44 -3
- package/build/services/DeviceEventRouter.js.map +1 -1
- package/build/services/GestureDotsService.d.ts +35 -0
- package/build/services/GestureDotsService.d.ts.map +1 -0
- package/build/services/GestureDotsService.js +149 -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 +43 -1
- package/build/services/LocalDisplayManager.d.ts.map +1 -1
- package/build/services/LocalDisplayManager.js +128 -2
- 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 +24 -3
- package/build/services/OtaInstallCoordinator.d.ts.map +1 -1
- package/build/services/OtaInstallCoordinator.js +17 -12
- 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/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/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 +12 -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/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 +1 -1
- package/build/services/otaInstallPolicy.d.ts.map +1 -1
- 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/glasses.d.ts +10 -4
- package/build/stores/glasses.d.ts.map +1 -1
- package/build/stores/glasses.js.map +1 -1
- package/build/stores/settings.d.ts.map +1 -1
- package/build/stores/settings.js +87 -3
- package/build/stores/settings.js.map +1 -1
- package/build/types/applet.d.ts +2 -0
- 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/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.map +1 -1
- package/build/types/index.d.ts +2 -6
- package/build/types/index.d.ts.map +1 -1
- package/build/types/index.js +2 -6
- package/build/types/index.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 +65 -33
- 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 +9 -3
- package/src/facades/display.ts +1 -1
- package/src/facades/glasses.ts +6 -4
- package/src/facades/ota.ts +33 -10
- package/src/facades/pairing.ts +103 -23
- 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 +29 -4
- package/src/react/MentraLiveOtaFlow.tsx +569 -0
- package/src/react/index.ts +21 -0
- package/src/react/useEngineSnapshot.ts +16 -0
- package/src/react/useMentraLiveOta.ts +707 -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 +575 -0
- package/src/services/DeviceEventRouter.ts +47 -5
- package/src/services/GestureDotsService.ts +147 -0
- package/src/services/GlassesStatusProjection.ts +20 -11
- package/src/services/HotspotOtaTransport.ts +8 -7
- package/src/services/LocalDisplayManager.ts +130 -2
- 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 +19 -13
- package/src/services/OtaService.ts +1 -2
- package/src/services/OtaUpdateCheckService.ts +33 -19
- package/src/services/asg/localNetworkTransport.ts +20 -19
- package/src/services/gestureClaims.ts +28 -0
- package/src/services/gestureDotGlyphs.ts +14 -0
- package/src/services/glassesClockSync.ts +5 -1
- package/src/services/otaDisplayState.ts +1 -1
- package/src/services/otaInstallPolicy.ts +1 -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/glasses.ts +13 -6
- package/src/stores/settings.ts +87 -3
- package/src/types/applet.ts +2 -0
- package/src/types/enums.ts +1 -9
- package/src/types/hardware.ts +1 -1
- package/src/types/index.ts +2 -6
- 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
|
@@ -879,6 +879,22 @@ class AppRegistry {
|
|
|
879
879
|
this.notify()
|
|
880
880
|
}
|
|
881
881
|
|
|
882
|
+
/**
|
|
883
|
+
* Update whether a registered offline app is projected into launcher UIs.
|
|
884
|
+
*
|
|
885
|
+
* Offline apps are registered once at host startup, so settings-backed
|
|
886
|
+
* visibility changes need to update the projection without reinstalling or
|
|
887
|
+
* duplicating the app. No-op when the app is missing or already matches.
|
|
888
|
+
*/
|
|
889
|
+
public setOfflineAppHidden(packageName: string, hidden: boolean): void {
|
|
890
|
+
const app = this.offlineApps.find((candidate) => candidate.packageName === packageName)
|
|
891
|
+
if (!app || app.hidden === hidden) return
|
|
892
|
+
|
|
893
|
+
app.hidden = hidden
|
|
894
|
+
this.refreshNeeded = true
|
|
895
|
+
this.notify()
|
|
896
|
+
}
|
|
897
|
+
|
|
882
898
|
/**
|
|
883
899
|
* Registered as needing the on-device STT model before it can start (the
|
|
884
900
|
* host declares this per offline app; the apps store gates start() on it).
|
|
@@ -40,8 +40,15 @@ const LOG_TAG = "LocalMiniappRuntime"
|
|
|
40
40
|
/** Per-app blob quota: 1 GB. */
|
|
41
41
|
export const BLOB_QUOTA_BYTES = 1024 * 1024 * 1024
|
|
42
42
|
const ROOT_DIR_NAME = "glassly_blobs"
|
|
43
|
-
/** Cache dir root holding short-lived, properly-named copies handed to the OS share sheet (one
|
|
43
|
+
/** Cache dir root holding short-lived, properly-named copies handed to the OS share sheet (one subdir per stored blob). */
|
|
44
44
|
const SHARE_DIR_NAME = "blob_share"
|
|
45
|
+
/**
|
|
46
|
+
* Keep prepared share handoffs alive long enough for recipient apps to read
|
|
47
|
+
* them asynchronously (for example, an email composer attaching the file).
|
|
48
|
+
* Stale copies are collected before the next share; the OS may evict cache
|
|
49
|
+
* files sooner under storage pressure.
|
|
50
|
+
*/
|
|
51
|
+
const SHARE_CACHE_MAX_AGE_MS = 24 * 60 * 60 * 1000
|
|
45
52
|
const META_KEY_ROOT = "blobmeta_"
|
|
46
53
|
/** Cap md5 computation so we don't block the JS thread hashing a huge file. */
|
|
47
54
|
const MD5_MAX_BYTES = 50 * 1024 * 1024
|
|
@@ -662,6 +669,32 @@ export class BlobStore {
|
|
|
662
669
|
|
|
663
670
|
// ---- share ---------------------------------------------------------------
|
|
664
671
|
|
|
672
|
+
/** Best-effort GC for prepared share handoffs retained in the OS cache. */
|
|
673
|
+
private cleanupShareCache(now = Date.now()): void {
|
|
674
|
+
try {
|
|
675
|
+
const root = new Directory(Paths.cache, SHARE_DIR_NAME)
|
|
676
|
+
if (!root.exists) return
|
|
677
|
+
const cutoff = now - SHARE_CACHE_MAX_AGE_MS
|
|
678
|
+
for (const entry of root.list()) {
|
|
679
|
+
if (!(entry instanceof Directory)) continue
|
|
680
|
+
try {
|
|
681
|
+
const info = entry.info()
|
|
682
|
+
// modificationTime is available on both platforms; creationTime can
|
|
683
|
+
// be absent on Android versions before API 26. The directory id
|
|
684
|
+
// begins with Date.now().toString(36), which is a final fallback.
|
|
685
|
+
const encoded = entry.name.split("-", 1)[0]
|
|
686
|
+
const encodedTime = /^[0-9a-z]+$/.test(encoded) ? Number.parseInt(encoded, 36) : Number.NaN
|
|
687
|
+
const timestamp = info.modificationTime ?? info.creationTime ?? encodedTime
|
|
688
|
+
if (Number.isFinite(timestamp) && timestamp < cutoff) entry.delete()
|
|
689
|
+
} catch {
|
|
690
|
+
// Keep scanning if one cache entry disappears or is unreadable.
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
} catch {
|
|
694
|
+
// Cache cleanup must never prevent a share.
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
|
|
665
698
|
async handleShare(packageName: string, payload: Record<string, unknown>, requestId?: string): Promise<void> {
|
|
666
699
|
const key = payload.key as string
|
|
667
700
|
const meta = this.readMeta(packageName, key)
|
|
@@ -679,23 +712,33 @@ export class BlobStore {
|
|
|
679
712
|
// applies to base64/data payloads, not file:// URLs), so sharing `file.uri`
|
|
680
713
|
// directly would hand the recipient a machine-named file. Copy to a temp
|
|
681
714
|
// file that carries the real display name + extension, share that, then
|
|
682
|
-
// clean it up.
|
|
715
|
+
// clean it up after the recipient has had time to consume it.
|
|
683
716
|
const shareName = shareFileName(meta)
|
|
684
|
-
//
|
|
685
|
-
//
|
|
686
|
-
//
|
|
687
|
-
//
|
|
717
|
+
// Key the cache directory by the immutable stored filename. Repeated or
|
|
718
|
+
// concurrent shares of the same blob can safely reference the same bytes,
|
|
719
|
+
// avoiding an extra full-sized cache copy for every share. Different blobs
|
|
720
|
+
// still get isolated directories even when they use the same display name.
|
|
721
|
+
// The file keeps `shareName` as its basename so the OS share sheet shows the
|
|
722
|
+
// right name + extension.
|
|
688
723
|
let tempDir: Directory | null = null
|
|
724
|
+
let cacheReady = false
|
|
689
725
|
try {
|
|
690
|
-
|
|
691
|
-
tempDir.
|
|
726
|
+
this.cleanupShareCache()
|
|
727
|
+
tempDir = new Directory(Paths.cache, SHARE_DIR_NAME, sanitizeSegment(meta.fileName))
|
|
728
|
+
if (!tempDir.exists) tempDir.create({intermediates: true})
|
|
692
729
|
const temp = new File(tempDir, shareName)
|
|
693
|
-
|
|
730
|
+
if (!temp.exists || temp.size !== meta.bytes) {
|
|
731
|
+
if (temp.exists) temp.delete()
|
|
732
|
+
file.copy(temp)
|
|
733
|
+
}
|
|
734
|
+
cacheReady = true
|
|
694
735
|
await Share.open({
|
|
695
736
|
url: temp.uri,
|
|
696
737
|
type: meta.mimeType || OCTET,
|
|
697
738
|
filename: shareName,
|
|
698
739
|
})
|
|
740
|
+
// Share.open resolves once the OS accepts the handoff. Recipient apps may
|
|
741
|
+
// still read the URI asynchronously, so retain this cache copy for GC.
|
|
699
742
|
this.hooks.sendResult(packageName, requestId, true, {success: true})
|
|
700
743
|
} catch (error: any) {
|
|
701
744
|
if (error?.message?.includes("User did not share")) {
|
|
@@ -705,7 +748,11 @@ export class BlobStore {
|
|
|
705
748
|
this.hooks.sendResult(packageName, requestId, true, {success: false})
|
|
706
749
|
}
|
|
707
750
|
} finally {
|
|
708
|
-
if
|
|
751
|
+
// Once a reusable cache entry is ready, retain it even if this particular
|
|
752
|
+
// sheet is cancelled or fails: another concurrent/recent share may still
|
|
753
|
+
// be reading the same file. Only remove an entry whose preparation did
|
|
754
|
+
// not finish; ready entries are collected after the retention window.
|
|
755
|
+
if (tempDir && !cacheReady) {
|
|
709
756
|
try {
|
|
710
757
|
if (tempDir.exists) tempDir.delete()
|
|
711
758
|
} catch {
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type {ClientApp} from "../types/applet"
|
|
2
|
+
import {DeviceTypes} from "../types/enums"
|
|
3
|
+
|
|
4
|
+
/** Canonical widget order; also the default layout order. */
|
|
5
|
+
export const DASHBOARD_WIDGET_IDS = ["clock", "calendar", "apps", "notifications"] as const
|
|
6
|
+
export type DashboardWidgetId = (typeof DASHBOARD_WIDGET_IDS)[number]
|
|
7
|
+
export type DashboardWidgetSetting = {id: DashboardWidgetId; enabled: boolean}
|
|
8
|
+
|
|
9
|
+
export type DashboardAction = {kind: "apps-menu"} | {kind: "notifications"}
|
|
10
|
+
export type AppMenuAction = {kind: "app"; packageName: string}
|
|
11
|
+
|
|
12
|
+
export type MenuAppSetting = {packageName: string; name: string; running?: boolean}
|
|
13
|
+
export type CustomDashboardClockStyle = "compact" | "large"
|
|
14
|
+
|
|
15
|
+
/** G2.swift appends this marker after the firmware advertises EVENCFW capabilities. */
|
|
16
|
+
export function isGlasslyCfwFirmwareVersion(firmwareVersion: string | null | undefined): boolean {
|
|
17
|
+
return firmwareVersion?.toLowerCase().includes("glassly cfw") === true
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Stock G2 and NIMO are intentionally excluded. */
|
|
21
|
+
export function isCustomDashboardEligible(info: {model?: string | null; firmwareVersion?: string | null}): boolean {
|
|
22
|
+
return info.model === DeviceTypes.G2 && isGlasslyCfwFirmwareVersion(info.firmwareVersion)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function formatDashboardClock(
|
|
26
|
+
date: Date,
|
|
27
|
+
options: {twelveHour: boolean; showDate: boolean},
|
|
28
|
+
): {time: string; date?: string} {
|
|
29
|
+
const time = new Intl.DateTimeFormat(undefined, {
|
|
30
|
+
hour: "numeric",
|
|
31
|
+
minute: "2-digit",
|
|
32
|
+
hour12: options.twelveHour,
|
|
33
|
+
}).format(date)
|
|
34
|
+
if (!options.showDate) return {time}
|
|
35
|
+
return {
|
|
36
|
+
time,
|
|
37
|
+
date: new Intl.DateTimeFormat(undefined, {weekday: "short", month: "short", day: "numeric"}).format(date),
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Saved widget order, sanitized: unknown/duplicate entries dropped, widgets
|
|
43
|
+
* missing from the saved value appended enabled in canonical order.
|
|
44
|
+
*/
|
|
45
|
+
export function normalizeDashboardWidgets(raw: unknown): DashboardWidgetSetting[] {
|
|
46
|
+
const widgets: DashboardWidgetSetting[] = []
|
|
47
|
+
if (Array.isArray(raw)) {
|
|
48
|
+
for (const entry of raw) {
|
|
49
|
+
if (!entry || typeof entry !== "object") continue
|
|
50
|
+
const id = (entry as {id?: unknown}).id as DashboardWidgetId
|
|
51
|
+
if (!DASHBOARD_WIDGET_IDS.includes(id)) continue
|
|
52
|
+
if (widgets.some((widget) => widget.id === id)) continue
|
|
53
|
+
widgets.push({id, enabled: (entry as {enabled?: unknown}).enabled !== false})
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
for (const id of DASHBOARD_WIDGET_IDS) {
|
|
57
|
+
if (!widgets.some((widget) => widget.id === id)) widgets.push({id, enabled: true})
|
|
58
|
+
}
|
|
59
|
+
return widgets
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function orderedDashboardApps(menu: MenuAppSetting[] | null | undefined, apps: ClientApp[]): ClientApp[] {
|
|
63
|
+
if (!Array.isArray(menu)) return []
|
|
64
|
+
return menu
|
|
65
|
+
.map((item) => apps.find((app) => app.packageName === item.packageName))
|
|
66
|
+
.filter((app): app is ClientApp => Boolean(app && !app.hidden && app.compatibility?.isCompatible !== false))
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Focusable main-view actions, following the user's widget order. */
|
|
70
|
+
export function dashboardActions(widgets: DashboardWidgetSetting[]): DashboardAction[] {
|
|
71
|
+
const actions: DashboardAction[] = []
|
|
72
|
+
for (const widget of widgets) {
|
|
73
|
+
if (!widget.enabled) continue
|
|
74
|
+
if (widget.id === "apps") actions.push({kind: "apps-menu"})
|
|
75
|
+
else if (widget.id === "notifications") actions.push({kind: "notifications"})
|
|
76
|
+
}
|
|
77
|
+
return actions
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** App-menu view rows: the Glasses Menu apps in order (double tap leaves the view). */
|
|
81
|
+
export function appMenuActions(apps: ClientApp[]): AppMenuAction[] {
|
|
82
|
+
return apps.map((app): AppMenuAction => ({kind: "app", packageName: app.packageName}))
|
|
83
|
+
}
|