@mentra/engine 0.1.0-dev.0 → 0.1.0-dev.1
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 +42 -1
- package/build/engine.d.ts +11 -3
- package/build/engine.d.ts.map +1 -1
- package/build/engine.js +7 -2
- 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/gallery.js +1 -1
- package/build/facades/gallery.js.map +1 -1
- package/build/facades/glasses.d.ts +1 -0
- package/build/facades/glasses.d.ts.map +1 -1
- package/build/facades/glasses.js +19 -4
- 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 +11 -2
- package/build/facades/ota.d.ts.map +1 -1
- package/build/facades/ota.js +19 -4
- package/build/facades/ota.js.map +1 -1
- package/build/facades/pairing.d.ts +4 -0
- package/build/facades/pairing.d.ts.map +1 -1
- package/build/facades/pairing.js +10 -8
- 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/index.d.ts +6 -3
- package/build/index.d.ts.map +1 -1
- package/build/index.js +4 -2
- package/build/index.js.map +1 -1
- package/build/internal.d.ts +3 -2
- package/build/internal.d.ts.map +1 -1
- package/build/internal.js +2 -1
- package/build/internal.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 +33 -57
- package/build/services/AppRegistry.d.ts.map +1 -1
- package/build/services/AppRegistry.js +193 -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 +94 -14
- package/build/services/OtaInstallCoordinator.d.ts.map +1 -1
- package/build/services/OtaInstallCoordinator.js +574 -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 +38 -3
- package/build/services/OtaUpdateCheckService.d.ts.map +1 -1
- package/build/services/OtaUpdateCheckService.js +142 -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.js +2 -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 +22 -11
- 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 +15 -3
- 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/otaManifestUrl.d.ts +2 -0
- package/build/services/otaManifestUrl.d.ts.map +1 -0
- package/build/services/{asgOtaVersionUrl.js → otaManifestUrl.js} +15 -5
- 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 +1 -0
- package/build/types/enums.d.ts.map +1 -1
- package/build/types/enums.js +1 -0
- package/build/types/enums.js.map +1 -1
- package/build/types/hardware.d.ts +1 -0
- package/build/types/hardware.d.ts.map +1 -1
- package/build/types/hardware.js +14 -0
- package/build/types/hardware.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 +25 -15
- package/src/assets/silence.wav +0 -0
- package/src/engine.ts +7 -2
- package/src/facades/dev.ts +17 -12
- package/src/facades/gallery.ts +1 -1
- package/src/facades/glasses.ts +17 -4
- package/src/facades/glassesSettings.ts +5 -0
- package/src/facades/miniapps.ts +7 -0
- package/src/facades/ota.ts +19 -4
- package/src/facades/pairing.ts +11 -7
- package/src/facades/permissions.ts +14 -0
- package/src/facades/phoneNotifications.ts +4 -13
- package/src/index.ts +26 -2
- package/src/internal.ts +3 -1
- 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 +224 -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 +645 -143
- package/src/services/OtaService.ts +11 -5
- package/src/services/OtaUpdateCheckService.ts +180 -20
- 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 +2 -2
- package/src/services/manifestActions.ts +30 -0
- package/src/services/otaDisplayState.ts +57 -16
- package/src/services/otaInstallPolicy.ts +29 -3
- package/src/services/{asgOtaVersionUrl.ts → otaManifestUrl.ts} +16 -4
- 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 +2 -1
- package/src/types/hardware.ts +17 -0
- 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/services/asgOtaVersionUrl.d.ts +0 -2
- package/build/services/asgOtaVersionUrl.d.ts.map +0 -1
- package/build/services/asgOtaVersionUrl.js.map +0 -1
|
@@ -14,7 +14,8 @@ import {INVALID_DOWNLOADED_MEDIA, validateDownloadedMediaFile} from "./galleryMe
|
|
|
14
14
|
import {reportInvalidGalleryMedia} from "./GalleryMediaIntegrityReportService"
|
|
15
15
|
import {useGallerySyncStore} from "../../stores/gallerySync"
|
|
16
16
|
import {BgTimer} from "../../utils/timers"
|
|
17
|
-
import {
|
|
17
|
+
import {galleryTransferLedger, GalleryTransferEntry} from "./galleryTransferLedger"
|
|
18
|
+
import {cameraRollExportCoordinator} from "./cameraRollExportCoordinator"
|
|
18
19
|
|
|
19
20
|
const TAG = "[MediaProcessingQueue]"
|
|
20
21
|
|
|
@@ -48,6 +49,8 @@ export interface ProcessingItem {
|
|
|
48
49
|
thumbnailPath?: string
|
|
49
50
|
/** File names to delete from glasses after processing completes */
|
|
50
51
|
deleteFromGlasses?: string[]
|
|
52
|
+
/** Negotiated gallery protocol; absent entries are inferred from the transfer ledger. */
|
|
53
|
+
protocolVersion?: number
|
|
51
54
|
}
|
|
52
55
|
|
|
53
56
|
class MediaProcessingQueue {
|
|
@@ -93,7 +96,7 @@ class MediaProcessingQueue {
|
|
|
93
96
|
}
|
|
94
97
|
|
|
95
98
|
/** Returns a promise that resolves when the queue is fully drained, or rejects on timeout. */
|
|
96
|
-
waitUntilDrained(timeoutMs: number =
|
|
99
|
+
waitUntilDrained(timeoutMs: number = 4 * 60 * 60 * 1000): Promise<void> {
|
|
97
100
|
if (!this.hasPending) return Promise.resolve()
|
|
98
101
|
return new Promise((resolve, reject) => {
|
|
99
102
|
const deadline = Date.now() + timeoutMs
|
|
@@ -112,6 +115,71 @@ class MediaProcessingQueue {
|
|
|
112
115
|
})
|
|
113
116
|
}
|
|
114
117
|
|
|
118
|
+
/** Retry durable export/ack work after an app restart without downloading media again. */
|
|
119
|
+
async retryPending(): Promise<{retried: number; failed: number}> {
|
|
120
|
+
let retried = 0
|
|
121
|
+
let failed = 0
|
|
122
|
+
for (let entry of galleryTransferLedger.pendingRecovery()) {
|
|
123
|
+
try {
|
|
124
|
+
if (entry.state === "RESTORE_PENDING") {
|
|
125
|
+
await asgCameraApi.restoreCapture(entry.captureId)
|
|
126
|
+
galleryTransferLedger.transition(entry.captureId, "DISCOVERED", {lastError: undefined})
|
|
127
|
+
retried++
|
|
128
|
+
continue
|
|
129
|
+
}
|
|
130
|
+
if (!entry.finalPath || !(await RNFS.exists(entry.finalPath))) {
|
|
131
|
+
throw new Error(`Recovery file is missing for ${entry.captureId}`)
|
|
132
|
+
}
|
|
133
|
+
// Rebuild a missing app index before any source acknowledgement, even when camera-roll
|
|
134
|
+
// saving is disabled or an export receipt was already committed before restart.
|
|
135
|
+
const downloadedFile = await this.recoverDownloadedFile(entry)
|
|
136
|
+
if (entry.state === "EXPORT_PENDING" || entry.state === "INDEXED") {
|
|
137
|
+
const shouldAutoSave = await cameraRollExportCoordinator.isAutoSaveEnabled()
|
|
138
|
+
entry = galleryTransferLedger.update(entry.captureId, {shouldAutoSave, lastError: undefined})
|
|
139
|
+
if (shouldAutoSave) {
|
|
140
|
+
if (entry.state !== "EXPORT_PENDING") {
|
|
141
|
+
entry = galleryTransferLedger.transition(entry.captureId, "EXPORT_PENDING")
|
|
142
|
+
}
|
|
143
|
+
const receipt = await cameraRollExportCoordinator.exportForSource(downloadedFile, entry.captureId)
|
|
144
|
+
entry = receipt
|
|
145
|
+
? galleryTransferLedger.transition(entry.captureId, "EXPORTED", {assetReceipt: receipt})
|
|
146
|
+
: galleryTransferLedger.transition(entry.captureId, "INDEXED", {
|
|
147
|
+
shouldAutoSave: false,
|
|
148
|
+
lastError: undefined,
|
|
149
|
+
})
|
|
150
|
+
} else if (entry.state === "EXPORT_PENDING") {
|
|
151
|
+
entry = galleryTransferLedger.transition(entry.captureId, "INDEXED", {
|
|
152
|
+
shouldAutoSave: false,
|
|
153
|
+
lastError: undefined,
|
|
154
|
+
})
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
entry = galleryTransferLedger.transition(entry.captureId, "ACK_PENDING")
|
|
158
|
+
await this.acknowledgeEntry(entry)
|
|
159
|
+
galleryTransferLedger.transition(entry.captureId, "TRASHED", {lastError: undefined})
|
|
160
|
+
retried++
|
|
161
|
+
} catch (error) {
|
|
162
|
+
galleryTransferLedger.recordFailure(entry.captureId, error)
|
|
163
|
+
failed++
|
|
164
|
+
console.warn(`${TAG} Recovery remains pending for ${entry.captureId}:`, error)
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return {retried, failed}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
private async acknowledgeEntry(entry: GalleryTransferEntry): Promise<void> {
|
|
171
|
+
if (entry.protocolVersion >= 3) {
|
|
172
|
+
await asgCameraApi.acknowledgeCapture(entry.captureId, entry.ackId)
|
|
173
|
+
return
|
|
174
|
+
}
|
|
175
|
+
const result = await asgCameraApi.deleteFilesFromServer(
|
|
176
|
+
entry.deleteFromGlasses.length > 0 ? entry.deleteFromGlasses : [entry.captureId],
|
|
177
|
+
)
|
|
178
|
+
if (result.failed.length > 0 || result.deleted.length === 0) {
|
|
179
|
+
throw new Error(`Glasses did not acknowledge ${entry.captureId}`)
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
115
183
|
/** Process items one at a time. */
|
|
116
184
|
private async processLoop(): Promise<void> {
|
|
117
185
|
if (this.isRunning) return
|
|
@@ -126,6 +194,10 @@ class MediaProcessingQueue {
|
|
|
126
194
|
} catch (error) {
|
|
127
195
|
console.error(`${TAG} Error processing ${item.id}:`, error)
|
|
128
196
|
processFailed = true
|
|
197
|
+
const store = useGallerySyncStore.getState()
|
|
198
|
+
if (!store.failedFiles.includes(item.id)) {
|
|
199
|
+
store.onFileFailed(item.id, error instanceof Error ? error.message : String(error))
|
|
200
|
+
}
|
|
129
201
|
}
|
|
130
202
|
|
|
131
203
|
// Exit if generation changed (reset was called during processing)
|
|
@@ -136,9 +208,8 @@ class MediaProcessingQueue {
|
|
|
136
208
|
// Success: increment processedFiles and remove from processingFiles set.
|
|
137
209
|
store.onFileProcessed(item.id)
|
|
138
210
|
} else {
|
|
139
|
-
// Failure
|
|
140
|
-
//
|
|
141
|
-
// Still clear the processingFiles entry so the UI doesn't show it as in-progress.
|
|
211
|
+
// Failure is counted exactly once above. Still clear the processingFiles entry so the UI
|
|
212
|
+
// does not show it as in-progress.
|
|
142
213
|
const current = store.processingFiles
|
|
143
214
|
if (current.has(item.id)) {
|
|
144
215
|
const newSet = new Set(current)
|
|
@@ -158,6 +229,23 @@ class MediaProcessingQueue {
|
|
|
158
229
|
private async processItem(item: ProcessingItem): Promise<void> {
|
|
159
230
|
const startTime = Date.now()
|
|
160
231
|
let filePathToSave = item.primaryPath
|
|
232
|
+
let ledgerEntry = galleryTransferLedger.get(item.id)
|
|
233
|
+
if (!ledgerEntry) {
|
|
234
|
+
ledgerEntry = galleryTransferLedger.ensureCapture(
|
|
235
|
+
{
|
|
236
|
+
capture_id: item.id,
|
|
237
|
+
type: item.type,
|
|
238
|
+
timestamp: item.timestamp || Date.now(),
|
|
239
|
+
total_size: item.totalSize,
|
|
240
|
+
files: (item.deleteFromGlasses?.length ? item.deleteFromGlasses : [item.id]).map((name, index) => ({
|
|
241
|
+
name,
|
|
242
|
+
size: index === 0 ? item.totalSize : 0,
|
|
243
|
+
role: index === 0 ? "primary" : "sidecar",
|
|
244
|
+
})),
|
|
245
|
+
},
|
|
246
|
+
item.protocolVersion || 2,
|
|
247
|
+
)
|
|
248
|
+
}
|
|
161
249
|
|
|
162
250
|
// 1. HDR merge (photos with brackets only)
|
|
163
251
|
if (item.shouldProcess && item.type === "photo" && item.bracketPaths && item.bracketPaths.length >= 3) {
|
|
@@ -231,9 +319,8 @@ class MediaProcessingQueue {
|
|
|
231
319
|
mediaKind: item.type,
|
|
232
320
|
})
|
|
233
321
|
} catch (validationError: any) {
|
|
234
|
-
const reason =
|
|
235
|
-
validationError
|
|
236
|
-
validationError.message
|
|
322
|
+
const reason = validationError?.message?.includes(INVALID_DOWNLOADED_MEDIA)
|
|
323
|
+
? validationError.message
|
|
237
324
|
: `${INVALID_DOWNLOADED_MEDIA}: ${item.id}`
|
|
238
325
|
console.error(`${TAG} Validation failed for ${item.id}: ${reason}`)
|
|
239
326
|
reportInvalidGalleryMedia({
|
|
@@ -248,41 +335,23 @@ class MediaProcessingQueue {
|
|
|
248
335
|
})
|
|
249
336
|
const store = useGallerySyncStore.getState()
|
|
250
337
|
store.onFileFailed(item.id, reason)
|
|
251
|
-
|
|
252
|
-
for (const intermediate of [
|
|
253
|
-
item.primaryPath + ".hdr.jpg",
|
|
254
|
-
item.primaryPath + ".processed.jpg",
|
|
255
|
-
item.primaryPath + ".stabilized.mp4",
|
|
256
|
-
]) {
|
|
257
|
-
if (intermediate !== filePathToSave) {
|
|
258
|
-
await RNFS.unlink(intermediate).catch(() => {})
|
|
259
|
-
}
|
|
260
|
-
}
|
|
338
|
+
galleryTransferLedger.recordFailure(item.id, validationError)
|
|
261
339
|
throw new Error(reason)
|
|
262
340
|
}
|
|
263
341
|
|
|
264
|
-
// 6.
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
if (success) {
|
|
268
|
-
console.log(`${TAG} ✅ Saved to camera roll: ${item.id}`)
|
|
269
|
-
} else {
|
|
270
|
-
console.warn(`${TAG} ❌ Failed to save to camera roll: ${item.id}`)
|
|
271
|
-
// D1: Surface camera roll save failures to the UI
|
|
272
|
-
const store = useGallerySyncStore.getState()
|
|
273
|
-
store.onFileFailed(item.id, "camera roll save failed")
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
// 7. Save metadata
|
|
342
|
+
// 6. Commit the app-local index before any export or source acknowledgement. Use the final
|
|
343
|
+
// on-disk output metadata for camera-roll identity: processing can change byte size, while
|
|
344
|
+
// recovery reconstructs this row from the same file and the transfer ledger timestamp.
|
|
278
345
|
const isVideo = item.type === "video"
|
|
346
|
+
const finalFileStat = await RNFS.stat(filePathToSave)
|
|
347
|
+
const finalFileSize = Number(finalFileStat.size)
|
|
279
348
|
const downloadedFile = localStorageService.convertToDownloadedFile(
|
|
280
349
|
{
|
|
281
350
|
name: item.id,
|
|
282
351
|
url: "",
|
|
283
352
|
download: "",
|
|
284
|
-
size:
|
|
285
|
-
modified:
|
|
353
|
+
size: finalFileSize,
|
|
354
|
+
modified: ledgerEntry.timestamp,
|
|
286
355
|
is_video: isVideo,
|
|
287
356
|
thumbnail_data: item.thumbnailData,
|
|
288
357
|
duration: item.duration,
|
|
@@ -293,15 +362,52 @@ class MediaProcessingQueue {
|
|
|
293
362
|
localThumbnailPath,
|
|
294
363
|
item.glassesModel,
|
|
295
364
|
)
|
|
365
|
+
downloadedFile.capture_id = item.id
|
|
366
|
+
const shouldAutoSave = await cameraRollExportCoordinator.isAutoSaveEnabled()
|
|
296
367
|
try {
|
|
297
368
|
await localStorageService.saveDownloadedFile(downloadedFile)
|
|
369
|
+
ledgerEntry = galleryTransferLedger.transition(item.id, "INDEXED", {
|
|
370
|
+
finalPath: filePathToSave,
|
|
371
|
+
thumbnailPath: localThumbnailPath,
|
|
372
|
+
shouldAutoSave,
|
|
373
|
+
})
|
|
374
|
+
await cameraRollExportCoordinator.recordIndexed(
|
|
375
|
+
downloadedFile,
|
|
376
|
+
shouldAutoSave,
|
|
377
|
+
shouldAutoSave ? "SOURCE_BARRIER" : "HISTORICAL_BACKFILL",
|
|
378
|
+
item.id,
|
|
379
|
+
)
|
|
298
380
|
} catch (metadataError) {
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
await RNFS.unlink(filePathToSave).catch(() => {})
|
|
381
|
+
console.error(`${TAG} Metadata save failed for ${item.id}; retaining local and glasses copies:`, metadataError)
|
|
382
|
+
galleryTransferLedger.recordFailure(item.id, metadataError)
|
|
302
383
|
throw metadataError
|
|
303
384
|
}
|
|
304
385
|
|
|
386
|
+
// 7. While enabled, export is a durable, receipted step whose failures block ack. Turning
|
|
387
|
+
// auto-save off safely falls back to the already-verified app-local copy and later backfill.
|
|
388
|
+
if (shouldAutoSave) {
|
|
389
|
+
galleryTransferLedger.transition(item.id, "EXPORT_PENDING")
|
|
390
|
+
let receipt
|
|
391
|
+
try {
|
|
392
|
+
receipt = await cameraRollExportCoordinator.exportForSource(downloadedFile, item.id)
|
|
393
|
+
} catch (error) {
|
|
394
|
+
galleryTransferLedger.recordFailure(item.id, error)
|
|
395
|
+
throw error
|
|
396
|
+
}
|
|
397
|
+
if (receipt) {
|
|
398
|
+
ledgerEntry = galleryTransferLedger.transition(item.id, "EXPORTED", {
|
|
399
|
+
assetReceipt: receipt,
|
|
400
|
+
})
|
|
401
|
+
console.log(`${TAG} ✅ Saved to camera roll with receipt: ${item.id}`)
|
|
402
|
+
} else {
|
|
403
|
+
ledgerEntry = galleryTransferLedger.transition(item.id, "INDEXED", {
|
|
404
|
+
shouldAutoSave: false,
|
|
405
|
+
lastError: undefined,
|
|
406
|
+
})
|
|
407
|
+
console.log(`${TAG} Automatic camera-roll saving was disabled; keeping app-local copy: ${item.id}`)
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
305
411
|
// S4: Clean up intermediate processing files
|
|
306
412
|
const intermediates = [
|
|
307
413
|
item.primaryPath + ".hdr.jpg",
|
|
@@ -326,8 +432,8 @@ class MediaProcessingQueue {
|
|
|
326
432
|
name: item.id,
|
|
327
433
|
url: localFileUrl,
|
|
328
434
|
download: localFileUrl,
|
|
329
|
-
size:
|
|
330
|
-
modified:
|
|
435
|
+
size: finalFileSize,
|
|
436
|
+
modified: ledgerEntry.timestamp,
|
|
331
437
|
is_video: isVideo,
|
|
332
438
|
filePath: filePathToSave,
|
|
333
439
|
mime_type: isVideo ? "video/mp4" : "image/jpeg",
|
|
@@ -336,35 +442,76 @@ class MediaProcessingQueue {
|
|
|
336
442
|
duration: item.duration,
|
|
337
443
|
})
|
|
338
444
|
|
|
339
|
-
// 9.
|
|
340
|
-
//
|
|
341
|
-
|
|
342
|
-
|
|
445
|
+
// 9. Acknowledge only after VERIFIED -> INDEXED -> (EXPORTED). The server turns this into
|
|
446
|
+
// recoverable trash; failures remain ACK_PENDING and are retried without re-downloading.
|
|
447
|
+
const localFileExists = await RNFS.exists(filePathToSave)
|
|
448
|
+
const localStat = localFileExists ? await RNFS.stat(filePathToSave) : null
|
|
449
|
+
if (!localStat || localStat.size <= 0) {
|
|
450
|
+
const error = new Error(`Cannot acknowledge ${item.id}: committed local file is missing or empty`)
|
|
451
|
+
galleryTransferLedger.recordFailure(item.id, error)
|
|
452
|
+
throw error
|
|
453
|
+
}
|
|
454
|
+
ledgerEntry = galleryTransferLedger.transition(item.id, "ACK_PENDING")
|
|
455
|
+
try {
|
|
456
|
+
await this.acknowledgeEntry(ledgerEntry)
|
|
457
|
+
galleryTransferLedger.transition(item.id, "TRASHED", {lastError: undefined})
|
|
458
|
+
} catch (ackError) {
|
|
459
|
+
galleryTransferLedger.recordFailure(item.id, ackError)
|
|
460
|
+
throw ackError
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
const elapsed = Date.now() - startTime
|
|
464
|
+
console.log(`${TAG} ✅ Finished ${item.id} in ${elapsed}ms`)
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/** Rebuild a missing app-local index row from the durable transfer ledger. */
|
|
468
|
+
private async recoverDownloadedFile(entry: GalleryTransferEntry) {
|
|
469
|
+
if (!entry.finalPath) throw new Error(`Recovery file is missing for ${entry.captureId}`)
|
|
470
|
+
|
|
471
|
+
// The transfer ledger is the durable source of truth for a committed generation. A local
|
|
472
|
+
// index row can survive an interrupted replacement and still point at an older/missing path,
|
|
473
|
+
// or contain the pre-processing size. Only reuse it when its path and identity still match the
|
|
474
|
+
// ledger's committed file; otherwise rebuild the row before export or source acknowledgement.
|
|
475
|
+
const finalPath = entry.finalPath.replace(/^file:\/\//, "")
|
|
476
|
+
const indexed = await localStorageService.getDownloadedFile(entry.captureId)
|
|
477
|
+
if (indexed) {
|
|
343
478
|
try {
|
|
344
|
-
const
|
|
345
|
-
if (
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
console.error(`${TAG} Skipping glasses deletion for ${item.id}: local file is 0 bytes`)
|
|
351
|
-
} else if (item.totalSize > 0 && localStat.size < item.totalSize * 0.5) {
|
|
352
|
-
// If local file is less than 50% of expected size, something went wrong
|
|
353
|
-
console.error(
|
|
354
|
-
`${TAG} Skipping glasses deletion for ${item.id}: local file ${localStat.size} bytes is much smaller than expected ${item.totalSize} bytes`,
|
|
355
|
-
)
|
|
356
|
-
} else {
|
|
357
|
-
await asgCameraApi.deleteFilesFromServer(item.deleteFromGlasses)
|
|
358
|
-
console.log(`${TAG} 🗑️ Deleted ${item.deleteFromGlasses.join(", ")} from glasses`)
|
|
479
|
+
const indexedPath = indexed.filePath?.replace(/^file:\/\//, "")
|
|
480
|
+
if (indexedPath === finalPath && (await RNFS.exists(indexedPath))) {
|
|
481
|
+
const indexedStat = await RNFS.stat(indexedPath)
|
|
482
|
+
const indexedSize = Number(indexedStat.size)
|
|
483
|
+
if (indexedSize > 0 && indexed.size === indexedSize && indexed.modified === entry.timestamp) {
|
|
484
|
+
return indexed
|
|
359
485
|
}
|
|
360
486
|
}
|
|
361
|
-
} catch (
|
|
362
|
-
console.warn(`${TAG}
|
|
487
|
+
} catch (error) {
|
|
488
|
+
console.warn(`${TAG} Ignoring stale local index for ${entry.captureId}:`, error)
|
|
363
489
|
}
|
|
364
490
|
}
|
|
365
491
|
|
|
366
|
-
const
|
|
367
|
-
|
|
492
|
+
const stat = await RNFS.stat(finalPath)
|
|
493
|
+
const finalSize = Number(stat.size)
|
|
494
|
+
if (!Number.isFinite(finalSize) || finalSize <= 0) {
|
|
495
|
+
throw new Error(`Recovery file is missing or empty for ${entry.captureId}`)
|
|
496
|
+
}
|
|
497
|
+
const recovered = localStorageService.convertToDownloadedFile(
|
|
498
|
+
{
|
|
499
|
+
name: entry.captureId,
|
|
500
|
+
url: "",
|
|
501
|
+
download: "",
|
|
502
|
+
size: finalSize,
|
|
503
|
+
modified: entry.timestamp,
|
|
504
|
+
mime_type: entry.captureType === "video" ? "video/mp4" : "image/jpeg",
|
|
505
|
+
is_video: entry.captureType === "video",
|
|
506
|
+
filePath: finalPath,
|
|
507
|
+
},
|
|
508
|
+
finalPath,
|
|
509
|
+
entry.thumbnailPath,
|
|
510
|
+
)
|
|
511
|
+
recovered.capture_id = entry.captureId
|
|
512
|
+
recovered.assetReceipt = entry.assetReceipt
|
|
513
|
+
await localStorageService.saveDownloadedFile(recovered)
|
|
514
|
+
return recovered
|
|
368
515
|
}
|
|
369
516
|
}
|
|
370
517
|
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type {AudioSource} from "expo-audio"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A tiny bundled source used to detach completed or interrupted audio without
|
|
5
|
+
* destroying the reusable native player. If a media control later sends Play,
|
|
6
|
+
* only this silence can be resumed instead of the previous miniapp audio.
|
|
7
|
+
*/
|
|
8
|
+
export const SILENT_AUDIO_SOURCE: AudioSource = require("../assets/silence.wav")
|
|
@@ -32,7 +32,7 @@ export function isSyncManifestEmpty(syncData: {
|
|
|
32
32
|
captures?: unknown[]
|
|
33
33
|
changed_files?: unknown[]
|
|
34
34
|
}): boolean {
|
|
35
|
-
if (syncData.api_version
|
|
35
|
+
if ((syncData.api_version || 0) >= 2 && syncData.captures && syncData.captures.length > 0) {
|
|
36
36
|
return false
|
|
37
37
|
}
|
|
38
38
|
return !syncData.changed_files || syncData.changed_files.length === 0
|
|
@@ -6,7 +6,7 @@ import BluetoothSdk from "@mentra/bluetooth-sdk/internal"
|
|
|
6
6
|
import {BgTimer} from "../utils/timers"
|
|
7
7
|
import {useGlassesStore} from "../stores/glasses"
|
|
8
8
|
|
|
9
|
-
import {
|
|
9
|
+
import {resolveOtaManifestUrl} from "./otaManifestUrl"
|
|
10
10
|
import {detectClockSkew} from "./gallerySyncClock"
|
|
11
11
|
|
|
12
12
|
export const CLOCK_SETTLE_MS = 500
|
|
@@ -86,7 +86,7 @@ export async function handleOtaClockSkewFromGlasses(
|
|
|
86
86
|
|
|
87
87
|
lastOtaClockFixAt = Date.now()
|
|
88
88
|
const {buildNumber, otaVersionUrl} = useGlassesStore.getState()
|
|
89
|
-
const manifestUrl =
|
|
89
|
+
const manifestUrl = resolveOtaManifestUrl(otaVersionUrl, buildNumber)
|
|
90
90
|
console.log("[GlassesClockSync] ⏰ Restarting OTA with ota_start after clock fix")
|
|
91
91
|
await BluetoothSdk.startOtaUpdate(manifestUrl)
|
|
92
92
|
return true
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type {DeclaredAction} from "../types/applet"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Normalize manifest actions defensively for installed and dev-sideloaded
|
|
5
|
+
* bundles. Input and output schemas are passed through as opaque JSON Schema.
|
|
6
|
+
*/
|
|
7
|
+
export function normalizeManifestActions(raw: unknown): DeclaredAction[] {
|
|
8
|
+
if (!Array.isArray(raw)) return []
|
|
9
|
+
const out: DeclaredAction[] = []
|
|
10
|
+
for (const a of raw as Array<{
|
|
11
|
+
id?: unknown
|
|
12
|
+
description?: unknown
|
|
13
|
+
parameters?: unknown
|
|
14
|
+
outputSchema?: unknown
|
|
15
|
+
}>) {
|
|
16
|
+
if (a && typeof a.id === "string" && typeof a.description === "string") {
|
|
17
|
+
out.push({
|
|
18
|
+
id: a.id,
|
|
19
|
+
description: a.description,
|
|
20
|
+
...(a.parameters && typeof a.parameters === "object" && !Array.isArray(a.parameters)
|
|
21
|
+
? {parameters: a.parameters as Record<string, unknown>}
|
|
22
|
+
: {}),
|
|
23
|
+
...(a.outputSchema && typeof a.outputSchema === "object" && !Array.isArray(a.outputSchema)
|
|
24
|
+
? {outputSchema: a.outputSchema as Record<string, unknown>}
|
|
25
|
+
: {}),
|
|
26
|
+
})
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return out
|
|
30
|
+
}
|
|
@@ -18,38 +18,80 @@ export type DisplayState = "starting" | "updating" | "complete" | "failed" | "di
|
|
|
18
18
|
*
|
|
19
19
|
* Priority-ordered rules (first match wins):
|
|
20
20
|
* 1. errorMsg !== "" -> "failed"
|
|
21
|
-
* 2. BES
|
|
22
|
-
* 3. BES
|
|
21
|
+
* 2. BES reboot recovery complete -> "complete"
|
|
22
|
+
* 3. BES reboot recovery awaiting reconnect -> "restarting"
|
|
23
|
+
* 4. BES terminal (any connection state) -> "restarting"
|
|
23
24
|
* (legacy-shaped BES "complete" counts only in install phase)
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
25
|
+
* 6. apkCompletedViaBuildIncrease (legacy build-number port) -> "complete"
|
|
26
|
+
* 7. otaStatus.status === "failed" -> "failed"
|
|
27
|
+
* 8. otaStatus.status === "complete" (non-BES) -> "complete"
|
|
27
28
|
* (unless legacy-shaped download-complete or held apk settle -> in flight)
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
29
|
+
* 9. !connected + legacy in-flight complete (held apk) -> "updating"
|
|
30
|
+
* 10. !connected + APK step_complete with totalSteps > 1 -> "starting" (expected reboot)
|
|
31
|
+
* 11. !connected + BES in flight -> "restarting" (defensive)
|
|
32
|
+
* 12. !connected -> "disconnected"
|
|
33
|
+
* 13. connected + in_progress | step_complete | legacy in-flight complete -> "updating"
|
|
34
|
+
* 14. fallback -> "starting"
|
|
34
35
|
*/
|
|
35
36
|
export function deriveDisplayState(args: {
|
|
36
37
|
otaStatus: OtaStatus | null
|
|
37
38
|
otaProgress: OtaProgress | null
|
|
38
39
|
connected: boolean
|
|
39
40
|
errorMsg: string
|
|
41
|
+
/**
|
|
42
|
+
* Retained for host API compatibility. A session-wide reconnect edge is not
|
|
43
|
+
* sufficient for BES completion because it may belong to an earlier APK step.
|
|
44
|
+
*/
|
|
40
45
|
sawReconnectEdge: boolean
|
|
41
46
|
/** Coordinator is holding a legacy apk install "complete" for the settle window (WP 8C). */
|
|
42
47
|
legacyApkSettleHold?: boolean
|
|
43
48
|
/** Legacy APK completion detected by build-number increase (WP 8C). */
|
|
44
49
|
apkCompletedViaBuildIncrease?: boolean
|
|
50
|
+
/** Phone-observed BES disconnect/reconnect recovery, independent of stale OTA progress. */
|
|
51
|
+
besRestartRecovery?: "awaiting" | "complete" | null
|
|
52
|
+
/** Downgrade detour: the reconnected glasses reported exactly the pinned target version. */
|
|
53
|
+
versionChangeConverged?: boolean
|
|
54
|
+
/** True for the whole downgrade (version-change) session. */
|
|
55
|
+
versionChangeSession?: boolean
|
|
45
56
|
}): DisplayState {
|
|
46
|
-
const {
|
|
47
|
-
|
|
57
|
+
const {
|
|
58
|
+
otaStatus,
|
|
59
|
+
otaProgress,
|
|
60
|
+
connected,
|
|
61
|
+
errorMsg,
|
|
62
|
+
legacyApkSettleHold,
|
|
63
|
+
apkCompletedViaBuildIncrease,
|
|
64
|
+
besRestartRecovery,
|
|
65
|
+
versionChangeConverged,
|
|
66
|
+
versionChangeSession,
|
|
67
|
+
} = args
|
|
48
68
|
|
|
49
69
|
if (errorMsg) return "failed"
|
|
50
70
|
|
|
71
|
+
// Downgrade detour completion: exact-version convergence after reconnect is the
|
|
72
|
+
// only completion signal (the wipe destroys ASG's ota_session, and the target is
|
|
73
|
+
// a LOWER build so no build-number increase fires). Outranks the stale install
|
|
74
|
+
// status still sitting in the store from before the handoff.
|
|
75
|
+
if (versionChangeConverged) return "complete"
|
|
76
|
+
|
|
77
|
+
// A BES reboot destroys or strands the glasses-side OTA session on older
|
|
78
|
+
// clients. Once the phone observed that reboot, stale pre-reboot progress must
|
|
79
|
+
// not move the UI from restarting back to updating.
|
|
80
|
+
if (besRestartRecovery === "complete") return "complete"
|
|
81
|
+
if (besRestartRecovery === "awaiting") return "restarting"
|
|
82
|
+
|
|
83
|
+
// In a version-change session, convergence (above) is the ONLY completion for the ASG
|
|
84
|
+
// downgrade. A bare non-firmware "complete" before convergence — the glasses refusing
|
|
85
|
+
// the downgrade (floor/gate) with no install, or a stale pre-handoff status — must never
|
|
86
|
+
// present as success. Firmware-step completions (mtk/bes) DO pass through: a combined
|
|
87
|
+
// downgrade+firmware manifest applies firmware first on its own pass, which must finish
|
|
88
|
+
// so the phone re-checks and re-offers the now-firmware-free downgrade.
|
|
89
|
+
const isFirmwareComplete = otaStatus?.stepType === "mtk" || otaStatus?.stepType === "bes"
|
|
90
|
+
if (versionChangeSession && otaStatus?.status === "complete" && !isFirmwareComplete) {
|
|
91
|
+
return connected ? "updating" : "disconnected"
|
|
92
|
+
}
|
|
93
|
+
|
|
51
94
|
const besTerminal = isBesTerminal(otaStatus, otaProgress)
|
|
52
|
-
if (besTerminal && connected && sawReconnectEdge) return "complete"
|
|
53
95
|
if (besTerminal) return "restarting"
|
|
54
96
|
|
|
55
97
|
// Legacy build-number APK completion: the coordinator clears errorMsg when it fires,
|
|
@@ -85,8 +127,7 @@ export function deriveDisplayState(args: {
|
|
|
85
127
|
// has not started); the legacy screen treated that drop as a plain disconnect.
|
|
86
128
|
const besStatusInFlight =
|
|
87
129
|
otaStatus?.stepType === "bes" && !(isLegacyShapedOtaStatus(otaStatus) && otaStatus.phase === "download")
|
|
88
|
-
const besInFlight =
|
|
89
|
-
besStatusInFlight || (otaProgress?.currentUpdate === "bes" && otaProgress?.stage === "install")
|
|
130
|
+
const besInFlight = besStatusInFlight || (otaProgress?.currentUpdate === "bes" && otaProgress?.stage === "install")
|
|
90
131
|
if (besInFlight) return "restarting"
|
|
91
132
|
|
|
92
133
|
return "disconnected"
|
|
@@ -10,6 +10,7 @@ import type {OtaProgress, OtaStatus} from "@mentra/bluetooth-sdk/internal"
|
|
|
10
10
|
export const MINIMUM_OTA_STATUS_BUILD = 37
|
|
11
11
|
|
|
12
12
|
export const MAX_RETRIES = 3
|
|
13
|
+
/** Wait between ended native ota_start requests; never overlaps a pending request. */
|
|
13
14
|
export const RETRY_INTERVAL_MS = 5000
|
|
14
15
|
/** APK/BES and general install when progress events are expected */
|
|
15
16
|
export const PROGRESS_TIMEOUT_MS = 120_000
|
|
@@ -19,8 +20,6 @@ export const DOWNLOAD_STUCK_TIMEOUT_MS = 70_000
|
|
|
19
20
|
export const MTK_INSTALL_TIMEOUT_MS = 300_000
|
|
20
21
|
/** Whole multi-step session cap */
|
|
21
22
|
export const GLOBAL_OTA_TIMEOUT_MS = 20 * 60 * 1000
|
|
22
|
-
/** After APK reboot, ASG OTA service needs time before next ota_start */
|
|
23
|
-
export const POST_APK_OTA_START_DELAY_MS = 6000
|
|
24
23
|
/** BLE keepalive during OTA */
|
|
25
24
|
export const PING_INTERVAL_MS = 10_000
|
|
26
25
|
/**
|
|
@@ -71,6 +70,8 @@ export const LEGACY_MTK_STALL_ZONE_MAX_PERCENT = 55
|
|
|
71
70
|
export const LEGACY_MTK_SIM_FLOOR_PERCENT = 51
|
|
72
71
|
export const LEGACY_MTK_SIM_CAP_PERCENT = 60
|
|
73
72
|
|
|
73
|
+
export type OtaProtocolProfile = "legacy" | "unified"
|
|
74
|
+
|
|
74
75
|
/**
|
|
75
76
|
* Legacy-shaped ota_status: old (< 37) ASG builds send `ota_progress`, which the SDK
|
|
76
77
|
* (Android MentraLive.kt / iOS MentraLive.swift) maps to a unified ota_status with
|
|
@@ -98,9 +99,34 @@ export function isLegacyShapedOtaSession(
|
|
|
98
99
|
return Number.isFinite(build) && build > 0 && build < MINIMUM_OTA_STATUS_BUILD
|
|
99
100
|
}
|
|
100
101
|
|
|
102
|
+
/**
|
|
103
|
+
* Select the compatibility profile once at install-screen attach. The old split
|
|
104
|
+
* routes made this decision from the pre-update build and kept it for the whole
|
|
105
|
+
* mounted flow, even when an APK step restarted into a newer ASG build. Returning
|
|
106
|
+
* null leaves the coordinator undecided until the first meaningful OTA event.
|
|
107
|
+
*/
|
|
108
|
+
export function selectOtaProtocolProfile(
|
|
109
|
+
otaStatus: OtaStatus | null,
|
|
110
|
+
otaProgress: OtaProgress | null,
|
|
111
|
+
buildNumber: string | null | undefined,
|
|
112
|
+
): OtaProtocolProfile | null {
|
|
113
|
+
const build = Number.parseInt(buildNumber ?? "", 10)
|
|
114
|
+
if (Number.isFinite(build) && build > 0) {
|
|
115
|
+
return build < MINIMUM_OTA_STATUS_BUILD ? "legacy" : "unified"
|
|
116
|
+
}
|
|
117
|
+
if (otaStatus) return isLegacyShapedOtaStatus(otaStatus) ? "legacy" : "unified"
|
|
118
|
+
if (otaProgress) return "legacy"
|
|
119
|
+
return null
|
|
120
|
+
}
|
|
121
|
+
|
|
101
122
|
export const OtaProgressMessages = {
|
|
102
|
-
noAckResponse: "Unable to start update. Glasses did not respond.",
|
|
103
123
|
stalledOrStuck: "Update may have failed. Ensure glasses have internet access and try again.",
|
|
104
124
|
globalTimeout: "Update took too long. Please try again.",
|
|
105
125
|
sendOtaStartFailed: "Failed to communicate with glasses.",
|
|
126
|
+
hotspotArtifactDownloadFailed: "Could not download the update to your phone. Check phone internet and try again.",
|
|
127
|
+
hotspotArtifactVerifyFailed: "The update package could not be verified. Please try again later.",
|
|
128
|
+
hotspotWifiPermissionDenied: "Allow nearby devices access so the phone can connect to the glasses hotspot.",
|
|
129
|
+
hotspotStartFailed: "Could not start the glasses hotspot. Please try again.",
|
|
130
|
+
hotspotJoinFailed: "Could not connect the phone to the glasses hotspot. Please try again.",
|
|
131
|
+
hotspotServerFailed: "Could not make the update available to the glasses. Please try again.",
|
|
106
132
|
} as const
|
|
@@ -1,6 +1,13 @@
|
|
|
1
|
+
import {sdkPinnedOtaManifestUrl} from "@mentra/bluetooth-sdk/internal"
|
|
2
|
+
|
|
1
3
|
import {SETTINGS, useSettingsStore} from "../stores/settings"
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
// Legacy production manifest — pre-39 glasses install from this (they ignore ota_start's URL).
|
|
6
|
+
const OTA_VERSION_URL_LEGACY_PROD = "https://ota.mentraglass.com/prod_live_version.json"
|
|
7
|
+
// Modern production fleet manifest (v2). Only a last-resort fallback when the SDK-derived pin
|
|
8
|
+
// URL is somehow unavailable; modern glasses honor ota_start's URL, so this must be the v2
|
|
9
|
+
// artifact, not the legacy v1 rescue manifest.
|
|
10
|
+
const OTA_VERSION_URL_PROD = "https://ota.mentraglass.com/prod_live_version_v2.json"
|
|
4
11
|
|
|
5
12
|
function isLegacyAsgOtaStartBuild(glassesBuildNumber?: string | null): boolean {
|
|
6
13
|
const buildNumber = Number.parseInt(glassesBuildNumber ?? "", 10)
|
|
@@ -20,12 +27,12 @@ function getOtaVersionUrlDevOverride(): string | null {
|
|
|
20
27
|
return trimmed || null
|
|
21
28
|
}
|
|
22
29
|
|
|
23
|
-
export function
|
|
30
|
+
export function resolveOtaManifestUrl(glassesUrl?: string | null, glassesBuildNumber?: string | null): string {
|
|
24
31
|
const deviceUrl = glassesUrl?.trim()
|
|
25
32
|
if (isLegacyAsgOtaStartBuild(glassesBuildNumber)) {
|
|
26
33
|
// Legacy glasses ignore ota_start.ota_version_url and install from their
|
|
27
34
|
// compiled default, so the developer override does not apply to them either.
|
|
28
|
-
return deviceUrl ||
|
|
35
|
+
return deviceUrl || OTA_VERSION_URL_LEGACY_PROD
|
|
29
36
|
}
|
|
30
37
|
|
|
31
38
|
const devOverrideUrl = getOtaVersionUrlDevOverride()
|
|
@@ -37,5 +44,10 @@ export function getAsgOtaVersionUrl(glassesUrl?: string | null, glassesBuildNumb
|
|
|
37
44
|
if (envUrl) {
|
|
38
45
|
return envUrl
|
|
39
46
|
}
|
|
40
|
-
|
|
47
|
+
|
|
48
|
+
// The phone owns manifest selection: modern glasses are driven with the manifest pinned to
|
|
49
|
+
// this app's Bluetooth SDK version. Modern ASG builds report no manifest URL of their own;
|
|
50
|
+
// a glasses-reported URL (older modern builds advertising their baked fleet default) is only
|
|
51
|
+
// a last resort when the SDK version is somehow unavailable.
|
|
52
|
+
return sdkPinnedOtaManifestUrl() || deviceUrl || OTA_VERSION_URL_PROD
|
|
41
53
|
}
|