@mentra/crust 3.3.0-dev.542 → 3.3.0-dev.548
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/android/src/main/AndroidManifest.xml +0 -5
- package/android/src/main/java/com/mentra/crust/CrustModule.kt +0 -14
- package/build/Crust.types.d.ts +0 -14
- package/build/Crust.types.d.ts.map +1 -1
- package/build/Crust.types.js.map +1 -1
- package/build/CrustModule.d.ts +0 -2
- package/build/CrustModule.d.ts.map +1 -1
- package/build/CrustModule.js.map +1 -1
- package/ios/CrustModule.swift +0 -1
- package/package.json +2 -2
- package/src/Crust.types.ts +0 -15
- package/src/CrustModule.ts +0 -2
- package/android/src/main/java/com/mentra/crust/receivers/IncidentReportDelivery.kt +0 -56
- package/android/src/main/java/com/mentra/crust/receivers/SubmitIncidentReportReceiver.kt +0 -73
- package/android/src/test/java/com/mentra/crust/receivers/IncidentReportDeliveryTest.kt +0 -85
- package/android/src/test/java/com/mentra/crust/receivers/SubmitIncidentReportReceiverTest.kt +0 -126
|
@@ -5,11 +5,6 @@
|
|
|
5
5
|
<uses-permission android:name="${applicationId}.permission.CRUST_NOTIFICATION_BRIDGE" />
|
|
6
6
|
|
|
7
7
|
<application>
|
|
8
|
-
<receiver android:name="com.mentra.crust.receivers.SubmitIncidentReportReceiver" android:exported="true">
|
|
9
|
-
<intent-filter>
|
|
10
|
-
<action android:name="com.mentra.SUBMIT_INCIDENT_REPORT" />
|
|
11
|
-
</intent-filter>
|
|
12
|
-
</receiver>
|
|
13
8
|
|
|
14
9
|
<meta-data
|
|
15
10
|
android:name="com.mentra.bluetoothsdk.RUNTIME_RECOVERY_SERVICE"
|
|
@@ -14,14 +14,12 @@ import com.mentra.crust.heading.HeadingManager
|
|
|
14
14
|
import com.mentra.crust.jsc.JSCRuntime
|
|
15
15
|
import com.mentra.crust.jsc.InstalledMiniappManifest
|
|
16
16
|
import com.mentra.crust.jsc.JSCPolyfillBridge
|
|
17
|
-
import com.mentra.crust.receivers.IncidentReportDelivery
|
|
18
17
|
|
|
19
18
|
class CrustModule : Module() {
|
|
20
19
|
companion object {
|
|
21
20
|
private const val TAG = "CrustModule"
|
|
22
21
|
|
|
23
22
|
@Volatile private var eventEmitter: ((String, Map<String, Any>) -> Unit)? = null
|
|
24
|
-
private val incidentReports = IncidentReportDelivery()
|
|
25
23
|
|
|
26
24
|
fun emitPhoneNotification(
|
|
27
25
|
context: android.content.Context,
|
|
@@ -53,9 +51,6 @@ class CrustModule : Module() {
|
|
|
53
51
|
NotificationProcessBridge.emitDismissed(context, notificationKey, packageName)
|
|
54
52
|
}
|
|
55
53
|
|
|
56
|
-
/** Returns false when no started JS report service received the request. */
|
|
57
|
-
fun emitSubmitIncidentReport(data: Map<String, Any>): Boolean = incidentReports.deliver(data)
|
|
58
|
-
|
|
59
54
|
private fun emitEvent(eventName: String, data: Map<String, Any>) {
|
|
60
55
|
val emitter = eventEmitter
|
|
61
56
|
if (emitter == null) {
|
|
@@ -122,7 +117,6 @@ class CrustModule : Module() {
|
|
|
122
117
|
"onChange",
|
|
123
118
|
"phone_notification",
|
|
124
119
|
"phone_notification_dismissed",
|
|
125
|
-
"submit_incident_report",
|
|
126
120
|
"onNavManeuver",
|
|
127
121
|
"onNavRerouting",
|
|
128
122
|
"onNavArrived",
|
|
@@ -138,7 +132,6 @@ class CrustModule : Module() {
|
|
|
138
132
|
|
|
139
133
|
OnCreate {
|
|
140
134
|
eventEmitter = { eventName, data -> sendEvent(eventName, data) }
|
|
141
|
-
incidentReports.attach { data -> sendEvent("submit_incident_report", data) }
|
|
142
135
|
registerNotificationBridgeIfPossible()
|
|
143
136
|
installRuntimeIfPossible("OnCreate")
|
|
144
137
|
}
|
|
@@ -152,19 +145,12 @@ class CrustModule : Module() {
|
|
|
152
145
|
notificationEventReceiver = null
|
|
153
146
|
notificationBridgeContext = null
|
|
154
147
|
eventEmitter = null
|
|
155
|
-
incidentReports.detach()
|
|
156
148
|
}
|
|
157
149
|
|
|
158
150
|
Function("hello") {
|
|
159
151
|
"Hello world! 👋"
|
|
160
152
|
}
|
|
161
153
|
|
|
162
|
-
// SubmitIncidentReportService reports its listener state so broadcasts that
|
|
163
|
-
// arrive before it subscribes get an immediate failed receipt, not silence.
|
|
164
|
-
Function("setIncidentReportServiceReady") { ready: Boolean ->
|
|
165
|
-
incidentReports.setServiceReady(ready)
|
|
166
|
-
}
|
|
167
|
-
|
|
168
154
|
AsyncFunction("setValueAsync") { value: String ->
|
|
169
155
|
sendEvent("onChange", mapOf("value" to value))
|
|
170
156
|
}
|
package/build/Crust.types.d.ts
CHANGED
|
@@ -14,7 +14,6 @@ export type CrustModuleEvents = {
|
|
|
14
14
|
onHeading: (params: HeadingPayload) => void;
|
|
15
15
|
phone_notification: (event: PhoneNotificationEvent) => void;
|
|
16
16
|
phone_notification_dismissed: (event: PhoneNotificationDismissedEvent) => void;
|
|
17
|
-
submit_incident_report: (event: SubmitIncidentReportEvent) => void;
|
|
18
17
|
};
|
|
19
18
|
export type NavOffRoutePayload = {
|
|
20
19
|
/** Approximate perpendicular distance in meters from the route. */
|
|
@@ -133,19 +132,6 @@ export type PhoneNotificationDismissedEvent = {
|
|
|
133
132
|
packageName: string;
|
|
134
133
|
notificationId: string;
|
|
135
134
|
};
|
|
136
|
-
export type SubmitIncidentReportEvent = {
|
|
137
|
-
action?: string;
|
|
138
|
-
timestamp?: number;
|
|
139
|
-
failure_code?: string;
|
|
140
|
-
failure_message?: string;
|
|
141
|
-
test_run_id?: string;
|
|
142
|
-
scenario_name?: string;
|
|
143
|
-
alert_id?: string;
|
|
144
|
-
expected_behavior?: string;
|
|
145
|
-
dashboard_url?: string;
|
|
146
|
-
source?: string;
|
|
147
|
-
[key: string]: unknown;
|
|
148
|
-
};
|
|
149
135
|
export type CrustViewProps = {
|
|
150
136
|
url: string;
|
|
151
137
|
onLoad: (event: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Crust.types.d.ts","sourceRoot":"","sources":["../src/Crust.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,SAAS,EAAE,SAAS,EAAC,MAAM,cAAc,CAAA;AAEtD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,GAAG,EAAE,MAAM,CAAA;CACZ,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,EAAE,CAAC,MAAM,EAAE,kBAAkB,KAAK,IAAI,CAAA;IAC9C,aAAa,EAAE,CAAC,MAAM,EAAE,kBAAkB,KAAK,IAAI,CAAA;IACnD,cAAc,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,IAAI,CAAA;IACvD,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,IAAI,CAAA;IACrD,UAAU,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,CAAA;IAC7C,aAAa,EAAE,CAAC,MAAM,EAAE,kBAAkB,KAAK,IAAI,CAAA;IACnD,UAAU,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,CAAA;IAC7C,aAAa,EAAE,CAAC,MAAM,EAAE,kBAAkB,KAAK,IAAI,CAAA;IACnD,SAAS,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,IAAI,CAAA;IAC3C,kBAAkB,EAAE,CAAC,KAAK,EAAE,sBAAsB,KAAK,IAAI,CAAA;IAC3D,4BAA4B,EAAE,CAAC,KAAK,EAAE,+BAA+B,KAAK,IAAI,CAAA;
|
|
1
|
+
{"version":3,"file":"Crust.types.d.ts","sourceRoot":"","sources":["../src/Crust.types.ts"],"names":[],"mappings":"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"}
|
package/build/Crust.types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Crust.types.js","sourceRoot":"","sources":["../src/Crust.types.ts"],"names":[],"mappings":"","sourcesContent":["import type {StyleProp, ViewStyle} from \"react-native\"\n\nexport type OnLoadEventPayload = {\n url: string\n}\n\nexport type CrustModuleEvents = {\n onChange: (params: ChangeEventPayload) => void\n onNavManeuver: (params: NavManeuverPayload) => void\n onNavRerouting: (params: Record<string, never>) => void\n onNavArrived: (params: Record<string, never>) => void\n onNavError: (params: NavErrorPayload) => void\n onNavLocation: (params: NavLocationPayload) => void\n onNavRoute: (params: NavRoutePayload) => void\n onNavOffRoute: (params: NavOffRoutePayload) => void\n onHeading: (params: HeadingPayload) => void\n phone_notification: (event: PhoneNotificationEvent) => void\n phone_notification_dismissed: (event: PhoneNotificationDismissedEvent) => void\n
|
|
1
|
+
{"version":3,"file":"Crust.types.js","sourceRoot":"","sources":["../src/Crust.types.ts"],"names":[],"mappings":"","sourcesContent":["import type {StyleProp, ViewStyle} from \"react-native\"\n\nexport type OnLoadEventPayload = {\n url: string\n}\n\nexport type CrustModuleEvents = {\n onChange: (params: ChangeEventPayload) => void\n onNavManeuver: (params: NavManeuverPayload) => void\n onNavRerouting: (params: Record<string, never>) => void\n onNavArrived: (params: Record<string, never>) => void\n onNavError: (params: NavErrorPayload) => void\n onNavLocation: (params: NavLocationPayload) => void\n onNavRoute: (params: NavRoutePayload) => void\n onNavOffRoute: (params: NavOffRoutePayload) => void\n onHeading: (params: HeadingPayload) => void\n phone_notification: (event: PhoneNotificationEvent) => void\n phone_notification_dismissed: (event: PhoneNotificationDismissedEvent) => void\n}\n\nexport type NavOffRoutePayload = {\n /** Approximate perpendicular distance in meters from the route. */\n offRouteDistanceMeters: number\n}\n\nexport type HeadingPayload = {\n /** Compass heading in degrees, 0 = north, 90 = east. */\n degrees: number\n}\n\nexport type NavRoutePayload = {\n points: Array<{lat: number; lng: number}>\n /**\n * Ordered step list along the route, when supplied by the host. Each\n * step is one navigable segment — typically one straight stretch of\n * road ending in a maneuver. Optional for back-compat with older\n * hosts that only ship the polyline. The SDK's pivot module consumes\n * this to enrich pivots with `fromRoad` / `toRoad` metadata.\n */\n steps?: NavRouteStepPayload[]\n}\n\nexport type NavRouteStepPayload = {\n /** Coordinate where this step begins (== end of the previous step). */\n lat: number\n lng: number\n /**\n * Index into `NavRoutePayload.points[]` where this step starts.\n * Lets consumers correlate step boundaries with polyline geometry.\n */\n routeIndex: number\n /** Name of the road traversed during this step. Null when the engine has no name. */\n road?: string | null\n /**\n * Categorical maneuver performed at the END of this step (i.e. how\n * the user exits this step). Same vocabulary as `NavManeuverPayload.maneuverType`.\n */\n maneuver: string\n /** Length of this step in meters. */\n distanceMeters: number\n}\n\nexport type NavLocationPayload = {\n lat: number\n lng: number\n /** Horizontal accuracy in meters, if reported by the platform. */\n accuracy: number | null\n /** Unix ms timestamp of the fix. */\n timestamp: number\n}\n\nexport type ChangeEventPayload = {\n value: string\n}\n\nexport type NavManeuverPayload = {\n /**\n * Categorical type of the upcoming maneuver. One of: STRAIGHT,\n * SLIGHT_LEFT, SLIGHT_RIGHT, TURN_LEFT, TURN_RIGHT, SHARP_LEFT,\n * SHARP_RIGHT, U_TURN, ARRIVE.\n */\n maneuverType: string\n /** Distance in meters from the user's current position to that maneuver. -1 if unknown. */\n distanceMeters: number\n /** Road the user is currently on, per the Nav SDK. Null if unavailable. */\n fromRoad?: string | null\n /**\n * Legacy \"next road\" field — historically populated from the same\n * source as `fromRoad` (the current step's road, NOT the road\n * after the upcoming turn). Kept for back-compat. New consumers\n * should read `nextStepRoad` instead.\n */\n toRoad?: string | null\n /**\n * Road the user will be on AFTER the upcoming maneuver, sourced\n * from the Nav SDK's `remainingSteps[0]`. This is the value to\n * show as the \"next street\" headline on the maneuver card. Null\n * when the SDK hasn't surfaced a remaining step yet (final leg,\n * pre-first-NavInfo, etc.).\n */\n nextStepRoad?: string | null\n /** Total remaining distance to final destination, meters. -1 if unknown. */\n distanceToDestinationMeters?: number\n /** Total remaining travel time, seconds. -1 if unknown. */\n timeToDestinationSeconds?: number\n /** Current speed in m/s. Null if unavailable. */\n currentSpeedMps?: number | null\n /** Speed limit on the current road segment in m/s. Null if unknown / not regulated. */\n speedLimitMps?: number | null\n /** Bearing along the route at the user's current position, 0–360. Null if unknown. */\n routeHeadingDeg?: number | null\n}\n\nexport type NavErrorPayload = {\n message: string\n}\n\nexport type InstalledApp = {\n packageName: string\n appName: string\n isBlocked: boolean\n icon: string | null\n}\n\nexport type PhoneNotificationEvent = {\n notificationId: string\n app: string\n title: string\n content: string\n /**\n * Android notification priority: PRIORITY_MIN (-2) through PRIORITY_MAX (2).\n * Derived from the channel importance when the ranking is available, since\n * Notification.priority reads 0 for channel-based notifications.\n */\n priority: number\n timestamp: number\n packageName: string\n}\n\nexport type PhoneNotificationDismissedEvent = {\n notificationKey: string\n packageName: string\n notificationId: string\n}\n\nexport type CrustViewProps = {\n url: string\n onLoad: (event: {nativeEvent: OnLoadEventPayload}) => void\n style?: StyleProp<ViewStyle>\n}\n"]}
|
package/build/CrustModule.d.ts
CHANGED
|
@@ -13,8 +13,6 @@ declare class CrustModule extends NativeModule<CrustModuleEvents> {
|
|
|
13
13
|
body: string;
|
|
14
14
|
}>;
|
|
15
15
|
showAVRoutePicker(tintColor?: string | null): void;
|
|
16
|
-
/** Android: whether the JS incident report service is subscribed to submit_incident_report. */
|
|
17
|
-
setIncidentReportServiceReady(ready: boolean): void;
|
|
18
16
|
/**
|
|
19
17
|
* iOS: configure `preferredScreenEdgesDeferringSystemGestures`. When an
|
|
20
18
|
* edge is deferred, the first swipe across that edge is consumed by the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CrustModule.d.ts","sourceRoot":"","sources":["../src/CrustModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,YAAY,EAAsB,MAAM,MAAM,CAAA;AAEtD,OAAO,EAAC,iBAAiB,EAAE,YAAY,EAAC,MAAM,eAAe,CAAA;AAE7D,OAAO,OAAO,WAAY,SAAQ,YAAY,CAAC,iBAAiB,CAAC;IAC/D,mEAAmE;IACnE,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAA;IAChC,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,IAAI,MAAM;IACf,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAC3C,iBAAiB,CACf,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,GACnB,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAC,CAAC;IAC/F,iBAAiB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;
|
|
1
|
+
{"version":3,"file":"CrustModule.d.ts","sourceRoot":"","sources":["../src/CrustModule.ts"],"names":[],"mappings":"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"}
|
package/build/CrustModule.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CrustModule.js","sourceRoot":"","sources":["../src/CrustModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,mBAAmB,EAAC,MAAM,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"CrustModule.js","sourceRoot":"","sources":["../src/CrustModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,mBAAmB,EAAC,MAAM,MAAM,CAAA;AAgMtD,yDAAyD;AACzD,eAAe,mBAAmB,CAAc,OAAO,CAAC,CAAA","sourcesContent":["import {NativeModule, requireNativeModule} from \"expo\"\n\nimport {CrustModuleEvents, InstalledApp} from \"./Crust.types\"\n\ndeclare class CrustModule extends NativeModule<CrustModuleEvents> {\n /** True for the native iOS app running on an Apple Silicon Mac. */\n readonly isIOSAppOnMac?: boolean\n PI: number\n hello(): string\n setValueAsync(value: string): Promise<void>\n nativeHttpRequest(\n method: string,\n url: string,\n headers: Record<string, string>,\n body?: string | null,\n ): Promise<{status: number; statusText: string; headers: Record<string, string>; body: string}>\n showAVRoutePicker(tintColor?: string | null): void\n\n /**\n * iOS: configure `preferredScreenEdgesDeferringSystemGestures`. When an\n * edge is deferred, the first swipe across that edge is consumed by the\n * app and the system gesture (Control Center, Notification Center, Home\n * indicator) only fires on a second swipe — i.e. a two-swipe-to-exit UX.\n *\n * Pass `[]` to restore default behavior. Android: no-op (Android has no\n * per-app equivalent; system gestures are configured at the OS level).\n */\n setDeferredSystemGestures(edges: Array<\"top\" | \"bottom\" | \"left\" | \"right\" | \"all\">): Promise<void>\n\n // MentraOS Notification Commands\n setNotificationConfig(listenerEnabled: boolean, blocklist: string[]): Promise<void>\n getInstalledApps(): Promise<InstalledApp[]>\n getInstalledAppsForNotifications(): Promise<InstalledApp[]>\n hasNotificationListenerPermission(): Promise<boolean>\n refreshNotificationListener(): Promise<boolean>\n openNotificationListenerSettings(): Promise<boolean>\n isBetaBuild(): Promise<boolean>\n // location services commands\n showLocationServicesDialog(): Promise<boolean>\n isLocationServicesEnabled(): Promise<boolean>\n openLocationSettings(): Promise<boolean>\n openAppSettings(): Promise<boolean>\n openBluetoothSettings(): Promise<boolean>\n\n // Image Processing Commands\n processGalleryImage(\n inputPath: string,\n outputPath: string,\n options: {\n lensCorrection?: boolean\n colorCorrection?: boolean\n },\n ): Promise<{\n success: boolean\n outputPath?: string\n processingTimeMs?: number\n error?: string\n }>\n\n mergeHdrBrackets(\n underPath: string,\n normalPath: string,\n overPath: string,\n outputPath: string,\n ): Promise<{\n success: boolean\n outputPath?: string\n processingTimeMs?: number\n error?: string\n }>\n\n stabilizeVideo(\n inputPath: string,\n imuPath: string,\n outputPath: string,\n ): Promise<{\n success: boolean\n outputPath?: string\n processingTimeMs?: number\n error?: string\n }>\n\n // Media Library Commands\n saveToGalleryWithDate(\n filePath: string,\n captureTimeMillis?: number,\n displayName?: string,\n ): Promise<{\n success: boolean\n uri?: string\n identifier?: string\n existing?: boolean\n error?: string\n }>\n\n // Navigation (Android only — iOS stubs return error)\n startNavigation(\n lat: number,\n lng: number,\n options?: {\n simulate?: boolean\n speedMultiplier?: number\n /** Optional multi-stop list. When present takes precedence over lat/lng. Last entry is the final destination. */\n stops?: Array<{lat: number; lng: number}>\n /** \"walking\" | \"driving\" | \"cycling\" | \"two_wheeler\". Defaults driving. */\n mode?: string\n avoid?: {highways?: boolean; tolls?: boolean; ferries?: boolean}\n /** Force a reroute when the user is more than N meters past a pivot they didn't take. */\n missedTurnRerouteMeters?: number\n },\n ): Promise<{ok: boolean; error?: string}>\n stopNavigation(): Promise<{ok: boolean; error?: string}>\n\n /**\n * Show the Google Nav SDK Terms & Conditions dialog if not already\n * accepted. Idempotent — resolves immediately with `{accepted: true}`\n * when the user has already accepted (cached in-process / on-disk /\n * inside the SDK).\n */\n requestNavigationPermission(): Promise<{ok: boolean; accepted: boolean; error?: string}>\n\n /**\n * Dev-only: clear cached T&C acceptance (SDK flag + on-disk pref +\n * in-process flag) so the next requestNavigationPermission() re-shows\n * the dialog. Android only; iOS returns {ok: false, error: \"not\n * supported on iOS\"}.\n */\n resetNavigationPermission(): Promise<{ok: boolean; error?: string}>\n\n /**\n * Dev-only: nudge the simulated user position ~offsetMeters perpendicular\n * to the route so the Nav SDK reroutes. Default 20m. Android only.\n */\n simulateDeviation(offsetMeters?: number): Promise<{ok: boolean; error?: string}>\n\n /**\n * Dev toggle. When enabled, the native NavigationManager shifts every\n * reported location ~8m perpendicular to the route bearing, simulating\n * a pedestrian walking on the wrong sidewalk. Only meaningful in\n * simulate mode; lets us verify the SDK's along-path pivot trigger\n * fires even when the user never crosses the 7m pivot point radius.\n * Android-only today; iOS is a no-op stub.\n */\n setWrongSidewalkOffset(enabled: boolean): Promise<{ok: boolean; error?: string}>\n\n /**\n * Dev toggle. When enabled, the native NavigationManager takes over\n * from the Google simulator and walks the user along a modified\n * polyline that omits crossing micro-steps — reproducing the\n * wrong-sidewalk-then-missed-the-turn scenario. Android-only today;\n * iOS is a no-op stub.\n */\n setSkipCrossings(enabled: boolean): Promise<{ok: boolean; error?: string}>\n\n // Heading / compass (Android only)\n startHeading(): Promise<{ok: boolean; error?: string}>\n stopHeading(): Promise<{ok: boolean; error?: string}>\n\n // MentraJS Runtime — per-miniapp JSContext lifecycle.\n /**\n * Spawn a per-miniapp JS context. Re-spawn is allowed: a live context\n * with the same packageName is killed first. Returns true if the\n * polyfill bundle + miniapp source evaluated without throwing.\n *\n * The polyfill bundle is `mobile/modules/mentrajs-runtime/dist/startup.js`\n * (shipped inside the host binary). It installs window-style globals\n * (console, timers, fetch, localStorage, crypto) atop the JSC runtime.\n */\n mentraJsSpawn(packageName: string, polyfillBundle: string, miniappJs: string): Promise<boolean>\n mentraJsEvaluate(packageName: string, source: string): Promise<unknown>\n mentraJsKill(packageName: string): Promise<void>\n /**\n * Push a `{kind: \"event\"|\"response\", …}` envelope into the named\n * context's globalThis.__deliver. Returns when the underlying\n * evaluateScript completes (the JS handler runs synchronously on the\n * context's queue).\n */\n mentraJsDispatchToJs(packageName: string, envelope: Record<string, unknown>): Promise<void>\n mentraJsSetManifest(packageName: string, permissions: string[]): Promise<void>\n /** Diagnostic — returns the packageNames of every live JSContext. */\n mentraJsAlivePackages(): string[]\n /** Diagnostic — force a GC cycle on the named context. */\n mentraJsDebugForceGC(packageName: string): Promise<boolean>\n /**\n * Read the bundled MentraJS polyfill (startup.js) shipped inside the\n * host binary. Synchronous — host RN code calls this once on app boot\n * and caches the string, then passes it to every mentraJsSpawn so\n * every JSContext starts with the same polyfill ABI.\n */\n mentraJsLoadPolyfillBundle(): string\n}\n\n// This call loads the native module object from the JSI.\nexport default requireNativeModule<CrustModule>(\"Crust\")\n"]}
|
package/ios/CrustModule.swift
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mentra/crust",
|
|
3
|
-
"version": "3.3.0-dev.
|
|
3
|
+
"version": "3.3.0-dev.548",
|
|
4
4
|
"description": "Mentra Native Module",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"types": "build/index.d.ts",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"license": "Apache-2.0",
|
|
36
36
|
"homepage": "https://github.com/Mentra-Community/MentraOS/tree/dev/mobile/modules/crust#readme",
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@mentra/jspolyfill": "3.3.0-dev.
|
|
38
|
+
"@mentra/jspolyfill": "3.3.0-dev.548"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@expo/config-plugins": ">=8.0.0",
|
package/src/Crust.types.ts
CHANGED
|
@@ -16,7 +16,6 @@ export type CrustModuleEvents = {
|
|
|
16
16
|
onHeading: (params: HeadingPayload) => void
|
|
17
17
|
phone_notification: (event: PhoneNotificationEvent) => void
|
|
18
18
|
phone_notification_dismissed: (event: PhoneNotificationDismissedEvent) => void
|
|
19
|
-
submit_incident_report: (event: SubmitIncidentReportEvent) => void
|
|
20
19
|
}
|
|
21
20
|
|
|
22
21
|
export type NavOffRoutePayload = {
|
|
@@ -144,20 +143,6 @@ export type PhoneNotificationDismissedEvent = {
|
|
|
144
143
|
notificationId: string
|
|
145
144
|
}
|
|
146
145
|
|
|
147
|
-
export type SubmitIncidentReportEvent = {
|
|
148
|
-
action?: string
|
|
149
|
-
timestamp?: number
|
|
150
|
-
failure_code?: string
|
|
151
|
-
failure_message?: string
|
|
152
|
-
test_run_id?: string
|
|
153
|
-
scenario_name?: string
|
|
154
|
-
alert_id?: string
|
|
155
|
-
expected_behavior?: string
|
|
156
|
-
dashboard_url?: string
|
|
157
|
-
source?: string
|
|
158
|
-
[key: string]: unknown
|
|
159
|
-
}
|
|
160
|
-
|
|
161
146
|
export type CrustViewProps = {
|
|
162
147
|
url: string
|
|
163
148
|
onLoad: (event: {nativeEvent: OnLoadEventPayload}) => void
|
package/src/CrustModule.ts
CHANGED
|
@@ -15,8 +15,6 @@ declare class CrustModule extends NativeModule<CrustModuleEvents> {
|
|
|
15
15
|
body?: string | null,
|
|
16
16
|
): Promise<{status: number; statusText: string; headers: Record<string, string>; body: string}>
|
|
17
17
|
showAVRoutePicker(tintColor?: string | null): void
|
|
18
|
-
/** Android: whether the JS incident report service is subscribed to submit_incident_report. */
|
|
19
|
-
setIncidentReportServiceReady(ready: boolean): void
|
|
20
18
|
|
|
21
19
|
/**
|
|
22
20
|
* iOS: configure `preferredScreenEdgesDeferringSystemGestures`. When an
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
package com.mentra.crust.receivers
|
|
2
|
-
|
|
3
|
-
import android.util.Log
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Hands incident requests to the JS report service only while it is subscribed.
|
|
7
|
-
*
|
|
8
|
-
* Crust's native OnCreate is not enough: the JS listener starts later, from the
|
|
9
|
-
* signed-in engine, and Expo drops events that no listener receives. Requests are
|
|
10
|
-
* not queued; [deliver] returns false so the receiver can answer immediately.
|
|
11
|
-
*/
|
|
12
|
-
internal class IncidentReportDelivery {
|
|
13
|
-
@Volatile private var emitter: ((Map<String, Any>) -> Unit)? = null
|
|
14
|
-
@Volatile private var serviceReady = false
|
|
15
|
-
|
|
16
|
-
/** Called from Crust OnCreate. A new JS runtime has not subscribed yet. */
|
|
17
|
-
fun attach(emit: (Map<String, Any>) -> Unit) {
|
|
18
|
-
serviceReady = false
|
|
19
|
-
emitter = emit
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/** Called from Crust OnDestroy. */
|
|
23
|
-
fun detach() {
|
|
24
|
-
serviceReady = false
|
|
25
|
-
emitter = null
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/** Called by the JS report service after subscribing and before unsubscribing. */
|
|
29
|
-
fun setServiceReady(ready: Boolean) {
|
|
30
|
-
serviceReady = ready
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/** Returns true only when the request was handed to a subscribed JS report service. */
|
|
34
|
-
fun deliver(request: Map<String, Any>): Boolean {
|
|
35
|
-
val emit = emitter
|
|
36
|
-
if (emit == null) {
|
|
37
|
-
Log.w(TAG, "Incident report request not delivered: Crust module is not created")
|
|
38
|
-
return false
|
|
39
|
-
}
|
|
40
|
-
if (!serviceReady) {
|
|
41
|
-
Log.w(TAG, "Incident report request not delivered: report service is not started")
|
|
42
|
-
return false
|
|
43
|
-
}
|
|
44
|
-
return try {
|
|
45
|
-
emit(request)
|
|
46
|
-
true
|
|
47
|
-
} catch (e: Exception) {
|
|
48
|
-
Log.e(TAG, "Error delivering incident report request", e)
|
|
49
|
-
false
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
private companion object {
|
|
54
|
-
const val TAG = "SubmitIncidentReport"
|
|
55
|
-
}
|
|
56
|
-
}
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
package com.mentra.crust.receivers
|
|
2
|
-
|
|
3
|
-
import android.content.BroadcastReceiver
|
|
4
|
-
import android.content.Context
|
|
5
|
-
import android.content.Intent
|
|
6
|
-
import android.util.Log
|
|
7
|
-
import com.mentra.crust.CrustModule
|
|
8
|
-
import org.json.JSONObject
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Receives incident-report requests and forwards them into React Native so
|
|
12
|
-
* the app can file a normal incident through the existing mobile incident pipeline.
|
|
13
|
-
* When the JS report service is not subscribed, the request is not queued: the
|
|
14
|
-
* caller immediately receives a correlated failed INCIDENT_REPORT_RESULT.
|
|
15
|
-
*/
|
|
16
|
-
class SubmitIncidentReportReceiver internal constructor(
|
|
17
|
-
private val emitReport: (Map<String, Any>) -> Boolean,
|
|
18
|
-
) : BroadcastReceiver() {
|
|
19
|
-
constructor() : this(CrustModule::emitSubmitIncidentReport)
|
|
20
|
-
|
|
21
|
-
@Suppress("DEPRECATION")
|
|
22
|
-
override fun onReceive(context: Context?, intent: Intent?) {
|
|
23
|
-
if (intent?.action != "com.mentra.SUBMIT_INCIDENT_REPORT") {
|
|
24
|
-
Log.w("SubmitIncidentReport", "Ignoring unsupported incident report action")
|
|
25
|
-
return
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
val body =
|
|
29
|
-
hashMapOf<String, Any>(
|
|
30
|
-
"action" to (intent.action ?: "unknown"),
|
|
31
|
-
"timestamp" to System.currentTimeMillis(),
|
|
32
|
-
)
|
|
33
|
-
|
|
34
|
-
intent.extras?.keySet()?.forEach { key ->
|
|
35
|
-
if (body.containsKey(key)) {
|
|
36
|
-
return@forEach
|
|
37
|
-
}
|
|
38
|
-
val value = intent.extras?.get(key) ?: return@forEach
|
|
39
|
-
when (value) {
|
|
40
|
-
is String, is Int, is Long, is Boolean, is Double, is Float -> body[key] = value
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (!emitReport(body)) {
|
|
45
|
-
Log.i(RESULT_LOG_TAG, "INCIDENT_REPORT_RESULT ${unavailableResult(body)}")
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
internal companion object {
|
|
50
|
-
// Callers read every receipt from the ReactNativeJS tag, where the JS report
|
|
51
|
-
// service logs filed/skipped/failed results; keep this one on the same tag.
|
|
52
|
-
const val RESULT_LOG_TAG = "ReactNativeJS"
|
|
53
|
-
const val UNAVAILABLE_ERROR =
|
|
54
|
-
"Incident report service is not running; the request was not queued. " +
|
|
55
|
-
"Open the Mentra App, sign in, and send a new request."
|
|
56
|
-
|
|
57
|
-
/** Mirrors SubmitIncidentReportService's IncidentReportResult correlation fields. */
|
|
58
|
-
fun unavailableResult(request: Map<String, Any>): String {
|
|
59
|
-
fun read(key: String) = (request[key] as? String)?.takeIf { it.isNotBlank() }
|
|
60
|
-
val testRunId = read("test_run_id")
|
|
61
|
-
return JSONObject()
|
|
62
|
-
.apply {
|
|
63
|
-
(read("alert_id") ?: testRunId)?.let { put("alert_id", it) }
|
|
64
|
-
testRunId?.let { put("test_run_id", it) }
|
|
65
|
-
put("failure_code", read("failure_code") ?: "unknown")
|
|
66
|
-
read("scenario_name")?.let { put("scenario_name", it) }
|
|
67
|
-
put("status", "failed")
|
|
68
|
-
put("error", UNAVAILABLE_ERROR)
|
|
69
|
-
}
|
|
70
|
-
.toString()
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
}
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
package com.mentra.crust.receivers
|
|
2
|
-
|
|
3
|
-
import org.junit.Assert.assertEquals
|
|
4
|
-
import org.junit.Assert.assertFalse
|
|
5
|
-
import org.junit.Assert.assertTrue
|
|
6
|
-
import org.junit.Test
|
|
7
|
-
import org.junit.runner.RunWith
|
|
8
|
-
import org.robolectric.RobolectricTestRunner
|
|
9
|
-
import org.robolectric.annotation.Config
|
|
10
|
-
|
|
11
|
-
@RunWith(RobolectricTestRunner::class)
|
|
12
|
-
@Config(sdk = [28], manifest = Config.NONE)
|
|
13
|
-
class IncidentReportDeliveryTest {
|
|
14
|
-
private val request = mapOf<String, Any>("alert_id" to "request-1")
|
|
15
|
-
|
|
16
|
-
@Test
|
|
17
|
-
fun refusesRequestBeforeCrustIsCreated() {
|
|
18
|
-
val delivery = IncidentReportDelivery()
|
|
19
|
-
delivery.setServiceReady(true)
|
|
20
|
-
assertFalse(delivery.deliver(request))
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
@Test
|
|
24
|
-
fun refusesRequestAfterCrustIsCreatedButBeforeReportServiceSubscribes() {
|
|
25
|
-
val emitted = mutableListOf<Map<String, Any>>()
|
|
26
|
-
val delivery = IncidentReportDelivery()
|
|
27
|
-
delivery.attach { emitted.add(it) }
|
|
28
|
-
assertFalse(delivery.deliver(request))
|
|
29
|
-
assertTrue(emitted.isEmpty())
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
@Test
|
|
33
|
-
fun deliversWhileReportServiceIsSubscribed() {
|
|
34
|
-
val emitted = mutableListOf<Map<String, Any>>()
|
|
35
|
-
val delivery = IncidentReportDelivery()
|
|
36
|
-
delivery.attach { emitted.add(it) }
|
|
37
|
-
delivery.setServiceReady(true)
|
|
38
|
-
assertTrue(delivery.deliver(request))
|
|
39
|
-
assertEquals(listOf(request), emitted)
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
@Test
|
|
43
|
-
fun refusedRequestsAreNotReplayedWhenTheServiceStarts() {
|
|
44
|
-
val emitted = mutableListOf<Map<String, Any>>()
|
|
45
|
-
val delivery = IncidentReportDelivery()
|
|
46
|
-
delivery.attach { emitted.add(it) }
|
|
47
|
-
assertFalse(delivery.deliver(request))
|
|
48
|
-
delivery.setServiceReady(true)
|
|
49
|
-
assertTrue(emitted.isEmpty())
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
@Test
|
|
53
|
-
fun serviceStopAndRestartGateDelivery() {
|
|
54
|
-
val emitted = mutableListOf<Map<String, Any>>()
|
|
55
|
-
val delivery = IncidentReportDelivery()
|
|
56
|
-
delivery.attach { emitted.add(it) }
|
|
57
|
-
delivery.setServiceReady(true)
|
|
58
|
-
delivery.setServiceReady(false)
|
|
59
|
-
assertFalse(delivery.deliver(request))
|
|
60
|
-
delivery.setServiceReady(true)
|
|
61
|
-
assertTrue(delivery.deliver(request))
|
|
62
|
-
assertEquals(1, emitted.size)
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
@Test
|
|
66
|
-
fun crustRecreationRequiresTheNewRuntimeToSubscribe() {
|
|
67
|
-
val emitted = mutableListOf<Map<String, Any>>()
|
|
68
|
-
val delivery = IncidentReportDelivery()
|
|
69
|
-
delivery.attach { emitted.add(it) }
|
|
70
|
-
delivery.setServiceReady(true)
|
|
71
|
-
delivery.detach()
|
|
72
|
-
assertFalse(delivery.deliver(request))
|
|
73
|
-
delivery.attach { emitted.add(it) }
|
|
74
|
-
assertFalse(delivery.deliver(request))
|
|
75
|
-
assertTrue(emitted.isEmpty())
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
@Test
|
|
79
|
-
fun emitterFailureIsReportedAsUndelivered() {
|
|
80
|
-
val delivery = IncidentReportDelivery()
|
|
81
|
-
delivery.attach { throw IllegalStateException("bridge gone") }
|
|
82
|
-
delivery.setServiceReady(true)
|
|
83
|
-
assertFalse(delivery.deliver(request))
|
|
84
|
-
}
|
|
85
|
-
}
|
package/android/src/test/java/com/mentra/crust/receivers/SubmitIncidentReportReceiverTest.kt
DELETED
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
package com.mentra.crust.receivers
|
|
2
|
-
|
|
3
|
-
import android.content.Intent
|
|
4
|
-
import android.net.Uri
|
|
5
|
-
import org.junit.Assert.assertEquals
|
|
6
|
-
import org.junit.Assert.assertFalse
|
|
7
|
-
import org.junit.Assert.assertTrue
|
|
8
|
-
import org.junit.Before
|
|
9
|
-
import org.junit.Test
|
|
10
|
-
import org.junit.runner.RunWith
|
|
11
|
-
import org.json.JSONObject
|
|
12
|
-
import org.robolectric.RobolectricTestRunner
|
|
13
|
-
import org.robolectric.annotation.Config
|
|
14
|
-
import org.robolectric.shadows.ShadowLog
|
|
15
|
-
|
|
16
|
-
@RunWith(RobolectricTestRunner::class)
|
|
17
|
-
@Config(sdk = [28], manifest = Config.NONE)
|
|
18
|
-
class SubmitIncidentReportReceiverTest {
|
|
19
|
-
@Before
|
|
20
|
-
fun clearLogs() {
|
|
21
|
-
ShadowLog.clear()
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
private fun receipts(): List<JSONObject> =
|
|
25
|
-
ShadowLog.getLogsForTag("ReactNativeJS")
|
|
26
|
-
.map { it.msg }
|
|
27
|
-
.filter { it.startsWith("INCIDENT_REPORT_RESULT ") }
|
|
28
|
-
.map { JSONObject(it.removePrefix("INCIDENT_REPORT_RESULT ")) }
|
|
29
|
-
|
|
30
|
-
@Test
|
|
31
|
-
fun forwardsReportMetadataAndPrimitiveExtras() {
|
|
32
|
-
val events = mutableListOf<Map<String, Any>>()
|
|
33
|
-
val receiver = SubmitIncidentReportReceiver { events.add(it) }
|
|
34
|
-
receiver.onReceive(null, Intent("com.mentra.SUBMIT_INCIDENT_REPORT").apply {
|
|
35
|
-
putExtra("alert_id", "request-1")
|
|
36
|
-
putExtra("test_run_id", "run-1")
|
|
37
|
-
putExtra("failure_message", "Update failed")
|
|
38
|
-
putExtra("source", "mentra_automated_testing")
|
|
39
|
-
putExtra("attempt", 2)
|
|
40
|
-
putExtra("recoverable", true)
|
|
41
|
-
putExtra("action", "spoofed")
|
|
42
|
-
putExtra("timestamp", -1L)
|
|
43
|
-
putExtra("unsupported", Uri.parse("content://unsupported"))
|
|
44
|
-
})
|
|
45
|
-
val event = events.single()
|
|
46
|
-
assertEquals("com.mentra.SUBMIT_INCIDENT_REPORT", event["action"])
|
|
47
|
-
assertEquals("request-1", event["alert_id"])
|
|
48
|
-
assertEquals("run-1", event["test_run_id"])
|
|
49
|
-
assertEquals("Update failed", event["failure_message"])
|
|
50
|
-
assertEquals("mentra_automated_testing", event["source"])
|
|
51
|
-
assertEquals(2, event["attempt"])
|
|
52
|
-
assertEquals(true, event["recoverable"])
|
|
53
|
-
assertTrue((event["timestamp"] as Long) > 0)
|
|
54
|
-
assertFalse(event.containsKey("unsupported"))
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
@Test
|
|
58
|
-
fun ignoresNullAndUnrelatedExplicitIntents() {
|
|
59
|
-
val events = mutableListOf<Map<String, Any>>()
|
|
60
|
-
val receiver = SubmitIncidentReportReceiver { events.add(it) }
|
|
61
|
-
receiver.onReceive(null, null)
|
|
62
|
-
receiver.onReceive(null, Intent("com.mentra.OTHER_ACTION"))
|
|
63
|
-
assertTrue(events.isEmpty())
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
@Test
|
|
67
|
-
fun deliveredRequestsLeaveTheReceiptToTheReportService() {
|
|
68
|
-
val receiver = SubmitIncidentReportReceiver { true }
|
|
69
|
-
receiver.onReceive(null, Intent("com.mentra.SUBMIT_INCIDENT_REPORT").putExtra("alert_id", "request-1"))
|
|
70
|
-
assertTrue(receipts().isEmpty())
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
@Test
|
|
74
|
-
fun undeliveredRequestGetsAnImmediateCorrelatedFailedReceipt() {
|
|
75
|
-
val receiver = SubmitIncidentReportReceiver { false }
|
|
76
|
-
receiver.onReceive(null, Intent("com.mentra.SUBMIT_INCIDENT_REPORT").apply {
|
|
77
|
-
putExtra("alert_id", "request-1")
|
|
78
|
-
putExtra("test_run_id", "run-1")
|
|
79
|
-
putExtra("failure_code", "unpair_failed")
|
|
80
|
-
putExtra("scenario_name", "no-glasses-android")
|
|
81
|
-
putExtra("failure_message", "Crash during Unpair")
|
|
82
|
-
})
|
|
83
|
-
val receipt = receipts().single()
|
|
84
|
-
assertEquals("request-1", receipt.getString("alert_id"))
|
|
85
|
-
assertEquals("run-1", receipt.getString("test_run_id"))
|
|
86
|
-
assertEquals("unpair_failed", receipt.getString("failure_code"))
|
|
87
|
-
assertEquals("no-glasses-android", receipt.getString("scenario_name"))
|
|
88
|
-
assertEquals("failed", receipt.getString("status"))
|
|
89
|
-
assertEquals(SubmitIncidentReportReceiver.UNAVAILABLE_ERROR, receipt.getString("error"))
|
|
90
|
-
assertFalse(receipt.has("report_id"))
|
|
91
|
-
assertFalse(receipt.has("incident_id"))
|
|
92
|
-
assertFalse(receipt.toString().contains("Crash during Unpair"))
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
@Test
|
|
96
|
-
fun undeliveredReceiptUsesTheServiceCorrelationDefaults() {
|
|
97
|
-
val receiver = SubmitIncidentReportReceiver { false }
|
|
98
|
-
receiver.onReceive(null, Intent("com.mentra.SUBMIT_INCIDENT_REPORT").apply {
|
|
99
|
-
putExtra("alert_id", " ")
|
|
100
|
-
putExtra("test_run_id", "run-1")
|
|
101
|
-
putExtra("failure_code", 7)
|
|
102
|
-
})
|
|
103
|
-
val receipt = receipts().single()
|
|
104
|
-
assertEquals("run-1", receipt.getString("alert_id"))
|
|
105
|
-
assertEquals("unknown", receipt.getString("failure_code"))
|
|
106
|
-
assertFalse(receipt.has("scenario_name"))
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
@Test
|
|
110
|
-
fun eachUndeliveredRetransmitGetsItsOwnReceiptAndNothingIsQueued() {
|
|
111
|
-
val attempts = mutableListOf<Map<String, Any>>()
|
|
112
|
-
val receiver = SubmitIncidentReportReceiver { attempts.add(it); false }
|
|
113
|
-
val intent = Intent("com.mentra.SUBMIT_INCIDENT_REPORT").putExtra("alert_id", "request-1")
|
|
114
|
-
receiver.onReceive(null, intent)
|
|
115
|
-
receiver.onReceive(null, intent)
|
|
116
|
-
assertEquals(2, attempts.size)
|
|
117
|
-
assertEquals(listOf("request-1", "request-1"), receipts().map { it.getString("alert_id") })
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
@Test
|
|
121
|
-
fun unsupportedActionsDoNotProduceReceipts() {
|
|
122
|
-
val receiver = SubmitIncidentReportReceiver { false }
|
|
123
|
-
receiver.onReceive(null, Intent("com.mentra.OTHER_ACTION"))
|
|
124
|
-
assertTrue(receipts().isEmpty())
|
|
125
|
-
}
|
|
126
|
-
}
|