@mentra/engine 3.2.0-dev.181 → 3.2.0-dev.187
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 +13 -0
- package/build/engine.d.ts +7 -0
- package/build/engine.d.ts.map +1 -1
- package/build/facades/phoneNotifications.d.ts +9 -0
- package/build/facades/phoneNotifications.d.ts.map +1 -1
- package/build/facades/phoneNotifications.js +15 -0
- package/build/facades/phoneNotifications.js.map +1 -1
- package/build/generated/releaseMetadata.js +5 -5
- package/build/generated/releaseMetadata.js.map +1 -1
- package/build/services/NativeNotificationPolicy.d.ts +13 -0
- package/build/services/NativeNotificationPolicy.d.ts.map +1 -0
- package/build/services/NativeNotificationPolicy.js +12 -0
- package/build/services/NativeNotificationPolicy.js.map +1 -0
- package/build/services/PhoneNotificationsSync.d.ts +13 -0
- package/build/services/PhoneNotificationsSync.d.ts.map +1 -1
- package/build/services/PhoneNotificationsSync.js +102 -26
- package/build/services/PhoneNotificationsSync.js.map +1 -1
- package/build/stores/settings.d.ts.map +1 -1
- package/build/stores/settings.js +30 -0
- package/build/stores/settings.js.map +1 -1
- package/build/types/capabilities/even-realities-g2.d.ts.map +1 -1
- package/build/types/capabilities/even-realities-g2.js +1 -0
- package/build/types/capabilities/even-realities-g2.js.map +1 -1
- package/build/types/hardware.d.ts +2 -0
- package/build/types/hardware.d.ts.map +1 -1
- package/build/types/hardware.js.map +1 -1
- package/package.json +7 -7
- package/src/facades/phoneNotifications.ts +21 -0
- package/src/generated/releaseMetadata.ts +5 -5
- package/src/services/NativeNotificationPolicy.ts +24 -0
- package/src/services/PhoneNotificationsSync.ts +115 -31
- package/src/stores/settings.ts +34 -7
- package/src/types/capabilities/even-realities-g2.ts +1 -0
- package/src/types/hardware.ts +2 -0
package/README.md
CHANGED
|
@@ -132,3 +132,16 @@ combined run. Per-file processes give every suite an isolated registry.
|
|
|
132
132
|
The same rule applies when writing tests: it is fine to `mock.module` any
|
|
133
133
|
specifier your suite needs, but never rely on a mock installed by a
|
|
134
134
|
_different_ test file — each file must set up everything it imports.
|
|
135
|
+
|
|
136
|
+
## Phone notification presentation
|
|
137
|
+
|
|
138
|
+
`engine.phoneNotifications` owns native-notification configuration alongside the existing Android listener kill switch and per-app blocklist. Capture remains independent of presentation. A host starts/stops its presentation owner with `setPresentationActive(boolean)`; the Mentra App uses Notify's running state on both platforms.
|
|
139
|
+
|
|
140
|
+
- `nativeCapabilities()` distinguishes native presentation, phone content access, app filtering, and removal support.
|
|
141
|
+
- `nativeStatus()` / `onNativeStatus(listener)` expose native availability, authorization, desired controls, and failures.
|
|
142
|
+
- `usesNativePresentation()` selects firmware presentation. A host forwards its captured event through `presentNative(event)` and skips its local card for that event. This does not replace permission-gated miniapp forwarding.
|
|
143
|
+
- `onNativeDelivery(listener)` exposes Android transfer outcomes without message content.
|
|
144
|
+
|
|
145
|
+
Settings are `native_notifications_enabled`, `native_notifications_auto_display`, `native_notifications_duration`, and `native_notifications_do_not_disturb`. Native presentation defaults off. The Android `notifications_blocklist` is also passed to native configuration so pending work is invalidated when filtering changes. Disconnected events are not retained for replay.
|
|
146
|
+
|
|
147
|
+
On iOS, G2 receives ANCS directly; the host does not upload the app-identity relay back to the glasses. Full title/body access, editing the firmware app filter, and phone-dismissal synchronization remain unsupported. The existing G2 app-identity event is not evidence of full content access.
|
package/build/engine.d.ts
CHANGED
|
@@ -238,6 +238,13 @@ export declare const engine: {
|
|
|
238
238
|
};
|
|
239
239
|
/** Phone→glasses notification forwarding (Android). */
|
|
240
240
|
phoneNotifications: {
|
|
241
|
+
nativeCapabilities: typeof import("./services/PhoneNotificationsSync").nativeNotificationCapabilities;
|
|
242
|
+
setPresentationActive: typeof import("./services/PhoneNotificationsSync").setPhoneNotificationPresentationActive;
|
|
243
|
+
usesNativePresentation: typeof import("./services/PhoneNotificationsSync").usesNativeNotificationPresentation;
|
|
244
|
+
presentNative: typeof import("./services/PhoneNotificationsSync").presentNativePhoneNotification;
|
|
245
|
+
nativeStatus: () => Promise<import("@mentra/bluetooth-sdk").NativeNotificationStatus>;
|
|
246
|
+
onNativeStatus: (listener: (status: import("@mentra/bluetooth-sdk").NativeNotificationStatus) => void) => () => void;
|
|
247
|
+
onNativeDelivery: (listener: (event: import("@mentra/bluetooth-sdk").NativeNotificationDelivery) => void) => () => void;
|
|
241
248
|
installedApps: () => Promise<Array<{
|
|
242
249
|
packageName: string;
|
|
243
250
|
appName: string;
|
package/build/engine.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EACL,SAAS,EAIT,aAAa,EACd,MAAM,qBAAqB,CAAA;AAuC5B,eAAO,MAAM,MAAM;IACjB,2EAA2E;;IAE3E,8EAA8E;;IAE9E;iDAC6C;;IAkF7C;;yCAEqC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IA2BrC,mFAAmF;;;;;;;;IAEnF,sEAAsE;;;;;;;;;;;;IAEtE,qFAAqF;;;gBApGjF,CAAL;mBAAiB,CAAC;;;gBAKX,CAAC;mBAAiB,CAAC;;;;;;;;;;;;;;;;IAiGzB,qEAAqE;;;;IAErE,sFAAsF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAEtF,6EAA6E;;;;;;;;;;;;IAE7E,iFAAiF;;;;;;;;;;;;;;;;;;;IAEjF,8CAA8C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAE9C,uDAAuD
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EACL,SAAS,EAIT,aAAa,EACd,MAAM,qBAAqB,CAAA;AAuC5B,eAAO,MAAM,MAAM;IACjB,2EAA2E;;IAE3E,8EAA8E;;IAE9E;iDAC6C;;IAkF7C;;yCAEqC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IA2BrC,mFAAmF;;;;;;;;IAEnF,sEAAsE;;;;;;;;;;;;IAEtE,qFAAqF;;;gBApGjF,CAAL;mBAAiB,CAAC;;;gBAKX,CAAC;mBAAiB,CAAC;;;;;;;;;;;;;;;;IAiGzB,qEAAqE;;;;IAErE,sFAAsF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAEtF,6EAA6E;;;;;;;;;;;;IAE7E,iFAAiF;;;;;;;;;;;;;;;;;;;IAEjF,8CAA8C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAE9C,uDAAuD;;;;;;;;;;;;;;;;;;;IAEvD,8EAA8E;;;;;;;IAE9E,qFAAqF;;;;;;;;;;;;;;CAGtF,CAAA"}
|
|
@@ -1,4 +1,13 @@
|
|
|
1
|
+
import type { NativeNotificationStatus, NativeNotificationDelivery } from "@mentra/bluetooth-sdk";
|
|
2
|
+
import { nativeNotificationCapabilities, setPhoneNotificationPresentationActive, usesNativeNotificationPresentation, presentNativePhoneNotification } from "../services/PhoneNotificationsSync";
|
|
1
3
|
export declare const phoneNotifications: {
|
|
4
|
+
nativeCapabilities: typeof nativeNotificationCapabilities;
|
|
5
|
+
setPresentationActive: typeof setPhoneNotificationPresentationActive;
|
|
6
|
+
usesNativePresentation: typeof usesNativeNotificationPresentation;
|
|
7
|
+
presentNative: typeof presentNativePhoneNotification;
|
|
8
|
+
nativeStatus: () => Promise<NativeNotificationStatus>;
|
|
9
|
+
onNativeStatus: (listener: (status: NativeNotificationStatus) => void) => () => void;
|
|
10
|
+
onNativeDelivery: (listener: (event: NativeNotificationDelivery) => void) => () => void;
|
|
2
11
|
/** The phone's installed apps (package, name, icon). (empty on iOS) */
|
|
3
12
|
installedApps: () => Promise<Array<{
|
|
4
13
|
packageName: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"phoneNotifications.d.ts","sourceRoot":"","sources":["../../src/facades/phoneNotifications.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"phoneNotifications.d.ts","sourceRoot":"","sources":["../../src/facades/phoneNotifications.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAC,wBAAwB,EAAE,0BAA0B,EAAC,MAAM,uBAAuB,CAAA;AAC/F,OAAO,EACL,8BAA8B,EAC9B,sCAAsC,EACtC,kCAAkC,EAClC,8BAA8B,EAC/B,MAAM,oCAAoC,CAAA;AAG3C,eAAO,MAAM,kBAAkB;;;;;wBAKX,OAAO,CAAC,wBAAwB,CAAC;+BACxB,CAAC,MAAM,EAAE,wBAAwB,KAAK,IAAI;iCAIxC,CAAC,KAAK,EAAE,0BAA0B,KAAK,IAAI;IAIxE,uEAAuE;yBACpD,OAAO,CAAC,KAAK,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;KAAC,CAAC,CAAC;IAK9F,mFAAmF;qBACpE,MAAM,EAAE;IAIvB,qCAAqC;iCACR,MAAM,EAAE;IAGrC,qFAAqF;iCAC1D,OAAO,CAAC,OAAO,CAAC;IAI3C,yFAAyF;;CAK1F,CAAA"}
|
|
@@ -10,8 +10,23 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { Platform } from "react-native";
|
|
12
12
|
import CrustModule from "@mentra/crust";
|
|
13
|
+
import BluetoothSdk from "@mentra/bluetooth-sdk/internal";
|
|
14
|
+
import { nativeNotificationCapabilities, setPhoneNotificationPresentationActive, usesNativeNotificationPresentation, presentNativePhoneNotification, } from "../services/PhoneNotificationsSync";
|
|
13
15
|
import { useSettingsStore, SETTINGS } from "../stores/settings";
|
|
14
16
|
export const phoneNotifications = {
|
|
17
|
+
nativeCapabilities: nativeNotificationCapabilities,
|
|
18
|
+
setPresentationActive: setPhoneNotificationPresentationActive,
|
|
19
|
+
usesNativePresentation: usesNativeNotificationPresentation,
|
|
20
|
+
presentNative: presentNativePhoneNotification,
|
|
21
|
+
nativeStatus: () => BluetoothSdk.getNativeNotificationStatus(),
|
|
22
|
+
onNativeStatus: (listener) => {
|
|
23
|
+
const subscription = BluetoothSdk.addListener("native_notification_status", listener);
|
|
24
|
+
return () => subscription.remove();
|
|
25
|
+
},
|
|
26
|
+
onNativeDelivery: (listener) => {
|
|
27
|
+
const subscription = BluetoothSdk.addListener("native_notification_delivery", listener);
|
|
28
|
+
return () => subscription.remove();
|
|
29
|
+
},
|
|
15
30
|
/** The phone's installed apps (package, name, icon). (empty on iOS) */
|
|
16
31
|
installedApps: () => {
|
|
17
32
|
if (Platform.OS !== "android")
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"phoneNotifications.js","sourceRoot":"","sources":["../../src/facades/phoneNotifications.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAC,QAAQ,EAAC,MAAM,cAAc,CAAA;AACrC,OAAO,WAAW,MAAM,eAAe,CAAA;AACvC,OAAO,EAAC,gBAAgB,EAAE,QAAQ,EAAC,MAAM,oBAAoB,CAAA;AAE7D,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,uEAAuE;IACvE,aAAa,EAAE,GAAgF,EAAE;QAC/F,IAAI,QAAQ,CAAC,EAAE,KAAK,SAAS;YAAE,OAAO,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;QACzD,OAAO,WAAW,CAAC,gBAAgB,EAAE,CAAA;IACvC,CAAC;IAED,mFAAmF;IACnF,SAAS,EAAE,GAAa,EAAE;QACxB,MAAM,CAAC,GAAG,gBAAgB,CAAC,QAAQ,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,uBAAuB,CAAC,GAAG,CAAC,CAAA;QACtF,OAAO,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;IAClC,CAAC;IACD,qCAAqC;IACrC,YAAY,EAAE,CAAC,YAAsB,EAAE,EAAE,CACvC,gBAAgB,CAAC,QAAQ,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,uBAAuB,CAAC,GAAG,EAAE,YAAY,CAAC;IAE5F,qFAAqF;IACrF,qBAAqB,EAAE,GAAqB,EAAE;QAC5C,IAAI,QAAQ,CAAC,EAAE,KAAK,SAAS;YAAE,OAAO,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;QAC5D,OAAO,WAAW,CAAC,iCAAiC,EAAE,CAAA;IACxD,CAAC;IACD,yFAAyF;IACzF,yBAAyB,EAAE,GAAG,EAAE;QAC9B,IAAI,QAAQ,CAAC,EAAE,KAAK,SAAS;YAAE,OAAM;QACrC,OAAO,WAAW,CAAC,gCAAgC,EAAE,CAAA;IACvD,CAAC;CACF,CAAA","sourcesContent":["/**\n * phoneNotifications facade — `engine.phoneNotifications`: forward phone\n * notifications to the glasses, with a per-app blocklist. Android-only (the\n * NotificationListenerService); the getters return sensible defaults on iOS.\n *\n * The per-app blocklist is an engine setting (auto-synced to the native\n * listener); the installed-apps list + listener permission are CrustModule.\n * Capture itself is controlled only by Android notification access and the\n * hidden operational kill switch.\n */\nimport {Platform} from \"react-native\"\nimport CrustModule from \"@mentra/crust\"\nimport {useSettingsStore, SETTINGS} from \"../stores/settings\"\n\nexport const phoneNotifications = {\n /** The phone's installed apps (package, name, icon). (empty on iOS) */\n installedApps: (): Promise<Array<{packageName: string; appName: string; icon: string | null}>> => {\n if (Platform.OS !== \"android\") return Promise.resolve([])\n return CrustModule.getInstalledApps()\n },\n\n /** The per-app blocklist (package names whose notifications are NOT forwarded). */\n blocklist: (): string[] => {\n const b = useSettingsStore.getState().getSetting(SETTINGS.notifications_blocklist.key)\n return Array.isArray(b) ? b : []\n },\n /** Replace the per-app blocklist. */\n setBlocklist: (packageNames: string[]) =>\n useSettingsStore.getState().setSetting(SETTINGS.notifications_blocklist.key, packageNames),\n\n /** Does the app have the Android notification-listener permission? (false on iOS) */\n hasListenerPermission: (): Promise<boolean> => {\n if (Platform.OS !== \"android\") return Promise.resolve(false)\n return CrustModule.hasNotificationListenerPermission()\n },\n /** Open the OS settings to grant the notification-listener permission. (no-op on iOS) */\n requestListenerPermission: () => {\n if (Platform.OS !== \"android\") return\n return CrustModule.openNotificationListenerSettings()\n },\n}\n"]}
|
|
1
|
+
{"version":3,"file":"phoneNotifications.js","sourceRoot":"","sources":["../../src/facades/phoneNotifications.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAC,QAAQ,EAAC,MAAM,cAAc,CAAA;AACrC,OAAO,WAAW,MAAM,eAAe,CAAA;AACvC,OAAO,YAAY,MAAM,gCAAgC,CAAA;AAEzD,OAAO,EACL,8BAA8B,EAC9B,sCAAsC,EACtC,kCAAkC,EAClC,8BAA8B,GAC/B,MAAM,oCAAoC,CAAA;AAC3C,OAAO,EAAC,gBAAgB,EAAE,QAAQ,EAAC,MAAM,oBAAoB,CAAA;AAE7D,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,kBAAkB,EAAE,8BAA8B;IAClD,qBAAqB,EAAE,sCAAsC;IAC7D,sBAAsB,EAAE,kCAAkC;IAC1D,aAAa,EAAE,8BAA8B;IAC7C,YAAY,EAAE,GAAsC,EAAE,CAAC,YAAY,CAAC,2BAA2B,EAAE;IACjG,cAAc,EAAE,CAAC,QAAoD,EAAE,EAAE;QACvE,MAAM,YAAY,GAAG,YAAY,CAAC,WAAW,CAAC,4BAA4B,EAAE,QAAQ,CAAC,CAAA;QACrF,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,CAAA;IACpC,CAAC;IACD,gBAAgB,EAAE,CAAC,QAAqD,EAAE,EAAE;QAC1E,MAAM,YAAY,GAAG,YAAY,CAAC,WAAW,CAAC,8BAA8B,EAAE,QAAQ,CAAC,CAAA;QACvF,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,CAAA;IACpC,CAAC;IACD,uEAAuE;IACvE,aAAa,EAAE,GAAgF,EAAE;QAC/F,IAAI,QAAQ,CAAC,EAAE,KAAK,SAAS;YAAE,OAAO,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;QACzD,OAAO,WAAW,CAAC,gBAAgB,EAAE,CAAA;IACvC,CAAC;IAED,mFAAmF;IACnF,SAAS,EAAE,GAAa,EAAE;QACxB,MAAM,CAAC,GAAG,gBAAgB,CAAC,QAAQ,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,uBAAuB,CAAC,GAAG,CAAC,CAAA;QACtF,OAAO,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;IAClC,CAAC;IACD,qCAAqC;IACrC,YAAY,EAAE,CAAC,YAAsB,EAAE,EAAE,CACvC,gBAAgB,CAAC,QAAQ,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,uBAAuB,CAAC,GAAG,EAAE,YAAY,CAAC;IAE5F,qFAAqF;IACrF,qBAAqB,EAAE,GAAqB,EAAE;QAC5C,IAAI,QAAQ,CAAC,EAAE,KAAK,SAAS;YAAE,OAAO,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;QAC5D,OAAO,WAAW,CAAC,iCAAiC,EAAE,CAAA;IACxD,CAAC;IACD,yFAAyF;IACzF,yBAAyB,EAAE,GAAG,EAAE;QAC9B,IAAI,QAAQ,CAAC,EAAE,KAAK,SAAS;YAAE,OAAM;QACrC,OAAO,WAAW,CAAC,gCAAgC,EAAE,CAAA;IACvD,CAAC;CACF,CAAA","sourcesContent":["/**\n * phoneNotifications facade — `engine.phoneNotifications`: forward phone\n * notifications to the glasses, with a per-app blocklist. Android-only (the\n * NotificationListenerService); the getters return sensible defaults on iOS.\n *\n * The per-app blocklist is an engine setting (auto-synced to the native\n * listener); the installed-apps list + listener permission are CrustModule.\n * Capture itself is controlled only by Android notification access and the\n * hidden operational kill switch.\n */\nimport {Platform} from \"react-native\"\nimport CrustModule from \"@mentra/crust\"\nimport BluetoothSdk from \"@mentra/bluetooth-sdk/internal\"\nimport type {NativeNotificationStatus, NativeNotificationDelivery} from \"@mentra/bluetooth-sdk\"\nimport {\n nativeNotificationCapabilities,\n setPhoneNotificationPresentationActive,\n usesNativeNotificationPresentation,\n presentNativePhoneNotification,\n} from \"../services/PhoneNotificationsSync\"\nimport {useSettingsStore, SETTINGS} from \"../stores/settings\"\n\nexport const phoneNotifications = {\n nativeCapabilities: nativeNotificationCapabilities,\n setPresentationActive: setPhoneNotificationPresentationActive,\n usesNativePresentation: usesNativeNotificationPresentation,\n presentNative: presentNativePhoneNotification,\n nativeStatus: (): Promise<NativeNotificationStatus> => BluetoothSdk.getNativeNotificationStatus(),\n onNativeStatus: (listener: (status: NativeNotificationStatus) => void) => {\n const subscription = BluetoothSdk.addListener(\"native_notification_status\", listener)\n return () => subscription.remove()\n },\n onNativeDelivery: (listener: (event: NativeNotificationDelivery) => void) => {\n const subscription = BluetoothSdk.addListener(\"native_notification_delivery\", listener)\n return () => subscription.remove()\n },\n /** The phone's installed apps (package, name, icon). (empty on iOS) */\n installedApps: (): Promise<Array<{packageName: string; appName: string; icon: string | null}>> => {\n if (Platform.OS !== \"android\") return Promise.resolve([])\n return CrustModule.getInstalledApps()\n },\n\n /** The per-app blocklist (package names whose notifications are NOT forwarded). */\n blocklist: (): string[] => {\n const b = useSettingsStore.getState().getSetting(SETTINGS.notifications_blocklist.key)\n return Array.isArray(b) ? b : []\n },\n /** Replace the per-app blocklist. */\n setBlocklist: (packageNames: string[]) =>\n useSettingsStore.getState().setSetting(SETTINGS.notifications_blocklist.key, packageNames),\n\n /** Does the app have the Android notification-listener permission? (false on iOS) */\n hasListenerPermission: (): Promise<boolean> => {\n if (Platform.OS !== \"android\") return Promise.resolve(false)\n return CrustModule.hasNotificationListenerPermission()\n },\n /** Open the OS settings to grant the notification-listener permission. (no-op on iOS) */\n requestListenerPermission: () => {\n if (Platform.OS !== \"android\") return\n return CrustModule.openNotificationListenerSettings()\n },\n}\n"]}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
export const ENGINE_RELEASE_METADATA = Object.freeze({
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"familyBaseVersion": "3.2.0",
|
|
4
|
-
"releaseIdentity": "3.2.0-dev.
|
|
5
|
-
"releaseSetId": "mentra-3.2.0-dev.
|
|
6
|
-
"sourceCommit": "
|
|
7
|
-
"otaManifestUrl": "https://github.com/Mentra-Community/MentraOS/releases/download/mentra-builds-v3.2.0/mentra-live-ota-3.2.0-dev.
|
|
8
|
-
"otaManifestSha256": "
|
|
4
|
+
"releaseIdentity": "3.2.0-dev.187",
|
|
5
|
+
"releaseSetId": "mentra-3.2.0-dev.187",
|
|
6
|
+
"sourceCommit": "d8b34892f11593baa3c075b94380253b06eb874d",
|
|
7
|
+
"otaManifestUrl": "https://github.com/Mentra-Community/MentraOS/releases/download/mentra-builds-v3.2.0/mentra-live-ota-3.2.0-dev.187.json",
|
|
8
|
+
"otaManifestSha256": "cf21f4ab6347acb12eefa38d2f0a3b433bc0e05428c527bcda57a8693d7a72f9"
|
|
9
9
|
});
|
|
10
10
|
//# sourceMappingURL=releaseMetadata.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"releaseMetadata.js","sourceRoot":"","sources":["../../src/generated/releaseMetadata.ts"],"names":[],"mappings":"AAWA,MAAM,CAAC,MAAM,uBAAuB,GAAoC,MAAM,CAAC,MAAM,CAAC;IACpF,eAAe,EAAE,CAAC;IAClB,mBAAmB,EAAE,OAAO;IAC5B,iBAAiB,EAAE,eAAe;IAClC,cAAc,EAAE,sBAAsB;IACtC,cAAc,EAAE,0CAA0C;IAC1D,gBAAgB,EAAE,wHAAwH;IAC1I,mBAAmB,EAAE,kEAAkE;CACxF,CAAC,CAAA","sourcesContent":["/** Generated by release CI. Do not edit in a release checkout. */\nexport interface EngineReleaseMetadata {\n schemaVersion: 1\n familyBaseVersion: string | null\n releaseIdentity: string | null\n releaseSetId: string | null\n sourceCommit: string | null\n otaManifestUrl: string | null\n otaManifestSha256: string | null\n}\n\nexport const ENGINE_RELEASE_METADATA: Readonly<EngineReleaseMetadata> = Object.freeze({\n \"schemaVersion\": 1,\n \"familyBaseVersion\": \"3.2.0\",\n \"releaseIdentity\": \"3.2.0-dev.
|
|
1
|
+
{"version":3,"file":"releaseMetadata.js","sourceRoot":"","sources":["../../src/generated/releaseMetadata.ts"],"names":[],"mappings":"AAWA,MAAM,CAAC,MAAM,uBAAuB,GAAoC,MAAM,CAAC,MAAM,CAAC;IACpF,eAAe,EAAE,CAAC;IAClB,mBAAmB,EAAE,OAAO;IAC5B,iBAAiB,EAAE,eAAe;IAClC,cAAc,EAAE,sBAAsB;IACtC,cAAc,EAAE,0CAA0C;IAC1D,gBAAgB,EAAE,wHAAwH;IAC1I,mBAAmB,EAAE,kEAAkE;CACxF,CAAC,CAAA","sourcesContent":["/** Generated by release CI. Do not edit in a release checkout. */\nexport interface EngineReleaseMetadata {\n schemaVersion: 1\n familyBaseVersion: string | null\n releaseIdentity: string | null\n releaseSetId: string | null\n sourceCommit: string | null\n otaManifestUrl: string | null\n otaManifestSha256: string | null\n}\n\nexport const ENGINE_RELEASE_METADATA: Readonly<EngineReleaseMetadata> = Object.freeze({\n \"schemaVersion\": 1,\n \"familyBaseVersion\": \"3.2.0\",\n \"releaseIdentity\": \"3.2.0-dev.187\",\n \"releaseSetId\": \"mentra-3.2.0-dev.187\",\n \"sourceCommit\": \"d8b34892f11593baa3c075b94380253b06eb874d\",\n \"otaManifestUrl\": \"https://github.com/Mentra-Community/MentraOS/releases/download/mentra-builds-v3.2.0/mentra-live-ota-3.2.0-dev.187.json\",\n \"otaManifestSha256\": \"cf21f4ab6347acb12eefa38d2f0a3b433bc0e05428c527bcda57a8693d7a72f9\"\n})\n"]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { NativeNotificationConfig } from "@mentra/bluetooth-sdk";
|
|
2
|
+
export interface NotificationPolicyInput {
|
|
3
|
+
supported: boolean;
|
|
4
|
+
selected: boolean;
|
|
5
|
+
presentationActive: boolean;
|
|
6
|
+
sourceEnabled: boolean;
|
|
7
|
+
autoDisplay: boolean;
|
|
8
|
+
durationSeconds: number;
|
|
9
|
+
doNotDisturb: boolean;
|
|
10
|
+
blockedApps: string[];
|
|
11
|
+
}
|
|
12
|
+
export declare function nativeNotificationConfig(input: NotificationPolicyInput): NativeNotificationConfig;
|
|
13
|
+
//# sourceMappingURL=NativeNotificationPolicy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NativeNotificationPolicy.d.ts","sourceRoot":"","sources":["../../src/services/NativeNotificationPolicy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,wBAAwB,EAAC,MAAM,uBAAuB,CAAA;AAEnE,MAAM,WAAW,uBAAuB;IACtC,SAAS,EAAE,OAAO,CAAA;IAClB,QAAQ,EAAE,OAAO,CAAA;IACjB,kBAAkB,EAAE,OAAO,CAAA;IAC3B,aAAa,EAAE,OAAO,CAAA;IACtB,WAAW,EAAE,OAAO,CAAA;IACpB,eAAe,EAAE,MAAM,CAAA;IACvB,YAAY,EAAE,OAAO,CAAA;IACrB,WAAW,EAAE,MAAM,EAAE,CAAA;CACtB;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,uBAAuB,GAAG,wBAAwB,CAUjG"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export function nativeNotificationConfig(input) {
|
|
2
|
+
return {
|
|
3
|
+
enabled: input.supported && input.selected && input.presentationActive && input.sourceEnabled,
|
|
4
|
+
autoDisplay: input.autoDisplay,
|
|
5
|
+
durationSeconds: Number.isFinite(input.durationSeconds)
|
|
6
|
+
? Math.min(30, Math.max(1, Math.round(input.durationSeconds)))
|
|
7
|
+
: 5,
|
|
8
|
+
doNotDisturb: input.doNotDisturb,
|
|
9
|
+
blockedApps: input.blockedApps,
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=NativeNotificationPolicy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NativeNotificationPolicy.js","sourceRoot":"","sources":["../../src/services/NativeNotificationPolicy.ts"],"names":[],"mappings":"AAaA,MAAM,UAAU,wBAAwB,CAAC,KAA8B;IACrE,OAAO;QACL,OAAO,EAAE,KAAK,CAAC,SAAS,IAAI,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,kBAAkB,IAAI,KAAK,CAAC,aAAa;QAC7F,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,eAAe,EAAE,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,eAAe,CAAC;YACrD,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,CAAC;YAC9D,CAAC,CAAC,CAAC;QACL,YAAY,EAAE,KAAK,CAAC,YAAY;QAChC,WAAW,EAAE,KAAK,CAAC,WAAW;KAC/B,CAAA;AACH,CAAC","sourcesContent":["import type {NativeNotificationConfig} from \"@mentra/bluetooth-sdk\"\n\nexport interface NotificationPolicyInput {\n supported: boolean\n selected: boolean\n presentationActive: boolean\n sourceEnabled: boolean\n autoDisplay: boolean\n durationSeconds: number\n doNotDisturb: boolean\n blockedApps: string[]\n}\n\nexport function nativeNotificationConfig(input: NotificationPolicyInput): NativeNotificationConfig {\n return {\n enabled: input.supported && input.selected && input.presentationActive && input.sourceEnabled,\n autoDisplay: input.autoDisplay,\n durationSeconds: Number.isFinite(input.durationSeconds)\n ? Math.min(30, Math.max(1, Math.round(input.durationSeconds)))\n : 5,\n doNotDisturb: input.doNotDisturb,\n blockedApps: input.blockedApps,\n }\n}\n"]}
|
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
import type { PhoneNotificationEvent } from "@mentra/crust";
|
|
2
|
+
export declare function nativeNotificationCapabilities(): {
|
|
3
|
+
supported: boolean;
|
|
4
|
+
source: "phone" | "ancs";
|
|
5
|
+
contentAccess: "full" | "unsupported" | "app_identity";
|
|
6
|
+
perAppFiltering: boolean;
|
|
7
|
+
removal: boolean;
|
|
8
|
+
};
|
|
9
|
+
/** Hosts activate their presentation owner (Notify in the Mentra App) through this seam. */
|
|
10
|
+
export declare function setPhoneNotificationPresentationActive(active: boolean): void;
|
|
11
|
+
export declare function usesNativeNotificationPresentation(): boolean;
|
|
12
|
+
/** Return true when the selected firmware presentation owns this event, including quiet/history mode. */
|
|
13
|
+
export declare function presentNativePhoneNotification(event: PhoneNotificationEvent): Promise<boolean>;
|
|
1
14
|
export declare function startPhoneNotificationsSync(): void;
|
|
2
15
|
export declare function stopPhoneNotificationsSync(): void;
|
|
3
16
|
//# sourceMappingURL=PhoneNotificationsSync.d.ts.map
|
|
@@ -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":"AAKA,OAAO,KAAK,EAAC,sBAAsB,EAAC,MAAM,eAAe,CAAA;AAWzD,wBAAgB,8BAA8B;;;;;;EAY7C;AA0BD,4FAA4F;AAC5F,wBAAgB,sCAAsC,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAI5E;AAED,wBAAgB,kCAAkC,IAAI,OAAO,CAM5D;AAED,yGAAyG;AACzG,wBAAsB,8BAA8B,CAAC,KAAK,EAAE,sBAAsB,GAAG,OAAO,CAAC,OAAO,CAAC,CAkBpG;AAcD,wBAAgB,2BAA2B,IAAI,IAAI,CAqBlD;AAED,wBAAgB,0BAA0B,IAAI,IAAI,CAIjD"}
|
|
@@ -1,37 +1,113 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
* operational kill switch + config (`android_notification_listener_enabled`
|
|
4
|
-
* and `notifications_blocklist`) to the native
|
|
5
|
-
* NotificationListener via `CrustModule.setNotificationConfig`, so
|
|
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()`.
|
|
13
|
-
*/
|
|
1
|
+
/** One engine-owned notification policy: capture remains independent of presentation. */
|
|
2
|
+
import { Platform } from "react-native";
|
|
14
3
|
import { shallow } from "zustand/shallow";
|
|
15
4
|
import CrustModule from "@mentra/crust";
|
|
5
|
+
import BluetoothSdk from "@mentra/bluetooth-sdk/internal";
|
|
16
6
|
import { useSettingsStore, SETTINGS } from "../stores/settings";
|
|
17
|
-
|
|
7
|
+
import { useGlassesStore, isGlassesReady } from "../stores/glasses";
|
|
8
|
+
import { getModelCapabilities } from "../types/hardware";
|
|
9
|
+
import { nativeNotificationConfig } from "./NativeNotificationPolicy";
|
|
10
|
+
let unsubscribers = [];
|
|
11
|
+
let presentationActive = false;
|
|
12
|
+
export function nativeNotificationCapabilities() {
|
|
13
|
+
const supported = Boolean(getModelCapabilities(useGlassesStore.getState().deviceModel)?.hasNativeNotifications);
|
|
14
|
+
return {
|
|
15
|
+
supported,
|
|
16
|
+
source: Platform.OS === "ios" ? "ancs" : "phone",
|
|
17
|
+
contentAccess: Platform.OS === "android" ? "full" : supported ? "app_identity" : "unsupported",
|
|
18
|
+
perAppFiltering: Platform.OS === "android",
|
|
19
|
+
removal: false, // The verified G2 protocol does not yet include a removal command.
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function desiredConfig() {
|
|
23
|
+
const settings = useSettingsStore.getState();
|
|
24
|
+
return nativeNotificationConfig({
|
|
25
|
+
supported: nativeNotificationCapabilities().supported,
|
|
26
|
+
selected: Boolean(settings.getSetting(SETTINGS.native_notifications_enabled.key)),
|
|
27
|
+
presentationActive,
|
|
28
|
+
sourceEnabled: Platform.OS !== "android" || Boolean(settings.getSetting(SETTINGS.android_notification_listener_enabled.key)),
|
|
29
|
+
autoDisplay: Boolean(settings.getSetting(SETTINGS.native_notifications_auto_display.key)),
|
|
30
|
+
durationSeconds: Number(settings.getSetting(SETTINGS.native_notifications_duration.key)),
|
|
31
|
+
doNotDisturb: Boolean(settings.getSetting(SETTINGS.native_notifications_do_not_disturb.key)),
|
|
32
|
+
blockedApps: Platform.OS === "android" ? settings.getSetting(SETTINGS.notifications_blocklist.key) : [],
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
function pushNativeConfig() {
|
|
36
|
+
if (!nativeNotificationCapabilities().supported)
|
|
37
|
+
return;
|
|
38
|
+
void BluetoothSdk.configureNativeNotifications(desiredConfig()).catch((error) => {
|
|
39
|
+
if (isGlassesReady(useGlassesStore.getState().connection)) {
|
|
40
|
+
console.warn("PhoneNotificationsSync: native notification configuration failed", error);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
/** Hosts activate their presentation owner (Notify in the Mentra App) through this seam. */
|
|
45
|
+
export function setPhoneNotificationPresentationActive(active) {
|
|
46
|
+
if (active === presentationActive)
|
|
47
|
+
return;
|
|
48
|
+
presentationActive = active;
|
|
49
|
+
pushNativeConfig();
|
|
50
|
+
}
|
|
51
|
+
export function usesNativeNotificationPresentation() {
|
|
52
|
+
return (nativeNotificationCapabilities().supported &&
|
|
53
|
+
presentationActive &&
|
|
54
|
+
Boolean(useSettingsStore.getState().getSetting(SETTINGS.native_notifications_enabled.key)));
|
|
55
|
+
}
|
|
56
|
+
/** Return true when the selected firmware presentation owns this event, including quiet/history mode. */
|
|
57
|
+
export async function presentNativePhoneNotification(event) {
|
|
58
|
+
if (!usesNativeNotificationPresentation())
|
|
59
|
+
return false;
|
|
60
|
+
if (!desiredConfig().enabled)
|
|
61
|
+
return true;
|
|
62
|
+
if (Platform.OS !== "android")
|
|
63
|
+
return true; // G2 receives ANCS directly; never upload metadata back.
|
|
64
|
+
const blocked = useSettingsStore.getState().getSetting(SETTINGS.notifications_blocklist.key);
|
|
65
|
+
if (Array.isArray(blocked) && blocked.includes(event.packageName))
|
|
66
|
+
return true;
|
|
67
|
+
if (!isGlassesReady(useGlassesStore.getState().connection))
|
|
68
|
+
return true;
|
|
69
|
+
await BluetoothSdk.sendPhoneNotification({
|
|
70
|
+
notificationId: String(event.notificationId ?? ""),
|
|
71
|
+
packageName: String(event.packageName ?? ""),
|
|
72
|
+
appName: String(event.app ?? ""),
|
|
73
|
+
title: String(event.title ?? ""),
|
|
74
|
+
subtitle: "",
|
|
75
|
+
body: String(event.content ?? ""),
|
|
76
|
+
timestampMs: Number(event.timestamp ?? Date.now()),
|
|
77
|
+
action: 0,
|
|
78
|
+
});
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
18
81
|
function pushConfig() {
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
82
|
+
const settings = useSettingsStore.getState();
|
|
83
|
+
if (Platform.OS === "android") {
|
|
84
|
+
const enabled = Boolean(settings.getSetting(SETTINGS.android_notification_listener_enabled.key));
|
|
85
|
+
const blocklist = settings.getSetting(SETTINGS.notifications_blocklist.key);
|
|
86
|
+
void CrustModule.setNotificationConfig(enabled, Array.isArray(blocklist) ? blocklist : []).catch((error) => console.warn("PhoneNotificationsSync: listener configuration failed", error));
|
|
87
|
+
}
|
|
88
|
+
pushNativeConfig();
|
|
23
89
|
}
|
|
24
90
|
export function startPhoneNotificationsSync() {
|
|
25
|
-
if (
|
|
91
|
+
if (unsubscribers.length)
|
|
26
92
|
return;
|
|
27
|
-
pushConfig();
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
93
|
+
pushConfig();
|
|
94
|
+
unsubscribers = [
|
|
95
|
+
useSettingsStore.subscribe((state) => [
|
|
96
|
+
SETTINGS.android_notification_listener_enabled,
|
|
97
|
+
SETTINGS.notifications_blocklist,
|
|
98
|
+
SETTINGS.native_notifications_enabled,
|
|
99
|
+
SETTINGS.native_notifications_auto_display,
|
|
100
|
+
SETTINGS.native_notifications_duration,
|
|
101
|
+
SETTINGS.native_notifications_do_not_disturb,
|
|
102
|
+
].map((setting) => state.getSetting(setting.key)), pushConfig, { equalityFn: shallow }),
|
|
103
|
+
useGlassesStore.subscribe((state) => ({ model: state.deviceModel, connection: state.connection }), pushNativeConfig, {
|
|
104
|
+
equalityFn: shallow,
|
|
105
|
+
}),
|
|
106
|
+
];
|
|
32
107
|
}
|
|
33
108
|
export function stopPhoneNotificationsSync() {
|
|
34
|
-
|
|
35
|
-
unsubscribe
|
|
109
|
+
setPhoneNotificationPresentationActive(false);
|
|
110
|
+
unsubscribers.forEach((unsubscribe) => unsubscribe());
|
|
111
|
+
unsubscribers = [];
|
|
36
112
|
}
|
|
37
113
|
//# sourceMappingURL=PhoneNotificationsSync.js.map
|
|
@@ -1 +1 @@
|
|
|
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"]}
|
|
1
|
+
{"version":3,"file":"PhoneNotificationsSync.js","sourceRoot":"","sources":["../../src/services/PhoneNotificationsSync.ts"],"names":[],"mappings":"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","sourcesContent":["/** One engine-owned notification policy: capture remains independent of presentation. */\nimport {Platform} from \"react-native\"\nimport {shallow} from \"zustand/shallow\"\nimport CrustModule from \"@mentra/crust\"\nimport BluetoothSdk from \"@mentra/bluetooth-sdk/internal\"\nimport type {PhoneNotificationEvent} from \"@mentra/crust\"\n\nimport {useSettingsStore, SETTINGS} from \"../stores/settings\"\nimport {useGlassesStore, isGlassesReady} from \"../stores/glasses\"\nimport {DeviceTypes} from \"../types/enums\"\nimport {getModelCapabilities} from \"../types/hardware\"\nimport {nativeNotificationConfig} from \"./NativeNotificationPolicy\"\n\nlet unsubscribers: Array<() => void> = []\nlet presentationActive = false\n\nexport function nativeNotificationCapabilities() {\n const supported = Boolean(\n getModelCapabilities(useGlassesStore.getState().deviceModel as DeviceTypes)?.hasNativeNotifications,\n )\n return {\n supported,\n source: Platform.OS === \"ios\" ? (\"ancs\" as const) : (\"phone\" as const),\n contentAccess:\n Platform.OS === \"android\" ? (\"full\" as const) : supported ? (\"app_identity\" as const) : (\"unsupported\" as const),\n perAppFiltering: Platform.OS === \"android\",\n removal: false, // The verified G2 protocol does not yet include a removal command.\n }\n}\n\nfunction desiredConfig() {\n const settings = useSettingsStore.getState()\n return nativeNotificationConfig({\n supported: nativeNotificationCapabilities().supported,\n selected: Boolean(settings.getSetting(SETTINGS.native_notifications_enabled.key)),\n presentationActive,\n sourceEnabled:\n Platform.OS !== \"android\" || Boolean(settings.getSetting(SETTINGS.android_notification_listener_enabled.key)),\n autoDisplay: Boolean(settings.getSetting(SETTINGS.native_notifications_auto_display.key)),\n durationSeconds: Number(settings.getSetting(SETTINGS.native_notifications_duration.key)),\n doNotDisturb: Boolean(settings.getSetting(SETTINGS.native_notifications_do_not_disturb.key)),\n blockedApps: Platform.OS === \"android\" ? settings.getSetting(SETTINGS.notifications_blocklist.key) : [],\n })\n}\n\nfunction pushNativeConfig(): void {\n if (!nativeNotificationCapabilities().supported) return\n void BluetoothSdk.configureNativeNotifications(desiredConfig()).catch((error: unknown) => {\n if (isGlassesReady(useGlassesStore.getState().connection)) {\n console.warn(\"PhoneNotificationsSync: native notification configuration failed\", error)\n }\n })\n}\n\n/** Hosts activate their presentation owner (Notify in the Mentra App) through this seam. */\nexport function setPhoneNotificationPresentationActive(active: boolean): void {\n if (active === presentationActive) return\n presentationActive = active\n pushNativeConfig()\n}\n\nexport function usesNativeNotificationPresentation(): boolean {\n return (\n nativeNotificationCapabilities().supported &&\n presentationActive &&\n Boolean(useSettingsStore.getState().getSetting(SETTINGS.native_notifications_enabled.key))\n )\n}\n\n/** Return true when the selected firmware presentation owns this event, including quiet/history mode. */\nexport async function presentNativePhoneNotification(event: PhoneNotificationEvent): Promise<boolean> {\n if (!usesNativeNotificationPresentation()) return false\n if (!desiredConfig().enabled) return true\n if (Platform.OS !== \"android\") return true // G2 receives ANCS directly; never upload metadata back.\n const blocked = useSettingsStore.getState().getSetting(SETTINGS.notifications_blocklist.key)\n if (Array.isArray(blocked) && blocked.includes(event.packageName)) return true\n if (!isGlassesReady(useGlassesStore.getState().connection)) return true\n await BluetoothSdk.sendPhoneNotification({\n notificationId: String(event.notificationId ?? \"\"),\n packageName: String(event.packageName ?? \"\"),\n appName: String(event.app ?? \"\"),\n title: String(event.title ?? \"\"),\n subtitle: \"\",\n body: String(event.content ?? \"\"),\n timestampMs: Number(event.timestamp ?? Date.now()),\n action: 0,\n })\n return true\n}\n\nfunction pushConfig(): void {\n const settings = useSettingsStore.getState()\n if (Platform.OS === \"android\") {\n const enabled = Boolean(settings.getSetting(SETTINGS.android_notification_listener_enabled.key))\n const blocklist = settings.getSetting(SETTINGS.notifications_blocklist.key)\n void CrustModule.setNotificationConfig(enabled, Array.isArray(blocklist) ? blocklist : []).catch((error: unknown) =>\n console.warn(\"PhoneNotificationsSync: listener configuration failed\", error),\n )\n }\n pushNativeConfig()\n}\n\nexport function startPhoneNotificationsSync(): void {\n if (unsubscribers.length) return\n pushConfig()\n unsubscribers = [\n useSettingsStore.subscribe(\n (state) =>\n [\n SETTINGS.android_notification_listener_enabled,\n SETTINGS.notifications_blocklist,\n SETTINGS.native_notifications_enabled,\n SETTINGS.native_notifications_auto_display,\n SETTINGS.native_notifications_duration,\n SETTINGS.native_notifications_do_not_disturb,\n ].map((setting) => state.getSetting(setting.key)),\n pushConfig,\n {equalityFn: shallow},\n ),\n useGlassesStore.subscribe((state) => ({model: state.deviceModel, connection: state.connection}), pushNativeConfig, {\n equalityFn: shallow,\n }),\n ]\n}\n\nexport function stopPhoneNotificationsSync(): void {\n setPhoneNotificationPresentationActive(false)\n unsubscribers.forEach((unsubscribe) => unsubscribe())\n unsubscribers = []\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../../src/stores/settings.ts"],"names":[],"mappings":"AAEA,OAAO,EAAC,WAAW,EAAwB,MAAM,aAAa,CAAA;AAQ9D,OAAO,EAAC,wBAAwB,EAAE,gCAAgC,EAAC,MAAM,wBAAwB,CAAA;AAEjG,MAAM,WAAW,OAAO;IACtB,GAAG,EAAE,MAAM,CAAA;IACX,YAAY,EAAE,MAAM,GAAG,CAAA;IACvB,QAAQ,EAAE,OAAO,CAAA;IACjB,YAAY,EAAE,OAAO,CAAA;IAGrB,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAA;IAEjC,QAAQ,CAAC,EAAE,MAAM,GAAG,CAAA;IAEpB,OAAO,EAAE,OAAO,CAAA;IAQhB,mBAAmB,CAAC,EAAE,IAAI,CAAA;IAC1B;;;;;;;;;;;;;;;OAeG;IACH,qBAAqB,CAAC,EAAE,IAAI,CAAA;CAC7B;AAED,eAAO,MAAM,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,
|
|
1
|
+
{"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../../src/stores/settings.ts"],"names":[],"mappings":"AAEA,OAAO,EAAC,WAAW,EAAwB,MAAM,aAAa,CAAA;AAQ9D,OAAO,EAAC,wBAAwB,EAAE,gCAAgC,EAAC,MAAM,wBAAwB,CAAA;AAEjG,MAAM,WAAW,OAAO;IACtB,GAAG,EAAE,MAAM,CAAA;IACX,YAAY,EAAE,MAAM,GAAG,CAAA;IACvB,QAAQ,EAAE,OAAO,CAAA;IACjB,YAAY,EAAE,OAAO,CAAA;IAGrB,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAA;IAEjC,QAAQ,CAAC,EAAE,MAAM,GAAG,CAAA;IAEpB,OAAO,EAAE,OAAO,CAAA;IAQhB,mBAAmB,CAAC,EAAE,IAAI,CAAA;IAC1B;;;;;;;;;;;;;;;OAeG;IACH,qBAAqB,CAAC,EAAE,IAAI,CAAA;CAC7B;AAED,eAAO,MAAM,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAyqBnC,CAAA;AAEV,eAAO,MAAM,eAAe,EAAE,MAAM,EAAqD,CAAA;AAIzF,eAAO,MAAM,sBAAsB,EAAE,MAAM,EAiD1C,CAAA;AAeD,eAAO,MAAM,qBAAqB,EAAE,MAAM,EAEV,CAAA;AAIhC,MAAM,WAAW,aAAa;IAE5B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAE7B,aAAa,EAAE,OAAO,CAAA;IAEtB,UAAU,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,YAAY,CAAC,EAAE,OAAO,KAAK,WAAW,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IACzF,cAAc,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,WAAW,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IAC3E,UAAU,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,GAAG,CAAA;IAEhC,eAAe,EAAE,MAAM,WAAW,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IAE/C,oBAAoB,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAC/C,uBAAuB,EAAE,MAAM,IAAI,CAAA;CACpC;AAkBD,eAAO,MAAM,gBAAgB;;;;;;;;EA8V5B,CAAA;AAED,eAAO,MAAM,UAAU,GAAI,CAAC,GAAG,GAAG,EAAE,KAAK,MAAM,KAAG,CAAC,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,WAAW,CAAC,IAAI,EAAE,KAAK,CAAC,CAK3F,CAAA"}
|
package/build/stores/settings.js
CHANGED
|
@@ -124,6 +124,36 @@ export const SETTINGS = {
|
|
|
124
124
|
saveOnServer: false,
|
|
125
125
|
persist: true,
|
|
126
126
|
},
|
|
127
|
+
// Select firmware-owned presentation instead of the Mentra card. Notify still
|
|
128
|
+
// controls whether presentation is running on either phone platform.
|
|
129
|
+
native_notifications_enabled: {
|
|
130
|
+
key: "native_notifications_enabled",
|
|
131
|
+
defaultValue: () => false,
|
|
132
|
+
writable: true,
|
|
133
|
+
saveOnServer: false,
|
|
134
|
+
persist: true,
|
|
135
|
+
},
|
|
136
|
+
native_notifications_auto_display: {
|
|
137
|
+
key: "native_notifications_auto_display",
|
|
138
|
+
defaultValue: () => true,
|
|
139
|
+
writable: true,
|
|
140
|
+
saveOnServer: false,
|
|
141
|
+
persist: true,
|
|
142
|
+
},
|
|
143
|
+
native_notifications_duration: {
|
|
144
|
+
key: "native_notifications_duration",
|
|
145
|
+
defaultValue: () => 5,
|
|
146
|
+
writable: true,
|
|
147
|
+
saveOnServer: false,
|
|
148
|
+
persist: true,
|
|
149
|
+
},
|
|
150
|
+
native_notifications_do_not_disturb: {
|
|
151
|
+
key: "native_notifications_do_not_disturb",
|
|
152
|
+
defaultValue: () => false,
|
|
153
|
+
writable: true,
|
|
154
|
+
saveOnServer: false,
|
|
155
|
+
persist: true,
|
|
156
|
+
},
|
|
127
157
|
china_deployment: {
|
|
128
158
|
key: "china_deployment",
|
|
129
159
|
defaultValue: () => (process.env.EXPO_PUBLIC_DEPLOYMENT_REGION === "china" ? true : false),
|