@elizaos/capacitor-mobile-signals 1.0.0

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.
@@ -0,0 +1,162 @@
1
+ import type { PluginListenerHandle } from "@capacitor/core";
2
+ export type MobileSignalsPlatform = "android" | "ios" | "web";
3
+ export type MobileSignalsSource = "mobile_device" | "mobile_health";
4
+ export type MobileSignalsState = "active" | "idle" | "background" | "locked" | "sleeping";
5
+ export type MobileSignalsHealthSource = "healthkit" | "health_connect";
6
+ export type MobileSignalsSettingsTarget = "app" | "health" | "healthConnect" | "screenTime" | "usageAccess" | "notification" | "batteryOptimization" | "localNetwork" | "deviceSettings";
7
+ export type MobileSignalsSetupActionStatus = "ready" | "needs-action" | "unavailable";
8
+ export interface MobileSignalsSetupAction {
9
+ id: "health_permissions" | "screen_time_authorization" | "android_usage_access" | "app_settings" | "notification_settings" | "battery_optimization" | "local_network";
10
+ label: string;
11
+ status: MobileSignalsSetupActionStatus;
12
+ canRequest: boolean;
13
+ canOpenSettings: boolean;
14
+ settingsTarget: MobileSignalsSettingsTarget | null;
15
+ reason: string | null;
16
+ }
17
+ export interface MobileSignalsOpenSettingsOptions {
18
+ target?: MobileSignalsSettingsTarget;
19
+ }
20
+ export interface MobileSignalsOpenSettingsResult {
21
+ opened: boolean;
22
+ target: MobileSignalsSettingsTarget;
23
+ actualTarget: MobileSignalsSettingsTarget;
24
+ reason: string | null;
25
+ }
26
+ export interface MobileSignalsScreenTimeStatus {
27
+ supported: boolean;
28
+ requirements: {
29
+ entitlements: {
30
+ familyControls: string;
31
+ };
32
+ frameworks: string[];
33
+ deviceActivityReportExtension: boolean;
34
+ deviceActivityMonitorExtension: boolean;
35
+ android?: {
36
+ usageStatsPermission: string;
37
+ usageAccessSettingsAction: string;
38
+ };
39
+ };
40
+ entitlements: {
41
+ familyControls: boolean;
42
+ };
43
+ provisioning: {
44
+ satisfied: boolean;
45
+ inspected: "code-signature" | "not-inspectable";
46
+ reason: string | null;
47
+ };
48
+ authorization: {
49
+ status: "approved" | "denied" | "not-determined" | "unavailable";
50
+ canRequest: boolean;
51
+ };
52
+ reportAvailable: boolean;
53
+ coarseSummaryAvailable: boolean;
54
+ thresholdEventsAvailable: boolean;
55
+ rawUsageExportAvailable: false;
56
+ android?: {
57
+ usageAccessGranted: boolean;
58
+ packageUsageStatsPermissionDeclared: boolean;
59
+ canOpenUsageAccessSettings: boolean;
60
+ foregroundEventsAvailable: boolean;
61
+ totalTimeForegroundMs: number | null;
62
+ };
63
+ reason: string | null;
64
+ }
65
+ export interface MobileSignalsHealthSleepSnapshot {
66
+ available: boolean;
67
+ isSleeping: boolean;
68
+ asleepAt: number | null;
69
+ awakeAt: number | null;
70
+ durationMinutes: number | null;
71
+ stage: string | null;
72
+ }
73
+ export interface MobileSignalsHealthBiometricSnapshot {
74
+ sampleAt: number | null;
75
+ heartRateBpm: number | null;
76
+ restingHeartRateBpm: number | null;
77
+ heartRateVariabilityMs: number | null;
78
+ respiratoryRate: number | null;
79
+ bloodOxygenPercent: number | null;
80
+ }
81
+ export interface MobileSignalsHealthSnapshot {
82
+ source: "mobile_health";
83
+ platform: MobileSignalsPlatform;
84
+ state: "idle" | "sleeping";
85
+ observedAt: number;
86
+ idleState: "active" | "idle" | "locked" | "unknown" | null;
87
+ idleTimeSeconds: number | null;
88
+ onBattery: boolean | null;
89
+ healthSource: MobileSignalsHealthSource;
90
+ screenTime: MobileSignalsScreenTimeStatus;
91
+ permissions: {
92
+ sleep: boolean;
93
+ biometrics: boolean;
94
+ };
95
+ sleep: MobileSignalsHealthSleepSnapshot;
96
+ biometrics: MobileSignalsHealthBiometricSnapshot;
97
+ warnings: string[];
98
+ metadata: Record<string, unknown>;
99
+ }
100
+ export interface MobileSignalsSnapshot {
101
+ source: "mobile_device";
102
+ platform: MobileSignalsPlatform;
103
+ state: MobileSignalsState;
104
+ observedAt: number;
105
+ idleState: "active" | "idle" | "locked" | "unknown" | null;
106
+ idleTimeSeconds: number | null;
107
+ onBattery: boolean | null;
108
+ metadata: Record<string, unknown>;
109
+ }
110
+ export type MobileSignalsSignal = MobileSignalsSnapshot | MobileSignalsHealthSnapshot;
111
+ export interface MobileSignalsStartOptions {
112
+ emitInitial?: boolean;
113
+ }
114
+ export interface MobileSignalsStartResult {
115
+ enabled: boolean;
116
+ supported: boolean;
117
+ platform: MobileSignalsPlatform;
118
+ snapshot: MobileSignalsSnapshot | null;
119
+ healthSnapshot: MobileSignalsHealthSnapshot | null;
120
+ }
121
+ export interface MobileSignalsStopResult {
122
+ stopped: boolean;
123
+ }
124
+ export interface MobileSignalsSnapshotResult {
125
+ supported: boolean;
126
+ snapshot: MobileSignalsSnapshot | null;
127
+ healthSnapshot: MobileSignalsHealthSnapshot | null;
128
+ }
129
+ export interface MobileSignalsBackgroundRefreshResult {
130
+ scheduled: boolean;
131
+ identifier?: string;
132
+ earliestBeginInSeconds?: number;
133
+ reason?: string;
134
+ }
135
+ export interface MobileSignalsCancelBackgroundRefreshResult {
136
+ cancelled: boolean;
137
+ reason?: string;
138
+ }
139
+ export interface MobileSignalsPlugin {
140
+ checkPermissions(): Promise<MobileSignalsPermissionStatus>;
141
+ requestPermissions(): Promise<MobileSignalsPermissionStatus>;
142
+ openSettings(options?: MobileSignalsOpenSettingsOptions): Promise<MobileSignalsOpenSettingsResult>;
143
+ startMonitoring(options?: MobileSignalsStartOptions): Promise<MobileSignalsStartResult>;
144
+ stopMonitoring(): Promise<MobileSignalsStopResult>;
145
+ getSnapshot(): Promise<MobileSignalsSnapshotResult>;
146
+ scheduleBackgroundRefresh(): Promise<MobileSignalsBackgroundRefreshResult>;
147
+ cancelBackgroundRefresh(): Promise<MobileSignalsCancelBackgroundRefreshResult>;
148
+ addListener(eventName: "signal", listenerFunc: (event: MobileSignalsSignal) => void): Promise<PluginListenerHandle>;
149
+ removeAllListeners(): Promise<void>;
150
+ }
151
+ export interface MobileSignalsPermissionStatus {
152
+ status: "granted" | "denied" | "not-determined" | "not-applicable";
153
+ canRequest: boolean;
154
+ reason?: string;
155
+ screenTime: MobileSignalsScreenTimeStatus;
156
+ setupActions: MobileSignalsSetupAction[];
157
+ permissions: {
158
+ sleep: boolean;
159
+ biometrics: boolean;
160
+ };
161
+ }
162
+ //# sourceMappingURL=definitions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"definitions.d.ts","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,4 @@
1
+ import type { MobileSignalsPlugin } from "./definitions";
2
+ export * from "./definitions";
3
+ export declare const MobileSignals: MobileSignalsPlugin;
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAEzD,cAAc,eAAe,CAAC;AAI9B,eAAO,MAAM,aAAa,qBAKzB,CAAC"}
@@ -0,0 +1,6 @@
1
+ import { registerPlugin } from "@capacitor/core";
2
+ export * from "./definitions";
3
+ const loadWeb = () => import("./web").then((m) => new m.MobileSignalsWeb());
4
+ export const MobileSignals = registerPlugin("MobileSignals", {
5
+ web: loadWeb,
6
+ });
@@ -0,0 +1,29 @@
1
+ import { WebPlugin } from "@capacitor/core";
2
+ import type { MobileSignalsOpenSettingsOptions, MobileSignalsOpenSettingsResult, MobileSignalsPermissionStatus, MobileSignalsPlugin, MobileSignalsScreenTimeStatus, MobileSignalsSnapshotResult, MobileSignalsStartOptions, MobileSignalsStartResult, MobileSignalsStopResult } from "./definitions";
3
+ declare function buildScreenTimeStatus(reason: string): MobileSignalsScreenTimeStatus;
4
+ export declare class MobileSignalsWeb extends WebPlugin implements MobileSignalsPlugin {
5
+ private monitoring;
6
+ private cleanup;
7
+ checkPermissions(): Promise<MobileSignalsPermissionStatus>;
8
+ requestPermissions(): Promise<MobileSignalsPermissionStatus>;
9
+ openSettings(options?: MobileSignalsOpenSettingsOptions): Promise<MobileSignalsOpenSettingsResult>;
10
+ private emitSignal;
11
+ private attachListeners;
12
+ private clearListeners;
13
+ startMonitoring(options?: MobileSignalsStartOptions): Promise<MobileSignalsStartResult>;
14
+ stopMonitoring(): Promise<MobileSignalsStopResult>;
15
+ getSnapshot(): Promise<MobileSignalsSnapshotResult>;
16
+ scheduleBackgroundRefresh(): Promise<{
17
+ scheduled: boolean;
18
+ reason: string;
19
+ }>;
20
+ cancelBackgroundRefresh(): Promise<{
21
+ cancelled: boolean;
22
+ reason: string;
23
+ }>;
24
+ }
25
+ export declare const __internal: {
26
+ buildScreenTimeStatus: typeof buildScreenTimeStatus;
27
+ };
28
+ export {};
29
+ //# sourceMappingURL=web.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web.d.ts","sourceRoot":"","sources":["../../src/web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,KAAK,EAEV,gCAAgC,EAChC,+BAA+B,EAC/B,6BAA6B,EAE7B,mBAAmB,EACnB,6BAA6B,EAG7B,2BAA2B,EAC3B,yBAAyB,EACzB,wBAAwB,EACxB,uBAAuB,EACxB,MAAM,eAAe,CAAC;AAiCvB,iBAAS,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,6BAA6B,CA6B5E;AA6HD,qBAAa,gBAAiB,SAAQ,SAAU,YAAW,mBAAmB;IAC5E,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,OAAO,CAAiB;IAE1B,gBAAgB,IAAI,OAAO,CAAC,6BAA6B,CAAC;IAkB1D,kBAAkB,IAAI,OAAO,CAAC,6BAA6B,CAAC;IAI5D,YAAY,CAChB,OAAO,GAAE,gCAAqC,GAC7C,OAAO,CAAC,+BAA+B,CAAC;IAS3C,OAAO,CAAC,UAAU,CAKhB;IAEF,OAAO,CAAC,eAAe;IA4BvB,OAAO,CAAC,cAAc;IAOhB,eAAe,CACnB,OAAO,GAAE,yBAA8B,GACtC,OAAO,CAAC,wBAAwB,CAAC;IAqB9B,cAAc,IAAI,OAAO,CAAC,uBAAuB,CAAC;IAMlD,WAAW,IAAI,OAAO,CAAC,2BAA2B,CAAC;IASnD,yBAAyB,IAAI,OAAO,CAAC;QACzC,SAAS,EAAE,OAAO,CAAC;QACnB,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;IAOI,uBAAuB,IAAI,OAAO,CAAC;QACvC,SAAS,EAAE,OAAO,CAAC;QACnB,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;CAMH;AAED,eAAO,MAAM,UAAU;;CAEtB,CAAC"}
@@ -0,0 +1,272 @@
1
+ import { WebPlugin } from "@capacitor/core";
2
+ const SCREEN_TIME_REQUIREMENTS = {
3
+ entitlements: {
4
+ familyControls: "com.apple.developer.family-controls",
5
+ },
6
+ frameworks: ["FamilyControls", "DeviceActivity"],
7
+ deviceActivityReportExtension: false,
8
+ deviceActivityMonitorExtension: false,
9
+ android: {
10
+ usageStatsPermission: "android.permission.PACKAGE_USAGE_STATS",
11
+ usageAccessSettingsAction: "android.settings.USAGE_ACCESS_SETTINGS",
12
+ },
13
+ };
14
+ function getPlatform() {
15
+ if (typeof navigator === "undefined") {
16
+ return "web";
17
+ }
18
+ const ua = navigator.userAgent.toLowerCase();
19
+ if (ua.includes("android"))
20
+ return "android";
21
+ if (ua.includes("iphone") || ua.includes("ipad") || ua.includes("ipod")) {
22
+ return "ios";
23
+ }
24
+ return "web";
25
+ }
26
+ function buildScreenTimeStatus(reason) {
27
+ return {
28
+ supported: false,
29
+ requirements: SCREEN_TIME_REQUIREMENTS,
30
+ entitlements: {
31
+ familyControls: false,
32
+ },
33
+ provisioning: {
34
+ satisfied: false,
35
+ inspected: "not-inspectable",
36
+ reason,
37
+ },
38
+ authorization: {
39
+ status: "unavailable",
40
+ canRequest: false,
41
+ },
42
+ reportAvailable: false,
43
+ coarseSummaryAvailable: false,
44
+ thresholdEventsAvailable: false,
45
+ rawUsageExportAvailable: false,
46
+ android: {
47
+ usageAccessGranted: false,
48
+ packageUsageStatsPermissionDeclared: false,
49
+ canOpenUsageAccessSettings: false,
50
+ foregroundEventsAvailable: false,
51
+ totalTimeForegroundMs: null,
52
+ },
53
+ reason,
54
+ };
55
+ }
56
+ function buildSetupActions(reason) {
57
+ return [
58
+ {
59
+ id: "health_permissions",
60
+ label: "Health permissions",
61
+ status: "unavailable",
62
+ canRequest: false,
63
+ canOpenSettings: false,
64
+ settingsTarget: null,
65
+ reason,
66
+ },
67
+ {
68
+ id: "screen_time_authorization",
69
+ label: "Screen Time",
70
+ status: "unavailable",
71
+ canRequest: false,
72
+ canOpenSettings: false,
73
+ settingsTarget: null,
74
+ reason: "Web fallback cannot open native Screen Time settings.",
75
+ },
76
+ ];
77
+ }
78
+ async function getBatterySnapshot() {
79
+ const nav = typeof navigator !== "undefined"
80
+ ? navigator
81
+ : null;
82
+ if (!nav || typeof nav.getBattery !== "function") {
83
+ return { onBattery: null, batteryLevel: null, isCharging: null };
84
+ }
85
+ const battery = await nav.getBattery();
86
+ return {
87
+ onBattery: !battery.charging,
88
+ batteryLevel: typeof battery.level === "number"
89
+ ? Math.max(0, Math.min(1, battery.level))
90
+ : null,
91
+ isCharging: battery.charging,
92
+ };
93
+ }
94
+ async function buildSnapshot(reason) {
95
+ const isVisible = typeof document !== "undefined"
96
+ ? document.visibilityState === "visible"
97
+ : true;
98
+ const hasFocus = typeof document !== "undefined" && typeof document.hasFocus === "function"
99
+ ? document.hasFocus()
100
+ : true;
101
+ const battery = await getBatterySnapshot();
102
+ const state = isVisible && hasFocus ? "active" : "background";
103
+ const idleState = isVisible
104
+ ? "active"
105
+ : "idle";
106
+ return {
107
+ source: "mobile_device",
108
+ platform: getPlatform(),
109
+ state,
110
+ observedAt: Date.now(),
111
+ idleState,
112
+ idleTimeSeconds: null,
113
+ onBattery: battery.onBattery,
114
+ metadata: {
115
+ reason,
116
+ visibilityState: typeof document !== "undefined" ? document.visibilityState : "visible",
117
+ hasFocus,
118
+ ...battery,
119
+ },
120
+ };
121
+ }
122
+ function buildHealthSnapshot(reason) {
123
+ return {
124
+ source: "mobile_health",
125
+ platform: getPlatform(),
126
+ state: "idle",
127
+ observedAt: Date.now(),
128
+ idleState: null,
129
+ idleTimeSeconds: null,
130
+ onBattery: null,
131
+ healthSource: "healthkit",
132
+ screenTime: buildScreenTimeStatus("Web fallback has no Family Controls or DeviceActivity access."),
133
+ permissions: {
134
+ sleep: false,
135
+ biometrics: false,
136
+ },
137
+ sleep: {
138
+ available: false,
139
+ isSleeping: false,
140
+ asleepAt: null,
141
+ awakeAt: null,
142
+ durationMinutes: null,
143
+ stage: null,
144
+ },
145
+ biometrics: {
146
+ sampleAt: null,
147
+ heartRateBpm: null,
148
+ restingHeartRateBpm: null,
149
+ heartRateVariabilityMs: null,
150
+ respiratoryRate: null,
151
+ bloodOxygenPercent: null,
152
+ },
153
+ warnings: [`web fallback has no health access (${reason})`],
154
+ metadata: {
155
+ reason,
156
+ platform: getPlatform(),
157
+ supported: false,
158
+ },
159
+ };
160
+ }
161
+ export class MobileSignalsWeb extends WebPlugin {
162
+ constructor() {
163
+ super(...arguments);
164
+ this.monitoring = false;
165
+ this.cleanup = [];
166
+ this.emitSignal = async (reason) => {
167
+ if (!this.monitoring)
168
+ return;
169
+ const snapshot = await buildSnapshot(reason);
170
+ this.notifyListeners("signal", snapshot);
171
+ this.notifyListeners("signal", buildHealthSnapshot(reason));
172
+ };
173
+ }
174
+ async checkPermissions() {
175
+ return {
176
+ status: "not-applicable",
177
+ canRequest: false,
178
+ screenTime: buildScreenTimeStatus("Web fallback has no Family Controls or DeviceActivity access."),
179
+ setupActions: buildSetupActions("Web fallback has no HealthKit or Health Connect access."),
180
+ permissions: {
181
+ sleep: false,
182
+ biometrics: false,
183
+ },
184
+ reason: "Web fallback has no HealthKit or Health Connect access.",
185
+ };
186
+ }
187
+ async requestPermissions() {
188
+ return this.checkPermissions();
189
+ }
190
+ async openSettings(options = {}) {
191
+ return {
192
+ opened: false,
193
+ target: options.target ?? "app",
194
+ actualTarget: "app",
195
+ reason: "Web fallback cannot open native device settings.",
196
+ };
197
+ }
198
+ attachListeners() {
199
+ if (typeof document !== "undefined") {
200
+ const handleVisibilityChange = () => {
201
+ void this.emitSignal("visibilitychange");
202
+ };
203
+ document.addEventListener("visibilitychange", handleVisibilityChange);
204
+ this.cleanup.push(() => document.removeEventListener("visibilitychange", handleVisibilityChange));
205
+ }
206
+ if (typeof window !== "undefined") {
207
+ const handleFocus = () => {
208
+ void this.emitSignal("focus");
209
+ };
210
+ const handleBlur = () => {
211
+ void this.emitSignal("blur");
212
+ };
213
+ window.addEventListener("focus", handleFocus);
214
+ window.addEventListener("blur", handleBlur);
215
+ this.cleanup.push(() => window.removeEventListener("focus", handleFocus));
216
+ this.cleanup.push(() => window.removeEventListener("blur", handleBlur));
217
+ }
218
+ }
219
+ clearListeners() {
220
+ while (this.cleanup.length > 0) {
221
+ const cleanup = this.cleanup.pop();
222
+ cleanup?.();
223
+ }
224
+ }
225
+ async startMonitoring(options = {}) {
226
+ if (!this.monitoring) {
227
+ this.monitoring = true;
228
+ this.attachListeners();
229
+ }
230
+ const snapshot = await buildSnapshot("start");
231
+ const healthSnapshot = buildHealthSnapshot("start");
232
+ if (options.emitInitial ?? true) {
233
+ this.notifyListeners("signal", snapshot);
234
+ this.notifyListeners("signal", healthSnapshot);
235
+ }
236
+ return {
237
+ enabled: this.monitoring,
238
+ supported: true,
239
+ platform: snapshot.platform,
240
+ snapshot,
241
+ healthSnapshot,
242
+ };
243
+ }
244
+ async stopMonitoring() {
245
+ this.monitoring = false;
246
+ this.clearListeners();
247
+ return { stopped: true };
248
+ }
249
+ async getSnapshot() {
250
+ const snapshot = await buildSnapshot("snapshot");
251
+ return {
252
+ supported: true,
253
+ snapshot,
254
+ healthSnapshot: buildHealthSnapshot("snapshot"),
255
+ };
256
+ }
257
+ async scheduleBackgroundRefresh() {
258
+ return {
259
+ scheduled: false,
260
+ reason: "Web fallback cannot schedule native background refresh tasks.",
261
+ };
262
+ }
263
+ async cancelBackgroundRefresh() {
264
+ return {
265
+ cancelled: false,
266
+ reason: "Web fallback has no native background refresh task to cancel.",
267
+ };
268
+ }
269
+ }
270
+ export const __internal = {
271
+ buildScreenTimeStatus,
272
+ };
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=web.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web.test.d.ts","sourceRoot":"","sources":["../../src/web.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,75 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { MobileSignalsWeb } from "./web.js";
3
+ describe("MobileSignalsWeb Screen Time status", () => {
4
+ it("reports Screen Time as unavailable without fabricating usage data", async () => {
5
+ const plugin = new MobileSignalsWeb();
6
+ const permissions = await plugin.checkPermissions();
7
+ expect(permissions.screenTime).toEqual({
8
+ supported: false,
9
+ requirements: {
10
+ entitlements: {
11
+ familyControls: "com.apple.developer.family-controls",
12
+ },
13
+ frameworks: ["FamilyControls", "DeviceActivity"],
14
+ deviceActivityReportExtension: false,
15
+ deviceActivityMonitorExtension: false,
16
+ android: {
17
+ usageStatsPermission: "android.permission.PACKAGE_USAGE_STATS",
18
+ usageAccessSettingsAction: "android.settings.USAGE_ACCESS_SETTINGS",
19
+ },
20
+ },
21
+ entitlements: {
22
+ familyControls: false,
23
+ },
24
+ provisioning: {
25
+ satisfied: false,
26
+ inspected: "not-inspectable",
27
+ reason: "Web fallback has no Family Controls or DeviceActivity access.",
28
+ },
29
+ authorization: {
30
+ status: "unavailable",
31
+ canRequest: false,
32
+ },
33
+ reportAvailable: false,
34
+ coarseSummaryAvailable: false,
35
+ thresholdEventsAvailable: false,
36
+ rawUsageExportAvailable: false,
37
+ android: {
38
+ usageAccessGranted: false,
39
+ packageUsageStatsPermissionDeclared: false,
40
+ canOpenUsageAccessSettings: false,
41
+ foregroundEventsAvailable: false,
42
+ totalTimeForegroundMs: null,
43
+ },
44
+ reason: "Web fallback has no Family Controls or DeviceActivity access.",
45
+ });
46
+ expect(permissions.setupActions).toEqual([
47
+ {
48
+ id: "health_permissions",
49
+ label: "Health permissions",
50
+ status: "unavailable",
51
+ canRequest: false,
52
+ canOpenSettings: false,
53
+ settingsTarget: null,
54
+ reason: "Web fallback has no HealthKit or Health Connect access.",
55
+ },
56
+ {
57
+ id: "screen_time_authorization",
58
+ label: "Screen Time",
59
+ status: "unavailable",
60
+ canRequest: false,
61
+ canOpenSettings: false,
62
+ settingsTarget: null,
63
+ reason: "Web fallback cannot open native Screen Time settings.",
64
+ },
65
+ ]);
66
+ const snapshot = await plugin.getSnapshot();
67
+ expect(snapshot.healthSnapshot?.screenTime).toEqual(permissions.screenTime);
68
+ await expect(plugin.openSettings({ target: "usageAccess" })).resolves.toEqual({
69
+ opened: false,
70
+ target: "usageAccess",
71
+ actualTarget: "app",
72
+ reason: "Web fallback cannot open native device settings.",
73
+ });
74
+ });
75
+ });