@mentra/engine 0.1.0-dev.0 → 3.1.0-dev.10
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 +55 -18
- 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/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 +24 -16
- package/build/engine.d.ts.map +1 -1
- package/build/engine.js +9 -4
- package/build/engine.js.map +1 -1
- package/build/facades/dev.d.ts +3 -2
- package/build/facades/dev.d.ts.map +1 -1
- package/build/facades/dev.js +18 -12
- package/build/facades/dev.js.map +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/gallery.js +1 -1
- package/build/facades/gallery.js.map +1 -1
- package/build/facades/glasses.d.ts +4 -13
- package/build/facades/glasses.d.ts.map +1 -1
- package/build/facades/glasses.js +21 -6
- package/build/facades/glasses.js.map +1 -1
- package/build/facades/glassesSettings.d.ts.map +1 -1
- package/build/facades/glassesSettings.js +1 -0
- package/build/facades/glassesSettings.js.map +1 -1
- package/build/facades/miniapps.d.ts +4 -0
- package/build/facades/miniapps.d.ts.map +1 -1
- package/build/facades/miniapps.js +5 -0
- package/build/facades/miniapps.js.map +1 -1
- package/build/facades/ota.d.ts +14 -16
- package/build/facades/ota.d.ts.map +1 -1
- package/build/facades/ota.js +22 -7
- package/build/facades/ota.js.map +1 -1
- package/build/facades/pairing.d.ts +8 -5
- package/build/facades/pairing.d.ts.map +1 -1
- package/build/facades/pairing.js +12 -10
- package/build/facades/pairing.js.map +1 -1
- package/build/facades/permissions.d.ts +10 -0
- package/build/facades/permissions.d.ts.map +1 -1
- package/build/facades/permissions.js +10 -0
- package/build/facades/permissions.js.map +1 -1
- package/build/facades/phoneNotifications.d.ts +0 -4
- package/build/facades/phoneNotifications.d.ts.map +1 -1
- package/build/facades/phoneNotifications.js +4 -14
- package/build/facades/phoneNotifications.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 +10 -8
- package/build/index.d.ts.map +1 -1
- package/build/index.js +6 -5
- package/build/index.js.map +1 -1
- package/build/react/MentraLiveOtaFlow.d.ts +38 -0
- package/build/react/MentraLiveOtaFlow.d.ts.map +1 -0
- package/build/react/MentraLiveOtaFlow.js +528 -0
- package/build/react/MentraLiveOtaFlow.js.map +1 -0
- package/build/react/index.d.ts +2 -0
- package/build/react/index.d.ts.map +1 -0
- package/build/react/index.js +2 -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/runtime/bootstrap.d.ts +24 -1
- package/build/runtime/bootstrap.d.ts.map +1 -1
- package/build/runtime/bootstrap.js +17 -0
- package/build/runtime/bootstrap.js.map +1 -1
- package/build/runtime/scanQrSeam.d.ts +7 -0
- package/build/runtime/scanQrSeam.d.ts.map +1 -0
- package/build/runtime/scanQrSeam.js +16 -0
- package/build/runtime/scanQrSeam.js.map +1 -0
- package/build/services/AppRegistry.d.ts +41 -57
- package/build/services/AppRegistry.d.ts.map +1 -1
- package/build/services/AppRegistry.js +208 -144
- package/build/services/AppRegistry.js.map +1 -1
- package/build/services/AudioCloudUplink.d.ts +12 -0
- package/build/services/AudioCloudUplink.d.ts.map +1 -1
- package/build/services/AudioCloudUplink.js +22 -1
- package/build/services/AudioCloudUplink.js.map +1 -1
- package/build/services/AudioPlaybackService.d.ts +41 -2
- package/build/services/AudioPlaybackService.d.ts.map +1 -1
- package/build/services/AudioPlaybackService.js +250 -33
- package/build/services/AudioPlaybackService.js.map +1 -1
- package/build/services/BlobStore.d.ts +2 -1
- package/build/services/BlobStore.d.ts.map +1 -1
- package/build/services/BlobStore.js +17 -9
- package/build/services/BlobStore.js.map +1 -1
- package/build/services/CloudAudioSubscriptionSync.d.ts.map +1 -1
- package/build/services/CloudAudioSubscriptionSync.js +13 -1
- package/build/services/CloudAudioSubscriptionSync.js.map +1 -1
- package/build/services/CloudClientService.d.ts +15 -0
- package/build/services/CloudClientService.d.ts.map +1 -1
- package/build/services/CloudClientService.js +93 -1
- package/build/services/CloudClientService.js.map +1 -1
- package/build/services/DeviceEventRouter.js +5 -5
- package/build/services/DeviceEventRouter.js.map +1 -1
- package/build/services/ForegroundLocationPermission.d.ts +13 -0
- package/build/services/ForegroundLocationPermission.d.ts.map +1 -0
- package/build/services/ForegroundLocationPermission.js +12 -0
- package/build/services/ForegroundLocationPermission.js.map +1 -0
- package/build/services/GlassesSettingsSync.d.ts.map +1 -1
- package/build/services/GlassesSettingsSync.js +9 -3
- package/build/services/GlassesSettingsSync.js.map +1 -1
- package/build/services/GlassesStatusProjection.d.ts +2 -1
- package/build/services/GlassesStatusProjection.d.ts.map +1 -1
- package/build/services/GlassesStatusProjection.js +15 -8
- package/build/services/GlassesStatusProjection.js.map +1 -1
- package/build/services/HotspotOtaTransport.d.ts +29 -0
- package/build/services/HotspotOtaTransport.d.ts.map +1 -0
- package/build/services/HotspotOtaTransport.js +146 -0
- package/build/services/HotspotOtaTransport.js.map +1 -0
- package/build/services/HotspotShutdown.d.ts +17 -0
- package/build/services/HotspotShutdown.d.ts.map +1 -0
- package/build/services/HotspotShutdown.js +29 -0
- package/build/services/HotspotShutdown.js.map +1 -0
- package/build/services/LocalDisplayManager.d.ts +15 -0
- package/build/services/LocalDisplayManager.d.ts.map +1 -1
- package/build/services/LocalDisplayManager.js +106 -20
- package/build/services/LocalDisplayManager.js.map +1 -1
- package/build/services/LocalMiniappRuntime.d.ts +74 -2
- package/build/services/LocalMiniappRuntime.d.ts.map +1 -1
- package/build/services/LocalMiniappRuntime.js +785 -291
- package/build/services/LocalMiniappRuntime.js.map +1 -1
- package/build/services/LocalMiniappStorage.d.ts +32 -0
- package/build/services/LocalMiniappStorage.d.ts.map +1 -0
- package/build/services/LocalMiniappStorage.js +66 -0
- package/build/services/LocalMiniappStorage.js.map +1 -0
- package/build/services/LocalMiniappUserIdentity.d.ts +20 -0
- package/build/services/LocalMiniappUserIdentity.d.ts.map +1 -0
- package/build/services/LocalMiniappUserIdentity.js +43 -0
- package/build/services/LocalMiniappUserIdentity.js.map +1 -0
- package/build/services/LocalSttFallbackCoordinator.d.ts +9 -6
- package/build/services/LocalSttFallbackCoordinator.d.ts.map +1 -1
- package/build/services/LocalSttFallbackCoordinator.js +18 -12
- package/build/services/LocalSttFallbackCoordinator.js.map +1 -1
- package/build/services/MicStateCoordinator.d.ts +42 -1
- package/build/services/MicStateCoordinator.d.ts.map +1 -1
- package/build/services/MicStateCoordinator.js +141 -3
- package/build/services/MicStateCoordinator.js.map +1 -1
- package/build/services/MiniappLauncher.d.ts.map +1 -1
- package/build/services/MiniappLauncher.js +23 -3
- package/build/services/MiniappLauncher.js.map +1 -1
- package/build/services/MiniappLiveness.d.ts +12 -0
- package/build/services/MiniappLiveness.d.ts.map +1 -0
- package/build/services/MiniappLiveness.js +16 -0
- package/build/services/MiniappLiveness.js.map +1 -0
- package/build/services/NativeHttpResponse.d.ts +3 -0
- package/build/services/NativeHttpResponse.d.ts.map +1 -0
- package/build/services/NativeHttpResponse.js +6 -0
- package/build/services/NativeHttpResponse.js.map +1 -0
- package/build/services/OtaArtifactDownloader.d.ts +65 -0
- package/build/services/OtaArtifactDownloader.d.ts.map +1 -0
- package/build/services/OtaArtifactDownloader.js +226 -0
- package/build/services/OtaArtifactDownloader.js.map +1 -0
- package/build/services/OtaAutoChain.d.ts +34 -0
- package/build/services/OtaAutoChain.d.ts.map +1 -0
- package/build/services/OtaAutoChain.js +83 -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 +95 -15
- package/build/services/OtaInstallCoordinator.d.ts.map +1 -1
- package/build/services/OtaInstallCoordinator.js +577 -130
- package/build/services/OtaInstallCoordinator.js.map +1 -1
- package/build/services/OtaService.d.ts.map +1 -1
- package/build/services/OtaService.js +8 -1
- package/build/services/OtaService.js.map +1 -1
- package/build/services/OtaUpdateCheckService.d.ts +39 -4
- package/build/services/OtaUpdateCheckService.d.ts.map +1 -1
- package/build/services/OtaUpdateCheckService.js +138 -22
- package/build/services/OtaUpdateCheckService.js.map +1 -1
- package/build/services/PhoneCalendarHelpers.d.ts +32 -0
- package/build/services/PhoneCalendarHelpers.d.ts.map +1 -0
- package/build/services/PhoneCalendarHelpers.js +157 -0
- package/build/services/PhoneCalendarHelpers.js.map +1 -0
- package/build/services/PhoneCalendarService.d.ts +5 -0
- package/build/services/PhoneCalendarService.d.ts.map +1 -0
- package/build/services/PhoneCalendarService.js +45 -0
- package/build/services/PhoneCalendarService.js.map +1 -0
- package/build/services/PhoneCameraFovCoordinator.d.ts +30 -0
- package/build/services/PhoneCameraFovCoordinator.d.ts.map +1 -0
- package/build/services/PhoneCameraFovCoordinator.js +194 -0
- package/build/services/PhoneCameraFovCoordinator.js.map +1 -0
- package/build/services/PhoneNotificationsSync.d.ts.map +1 -1
- package/build/services/PhoneNotificationsSync.js +12 -10
- package/build/services/PhoneNotificationsSync.js.map +1 -1
- package/build/services/PhonePhotoCoordinator.d.ts +26 -2
- package/build/services/PhonePhotoCoordinator.d.ts.map +1 -1
- package/build/services/PhonePhotoCoordinator.js +79 -9
- package/build/services/PhonePhotoCoordinator.js.map +1 -1
- package/build/services/PhoneStreamCoordinator.d.ts +8 -1
- package/build/services/PhoneStreamCoordinator.d.ts.map +1 -1
- package/build/services/PhoneStreamCoordinator.js +125 -13
- package/build/services/PhoneStreamCoordinator.js.map +1 -1
- package/build/services/PhoneVideoCoordinator.d.ts +2 -0
- package/build/services/PhoneVideoCoordinator.d.ts.map +1 -1
- package/build/services/PhoneVideoCoordinator.js +10 -0
- package/build/services/PhoneVideoCoordinator.js.map +1 -1
- package/build/services/SceneRenderer.d.ts.map +1 -1
- package/build/services/SceneRenderer.js +15 -0
- package/build/services/SceneRenderer.js.map +1 -1
- package/build/services/SentenceTtsPipeline.d.ts +31 -0
- package/build/services/SentenceTtsPipeline.d.ts.map +1 -0
- package/build/services/SentenceTtsPipeline.js +67 -0
- package/build/services/SentenceTtsPipeline.js.map +1 -0
- package/build/services/SupportProfileSync.d.ts +22 -0
- package/build/services/SupportProfileSync.d.ts.map +1 -0
- package/build/services/SupportProfileSync.js +213 -0
- package/build/services/SupportProfileSync.js.map +1 -0
- package/build/services/TranscriptionRouting.d.ts +12 -0
- package/build/services/TranscriptionRouting.d.ts.map +1 -0
- package/build/services/TranscriptionRouting.js +24 -0
- package/build/services/TranscriptionRouting.js.map +1 -0
- package/build/services/TtsTextSanitizer.d.ts +5 -0
- package/build/services/TtsTextSanitizer.d.ts.map +1 -0
- package/build/services/TtsTextSanitizer.js +61 -0
- package/build/services/TtsTextSanitizer.js.map +1 -0
- package/build/services/WifiAdbRequest.d.ts +17 -0
- package/build/services/WifiAdbRequest.d.ts.map +1 -0
- package/build/services/WifiAdbRequest.js +35 -0
- package/build/services/WifiAdbRequest.js.map +1 -0
- package/build/services/asg/asgCameraApi.d.ts +43 -0
- package/build/services/asg/asgCameraApi.d.ts.map +1 -1
- package/build/services/asg/asgCameraApi.js +509 -26
- package/build/services/asg/asgCameraApi.js.map +1 -1
- package/build/services/asg/cameraRollExportCoordinator.d.ts +60 -0
- package/build/services/asg/cameraRollExportCoordinator.d.ts.map +1 -0
- package/build/services/asg/cameraRollExportCoordinator.js +618 -0
- package/build/services/asg/cameraRollExportCoordinator.js.map +1 -0
- package/build/services/asg/cameraRollExportLedger.d.ts +44 -0
- package/build/services/asg/cameraRollExportLedger.d.ts.map +1 -0
- package/build/services/asg/cameraRollExportLedger.js +78 -0
- package/build/services/asg/cameraRollExportLedger.js.map +1 -0
- package/build/services/asg/galleryNotices.d.ts +1 -1
- package/build/services/asg/galleryNotices.d.ts.map +1 -1
- package/build/services/asg/galleryNotices.js.map +1 -1
- package/build/services/asg/galleryPathMigration.d.ts +3 -0
- package/build/services/asg/galleryPathMigration.d.ts.map +1 -0
- package/build/services/asg/galleryPathMigration.js +26 -0
- package/build/services/asg/galleryPathMigration.js.map +1 -0
- package/build/services/asg/gallerySettingsService.d.ts +1 -0
- package/build/services/asg/gallerySettingsService.d.ts.map +1 -1
- package/build/services/asg/gallerySettingsService.js +2 -0
- package/build/services/asg/gallerySettingsService.js.map +1 -1
- package/build/services/asg/gallerySyncService.d.ts.map +1 -1
- package/build/services/asg/gallerySyncService.js +162 -56
- package/build/services/asg/gallerySyncService.js.map +1 -1
- package/build/services/asg/galleryTransferLedger.d.ts +58 -0
- package/build/services/asg/galleryTransferLedger.d.ts.map +1 -0
- package/build/services/asg/galleryTransferLedger.js +225 -0
- package/build/services/asg/galleryTransferLedger.js.map +1 -0
- package/build/services/asg/localNetworkTransport.d.ts +15 -0
- package/build/services/asg/localNetworkTransport.d.ts.map +1 -0
- package/build/services/asg/localNetworkTransport.js +144 -0
- package/build/services/asg/localNetworkTransport.js.map +1 -0
- package/build/services/asg/localStorageService.d.ts +18 -0
- package/build/services/asg/localStorageService.d.ts.map +1 -1
- package/build/services/asg/localStorageService.js +168 -19
- package/build/services/asg/localStorageService.js.map +1 -1
- package/build/services/asg/mediaProcessingQueue.d.ts +10 -0
- package/build/services/asg/mediaProcessingQueue.d.ts.map +1 -1
- package/build/services/asg/mediaProcessingQueue.js +193 -62
- package/build/services/asg/mediaProcessingQueue.js.map +1 -1
- package/build/services/audioPlaybackAssets.d.ts +8 -0
- package/build/services/audioPlaybackAssets.d.ts.map +1 -0
- package/build/services/audioPlaybackAssets.js +7 -0
- package/build/services/audioPlaybackAssets.js.map +1 -0
- package/build/services/gallerySyncClock.js +1 -1
- package/build/services/gallerySyncClock.js.map +1 -1
- package/build/services/glassesClockSync.d.ts.map +1 -1
- package/build/services/glassesClockSync.js +6 -2
- package/build/services/glassesClockSync.js.map +1 -1
- package/build/services/manifestActions.d.ts +7 -0
- package/build/services/manifestActions.d.ts.map +1 -0
- package/build/services/manifestActions.js +25 -0
- package/build/services/manifestActions.js.map +1 -0
- package/build/services/otaDisplayState.d.ts +23 -12
- package/build/services/otaDisplayState.d.ts.map +1 -1
- package/build/services/otaDisplayState.js +36 -14
- package/build/services/otaDisplayState.js.map +1 -1
- package/build/services/otaInstallPolicy.d.ts +16 -4
- package/build/services/otaInstallPolicy.d.ts.map +1 -1
- package/build/services/otaInstallPolicy.js +24 -3
- 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 +19 -0
- package/build/services/otaManifestPolicy.js.map +1 -0
- package/build/services/otaManifestUrl.d.ts +3 -0
- package/build/services/otaManifestUrl.d.ts.map +1 -0
- package/build/services/otaManifestUrl.js +38 -0
- package/build/services/otaManifestUrl.js.map +1 -0
- package/build/services/slimStreamStatus.d.ts +11 -0
- package/build/services/slimStreamStatus.d.ts.map +1 -1
- package/build/services/slimStreamStatus.js +14 -4
- package/build/services/slimStreamStatus.js.map +1 -1
- package/build/stores/apps.d.ts.map +1 -1
- package/build/stores/apps.js +11 -0
- package/build/stores/apps.js.map +1 -1
- package/build/stores/bluetoothSettingKeys.d.ts.map +1 -1
- package/build/stores/bluetoothSettingKeys.js +2 -0
- package/build/stores/bluetoothSettingKeys.js.map +1 -1
- package/build/stores/gallerySync.d.ts +2 -0
- package/build/stores/gallerySync.d.ts.map +1 -1
- package/build/stores/gallerySync.js +2 -0
- package/build/stores/gallerySync.js.map +1 -1
- package/build/stores/glasses.d.ts.map +1 -1
- package/build/stores/glasses.js +3 -0
- package/build/stores/glasses.js.map +1 -1
- package/build/stores/settings.d.ts +17 -0
- package/build/stores/settings.d.ts.map +1 -1
- package/build/stores/settings.js +157 -17
- 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.map +1 -1
- package/build/types/asg.d.ts +7 -0
- package/build/types/asg.d.ts.map +1 -1
- package/build/types/asg.js.map +1 -1
- package/build/types/enums.d.ts +2 -25
- package/build/types/enums.d.ts.map +1 -1
- package/build/types/enums.js +2 -27
- package/build/types/enums.js.map +1 -1
- package/build/types/hardware.d.ts +4 -13
- package/build/types/hardware.d.ts.map +1 -1
- package/build/types/hardware.js +15 -3
- 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/configuredDevHost.d.ts +6 -0
- package/build/utils/configuredDevHost.d.ts.map +1 -0
- package/build/utils/configuredDevHost.js +19 -0
- package/build/utils/configuredDevHost.js.map +1 -0
- package/build/utils/devMiniappLaunch.d.ts +19 -8
- package/build/utils/devMiniappLaunch.d.ts.map +1 -1
- package/build/utils/devMiniappLaunch.js +144 -21
- package/build/utils/devMiniappLaunch.js.map +1 -1
- package/build/utils/diagnosticContext.d.ts.map +1 -1
- package/build/utils/diagnosticContext.js +15 -17
- package/build/utils/diagnosticContext.js.map +1 -1
- package/build/utils/hardware/hardware.d.ts.map +1 -1
- package/build/utils/hardware/hardware.js +7 -3
- package/build/utils/hardware/hardware.js.map +1 -1
- package/build/utils/miniappDiagnostics.d.ts +64 -0
- package/build/utils/miniappDiagnostics.d.ts.map +1 -0
- package/build/utils/miniappDiagnostics.js +173 -0
- package/build/utils/miniappDiagnostics.js.map +1 -0
- package/build/utils/permissions/MediaLibraryPermissions.d.ts +11 -0
- package/build/utils/permissions/MediaLibraryPermissions.d.ts.map +1 -1
- package/build/utils/permissions/MediaLibraryPermissions.js +27 -4
- package/build/utils/permissions/MediaLibraryPermissions.js.map +1 -1
- package/build/utils/timers/timers.d.ts.map +1 -1
- package/build/utils/timers/timers.js +55 -18
- package/build/utils/timers/timers.js.map +1 -1
- package/package.json +59 -31
- package/src/assets/silence.wav +0 -0
- package/src/bluetooth-sdk/debug.ts +1 -0
- package/src/bluetooth-sdk/index.ts +2 -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 -4
- package/src/facades/dev.ts +17 -12
- package/src/facades/display.ts +1 -1
- package/src/facades/gallery.ts +1 -1
- package/src/facades/glasses.ts +20 -7
- package/src/facades/glassesSettings.ts +5 -0
- package/src/facades/miniapps.ts +7 -0
- package/src/facades/ota.ts +23 -8
- package/src/facades/pairing.ts +15 -11
- package/src/facades/permissions.ts +14 -0
- package/src/facades/phoneNotifications.ts +4 -13
- package/src/facades/speech.ts +21 -7
- package/src/generated/releaseMetadata.ts +20 -0
- package/src/index.ts +39 -13
- package/src/react/MentraLiveOtaFlow.tsx +856 -0
- package/src/react/index.ts +7 -0
- package/src/react/useEngineSnapshot.ts +16 -0
- package/src/runtime/bootstrap.ts +33 -1
- package/src/runtime/scanQrSeam.ts +20 -0
- package/src/services/AppRegistry.ts +240 -149
- package/src/services/AudioCloudUplink.ts +23 -1
- package/src/services/AudioPlaybackService.ts +287 -38
- package/src/services/BlobStore.ts +22 -9
- package/src/services/CloudAudioSubscriptionSync.ts +13 -1
- package/src/services/CloudClientService.ts +96 -1
- package/src/services/DeviceEventRouter.ts +5 -5
- package/src/services/ForegroundLocationPermission.ts +24 -0
- package/src/services/GlassesSettingsSync.ts +9 -3
- package/src/services/GlassesStatusProjection.ts +16 -8
- package/src/services/HotspotOtaTransport.ts +184 -0
- package/src/services/HotspotShutdown.ts +41 -0
- package/src/services/LocalDisplayManager.ts +112 -21
- package/src/services/LocalMiniappRuntime.ts +967 -346
- package/src/services/LocalMiniappStorage.ts +82 -0
- package/src/services/LocalMiniappUserIdentity.ts +46 -0
- package/src/services/LocalSttFallbackCoordinator.ts +19 -12
- package/src/services/MicStateCoordinator.ts +169 -4
- package/src/services/MiniappLauncher.ts +30 -3
- package/src/services/MiniappLiveness.ts +24 -0
- package/src/services/NativeHttpResponse.ts +6 -0
- package/src/services/OtaArtifactDownloader.ts +312 -0
- package/src/services/OtaAutoChain.ts +106 -0
- package/src/services/OtaErrorMapping.ts +80 -0
- package/src/services/OtaInstallCoordinator.ts +649 -144
- package/src/services/OtaService.ts +11 -5
- package/src/services/OtaUpdateCheckService.ts +181 -21
- package/src/services/PhoneCalendarHelpers.ts +212 -0
- package/src/services/PhoneCalendarService.ts +65 -0
- package/src/services/PhoneCameraFovCoordinator.ts +210 -0
- package/src/services/PhoneNotificationsSync.ts +12 -10
- package/src/services/PhonePhotoCoordinator.ts +116 -18
- package/src/services/PhoneStreamCoordinator.ts +139 -25
- package/src/services/PhoneVideoCoordinator.ts +11 -0
- package/src/services/SceneRenderer.ts +16 -0
- package/src/services/SentenceTtsPipeline.ts +106 -0
- package/src/services/SupportProfileSync.ts +218 -0
- package/src/services/TranscriptionRouting.ts +37 -0
- package/src/services/TtsTextSanitizer.ts +66 -0
- package/src/services/WifiAdbRequest.ts +53 -0
- package/src/services/asg/asgCameraApi.ts +615 -33
- package/src/services/asg/cameraRollExportCoordinator.ts +627 -0
- package/src/services/asg/cameraRollExportLedger.ts +122 -0
- package/src/services/asg/galleryNotices.ts +1 -0
- package/src/services/asg/galleryPathMigration.ts +22 -0
- package/src/services/asg/gallerySettingsService.ts +3 -0
- package/src/services/asg/gallerySyncService.ts +184 -73
- package/src/services/asg/galleryTransferLedger.ts +312 -0
- package/src/services/asg/localNetworkTransport.ts +169 -0
- package/src/services/asg/localStorageService.ts +179 -24
- package/src/services/asg/mediaProcessingQueue.ts +209 -62
- package/src/services/audioPlaybackAssets.ts +8 -0
- package/src/services/gallerySyncClock.ts +1 -1
- package/src/services/glassesClockSync.ts +6 -2
- package/src/services/manifestActions.ts +30 -0
- package/src/services/otaDisplayState.ts +58 -17
- package/src/services/otaInstallPolicy.ts +30 -4
- package/src/services/otaLegacyMapping.ts +1 -1
- package/src/services/otaManifestPolicy.ts +42 -0
- package/src/services/otaManifestUrl.ts +44 -0
- package/src/services/slimStreamStatus.ts +16 -5
- package/src/stores/apps.ts +12 -0
- package/src/stores/bluetoothSettingKeys.ts +2 -0
- package/src/stores/gallerySync.ts +4 -0
- package/src/stores/glasses.ts +3 -0
- package/src/stores/settings.ts +179 -18
- package/src/types/applet.ts +2 -0
- package/src/types/asg.ts +7 -0
- package/src/types/enums.ts +3 -28
- package/src/types/hardware.ts +20 -13
- package/src/types/index.ts +2 -6
- package/src/utils/configuredDevHost.ts +17 -0
- package/src/utils/devMiniappLaunch.ts +151 -23
- package/src/utils/diagnosticContext.ts +17 -17
- package/src/utils/hardware/hardware.ts +7 -9
- package/src/utils/miniappDiagnostics.ts +226 -0
- package/src/utils/permissions/MediaLibraryPermissions.ts +35 -4
- package/src/utils/timers/timers.ts +57 -21
- 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 -66
- package/build/internal.d.ts.map +0 -1
- package/build/internal.js +0 -89
- package/build/internal.js.map +0 -1
- package/build/services/asgOtaVersionUrl.d.ts +0 -2
- package/build/services/asgOtaVersionUrl.d.ts.map +0 -1
- package/build/services/asgOtaVersionUrl.js +0 -36
- package/build/services/asgOtaVersionUrl.js.map +0 -1
- package/src/devtools.ts +0 -13
- package/src/internal.ts +0 -158
- package/src/services/asgOtaVersionUrl.ts +0 -41
|
@@ -15,11 +15,7 @@ import type {OtaStatus} from "@mentra/bluetooth-sdk/internal"
|
|
|
15
15
|
import GlobalEventEmitter from "../utils/GlobalEventEmitter"
|
|
16
16
|
import {useGlassesStore} from "../stores/glasses"
|
|
17
17
|
import {handleOtaClockSkewFromGlasses} from "./glassesClockSync"
|
|
18
|
-
import {
|
|
19
|
-
legacyOtaProgressFromOtaStatusEvent,
|
|
20
|
-
normalizeOtaStatusEvent,
|
|
21
|
-
otaStatusFromNormalized,
|
|
22
|
-
} from "./otaLegacyMapping"
|
|
18
|
+
import {legacyOtaProgressFromOtaStatusEvent, normalizeOtaStatusEvent, otaStatusFromNormalized} from "./otaLegacyMapping"
|
|
23
19
|
|
|
24
20
|
let subs: Array<{remove: () => void}> = []
|
|
25
21
|
|
|
@@ -40,6 +36,16 @@ export function startOtaService(): void {
|
|
|
40
36
|
}),
|
|
41
37
|
)
|
|
42
38
|
|
|
39
|
+
// The glasses process restarted under a live BLE link (sid changed in
|
|
40
|
+
// glasses_ready/version_info_1). The physical connection never dropped, so this is
|
|
41
|
+
// the ONLY reconnect-edge signal the OTA coordinator gets after an APK install.
|
|
42
|
+
subs.push(
|
|
43
|
+
BluetoothSdk.addListener("glasses_session_changed", (event) => {
|
|
44
|
+
console.log(`[OTA] glasses_session_changed ${event.previous_sid || "<none>"} -> ${event.sid}`)
|
|
45
|
+
GlobalEventEmitter.emit("glasses_session_changed", {previousSid: event.previous_sid, sid: event.sid})
|
|
46
|
+
}),
|
|
47
|
+
)
|
|
48
|
+
|
|
43
49
|
// Install progress / lifecycle. Updates both the new (otaStatus) and the legacy
|
|
44
50
|
// (otaProgress) store shapes; auto-fixes clock skew on the relevant failures.
|
|
45
51
|
subs.push(
|
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
import BluetoothSdk from "@mentra/bluetooth-sdk/internal"
|
|
2
|
-
import type {OtaUpdateInfo} from "@mentra/bluetooth-sdk
|
|
2
|
+
import type {OtaUpdateInfo} from "@mentra/bluetooth-sdk"
|
|
3
3
|
import {getGlassesSystemTimeMs, isGlassesConnected, useGlassesStore, waitForGlassesState} from "../stores/glasses"
|
|
4
4
|
import {maybeFixGlassesClockFromVersionInfo} from "./glassesClockSync"
|
|
5
|
-
import {
|
|
5
|
+
import {hasConfiguredModernOtaManifestPin, resolveOtaManifestUrl} from "./otaManifestUrl"
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Phone-side mirror of ASG's `DOWNGRADE_FLOOR_VERSION_CODE`. Set to the versionCode of the first
|
|
9
|
+
* release shipping the shared media root (the first downgrade-safe build). A non-positive value
|
|
10
|
+
* means downgrades are disabled, so the phone must NOT offer them — offering a downgrade the
|
|
11
|
+
* glasses' `DowngradeGate` will refuse leaves the version-change session with no install and the
|
|
12
|
+
* glasses reporting a bare "complete", which would otherwise present as false success. Keep this
|
|
13
|
+
* in lockstep with the ASG/recovery constant at release time.
|
|
14
|
+
*/
|
|
15
|
+
export const DOWNGRADE_FLOOR_VERSION_CODE = 0
|
|
6
16
|
|
|
7
17
|
export interface VersionInfo {
|
|
8
18
|
versionCode: number
|
|
@@ -18,11 +28,13 @@ export interface MtkPatch {
|
|
|
18
28
|
start_firmware: string
|
|
19
29
|
end_firmware: string
|
|
20
30
|
url: string
|
|
31
|
+
sha256?: string
|
|
21
32
|
}
|
|
22
33
|
|
|
23
34
|
export interface BesFirmware {
|
|
24
35
|
version: string
|
|
25
36
|
url: string
|
|
37
|
+
sha256?: string
|
|
26
38
|
}
|
|
27
39
|
|
|
28
40
|
export interface VersionJson {
|
|
@@ -46,9 +58,23 @@ export interface OtaCheckResult {
|
|
|
46
58
|
updates: string[]
|
|
47
59
|
mtkPatch: MtkPatch | null
|
|
48
60
|
besVersion: string | null
|
|
61
|
+
/** True when the pending APK step installs an OLDER build than the glasses currently run. */
|
|
62
|
+
isApkDowngrade: boolean
|
|
63
|
+
/**
|
|
64
|
+
* Raw manifest JSON (stringified) this check actually fetched, or null when the
|
|
65
|
+
* fetch failed. Lets change-watchers baseline on exactly what the check saw,
|
|
66
|
+
* instead of racing it with a second fetch of the same URL.
|
|
67
|
+
*/
|
|
68
|
+
manifestBody: string | null
|
|
69
|
+
/**
|
|
70
|
+
* Why the check failed, when it did. "network" is retryable (connection/server
|
|
71
|
+
* trouble); "pin_unavailable" is permanent for this app build (manifest 404/gone,
|
|
72
|
+
* unparseable, or carrying no valid ASG pin) — the remedy is updating the app.
|
|
73
|
+
*/
|
|
74
|
+
checkFailureReason?: "network" | "pin_unavailable"
|
|
49
75
|
}
|
|
50
76
|
|
|
51
|
-
export type OtaCheckSkippedReason = "disconnected" | "missing_build"
|
|
77
|
+
export type OtaCheckSkippedReason = "disconnected" | "missing_build" | "dev_build"
|
|
52
78
|
|
|
53
79
|
export interface OtaCheckCurrentGlassesResult extends OtaCheckResult {
|
|
54
80
|
updateInfo: OtaUpdateInfo | null
|
|
@@ -66,6 +92,8 @@ export interface OtaCheckCurrentGlassesOptions {
|
|
|
66
92
|
waitForMtkVersionMs?: number
|
|
67
93
|
refreshVersionInfo?: boolean
|
|
68
94
|
fixClockBeforeCheck?: boolean
|
|
95
|
+
/** Downgrade floor override (defaults to DOWNGRADE_FLOOR_VERSION_CODE); for tests. */
|
|
96
|
+
floorVersionCode?: number
|
|
69
97
|
}
|
|
70
98
|
|
|
71
99
|
function emptyCheckResult(skippedReason?: OtaCheckSkippedReason): OtaCheckCurrentGlassesResult {
|
|
@@ -76,6 +104,8 @@ function emptyCheckResult(skippedReason?: OtaCheckSkippedReason): OtaCheckCurren
|
|
|
76
104
|
updates: [],
|
|
77
105
|
mtkPatch: null,
|
|
78
106
|
besVersion: null,
|
|
107
|
+
isApkDowngrade: false,
|
|
108
|
+
manifestBody: null,
|
|
79
109
|
updateInfo: null,
|
|
80
110
|
isRequired: true,
|
|
81
111
|
skippedReason,
|
|
@@ -86,46 +116,90 @@ function glassesConnectedNow(): boolean {
|
|
|
86
116
|
return isGlassesConnected(useGlassesStore.getState().connection)
|
|
87
117
|
}
|
|
88
118
|
|
|
89
|
-
|
|
119
|
+
type ManifestFetchResult = {json: VersionJson | null; failureReason?: "network" | "pin_unavailable"}
|
|
120
|
+
|
|
121
|
+
async function fetchVersionInfoDetailed(url: string): Promise<ManifestFetchResult> {
|
|
90
122
|
try {
|
|
91
123
|
const response = await fetch(url)
|
|
92
124
|
if (!response.ok) {
|
|
93
125
|
console.error("Failed to fetch version info:", response.status)
|
|
94
|
-
|
|
126
|
+
// 4xx: the manifest does not exist (or is gone) — permanent for this app
|
|
127
|
+
// build, retrying cannot help. Everything else is transient server/network
|
|
128
|
+
// trouble and retryable.
|
|
129
|
+
return {
|
|
130
|
+
json: null,
|
|
131
|
+
failureReason: response.status >= 400 && response.status < 500 ? "pin_unavailable" : "network",
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
try {
|
|
135
|
+
return {json: await response.json()}
|
|
136
|
+
} catch (parseError) {
|
|
137
|
+
console.error("OTA: manifest is not valid JSON:", parseError)
|
|
138
|
+
return {json: null, failureReason: "pin_unavailable"}
|
|
95
139
|
}
|
|
96
|
-
return await response.json()
|
|
97
140
|
} catch (error) {
|
|
98
141
|
console.error("OTA: Error fetching version info:", error)
|
|
99
|
-
return null
|
|
142
|
+
return {json: null, failureReason: "network"}
|
|
100
143
|
}
|
|
101
144
|
}
|
|
102
145
|
|
|
146
|
+
export async function fetchVersionInfo(url: string): Promise<VersionJson | null> {
|
|
147
|
+
return (await fetchVersionInfoDetailed(url)).json
|
|
148
|
+
}
|
|
149
|
+
|
|
103
150
|
export function checkVersionUpdateAvailable(
|
|
104
151
|
currentBuildNumber: string | undefined,
|
|
105
152
|
versionJson: VersionJson | null,
|
|
153
|
+
floorVersionCode: number = DOWNGRADE_FLOOR_VERSION_CODE,
|
|
106
154
|
): boolean {
|
|
155
|
+
return getApkUpdateDirection(currentBuildNumber, versionJson, floorVersionCode) !== null
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Direction of the pending APK change, or null when the glasses already match the manifest.
|
|
160
|
+
* Every apps-shaped manifest the phone drives is an exact pin, so both directions are
|
|
161
|
+
* actionable (mirroring the glasses-side `DowngradeGate`); the ancient top-level manifest
|
|
162
|
+
* shape stays upgrade-only, and zeroed rescue pins are never actionable.
|
|
163
|
+
*/
|
|
164
|
+
export function getApkUpdateDirection(
|
|
165
|
+
currentBuildNumber: string | undefined,
|
|
166
|
+
versionJson: VersionJson | null,
|
|
167
|
+
floorVersionCode: number = DOWNGRADE_FLOOR_VERSION_CODE,
|
|
168
|
+
): "upgrade" | "downgrade" | null {
|
|
107
169
|
if (!currentBuildNumber || !versionJson) {
|
|
108
|
-
return
|
|
170
|
+
return null
|
|
109
171
|
}
|
|
110
172
|
|
|
111
173
|
const currentVersion = parseInt(currentBuildNumber, 10)
|
|
112
174
|
if (isNaN(currentVersion)) {
|
|
113
|
-
return
|
|
175
|
+
return null
|
|
114
176
|
}
|
|
115
177
|
|
|
116
178
|
let serverVersion: number | undefined
|
|
179
|
+
let exactPin = false
|
|
117
180
|
|
|
118
|
-
|
|
119
|
-
|
|
181
|
+
const appEntry = versionJson.apps?.["com.mentra.asg_client"]
|
|
182
|
+
if (appEntry) {
|
|
183
|
+
serverVersion = appEntry.versionCode
|
|
184
|
+
exactPin = true
|
|
120
185
|
} else if (versionJson.versionCode) {
|
|
121
186
|
serverVersion = versionJson.versionCode
|
|
122
187
|
}
|
|
123
188
|
|
|
124
189
|
if (!serverVersion || isNaN(serverVersion)) {
|
|
125
|
-
return
|
|
190
|
+
return null
|
|
126
191
|
}
|
|
127
192
|
|
|
128
|
-
|
|
193
|
+
if (serverVersion > currentVersion) {
|
|
194
|
+
return "upgrade"
|
|
195
|
+
}
|
|
196
|
+
// Downgrades require an exact pin AND a target at/above the enabled floor. A non-positive floor
|
|
197
|
+
// disables downgrades entirely, matching ASG's fail-closed DowngradeGate — so the phone never
|
|
198
|
+
// offers what the glasses would refuse.
|
|
199
|
+
if (serverVersion < currentVersion && exactPin && floorVersionCode > 0 && serverVersion >= floorVersionCode) {
|
|
200
|
+
return "downgrade"
|
|
201
|
+
}
|
|
202
|
+
return null
|
|
129
203
|
}
|
|
130
204
|
|
|
131
205
|
export function getLatestVersionInfo(versionJson: VersionJson | null): VersionInfo | null {
|
|
@@ -184,7 +258,7 @@ export function checkBesUpdate(besFirmware: BesFirmware | undefined, currentVers
|
|
|
184
258
|
return compareVersions(besFirmware.version, currentVersion) > 0
|
|
185
259
|
}
|
|
186
260
|
|
|
187
|
-
function compareVersions(version1: string, version2: string): number {
|
|
261
|
+
export function compareVersions(version1: string, version2: string): number {
|
|
188
262
|
if (version1.includes(".") && version2.includes(".")) {
|
|
189
263
|
const parts1 = version1.split(".")
|
|
190
264
|
const parts2 = version2.split(".")
|
|
@@ -208,14 +282,62 @@ export async function checkForOtaUpdate(
|
|
|
208
282
|
currentBuildNumber: string,
|
|
209
283
|
currentMtkVersion?: string,
|
|
210
284
|
currentBesVersion?: string,
|
|
285
|
+
floorVersionCode: number = DOWNGRADE_FLOOR_VERSION_CODE,
|
|
211
286
|
): Promise<OtaCheckResult> {
|
|
212
287
|
try {
|
|
213
288
|
console.log("OTA: Checking for OTA update - URL: " + otaVersionUrl + ", current build: " + currentBuildNumber)
|
|
214
|
-
const
|
|
289
|
+
const fetched = await fetchVersionInfoDetailed(otaVersionUrl)
|
|
290
|
+
const versionJson = fetched.json
|
|
291
|
+
if (!versionJson) {
|
|
292
|
+
return {
|
|
293
|
+
hasCheckCompleted: false,
|
|
294
|
+
updateAvailable: false,
|
|
295
|
+
latestVersionInfo: null,
|
|
296
|
+
updates: [],
|
|
297
|
+
mtkPatch: null,
|
|
298
|
+
besVersion: null,
|
|
299
|
+
isApkDowngrade: false,
|
|
300
|
+
manifestBody: null,
|
|
301
|
+
checkFailureReason: fetched.failureReason ?? "network",
|
|
302
|
+
}
|
|
303
|
+
}
|
|
215
304
|
const latestVersionInfo = getLatestVersionInfo(versionJson)
|
|
216
305
|
|
|
217
|
-
|
|
218
|
-
|
|
306
|
+
// A manifest whose ASG pin is missing/zeroed cannot verify anything for modern
|
|
307
|
+
// glasses (zeroed pins are only legitimate in the frozen legacy rescue manifests
|
|
308
|
+
// that pre-39 glasses are checked against). Report the check as FAILED rather
|
|
309
|
+
// than silently completing as "up to date" — an unverifiable state must never
|
|
310
|
+
// present as a verified one.
|
|
311
|
+
const currentVersionNumber = parseInt(currentBuildNumber, 10)
|
|
312
|
+
const pinnedEntry = versionJson?.apps?.["com.mentra.asg_client"]
|
|
313
|
+
const pinnedVersion = pinnedEntry?.versionCode ?? versionJson?.versionCode
|
|
314
|
+
if (
|
|
315
|
+
versionJson &&
|
|
316
|
+
Number.isFinite(currentVersionNumber) &&
|
|
317
|
+
currentVersionNumber >= 39 &&
|
|
318
|
+
!(typeof pinnedVersion === "number" && pinnedVersion > 0)
|
|
319
|
+
) {
|
|
320
|
+
console.error(
|
|
321
|
+
`OTA: manifest at ${otaVersionUrl} has no valid ASG pin (versionCode=${String(pinnedVersion)}) - treating check as failed`,
|
|
322
|
+
)
|
|
323
|
+
return {
|
|
324
|
+
hasCheckCompleted: false,
|
|
325
|
+
updateAvailable: false,
|
|
326
|
+
latestVersionInfo: null,
|
|
327
|
+
updates: [],
|
|
328
|
+
mtkPatch: null,
|
|
329
|
+
besVersion: null,
|
|
330
|
+
isApkDowngrade: false,
|
|
331
|
+
manifestBody: null,
|
|
332
|
+
checkFailureReason: "pin_unavailable",
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
const apkDirection = getApkUpdateDirection(currentBuildNumber, versionJson, floorVersionCode)
|
|
337
|
+
const apkUpdateAvailable = apkDirection !== null
|
|
338
|
+
console.log(
|
|
339
|
+
`OTA: APK update available: ${apkUpdateAvailable} (current: ${currentBuildNumber}, direction: ${apkDirection ?? "none"})`,
|
|
340
|
+
)
|
|
219
341
|
|
|
220
342
|
const mtkPatch = findMatchingMtkPatch(versionJson?.mtk_patches, currentMtkVersion)
|
|
221
343
|
const mtkUpdateAvailable = mtkPatch !== null
|
|
@@ -243,6 +365,8 @@ export async function checkForOtaUpdate(
|
|
|
243
365
|
updates,
|
|
244
366
|
mtkPatch,
|
|
245
367
|
besVersion: versionJson?.bes_firmware?.version || null,
|
|
368
|
+
isApkDowngrade: apkDirection === "downgrade",
|
|
369
|
+
manifestBody: versionJson ? JSON.stringify(versionJson) : null,
|
|
246
370
|
}
|
|
247
371
|
} catch (error) {
|
|
248
372
|
console.error("Error checking for OTA update:", error)
|
|
@@ -253,6 +377,9 @@ export async function checkForOtaUpdate(
|
|
|
253
377
|
updates: [],
|
|
254
378
|
mtkPatch: null,
|
|
255
379
|
besVersion: null,
|
|
380
|
+
isApkDowngrade: false,
|
|
381
|
+
manifestBody: null,
|
|
382
|
+
checkFailureReason: "network",
|
|
256
383
|
}
|
|
257
384
|
}
|
|
258
385
|
}
|
|
@@ -266,12 +393,18 @@ export async function checkCurrentGlassesForUpdate(
|
|
|
266
393
|
waitForMtkVersionMs = 2000,
|
|
267
394
|
refreshVersionInfo = true,
|
|
268
395
|
fixClockBeforeCheck = true,
|
|
396
|
+
floorVersionCode = DOWNGRADE_FLOOR_VERSION_CODE,
|
|
269
397
|
} = options
|
|
270
398
|
|
|
271
399
|
if (!glassesConnectedNow()) {
|
|
272
400
|
return emptyCheckResult("disconnected")
|
|
273
401
|
}
|
|
274
402
|
|
|
403
|
+
if (!hasConfiguredModernOtaManifestPin()) {
|
|
404
|
+
console.log("OTA: check skipped - this app or Engine package has no embedded OTA manifest pin")
|
|
405
|
+
return emptyCheckResult("dev_build")
|
|
406
|
+
}
|
|
407
|
+
|
|
275
408
|
if (refreshVersionInfo) {
|
|
276
409
|
void BluetoothSdk.requestVersionInfo().catch((error) => {
|
|
277
410
|
console.warn("OTA: Failed to request version_info from glasses:", error)
|
|
@@ -284,7 +417,10 @@ export async function checkCurrentGlassesForUpdate(
|
|
|
284
417
|
buildNumber = useGlassesStore.getState().buildNumber
|
|
285
418
|
}
|
|
286
419
|
|
|
287
|
-
if (!buildNumber) {
|
|
420
|
+
if (!buildNumber || !(parseInt(buildNumber, 10) > 0)) {
|
|
421
|
+
// Covers absent, non-numeric, and zero build numbers: a glasses version we
|
|
422
|
+
// cannot parse means nothing is verifiable, so skip rather than ghost an
|
|
423
|
+
// "up to date" result from a comparison that never really ran.
|
|
288
424
|
return emptyCheckResult("missing_build")
|
|
289
425
|
}
|
|
290
426
|
|
|
@@ -325,8 +461,18 @@ export async function checkCurrentGlassesForUpdate(
|
|
|
325
461
|
})
|
|
326
462
|
}
|
|
327
463
|
|
|
328
|
-
const manifestUrl =
|
|
329
|
-
|
|
464
|
+
const manifestUrl = resolveOtaManifestUrl(useGlassesStore.getState().otaVersionUrl, buildNumber)
|
|
465
|
+
if (!manifestUrl) {
|
|
466
|
+
console.log("OTA: check skipped - no OTA manifest pin resolved")
|
|
467
|
+
return emptyCheckResult("dev_build")
|
|
468
|
+
}
|
|
469
|
+
const result = await checkForOtaUpdate(
|
|
470
|
+
manifestUrl,
|
|
471
|
+
buildNumber,
|
|
472
|
+
mtkFirmwareVersion,
|
|
473
|
+
besFirmwareVersion,
|
|
474
|
+
floorVersionCode,
|
|
475
|
+
)
|
|
330
476
|
|
|
331
477
|
if (!result.hasCheckCompleted) {
|
|
332
478
|
return {
|
|
@@ -360,6 +506,7 @@ export async function checkCurrentGlassesForUpdate(
|
|
|
360
506
|
)
|
|
361
507
|
}
|
|
362
508
|
const updateAvailable = filteredUpdates.length > 0 && !!result.latestVersionInfo
|
|
509
|
+
const isApkDowngrade = result.isApkDowngrade && filteredUpdates.includes("apk")
|
|
363
510
|
const updateInfo = updateAvailable
|
|
364
511
|
? {
|
|
365
512
|
available: true,
|
|
@@ -367,6 +514,8 @@ export async function checkCurrentGlassesForUpdate(
|
|
|
367
514
|
versionName: result.latestVersionInfo?.versionName || "",
|
|
368
515
|
updates: filteredUpdates,
|
|
369
516
|
totalSize: 0,
|
|
517
|
+
...(filteredUpdates.includes("bes") && result.besVersion ? {besVersion: result.besVersion} : {}),
|
|
518
|
+
isDowngrade: isApkDowngrade,
|
|
370
519
|
}
|
|
371
520
|
: null
|
|
372
521
|
|
|
@@ -376,8 +525,19 @@ export async function checkCurrentGlassesForUpdate(
|
|
|
376
525
|
...result,
|
|
377
526
|
updateAvailable,
|
|
378
527
|
updates: filteredUpdates,
|
|
528
|
+
isApkDowngrade,
|
|
379
529
|
updateInfo,
|
|
380
|
-
|
|
530
|
+
// Required-update policy: an EXPLICIT isRequired in the manifest is the
|
|
531
|
+
// release-engineering escape hatch and wins in both directions. When absent,
|
|
532
|
+
// upgrades keep the historical forced default, but downgrades are always
|
|
533
|
+
// skippable — a version change that resets glasses settings must be
|
|
534
|
+
// declinable unless someone explicitly forced it.
|
|
535
|
+
isRequired:
|
|
536
|
+
result.latestVersionInfo?.isRequired === true
|
|
537
|
+
? true
|
|
538
|
+
: result.latestVersionInfo?.isRequired === false
|
|
539
|
+
? false
|
|
540
|
+
: !isApkDowngrade,
|
|
381
541
|
manifestUrl,
|
|
382
542
|
buildNumber,
|
|
383
543
|
mtkFirmwareVersion,
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import {MiniappErrorCode, type CalendarEvent} from "@mentra/miniapp"
|
|
2
|
+
|
|
3
|
+
const DEFAULT_LIMIT = 50
|
|
4
|
+
const MAX_LIMIT = 100
|
|
5
|
+
const MAX_RANGE_MS = 31 * 24 * 60 * 60 * 1000
|
|
6
|
+
|
|
7
|
+
export interface CalendarListRequest {
|
|
8
|
+
startsAt?: unknown
|
|
9
|
+
endsAt?: unknown
|
|
10
|
+
limit?: unknown
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export class PhoneCalendarError extends Error {
|
|
14
|
+
constructor(
|
|
15
|
+
public readonly code: MiniappErrorCode,
|
|
16
|
+
message: string,
|
|
17
|
+
) {
|
|
18
|
+
super(message)
|
|
19
|
+
this.name = "PhoneCalendarError"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface ValidatedCalendarListRequest {
|
|
24
|
+
startsAt: Date
|
|
25
|
+
endsAt: Date
|
|
26
|
+
limit: number
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface RawCalendarEvent {
|
|
30
|
+
id?: string
|
|
31
|
+
calendarId?: string
|
|
32
|
+
title?: string | null
|
|
33
|
+
startDate: string | Date
|
|
34
|
+
endDate: string | Date
|
|
35
|
+
timeZone?: string | null
|
|
36
|
+
allDay?: boolean
|
|
37
|
+
location?: string | null
|
|
38
|
+
notes?: string | null
|
|
39
|
+
url?: string | null
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function validateCalendarListRequest(request: CalendarListRequest): ValidatedCalendarListRequest {
|
|
43
|
+
const startsAt = parseDate(request.startsAt, "startsAt")
|
|
44
|
+
const endsAt = parseDate(request.endsAt, "endsAt")
|
|
45
|
+
if (endsAt.getTime() <= startsAt.getTime()) {
|
|
46
|
+
throw new PhoneCalendarError(MiniappErrorCode.INVALID_ARGUMENT, "endsAt must be after startsAt")
|
|
47
|
+
}
|
|
48
|
+
if (endsAt.getTime() - startsAt.getTime() > MAX_RANGE_MS) {
|
|
49
|
+
throw new PhoneCalendarError(MiniappErrorCode.INVALID_ARGUMENT, "Calendar query range may not exceed 31 days")
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const limit = request.limit === undefined ? DEFAULT_LIMIT : request.limit
|
|
53
|
+
if (typeof limit !== "number" || !Number.isInteger(limit) || limit < 1 || limit > MAX_LIMIT) {
|
|
54
|
+
throw new PhoneCalendarError(
|
|
55
|
+
MiniappErrorCode.INVALID_ARGUMENT,
|
|
56
|
+
`limit must be an integer between 1 and ${MAX_LIMIT}`,
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
return {startsAt, endsAt, limit}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function parseDate(value: unknown, field: string): Date {
|
|
63
|
+
if (typeof value !== "string") {
|
|
64
|
+
throw new PhoneCalendarError(MiniappErrorCode.INVALID_ARGUMENT, `${field} must be an ISO 8601 string`)
|
|
65
|
+
}
|
|
66
|
+
const date = new Date(value)
|
|
67
|
+
if (Number.isNaN(date.getTime())) {
|
|
68
|
+
throw new PhoneCalendarError(MiniappErrorCode.INVALID_ARGUMENT, `${field} must be a valid ISO 8601 string`)
|
|
69
|
+
}
|
|
70
|
+
return date
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function extractHttpsLinks(...values: Array<string | null | undefined>): string[] {
|
|
74
|
+
const links = new Set<string>()
|
|
75
|
+
for (const value of values) {
|
|
76
|
+
for (const match of value?.match(/https:\/\/[^\s<>"']+/gi) ?? []) {
|
|
77
|
+
let link = match
|
|
78
|
+
while (".,;:!?)]}".includes(link[link.length - 1] ?? "")) link = link.slice(0, -1)
|
|
79
|
+
if (link) links.add(link)
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return [...links]
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
interface CalendarDateParts {
|
|
86
|
+
year: number
|
|
87
|
+
month: number
|
|
88
|
+
day: number
|
|
89
|
+
hour: number
|
|
90
|
+
minute: number
|
|
91
|
+
second: number
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function localDateParts(date: Date): CalendarDateParts {
|
|
95
|
+
return {
|
|
96
|
+
year: date.getFullYear(),
|
|
97
|
+
month: date.getMonth() + 1,
|
|
98
|
+
day: date.getDate(),
|
|
99
|
+
hour: date.getHours(),
|
|
100
|
+
minute: date.getMinutes(),
|
|
101
|
+
second: date.getSeconds(),
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function datePartsInTimeZone(date: Date, timeZone: string): CalendarDateParts | undefined {
|
|
106
|
+
try {
|
|
107
|
+
const parts = new Intl.DateTimeFormat("en-US", {
|
|
108
|
+
timeZone,
|
|
109
|
+
year: "numeric",
|
|
110
|
+
month: "2-digit",
|
|
111
|
+
day: "2-digit",
|
|
112
|
+
hour: "2-digit",
|
|
113
|
+
minute: "2-digit",
|
|
114
|
+
second: "2-digit",
|
|
115
|
+
hourCycle: "h23",
|
|
116
|
+
}).formatToParts(date)
|
|
117
|
+
const value = (type: Intl.DateTimeFormatPartTypes) => Number(parts.find((part) => part.type === type)?.value)
|
|
118
|
+
return {
|
|
119
|
+
year: value("year"),
|
|
120
|
+
month: value("month"),
|
|
121
|
+
day: value("day"),
|
|
122
|
+
hour: value("hour"),
|
|
123
|
+
minute: value("minute"),
|
|
124
|
+
second: value("second"),
|
|
125
|
+
}
|
|
126
|
+
} catch {
|
|
127
|
+
return undefined
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function timeZoneOffsetMinutes(date: Date, timeZone: string): number | undefined {
|
|
132
|
+
const parts = datePartsInTimeZone(date, timeZone)
|
|
133
|
+
if (!parts) return undefined
|
|
134
|
+
const representedAsUtc = Date.UTC(
|
|
135
|
+
parts.year,
|
|
136
|
+
parts.month - 1,
|
|
137
|
+
parts.day,
|
|
138
|
+
parts.hour,
|
|
139
|
+
parts.minute,
|
|
140
|
+
parts.second,
|
|
141
|
+
date.getUTCMilliseconds(),
|
|
142
|
+
)
|
|
143
|
+
return Math.round((representedAsUtc - date.getTime()) / 60_000)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function pad(value: number, length = 2): string {
|
|
147
|
+
return String(value).padStart(length, "0")
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function formatOffset(offsetMinutes: number): string {
|
|
151
|
+
if (offsetMinutes === 0) return "Z"
|
|
152
|
+
const sign = offsetMinutes < 0 ? "-" : "+"
|
|
153
|
+
const absolute = Math.abs(offsetMinutes)
|
|
154
|
+
return `${sign}${pad(Math.floor(absolute / 60))}:${pad(absolute % 60)}`
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function formatAllDayDate(value: string | Date, timeZone?: string | null): string {
|
|
158
|
+
const dateOnly = typeof value === "string" ? /^(\d{4})-(\d{2})-(\d{2})$/.exec(value) : null
|
|
159
|
+
if (dateOnly) {
|
|
160
|
+
const [, year, month, day] = dateOnly
|
|
161
|
+
const localMidnight = new Date(Number(year), Number(month) - 1, Number(day))
|
|
162
|
+
let offsetMinutes = -localMidnight.getTimezoneOffset()
|
|
163
|
+
if (timeZone) {
|
|
164
|
+
// Resolve the offset at midnight in the event timezone. Repeating once
|
|
165
|
+
// accounts for the initial UTC guess landing across a DST boundary.
|
|
166
|
+
const wallTime = Date.UTC(Number(year), Number(month) - 1, Number(day))
|
|
167
|
+
let instant = new Date(wallTime)
|
|
168
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
169
|
+
const offset = timeZoneOffsetMinutes(instant, timeZone)
|
|
170
|
+
if (offset === undefined) break
|
|
171
|
+
offsetMinutes = offset
|
|
172
|
+
instant = new Date(wallTime - offset * 60_000)
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return `${year}-${month}-${day}T00:00:00.000${formatOffset(offsetMinutes)}`
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const date = new Date(value)
|
|
179
|
+
const zonedParts = timeZone ? datePartsInTimeZone(date, timeZone) : undefined
|
|
180
|
+
const parts = zonedParts ?? localDateParts(date)
|
|
181
|
+
const offsetMinutes =
|
|
182
|
+
timeZone && zonedParts
|
|
183
|
+
? (timeZoneOffsetMinutes(date, timeZone) ?? -date.getTimezoneOffset())
|
|
184
|
+
: -date.getTimezoneOffset()
|
|
185
|
+
return `${pad(parts.year, 4)}-${pad(parts.month)}-${pad(parts.day)}T${pad(parts.hour)}:${pad(parts.minute)}:${pad(parts.second)}.${pad(date.getUTCMilliseconds(), 3)}${formatOffset(offsetMinutes)}`
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export function normalizeCalendarEvent(event: RawCalendarEvent): CalendarEvent {
|
|
189
|
+
const startsAt = event.allDay
|
|
190
|
+
? formatAllDayDate(event.startDate, event.timeZone)
|
|
191
|
+
: new Date(event.startDate).toISOString()
|
|
192
|
+
const endsAt = event.allDay ? formatAllDayDate(event.endDate, event.timeZone) : new Date(event.endDate).toISOString()
|
|
193
|
+
const calendarId = event.calendarId ?? ""
|
|
194
|
+
const eventId = event.id ?? ""
|
|
195
|
+
return {
|
|
196
|
+
id: `${calendarId}:${eventId}:${startsAt}`,
|
|
197
|
+
calendarId,
|
|
198
|
+
title: event.title ?? "",
|
|
199
|
+
startsAt,
|
|
200
|
+
endsAt,
|
|
201
|
+
...(event.timeZone ? {timezone: event.timeZone} : {}),
|
|
202
|
+
allDay: !!event.allDay,
|
|
203
|
+
...(event.location ? {location: event.location} : {}),
|
|
204
|
+
...(event.notes ? {notes: event.notes} : {}),
|
|
205
|
+
...(event.url ? {url: event.url} : {}),
|
|
206
|
+
links: extractHttpsLinks(event.url, event.location, event.notes),
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export function calendarEventOverlapsWindow(event: CalendarEvent, startsAt: Date, endsAt: Date): boolean {
|
|
211
|
+
return Date.parse(event.endsAt) > startsAt.getTime() && Date.parse(event.startsAt) < endsAt.getTime()
|
|
212
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import * as Calendar from "expo-calendar"
|
|
2
|
+
import {Platform} from "react-native"
|
|
3
|
+
import {check, PERMISSIONS, RESULTS} from "react-native-permissions"
|
|
4
|
+
|
|
5
|
+
import {MiniappErrorCode, type CalendarListResult} from "@mentra/miniapp"
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
calendarEventOverlapsWindow,
|
|
9
|
+
type CalendarListRequest,
|
|
10
|
+
normalizeCalendarEvent,
|
|
11
|
+
PhoneCalendarError,
|
|
12
|
+
validateCalendarListRequest,
|
|
13
|
+
} from "./PhoneCalendarHelpers"
|
|
14
|
+
|
|
15
|
+
export {PhoneCalendarError} from "./PhoneCalendarHelpers"
|
|
16
|
+
|
|
17
|
+
type CalendarPermissionStatus = Awaited<
|
|
18
|
+
ReturnType<typeof Calendar.getCalendarPermissionsAsync>
|
|
19
|
+
>["status"]
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* expo-calendar reports anything below EventKit full access as not granted,
|
|
23
|
+
* so distinguish the states the user can actually fix. "Reopen the miniapp"
|
|
24
|
+
* is only true while the permission is undetermined — once iOS has resolved
|
|
25
|
+
* it (denied or write-only/limited), only Settings can change it.
|
|
26
|
+
*/
|
|
27
|
+
async function calendarPermissionMessage(status: CalendarPermissionStatus): Promise<string> {
|
|
28
|
+
if (status === "undetermined") {
|
|
29
|
+
return "Calendar permission has not been granted yet. Reopen the miniapp and allow calendar access."
|
|
30
|
+
}
|
|
31
|
+
if (Platform.OS === "ios") {
|
|
32
|
+
const writeOnly = await check(PERMISSIONS.IOS.CALENDARS_WRITE_ONLY).catch(() => null)
|
|
33
|
+
if (writeOnly === RESULTS.GRANTED || writeOnly === RESULTS.LIMITED) {
|
|
34
|
+
return 'Calendar access is limited to write-only. Enable "Full Access" for Mentra in Settings › Privacy & Security › Calendars.'
|
|
35
|
+
}
|
|
36
|
+
return "Calendar access is denied. Enable it for Mentra in Settings › Privacy & Security › Calendars."
|
|
37
|
+
}
|
|
38
|
+
return "Calendar access is denied. Enable the Calendar permission for Mentra in your phone's app settings."
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export async function listPhoneCalendarEvents(request: CalendarListRequest): Promise<CalendarListResult> {
|
|
42
|
+
const {startsAt, endsAt, limit} = validateCalendarListRequest(request)
|
|
43
|
+
const permission = await Calendar.getCalendarPermissionsAsync()
|
|
44
|
+
if (permission.status !== "granted") {
|
|
45
|
+
throw new PhoneCalendarError(MiniappErrorCode.PERMISSION_DENIED, await calendarPermissionMessage(permission.status))
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const calendars = await Calendar.getCalendarsAsync(Calendar.EntityTypes.EVENT)
|
|
49
|
+
if (calendars.length === 0) return {events: [], truncated: false}
|
|
50
|
+
|
|
51
|
+
const rawEvents = await Calendar.getEventsAsync(
|
|
52
|
+
calendars.map((calendar) => calendar.id),
|
|
53
|
+
startsAt,
|
|
54
|
+
endsAt,
|
|
55
|
+
)
|
|
56
|
+
const events = rawEvents
|
|
57
|
+
.map(normalizeCalendarEvent)
|
|
58
|
+
.filter((event) => calendarEventOverlapsWindow(event, startsAt, endsAt))
|
|
59
|
+
.sort((a, b) => Date.parse(a.startsAt) - Date.parse(b.startsAt) || Date.parse(a.endsAt) - Date.parse(b.endsAt))
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
events: events.slice(0, limit),
|
|
63
|
+
truncated: events.length > limit,
|
|
64
|
+
}
|
|
65
|
+
}
|