@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.
Files changed (441) hide show
  1. package/README.md +42 -1
  2. package/build/engine.d.ts +11 -3
  3. package/build/engine.d.ts.map +1 -1
  4. package/build/engine.js +7 -2
  5. package/build/engine.js.map +1 -1
  6. package/build/facades/dev.d.ts +3 -2
  7. package/build/facades/dev.d.ts.map +1 -1
  8. package/build/facades/dev.js +18 -12
  9. package/build/facades/dev.js.map +1 -1
  10. package/build/facades/gallery.js +1 -1
  11. package/build/facades/gallery.js.map +1 -1
  12. package/build/facades/glasses.d.ts +1 -0
  13. package/build/facades/glasses.d.ts.map +1 -1
  14. package/build/facades/glasses.js +19 -4
  15. package/build/facades/glasses.js.map +1 -1
  16. package/build/facades/glassesSettings.d.ts.map +1 -1
  17. package/build/facades/glassesSettings.js +1 -0
  18. package/build/facades/glassesSettings.js.map +1 -1
  19. package/build/facades/miniapps.d.ts +4 -0
  20. package/build/facades/miniapps.d.ts.map +1 -1
  21. package/build/facades/miniapps.js +5 -0
  22. package/build/facades/miniapps.js.map +1 -1
  23. package/build/facades/ota.d.ts +11 -2
  24. package/build/facades/ota.d.ts.map +1 -1
  25. package/build/facades/ota.js +19 -4
  26. package/build/facades/ota.js.map +1 -1
  27. package/build/facades/pairing.d.ts +4 -0
  28. package/build/facades/pairing.d.ts.map +1 -1
  29. package/build/facades/pairing.js +10 -8
  30. package/build/facades/pairing.js.map +1 -1
  31. package/build/facades/permissions.d.ts +10 -0
  32. package/build/facades/permissions.d.ts.map +1 -1
  33. package/build/facades/permissions.js +10 -0
  34. package/build/facades/permissions.js.map +1 -1
  35. package/build/facades/phoneNotifications.d.ts +0 -4
  36. package/build/facades/phoneNotifications.d.ts.map +1 -1
  37. package/build/facades/phoneNotifications.js +4 -14
  38. package/build/facades/phoneNotifications.js.map +1 -1
  39. package/build/index.d.ts +6 -3
  40. package/build/index.d.ts.map +1 -1
  41. package/build/index.js +4 -2
  42. package/build/index.js.map +1 -1
  43. package/build/internal.d.ts +3 -2
  44. package/build/internal.d.ts.map +1 -1
  45. package/build/internal.js +2 -1
  46. package/build/internal.js.map +1 -1
  47. package/build/react/MentraLiveOtaFlow.d.ts +38 -0
  48. package/build/react/MentraLiveOtaFlow.d.ts.map +1 -0
  49. package/build/react/MentraLiveOtaFlow.js +528 -0
  50. package/build/react/MentraLiveOtaFlow.js.map +1 -0
  51. package/build/react/index.d.ts +2 -0
  52. package/build/react/index.d.ts.map +1 -0
  53. package/build/react/index.js +2 -0
  54. package/build/react/index.js.map +1 -0
  55. package/build/react/useEngineSnapshot.d.ts +2 -0
  56. package/build/react/useEngineSnapshot.d.ts.map +1 -0
  57. package/build/react/useEngineSnapshot.js +14 -0
  58. package/build/react/useEngineSnapshot.js.map +1 -0
  59. package/build/runtime/bootstrap.d.ts +24 -1
  60. package/build/runtime/bootstrap.d.ts.map +1 -1
  61. package/build/runtime/bootstrap.js +17 -0
  62. package/build/runtime/bootstrap.js.map +1 -1
  63. package/build/runtime/scanQrSeam.d.ts +7 -0
  64. package/build/runtime/scanQrSeam.d.ts.map +1 -0
  65. package/build/runtime/scanQrSeam.js +16 -0
  66. package/build/runtime/scanQrSeam.js.map +1 -0
  67. package/build/services/AppRegistry.d.ts +33 -57
  68. package/build/services/AppRegistry.d.ts.map +1 -1
  69. package/build/services/AppRegistry.js +193 -144
  70. package/build/services/AppRegistry.js.map +1 -1
  71. package/build/services/AudioCloudUplink.d.ts +12 -0
  72. package/build/services/AudioCloudUplink.d.ts.map +1 -1
  73. package/build/services/AudioCloudUplink.js +22 -1
  74. package/build/services/AudioCloudUplink.js.map +1 -1
  75. package/build/services/AudioPlaybackService.d.ts +41 -2
  76. package/build/services/AudioPlaybackService.d.ts.map +1 -1
  77. package/build/services/AudioPlaybackService.js +250 -33
  78. package/build/services/AudioPlaybackService.js.map +1 -1
  79. package/build/services/BlobStore.d.ts +2 -1
  80. package/build/services/BlobStore.d.ts.map +1 -1
  81. package/build/services/BlobStore.js +17 -9
  82. package/build/services/BlobStore.js.map +1 -1
  83. package/build/services/CloudAudioSubscriptionSync.d.ts.map +1 -1
  84. package/build/services/CloudAudioSubscriptionSync.js +13 -1
  85. package/build/services/CloudAudioSubscriptionSync.js.map +1 -1
  86. package/build/services/CloudClientService.d.ts +15 -0
  87. package/build/services/CloudClientService.d.ts.map +1 -1
  88. package/build/services/CloudClientService.js +93 -1
  89. package/build/services/CloudClientService.js.map +1 -1
  90. package/build/services/DeviceEventRouter.js +5 -5
  91. package/build/services/DeviceEventRouter.js.map +1 -1
  92. package/build/services/ForegroundLocationPermission.d.ts +13 -0
  93. package/build/services/ForegroundLocationPermission.d.ts.map +1 -0
  94. package/build/services/ForegroundLocationPermission.js +12 -0
  95. package/build/services/ForegroundLocationPermission.js.map +1 -0
  96. package/build/services/GlassesSettingsSync.d.ts.map +1 -1
  97. package/build/services/GlassesSettingsSync.js +9 -3
  98. package/build/services/GlassesSettingsSync.js.map +1 -1
  99. package/build/services/GlassesStatusProjection.d.ts +2 -1
  100. package/build/services/GlassesStatusProjection.d.ts.map +1 -1
  101. package/build/services/GlassesStatusProjection.js +15 -8
  102. package/build/services/GlassesStatusProjection.js.map +1 -1
  103. package/build/services/HotspotOtaTransport.d.ts +29 -0
  104. package/build/services/HotspotOtaTransport.d.ts.map +1 -0
  105. package/build/services/HotspotOtaTransport.js +146 -0
  106. package/build/services/HotspotOtaTransport.js.map +1 -0
  107. package/build/services/HotspotShutdown.d.ts +17 -0
  108. package/build/services/HotspotShutdown.d.ts.map +1 -0
  109. package/build/services/HotspotShutdown.js +29 -0
  110. package/build/services/HotspotShutdown.js.map +1 -0
  111. package/build/services/LocalDisplayManager.d.ts +15 -0
  112. package/build/services/LocalDisplayManager.d.ts.map +1 -1
  113. package/build/services/LocalDisplayManager.js +106 -20
  114. package/build/services/LocalDisplayManager.js.map +1 -1
  115. package/build/services/LocalMiniappRuntime.d.ts +74 -2
  116. package/build/services/LocalMiniappRuntime.d.ts.map +1 -1
  117. package/build/services/LocalMiniappRuntime.js +785 -291
  118. package/build/services/LocalMiniappRuntime.js.map +1 -1
  119. package/build/services/LocalMiniappStorage.d.ts +32 -0
  120. package/build/services/LocalMiniappStorage.d.ts.map +1 -0
  121. package/build/services/LocalMiniappStorage.js +66 -0
  122. package/build/services/LocalMiniappStorage.js.map +1 -0
  123. package/build/services/LocalMiniappUserIdentity.d.ts +20 -0
  124. package/build/services/LocalMiniappUserIdentity.d.ts.map +1 -0
  125. package/build/services/LocalMiniappUserIdentity.js +43 -0
  126. package/build/services/LocalMiniappUserIdentity.js.map +1 -0
  127. package/build/services/LocalSttFallbackCoordinator.d.ts +9 -6
  128. package/build/services/LocalSttFallbackCoordinator.d.ts.map +1 -1
  129. package/build/services/LocalSttFallbackCoordinator.js +18 -12
  130. package/build/services/LocalSttFallbackCoordinator.js.map +1 -1
  131. package/build/services/MicStateCoordinator.d.ts +42 -1
  132. package/build/services/MicStateCoordinator.d.ts.map +1 -1
  133. package/build/services/MicStateCoordinator.js +141 -3
  134. package/build/services/MicStateCoordinator.js.map +1 -1
  135. package/build/services/MiniappLauncher.d.ts.map +1 -1
  136. package/build/services/MiniappLauncher.js +23 -3
  137. package/build/services/MiniappLauncher.js.map +1 -1
  138. package/build/services/MiniappLiveness.d.ts +12 -0
  139. package/build/services/MiniappLiveness.d.ts.map +1 -0
  140. package/build/services/MiniappLiveness.js +16 -0
  141. package/build/services/MiniappLiveness.js.map +1 -0
  142. package/build/services/NativeHttpResponse.d.ts +3 -0
  143. package/build/services/NativeHttpResponse.d.ts.map +1 -0
  144. package/build/services/NativeHttpResponse.js +6 -0
  145. package/build/services/NativeHttpResponse.js.map +1 -0
  146. package/build/services/OtaArtifactDownloader.d.ts +65 -0
  147. package/build/services/OtaArtifactDownloader.d.ts.map +1 -0
  148. package/build/services/OtaArtifactDownloader.js +226 -0
  149. package/build/services/OtaArtifactDownloader.js.map +1 -0
  150. package/build/services/OtaAutoChain.d.ts +34 -0
  151. package/build/services/OtaAutoChain.d.ts.map +1 -0
  152. package/build/services/OtaAutoChain.js +83 -0
  153. package/build/services/OtaAutoChain.js.map +1 -0
  154. package/build/services/OtaErrorMapping.d.ts +18 -0
  155. package/build/services/OtaErrorMapping.d.ts.map +1 -0
  156. package/build/services/OtaErrorMapping.js +63 -0
  157. package/build/services/OtaErrorMapping.js.map +1 -0
  158. package/build/services/OtaInstallCoordinator.d.ts +94 -14
  159. package/build/services/OtaInstallCoordinator.d.ts.map +1 -1
  160. package/build/services/OtaInstallCoordinator.js +574 -130
  161. package/build/services/OtaInstallCoordinator.js.map +1 -1
  162. package/build/services/OtaService.d.ts.map +1 -1
  163. package/build/services/OtaService.js +8 -1
  164. package/build/services/OtaService.js.map +1 -1
  165. package/build/services/OtaUpdateCheckService.d.ts +38 -3
  166. package/build/services/OtaUpdateCheckService.d.ts.map +1 -1
  167. package/build/services/OtaUpdateCheckService.js +142 -22
  168. package/build/services/OtaUpdateCheckService.js.map +1 -1
  169. package/build/services/PhoneCalendarHelpers.d.ts +32 -0
  170. package/build/services/PhoneCalendarHelpers.d.ts.map +1 -0
  171. package/build/services/PhoneCalendarHelpers.js +157 -0
  172. package/build/services/PhoneCalendarHelpers.js.map +1 -0
  173. package/build/services/PhoneCalendarService.d.ts +5 -0
  174. package/build/services/PhoneCalendarService.d.ts.map +1 -0
  175. package/build/services/PhoneCalendarService.js +45 -0
  176. package/build/services/PhoneCalendarService.js.map +1 -0
  177. package/build/services/PhoneCameraFovCoordinator.d.ts +30 -0
  178. package/build/services/PhoneCameraFovCoordinator.d.ts.map +1 -0
  179. package/build/services/PhoneCameraFovCoordinator.js +194 -0
  180. package/build/services/PhoneCameraFovCoordinator.js.map +1 -0
  181. package/build/services/PhoneNotificationsSync.d.ts.map +1 -1
  182. package/build/services/PhoneNotificationsSync.js +12 -10
  183. package/build/services/PhoneNotificationsSync.js.map +1 -1
  184. package/build/services/PhonePhotoCoordinator.d.ts +26 -2
  185. package/build/services/PhonePhotoCoordinator.d.ts.map +1 -1
  186. package/build/services/PhonePhotoCoordinator.js +79 -9
  187. package/build/services/PhonePhotoCoordinator.js.map +1 -1
  188. package/build/services/PhoneStreamCoordinator.d.ts +8 -1
  189. package/build/services/PhoneStreamCoordinator.d.ts.map +1 -1
  190. package/build/services/PhoneStreamCoordinator.js +125 -13
  191. package/build/services/PhoneStreamCoordinator.js.map +1 -1
  192. package/build/services/PhoneVideoCoordinator.d.ts +2 -0
  193. package/build/services/PhoneVideoCoordinator.d.ts.map +1 -1
  194. package/build/services/PhoneVideoCoordinator.js +10 -0
  195. package/build/services/PhoneVideoCoordinator.js.map +1 -1
  196. package/build/services/SceneRenderer.d.ts.map +1 -1
  197. package/build/services/SceneRenderer.js +15 -0
  198. package/build/services/SceneRenderer.js.map +1 -1
  199. package/build/services/SentenceTtsPipeline.d.ts +31 -0
  200. package/build/services/SentenceTtsPipeline.d.ts.map +1 -0
  201. package/build/services/SentenceTtsPipeline.js +67 -0
  202. package/build/services/SentenceTtsPipeline.js.map +1 -0
  203. package/build/services/SupportProfileSync.d.ts +22 -0
  204. package/build/services/SupportProfileSync.d.ts.map +1 -0
  205. package/build/services/SupportProfileSync.js +213 -0
  206. package/build/services/SupportProfileSync.js.map +1 -0
  207. package/build/services/TranscriptionRouting.d.ts +12 -0
  208. package/build/services/TranscriptionRouting.d.ts.map +1 -0
  209. package/build/services/TranscriptionRouting.js +24 -0
  210. package/build/services/TranscriptionRouting.js.map +1 -0
  211. package/build/services/TtsTextSanitizer.d.ts +5 -0
  212. package/build/services/TtsTextSanitizer.d.ts.map +1 -0
  213. package/build/services/TtsTextSanitizer.js +61 -0
  214. package/build/services/TtsTextSanitizer.js.map +1 -0
  215. package/build/services/WifiAdbRequest.d.ts +17 -0
  216. package/build/services/WifiAdbRequest.d.ts.map +1 -0
  217. package/build/services/WifiAdbRequest.js +35 -0
  218. package/build/services/WifiAdbRequest.js.map +1 -0
  219. package/build/services/asg/asgCameraApi.d.ts +43 -0
  220. package/build/services/asg/asgCameraApi.d.ts.map +1 -1
  221. package/build/services/asg/asgCameraApi.js +509 -26
  222. package/build/services/asg/asgCameraApi.js.map +1 -1
  223. package/build/services/asg/cameraRollExportCoordinator.d.ts +60 -0
  224. package/build/services/asg/cameraRollExportCoordinator.d.ts.map +1 -0
  225. package/build/services/asg/cameraRollExportCoordinator.js +618 -0
  226. package/build/services/asg/cameraRollExportCoordinator.js.map +1 -0
  227. package/build/services/asg/cameraRollExportLedger.d.ts +44 -0
  228. package/build/services/asg/cameraRollExportLedger.d.ts.map +1 -0
  229. package/build/services/asg/cameraRollExportLedger.js +78 -0
  230. package/build/services/asg/cameraRollExportLedger.js.map +1 -0
  231. package/build/services/asg/galleryNotices.d.ts +1 -1
  232. package/build/services/asg/galleryNotices.d.ts.map +1 -1
  233. package/build/services/asg/galleryNotices.js.map +1 -1
  234. package/build/services/asg/galleryPathMigration.d.ts +3 -0
  235. package/build/services/asg/galleryPathMigration.d.ts.map +1 -0
  236. package/build/services/asg/galleryPathMigration.js +26 -0
  237. package/build/services/asg/galleryPathMigration.js.map +1 -0
  238. package/build/services/asg/gallerySettingsService.d.ts +1 -0
  239. package/build/services/asg/gallerySettingsService.d.ts.map +1 -1
  240. package/build/services/asg/gallerySettingsService.js +2 -0
  241. package/build/services/asg/gallerySettingsService.js.map +1 -1
  242. package/build/services/asg/gallerySyncService.d.ts.map +1 -1
  243. package/build/services/asg/gallerySyncService.js +162 -56
  244. package/build/services/asg/gallerySyncService.js.map +1 -1
  245. package/build/services/asg/galleryTransferLedger.d.ts +58 -0
  246. package/build/services/asg/galleryTransferLedger.d.ts.map +1 -0
  247. package/build/services/asg/galleryTransferLedger.js +225 -0
  248. package/build/services/asg/galleryTransferLedger.js.map +1 -0
  249. package/build/services/asg/localNetworkTransport.d.ts +15 -0
  250. package/build/services/asg/localNetworkTransport.d.ts.map +1 -0
  251. package/build/services/asg/localNetworkTransport.js +144 -0
  252. package/build/services/asg/localNetworkTransport.js.map +1 -0
  253. package/build/services/asg/localStorageService.d.ts +18 -0
  254. package/build/services/asg/localStorageService.d.ts.map +1 -1
  255. package/build/services/asg/localStorageService.js +168 -19
  256. package/build/services/asg/localStorageService.js.map +1 -1
  257. package/build/services/asg/mediaProcessingQueue.d.ts +10 -0
  258. package/build/services/asg/mediaProcessingQueue.d.ts.map +1 -1
  259. package/build/services/asg/mediaProcessingQueue.js +193 -62
  260. package/build/services/asg/mediaProcessingQueue.js.map +1 -1
  261. package/build/services/audioPlaybackAssets.d.ts +8 -0
  262. package/build/services/audioPlaybackAssets.d.ts.map +1 -0
  263. package/build/services/audioPlaybackAssets.js +7 -0
  264. package/build/services/audioPlaybackAssets.js.map +1 -0
  265. package/build/services/gallerySyncClock.js +1 -1
  266. package/build/services/gallerySyncClock.js.map +1 -1
  267. package/build/services/glassesClockSync.js +2 -2
  268. package/build/services/glassesClockSync.js.map +1 -1
  269. package/build/services/manifestActions.d.ts +7 -0
  270. package/build/services/manifestActions.d.ts.map +1 -0
  271. package/build/services/manifestActions.js +25 -0
  272. package/build/services/manifestActions.js.map +1 -0
  273. package/build/services/otaDisplayState.d.ts +22 -11
  274. package/build/services/otaDisplayState.d.ts.map +1 -1
  275. package/build/services/otaDisplayState.js +36 -14
  276. package/build/services/otaDisplayState.js.map +1 -1
  277. package/build/services/otaInstallPolicy.d.ts +15 -3
  278. package/build/services/otaInstallPolicy.d.ts.map +1 -1
  279. package/build/services/otaInstallPolicy.js +24 -3
  280. package/build/services/otaInstallPolicy.js.map +1 -1
  281. package/build/services/otaManifestUrl.d.ts +2 -0
  282. package/build/services/otaManifestUrl.d.ts.map +1 -0
  283. package/build/services/{asgOtaVersionUrl.js → otaManifestUrl.js} +15 -5
  284. package/build/services/otaManifestUrl.js.map +1 -0
  285. package/build/services/slimStreamStatus.d.ts +11 -0
  286. package/build/services/slimStreamStatus.d.ts.map +1 -1
  287. package/build/services/slimStreamStatus.js +14 -4
  288. package/build/services/slimStreamStatus.js.map +1 -1
  289. package/build/stores/apps.d.ts.map +1 -1
  290. package/build/stores/apps.js +11 -0
  291. package/build/stores/apps.js.map +1 -1
  292. package/build/stores/bluetoothSettingKeys.d.ts.map +1 -1
  293. package/build/stores/bluetoothSettingKeys.js +2 -0
  294. package/build/stores/bluetoothSettingKeys.js.map +1 -1
  295. package/build/stores/gallerySync.d.ts +2 -0
  296. package/build/stores/gallerySync.d.ts.map +1 -1
  297. package/build/stores/gallerySync.js +2 -0
  298. package/build/stores/gallerySync.js.map +1 -1
  299. package/build/stores/glasses.d.ts.map +1 -1
  300. package/build/stores/glasses.js +3 -0
  301. package/build/stores/glasses.js.map +1 -1
  302. package/build/stores/settings.d.ts +17 -0
  303. package/build/stores/settings.d.ts.map +1 -1
  304. package/build/stores/settings.js +157 -17
  305. package/build/stores/settings.js.map +1 -1
  306. package/build/types/applet.d.ts +2 -0
  307. package/build/types/applet.d.ts.map +1 -1
  308. package/build/types/applet.js.map +1 -1
  309. package/build/types/asg.d.ts +7 -0
  310. package/build/types/asg.d.ts.map +1 -1
  311. package/build/types/asg.js.map +1 -1
  312. package/build/types/enums.d.ts +1 -0
  313. package/build/types/enums.d.ts.map +1 -1
  314. package/build/types/enums.js +1 -0
  315. package/build/types/enums.js.map +1 -1
  316. package/build/types/hardware.d.ts +1 -0
  317. package/build/types/hardware.d.ts.map +1 -1
  318. package/build/types/hardware.js +14 -0
  319. package/build/types/hardware.js.map +1 -1
  320. package/build/utils/configuredDevHost.d.ts +6 -0
  321. package/build/utils/configuredDevHost.d.ts.map +1 -0
  322. package/build/utils/configuredDevHost.js +19 -0
  323. package/build/utils/configuredDevHost.js.map +1 -0
  324. package/build/utils/devMiniappLaunch.d.ts +19 -8
  325. package/build/utils/devMiniappLaunch.d.ts.map +1 -1
  326. package/build/utils/devMiniappLaunch.js +144 -21
  327. package/build/utils/devMiniappLaunch.js.map +1 -1
  328. package/build/utils/diagnosticContext.d.ts.map +1 -1
  329. package/build/utils/diagnosticContext.js +15 -17
  330. package/build/utils/diagnosticContext.js.map +1 -1
  331. package/build/utils/hardware/hardware.d.ts.map +1 -1
  332. package/build/utils/hardware/hardware.js +7 -3
  333. package/build/utils/hardware/hardware.js.map +1 -1
  334. package/build/utils/miniappDiagnostics.d.ts +64 -0
  335. package/build/utils/miniappDiagnostics.d.ts.map +1 -0
  336. package/build/utils/miniappDiagnostics.js +173 -0
  337. package/build/utils/miniappDiagnostics.js.map +1 -0
  338. package/build/utils/permissions/MediaLibraryPermissions.d.ts +11 -0
  339. package/build/utils/permissions/MediaLibraryPermissions.d.ts.map +1 -1
  340. package/build/utils/permissions/MediaLibraryPermissions.js +27 -4
  341. package/build/utils/permissions/MediaLibraryPermissions.js.map +1 -1
  342. package/build/utils/timers/timers.d.ts.map +1 -1
  343. package/build/utils/timers/timers.js +55 -18
  344. package/build/utils/timers/timers.js.map +1 -1
  345. package/package.json +25 -15
  346. package/src/assets/silence.wav +0 -0
  347. package/src/engine.ts +7 -2
  348. package/src/facades/dev.ts +17 -12
  349. package/src/facades/gallery.ts +1 -1
  350. package/src/facades/glasses.ts +17 -4
  351. package/src/facades/glassesSettings.ts +5 -0
  352. package/src/facades/miniapps.ts +7 -0
  353. package/src/facades/ota.ts +19 -4
  354. package/src/facades/pairing.ts +11 -7
  355. package/src/facades/permissions.ts +14 -0
  356. package/src/facades/phoneNotifications.ts +4 -13
  357. package/src/index.ts +26 -2
  358. package/src/internal.ts +3 -1
  359. package/src/react/MentraLiveOtaFlow.tsx +856 -0
  360. package/src/react/index.ts +7 -0
  361. package/src/react/useEngineSnapshot.ts +16 -0
  362. package/src/runtime/bootstrap.ts +33 -1
  363. package/src/runtime/scanQrSeam.ts +20 -0
  364. package/src/services/AppRegistry.ts +224 -149
  365. package/src/services/AudioCloudUplink.ts +23 -1
  366. package/src/services/AudioPlaybackService.ts +287 -38
  367. package/src/services/BlobStore.ts +22 -9
  368. package/src/services/CloudAudioSubscriptionSync.ts +13 -1
  369. package/src/services/CloudClientService.ts +96 -1
  370. package/src/services/DeviceEventRouter.ts +5 -5
  371. package/src/services/ForegroundLocationPermission.ts +24 -0
  372. package/src/services/GlassesSettingsSync.ts +9 -3
  373. package/src/services/GlassesStatusProjection.ts +16 -8
  374. package/src/services/HotspotOtaTransport.ts +184 -0
  375. package/src/services/HotspotShutdown.ts +41 -0
  376. package/src/services/LocalDisplayManager.ts +112 -21
  377. package/src/services/LocalMiniappRuntime.ts +967 -346
  378. package/src/services/LocalMiniappStorage.ts +82 -0
  379. package/src/services/LocalMiniappUserIdentity.ts +46 -0
  380. package/src/services/LocalSttFallbackCoordinator.ts +19 -12
  381. package/src/services/MicStateCoordinator.ts +169 -4
  382. package/src/services/MiniappLauncher.ts +30 -3
  383. package/src/services/MiniappLiveness.ts +24 -0
  384. package/src/services/NativeHttpResponse.ts +6 -0
  385. package/src/services/OtaArtifactDownloader.ts +312 -0
  386. package/src/services/OtaAutoChain.ts +106 -0
  387. package/src/services/OtaErrorMapping.ts +80 -0
  388. package/src/services/OtaInstallCoordinator.ts +645 -143
  389. package/src/services/OtaService.ts +11 -5
  390. package/src/services/OtaUpdateCheckService.ts +180 -20
  391. package/src/services/PhoneCalendarHelpers.ts +212 -0
  392. package/src/services/PhoneCalendarService.ts +65 -0
  393. package/src/services/PhoneCameraFovCoordinator.ts +210 -0
  394. package/src/services/PhoneNotificationsSync.ts +12 -10
  395. package/src/services/PhonePhotoCoordinator.ts +116 -18
  396. package/src/services/PhoneStreamCoordinator.ts +139 -25
  397. package/src/services/PhoneVideoCoordinator.ts +11 -0
  398. package/src/services/SceneRenderer.ts +16 -0
  399. package/src/services/SentenceTtsPipeline.ts +106 -0
  400. package/src/services/SupportProfileSync.ts +218 -0
  401. package/src/services/TranscriptionRouting.ts +37 -0
  402. package/src/services/TtsTextSanitizer.ts +66 -0
  403. package/src/services/WifiAdbRequest.ts +53 -0
  404. package/src/services/asg/asgCameraApi.ts +615 -33
  405. package/src/services/asg/cameraRollExportCoordinator.ts +627 -0
  406. package/src/services/asg/cameraRollExportLedger.ts +122 -0
  407. package/src/services/asg/galleryNotices.ts +1 -0
  408. package/src/services/asg/galleryPathMigration.ts +22 -0
  409. package/src/services/asg/gallerySettingsService.ts +3 -0
  410. package/src/services/asg/gallerySyncService.ts +184 -73
  411. package/src/services/asg/galleryTransferLedger.ts +312 -0
  412. package/src/services/asg/localNetworkTransport.ts +169 -0
  413. package/src/services/asg/localStorageService.ts +179 -24
  414. package/src/services/asg/mediaProcessingQueue.ts +209 -62
  415. package/src/services/audioPlaybackAssets.ts +8 -0
  416. package/src/services/gallerySyncClock.ts +1 -1
  417. package/src/services/glassesClockSync.ts +2 -2
  418. package/src/services/manifestActions.ts +30 -0
  419. package/src/services/otaDisplayState.ts +57 -16
  420. package/src/services/otaInstallPolicy.ts +29 -3
  421. package/src/services/{asgOtaVersionUrl.ts → otaManifestUrl.ts} +16 -4
  422. package/src/services/slimStreamStatus.ts +16 -5
  423. package/src/stores/apps.ts +12 -0
  424. package/src/stores/bluetoothSettingKeys.ts +2 -0
  425. package/src/stores/gallerySync.ts +4 -0
  426. package/src/stores/glasses.ts +3 -0
  427. package/src/stores/settings.ts +179 -18
  428. package/src/types/applet.ts +2 -0
  429. package/src/types/asg.ts +7 -0
  430. package/src/types/enums.ts +2 -1
  431. package/src/types/hardware.ts +17 -0
  432. package/src/utils/configuredDevHost.ts +17 -0
  433. package/src/utils/devMiniappLaunch.ts +151 -23
  434. package/src/utils/diagnosticContext.ts +17 -17
  435. package/src/utils/hardware/hardware.ts +7 -9
  436. package/src/utils/miniappDiagnostics.ts +226 -0
  437. package/src/utils/permissions/MediaLibraryPermissions.ts +35 -4
  438. package/src/utils/timers/timers.ts +57 -21
  439. package/build/services/asgOtaVersionUrl.d.ts +0 -2
  440. package/build/services/asgOtaVersionUrl.d.ts.map +0 -1
  441. package/build/services/asgOtaVersionUrl.js.map +0 -1
@@ -1,27 +1,63 @@
1
- // until https://github.com/tconns/react-native-nitro-bg-timer/issues/2 is resolved, we need to use this class to disable this package on iOS:
2
- import { Platform } from "react-native";
1
+ // BgTimer — background-safe timers for the engine.
2
+ //
3
+ // Everything timing-critical in the engine runs through this class: the
4
+ // miniapp liveness watchdog, display durationMs expiries, boot windows,
5
+ // auth-token refresh. On Android, React Native PAUSES plain JS timers while
6
+ // the app is backgrounded, so those features only keep working in the
7
+ // background when the native implementation (react-native-nitro-bg-timer)
8
+ // is active.
9
+ //
10
+ // Who actually gets native timers:
11
+ // - Android, dev AND release: always attempted — dev must match production
12
+ // background behavior. There is no opt-in/opt-out env var. If the native
13
+ // module is unavailable (a dev client whose binary predates it, or a
14
+ // broken release binary), the failure is LOUD: a console.error with the
15
+ // remediation, plus a blocking dev alert. The JS-timer fallback still
16
+ // engages so foreground behavior keeps working, but backgrounded
17
+ // behavior is broken until the native rebuild — the error says exactly
18
+ // that. (A silent dev fallback previously cost a full debugging day of
19
+ // phantom "zombie miniapp" bugs; see OS-1790.)
20
+ // - iOS: never, dev or prod — the package is disabled pending
21
+ // https://github.com/tconns/react-native-nitro-bg-timer/issues/2. iOS
22
+ // suspends the whole process in background anyway, so background timing
23
+ // there is handled by native per-device queues, not JS timers.
24
+ import { Alert, Platform } from "react-native";
3
25
  let nitroTimer;
4
26
  let nitroDisabled = false;
5
- let warnedUnavailable = false;
6
- function warnUnavailable(reason) {
7
- if (warnedUnavailable) {
27
+ let failedLoud = false;
28
+ /**
29
+ * The native background timer is unavailable — say so ONCE, loudly, with the
30
+ * remediation. The JS-timer fallback keeps foreground behavior working, but
31
+ * everything timing-driven freezes while the app is backgrounded, which
32
+ * presents as phantom miniapp bugs (frozen captions, missed wake words,
33
+ * watchdog kills on resume). A quiet warning here has already cost a full
34
+ * debugging day; never downgrade this to console.warn.
35
+ */
36
+ function failUnavailable(reason) {
37
+ if (failedLoud) {
8
38
  return;
9
39
  }
10
- warnedUnavailable = true;
11
- console.warn(`BgTimer: ${reason}, using JS timers`);
40
+ failedLoud = true;
41
+ const message = `BgTimer: native background timer unavailable (${reason}). ` +
42
+ "Backgrounded behavior IS BROKEN until you rebuild the Android app: run `bun android`. " +
43
+ "Engine timers and local miniapps (captions, wake words) freeze whenever the app is not foregrounded.";
44
+ console.error(message);
45
+ if (__DEV__) {
46
+ // A stale dev client may ALSO show the library's own require-time red box
47
+ // (bridgeless LogBox surfaces it even though we catch below) — this alert
48
+ // is the actionable one: it names the fix.
49
+ try {
50
+ Alert.alert("Background timers unavailable", message);
51
+ }
52
+ catch {
53
+ /* Alert not available (tests / headless) — the console.error stands. */
54
+ }
55
+ }
12
56
  }
13
57
  function getNitroTimer() {
14
58
  if (nitroDisabled || Platform.OS !== "android") {
15
59
  return null;
16
60
  }
17
- // react-native-nitro-bg-timer calls createHybridObject() at require() time. On dev
18
- // builds with a stale native binary that throws NPE, React Native still surfaces a
19
- // red LogBox error even when the exception is caught. Skip nitro in __DEV__ unless
20
- // explicitly enabled after a native rebuild (bun android).
21
- if (__DEV__ && process.env.EXPO_PUBLIC_USE_NITRO_BG_TIMER !== "true") {
22
- warnUnavailable("nitro bg-timer disabled in dev (set EXPO_PUBLIC_USE_NITRO_BG_TIMER=true after native rebuild)");
23
- return null;
24
- }
25
61
  if (nitroTimer !== undefined) {
26
62
  return nitroTimer;
27
63
  }
@@ -29,7 +65,7 @@ function getNitroTimer() {
29
65
  const { isRuntimeAlive } = require("react-native-nitro-modules");
30
66
  if (typeof isRuntimeAlive === "function" && !isRuntimeAlive()) {
31
67
  nitroTimer = null;
32
- warnUnavailable("nitro runtime is not alive");
68
+ failUnavailable("nitro runtime is not alive");
33
69
  return nitroTimer;
34
70
  }
35
71
  const { BackgroundTimer } = require("react-native-nitro-bg-timer");
@@ -47,7 +83,7 @@ function getNitroTimer() {
47
83
  nitroDisabled = true;
48
84
  }
49
85
  if (!nitroTimer) {
50
- warnUnavailable("react-native-nitro-bg-timer unavailable");
86
+ failUnavailable("react-native-nitro-bg-timer failed to load");
51
87
  }
52
88
  return nitroTimer;
53
89
  }
@@ -62,7 +98,8 @@ function withNitro(run, fallback) {
62
98
  catch (error) {
63
99
  nitroDisabled = true;
64
100
  nitroTimer = null;
65
- console.warn("BgTimer: nitro timer failed, using JS timers", error);
101
+ failedLoud = false; // a mid-session failure is new information — re-announce
102
+ failUnavailable(`nitro timer call threw: ${String(error)}`);
66
103
  return fallback();
67
104
  }
68
105
  }
@@ -1 +1 @@
1
- {"version":3,"file":"timers.js","sourceRoot":"","sources":["../../../src/utils/timers/timers.ts"],"names":[],"mappings":"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","sourcesContent":["// until https://github.com/tconns/react-native-nitro-bg-timer/issues/2 is resolved, we need to use this class to disable this package on iOS:\n\nimport {Platform} from \"react-native\"\n\ntype NitroTimerApi = {\n setInterval: (callback: () => void, delay: number) => number\n clearInterval: (intervalId: number) => void\n setTimeout: (callback: () => void, delay: number) => number\n clearTimeout: (timeoutId: number) => void\n}\n\nlet nitroTimer: NitroTimerApi | null | undefined\nlet nitroDisabled = false\nlet warnedUnavailable = false\n\nfunction warnUnavailable(reason: string) {\n if (warnedUnavailable) {\n return\n }\n warnedUnavailable = true\n console.warn(`BgTimer: ${reason}, using JS timers`)\n}\n\nfunction getNitroTimer(): NitroTimerApi | null {\n if (nitroDisabled || Platform.OS !== \"android\") {\n return null\n }\n\n // react-native-nitro-bg-timer calls createHybridObject() at require() time. On dev\n // builds with a stale native binary that throws NPE, React Native still surfaces a\n // red LogBox error even when the exception is caught. Skip nitro in __DEV__ unless\n // explicitly enabled after a native rebuild (bun android).\n if (__DEV__ && process.env.EXPO_PUBLIC_USE_NITRO_BG_TIMER !== \"true\") {\n warnUnavailable(\"nitro bg-timer disabled in dev (set EXPO_PUBLIC_USE_NITRO_BG_TIMER=true after native rebuild)\")\n return null\n }\n\n if (nitroTimer !== undefined) {\n return nitroTimer\n }\n\n try {\n const {isRuntimeAlive} = require(\"react-native-nitro-modules\") as {isRuntimeAlive?: () => boolean}\n if (typeof isRuntimeAlive === \"function\" && !isRuntimeAlive()) {\n nitroTimer = null\n warnUnavailable(\"nitro runtime is not alive\")\n return nitroTimer\n }\n\n const {BackgroundTimer} = require(\"react-native-nitro-bg-timer\") as {BackgroundTimer?: NitroTimerApi}\n nitroTimer =\n BackgroundTimer &&\n typeof BackgroundTimer.setTimeout === \"function\" &&\n typeof BackgroundTimer.setInterval === \"function\" &&\n typeof BackgroundTimer.clearTimeout === \"function\" &&\n typeof BackgroundTimer.clearInterval === \"function\"\n ? BackgroundTimer\n : null\n } catch {\n nitroTimer = null\n nitroDisabled = true\n }\n\n if (!nitroTimer) {\n warnUnavailable(\"react-native-nitro-bg-timer unavailable\")\n }\n return nitroTimer\n}\n\nfunction withNitro<T>(run: (api: NitroTimerApi) => T, fallback: () => T): T {\n const api = getNitroTimer()\n if (!api) {\n return fallback()\n }\n try {\n return run(api)\n } catch (error) {\n nitroDisabled = true\n nitroTimer = null\n console.warn(\"BgTimer: nitro timer failed, using JS timers\", error)\n return fallback()\n }\n}\n\nexport class BgTimer {\n static setInterval(callback: () => void, delay: number): number {\n return withNitro(\n (api) => api.setInterval(callback, delay),\n () => setInterval(callback, delay) as unknown as number,\n )\n }\n\n static clearInterval(intervalId: number): void {\n withNitro(\n (api) => api.clearInterval(intervalId),\n () => clearInterval(intervalId),\n )\n }\n\n static setTimeout(callback: () => void, delay: number): number {\n return withNitro(\n (api) => api.setTimeout(callback, delay),\n () => setTimeout(callback, delay) as unknown as number,\n )\n }\n\n static clearTimeout(timeoutId: number): void {\n withNitro(\n (api) => api.clearTimeout(timeoutId),\n () => clearTimeout(timeoutId),\n )\n }\n}\n\nexport function throttle<T extends (...args: any[]) => any>(fn: T, ms: number): (...args: Parameters<T>) => void {\n let lastCalled = 0\n\n return (...args: Parameters<T>) => {\n const now = Date.now()\n if (now - lastCalled >= ms) {\n lastCalled = now\n fn(...args)\n }\n }\n}\n\nexport function debounce<T extends (...args: any[]) => any>(fn: T, ms: number): (...args: Parameters<T>) => void {\n let timeoutId: number | null = null\n\n return (...args: Parameters<T>) => {\n if (timeoutId) {\n BgTimer.clearTimeout(timeoutId)\n }\n timeoutId = BgTimer.setTimeout(() => {\n fn(...args)\n timeoutId = null\n }, ms)\n }\n}\n"]}
1
+ {"version":3,"file":"timers.js","sourceRoot":"","sources":["../../../src/utils/timers/timers.ts"],"names":[],"mappings":"AAAA,mDAAmD;AACnD,EAAE;AACF,wEAAwE;AACxE,wEAAwE;AACxE,4EAA4E;AAC5E,sEAAsE;AACtE,0EAA0E;AAC1E,aAAa;AACb,EAAE;AACF,mCAAmC;AACnC,6EAA6E;AAC7E,6EAA6E;AAC7E,yEAAyE;AACzE,4EAA4E;AAC5E,0EAA0E;AAC1E,qEAAqE;AACrE,2EAA2E;AAC3E,2EAA2E;AAC3E,mDAAmD;AACnD,gEAAgE;AAChE,0EAA0E;AAC1E,4EAA4E;AAC5E,mEAAmE;AAEnE,OAAO,EAAC,KAAK,EAAE,QAAQ,EAAC,MAAM,cAAc,CAAA;AAS5C,IAAI,UAA4C,CAAA;AAChD,IAAI,aAAa,GAAG,KAAK,CAAA;AACzB,IAAI,UAAU,GAAG,KAAK,CAAA;AAEtB;;;;;;;GAOG;AACH,SAAS,eAAe,CAAC,MAAc;IACrC,IAAI,UAAU,EAAE,CAAC;QACf,OAAM;IACR,CAAC;IACD,UAAU,GAAG,IAAI,CAAA;IACjB,MAAM,OAAO,GACX,iDAAiD,MAAM,KAAK;QAC5D,wFAAwF;QACxF,sGAAsG,CAAA;IACxG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IACtB,IAAI,OAAO,EAAE,CAAC;QACZ,0EAA0E;QAC1E,0EAA0E;QAC1E,2CAA2C;QAC3C,IAAI,CAAC;YACH,KAAK,CAAC,KAAK,CAAC,+BAA+B,EAAE,OAAO,CAAC,CAAA;QACvD,CAAC;QAAC,MAAM,CAAC;YACP,wEAAwE;QAC1E,CAAC;IACH,CAAC;AACH,CAAC;AAED,SAAS,aAAa;IACpB,IAAI,aAAa,IAAI,QAAQ,CAAC,EAAE,KAAK,SAAS,EAAE,CAAC;QAC/C,OAAO,IAAI,CAAA;IACb,CAAC;IAED,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;QAC7B,OAAO,UAAU,CAAA;IACnB,CAAC;IAED,IAAI,CAAC;QACH,MAAM,EAAC,cAAc,EAAC,GAAG,OAAO,CAAC,4BAA4B,CAAqC,CAAA;QAClG,IAAI,OAAO,cAAc,KAAK,UAAU,IAAI,CAAC,cAAc,EAAE,EAAE,CAAC;YAC9D,UAAU,GAAG,IAAI,CAAA;YACjB,eAAe,CAAC,4BAA4B,CAAC,CAAA;YAC7C,OAAO,UAAU,CAAA;QACnB,CAAC;QAED,MAAM,EAAC,eAAe,EAAC,GAAG,OAAO,CAAC,6BAA6B,CAAsC,CAAA;QACrG,UAAU;YACR,eAAe;gBACf,OAAO,eAAe,CAAC,UAAU,KAAK,UAAU;gBAChD,OAAO,eAAe,CAAC,WAAW,KAAK,UAAU;gBACjD,OAAO,eAAe,CAAC,YAAY,KAAK,UAAU;gBAClD,OAAO,eAAe,CAAC,aAAa,KAAK,UAAU;gBACjD,CAAC,CAAC,eAAe;gBACjB,CAAC,CAAC,IAAI,CAAA;IACZ,CAAC;IAAC,MAAM,CAAC;QACP,UAAU,GAAG,IAAI,CAAA;QACjB,aAAa,GAAG,IAAI,CAAA;IACtB,CAAC;IAED,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,eAAe,CAAC,4CAA4C,CAAC,CAAA;IAC/D,CAAC;IACD,OAAO,UAAU,CAAA;AACnB,CAAC;AAED,SAAS,SAAS,CAAI,GAA8B,EAAE,QAAiB;IACrE,MAAM,GAAG,GAAG,aAAa,EAAE,CAAA;IAC3B,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,OAAO,QAAQ,EAAE,CAAA;IACnB,CAAC;IACD,IAAI,CAAC;QACH,OAAO,GAAG,CAAC,GAAG,CAAC,CAAA;IACjB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,aAAa,GAAG,IAAI,CAAA;QACpB,UAAU,GAAG,IAAI,CAAA;QACjB,UAAU,GAAG,KAAK,CAAA,CAAC,yDAAyD;QAC5E,eAAe,CAAC,2BAA2B,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;QAC3D,OAAO,QAAQ,EAAE,CAAA;IACnB,CAAC;AACH,CAAC;AAED,MAAM,OAAO,OAAO;IAClB,MAAM,CAAC,WAAW,CAAC,QAAoB,EAAE,KAAa;QACpD,OAAO,SAAS,CACd,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,WAAW,CAAC,QAAQ,EAAE,KAAK,CAAC,EACzC,GAAG,EAAE,CAAC,WAAW,CAAC,QAAQ,EAAE,KAAK,CAAsB,CACxD,CAAA;IACH,CAAC;IAED,MAAM,CAAC,aAAa,CAAC,UAAkB;QACrC,SAAS,CACP,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,aAAa,CAAC,UAAU,CAAC,EACtC,GAAG,EAAE,CAAC,aAAa,CAAC,UAAU,CAAC,CAChC,CAAA;IACH,CAAC;IAED,MAAM,CAAC,UAAU,CAAC,QAAoB,EAAE,KAAa;QACnD,OAAO,SAAS,CACd,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,QAAQ,EAAE,KAAK,CAAC,EACxC,GAAG,EAAE,CAAC,UAAU,CAAC,QAAQ,EAAE,KAAK,CAAsB,CACvD,CAAA;IACH,CAAC;IAED,MAAM,CAAC,YAAY,CAAC,SAAiB;QACnC,SAAS,CACP,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,YAAY,CAAC,SAAS,CAAC,EACpC,GAAG,EAAE,CAAC,YAAY,CAAC,SAAS,CAAC,CAC9B,CAAA;IACH,CAAC;CACF;AAED,MAAM,UAAU,QAAQ,CAAoC,EAAK,EAAE,EAAU;IAC3E,IAAI,UAAU,GAAG,CAAC,CAAA;IAElB,OAAO,CAAC,GAAG,IAAmB,EAAE,EAAE;QAChC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;QACtB,IAAI,GAAG,GAAG,UAAU,IAAI,EAAE,EAAE,CAAC;YAC3B,UAAU,GAAG,GAAG,CAAA;YAChB,EAAE,CAAC,GAAG,IAAI,CAAC,CAAA;QACb,CAAC;IACH,CAAC,CAAA;AACH,CAAC;AAED,MAAM,UAAU,QAAQ,CAAoC,EAAK,EAAE,EAAU;IAC3E,IAAI,SAAS,GAAkB,IAAI,CAAA;IAEnC,OAAO,CAAC,GAAG,IAAmB,EAAE,EAAE;QAChC,IAAI,SAAS,EAAE,CAAC;YACd,OAAO,CAAC,YAAY,CAAC,SAAS,CAAC,CAAA;QACjC,CAAC;QACD,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,GAAG,EAAE;YAClC,EAAE,CAAC,GAAG,IAAI,CAAC,CAAA;YACX,SAAS,GAAG,IAAI,CAAA;QAClB,CAAC,EAAE,EAAE,CAAC,CAAA;IACR,CAAC,CAAA;AACH,CAAC","sourcesContent":["// BgTimer — background-safe timers for the engine.\n//\n// Everything timing-critical in the engine runs through this class: the\n// miniapp liveness watchdog, display durationMs expiries, boot windows,\n// auth-token refresh. On Android, React Native PAUSES plain JS timers while\n// the app is backgrounded, so those features only keep working in the\n// background when the native implementation (react-native-nitro-bg-timer)\n// is active.\n//\n// Who actually gets native timers:\n// - Android, dev AND release: always attempted — dev must match production\n// background behavior. There is no opt-in/opt-out env var. If the native\n// module is unavailable (a dev client whose binary predates it, or a\n// broken release binary), the failure is LOUD: a console.error with the\n// remediation, plus a blocking dev alert. The JS-timer fallback still\n// engages so foreground behavior keeps working, but backgrounded\n// behavior is broken until the native rebuild — the error says exactly\n// that. (A silent dev fallback previously cost a full debugging day of\n// phantom \"zombie miniapp\" bugs; see OS-1790.)\n// - iOS: never, dev or prod — the package is disabled pending\n// https://github.com/tconns/react-native-nitro-bg-timer/issues/2. iOS\n// suspends the whole process in background anyway, so background timing\n// there is handled by native per-device queues, not JS timers.\n\nimport {Alert, Platform} from \"react-native\"\n\ntype NitroTimerApi = {\n setInterval: (callback: () => void, delay: number) => number\n clearInterval: (intervalId: number) => void\n setTimeout: (callback: () => void, delay: number) => number\n clearTimeout: (timeoutId: number) => void\n}\n\nlet nitroTimer: NitroTimerApi | null | undefined\nlet nitroDisabled = false\nlet failedLoud = false\n\n/**\n * The native background timer is unavailable — say so ONCE, loudly, with the\n * remediation. The JS-timer fallback keeps foreground behavior working, but\n * everything timing-driven freezes while the app is backgrounded, which\n * presents as phantom miniapp bugs (frozen captions, missed wake words,\n * watchdog kills on resume). A quiet warning here has already cost a full\n * debugging day; never downgrade this to console.warn.\n */\nfunction failUnavailable(reason: string) {\n if (failedLoud) {\n return\n }\n failedLoud = true\n const message =\n `BgTimer: native background timer unavailable (${reason}). ` +\n \"Backgrounded behavior IS BROKEN until you rebuild the Android app: run `bun android`. \" +\n \"Engine timers and local miniapps (captions, wake words) freeze whenever the app is not foregrounded.\"\n console.error(message)\n if (__DEV__) {\n // A stale dev client may ALSO show the library's own require-time red box\n // (bridgeless LogBox surfaces it even though we catch below) — this alert\n // is the actionable one: it names the fix.\n try {\n Alert.alert(\"Background timers unavailable\", message)\n } catch {\n /* Alert not available (tests / headless) the console.error stands. */\n }\n }\n}\n\nfunction getNitroTimer(): NitroTimerApi | null {\n if (nitroDisabled || Platform.OS !== \"android\") {\n return null\n }\n\n if (nitroTimer !== undefined) {\n return nitroTimer\n }\n\n try {\n const {isRuntimeAlive} = require(\"react-native-nitro-modules\") as {isRuntimeAlive?: () => boolean}\n if (typeof isRuntimeAlive === \"function\" && !isRuntimeAlive()) {\n nitroTimer = null\n failUnavailable(\"nitro runtime is not alive\")\n return nitroTimer\n }\n\n const {BackgroundTimer} = require(\"react-native-nitro-bg-timer\") as {BackgroundTimer?: NitroTimerApi}\n nitroTimer =\n BackgroundTimer &&\n typeof BackgroundTimer.setTimeout === \"function\" &&\n typeof BackgroundTimer.setInterval === \"function\" &&\n typeof BackgroundTimer.clearTimeout === \"function\" &&\n typeof BackgroundTimer.clearInterval === \"function\"\n ? BackgroundTimer\n : null\n } catch {\n nitroTimer = null\n nitroDisabled = true\n }\n\n if (!nitroTimer) {\n failUnavailable(\"react-native-nitro-bg-timer failed to load\")\n }\n return nitroTimer\n}\n\nfunction withNitro<T>(run: (api: NitroTimerApi) => T, fallback: () => T): T {\n const api = getNitroTimer()\n if (!api) {\n return fallback()\n }\n try {\n return run(api)\n } catch (error) {\n nitroDisabled = true\n nitroTimer = null\n failedLoud = false // a mid-session failure is new information — re-announce\n failUnavailable(`nitro timer call threw: ${String(error)}`)\n return fallback()\n }\n}\n\nexport class BgTimer {\n static setInterval(callback: () => void, delay: number): number {\n return withNitro(\n (api) => api.setInterval(callback, delay),\n () => setInterval(callback, delay) as unknown as number,\n )\n }\n\n static clearInterval(intervalId: number): void {\n withNitro(\n (api) => api.clearInterval(intervalId),\n () => clearInterval(intervalId),\n )\n }\n\n static setTimeout(callback: () => void, delay: number): number {\n return withNitro(\n (api) => api.setTimeout(callback, delay),\n () => setTimeout(callback, delay) as unknown as number,\n )\n }\n\n static clearTimeout(timeoutId: number): void {\n withNitro(\n (api) => api.clearTimeout(timeoutId),\n () => clearTimeout(timeoutId),\n )\n }\n}\n\nexport function throttle<T extends (...args: any[]) => any>(fn: T, ms: number): (...args: Parameters<T>) => void {\n let lastCalled = 0\n\n return (...args: Parameters<T>) => {\n const now = Date.now()\n if (now - lastCalled >= ms) {\n lastCalled = now\n fn(...args)\n }\n }\n}\n\nexport function debounce<T extends (...args: any[]) => any>(fn: T, ms: number): (...args: Parameters<T>) => void {\n let timeoutId: number | null = null\n\n return (...args: Parameters<T>) => {\n if (timeoutId) {\n BgTimer.clearTimeout(timeoutId)\n }\n timeoutId = BgTimer.setTimeout(() => {\n fn(...args)\n timeoutId = null\n }, ms)\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mentra/engine",
3
- "version": "0.1.0-dev.0",
3
+ "version": "0.1.0-dev.1",
4
4
  "description": "Mentra Engine — miniapp registry, WebView bridge, and miniapp store",
5
5
  "main": "build/index.js",
6
6
  "react-native": "src/index.ts",
@@ -21,6 +21,11 @@
21
21
  "react-native": "./src/devtools.ts",
22
22
  "default": "./build/devtools.js"
23
23
  },
24
+ "./ota": {
25
+ "types": "./build/react/index.d.ts",
26
+ "react-native": "./src/react/index.ts",
27
+ "default": "./build/react/index.js"
28
+ },
24
29
  "./build/*": null,
25
30
  "./src/*": null,
26
31
  "./package.json": "./package.json"
@@ -36,7 +41,7 @@
36
41
  "build": "expo-module build",
37
42
  "clean": "expo-module clean",
38
43
  "lint": "expo-module lint",
39
- "test": "bun test src",
44
+ "test": "./scripts/test.sh",
40
45
  "build:module": "expo-module prepare",
41
46
  "prepublishOnly": "expo-module prepublishOnly",
42
47
  "expo-module": "expo-module"
@@ -81,26 +86,31 @@
81
86
  "typescript": "~5.9.2"
82
87
  },
83
88
  "peerDependencies": {
84
- "expo": "*",
85
- "expo-modules-core": "*",
89
+ "expo": "~55.0.24",
90
+ "expo-modules-core": "~55.0.25",
86
91
  "react": "*",
87
92
  "react-native": "*",
93
+ "react-native-safe-area-context": "*",
94
+ "react-native-svg": "*",
88
95
  "react-native-udp": "*",
89
96
  "@types/react": "*",
90
97
  "react-native-mmkv": "*",
91
98
  "react-native-nitro-bg-timer": "*",
92
99
  "react-native-nitro-modules": "*",
93
- "react-native-reanimated": "*",
94
- "expo-file-system": "*",
100
+ "react-native-reanimated": "4.2.1",
101
+ "react-native-worklets": "0.7.4",
102
+ "expo-file-system": "~55.0.20",
95
103
  "react-native-zip-archive": "*",
96
104
  "zustand": "*",
97
- "expo-battery": "*",
98
- "expo-audio": "*",
99
- "expo-clipboard": "*",
100
- "expo-constants": "*",
101
- "expo-location": "*",
102
- "expo-task-manager": "*",
103
- "expo-notifications": "*",
105
+ "expo-battery": "^55.0.13",
106
+ "expo-audio": "~55.0.14",
107
+ "expo-clipboard": "~55.0.13",
108
+ "expo-calendar": "~55.0.14",
109
+ "expo-constants": "~55.0.16",
110
+ "expo-device": "~55.0.17",
111
+ "expo-location": "~55.1.10",
112
+ "expo-task-manager": "~55.0.16",
113
+ "expo-notifications": "~55.0.23",
104
114
  "react-native-share": "*",
105
115
  "react-native-localize": "*",
106
116
  "react-native-ble-manager": "*",
@@ -111,9 +121,9 @@
111
121
  "@craftzdog/react-native-buffer": "*",
112
122
  "axios": "*",
113
123
  "@mentra/bluetooth-sdk": "*",
114
- "@mentra/crust": "^0.1.0-dev.0",
124
+ "@mentra/crust": "^0.1.0-dev.1",
115
125
  "@mentra/cloud-client": "^0.1.0-dev.0",
116
- "@mentra/miniapp": "^0.3.0-dev.0",
126
+ "@mentra/miniapp": "^0.3.0-dev.1",
117
127
  "@mentra/cloud-protocol": "^0.1.0-dev.0"
118
128
  }
119
129
  }
Binary file
package/src/engine.ts CHANGED
@@ -8,13 +8,14 @@
8
8
  * live on the `@mentra/engine/internal` entry (and the debug singletons on
9
9
  * `@mentra/engine/devtools`); they shrink as screens move onto `engine.*`.
10
10
  */
11
- import {configure, start as bootstrapStart, stop as bootstrapStop} from "./runtime/bootstrap"
11
+ import {configure, start as bootstrapStart, stop as bootstrapStop, updateUiSeams} from "./runtime/bootstrap"
12
12
  import {cloudClientService} from "./services/CloudClientService"
13
13
  import {hydrateDeviceStore, demoteOrphanedDefaultWearable} from "./services/DeviceStoreHydration"
14
14
  import {startGlassesSettingsSync, stopGlassesSettingsSync} from "./services/GlassesSettingsSync"
15
15
  import {startGlassesStatusProjection, stopGlassesStatusProjection} from "./services/GlassesStatusProjection"
16
16
  import {startOtaService, stopOtaService} from "./services/OtaService"
17
17
  import {startAudioCloudUplink, stopAudioCloudUplink} from "./services/AudioCloudUplink"
18
+ import {startSupportProfileSync, stopSupportProfileSync} from "./services/SupportProfileSync"
18
19
  import {startDeviceEventRouter, stopDeviceEventRouter} from "./services/DeviceEventRouter"
19
20
  import {startPhoneNotificationsSync, stopPhoneNotificationsSync} from "./services/PhoneNotificationsSync"
20
21
  import {startCaptionsTesterReportService, stopCaptionsTesterReportService} from "./services/CaptionsTesterReportService"
@@ -45,6 +46,8 @@ import {logBuffer} from "./utils/devLogging"
45
46
  export const engine = {
46
47
  /** Front door — hand engine auth + config, then start/stop the runtime. */
47
48
  configure,
49
+ /** Merge host-UI seams on a running runtime (scan overlay, wifi setup, …). */
50
+ updateUiSeams,
48
51
  /** Start the runtime: mark started + construct/connect the cloud client + begin
49
52
  * syncing device settings to the glasses. */
50
53
  async start() {
@@ -59,7 +62,7 @@ export const engine = {
59
62
  // Project native device status -> the engine stores (the inbound feed the rest
60
63
  // of the runtime reads). Established first so the stores are live before the
61
64
  // syncs below react to them.
62
- startGlassesStatusProjection()
65
+ startGlassesStatusProjection((changed) => localMiniappRuntime.forwardEvent("glasses_connection_state", changed))
63
66
  // Route the rest of the inbound device events (wifi/hotspot/gallery -> stores+bus,
64
67
  // photo/stream -> coordinators, button/touch/accel/head -> miniapps, save_setting ->
65
68
  // store, miniapp_selected -> launcher) so a bare OEM gets device data, not just the
@@ -96,6 +99,7 @@ export const engine = {
96
99
  error instanceof Error ? error.message : error,
97
100
  )
98
101
  }
102
+ startSupportProfileSync()
99
103
  // Push device-setting changes to the glasses for ANY host, so
100
104
  // engine.glasses.settings.set() reaches the device (not just the Mentra app).
101
105
  startGlassesSettingsSync()
@@ -135,6 +139,7 @@ export const engine = {
135
139
  await safely("device event router", stopDeviceEventRouter)
136
140
  await safely("ota service", stopOtaService)
137
141
  await safely("audio cloud uplink", stopAudioCloudUplink)
142
+ await safely("support profile sync", stopSupportProfileSync)
138
143
  await safely("phone notifications sync", stopPhoneNotificationsSync)
139
144
  await safely("captions tester report service", stopCaptionsTesterReportService)
140
145
  await safely("mentrajs crashloop report service", stopMentraJSCrashloopReportService)
@@ -125,8 +125,9 @@ export const dev = {
125
125
  /**
126
126
  * Side-load a miniapp being served from a dev machine (the `mentra dev` / CLI
127
127
  * flow) by its URL: probes `<url>/miniapp.json`, and on a reachable manifest
128
- * registers it into the single dev slot so it appears in `engine.miniapps`
129
- * and can be started. This is the OEM-host equivalent of the first-party
128
+ * registers it under its manifest package so it appears in `engine.miniapps`
129
+ * and can coexist with other dev miniapps. This is the OEM-host equivalent
130
+ * of the first-party
130
131
  * developer-URL screen — the registration itself is engine-internal, so a
131
132
  * host reaches it here rather than the internal registry. Rendering still
132
133
  * flows through the normal display path once the miniapp is started.
@@ -142,25 +143,29 @@ export const dev = {
142
143
  return {ok: false, error: "dev server unreachable, or no miniapp.json served there"}
143
144
  }
144
145
  const manifest = result.manifest
146
+ const appName = name ?? manifest.name ?? "Dev Miniapp"
147
+ const packageName = (manifest.packageName as string | undefined) ?? DEV_APP_PACKAGE_NAME
148
+ // Persist the host that answered — not the stale input IP — so home-tile
149
+ // relaunches skip a dead LAN address after mDNS/Metro failover.
150
+ const resolvedUrl = result.resolvedUrl || devUrl
145
151
  // The `mentra-miniapp dev` server puts the hot-reload/log sidecar on the
146
152
  // user port PLUS ONE — same convention as the first-party developer-URL
147
153
  // screen's deriveDevPort.
148
154
  let devPort: number | undefined
149
155
  try {
150
- const parsed = Number(new URL(devUrl).port)
156
+ const parsed = Number(new URL(resolvedUrl).port)
151
157
  devPort = Number.isFinite(parsed) && parsed > 0 ? parsed + 1 : undefined
152
158
  } catch {
153
159
  devPort = undefined
154
160
  }
155
- const appName = name ?? manifest.name ?? "Dev Miniapp"
156
- registerDevApp({
157
- // The REAL manifest package: registerDevApp preserves it as
158
- // sourcePackageName (miniapp auth + dev-slot cleanup key off it) before
159
- // forcing packageName to the single dev slot.
160
- packageName: (manifest.packageName as string | undefined) ?? DEV_APP_PACKAGE_NAME,
161
+ const existing = useAppStatusStore.getState().apps.find((app) => app.packageName === packageName)
162
+ if (existing?.running) await useAppStatusStore.getState().stop(packageName)
163
+ await registerDevApp({
164
+ packageName,
161
165
  name: appName,
162
- iconUrl: manifest.icon ? new URL(manifest.icon, `${devUrl}/`).toString() : `${devUrl}/icon.png`,
163
- devUrl,
166
+ iconUrl: manifest.icon ? new URL(manifest.icon, `${resolvedUrl}/`).toString() : `${resolvedUrl}/icon.png`,
167
+ // Persist the host that answered — not the stale input IP.
168
+ devUrl: resolvedUrl,
164
169
  devPort,
165
170
  type: manifest.type as DevAppRecord["type"],
166
171
  permissions: manifest.permissions as DevAppRecord["permissions"],
@@ -168,7 +173,7 @@ export const dev = {
168
173
  actions: manifest.actions as DevAppRecord["actions"],
169
174
  })
170
175
  await useAppStatusStore.getState().refresh()
171
- return {ok: true, packageName: DEV_APP_PACKAGE_NAME, name: appName}
176
+ return {ok: true, packageName, name: appName}
172
177
  },
173
178
 
174
179
  /** Current engine runtime status for engine-owned debug surfaces. */
@@ -22,7 +22,7 @@ function projectStatus() {
22
22
  processingFiles: Array.from(progress.processingFiles),
23
23
  processedFiles: s.processedFiles,
24
24
  isSyncing: selectIssyncing(s),
25
- isStarting: gallerySyncService.isSyncStarting(),
25
+ isStarting: s.syncStarting,
26
26
  glassesGallery: selectGlassesGalleryStatus(s),
27
27
  // Copy: the snapshot must not hand callers a mutable reference into the store
28
28
  // (each queue item is copied too — callers must not mutate them in place).
@@ -18,6 +18,7 @@ import {hasDefaultDevice} from "../services/DeviceStoreHydration"
18
18
  import {projectPairingIdentity} from "../services/PairingIdentity"
19
19
  import {isGlassesConnected, isGlassesReady} from "../services/GlassesReadiness"
20
20
  import {pushAllBluetoothSettings} from "../services/GlassesSettingsSync"
21
+ import {recordSupportProfileConnectionFailure} from "../services/SupportProfileSync"
21
22
  import {getModelCapabilities, type DeviceTypes} from "../types"
22
23
  import {glassesWifi} from "./glassesWifi"
23
24
  import {glassesSettings} from "./glassesSettings"
@@ -62,6 +63,7 @@ function projectInfo() {
62
63
  serialNumber: s.serialNumber,
63
64
  buildNumber: s.buildNumber,
64
65
  btMac: s.bluetoothMacAddress,
66
+ wifiMac: s.wifiMacAddress,
65
67
  leftMac: s.leftMacAddress,
66
68
  rightMac: s.rightMacAddress,
67
69
  // Copy: the snapshot must not hand callers a mutable reference into the store.
@@ -81,15 +83,25 @@ export const glasses = {
81
83
  * the glasses (this used to be a host-side step before `connectDefault`).
82
84
  */
83
85
  connectDefault: async (): Promise<void> => {
84
- await pushAllBluetoothSettings()
85
- return BluetoothSdk.connectDefault()
86
+ try {
87
+ await pushAllBluetoothSettings()
88
+ await BluetoothSdk.connectDefault()
89
+ } catch (error) {
90
+ recordSupportProfileConnectionFailure(error, "connect_default")
91
+ throw error
92
+ }
86
93
  },
87
94
  disconnect: (): Promise<void> => BluetoothSdk.disconnect(),
88
95
  forget: (): Promise<void> => BluetoothSdk.forget(),
89
96
  /** Connect to a specific (discovered) device. */
90
97
  connect: async (...args: Parameters<typeof BluetoothSdk.connect>): Promise<void> => {
91
- await pushAllBluetoothSettings()
92
- return BluetoothSdk.connect(...args)
98
+ try {
99
+ await pushAllBluetoothSettings()
100
+ await BluetoothSdk.connect(...args)
101
+ } catch (error) {
102
+ recordSupportProfileConnectionFailure(error, "connect")
103
+ throw error
104
+ }
93
105
  },
94
106
  /** Connect the built-in simulated glasses (dev/testing). */
95
107
  connectSimulated: (): Promise<void> => BluetoothSdk.connectSimulated(),
@@ -110,6 +122,7 @@ export const glasses = {
110
122
  await pushAllBluetoothSettings()
111
123
  await BluetoothSdk.connectDefault()
112
124
  } catch (error) {
125
+ recordSupportProfileConnectionFailure(error, "reconnect")
113
126
  console.warn("engine.glasses.reconnect failed:", error)
114
127
  return false
115
128
  }
@@ -22,6 +22,7 @@ const INTERNAL_KEYS = new Set<string>([
22
22
  SETTINGS.default_wearable.key,
23
23
  SETTINGS.device_name.key,
24
24
  SETTINGS.device_address.key,
25
+ SETTINGS.project_name.key,
25
26
  SETTINGS.pending_controller.key,
26
27
  SETTINGS.default_controller.key,
27
28
  SETTINGS.controller_device_name.key,
@@ -54,3 +55,7 @@ export const glassesSettings = {
54
55
  // Copy: callers must not be able to mutate the module-level key list.
55
56
  available: (): string[] => [...DEVICE_KEYS],
56
57
  }
58
+
59
+
60
+
61
+
@@ -9,6 +9,7 @@
9
9
  * `<WebView>` wired to them. This facade is the lifecycle half.
10
10
  */
11
11
  import {useAppStatusStore} from "../stores/apps"
12
+ import localMiniappRuntime from "../services/LocalMiniappRuntime"
12
13
  import type {ClientApp} from "../types"
13
14
 
14
15
  type AppsState = ReturnType<typeof useAppStatusStore.getState>
@@ -29,6 +30,12 @@ export const miniapps = {
29
30
  })
30
31
  },
31
32
 
33
+ /** Miniapps actively subscribed to Mentra Live hardware-button events. */
34
+ buttonPressSubscribers: (): string[] => localMiniappRuntime.getButtonPressSubscribers(),
35
+ /** Observe the active Mentra Live hardware-button subscriber set. */
36
+ onButtonPressSubscribersChanged: (cb: (packageNames: string[]) => void): (() => void) =>
37
+ localMiniappRuntime.onButtonPressSubscribersChanged(cb),
38
+
32
39
  /** Re-fetch the installed-app list from the backend. */
33
40
  refresh: () => useAppStatusStore.getState().refresh(),
34
41
  /** Start (foreground) a miniapp. */
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * ota facade — `engine.ota`: the OEM-facing OTA read/observe surface. Engine's
3
3
  * OtaService projects the glasses' OTA BLE events into the engine store; this facade
4
- * exposes the snapshot + change subscriptions the host renders its update prompt and
5
- * progress UI from. No host-injected UI the host owns all alerts/navigation/i18n.
4
+ * exposes the snapshot + change subscriptions behind the shared OTA flow exported by
5
+ * `@mentra/engine/ota`. Hosts retain only their surrounding navigation and theme/i18n adapters.
6
6
  *
7
7
  * Availability checks live in engine. Install orchestration lives in engine too:
8
8
  * `installSession` fronts the OtaInstallCoordinator state machine (WP 8B) — the host
@@ -11,8 +11,10 @@
11
11
  import BluetoothSdk from "@mentra/bluetooth-sdk/internal"
12
12
  import type {OtaProgress, OtaProgressStatus, OtaStatus, OtaUpdateInfo} from "@mentra/bluetooth-sdk/internal"
13
13
  import {isGlassesConnected, useGlassesStore} from "../stores/glasses"
14
- import {getAsgOtaVersionUrl} from "../services/asgOtaVersionUrl"
14
+ import {resolveOtaManifestUrl} from "../services/otaManifestUrl"
15
15
  import {otaInstallCoordinator, type OtaInstallSnapshot} from "../services/OtaInstallCoordinator"
16
+ import {startGlassesStatusProjection} from "../services/GlassesStatusProjection"
17
+ import {startOtaService} from "../services/OtaService"
16
18
  import {
17
19
  checkCurrentGlassesForUpdate,
18
20
  type OtaCheckCurrentGlassesOptions,
@@ -26,9 +28,10 @@ function projectSnapshot() {
26
28
  buildNumber: s.buildNumber || null,
27
29
  mtkFirmwareVersion: s.mtkFirmwareVersion || null,
28
30
  besFirmwareVersion: s.besFirmwareVersion || null,
31
+ hotspotOtaVersion: s.hotspotOtaVersion ?? 0,
29
32
  wifiConnected: s.wifi.state === "connected",
30
33
  wifiStatusKnown: s.wifiStatusKnown,
31
- manifestUrl: getAsgOtaVersionUrl(s.otaVersionUrl, s.buildNumber),
34
+ manifestUrl: resolveOtaManifestUrl(s.otaVersionUrl, s.buildNumber),
32
35
  updateAvailable: s.otaUpdateAvailable,
33
36
  status: s.otaStatus,
34
37
  legacyProgress: s.otaProgress,
@@ -50,6 +53,16 @@ export type {
50
53
 
51
54
  export const ota = {
52
55
  // --- actions ---
56
+ /**
57
+ * Start the device-status and OTA projections without starting the authenticated
58
+ * cloud/miniapp runtime. This is the explicit entry point for Bluetooth-only hosts.
59
+ * Idempotent and safe when the full engine runtime is already running.
60
+ */
61
+ initialize: async () => {
62
+ const hydrated = startGlassesStatusProjection()
63
+ startOtaService()
64
+ await hydrated
65
+ },
53
66
  /**
54
67
  * Start the firmware install with the resolved OTA manifest URL. Progress lands on
55
68
  * `status()`/`onStatus()`. (The host resolves the manifest URL — dev-override/env/prod
@@ -115,6 +128,8 @@ export const ota = {
115
128
  * the snapshot; every watchdog/retry/reconnect rule lives in the coordinator.
116
129
  */
117
130
  installSession: {
131
+ /** Select Wi-Fi or capability-gated hotspot transport for this checked manifest. */
132
+ prepare: (result: OtaCheckCurrentGlassesResult) => otaInstallCoordinator.prepare(result),
118
133
  /** Bind the machine to the mounted progress screen. Idempotent per attach/detach cycle. */
119
134
  attach: () => otaInstallCoordinator.attach(),
120
135
  /** Unbind on unmount: clears all timers/listeners and resets session state. */
@@ -291,25 +291,29 @@ export const pairing = {
291
291
  await BluetoothSdk.stopScan()
292
292
  return
293
293
  }
294
- await BluetoothSdk.disconnect()
295
294
  if (projectPairingIdentity().kind === "paired") {
296
295
  // The persisted settings describe a COMPLETE pairing: restore it to
297
296
  // native (the attempt's connect-by-name overwrote the native
298
297
  // device_name; and if native somehow lost its default entirely, this
299
298
  // repairs the divergence instead of forgetting a real pairing).
300
299
  console.log("PairingIdentity: abandonAttempt — preserving pairing; attempt cancelled, native identity re-seeded")
300
+ await BluetoothSdk.disconnect()
301
301
  await pushAllBluetoothSettings()
302
- } else if (nativeHasDefault) {
302
+ return
303
+ }
304
+ if (nativeHasDefault) {
303
305
  // Mid-relay: native promoted and its echoes are still landing — the
304
306
  // incomplete JS snapshot must not be pushed over the fresher native
305
307
  // identity (the on-connect replay's race). Native holds the truth.
306
308
  console.log("PairingIdentity: abandonAttempt — preserving pairing; JS identity mid-relay, native kept as-is")
307
- } else {
308
- // Forgetting requires CONSENSUS: no native default AND no complete
309
- // persisted pairing — a genuinely partial attempt.
310
- console.log("PairingIdentity: abandonAttempt — no pairing on either layer; forgetting the partial attempt")
311
- await BluetoothSdk.forget()
309
+ await BluetoothSdk.disconnect()
310
+ return
312
311
  }
312
+ // Partial attempt: forget owns teardown. Do NOT disconnect() first — that
313
+ // nulls the MentraLive SGC and used to skip Classic removeBond, leaving
314
+ // IBRT ACL up so glasses never re-advertise for the next scan.
315
+ console.log("PairingIdentity: abandonAttempt — no pairing on either layer; forgetting the partial attempt")
316
+ await BluetoothSdk.forget()
313
317
  },
314
318
 
315
319
  /** Subscribe to pairing failures; returns an unsubscribe. */
@@ -17,6 +17,7 @@ export const PermissionFeatures = {
17
17
  CAMERA: "camera",
18
18
  CALENDAR: "calendar",
19
19
  LOCATION: "location",
20
+ LOCAL_WIFI: "local_wifi",
20
21
  BACKGROUND_LOCATION: "background_location",
21
22
  BLUETOOTH: "bluetooth",
22
23
  PHONE_STATE: "phone_state",
@@ -27,6 +28,13 @@ const ANDROID = PermissionsAndroid.PERMISSIONS
27
28
  const apiLevel = typeof Platform.Version === "number" ? Platform.Version : parseInt(String(Platform.Version), 10)
28
29
  const requiresSeparateAndroidBackgroundLocation = apiLevel >= 29
29
30
 
31
+ export function localWifiPermissionForAndroid(
32
+ androidApiLevel: number,
33
+ androidPermissions: typeof PermissionsAndroid.PERMISSIONS = ANDROID,
34
+ ): AndroidPermission {
35
+ return androidApiLevel >= 33 ? androidPermissions.NEARBY_WIFI_DEVICES : androidPermissions.ACCESS_FINE_LOCATION
36
+ }
37
+
30
38
  async function areAndroidPermissionsGranted(perms: AndroidPermission[]): Promise<boolean> {
31
39
  for (const p of perms) {
32
40
  if (!(await PermissionsAndroid.check(p))) return false
@@ -66,6 +74,8 @@ function iosPerms(feature: string): Permission[] {
66
74
  return [PERMISSIONS.IOS.CALENDARS]
67
75
  case PermissionFeatures.LOCATION:
68
76
  return [PERMISSIONS.IOS.LOCATION_WHEN_IN_USE]
77
+ case PermissionFeatures.LOCAL_WIFI:
78
+ return []
69
79
  case PermissionFeatures.BACKGROUND_LOCATION:
70
80
  // iOS won't grant ALWAYS without WHEN_IN_USE first — request both (matches
71
81
  // the host PermissionsUtils mapping), else a cold request can't escalate.
@@ -84,10 +94,14 @@ function androidPerms(feature: string): AndroidPermission[] {
84
94
  case PermissionFeatures.CAMERA:
85
95
  return [ANDROID.CAMERA]
86
96
  case PermissionFeatures.CALENDAR:
97
+ // Expo Calendar requires both grants on Android, including for read APIs.
98
+ // The miniapp-facing calendar API remains read-only.
87
99
  return [ANDROID.READ_CALENDAR, ANDROID.WRITE_CALENDAR]
88
100
  case PermissionFeatures.LOCATION:
89
101
  case PermissionFeatures.BACKGROUND_LOCATION:
90
102
  return [ANDROID.ACCESS_FINE_LOCATION]
103
+ case PermissionFeatures.LOCAL_WIFI:
104
+ return [localWifiPermissionForAndroid(apiLevel)]
91
105
  case PermissionFeatures.PHONE_STATE:
92
106
  return [ANDROID.READ_PHONE_STATE]
93
107
  case PermissionFeatures.BLUETOOTH:
@@ -3,25 +3,16 @@
3
3
  * notifications to the glasses, with a per-app blocklist. Android-only (the
4
4
  * NotificationListenerService); the getters return sensible defaults on iOS.
5
5
  *
6
- * Enabled-state + blocklist are engine settings (auto-synced to the native
7
- * listener); the installed-apps list + the listener permission are CrustModule.
6
+ * The per-app blocklist is an engine setting (auto-synced to the native
7
+ * listener); the installed-apps list + listener permission are CrustModule.
8
+ * Capture itself is controlled only by Android notification access and the
9
+ * hidden operational kill switch.
8
10
  */
9
11
  import {Platform} from "react-native"
10
12
  import CrustModule from "@mentra/crust"
11
13
  import {useSettingsStore, SETTINGS} from "../stores/settings"
12
14
 
13
15
  export const phoneNotifications = {
14
- /** Is phone-notification forwarding enabled? (false on iOS) */
15
- enabled: (): boolean => {
16
- if (Platform.OS !== "android") return false
17
- return useSettingsStore.getState().getSetting(SETTINGS.notifications_enabled.key) ?? true
18
- },
19
- /** Enable/disable phone-notification forwarding. (no-op on iOS) */
20
- setEnabled: (enabled: boolean) => {
21
- if (Platform.OS !== "android") return
22
- return useSettingsStore.getState().setSetting(SETTINGS.notifications_enabled.key, enabled)
23
- },
24
-
25
16
  /** The phone's installed apps (package, name, icon). (empty on iOS) */
26
17
  installedApps: (): Promise<Array<{packageName: string; appName: string; icon: string | null}>> => {
27
18
  if (Platform.OS !== "android") return Promise.resolve([])