@symbiote-native/battery 0.0.1 → 0.2.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.
Files changed (88) hide show
  1. package/README.md +172 -27
  2. package/build/angular/index.d.ts +4 -0
  3. package/build/angular/index.js +8 -0
  4. package/build/angular/services/battery-level.service/index.d.ts +5 -0
  5. package/build/angular/services/battery-level.service/index.js +74 -0
  6. package/build/angular/services/battery-state.service/index.d.ts +6 -0
  7. package/build/angular/services/battery-state.service/index.js +69 -0
  8. package/build/angular/services/low-power-mode.service/index.d.ts +5 -0
  9. package/build/angular/services/low-power-mode.service/index.js +69 -0
  10. package/build/core/battery.d.ts +44 -0
  11. package/build/core/battery.js +79 -0
  12. package/build/core/index.d.ts +3 -0
  13. package/build/core/index.js +2 -0
  14. package/build/core/native-module.d.ts +13 -0
  15. package/build/core/native-module.js +3 -0
  16. package/build/core/types.d.ts +40 -0
  17. package/build/core/types.js +23 -0
  18. package/build/react/hooks/use-battery-level/index.d.ts +1 -0
  19. package/build/react/hooks/use-battery-level/index.js +16 -0
  20. package/build/react/hooks/use-battery-state/index.d.ts +2 -0
  21. package/build/react/hooks/use-battery-state/index.js +14 -0
  22. package/build/react/hooks/use-low-power-mode/index.d.ts +1 -0
  23. package/build/react/hooks/use-low-power-mode/index.js +15 -0
  24. package/build/react/index.d.ts +4 -0
  25. package/build/react/index.js +9 -0
  26. package/build/svelte/index.d.ts +4 -0
  27. package/build/svelte/index.js +9 -0
  28. package/build/svelte/runes/use-battery-level.svelte.d.ts +3 -0
  29. package/build/svelte/runes/use-battery-level.svelte.js +37 -0
  30. package/build/svelte/runes/use-battery-state.svelte.d.ts +4 -0
  31. package/build/svelte/runes/use-battery-state.svelte.js +22 -0
  32. package/build/svelte/runes/use-low-power-mode.svelte.d.ts +3 -0
  33. package/build/svelte/runes/use-low-power-mode.svelte.js +22 -0
  34. package/build/vue/composables/use-battery-level/index.d.ts +2 -0
  35. package/build/vue/composables/use-battery-level/index.js +23 -0
  36. package/build/vue/composables/use-battery-state/index.d.ts +3 -0
  37. package/build/vue/composables/use-battery-state/index.js +21 -0
  38. package/build/vue/composables/use-low-power-mode/index.d.ts +2 -0
  39. package/build/vue/composables/use-low-power-mode/index.js +22 -0
  40. package/build/vue/index.d.ts +4 -0
  41. package/build/vue/index.js +9 -0
  42. package/build-ngc/angular/index.d.ts +4 -0
  43. package/build-ngc/angular/index.js +9 -0
  44. package/build-ngc/angular/index.js.map +1 -0
  45. package/build-ngc/angular/services/battery-level.service/index.d.ts +8 -0
  46. package/build-ngc/angular/services/battery-level.service/index.js +33 -0
  47. package/build-ngc/angular/services/battery-level.service/index.js.map +1 -0
  48. package/build-ngc/angular/services/battery-state.service/index.d.ts +9 -0
  49. package/build-ngc/angular/services/battery-state.service/index.js +28 -0
  50. package/build-ngc/angular/services/battery-state.service/index.js.map +1 -0
  51. package/build-ngc/angular/services/low-power-mode.service/index.d.ts +8 -0
  52. package/build-ngc/angular/services/low-power-mode.service/index.js +28 -0
  53. package/build-ngc/angular/services/low-power-mode.service/index.js.map +1 -0
  54. package/build-ngc/core/battery.d.ts +44 -0
  55. package/build-ngc/core/battery.js +80 -0
  56. package/build-ngc/core/battery.js.map +1 -0
  57. package/build-ngc/core/index.d.ts +3 -0
  58. package/build-ngc/core/index.js +3 -0
  59. package/build-ngc/core/index.js.map +1 -0
  60. package/build-ngc/core/native-module.d.ts +13 -0
  61. package/build-ngc/core/native-module.js +4 -0
  62. package/build-ngc/core/native-module.js.map +1 -0
  63. package/build-ngc/core/types.d.ts +40 -0
  64. package/build-ngc/core/types.js +24 -0
  65. package/build-ngc/core/types.js.map +1 -0
  66. package/native-link.json +12 -0
  67. package/package.json +106 -1
  68. package/src/angular/index.ts +8 -0
  69. package/src/angular/services/battery-level.service/index.ts +32 -0
  70. package/src/angular/services/battery-state.service/index.ts +27 -0
  71. package/src/angular/services/low-power-mode.service/index.ts +27 -0
  72. package/src/core/battery.ts +105 -0
  73. package/src/core/index.ts +19 -0
  74. package/src/core/native-module.ts +31 -0
  75. package/src/core/types.ts +47 -0
  76. package/src/react/hooks/use-battery-level/index.ts +19 -0
  77. package/src/react/hooks/use-battery-state/index.ts +17 -0
  78. package/src/react/hooks/use-low-power-mode/index.ts +18 -0
  79. package/src/react/index.ts +10 -0
  80. package/src/svelte/index.ts +10 -0
  81. package/src/svelte/runes/use-battery-level.svelte.ts +41 -0
  82. package/src/svelte/runes/use-battery-state.svelte.ts +30 -0
  83. package/src/svelte/runes/use-low-power-mode.svelte.ts +29 -0
  84. package/src/svelte/svelte-ambient.d.ts +6 -0
  85. package/src/vue/composables/use-battery-level/index.ts +32 -0
  86. package/src/vue/composables/use-battery-state/index.ts +31 -0
  87. package/src/vue/composables/use-low-power-mode/index.ts +31 -0
  88. package/src/vue/index.ts +10 -0
@@ -0,0 +1,80 @@
1
+ import { expoBattery } from './native-module';
2
+ import { BatteryState, } from './types';
3
+ /**
4
+ * Resolves with whether the battery API is available on the current device. `true` on Android
5
+ * and physical iOS devices, `false` on iOS simulators.
6
+ */
7
+ export async function isAvailableAsync() {
8
+ return Promise.resolve((expoBattery && expoBattery.isSupported) || false);
9
+ }
10
+ /**
11
+ * Gets the battery level of the device as a number between `0` and `1`, inclusive. If the
12
+ * device does not support retrieving the battery level, this method returns `-1`.
13
+ */
14
+ export async function getBatteryLevelAsync() {
15
+ if (!expoBattery.getBatteryLevelAsync) {
16
+ return -1;
17
+ }
18
+ return expoBattery.getBatteryLevelAsync();
19
+ }
20
+ /** Tells the battery's current state. */
21
+ export async function getBatteryStateAsync() {
22
+ if (!expoBattery.getBatteryStateAsync) {
23
+ return BatteryState.UNKNOWN;
24
+ }
25
+ return expoBattery.getBatteryStateAsync();
26
+ }
27
+ /**
28
+ * Gets the current status of Power Saver mode on Android and Low Power mode on iOS. If a
29
+ * platform doesn't support low-power-mode reporting, this always resolves `false`, even if the
30
+ * device is actually in low-power mode.
31
+ */
32
+ export async function isLowPowerModeEnabledAsync() {
33
+ if (!expoBattery.isLowPowerModeEnabledAsync) {
34
+ return false;
35
+ }
36
+ return expoBattery.isLowPowerModeEnabledAsync();
37
+ }
38
+ /**
39
+ * Checks whether battery optimization is enabled for this app. If enabled, background tasks
40
+ * might be affected when the app goes into doze mode state.
41
+ * @platform android
42
+ */
43
+ export async function isBatteryOptimizationEnabledAsync() {
44
+ if (!expoBattery.isBatteryOptimizationEnabledAsync) {
45
+ return false;
46
+ }
47
+ return expoBattery.isBatteryOptimizationEnabledAsync();
48
+ }
49
+ /**
50
+ * Gets the power state of the device: battery level, whether it is plugged in, and whether the
51
+ * system is currently in Power Saver Mode (Android) / Low Power Mode (iOS).
52
+ */
53
+ export async function getPowerStateAsync() {
54
+ const [batteryLevel, batteryState, lowPowerMode] = await Promise.all([
55
+ getBatteryLevelAsync(),
56
+ getBatteryStateAsync(),
57
+ isLowPowerModeEnabledAsync(),
58
+ ]);
59
+ return { batteryLevel, batteryState, lowPowerMode };
60
+ }
61
+ /**
62
+ * Subscribe to battery level change updates. On Android this only fires on significant changes
63
+ * (crossing the low/okay battery thresholds); on iOS it fires per one-percent drop, at most once
64
+ * a minute.
65
+ */
66
+ export function addBatteryLevelListener(listener) {
67
+ return expoBattery.addListener('Expo.batteryLevelDidChange', listener);
68
+ }
69
+ /** Subscribe to battery state (charging/full/unplugged/unknown) change updates. */
70
+ export function addBatteryStateListener(listener) {
71
+ return expoBattery.addListener('Expo.batteryStateDidChange', listener);
72
+ }
73
+ /**
74
+ * Subscribe to Power Saver Mode (Android) / Low Power Mode (iOS) updates. Fires whenever the
75
+ * power mode is toggled.
76
+ */
77
+ export function addLowPowerModeListener(listener) {
78
+ return expoBattery.addListener('Expo.powerModeDidChange', listener);
79
+ }
80
+ //# sourceMappingURL=battery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"battery.js","sourceRoot":"","sources":["../../src/core/battery.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EACL,YAAY,GAKb,MAAM,SAAS,CAAC;AAEjB;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB;IACpC,OAAO,OAAO,CAAC,OAAO,CAAC,CAAC,WAAW,IAAI,WAAW,CAAC,WAAW,CAAC,IAAI,KAAK,CAAC,CAAC;AAC5E,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB;IACxC,IAAI,CAAC,WAAW,CAAC,oBAAoB,EAAE,CAAC;QACtC,OAAO,CAAC,CAAC,CAAC;IACZ,CAAC;IACD,OAAO,WAAW,CAAC,oBAAoB,EAAE,CAAC;AAC5C,CAAC;AAED,yCAAyC;AACzC,MAAM,CAAC,KAAK,UAAU,oBAAoB;IACxC,IAAI,CAAC,WAAW,CAAC,oBAAoB,EAAE,CAAC;QACtC,OAAO,YAAY,CAAC,OAAO,CAAC;IAC9B,CAAC;IACD,OAAO,WAAW,CAAC,oBAAoB,EAAE,CAAC;AAC5C,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,0BAA0B;IAC9C,IAAI,CAAC,WAAW,CAAC,0BAA0B,EAAE,CAAC;QAC5C,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,WAAW,CAAC,0BAA0B,EAAE,CAAC;AAClD,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,iCAAiC;IACrD,IAAI,CAAC,WAAW,CAAC,iCAAiC,EAAE,CAAC;QACnD,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,WAAW,CAAC,iCAAiC,EAAE,CAAC;AACzD,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB;IACtC,MAAM,CAAC,YAAY,EAAE,YAAY,EAAE,YAAY,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QACnE,oBAAoB,EAAE;QACtB,oBAAoB,EAAE;QACtB,0BAA0B,EAAE;KAC7B,CAAC,CAAC;IACH,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,CAAC;AACtD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,uBAAuB,CACrC,QAA4C;IAE5C,OAAO,WAAW,CAAC,WAAW,CAAC,4BAA4B,EAAE,QAAQ,CAAC,CAAC;AACzE,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,uBAAuB,CACrC,QAA4C;IAE5C,OAAO,WAAW,CAAC,WAAW,CAAC,4BAA4B,EAAE,QAAQ,CAAC,CAAC;AACzE,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CACrC,QAAyC;IAEzC,OAAO,WAAW,CAAC,WAAW,CAAC,yBAAyB,EAAE,QAAQ,CAAC,CAAC;AACtE,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { isAvailableAsync, getBatteryLevelAsync, getBatteryStateAsync, isLowPowerModeEnabledAsync, isBatteryOptimizationEnabledAsync, getPowerStateAsync, addBatteryLevelListener, addBatteryStateListener, addLowPowerModeListener, } from './battery';
2
+ export { BatteryState, type PowerState, type BatteryLevelEvent, type BatteryStateEvent, type PowerModeEvent, } from './types';
3
+ export type { EventSubscription } from 'expo-modules-core';
@@ -0,0 +1,3 @@
1
+ export { isAvailableAsync, getBatteryLevelAsync, getBatteryStateAsync, isLowPowerModeEnabledAsync, isBatteryOptimizationEnabledAsync, getPowerStateAsync, addBatteryLevelListener, addBatteryStateListener, addLowPowerModeListener, } from './battery';
2
+ export { BatteryState, } from './types';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,oBAAoB,EACpB,0BAA0B,EAC1B,iCAAiC,EACjC,kBAAkB,EAClB,uBAAuB,EACvB,uBAAuB,EACvB,uBAAuB,GACxB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,YAAY,GAKb,MAAM,SAAS,CAAC"}
@@ -0,0 +1,13 @@
1
+ import type { EventSubscription } from 'expo-modules-core';
2
+ import type { BatteryLevelEvent, BatteryState, BatteryStateEvent, PowerModeEvent } from './types';
3
+ export type INativeBatteryModule = {
4
+ isSupported?: boolean;
5
+ getBatteryLevelAsync?(): Promise<number>;
6
+ getBatteryStateAsync?(): Promise<BatteryState>;
7
+ isLowPowerModeEnabledAsync?(): Promise<boolean>;
8
+ isBatteryOptimizationEnabledAsync?(): Promise<boolean>;
9
+ addListener(eventName: 'Expo.batteryLevelDidChange', listener: (event: BatteryLevelEvent) => void): EventSubscription;
10
+ addListener(eventName: 'Expo.batteryStateDidChange', listener: (event: BatteryStateEvent) => void): EventSubscription;
11
+ addListener(eventName: 'Expo.powerModeDidChange', listener: (event: PowerModeEvent) => void): EventSubscription;
12
+ };
13
+ export declare const expoBattery: INativeBatteryModule;
@@ -0,0 +1,4 @@
1
+ import { requireNativeModule } from 'expo-modules-core';
2
+ const EXPO_BATTERY_MODULE_NAME = 'ExpoBattery';
3
+ export const expoBattery = requireNativeModule(EXPO_BATTERY_MODULE_NAME);
4
+ //# sourceMappingURL=native-module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"native-module.js","sourceRoot":"","sources":["../../src/core/native-module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAIxD,MAAM,wBAAwB,GAAG,aAAa,CAAC;AA0B/C,MAAM,CAAC,MAAM,WAAW,GAAG,mBAAmB,CAAuB,wBAAwB,CAAC,CAAC"}
@@ -0,0 +1,40 @@
1
+ export type PowerState = {
2
+ /** A number between `0` and `1`, inclusive, or `-1` if the battery level is unknown. */
3
+ batteryLevel: number;
4
+ /** An enum value representing the battery state. */
5
+ batteryState: BatteryState;
6
+ /** `true` if lowPowerMode is on, `false` if lowPowerMode is off. */
7
+ lowPowerMode: boolean;
8
+ };
9
+ export declare enum BatteryState {
10
+ /** If the battery state is unknown or inaccessible. */
11
+ UNKNOWN = 0,
12
+ /**
13
+ * If the battery is discharging (typically not connected to power). On Android, this
14
+ * corresponds to `BATTERY_STATUS_DISCHARGING`.
15
+ */
16
+ UNPLUGGED = 1,
17
+ /** If battery is charging. */
18
+ CHARGING = 2,
19
+ /** If the battery level is full. */
20
+ FULL = 3,
21
+ /**
22
+ * The battery is not charging while power is connected (AC/USB/wireless), for example when
23
+ * battery protection limits charge to 80%, or optimized charging pauses. This differs from
24
+ * `UNPLUGGED` (discharging on battery).
25
+ * @platform android
26
+ */
27
+ NOT_CHARGING = 4
28
+ }
29
+ export type BatteryLevelEvent = {
30
+ /** A number between `0` and `1`, inclusive, or `-1` if the battery level is unknown. */
31
+ batteryLevel: number;
32
+ };
33
+ export type BatteryStateEvent = {
34
+ /** An enum value representing the battery state. */
35
+ batteryState: BatteryState;
36
+ };
37
+ export type PowerModeEvent = {
38
+ /** `true` if lowPowerMode is on, `false` if lowPowerMode is off. */
39
+ lowPowerMode: boolean;
40
+ };
@@ -0,0 +1,24 @@
1
+ // Hand-ported from .vendors/expo/packages/expo-battery/src/Battery.types.ts (sdk-57) — plain
2
+ // data shapes, no `expo` meta-package import to swap out (unlike local-auth's types.ts).
3
+ export var BatteryState;
4
+ (function (BatteryState) {
5
+ /** If the battery state is unknown or inaccessible. */
6
+ BatteryState[BatteryState["UNKNOWN"] = 0] = "UNKNOWN";
7
+ /**
8
+ * If the battery is discharging (typically not connected to power). On Android, this
9
+ * corresponds to `BATTERY_STATUS_DISCHARGING`.
10
+ */
11
+ BatteryState[BatteryState["UNPLUGGED"] = 1] = "UNPLUGGED";
12
+ /** If battery is charging. */
13
+ BatteryState[BatteryState["CHARGING"] = 2] = "CHARGING";
14
+ /** If the battery level is full. */
15
+ BatteryState[BatteryState["FULL"] = 3] = "FULL";
16
+ /**
17
+ * The battery is not charging while power is connected (AC/USB/wireless), for example when
18
+ * battery protection limits charge to 80%, or optimized charging pauses. This differs from
19
+ * `UNPLUGGED` (discharging on battery).
20
+ * @platform android
21
+ */
22
+ BatteryState[BatteryState["NOT_CHARGING"] = 4] = "NOT_CHARGING";
23
+ })(BatteryState || (BatteryState = {}));
24
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAAA,6FAA6F;AAC7F,yFAAyF;AAWzF,MAAM,CAAN,IAAY,YAmBX;AAnBD,WAAY,YAAY;IACtB,uDAAuD;IACvD,qDAAW,CAAA;IACX;;;OAGG;IACH,yDAAS,CAAA;IACT,8BAA8B;IAC9B,uDAAQ,CAAA;IACR,oCAAoC;IACpC,+CAAI,CAAA;IACJ;;;;;OAKG;IACH,+DAAY,CAAA;AACd,CAAC,EAnBW,YAAY,KAAZ,YAAY,QAmBvB"}
@@ -0,0 +1,12 @@
1
+ {
2
+ "android": {
3
+ "gradleProjectName": "expo-battery",
4
+ "modules": [
5
+ {
6
+ "importPath": "expo.modules.battery.BatteryModule",
7
+ "className": "BatteryModule",
8
+ "nativeName": "ExpoBattery"
9
+ }
10
+ ]
11
+ }
12
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@symbiote-native/battery",
3
- "version": "0.0.1",
3
+ "version": "0.2.0",
4
4
  "description": "expo-battery wrapped for SymbioteNative — one framework-agnostic core, built once and reachable from the React, Vue, and Angular adapters. Battery level and state (charging/full/unplugged/unknown), low-power-mode detection, plus event listeners for level and state changes.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -13,7 +13,112 @@
13
13
  "url": "https://github.com/OneEyed1366/symbiote-native/issues"
14
14
  },
15
15
  "author": "Andrey Prokopenko <psevdoproger@gmail.com>",
16
+ "type": "module",
17
+ "main": "./build/core/index.js",
18
+ "module": "./build/core/index.js",
19
+ "types": "./build/core/index.d.ts",
20
+ "exports": {
21
+ ".": {
22
+ "types": "./build/core/index.d.ts",
23
+ "default": "./build/core/index.js"
24
+ },
25
+ "./vue": {
26
+ "types": "./build/vue/index.d.ts",
27
+ "default": "./build/vue/index.js"
28
+ },
29
+ "./react": {
30
+ "types": "./build/react/index.d.ts",
31
+ "default": "./build/react/index.js"
32
+ },
33
+ "./svelte": {
34
+ "types": "./build/svelte/index.d.ts",
35
+ "default": "./build/svelte/index.js"
36
+ },
37
+ "./angular": {
38
+ "types": "./build-ngc/angular/index.d.ts",
39
+ "react-native": "./build-ngc/angular/index.js",
40
+ "default": "./src/angular/index.ts"
41
+ }
42
+ },
43
+ "files": [
44
+ "src",
45
+ "build",
46
+ "build-ngc",
47
+ "native-link.json",
48
+ "!src/**/*.test.*",
49
+ "!src/**/*.spec.*",
50
+ "!src/**/*.detox.*"
51
+ ],
16
52
  "publishConfig": {
17
53
  "access": "public"
54
+ },
55
+ "dependencies": {
56
+ "expo-battery": "57.0.1",
57
+ "expo-modules-core": "57.0.5"
58
+ },
59
+ "peerDependencies": {
60
+ "@symbiote-native/angular": ">=0.6.2",
61
+ "@symbiote-native/engine": ">=0.1.7",
62
+ "@symbiote-native/react": ">=0.2.8",
63
+ "@symbiote-native/svelte": ">=0.1.0",
64
+ "@symbiote-native/vue": ">=0.3.8",
65
+ "@angular/core": ">=20",
66
+ "@vue/runtime-core": "^3.5.13",
67
+ "react": ">=19.0.0",
68
+ "react-native": ">=0.86",
69
+ "svelte": ">=5.56.0",
70
+ "vue": ">=3.5.0"
71
+ },
72
+ "peerDependenciesMeta": {
73
+ "@symbiote-native/angular": {
74
+ "optional": true
75
+ },
76
+ "@symbiote-native/react": {
77
+ "optional": true
78
+ },
79
+ "@symbiote-native/svelte": {
80
+ "optional": true
81
+ },
82
+ "@symbiote-native/vue": {
83
+ "optional": true
84
+ },
85
+ "@angular/core": {
86
+ "optional": true
87
+ },
88
+ "@vue/runtime-core": {
89
+ "optional": true
90
+ },
91
+ "react": {
92
+ "optional": true
93
+ },
94
+ "svelte": {
95
+ "optional": true
96
+ },
97
+ "vue": {
98
+ "optional": true
99
+ }
100
+ },
101
+ "devDependencies": {
102
+ "@angular/compiler": "~22.0.8",
103
+ "@angular/compiler-cli": "~22.0.8",
104
+ "@angular/core": "~22.0.8",
105
+ "@types/node": "^26.0.0",
106
+ "@types/react": "^19.2.0",
107
+ "@vue/runtime-core": "^3.5.13",
108
+ "react": "19.2.3",
109
+ "svelte": "^5.56.0",
110
+ "typescript": "~6.0.0",
111
+ "@symbiote-native/angular": "0.7.0",
112
+ "@symbiote-native/engine": "0.2.0",
113
+ "@symbiote-native/test-utils": "0.1.6",
114
+ "@symbiote-native/svelte": "0.2.0",
115
+ "@symbiote-native/react": "0.3.0",
116
+ "@symbiote-native/vue": "0.4.0"
117
+ },
118
+ "scripts": {
119
+ "typecheck": "tsc --build",
120
+ "clean": "rm -rf build-ngc",
121
+ "ng:build": "pnpm run clean && ngc -p tsconfig.angular.json",
122
+ "format": "prettier --write \"src/**/*.{ts,tsx}\""
18
123
  }
19
124
  }
@@ -0,0 +1,8 @@
1
+ // BatteryLevelService/BatteryStateService/LowPowerModeService are the Angular-only lifecycle
2
+ // half; the free functions and event subscription plumbing all live in core, shared with
3
+ // React/Vue. Three separate services (not one combined service), matching upstream's own
4
+ // useBatteryLevel/useBatteryState/useLowPowerMode being three separate hooks.
5
+ export { BatteryLevelService } from './services/battery-level.service';
6
+ export { BatteryStateService } from './services/battery-state.service';
7
+ export { LowPowerModeService } from './services/low-power-mode.service';
8
+ export * from '../core';
@@ -0,0 +1,32 @@
1
+ import { effect, inject, Injectable, Injector, signal, type Signal } from '@angular/core';
2
+ import { addBatteryLevelListener, getBatteryLevelAsync } from '../../../core';
3
+
4
+ // Angular twin of React's `useBatteryLevel` hook and Vue's `useBatteryLevel` composable.
5
+ // Angular has no per-instance hook — state and lifecycle live in DI instead, so `connect()`
6
+ // stands in for the hook's role: call it ONCE (typically from a component's field initializer,
7
+ // inside an injection context).
8
+ //
9
+ // readonly batteryLevel = inject(BatteryLevelService).connect();
10
+ // // template: {{ batteryLevel() }}
11
+ //
12
+ // Mirrors AccelerometerService.connect() — a single effect() that seeds the initial value with
13
+ // getBatteryLevelAsync(), subscribes once, and cleans up once.
14
+ @Injectable({ providedIn: 'root' })
15
+ export class BatteryLevelService {
16
+ private readonly injector = inject(Injector);
17
+
18
+ connect(): Signal<number> {
19
+ const batteryLevel = signal(-1);
20
+
21
+ effect(
22
+ onCleanup => {
23
+ getBatteryLevelAsync().then(level => batteryLevel.set(level));
24
+ const subscription = addBatteryLevelListener(event => batteryLevel.set(event.batteryLevel));
25
+ onCleanup(() => subscription.remove());
26
+ },
27
+ { injector: this.injector },
28
+ );
29
+
30
+ return batteryLevel.asReadonly();
31
+ }
32
+ }
@@ -0,0 +1,27 @@
1
+ import { effect, inject, Injectable, Injector, signal, type Signal } from '@angular/core';
2
+ import { addBatteryStateListener, BatteryState, getBatteryStateAsync } from '../../../core';
3
+
4
+ // Angular twin of React's `useBatteryState` hook and Vue's `useBatteryState` composable. See
5
+ // BatteryLevelService for the `connect()` pattern rationale.
6
+ //
7
+ // readonly batteryState = inject(BatteryStateService).connect();
8
+ // // template: {{ batteryState() }}
9
+ @Injectable({ providedIn: 'root' })
10
+ export class BatteryStateService {
11
+ private readonly injector = inject(Injector);
12
+
13
+ connect(): Signal<BatteryState> {
14
+ const batteryState = signal<BatteryState>(BatteryState.UNKNOWN);
15
+
16
+ effect(
17
+ onCleanup => {
18
+ getBatteryStateAsync().then(state => batteryState.set(state));
19
+ const subscription = addBatteryStateListener(event => batteryState.set(event.batteryState));
20
+ onCleanup(() => subscription.remove());
21
+ },
22
+ { injector: this.injector },
23
+ );
24
+
25
+ return batteryState.asReadonly();
26
+ }
27
+ }
@@ -0,0 +1,27 @@
1
+ import { effect, inject, Injectable, Injector, signal, type Signal } from '@angular/core';
2
+ import { addLowPowerModeListener, isLowPowerModeEnabledAsync } from '../../../core';
3
+
4
+ // Angular twin of React's `useLowPowerMode` hook and Vue's `useLowPowerMode` composable. See
5
+ // BatteryLevelService for the `connect()` pattern rationale.
6
+ //
7
+ // readonly lowPowerMode = inject(LowPowerModeService).connect();
8
+ // // template: {{ lowPowerMode() }}
9
+ @Injectable({ providedIn: 'root' })
10
+ export class LowPowerModeService {
11
+ private readonly injector = inject(Injector);
12
+
13
+ connect(): Signal<boolean> {
14
+ const lowPowerMode = signal(false);
15
+
16
+ effect(
17
+ onCleanup => {
18
+ isLowPowerModeEnabledAsync().then(enabled => lowPowerMode.set(enabled));
19
+ const subscription = addLowPowerModeListener(event => lowPowerMode.set(event.lowPowerMode));
20
+ onCleanup(() => subscription.remove());
21
+ },
22
+ { injector: this.injector },
23
+ );
24
+
25
+ return lowPowerMode.asReadonly();
26
+ }
27
+ }
@@ -0,0 +1,105 @@
1
+ // Hand-ported from .vendors/expo/packages/expo-battery/src/Battery.ts (sdk-57). Unlike
2
+ // local-auth/pedometer, upstream Battery never throws UnavailabilityError — every function
3
+ // falls back to a documented sentinel (-1 / BatteryState.UNKNOWN / false) when the native
4
+ // method is absent, so that fallback shape is preserved here verbatim rather than guessed.
5
+ import type { EventSubscription } from 'expo-modules-core';
6
+ import { expoBattery } from './native-module';
7
+ import {
8
+ BatteryState,
9
+ type BatteryLevelEvent,
10
+ type BatteryStateEvent,
11
+ type PowerModeEvent,
12
+ type PowerState,
13
+ } from './types';
14
+
15
+ /**
16
+ * Resolves with whether the battery API is available on the current device. `true` on Android
17
+ * and physical iOS devices, `false` on iOS simulators.
18
+ */
19
+ export async function isAvailableAsync(): Promise<boolean> {
20
+ return Promise.resolve((expoBattery && expoBattery.isSupported) || false);
21
+ }
22
+
23
+ /**
24
+ * Gets the battery level of the device as a number between `0` and `1`, inclusive. If the
25
+ * device does not support retrieving the battery level, this method returns `-1`.
26
+ */
27
+ export async function getBatteryLevelAsync(): Promise<number> {
28
+ if (!expoBattery.getBatteryLevelAsync) {
29
+ return -1;
30
+ }
31
+ return expoBattery.getBatteryLevelAsync();
32
+ }
33
+
34
+ /** Tells the battery's current state. */
35
+ export async function getBatteryStateAsync(): Promise<BatteryState> {
36
+ if (!expoBattery.getBatteryStateAsync) {
37
+ return BatteryState.UNKNOWN;
38
+ }
39
+ return expoBattery.getBatteryStateAsync();
40
+ }
41
+
42
+ /**
43
+ * Gets the current status of Power Saver mode on Android and Low Power mode on iOS. If a
44
+ * platform doesn't support low-power-mode reporting, this always resolves `false`, even if the
45
+ * device is actually in low-power mode.
46
+ */
47
+ export async function isLowPowerModeEnabledAsync(): Promise<boolean> {
48
+ if (!expoBattery.isLowPowerModeEnabledAsync) {
49
+ return false;
50
+ }
51
+ return expoBattery.isLowPowerModeEnabledAsync();
52
+ }
53
+
54
+ /**
55
+ * Checks whether battery optimization is enabled for this app. If enabled, background tasks
56
+ * might be affected when the app goes into doze mode state.
57
+ * @platform android
58
+ */
59
+ export async function isBatteryOptimizationEnabledAsync(): Promise<boolean> {
60
+ if (!expoBattery.isBatteryOptimizationEnabledAsync) {
61
+ return false;
62
+ }
63
+ return expoBattery.isBatteryOptimizationEnabledAsync();
64
+ }
65
+
66
+ /**
67
+ * Gets the power state of the device: battery level, whether it is plugged in, and whether the
68
+ * system is currently in Power Saver Mode (Android) / Low Power Mode (iOS).
69
+ */
70
+ export async function getPowerStateAsync(): Promise<PowerState> {
71
+ const [batteryLevel, batteryState, lowPowerMode] = await Promise.all([
72
+ getBatteryLevelAsync(),
73
+ getBatteryStateAsync(),
74
+ isLowPowerModeEnabledAsync(),
75
+ ]);
76
+ return { batteryLevel, batteryState, lowPowerMode };
77
+ }
78
+
79
+ /**
80
+ * Subscribe to battery level change updates. On Android this only fires on significant changes
81
+ * (crossing the low/okay battery thresholds); on iOS it fires per one-percent drop, at most once
82
+ * a minute.
83
+ */
84
+ export function addBatteryLevelListener(
85
+ listener: (event: BatteryLevelEvent) => void,
86
+ ): EventSubscription {
87
+ return expoBattery.addListener('Expo.batteryLevelDidChange', listener);
88
+ }
89
+
90
+ /** Subscribe to battery state (charging/full/unplugged/unknown) change updates. */
91
+ export function addBatteryStateListener(
92
+ listener: (event: BatteryStateEvent) => void,
93
+ ): EventSubscription {
94
+ return expoBattery.addListener('Expo.batteryStateDidChange', listener);
95
+ }
96
+
97
+ /**
98
+ * Subscribe to Power Saver Mode (Android) / Low Power Mode (iOS) updates. Fires whenever the
99
+ * power mode is toggled.
100
+ */
101
+ export function addLowPowerModeListener(
102
+ listener: (event: PowerModeEvent) => void,
103
+ ): EventSubscription {
104
+ return expoBattery.addListener('Expo.powerModeDidChange', listener);
105
+ }
@@ -0,0 +1,19 @@
1
+ export {
2
+ isAvailableAsync,
3
+ getBatteryLevelAsync,
4
+ getBatteryStateAsync,
5
+ isLowPowerModeEnabledAsync,
6
+ isBatteryOptimizationEnabledAsync,
7
+ getPowerStateAsync,
8
+ addBatteryLevelListener,
9
+ addBatteryStateListener,
10
+ addLowPowerModeListener,
11
+ } from './battery';
12
+ export {
13
+ BatteryState,
14
+ type PowerState,
15
+ type BatteryLevelEvent,
16
+ type BatteryStateEvent,
17
+ type PowerModeEvent,
18
+ } from './types';
19
+ export type { EventSubscription } from 'expo-modules-core';
@@ -0,0 +1,31 @@
1
+ import { requireNativeModule } from 'expo-modules-core';
2
+ import type { EventSubscription } from 'expo-modules-core';
3
+ import type { BatteryLevelEvent, BatteryState, BatteryStateEvent, PowerModeEvent } from './types';
4
+
5
+ const EXPO_BATTERY_MODULE_NAME = 'ExpoBattery';
6
+
7
+ // Upstream's own event names, verified against .vendors/expo/packages/expo-battery/src/Battery.ts
8
+ // (sdk-57) — a single native module fans out three distinct event shapes through one
9
+ // `addListener`, so the overloads below key the payload type off the literal event name, the
10
+ // same trick TypeScript uses for DOM's `addEventListener`.
11
+ export type INativeBatteryModule = {
12
+ isSupported?: boolean;
13
+ getBatteryLevelAsync?(): Promise<number>;
14
+ getBatteryStateAsync?(): Promise<BatteryState>;
15
+ isLowPowerModeEnabledAsync?(): Promise<boolean>;
16
+ isBatteryOptimizationEnabledAsync?(): Promise<boolean>;
17
+ addListener(
18
+ eventName: 'Expo.batteryLevelDidChange',
19
+ listener: (event: BatteryLevelEvent) => void,
20
+ ): EventSubscription;
21
+ addListener(
22
+ eventName: 'Expo.batteryStateDidChange',
23
+ listener: (event: BatteryStateEvent) => void,
24
+ ): EventSubscription;
25
+ addListener(
26
+ eventName: 'Expo.powerModeDidChange',
27
+ listener: (event: PowerModeEvent) => void,
28
+ ): EventSubscription;
29
+ };
30
+
31
+ export const expoBattery = requireNativeModule<INativeBatteryModule>(EXPO_BATTERY_MODULE_NAME);
@@ -0,0 +1,47 @@
1
+ // Hand-ported from .vendors/expo/packages/expo-battery/src/Battery.types.ts (sdk-57) — plain
2
+ // data shapes, no `expo` meta-package import to swap out (unlike local-auth's types.ts).
3
+
4
+ export type PowerState = {
5
+ /** A number between `0` and `1`, inclusive, or `-1` if the battery level is unknown. */
6
+ batteryLevel: number;
7
+ /** An enum value representing the battery state. */
8
+ batteryState: BatteryState;
9
+ /** `true` if lowPowerMode is on, `false` if lowPowerMode is off. */
10
+ lowPowerMode: boolean;
11
+ };
12
+
13
+ export enum BatteryState {
14
+ /** If the battery state is unknown or inaccessible. */
15
+ UNKNOWN = 0,
16
+ /**
17
+ * If the battery is discharging (typically not connected to power). On Android, this
18
+ * corresponds to `BATTERY_STATUS_DISCHARGING`.
19
+ */
20
+ UNPLUGGED,
21
+ /** If battery is charging. */
22
+ CHARGING,
23
+ /** If the battery level is full. */
24
+ FULL,
25
+ /**
26
+ * The battery is not charging while power is connected (AC/USB/wireless), for example when
27
+ * battery protection limits charge to 80%, or optimized charging pauses. This differs from
28
+ * `UNPLUGGED` (discharging on battery).
29
+ * @platform android
30
+ */
31
+ NOT_CHARGING,
32
+ }
33
+
34
+ export type BatteryLevelEvent = {
35
+ /** A number between `0` and `1`, inclusive, or `-1` if the battery level is unknown. */
36
+ batteryLevel: number;
37
+ };
38
+
39
+ export type BatteryStateEvent = {
40
+ /** An enum value representing the battery state. */
41
+ batteryState: BatteryState;
42
+ };
43
+
44
+ export type PowerModeEvent = {
45
+ /** `true` if lowPowerMode is on, `false` if lowPowerMode is off. */
46
+ lowPowerMode: boolean;
47
+ };
@@ -0,0 +1,19 @@
1
+ // React lifecycle wiring over the framework-agnostic core (core/battery.ts) — mirrors the
2
+ // lifecycle-bucket naming convention of adapters/react/src/hooks and the shape of
3
+ // packages/sensors/src/react/hooks/use-accelerometer. Seeds the initial value with a one-shot
4
+ // getBatteryLevelAsync() call before the first native event fires, matching upstream's own
5
+ // useBatteryLevel (.vendors/expo/packages/expo-battery/src/Battery.ts).
6
+ import { useEffect, useState } from 'react';
7
+ import { addBatteryLevelListener, getBatteryLevelAsync } from '../../../core';
8
+
9
+ export function useBatteryLevel(): number {
10
+ const [batteryLevel, setBatteryLevel] = useState(-1);
11
+
12
+ useEffect(() => {
13
+ getBatteryLevelAsync().then(setBatteryLevel);
14
+ const subscription = addBatteryLevelListener(event => setBatteryLevel(event.batteryLevel));
15
+ return () => subscription.remove();
16
+ }, []);
17
+
18
+ return batteryLevel;
19
+ }