@symbiote-native/battery 0.0.1 → 0.1.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 (75) 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/vue/composables/use-battery-level/index.d.ts +2 -0
  27. package/build/vue/composables/use-battery-level/index.js +23 -0
  28. package/build/vue/composables/use-battery-state/index.d.ts +3 -0
  29. package/build/vue/composables/use-battery-state/index.js +21 -0
  30. package/build/vue/composables/use-low-power-mode/index.d.ts +2 -0
  31. package/build/vue/composables/use-low-power-mode/index.js +22 -0
  32. package/build/vue/index.d.ts +4 -0
  33. package/build/vue/index.js +9 -0
  34. package/build-ngc/angular/index.d.ts +4 -0
  35. package/build-ngc/angular/index.js +9 -0
  36. package/build-ngc/angular/index.js.map +1 -0
  37. package/build-ngc/angular/services/battery-level.service/index.d.ts +8 -0
  38. package/build-ngc/angular/services/battery-level.service/index.js +33 -0
  39. package/build-ngc/angular/services/battery-level.service/index.js.map +1 -0
  40. package/build-ngc/angular/services/battery-state.service/index.d.ts +9 -0
  41. package/build-ngc/angular/services/battery-state.service/index.js +28 -0
  42. package/build-ngc/angular/services/battery-state.service/index.js.map +1 -0
  43. package/build-ngc/angular/services/low-power-mode.service/index.d.ts +8 -0
  44. package/build-ngc/angular/services/low-power-mode.service/index.js +28 -0
  45. package/build-ngc/angular/services/low-power-mode.service/index.js.map +1 -0
  46. package/build-ngc/core/battery.d.ts +44 -0
  47. package/build-ngc/core/battery.js +80 -0
  48. package/build-ngc/core/battery.js.map +1 -0
  49. package/build-ngc/core/index.d.ts +3 -0
  50. package/build-ngc/core/index.js +3 -0
  51. package/build-ngc/core/index.js.map +1 -0
  52. package/build-ngc/core/native-module.d.ts +13 -0
  53. package/build-ngc/core/native-module.js +4 -0
  54. package/build-ngc/core/native-module.js.map +1 -0
  55. package/build-ngc/core/types.d.ts +40 -0
  56. package/build-ngc/core/types.js +24 -0
  57. package/build-ngc/core/types.js.map +1 -0
  58. package/native-link.json +12 -0
  59. package/package.json +92 -1
  60. package/src/angular/index.ts +24 -0
  61. package/src/angular/services/battery-level.service/index.ts +32 -0
  62. package/src/angular/services/battery-state.service/index.ts +27 -0
  63. package/src/angular/services/low-power-mode.service/index.ts +27 -0
  64. package/src/core/battery.ts +105 -0
  65. package/src/core/index.ts +19 -0
  66. package/src/core/native-module.ts +31 -0
  67. package/src/core/types.ts +47 -0
  68. package/src/react/hooks/use-battery-level/index.ts +19 -0
  69. package/src/react/hooks/use-battery-state/index.ts +17 -0
  70. package/src/react/hooks/use-low-power-mode/index.ts +18 -0
  71. package/src/react/index.ts +26 -0
  72. package/src/vue/composables/use-battery-level/index.ts +32 -0
  73. package/src/vue/composables/use-battery-state/index.ts +31 -0
  74. package/src/vue/composables/use-low-power-mode/index.ts +31 -0
  75. package/src/vue/index.ts +26 -0
package/README.md CHANGED
@@ -1,19 +1,144 @@
1
1
  # @symbiote-native/battery
2
2
 
3
- **Status: skeleton only — not implemented, published early to reserve the npm name.**
4
- This package currently ships nothing but `package.json` and this README. No `src/`, no
5
- dependencies, no build. See the `symbiote-new-package-skeleton` project skill for what a
6
- tier-1 skeleton package is and isn't.
7
-
8
- Planned: a port of [`expo-battery`](https://docs.expo.dev/versions/latest/sdk/battery/) for
3
+ Port of [`expo-battery`](https://docs.expo.dev/versions/latest/sdk/battery/) for
9
4
  [SymbioteNative](../../README.md) — battery level and state, low-power-mode detection, reachable
10
5
  from every adapter (React, Vue, Angular), not just React.
11
6
 
12
- ## Planned API
7
+ Built the same way as [`@symbiote-native/local-auth`](../local-auth) and
8
+ [`@symbiote-native/sensors`](../sensors), an `expo-modules-core`-based wrapper (see the
9
+ `symbiote-expo-native-module` project skill for the full mechanism: why `expo-modules-core` is
10
+ depended on directly and never the `expo` meta-package, why the upstream JS is hand-ported into
11
+ `core/` rather than imported, and how autolinking picks up the native module).
12
+
13
+ ## Install
14
+
15
+ ```bash
16
+ npm install @symbiote-native/battery
17
+ ```
18
+
19
+ Depends on `expo-battery` and `expo-modules-core` directly (regular dependencies, pinned to exact
20
+ versions — never a caret range, since this package's `core/` is hand-ported against one specific
21
+ native API shape and a newer resolve could silently drift the two apart). Never install
22
+ `expo-battery` yourself, and never add the `expo` package to this project — it bundles its own
23
+ Metro/Babel pipeline that conflicts with this project's own.
24
+
25
+ ### Required one-time step: native autolinking wiring
26
+
27
+ Unlike a plain RN native module, `expo-battery`'s native code is discovered by
28
+ `expo-modules-autolinking`, not RN's own `react-native.config.cjs` mechanism — this needs wiring
29
+ into the native host app **once**, covering this package and every other `expo-modules-core`
30
+ package (`@symbiote-native/sensors`, `@symbiote-native/local-auth`) with zero further changes:
31
+
32
+ | Platform | Touches |
33
+ |---|---|
34
+ | iOS | `ios/Podfile` — add `use_expo_modules!` |
35
+ | iOS | `AppDelegate.swift` — Expo's runtime-bootstrap hook |
36
+ | Android | `settings.gradle` / `app/build.gradle` — resolve and include the Expo Gradle projects |
37
+ | Android | `MainApplication.kt` — Expo's bootstrap hook, plus a hand-written native-module name map (there's no `expo` meta-package here to auto-generate one) |
38
+
39
+ Full mechanics — the Podfile pieces that normally ship inside the `expo` package, the `expo`
40
+ peer-dependency exclusion list — live in the `symbiote-expo-native-module` skill. Reference
41
+ implementation: `examples/expo-react/ios/Podfile` and
42
+ `examples/expo-react/android/app/src/main/java/com/canaryexpo/MainApplication.kt`.
43
+
44
+ No platform permission string is needed for battery's basic surface —
45
+ `isBatteryOptimizationEnabledAsync` reads an Android-only system state with no runtime permission
46
+ prompt either.
47
+
48
+ ## Shape
49
+
50
+ ```
51
+ src/core/ Battery.ts — one-shot get*Async functions + addListener subscriptions.
52
+ native-module.ts resolves the native module through expo-modules-core's
53
+ requireNativeModule. types.ts — BatteryState enum, PowerState,
54
+ *Event shapes, hand-ported from Battery.types.ts.
55
+ src/react/hooks/ @symbiote-native/battery/react — useBatteryLevel, useBatteryState,
56
+ useLowPowerMode
57
+ src/vue/composables/ @symbiote-native/battery/vue — same three names, Vue lifecycle
58
+ src/angular/services/ @symbiote-native/battery/angular — BatteryLevelService, BatteryStateService,
59
+ LowPowerModeService (`.connect()` returns a Signal)
60
+ ```
61
+
62
+ Each adapter's hook/composable/service is a thin lifecycle wrapper (seed from the one-shot
63
+ `get*Async` call, subscribe to the matching listener, unsubscribe on unmount/cleanup) over the
64
+ same `core` functions — the subscription and fallback logic is written once and shared by all
65
+ three.
66
+
67
+ ## Use it
68
+
69
+ ```tsx
70
+ // React — examples/expo-react/screens/BatteryScreen.tsx
71
+ import { useBatteryLevel, useBatteryState, useLowPowerMode } from '@symbiote-native/battery/react';
72
+ import { BatteryState } from '@symbiote-native/battery';
73
+
74
+ function BatteryScreen() {
75
+ const batteryLevel = useBatteryLevel(); // number, -1 until the first reading arrives
76
+ const batteryState = useBatteryState(); // BatteryState, UNKNOWN until the first reading
77
+ const lowPowerMode = useLowPowerMode(); // boolean, false until the first reading
78
+
79
+ const batteryLevelLabel = batteryLevel < 0 ? 'unknown' : `${Math.round(batteryLevel * 100)}%`;
80
+
81
+ return (
82
+ <>
83
+ <Text>{batteryLevelLabel}</Text>
84
+ <Text>{batteryState === BatteryState.CHARGING ? 'Charging' : 'Not charging'}</Text>
85
+ <Text>{lowPowerMode ? 'On' : 'Off'}</Text>
86
+ </>
87
+ );
88
+ }
89
+ ```
90
+
91
+ ```vue
92
+ <!-- Vue — examples/expo-vue-sfc/screens/BatteryScreen.vue -->
93
+ <script setup lang="ts">
94
+ import { computed } from 'vue';
95
+ import { useBatteryLevel, useBatteryState, useLowPowerMode } from '@symbiote-native/battery/vue';
96
+
97
+ const batteryLevel = useBatteryLevel(); // Ref<number>
98
+ const batteryState = useBatteryState(); // Ref<BatteryState>
99
+ const lowPowerMode = useLowPowerMode(); // Ref<boolean>
100
+
101
+ const batteryLevelText = computed(() =>
102
+ batteryLevel.value < 0 ? 'unknown' : `${Math.round(batteryLevel.value * 100)}%`,
103
+ );
104
+ </script>
105
+ <template>
106
+ <Text>{{ batteryLevelText }}</Text>
107
+ <Text>{{ lowPowerMode ? 'ON' : 'OFF' }}</Text>
108
+ </template>
109
+ ```
110
+
111
+ ```ts
112
+ // Angular — examples/expo-angular/src/screens/BatteryScreen.ts
113
+ import { Component, inject } from '@angular/core';
114
+ import { BatteryLevelService, BatteryStateService, LowPowerModeService } from '@symbiote-native/battery/angular';
115
+
116
+ @Component({ /* ... */ })
117
+ export class BatteryScreen {
118
+ readonly batteryLevel = inject(BatteryLevelService).connect(); // Signal<number>
119
+ readonly batteryState = inject(BatteryStateService).connect(); // Signal<BatteryState>
120
+ readonly lowPowerMode = inject(LowPowerModeService).connect(); // Signal<boolean>
121
+
122
+ batteryLevelLabel(): string {
123
+ const level = this.batteryLevel();
124
+ return level < 0 ? 'unknown' : `${Math.round(level * 100)}%`;
125
+ }
126
+ }
127
+ ```
13
128
 
14
- Forward-looking reference only, transcribed from upstream's actual
15
- `.vendors/expo/packages/expo-battery/src/Battery.ts` — signatures here are what will be ported,
16
- not yet implemented.
129
+ Each of the three demo screens above also renders a one-shot capabilities card
130
+ (`isAvailableAsync()`, and Android-only `isBatteryOptimizationEnabledAsync()`) resolved directly
131
+ from `@symbiote-native/battery`'s core entry point — see the linked files for the full version.
132
+ The iOS Simulator reports the battery API as unavailable (`isAvailableAsync()` resolves `false`)
133
+ since simulators have no real battery hardware; a physical device is needed to see live readings.
134
+
135
+ ## API
136
+
137
+ A mix of stateless async functions and three listener-based subscriptions
138
+ (`addBatteryLevelListener`/`addBatteryStateListener`/`addLowPowerModeListener`), each with its
139
+ own adapter-level lifecycle hook — one hook/composable/service per listener, matching upstream's
140
+ own `useBatteryLevel`/`useBatteryState`/`useLowPowerMode` being three separate hooks, not one
141
+ combined hook.
17
142
 
18
143
  ```ts
19
144
  isAvailableAsync(): Promise<boolean> // battery API availability on this device
@@ -22,23 +147,43 @@ getBatteryStateAsync(): Promise<BatteryState> // UNKNOWN/UNPLUGG
22
147
  isLowPowerModeEnabledAsync(): Promise<boolean> // Low Power Mode (iOS) / Power Saver (Android)
23
148
  isBatteryOptimizationEnabledAsync(): Promise<boolean> // Android only
24
149
  getPowerStateAsync(): Promise<PowerState> // combined level + state + lowPowerMode
25
- addBatteryLevelListener(listener): Subscription // fires on battery level change
26
- addBatteryStateListener(listener): Subscription // fires on battery state change
27
- addLowPowerModeListener(listener): Subscription // fires when low-power mode toggles
150
+ addBatteryLevelListener(listener): EventSubscription // fires on battery level change
151
+ addBatteryStateListener(listener): EventSubscription // fires on battery state change
152
+ addLowPowerModeListener(listener): EventSubscription // fires when low-power mode toggles
28
153
  ```
29
154
 
30
155
  Plus `BatteryState` (enum), `PowerState`, `BatteryLevelEvent`, `BatteryStateEvent`,
31
- `PowerModeEvent` — ported from upstream's `Battery.types.ts`. Upstream also exposes
32
- `useBatteryLevel` / `useBatteryState` / `useLowPowerMode` / `usePowerState` React hooks; per this
33
- repo's core/adapter split, those become adapter-owned lifecycle wrappers (React hooks, Vue
34
- composables, …) over the same shared core, not core exports themselves.
35
-
36
- ## Reference
37
-
38
- - [`../local-auth`](../local-auth) — nearest already-implemented sibling package: an
39
- `expo-modules-core`-based wrapper with the same shape this package will follow (framework-agnostic
40
- `core/`, thin per-adapter re-export entries).
41
- - [`symbiote-expo-native-module`](../../.claude/skills/symbiote-expo-native-module/SKILL.md) — the
42
- real wrapping mechanics (depend on `expo-modules-core` only, never the `expo` meta-package; port
43
- upstream JS into `core/` rather than importing it; autolinking) that will apply once this moves
44
- past skeleton.
156
+ `PowerModeEvent` — ported from upstream's `Battery.types.ts`.
157
+
158
+ ```ts
159
+ import { getBatteryLevelAsync, addBatteryLevelListener } from '@symbiote-native/battery';
160
+
161
+ // framework-scoped entry points re-export the same free functions, plus a lifecycle
162
+ // hook/composable/service per listener:
163
+ import { useBatteryLevel, useBatteryState, useLowPowerMode } from '@symbiote-native/battery/react';
164
+ import { useBatteryLevel, useBatteryState, useLowPowerMode } from '@symbiote-native/battery/vue';
165
+ import {
166
+ BatteryLevelService,
167
+ BatteryStateService,
168
+ LowPowerModeService,
169
+ } from '@symbiote-native/battery/angular';
170
+ ```
171
+
172
+ Each hook/composable/service seeds its initial value from the matching one-shot
173
+ `get*Async`/`is*Async` call, then subscribes to the matching listener for updates, and
174
+ unsubscribes on unmount — mirroring upstream's own `useBatteryLevel`/`useBatteryState`/
175
+ `useLowPowerMode`.
176
+
177
+ ## Test it
178
+
179
+ No Fabric/Descriptor angle at all — battery is a pure async-function + `EventEmitter` listener
180
+ surface, never a view. Tests inject a fake native-module object in place of the real
181
+ `requireNativeModule` resolution (`src/core/battery.test.ts`,
182
+ `src/{react,vue,angular}/**/*.test.{ts,tsx}`, `vitest`), the same pattern
183
+ `@symbiote-native/sensors` and `@symbiote-native/local-auth` use — no `installFabric()`, no
184
+ ViewConfig. Native rendering itself is verified on-device — see the parent
185
+ [README](../../README.md).
186
+
187
+ The Android/iOS native wiring is done across all four `examples/expo-*` canary apps
188
+ (`examples/expo-react`, `examples/expo-vue-sfc`, `examples/expo-vue-tsx`, `examples/expo-angular`);
189
+ this package isn't yet in the public, non-Expo `examples/react` canary.
@@ -0,0 +1,4 @@
1
+ export { BatteryLevelService } from './services/battery-level.service';
2
+ export { BatteryStateService } from './services/battery-state.service';
3
+ export { LowPowerModeService } from './services/low-power-mode.service';
4
+ export { isAvailableAsync, getBatteryLevelAsync, getBatteryStateAsync, isLowPowerModeEnabledAsync, isBatteryOptimizationEnabledAsync, getPowerStateAsync, addBatteryLevelListener, addBatteryStateListener, addLowPowerModeListener, BatteryState, type PowerState, type BatteryLevelEvent, type BatteryStateEvent, type PowerModeEvent, type EventSubscription, } from '../core';
@@ -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/index.js';
6
+ export { BatteryStateService } from './services/battery-state.service/index.js';
7
+ export { LowPowerModeService } from './services/low-power-mode.service/index.js';
8
+ export { isAvailableAsync, getBatteryLevelAsync, getBatteryStateAsync, isLowPowerModeEnabledAsync, isBatteryOptimizationEnabledAsync, getPowerStateAsync, addBatteryLevelListener, addBatteryStateListener, addLowPowerModeListener, BatteryState, } from '../core/index.js';
@@ -0,0 +1,5 @@
1
+ import { type Signal } from '@angular/core';
2
+ export declare class BatteryLevelService {
3
+ private readonly injector;
4
+ connect(): Signal<number>;
5
+ }
@@ -0,0 +1,74 @@
1
+ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
2
+ function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
3
+ var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
4
+ var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
5
+ var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
6
+ var _, done = false;
7
+ for (var i = decorators.length - 1; i >= 0; i--) {
8
+ var context = {};
9
+ for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
10
+ for (var p in contextIn.access) context.access[p] = contextIn.access[p];
11
+ context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
12
+ var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
13
+ if (kind === "accessor") {
14
+ if (result === void 0) continue;
15
+ if (result === null || typeof result !== "object") throw new TypeError("Object expected");
16
+ if (_ = accept(result.get)) descriptor.get = _;
17
+ if (_ = accept(result.set)) descriptor.set = _;
18
+ if (_ = accept(result.init)) initializers.unshift(_);
19
+ }
20
+ else if (_ = accept(result)) {
21
+ if (kind === "field") initializers.unshift(_);
22
+ else descriptor[key] = _;
23
+ }
24
+ }
25
+ if (target) Object.defineProperty(target, contextIn.name, descriptor);
26
+ done = true;
27
+ };
28
+ var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
29
+ var useValue = arguments.length > 2;
30
+ for (var i = 0; i < initializers.length; i++) {
31
+ value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
32
+ }
33
+ return useValue ? value : void 0;
34
+ };
35
+ import { effect, inject, Injectable, Injector, signal } from '@angular/core';
36
+ import { addBatteryLevelListener, getBatteryLevelAsync } from '../../../core/index.js';
37
+ // Angular twin of React's `useBatteryLevel` hook and Vue's `useBatteryLevel` composable.
38
+ // Angular has no per-instance hook — state and lifecycle live in DI instead, so `connect()`
39
+ // stands in for the hook's role: call it ONCE (typically from a component's field initializer,
40
+ // inside an injection context).
41
+ //
42
+ // readonly batteryLevel = inject(BatteryLevelService).connect();
43
+ // // template: {{ batteryLevel() }}
44
+ //
45
+ // Mirrors AccelerometerService.connect() — a single effect() that seeds the initial value with
46
+ // getBatteryLevelAsync(), subscribes once, and cleans up once.
47
+ let BatteryLevelService = (() => {
48
+ let _classDecorators = [Injectable({ providedIn: 'root' })];
49
+ let _classDescriptor;
50
+ let _classExtraInitializers = [];
51
+ let _classThis;
52
+ var BatteryLevelService = class {
53
+ static { _classThis = this; }
54
+ static {
55
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
56
+ __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
57
+ BatteryLevelService = _classThis = _classDescriptor.value;
58
+ if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
59
+ __runInitializers(_classThis, _classExtraInitializers);
60
+ }
61
+ injector = inject(Injector);
62
+ connect() {
63
+ const batteryLevel = signal(-1);
64
+ effect(onCleanup => {
65
+ getBatteryLevelAsync().then(level => batteryLevel.set(level));
66
+ const subscription = addBatteryLevelListener(event => batteryLevel.set(event.batteryLevel));
67
+ onCleanup(() => subscription.remove());
68
+ }, { injector: this.injector });
69
+ return batteryLevel.asReadonly();
70
+ }
71
+ };
72
+ return BatteryLevelService = _classThis;
73
+ })();
74
+ export { BatteryLevelService };
@@ -0,0 +1,6 @@
1
+ import { type Signal } from '@angular/core';
2
+ import { BatteryState } from '../../../core';
3
+ export declare class BatteryStateService {
4
+ private readonly injector;
5
+ connect(): Signal<BatteryState>;
6
+ }
@@ -0,0 +1,69 @@
1
+ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
2
+ function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
3
+ var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
4
+ var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
5
+ var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
6
+ var _, done = false;
7
+ for (var i = decorators.length - 1; i >= 0; i--) {
8
+ var context = {};
9
+ for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
10
+ for (var p in contextIn.access) context.access[p] = contextIn.access[p];
11
+ context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
12
+ var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
13
+ if (kind === "accessor") {
14
+ if (result === void 0) continue;
15
+ if (result === null || typeof result !== "object") throw new TypeError("Object expected");
16
+ if (_ = accept(result.get)) descriptor.get = _;
17
+ if (_ = accept(result.set)) descriptor.set = _;
18
+ if (_ = accept(result.init)) initializers.unshift(_);
19
+ }
20
+ else if (_ = accept(result)) {
21
+ if (kind === "field") initializers.unshift(_);
22
+ else descriptor[key] = _;
23
+ }
24
+ }
25
+ if (target) Object.defineProperty(target, contextIn.name, descriptor);
26
+ done = true;
27
+ };
28
+ var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
29
+ var useValue = arguments.length > 2;
30
+ for (var i = 0; i < initializers.length; i++) {
31
+ value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
32
+ }
33
+ return useValue ? value : void 0;
34
+ };
35
+ import { effect, inject, Injectable, Injector, signal } from '@angular/core';
36
+ import { addBatteryStateListener, BatteryState, getBatteryStateAsync } from '../../../core/index.js';
37
+ // Angular twin of React's `useBatteryState` hook and Vue's `useBatteryState` composable. See
38
+ // BatteryLevelService for the `connect()` pattern rationale.
39
+ //
40
+ // readonly batteryState = inject(BatteryStateService).connect();
41
+ // // template: {{ batteryState() }}
42
+ let BatteryStateService = (() => {
43
+ let _classDecorators = [Injectable({ providedIn: 'root' })];
44
+ let _classDescriptor;
45
+ let _classExtraInitializers = [];
46
+ let _classThis;
47
+ var BatteryStateService = class {
48
+ static { _classThis = this; }
49
+ static {
50
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
51
+ __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
52
+ BatteryStateService = _classThis = _classDescriptor.value;
53
+ if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
54
+ __runInitializers(_classThis, _classExtraInitializers);
55
+ }
56
+ injector = inject(Injector);
57
+ connect() {
58
+ const batteryState = signal(BatteryState.UNKNOWN);
59
+ effect(onCleanup => {
60
+ getBatteryStateAsync().then(state => batteryState.set(state));
61
+ const subscription = addBatteryStateListener(event => batteryState.set(event.batteryState));
62
+ onCleanup(() => subscription.remove());
63
+ }, { injector: this.injector });
64
+ return batteryState.asReadonly();
65
+ }
66
+ };
67
+ return BatteryStateService = _classThis;
68
+ })();
69
+ export { BatteryStateService };
@@ -0,0 +1,5 @@
1
+ import { type Signal } from '@angular/core';
2
+ export declare class LowPowerModeService {
3
+ private readonly injector;
4
+ connect(): Signal<boolean>;
5
+ }
@@ -0,0 +1,69 @@
1
+ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
2
+ function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
3
+ var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
4
+ var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
5
+ var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
6
+ var _, done = false;
7
+ for (var i = decorators.length - 1; i >= 0; i--) {
8
+ var context = {};
9
+ for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
10
+ for (var p in contextIn.access) context.access[p] = contextIn.access[p];
11
+ context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
12
+ var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
13
+ if (kind === "accessor") {
14
+ if (result === void 0) continue;
15
+ if (result === null || typeof result !== "object") throw new TypeError("Object expected");
16
+ if (_ = accept(result.get)) descriptor.get = _;
17
+ if (_ = accept(result.set)) descriptor.set = _;
18
+ if (_ = accept(result.init)) initializers.unshift(_);
19
+ }
20
+ else if (_ = accept(result)) {
21
+ if (kind === "field") initializers.unshift(_);
22
+ else descriptor[key] = _;
23
+ }
24
+ }
25
+ if (target) Object.defineProperty(target, contextIn.name, descriptor);
26
+ done = true;
27
+ };
28
+ var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
29
+ var useValue = arguments.length > 2;
30
+ for (var i = 0; i < initializers.length; i++) {
31
+ value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
32
+ }
33
+ return useValue ? value : void 0;
34
+ };
35
+ import { effect, inject, Injectable, Injector, signal } from '@angular/core';
36
+ import { addLowPowerModeListener, isLowPowerModeEnabledAsync } from '../../../core/index.js';
37
+ // Angular twin of React's `useLowPowerMode` hook and Vue's `useLowPowerMode` composable. See
38
+ // BatteryLevelService for the `connect()` pattern rationale.
39
+ //
40
+ // readonly lowPowerMode = inject(LowPowerModeService).connect();
41
+ // // template: {{ lowPowerMode() }}
42
+ let LowPowerModeService = (() => {
43
+ let _classDecorators = [Injectable({ providedIn: 'root' })];
44
+ let _classDescriptor;
45
+ let _classExtraInitializers = [];
46
+ let _classThis;
47
+ var LowPowerModeService = class {
48
+ static { _classThis = this; }
49
+ static {
50
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
51
+ __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
52
+ LowPowerModeService = _classThis = _classDescriptor.value;
53
+ if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
54
+ __runInitializers(_classThis, _classExtraInitializers);
55
+ }
56
+ injector = inject(Injector);
57
+ connect() {
58
+ const lowPowerMode = signal(false);
59
+ effect(onCleanup => {
60
+ isLowPowerModeEnabledAsync().then(enabled => lowPowerMode.set(enabled));
61
+ const subscription = addLowPowerModeListener(event => lowPowerMode.set(event.lowPowerMode));
62
+ onCleanup(() => subscription.remove());
63
+ }, { injector: this.injector });
64
+ return lowPowerMode.asReadonly();
65
+ }
66
+ };
67
+ return LowPowerModeService = _classThis;
68
+ })();
69
+ export { LowPowerModeService };
@@ -0,0 +1,44 @@
1
+ import type { EventSubscription } from 'expo-modules-core';
2
+ import { BatteryState, type BatteryLevelEvent, type BatteryStateEvent, type PowerModeEvent, type PowerState } 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 declare function isAvailableAsync(): Promise<boolean>;
8
+ /**
9
+ * Gets the battery level of the device as a number between `0` and `1`, inclusive. If the
10
+ * device does not support retrieving the battery level, this method returns `-1`.
11
+ */
12
+ export declare function getBatteryLevelAsync(): Promise<number>;
13
+ /** Tells the battery's current state. */
14
+ export declare function getBatteryStateAsync(): Promise<BatteryState>;
15
+ /**
16
+ * Gets the current status of Power Saver mode on Android and Low Power mode on iOS. If a
17
+ * platform doesn't support low-power-mode reporting, this always resolves `false`, even if the
18
+ * device is actually in low-power mode.
19
+ */
20
+ export declare function isLowPowerModeEnabledAsync(): Promise<boolean>;
21
+ /**
22
+ * Checks whether battery optimization is enabled for this app. If enabled, background tasks
23
+ * might be affected when the app goes into doze mode state.
24
+ * @platform android
25
+ */
26
+ export declare function isBatteryOptimizationEnabledAsync(): Promise<boolean>;
27
+ /**
28
+ * Gets the power state of the device: battery level, whether it is plugged in, and whether the
29
+ * system is currently in Power Saver Mode (Android) / Low Power Mode (iOS).
30
+ */
31
+ export declare function getPowerStateAsync(): Promise<PowerState>;
32
+ /**
33
+ * Subscribe to battery level change updates. On Android this only fires on significant changes
34
+ * (crossing the low/okay battery thresholds); on iOS it fires per one-percent drop, at most once
35
+ * a minute.
36
+ */
37
+ export declare function addBatteryLevelListener(listener: (event: BatteryLevelEvent) => void): EventSubscription;
38
+ /** Subscribe to battery state (charging/full/unplugged/unknown) change updates. */
39
+ export declare function addBatteryStateListener(listener: (event: BatteryStateEvent) => void): EventSubscription;
40
+ /**
41
+ * Subscribe to Power Saver Mode (Android) / Low Power Mode (iOS) updates. Fires whenever the
42
+ * power mode is toggled.
43
+ */
44
+ export declare function addLowPowerModeListener(listener: (event: PowerModeEvent) => void): EventSubscription;
@@ -0,0 +1,79 @@
1
+ import { expoBattery } from './native-module.js';
2
+ import { BatteryState, } from './types.js';
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
+ }
@@ -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,2 @@
1
+ export { isAvailableAsync, getBatteryLevelAsync, getBatteryStateAsync, isLowPowerModeEnabledAsync, isBatteryOptimizationEnabledAsync, getPowerStateAsync, addBatteryLevelListener, addBatteryStateListener, addLowPowerModeListener, } from './battery.js';
2
+ export { BatteryState, } from './types.js';
@@ -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,3 @@
1
+ import { requireNativeModule } from 'expo-modules-core';
2
+ const EXPO_BATTERY_MODULE_NAME = 'ExpoBattery';
3
+ export const expoBattery = requireNativeModule(EXPO_BATTERY_MODULE_NAME);