@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
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { MiniappErrorCode } from "@mentra/miniapp";
|
|
2
|
+
const DEFAULT_LIMIT = 50;
|
|
3
|
+
const MAX_LIMIT = 100;
|
|
4
|
+
const MAX_RANGE_MS = 31 * 24 * 60 * 60 * 1000;
|
|
5
|
+
export class PhoneCalendarError extends Error {
|
|
6
|
+
code;
|
|
7
|
+
constructor(code, message) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.code = code;
|
|
10
|
+
this.name = "PhoneCalendarError";
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export function validateCalendarListRequest(request) {
|
|
14
|
+
const startsAt = parseDate(request.startsAt, "startsAt");
|
|
15
|
+
const endsAt = parseDate(request.endsAt, "endsAt");
|
|
16
|
+
if (endsAt.getTime() <= startsAt.getTime()) {
|
|
17
|
+
throw new PhoneCalendarError(MiniappErrorCode.INVALID_ARGUMENT, "endsAt must be after startsAt");
|
|
18
|
+
}
|
|
19
|
+
if (endsAt.getTime() - startsAt.getTime() > MAX_RANGE_MS) {
|
|
20
|
+
throw new PhoneCalendarError(MiniappErrorCode.INVALID_ARGUMENT, "Calendar query range may not exceed 31 days");
|
|
21
|
+
}
|
|
22
|
+
const limit = request.limit === undefined ? DEFAULT_LIMIT : request.limit;
|
|
23
|
+
if (typeof limit !== "number" || !Number.isInteger(limit) || limit < 1 || limit > MAX_LIMIT) {
|
|
24
|
+
throw new PhoneCalendarError(MiniappErrorCode.INVALID_ARGUMENT, `limit must be an integer between 1 and ${MAX_LIMIT}`);
|
|
25
|
+
}
|
|
26
|
+
return { startsAt, endsAt, limit };
|
|
27
|
+
}
|
|
28
|
+
function parseDate(value, field) {
|
|
29
|
+
if (typeof value !== "string") {
|
|
30
|
+
throw new PhoneCalendarError(MiniappErrorCode.INVALID_ARGUMENT, `${field} must be an ISO 8601 string`);
|
|
31
|
+
}
|
|
32
|
+
const date = new Date(value);
|
|
33
|
+
if (Number.isNaN(date.getTime())) {
|
|
34
|
+
throw new PhoneCalendarError(MiniappErrorCode.INVALID_ARGUMENT, `${field} must be a valid ISO 8601 string`);
|
|
35
|
+
}
|
|
36
|
+
return date;
|
|
37
|
+
}
|
|
38
|
+
export function extractHttpsLinks(...values) {
|
|
39
|
+
const links = new Set();
|
|
40
|
+
for (const value of values) {
|
|
41
|
+
for (const match of value?.match(/https:\/\/[^\s<>"']+/gi) ?? []) {
|
|
42
|
+
let link = match;
|
|
43
|
+
while (".,;:!?)]}".includes(link[link.length - 1] ?? ""))
|
|
44
|
+
link = link.slice(0, -1);
|
|
45
|
+
if (link)
|
|
46
|
+
links.add(link);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return [...links];
|
|
50
|
+
}
|
|
51
|
+
function localDateParts(date) {
|
|
52
|
+
return {
|
|
53
|
+
year: date.getFullYear(),
|
|
54
|
+
month: date.getMonth() + 1,
|
|
55
|
+
day: date.getDate(),
|
|
56
|
+
hour: date.getHours(),
|
|
57
|
+
minute: date.getMinutes(),
|
|
58
|
+
second: date.getSeconds(),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function datePartsInTimeZone(date, timeZone) {
|
|
62
|
+
try {
|
|
63
|
+
const parts = new Intl.DateTimeFormat("en-US", {
|
|
64
|
+
timeZone,
|
|
65
|
+
year: "numeric",
|
|
66
|
+
month: "2-digit",
|
|
67
|
+
day: "2-digit",
|
|
68
|
+
hour: "2-digit",
|
|
69
|
+
minute: "2-digit",
|
|
70
|
+
second: "2-digit",
|
|
71
|
+
hourCycle: "h23",
|
|
72
|
+
}).formatToParts(date);
|
|
73
|
+
const value = (type) => Number(parts.find((part) => part.type === type)?.value);
|
|
74
|
+
return {
|
|
75
|
+
year: value("year"),
|
|
76
|
+
month: value("month"),
|
|
77
|
+
day: value("day"),
|
|
78
|
+
hour: value("hour"),
|
|
79
|
+
minute: value("minute"),
|
|
80
|
+
second: value("second"),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function timeZoneOffsetMinutes(date, timeZone) {
|
|
88
|
+
const parts = datePartsInTimeZone(date, timeZone);
|
|
89
|
+
if (!parts)
|
|
90
|
+
return undefined;
|
|
91
|
+
const representedAsUtc = Date.UTC(parts.year, parts.month - 1, parts.day, parts.hour, parts.minute, parts.second, date.getUTCMilliseconds());
|
|
92
|
+
return Math.round((representedAsUtc - date.getTime()) / 60_000);
|
|
93
|
+
}
|
|
94
|
+
function pad(value, length = 2) {
|
|
95
|
+
return String(value).padStart(length, "0");
|
|
96
|
+
}
|
|
97
|
+
function formatOffset(offsetMinutes) {
|
|
98
|
+
if (offsetMinutes === 0)
|
|
99
|
+
return "Z";
|
|
100
|
+
const sign = offsetMinutes < 0 ? "-" : "+";
|
|
101
|
+
const absolute = Math.abs(offsetMinutes);
|
|
102
|
+
return `${sign}${pad(Math.floor(absolute / 60))}:${pad(absolute % 60)}`;
|
|
103
|
+
}
|
|
104
|
+
function formatAllDayDate(value, timeZone) {
|
|
105
|
+
const dateOnly = typeof value === "string" ? /^(\d{4})-(\d{2})-(\d{2})$/.exec(value) : null;
|
|
106
|
+
if (dateOnly) {
|
|
107
|
+
const [, year, month, day] = dateOnly;
|
|
108
|
+
const localMidnight = new Date(Number(year), Number(month) - 1, Number(day));
|
|
109
|
+
let offsetMinutes = -localMidnight.getTimezoneOffset();
|
|
110
|
+
if (timeZone) {
|
|
111
|
+
// Resolve the offset at midnight in the event timezone. Repeating once
|
|
112
|
+
// accounts for the initial UTC guess landing across a DST boundary.
|
|
113
|
+
const wallTime = Date.UTC(Number(year), Number(month) - 1, Number(day));
|
|
114
|
+
let instant = new Date(wallTime);
|
|
115
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
116
|
+
const offset = timeZoneOffsetMinutes(instant, timeZone);
|
|
117
|
+
if (offset === undefined)
|
|
118
|
+
break;
|
|
119
|
+
offsetMinutes = offset;
|
|
120
|
+
instant = new Date(wallTime - offset * 60_000);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return `${year}-${month}-${day}T00:00:00.000${formatOffset(offsetMinutes)}`;
|
|
124
|
+
}
|
|
125
|
+
const date = new Date(value);
|
|
126
|
+
const zonedParts = timeZone ? datePartsInTimeZone(date, timeZone) : undefined;
|
|
127
|
+
const parts = zonedParts ?? localDateParts(date);
|
|
128
|
+
const offsetMinutes = timeZone && zonedParts
|
|
129
|
+
? (timeZoneOffsetMinutes(date, timeZone) ?? -date.getTimezoneOffset())
|
|
130
|
+
: -date.getTimezoneOffset();
|
|
131
|
+
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)}`;
|
|
132
|
+
}
|
|
133
|
+
export function normalizeCalendarEvent(event) {
|
|
134
|
+
const startsAt = event.allDay
|
|
135
|
+
? formatAllDayDate(event.startDate, event.timeZone)
|
|
136
|
+
: new Date(event.startDate).toISOString();
|
|
137
|
+
const endsAt = event.allDay ? formatAllDayDate(event.endDate, event.timeZone) : new Date(event.endDate).toISOString();
|
|
138
|
+
const calendarId = event.calendarId ?? "";
|
|
139
|
+
const eventId = event.id ?? "";
|
|
140
|
+
return {
|
|
141
|
+
id: `${calendarId}:${eventId}:${startsAt}`,
|
|
142
|
+
calendarId,
|
|
143
|
+
title: event.title ?? "",
|
|
144
|
+
startsAt,
|
|
145
|
+
endsAt,
|
|
146
|
+
...(event.timeZone ? { timezone: event.timeZone } : {}),
|
|
147
|
+
allDay: !!event.allDay,
|
|
148
|
+
...(event.location ? { location: event.location } : {}),
|
|
149
|
+
...(event.notes ? { notes: event.notes } : {}),
|
|
150
|
+
...(event.url ? { url: event.url } : {}),
|
|
151
|
+
links: extractHttpsLinks(event.url, event.location, event.notes),
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
export function calendarEventOverlapsWindow(event, startsAt, endsAt) {
|
|
155
|
+
return Date.parse(event.endsAt) > startsAt.getTime() && Date.parse(event.startsAt) < endsAt.getTime();
|
|
156
|
+
}
|
|
157
|
+
//# sourceMappingURL=PhoneCalendarHelpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PhoneCalendarHelpers.js","sourceRoot":"","sources":["../../src/services/PhoneCalendarHelpers.ts"],"names":[],"mappings":"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","sourcesContent":["import {MiniappErrorCode, type CalendarEvent} from \"@mentra/miniapp\"\n\nconst DEFAULT_LIMIT = 50\nconst MAX_LIMIT = 100\nconst MAX_RANGE_MS = 31 * 24 * 60 * 60 * 1000\n\nexport interface CalendarListRequest {\n startsAt?: unknown\n endsAt?: unknown\n limit?: unknown\n}\n\nexport class PhoneCalendarError extends Error {\n constructor(\n public readonly code: MiniappErrorCode,\n message: string,\n ) {\n super(message)\n this.name = \"PhoneCalendarError\"\n }\n}\n\nexport interface ValidatedCalendarListRequest {\n startsAt: Date\n endsAt: Date\n limit: number\n}\n\nexport interface RawCalendarEvent {\n id?: string\n calendarId?: string\n title?: string | null\n startDate: string | Date\n endDate: string | Date\n timeZone?: string | null\n allDay?: boolean\n location?: string | null\n notes?: string | null\n url?: string | null\n}\n\nexport function validateCalendarListRequest(request: CalendarListRequest): ValidatedCalendarListRequest {\n const startsAt = parseDate(request.startsAt, \"startsAt\")\n const endsAt = parseDate(request.endsAt, \"endsAt\")\n if (endsAt.getTime() <= startsAt.getTime()) {\n throw new PhoneCalendarError(MiniappErrorCode.INVALID_ARGUMENT, \"endsAt must be after startsAt\")\n }\n if (endsAt.getTime() - startsAt.getTime() > MAX_RANGE_MS) {\n throw new PhoneCalendarError(MiniappErrorCode.INVALID_ARGUMENT, \"Calendar query range may not exceed 31 days\")\n }\n\n const limit = request.limit === undefined ? DEFAULT_LIMIT : request.limit\n if (typeof limit !== \"number\" || !Number.isInteger(limit) || limit < 1 || limit > MAX_LIMIT) {\n throw new PhoneCalendarError(\n MiniappErrorCode.INVALID_ARGUMENT,\n `limit must be an integer between 1 and ${MAX_LIMIT}`,\n )\n }\n return {startsAt, endsAt, limit}\n}\n\nfunction parseDate(value: unknown, field: string): Date {\n if (typeof value !== \"string\") {\n throw new PhoneCalendarError(MiniappErrorCode.INVALID_ARGUMENT, `${field} must be an ISO 8601 string`)\n }\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) {\n throw new PhoneCalendarError(MiniappErrorCode.INVALID_ARGUMENT, `${field} must be a valid ISO 8601 string`)\n }\n return date\n}\n\nexport function extractHttpsLinks(...values: Array<string | null | undefined>): string[] {\n const links = new Set<string>()\n for (const value of values) {\n for (const match of value?.match(/https:\\/\\/[^\\s<>\"']+/gi) ?? []) {\n let link = match\n while (\".,;:!?)]}\".includes(link[link.length - 1] ?? \"\")) link = link.slice(0, -1)\n if (link) links.add(link)\n }\n }\n return [...links]\n}\n\ninterface CalendarDateParts {\n year: number\n month: number\n day: number\n hour: number\n minute: number\n second: number\n}\n\nfunction localDateParts(date: Date): CalendarDateParts {\n return {\n year: date.getFullYear(),\n month: date.getMonth() + 1,\n day: date.getDate(),\n hour: date.getHours(),\n minute: date.getMinutes(),\n second: date.getSeconds(),\n }\n}\n\nfunction datePartsInTimeZone(date: Date, timeZone: string): CalendarDateParts | undefined {\n try {\n const parts = new Intl.DateTimeFormat(\"en-US\", {\n timeZone,\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\",\n hourCycle: \"h23\",\n }).formatToParts(date)\n const value = (type: Intl.DateTimeFormatPartTypes) => Number(parts.find((part) => part.type === type)?.value)\n return {\n year: value(\"year\"),\n month: value(\"month\"),\n day: value(\"day\"),\n hour: value(\"hour\"),\n minute: value(\"minute\"),\n second: value(\"second\"),\n }\n } catch {\n return undefined\n }\n}\n\nfunction timeZoneOffsetMinutes(date: Date, timeZone: string): number | undefined {\n const parts = datePartsInTimeZone(date, timeZone)\n if (!parts) return undefined\n const representedAsUtc = Date.UTC(\n parts.year,\n parts.month - 1,\n parts.day,\n parts.hour,\n parts.minute,\n parts.second,\n date.getUTCMilliseconds(),\n )\n return Math.round((representedAsUtc - date.getTime()) / 60_000)\n}\n\nfunction pad(value: number, length = 2): string {\n return String(value).padStart(length, \"0\")\n}\n\nfunction formatOffset(offsetMinutes: number): string {\n if (offsetMinutes === 0) return \"Z\"\n const sign = offsetMinutes < 0 ? \"-\" : \"+\"\n const absolute = Math.abs(offsetMinutes)\n return `${sign}${pad(Math.floor(absolute / 60))}:${pad(absolute % 60)}`\n}\n\nfunction formatAllDayDate(value: string | Date, timeZone?: string | null): string {\n const dateOnly = typeof value === \"string\" ? /^(\\d{4})-(\\d{2})-(\\d{2})$/.exec(value) : null\n if (dateOnly) {\n const [, year, month, day] = dateOnly\n const localMidnight = new Date(Number(year), Number(month) - 1, Number(day))\n let offsetMinutes = -localMidnight.getTimezoneOffset()\n if (timeZone) {\n // Resolve the offset at midnight in the event timezone. Repeating once\n // accounts for the initial UTC guess landing across a DST boundary.\n const wallTime = Date.UTC(Number(year), Number(month) - 1, Number(day))\n let instant = new Date(wallTime)\n for (let attempt = 0; attempt < 2; attempt += 1) {\n const offset = timeZoneOffsetMinutes(instant, timeZone)\n if (offset === undefined) break\n offsetMinutes = offset\n instant = new Date(wallTime - offset * 60_000)\n }\n }\n return `${year}-${month}-${day}T00:00:00.000${formatOffset(offsetMinutes)}`\n }\n\n const date = new Date(value)\n const zonedParts = timeZone ? datePartsInTimeZone(date, timeZone) : undefined\n const parts = zonedParts ?? localDateParts(date)\n const offsetMinutes =\n timeZone && zonedParts\n ? (timeZoneOffsetMinutes(date, timeZone) ?? -date.getTimezoneOffset())\n : -date.getTimezoneOffset()\n 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)}`\n}\n\nexport function normalizeCalendarEvent(event: RawCalendarEvent): CalendarEvent {\n const startsAt = event.allDay\n ? formatAllDayDate(event.startDate, event.timeZone)\n : new Date(event.startDate).toISOString()\n const endsAt = event.allDay ? formatAllDayDate(event.endDate, event.timeZone) : new Date(event.endDate).toISOString()\n const calendarId = event.calendarId ?? \"\"\n const eventId = event.id ?? \"\"\n return {\n id: `${calendarId}:${eventId}:${startsAt}`,\n calendarId,\n title: event.title ?? \"\",\n startsAt,\n endsAt,\n ...(event.timeZone ? {timezone: event.timeZone} : {}),\n allDay: !!event.allDay,\n ...(event.location ? {location: event.location} : {}),\n ...(event.notes ? {notes: event.notes} : {}),\n ...(event.url ? {url: event.url} : {}),\n links: extractHttpsLinks(event.url, event.location, event.notes),\n }\n}\n\nexport function calendarEventOverlapsWindow(event: CalendarEvent, startsAt: Date, endsAt: Date): boolean {\n return Date.parse(event.endsAt) > startsAt.getTime() && Date.parse(event.startsAt) < endsAt.getTime()\n}\n"]}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type CalendarListResult } from "@mentra/miniapp";
|
|
2
|
+
import { type CalendarListRequest } from "./PhoneCalendarHelpers";
|
|
3
|
+
export { PhoneCalendarError } from "./PhoneCalendarHelpers";
|
|
4
|
+
export declare function listPhoneCalendarEvents(request: CalendarListRequest): Promise<CalendarListResult>;
|
|
5
|
+
//# sourceMappingURL=PhoneCalendarService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PhoneCalendarService.d.ts","sourceRoot":"","sources":["../../src/services/PhoneCalendarService.ts"],"names":[],"mappings":"AAIA,OAAO,EAAmB,KAAK,kBAAkB,EAAC,MAAM,iBAAiB,CAAA;AAEzE,OAAO,EAEL,KAAK,mBAAmB,EAIzB,MAAM,wBAAwB,CAAA;AAE/B,OAAO,EAAC,kBAAkB,EAAC,MAAM,wBAAwB,CAAA;AA0BzD,wBAAsB,uBAAuB,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAwBvG"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import * as Calendar from "expo-calendar";
|
|
2
|
+
import { Platform } from "react-native";
|
|
3
|
+
import { check, PERMISSIONS, RESULTS } from "react-native-permissions";
|
|
4
|
+
import { MiniappErrorCode } from "@mentra/miniapp";
|
|
5
|
+
import { calendarEventOverlapsWindow, normalizeCalendarEvent, PhoneCalendarError, validateCalendarListRequest, } from "./PhoneCalendarHelpers";
|
|
6
|
+
export { PhoneCalendarError } from "./PhoneCalendarHelpers";
|
|
7
|
+
/**
|
|
8
|
+
* expo-calendar reports anything below EventKit full access as not granted,
|
|
9
|
+
* so distinguish the states the user can actually fix. "Reopen the miniapp"
|
|
10
|
+
* is only true while the permission is undetermined — once iOS has resolved
|
|
11
|
+
* it (denied or write-only/limited), only Settings can change it.
|
|
12
|
+
*/
|
|
13
|
+
async function calendarPermissionMessage(status) {
|
|
14
|
+
if (status === "undetermined") {
|
|
15
|
+
return "Calendar permission has not been granted yet. Reopen the miniapp and allow calendar access.";
|
|
16
|
+
}
|
|
17
|
+
if (Platform.OS === "ios") {
|
|
18
|
+
const writeOnly = await check(PERMISSIONS.IOS.CALENDARS_WRITE_ONLY).catch(() => null);
|
|
19
|
+
if (writeOnly === RESULTS.GRANTED || writeOnly === RESULTS.LIMITED) {
|
|
20
|
+
return 'Calendar access is limited to write-only. Enable "Full Access" for Mentra in Settings › Privacy & Security › Calendars.';
|
|
21
|
+
}
|
|
22
|
+
return "Calendar access is denied. Enable it for Mentra in Settings › Privacy & Security › Calendars.";
|
|
23
|
+
}
|
|
24
|
+
return "Calendar access is denied. Enable the Calendar permission for Mentra in your phone's app settings.";
|
|
25
|
+
}
|
|
26
|
+
export async function listPhoneCalendarEvents(request) {
|
|
27
|
+
const { startsAt, endsAt, limit } = validateCalendarListRequest(request);
|
|
28
|
+
const permission = await Calendar.getCalendarPermissionsAsync();
|
|
29
|
+
if (permission.status !== "granted") {
|
|
30
|
+
throw new PhoneCalendarError(MiniappErrorCode.PERMISSION_DENIED, await calendarPermissionMessage(permission.status));
|
|
31
|
+
}
|
|
32
|
+
const calendars = await Calendar.getCalendarsAsync(Calendar.EntityTypes.EVENT);
|
|
33
|
+
if (calendars.length === 0)
|
|
34
|
+
return { events: [], truncated: false };
|
|
35
|
+
const rawEvents = await Calendar.getEventsAsync(calendars.map((calendar) => calendar.id), startsAt, endsAt);
|
|
36
|
+
const events = rawEvents
|
|
37
|
+
.map(normalizeCalendarEvent)
|
|
38
|
+
.filter((event) => calendarEventOverlapsWindow(event, startsAt, endsAt))
|
|
39
|
+
.sort((a, b) => Date.parse(a.startsAt) - Date.parse(b.startsAt) || Date.parse(a.endsAt) - Date.parse(b.endsAt));
|
|
40
|
+
return {
|
|
41
|
+
events: events.slice(0, limit),
|
|
42
|
+
truncated: events.length > limit,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=PhoneCalendarService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PhoneCalendarService.js","sourceRoot":"","sources":["../../src/services/PhoneCalendarService.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAA;AACzC,OAAO,EAAC,QAAQ,EAAC,MAAM,cAAc,CAAA;AACrC,OAAO,EAAC,KAAK,EAAE,WAAW,EAAE,OAAO,EAAC,MAAM,0BAA0B,CAAA;AAEpE,OAAO,EAAC,gBAAgB,EAA0B,MAAM,iBAAiB,CAAA;AAEzE,OAAO,EACL,2BAA2B,EAE3B,sBAAsB,EACtB,kBAAkB,EAClB,2BAA2B,GAC5B,MAAM,wBAAwB,CAAA;AAE/B,OAAO,EAAC,kBAAkB,EAAC,MAAM,wBAAwB,CAAA;AAMzD;;;;;GAKG;AACH,KAAK,UAAU,yBAAyB,CAAC,MAAgC;IACvE,IAAI,MAAM,KAAK,cAAc,EAAE,CAAC;QAC9B,OAAO,6FAA6F,CAAA;IACtG,CAAC;IACD,IAAI,QAAQ,CAAC,EAAE,KAAK,KAAK,EAAE,CAAC;QAC1B,MAAM,SAAS,GAAG,MAAM,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAA;QACrF,IAAI,SAAS,KAAK,OAAO,CAAC,OAAO,IAAI,SAAS,KAAK,OAAO,CAAC,OAAO,EAAE,CAAC;YACnE,OAAO,yHAAyH,CAAA;QAClI,CAAC;QACD,OAAO,+FAA+F,CAAA;IACxG,CAAC;IACD,OAAO,oGAAoG,CAAA;AAC7G,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,OAA4B;IACxE,MAAM,EAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAC,GAAG,2BAA2B,CAAC,OAAO,CAAC,CAAA;IACtE,MAAM,UAAU,GAAG,MAAM,QAAQ,CAAC,2BAA2B,EAAE,CAAA;IAC/D,IAAI,UAAU,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QACpC,MAAM,IAAI,kBAAkB,CAAC,gBAAgB,CAAC,iBAAiB,EAAE,MAAM,yBAAyB,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAA;IACtH,CAAC;IAED,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,iBAAiB,CAAC,QAAQ,CAAC,WAAW,CAAC,KAAK,CAAC,CAAA;IAC9E,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAC,MAAM,EAAE,EAAE,EAAE,SAAS,EAAE,KAAK,EAAC,CAAA;IAEjE,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,cAAc,CAC7C,SAAS,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,EACxC,QAAQ,EACR,MAAM,CACP,CAAA;IACD,MAAM,MAAM,GAAG,SAAS;SACrB,GAAG,CAAC,sBAAsB,CAAC;SAC3B,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,2BAA2B,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;SACvE,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAA;IAEjH,OAAO;QACL,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC;QAC9B,SAAS,EAAE,MAAM,CAAC,MAAM,GAAG,KAAK;KACjC,CAAA;AACH,CAAC","sourcesContent":["import * as Calendar from \"expo-calendar\"\nimport {Platform} from \"react-native\"\nimport {check, PERMISSIONS, RESULTS} from \"react-native-permissions\"\n\nimport {MiniappErrorCode, type CalendarListResult} from \"@mentra/miniapp\"\n\nimport {\n calendarEventOverlapsWindow,\n type CalendarListRequest,\n normalizeCalendarEvent,\n PhoneCalendarError,\n validateCalendarListRequest,\n} from \"./PhoneCalendarHelpers\"\n\nexport {PhoneCalendarError} from \"./PhoneCalendarHelpers\"\n\ntype CalendarPermissionStatus = Awaited<\n ReturnType<typeof Calendar.getCalendarPermissionsAsync>\n>[\"status\"]\n\n/**\n * expo-calendar reports anything below EventKit full access as not granted,\n * so distinguish the states the user can actually fix. \"Reopen the miniapp\"\n * is only true while the permission is undetermined — once iOS has resolved\n * it (denied or write-only/limited), only Settings can change it.\n */\nasync function calendarPermissionMessage(status: CalendarPermissionStatus): Promise<string> {\n if (status === \"undetermined\") {\n return \"Calendar permission has not been granted yet. Reopen the miniapp and allow calendar access.\"\n }\n if (Platform.OS === \"ios\") {\n const writeOnly = await check(PERMISSIONS.IOS.CALENDARS_WRITE_ONLY).catch(() => null)\n if (writeOnly === RESULTS.GRANTED || writeOnly === RESULTS.LIMITED) {\n return 'Calendar access is limited to write-only. Enable \"Full Access\" for Mentra in Settings › Privacy & Security › Calendars.'\n }\n return \"Calendar access is denied. Enable it for Mentra in Settings › Privacy & Security › Calendars.\"\n }\n return \"Calendar access is denied. Enable the Calendar permission for Mentra in your phone's app settings.\"\n}\n\nexport async function listPhoneCalendarEvents(request: CalendarListRequest): Promise<CalendarListResult> {\n const {startsAt, endsAt, limit} = validateCalendarListRequest(request)\n const permission = await Calendar.getCalendarPermissionsAsync()\n if (permission.status !== \"granted\") {\n throw new PhoneCalendarError(MiniappErrorCode.PERMISSION_DENIED, await calendarPermissionMessage(permission.status))\n }\n\n const calendars = await Calendar.getCalendarsAsync(Calendar.EntityTypes.EVENT)\n if (calendars.length === 0) return {events: [], truncated: false}\n\n const rawEvents = await Calendar.getEventsAsync(\n calendars.map((calendar) => calendar.id),\n startsAt,\n endsAt,\n )\n const events = rawEvents\n .map(normalizeCalendarEvent)\n .filter((event) => calendarEventOverlapsWindow(event, startsAt, endsAt))\n .sort((a, b) => Date.parse(a.startsAt) - Date.parse(b.startsAt) || Date.parse(a.endsAt) - Date.parse(b.endsAt))\n\n return {\n events: events.slice(0, limit),\n truncated: events.length > limit,\n }\n}\n"]}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type CameraFovRequest, type CameraFovResult } from "@mentra/bluetooth-sdk/internal";
|
|
2
|
+
export declare const CAMERA_FOV_OVERRIDE_TTL_MS = 300000;
|
|
3
|
+
export declare const CAMERA_FOV_OVERRIDE_REFRESH_MS = 120000;
|
|
4
|
+
/**
|
|
5
|
+
* Serializes miniapp-owned FOV/ROI overrides. The last live owner wins; releasing it applies the
|
|
6
|
+
* previous owner or asks ASG to restore its persistent base. ASG refreshes are no-restart TTL
|
|
7
|
+
* extensions for crash safety.
|
|
8
|
+
*/
|
|
9
|
+
export declare class PhoneCameraFovCoordinator {
|
|
10
|
+
private readonly legacyRestartSettleMs;
|
|
11
|
+
private readonly overrides;
|
|
12
|
+
private effectiveLeaseId;
|
|
13
|
+
private sequence;
|
|
14
|
+
private queue;
|
|
15
|
+
private refreshTimer;
|
|
16
|
+
private legacyMode;
|
|
17
|
+
constructor(legacyRestartSettleMs?: number);
|
|
18
|
+
/** Report-safe FOV ownership snapshot for incident diagnostics. */
|
|
19
|
+
getDiagnosticSnapshot(): Record<string, unknown>;
|
|
20
|
+
setOverride(packageName: string, request: CameraFovRequest): Promise<CameraFovResult>;
|
|
21
|
+
releaseForApp(packageName: string): Promise<void>;
|
|
22
|
+
private scheduleRefresh;
|
|
23
|
+
private clearRefresh;
|
|
24
|
+
private setLegacyOverride;
|
|
25
|
+
private applyLegacyFov;
|
|
26
|
+
private waitForLegacyCameraRestart;
|
|
27
|
+
private enqueue;
|
|
28
|
+
}
|
|
29
|
+
export declare const phoneCameraFovCoordinator: PhoneCameraFovCoordinator;
|
|
30
|
+
//# sourceMappingURL=PhoneCameraFovCoordinator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PhoneCameraFovCoordinator.d.ts","sourceRoot":"","sources":["../../src/services/PhoneCameraFovCoordinator.ts"],"names":[],"mappings":"AAAA,OAAqB,EAAC,KAAK,gBAAgB,EAAE,KAAK,eAAe,EAAC,MAAM,gCAAgC,CAAA;AAExG,eAAO,MAAM,0BAA0B,SAAU,CAAA;AACjD,eAAO,MAAM,8BAA8B,SAAU,CAAA;AAyBrD;;;;GAIG;AACH,qBAAa,yBAAyB;IAQxB,OAAO,CAAC,QAAQ,CAAC,qBAAqB;IAPlD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAmC;IAC7D,OAAO,CAAC,gBAAgB,CAAoB;IAC5C,OAAO,CAAC,QAAQ,CAAI;IACpB,OAAO,CAAC,KAAK,CAAsC;IACnD,OAAO,CAAC,YAAY,CAA2C;IAC/D,OAAO,CAAC,UAAU,CAAQ;gBAEG,qBAAqB,SAAkC;IAEpF,mEAAmE;IACnE,qBAAqB,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAchD,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,eAAe,CAAC;IAkCrF,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAoDjD,OAAO,CAAC,eAAe;IAqBvB,OAAO,CAAC,YAAY;YAKN,iBAAiB;YAkBjB,cAAc;IAM5B,OAAO,CAAC,0BAA0B;IAKlC,OAAO,CAAC,OAAO;CAQhB;AAED,eAAO,MAAM,yBAAyB,2BAAkC,CAAA"}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import BluetoothSdk from "@mentra/bluetooth-sdk/internal";
|
|
2
|
+
export const CAMERA_FOV_OVERRIDE_TTL_MS = 300_000;
|
|
3
|
+
export const CAMERA_FOV_OVERRIDE_REFRESH_MS = 120_000;
|
|
4
|
+
// Legacy ASG clients restart the camera HAL for an FOV update but cannot acknowledge
|
|
5
|
+
// when it is usable again. The ASG cooldown is 5s; retain a small settling margin so
|
|
6
|
+
// miniapps cannot submit a photo against the stale camera service.
|
|
7
|
+
const LEGACY_CAMERA_RESTART_SETTLE_MS = 7_000;
|
|
8
|
+
let leaseCounter = 0;
|
|
9
|
+
function mintLeaseId() {
|
|
10
|
+
leaseCounter = (leaseCounter + 1) & 0xffff;
|
|
11
|
+
return `fov-${leaseCounter.toString(16).padStart(4, "0")}`;
|
|
12
|
+
}
|
|
13
|
+
function isLegacyFovCompatibilityError(error) {
|
|
14
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
15
|
+
return /timed out waiting for glasses response|not available|unknown method/i.test(message);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Serializes miniapp-owned FOV/ROI overrides. The last live owner wins; releasing it applies the
|
|
19
|
+
* previous owner or asks ASG to restore its persistent base. ASG refreshes are no-restart TTL
|
|
20
|
+
* extensions for crash safety.
|
|
21
|
+
*/
|
|
22
|
+
export class PhoneCameraFovCoordinator {
|
|
23
|
+
legacyRestartSettleMs;
|
|
24
|
+
overrides = new Map();
|
|
25
|
+
effectiveLeaseId;
|
|
26
|
+
sequence = 0;
|
|
27
|
+
queue = Promise.resolve();
|
|
28
|
+
refreshTimer;
|
|
29
|
+
legacyMode = false;
|
|
30
|
+
constructor(legacyRestartSettleMs = LEGACY_CAMERA_RESTART_SETTLE_MS) {
|
|
31
|
+
this.legacyRestartSettleMs = legacyRestartSettleMs;
|
|
32
|
+
}
|
|
33
|
+
/** Report-safe FOV ownership snapshot for incident diagnostics. */
|
|
34
|
+
getDiagnosticSnapshot() {
|
|
35
|
+
return {
|
|
36
|
+
owners: [...this.overrides.entries()]
|
|
37
|
+
.sort(([, a], [, b]) => b.order - a.order)
|
|
38
|
+
.map(([packageName, entry]) => ({
|
|
39
|
+
packageName,
|
|
40
|
+
effective: entry.leaseId === this.effectiveLeaseId,
|
|
41
|
+
fov: entry.fov,
|
|
42
|
+
roiPosition: entry.roiPosition,
|
|
43
|
+
})),
|
|
44
|
+
legacyMode: this.legacyMode,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
setOverride(packageName, request) {
|
|
48
|
+
return this.enqueue(async () => {
|
|
49
|
+
const previous = this.overrides.get(packageName);
|
|
50
|
+
const leaseId = previous?.leaseId ?? mintLeaseId();
|
|
51
|
+
const order = ++this.sequence;
|
|
52
|
+
try {
|
|
53
|
+
if (this.legacyMode)
|
|
54
|
+
return this.setLegacyOverride(packageName, request, leaseId, order);
|
|
55
|
+
const result = await BluetoothSdk.setCameraFovOverride({
|
|
56
|
+
...request,
|
|
57
|
+
leaseId,
|
|
58
|
+
ttlMs: CAMERA_FOV_OVERRIDE_TTL_MS,
|
|
59
|
+
});
|
|
60
|
+
this.overrides.set(packageName, {
|
|
61
|
+
leaseId,
|
|
62
|
+
fov: result.fov,
|
|
63
|
+
roiPosition: result.roiPosition,
|
|
64
|
+
order,
|
|
65
|
+
});
|
|
66
|
+
this.effectiveLeaseId = leaseId;
|
|
67
|
+
this.scheduleRefresh();
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
if (!isLegacyFovCompatibilityError(error))
|
|
72
|
+
throw error;
|
|
73
|
+
console.warn("[PhoneCameraFovCoordinator] override unavailable; using legacy FOV command", error);
|
|
74
|
+
const result = await this.setLegacyOverride(packageName, request, leaseId, order);
|
|
75
|
+
// Only cache compatibility mode after the fallback itself succeeds. A
|
|
76
|
+
// disconnect or missing native bridge must not permanently downgrade
|
|
77
|
+
// future requests on a healthy/newer glasses connection.
|
|
78
|
+
this.legacyMode = true;
|
|
79
|
+
return result;
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
releaseForApp(packageName) {
|
|
84
|
+
return this.enqueue(async () => {
|
|
85
|
+
const removed = this.overrides.get(packageName);
|
|
86
|
+
if (!removed)
|
|
87
|
+
return;
|
|
88
|
+
if (removed.leaseId !== this.effectiveLeaseId) {
|
|
89
|
+
this.overrides.delete(packageName);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const next = [...this.overrides.entries()]
|
|
93
|
+
.filter(([candidatePackage]) => candidatePackage !== packageName)
|
|
94
|
+
.map(([, entry]) => entry)
|
|
95
|
+
.sort((a, b) => b.order - a.order)[0];
|
|
96
|
+
if (this.legacyMode) {
|
|
97
|
+
if (next) {
|
|
98
|
+
await this.applyLegacyFov({ fov: next.fov, roiPosition: next.roiPosition });
|
|
99
|
+
this.overrides.delete(packageName);
|
|
100
|
+
this.effectiveLeaseId = next.leaseId;
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
// Legacy commands are one-way and have no lease-release protocol.
|
|
104
|
+
// Ask native to replay its unchanged persistent base setting, then
|
|
105
|
+
// probe the modern override path again for the next ownership cycle.
|
|
106
|
+
await BluetoothSdk.restoreLegacyCameraFov();
|
|
107
|
+
await this.waitForLegacyCameraRestart();
|
|
108
|
+
this.overrides.delete(packageName);
|
|
109
|
+
this.clearRefresh();
|
|
110
|
+
this.effectiveLeaseId = undefined;
|
|
111
|
+
this.legacyMode = false;
|
|
112
|
+
}
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
if (next) {
|
|
116
|
+
await BluetoothSdk.setCameraFovOverride({
|
|
117
|
+
leaseId: next.leaseId,
|
|
118
|
+
fov: next.fov,
|
|
119
|
+
roiPosition: next.roiPosition,
|
|
120
|
+
ttlMs: CAMERA_FOV_OVERRIDE_TTL_MS,
|
|
121
|
+
});
|
|
122
|
+
// Commit phone-side ownership only after ASG accepted the replacement. On failure the
|
|
123
|
+
// closing app remains effective here, allowing unregister/reconnect cleanup to retry.
|
|
124
|
+
this.overrides.delete(packageName);
|
|
125
|
+
this.effectiveLeaseId = next.leaseId;
|
|
126
|
+
this.scheduleRefresh();
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
await BluetoothSdk.releaseCameraFovOverride(removed.leaseId);
|
|
130
|
+
this.overrides.delete(packageName);
|
|
131
|
+
this.clearRefresh();
|
|
132
|
+
this.effectiveLeaseId = undefined;
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
scheduleRefresh() {
|
|
137
|
+
if (this.legacyMode)
|
|
138
|
+
return;
|
|
139
|
+
this.clearRefresh();
|
|
140
|
+
const leaseId = this.effectiveLeaseId;
|
|
141
|
+
if (!leaseId)
|
|
142
|
+
return;
|
|
143
|
+
this.refreshTimer = setTimeout(() => {
|
|
144
|
+
void this.enqueue(async () => {
|
|
145
|
+
if (this.effectiveLeaseId !== leaseId)
|
|
146
|
+
return;
|
|
147
|
+
const entry = [...this.overrides.values()].find((candidate) => candidate.leaseId === leaseId);
|
|
148
|
+
if (!entry)
|
|
149
|
+
return;
|
|
150
|
+
await BluetoothSdk.setCameraFovOverride({
|
|
151
|
+
leaseId,
|
|
152
|
+
fov: entry.fov,
|
|
153
|
+
roiPosition: entry.roiPosition,
|
|
154
|
+
ttlMs: CAMERA_FOV_OVERRIDE_TTL_MS,
|
|
155
|
+
});
|
|
156
|
+
this.scheduleRefresh();
|
|
157
|
+
}).catch((error) => console.warn("[PhoneCameraFovCoordinator] failed to refresh override", error));
|
|
158
|
+
}, CAMERA_FOV_OVERRIDE_REFRESH_MS);
|
|
159
|
+
}
|
|
160
|
+
clearRefresh() {
|
|
161
|
+
if (this.refreshTimer)
|
|
162
|
+
clearTimeout(this.refreshTimer);
|
|
163
|
+
this.refreshTimer = undefined;
|
|
164
|
+
}
|
|
165
|
+
async setLegacyOverride(packageName, request, leaseId, order) {
|
|
166
|
+
const result = await this.applyLegacyFov(request);
|
|
167
|
+
this.overrides.set(packageName, {
|
|
168
|
+
leaseId,
|
|
169
|
+
fov: result.fov,
|
|
170
|
+
roiPosition: result.roiPosition,
|
|
171
|
+
order,
|
|
172
|
+
});
|
|
173
|
+
this.effectiveLeaseId = leaseId;
|
|
174
|
+
this.clearRefresh();
|
|
175
|
+
return result;
|
|
176
|
+
}
|
|
177
|
+
async applyLegacyFov(request) {
|
|
178
|
+
const result = await BluetoothSdk.setLegacyCameraFov(request);
|
|
179
|
+
await this.waitForLegacyCameraRestart();
|
|
180
|
+
return result;
|
|
181
|
+
}
|
|
182
|
+
waitForLegacyCameraRestart() {
|
|
183
|
+
if (this.legacyRestartSettleMs <= 0)
|
|
184
|
+
return Promise.resolve();
|
|
185
|
+
return new Promise((resolve) => setTimeout(resolve, this.legacyRestartSettleMs));
|
|
186
|
+
}
|
|
187
|
+
enqueue(operation) {
|
|
188
|
+
const result = this.queue.then(operation, operation);
|
|
189
|
+
this.queue = result.then(() => undefined, () => undefined);
|
|
190
|
+
return result;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
export const phoneCameraFovCoordinator = new PhoneCameraFovCoordinator();
|
|
194
|
+
//# sourceMappingURL=PhoneCameraFovCoordinator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PhoneCameraFovCoordinator.js","sourceRoot":"","sources":["../../src/services/PhoneCameraFovCoordinator.ts"],"names":[],"mappings":"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","sourcesContent":["import BluetoothSdk, {type CameraFovRequest, type CameraFovResult} from \"@mentra/bluetooth-sdk/internal\"\n\nexport const CAMERA_FOV_OVERRIDE_TTL_MS = 300_000\nexport const CAMERA_FOV_OVERRIDE_REFRESH_MS = 120_000\n// Legacy ASG clients restart the camera HAL for an FOV update but cannot acknowledge\n// when it is usable again. The ASG cooldown is 5s; retain a small settling margin so\n// miniapps cannot submit a photo against the stale camera service.\nconst LEGACY_CAMERA_RESTART_SETTLE_MS = 7_000\n\ninterface OverrideEntry {\n leaseId: string\n fov: number\n roiPosition: \"center\" | \"bottom\" | \"top\"\n order: number\n}\n\nlet leaseCounter = 0\n\nfunction mintLeaseId(): string {\n leaseCounter = (leaseCounter + 1) & 0xffff\n return `fov-${leaseCounter.toString(16).padStart(4, \"0\")}`\n}\n\nfunction isLegacyFovCompatibilityError(error: unknown): boolean {\n const message = error instanceof Error ? error.message : String(error)\n return /timed out waiting for glasses response|not available|unknown method/i.test(message)\n}\n\n/**\n * Serializes miniapp-owned FOV/ROI overrides. The last live owner wins; releasing it applies the\n * previous owner or asks ASG to restore its persistent base. ASG refreshes are no-restart TTL\n * extensions for crash safety.\n */\nexport class PhoneCameraFovCoordinator {\n private readonly overrides = new Map<string, OverrideEntry>()\n private effectiveLeaseId: string | undefined\n private sequence = 0\n private queue: Promise<unknown> = Promise.resolve()\n private refreshTimer: ReturnType<typeof setTimeout> | undefined\n private legacyMode = false\n\n constructor(private readonly legacyRestartSettleMs = LEGACY_CAMERA_RESTART_SETTLE_MS) {}\n\n /** Report-safe FOV ownership snapshot for incident diagnostics. */\n getDiagnosticSnapshot(): Record<string, unknown> {\n return {\n owners: [...this.overrides.entries()]\n .sort(([, a], [, b]) => b.order - a.order)\n .map(([packageName, entry]) => ({\n packageName,\n effective: entry.leaseId === this.effectiveLeaseId,\n fov: entry.fov,\n roiPosition: entry.roiPosition,\n })),\n legacyMode: this.legacyMode,\n }\n }\n\n setOverride(packageName: string, request: CameraFovRequest): Promise<CameraFovResult> {\n return this.enqueue(async () => {\n const previous = this.overrides.get(packageName)\n const leaseId = previous?.leaseId ?? mintLeaseId()\n const order = ++this.sequence\n try {\n if (this.legacyMode) return this.setLegacyOverride(packageName, request, leaseId, order)\n const result = await BluetoothSdk.setCameraFovOverride({\n ...request,\n leaseId,\n ttlMs: CAMERA_FOV_OVERRIDE_TTL_MS,\n })\n this.overrides.set(packageName, {\n leaseId,\n fov: result.fov,\n roiPosition: result.roiPosition,\n order,\n })\n this.effectiveLeaseId = leaseId\n this.scheduleRefresh()\n return result\n } catch (error) {\n if (!isLegacyFovCompatibilityError(error)) throw error\n console.warn(\"[PhoneCameraFovCoordinator] override unavailable; using legacy FOV command\", error)\n const result = await this.setLegacyOverride(packageName, request, leaseId, order)\n // Only cache compatibility mode after the fallback itself succeeds. A\n // disconnect or missing native bridge must not permanently downgrade\n // future requests on a healthy/newer glasses connection.\n this.legacyMode = true\n return result\n }\n })\n }\n\n releaseForApp(packageName: string): Promise<void> {\n return this.enqueue(async () => {\n const removed = this.overrides.get(packageName)\n if (!removed) return\n if (removed.leaseId !== this.effectiveLeaseId) {\n this.overrides.delete(packageName)\n return\n }\n\n const next = [...this.overrides.entries()]\n .filter(([candidatePackage]) => candidatePackage !== packageName)\n .map(([, entry]) => entry)\n .sort((a, b) => b.order - a.order)[0]\n if (this.legacyMode) {\n if (next) {\n await this.applyLegacyFov({fov: next.fov, roiPosition: next.roiPosition})\n this.overrides.delete(packageName)\n this.effectiveLeaseId = next.leaseId\n } else {\n // Legacy commands are one-way and have no lease-release protocol.\n // Ask native to replay its unchanged persistent base setting, then\n // probe the modern override path again for the next ownership cycle.\n await BluetoothSdk.restoreLegacyCameraFov()\n await this.waitForLegacyCameraRestart()\n this.overrides.delete(packageName)\n this.clearRefresh()\n this.effectiveLeaseId = undefined\n this.legacyMode = false\n }\n return\n }\n if (next) {\n await BluetoothSdk.setCameraFovOverride({\n leaseId: next.leaseId,\n fov: next.fov,\n roiPosition: next.roiPosition,\n ttlMs: CAMERA_FOV_OVERRIDE_TTL_MS,\n })\n // Commit phone-side ownership only after ASG accepted the replacement. On failure the\n // closing app remains effective here, allowing unregister/reconnect cleanup to retry.\n this.overrides.delete(packageName)\n this.effectiveLeaseId = next.leaseId\n this.scheduleRefresh()\n } else {\n await BluetoothSdk.releaseCameraFovOverride(removed.leaseId)\n this.overrides.delete(packageName)\n this.clearRefresh()\n this.effectiveLeaseId = undefined\n }\n })\n }\n\n private scheduleRefresh(): void {\n if (this.legacyMode) return\n this.clearRefresh()\n const leaseId = this.effectiveLeaseId\n if (!leaseId) return\n this.refreshTimer = setTimeout(() => {\n void this.enqueue(async () => {\n if (this.effectiveLeaseId !== leaseId) return\n const entry = [...this.overrides.values()].find((candidate) => candidate.leaseId === leaseId)\n if (!entry) return\n await BluetoothSdk.setCameraFovOverride({\n leaseId,\n fov: entry.fov,\n roiPosition: entry.roiPosition,\n ttlMs: CAMERA_FOV_OVERRIDE_TTL_MS,\n })\n this.scheduleRefresh()\n }).catch((error) => console.warn(\"[PhoneCameraFovCoordinator] failed to refresh override\", error))\n }, CAMERA_FOV_OVERRIDE_REFRESH_MS)\n }\n\n private clearRefresh(): void {\n if (this.refreshTimer) clearTimeout(this.refreshTimer)\n this.refreshTimer = undefined\n }\n\n private async setLegacyOverride(\n packageName: string,\n request: CameraFovRequest,\n leaseId: string,\n order: number,\n ): Promise<CameraFovResult> {\n const result = await this.applyLegacyFov(request)\n this.overrides.set(packageName, {\n leaseId,\n fov: result.fov,\n roiPosition: result.roiPosition,\n order,\n })\n this.effectiveLeaseId = leaseId\n this.clearRefresh()\n return result\n }\n\n private async applyLegacyFov(request: CameraFovRequest): Promise<CameraFovResult> {\n const result = await BluetoothSdk.setLegacyCameraFov(request)\n await this.waitForLegacyCameraRestart()\n return result\n }\n\n private waitForLegacyCameraRestart(): Promise<void> {\n if (this.legacyRestartSettleMs <= 0) return Promise.resolve()\n return new Promise<void>((resolve) => setTimeout(resolve, this.legacyRestartSettleMs))\n }\n\n private enqueue<T>(operation: () => Promise<T>): Promise<T> {\n const result = this.queue.then(operation, operation)\n this.queue = result.then(\n () => undefined,\n () => undefined,\n )\n return result\n }\n}\n\nexport const phoneCameraFovCoordinator = new PhoneCameraFovCoordinator()\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PhoneNotificationsSync.d.ts","sourceRoot":"","sources":["../../src/services/PhoneNotificationsSync.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"PhoneNotificationsSync.d.ts","sourceRoot":"","sources":["../../src/services/PhoneNotificationsSync.ts"],"names":[],"mappings":"AA6BA,wBAAgB,2BAA2B,IAAI,IAAI,CAWlD;AAED,wBAAgB,0BAA0B,IAAI,IAAI,CAGjD"}
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Phone-notifications sync — engine-owned. Pushes the notification-forwarding
|
|
3
|
-
*
|
|
3
|
+
* operational kill switch + config (`android_notification_listener_enabled`
|
|
4
|
+
* and `notifications_blocklist`) to the native
|
|
4
5
|
* NotificationListener via `CrustModule.setNotificationConfig`, so
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* the blocklist reaches the listener for ANY host — not just the Mentra App,
|
|
7
|
+
* where this used to live in MantleManager. The Android service is desired by
|
|
8
|
+
* default now that its cold start is isolated from React Native. Native code
|
|
9
|
+
* keeps the component discoverable in Android's notification-access Settings,
|
|
10
|
+
* but only binds the service after access is granted. The debug setting remains
|
|
11
|
+
* a hidden emergency kill switch; Notify's running state gates presentation,
|
|
12
|
+
* not capture. Android-only; a no-op elsewhere. Started by `engine.start()`.
|
|
8
13
|
*/
|
|
9
14
|
import { shallow } from "zustand/shallow";
|
|
10
15
|
import CrustModule from "@mentra/crust";
|
|
@@ -12,19 +17,16 @@ import { useSettingsStore, SETTINGS } from "../stores/settings";
|
|
|
12
17
|
let unsubscribe = null;
|
|
13
18
|
function pushConfig() {
|
|
14
19
|
const s = useSettingsStore.getState();
|
|
15
|
-
const
|
|
20
|
+
const listenerEnabled = Boolean(s.getSetting(SETTINGS.android_notification_listener_enabled.key));
|
|
16
21
|
const blocklist = s.getSetting(SETTINGS.notifications_blocklist.key);
|
|
17
|
-
|
|
18
|
-
// Android-only, but CrustModule.setNotificationConfig is a safe no-op elsewhere;
|
|
19
|
-
// the Android-only gating lives on the facade's setEnabled/setBlocklist.
|
|
20
|
-
CrustModule.setNotificationConfig(enabled, Array.isArray(blocklist) ? blocklist : []).catch((err) => console.warn(`PhoneNotificationsSync: setNotificationConfig failed: ${err?.message ?? err}`));
|
|
22
|
+
CrustModule.setNotificationConfig(listenerEnabled, Array.isArray(blocklist) ? blocklist : []).catch((err) => console.warn(`PhoneNotificationsSync: setNotificationConfig failed: ${err?.message ?? err}`));
|
|
21
23
|
}
|
|
22
24
|
export function startPhoneNotificationsSync() {
|
|
23
25
|
if (unsubscribe)
|
|
24
26
|
return;
|
|
25
27
|
pushConfig(); // initial sync so the listener has the current config
|
|
26
28
|
unsubscribe = useSettingsStore.subscribe((state) => ({
|
|
27
|
-
|
|
29
|
+
listenerEnabled: state.getSetting(SETTINGS.android_notification_listener_enabled.key),
|
|
28
30
|
blocklist: state.getSetting(SETTINGS.notifications_blocklist.key),
|
|
29
31
|
}), () => pushConfig(), { equalityFn: shallow });
|
|
30
32
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PhoneNotificationsSync.js","sourceRoot":"","sources":["../../src/services/PhoneNotificationsSync.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"PhoneNotificationsSync.js","sourceRoot":"","sources":["../../src/services/PhoneNotificationsSync.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAC,OAAO,EAAC,MAAM,iBAAiB,CAAA;AACvC,OAAO,WAAW,MAAM,eAAe,CAAA;AAEvC,OAAO,EAAC,gBAAgB,EAAE,QAAQ,EAAC,MAAM,oBAAoB,CAAA;AAE7D,IAAI,WAAW,GAAwB,IAAI,CAAA;AAE3C,SAAS,UAAU;IACjB,MAAM,CAAC,GAAG,gBAAgB,CAAC,QAAQ,EAAE,CAAA;IACrC,MAAM,eAAe,GAAG,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,qCAAqC,CAAC,GAAG,CAAC,CAAC,CAAA;IACjG,MAAM,SAAS,GAAG,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,uBAAuB,CAAC,GAAG,CAAC,CAAA;IACpE,WAAW,CAAC,qBAAqB,CAAC,eAAe,EAAE,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE,CACnH,OAAO,CAAC,IAAI,CAAC,yDAA0D,GAAa,EAAE,OAAO,IAAI,GAAG,EAAE,CAAC,CACxG,CAAA;AACH,CAAC;AAED,MAAM,UAAU,2BAA2B;IACzC,IAAI,WAAW;QAAE,OAAM;IACvB,UAAU,EAAE,CAAA,CAAC,sDAAsD;IACnE,WAAW,GAAG,gBAAgB,CAAC,SAAS,CACtC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACV,eAAe,EAAE,KAAK,CAAC,UAAU,CAAC,QAAQ,CAAC,qCAAqC,CAAC,GAAG,CAAC;QACrF,SAAS,EAAE,KAAK,CAAC,UAAU,CAAC,QAAQ,CAAC,uBAAuB,CAAC,GAAG,CAAC;KAClE,CAAC,EACF,GAAG,EAAE,CAAC,UAAU,EAAE,EAClB,EAAC,UAAU,EAAE,OAAO,EAAC,CACtB,CAAA;AACH,CAAC;AAED,MAAM,UAAU,0BAA0B;IACxC,WAAW,EAAE,EAAE,CAAA;IACf,WAAW,GAAG,IAAI,CAAA;AACpB,CAAC","sourcesContent":["/**\n * Phone-notifications sync — engine-owned. Pushes the notification-forwarding\n * operational kill switch + config (`android_notification_listener_enabled`\n * and `notifications_blocklist`) to the native\n * NotificationListener via `CrustModule.setNotificationConfig`, so\n * the blocklist reaches the listener for ANY host — not just the Mentra App,\n * where this used to live in MantleManager. The Android service is desired by\n * default now that its cold start is isolated from React Native. Native code\n * keeps the component discoverable in Android's notification-access Settings,\n * but only binds the service after access is granted. The debug setting remains\n * a hidden emergency kill switch; Notify's running state gates presentation,\n * not capture. Android-only; a no-op elsewhere. Started by `engine.start()`.\n */\nimport {shallow} from \"zustand/shallow\"\nimport CrustModule from \"@mentra/crust\"\n\nimport {useSettingsStore, SETTINGS} from \"../stores/settings\"\n\nlet unsubscribe: (() => void) | null = null\n\nfunction pushConfig(): void {\n const s = useSettingsStore.getState()\n const listenerEnabled = Boolean(s.getSetting(SETTINGS.android_notification_listener_enabled.key))\n const blocklist = s.getSetting(SETTINGS.notifications_blocklist.key)\n CrustModule.setNotificationConfig(listenerEnabled, Array.isArray(blocklist) ? blocklist : []).catch((err: unknown) =>\n console.warn(`PhoneNotificationsSync: setNotificationConfig failed: ${(err as Error)?.message ?? err}`),\n )\n}\n\nexport function startPhoneNotificationsSync(): void {\n if (unsubscribe) return\n pushConfig() // initial sync so the listener has the current config\n unsubscribe = useSettingsStore.subscribe(\n (state) => ({\n listenerEnabled: state.getSetting(SETTINGS.android_notification_listener_enabled.key),\n blocklist: state.getSetting(SETTINGS.notifications_blocklist.key),\n }),\n () => pushConfig(),\n {equalityFn: shallow},\n )\n}\n\nexport function stopPhoneNotificationsSync(): void {\n unsubscribe?.()\n unsubscribe = null\n}\n"]}
|