@symbiote-native/brightness 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +290 -0
- package/build/angular/index.d.ts +2 -0
- package/build/angular/index.js +4 -0
- package/build/angular/services/permissions.service/index.d.ts +8 -0
- package/build/angular/services/permissions.service/index.js +78 -0
- package/build/core/brightness.d.ts +56 -0
- package/build/core/brightness.js +137 -0
- package/build/core/index.d.ts +3 -0
- package/build/core/index.js +2 -0
- package/build/core/native-module.d.ts +16 -0
- package/build/core/native-module.js +3 -0
- package/build/core/types.d.ts +16 -0
- package/build/core/types.js +16 -0
- package/build/react/hooks/use-permissions/index.d.ts +6 -0
- package/build/react/hooks/use-permissions/index.js +30 -0
- package/build/react/index.d.ts +2 -0
- package/build/react/index.js +6 -0
- package/build/vue/composables/use-permissions/index.d.ts +7 -0
- package/build/vue/composables/use-permissions/index.js +23 -0
- package/build/vue/index.d.ts +2 -0
- package/build/vue/index.js +6 -0
- package/build-ngc/angular/index.d.ts +2 -0
- package/build-ngc/angular/index.js +5 -0
- package/build-ngc/angular/index.js.map +1 -0
- package/build-ngc/angular/services/permissions.service/index.d.ts +11 -0
- package/build-ngc/angular/services/permissions.service/index.js +37 -0
- package/build-ngc/angular/services/permissions.service/index.js.map +1 -0
- package/build-ngc/core/brightness.d.ts +56 -0
- package/build-ngc/core/brightness.js +138 -0
- package/build-ngc/core/brightness.js.map +1 -0
- package/build-ngc/core/index.d.ts +3 -0
- package/build-ngc/core/index.js +3 -0
- package/build-ngc/core/index.js.map +1 -0
- package/build-ngc/core/native-module.d.ts +16 -0
- package/build-ngc/core/native-module.js +4 -0
- package/build-ngc/core/native-module.js.map +1 -0
- package/build-ngc/core/types.d.ts +16 -0
- package/build-ngc/core/types.js +17 -0
- package/build-ngc/core/types.js.map +1 -0
- package/native-link.json +12 -0
- package/package.json +107 -0
- package/src/angular/index.ts +23 -0
- package/src/angular/services/permissions.service/index.ts +37 -0
- package/src/angular/services/permissions.service/permissions.service.test.ts +107 -0
- package/src/core/brightness.test.ts +334 -0
- package/src/core/brightness.ts +155 -0
- package/src/core/index.ts +22 -0
- package/src/core/native-module.ts +32 -0
- package/src/core/types.ts +27 -0
- package/src/react/hooks/use-permissions/index.ts +41 -0
- package/src/react/hooks/use-permissions/use-permissions.test.tsx +100 -0
- package/src/react/index.ts +26 -0
- package/src/vue/composables/use-permissions/index.ts +36 -0
- package/src/vue/composables/use-permissions/use-permissions.test.ts +99 -0
- package/src/vue/index.ts +26 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 A. Prokopenko
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
# @symbiote-native/brightness
|
|
2
|
+
|
|
3
|
+
A wrapper package for [SymbioteNative](../../README.md) that makes
|
|
4
|
+
[`expo-brightness`](https://github.com/expo/expo/tree/main/packages/expo-brightness) — screen
|
|
5
|
+
brightness get/set, Android-only system-brightness-mode control, an iOS-only brightness-change
|
|
6
|
+
listener, and permission get/request — usable from **every** adapter, React, Vue, and Angular, not
|
|
7
|
+
just React. Unlike [`@symbiote-native/sensors`](../sensors) — a `DeviceSensor` class per instance,
|
|
8
|
+
subscribed through a hook/composable/service — this package's surface is mostly stateless free
|
|
9
|
+
async functions plus one listener; only the permission surface gets its own lifecycle hook/
|
|
10
|
+
composable/service, mirroring upstream's own `usePermissions`. Built the same way as
|
|
11
|
+
[`@symbiote-native/local-auth`](../local-auth), [`@symbiote-native/battery`](../battery), and
|
|
12
|
+
[`@symbiote-native/haptics`](../haptics) — an `expo-modules-core`-based wrapper (see the
|
|
13
|
+
`symbiote-expo-native-module` project skill for the full mechanism: why `expo-modules-core` is
|
|
14
|
+
depended on directly and never the `expo` meta-package, why the upstream JS is hand-ported into
|
|
15
|
+
`core/` rather than imported, and how autolinking picks up the native module).
|
|
16
|
+
|
|
17
|
+
## Install
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install @symbiote-native/brightness
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
`expo-brightness` and `expo-modules-core` come along as regular, pinned dependencies — never
|
|
24
|
+
install either yourself, and never add the `expo` meta-package to this project (it bundles its own
|
|
25
|
+
Metro/Babel pipeline that conflicts with this project's own).
|
|
26
|
+
|
|
27
|
+
### Required one-time step: native autolinking wiring
|
|
28
|
+
|
|
29
|
+
Unlike a plain RN native module, `expo-brightness`'s native code is discovered by
|
|
30
|
+
`expo-modules-autolinking`, not the `react-native.config.cjs`/podspec mechanism other wrappers in
|
|
31
|
+
this repo use — this needs wiring into the native host app **once**, covering this package and
|
|
32
|
+
every other `expo-modules-core` package with zero further changes:
|
|
33
|
+
|
|
34
|
+
| Platform | Touches |
|
|
35
|
+
|---|---|
|
|
36
|
+
| iOS | `ios/Podfile` — add `use_expo_modules!` |
|
|
37
|
+
| iOS | `AppDelegate.swift` — Expo's runtime-bootstrap hook |
|
|
38
|
+
| Android | `settings.gradle` / `app/build.gradle` — resolve and include the Expo Gradle projects |
|
|
39
|
+
| 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) |
|
|
40
|
+
| Android | `AndroidManifest.xml` — `android.permission.WRITE_SETTINGS`, required to set the system-wide brightness (`setSystemBrightnessAsync`/`setSystemBrightnessModeAsync`) |
|
|
41
|
+
|
|
42
|
+
Full mechanics — the Podfile pieces that normally ship inside the `expo` package, the `expo`
|
|
43
|
+
peer-dependency exclusion list — live in the `symbiote-expo-native-module` skill. Reference
|
|
44
|
+
implementation: `examples/expo-react/android/app/src/main/AndroidManifest.xml` (the
|
|
45
|
+
`WRITE_SETTINGS` permission) and
|
|
46
|
+
`examples/expo-react/android/app/src/main/java/com/canaryexpo/MainApplication.kt` (the
|
|
47
|
+
`BrightnessModule` registration) — the Vue and Angular Expo canaries wire the same three layers.
|
|
48
|
+
|
|
49
|
+
## Shape
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
src/core/ isAvailableAsync, get/setBrightnessAsync, the Android-only
|
|
53
|
+
system-brightness-mode surface, permission get/request, and
|
|
54
|
+
addBrightnessListener (iOS only); native-module.ts resolves
|
|
55
|
+
ExpoBrightness through expo-modules-core's requireNativeModule.
|
|
56
|
+
src/react/hooks/ @symbiote-native/brightness/react — usePermissions
|
|
57
|
+
src/vue/composables/ @symbiote-native/brightness/vue — usePermissions (same name)
|
|
58
|
+
src/angular/services/ @symbiote-native/brightness/angular — PermissionsService
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Each adapter's hook/composable/service wraps the same one-shot `getPermissionsAsync`/
|
|
62
|
+
`requestPermissionsAsync` pair in its own lifecycle idiom (auto-fetch on mount, exposed as a
|
|
63
|
+
tuple/object/signal). Every other export — brightness get/set, the system-brightness-mode surface,
|
|
64
|
+
the listener — is a stateless free-function re-export, written once in `core/` and shared verbatim
|
|
65
|
+
by all three adapters.
|
|
66
|
+
|
|
67
|
+
## Use it
|
|
68
|
+
|
|
69
|
+
```tsx
|
|
70
|
+
// React — trimmed from examples/expo-react/screens/BrightnessScreen.tsx
|
|
71
|
+
import { useEffect, useState } from 'react';
|
|
72
|
+
import { Pressable, Text, View } from '@symbiote-native/react';
|
|
73
|
+
import {
|
|
74
|
+
addBrightnessListener,
|
|
75
|
+
getBrightnessAsync,
|
|
76
|
+
setBrightnessAsync,
|
|
77
|
+
} from '@symbiote-native/brightness';
|
|
78
|
+
import { usePermissions } from '@symbiote-native/brightness/react';
|
|
79
|
+
|
|
80
|
+
function BrightnessScreen() {
|
|
81
|
+
const [brightness, setBrightness] = useState<number | null>(null);
|
|
82
|
+
const [permissionStatus, requestPermission] = usePermissions();
|
|
83
|
+
|
|
84
|
+
useEffect(() => {
|
|
85
|
+
getBrightnessAsync().then(setBrightness);
|
|
86
|
+
// iOS only — never fires on Android, so there the value only changes via setBrightnessAsync.
|
|
87
|
+
const subscription = addBrightnessListener(event => setBrightness(event.brightness));
|
|
88
|
+
return () => subscription.remove();
|
|
89
|
+
}, []);
|
|
90
|
+
|
|
91
|
+
return (
|
|
92
|
+
<View>
|
|
93
|
+
<Text>{brightness === null ? 'checking…' : `${Math.round(brightness * 100)}%`}</Text>
|
|
94
|
+
<Pressable onPress={() => setBrightnessAsync(0.5)}>
|
|
95
|
+
<Text>Set to 50%</Text>
|
|
96
|
+
</Pressable>
|
|
97
|
+
<Text>{permissionStatus?.status ?? 'checking…'}</Text>
|
|
98
|
+
<Pressable onPress={() => requestPermission()}>
|
|
99
|
+
<Text>Request permission</Text>
|
|
100
|
+
</Pressable>
|
|
101
|
+
</View>
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
```vue
|
|
107
|
+
<!-- Vue — trimmed from examples/expo-vue-sfc/screens/BrightnessScreen.vue -->
|
|
108
|
+
<script setup lang="ts">
|
|
109
|
+
import { onMounted, onUnmounted, ref } from 'vue';
|
|
110
|
+
import { Pressable, Text, View } from '@symbiote-native/vue';
|
|
111
|
+
import {
|
|
112
|
+
addBrightnessListener,
|
|
113
|
+
getBrightnessAsync,
|
|
114
|
+
setBrightnessAsync,
|
|
115
|
+
type EventSubscription,
|
|
116
|
+
} from '@symbiote-native/brightness';
|
|
117
|
+
import { usePermissions } from '@symbiote-native/brightness/vue';
|
|
118
|
+
|
|
119
|
+
const brightness = ref<number | null>(null);
|
|
120
|
+
const { status: permissionStatus, request: requestPermission } = usePermissions();
|
|
121
|
+
|
|
122
|
+
let subscription: EventSubscription | undefined;
|
|
123
|
+
|
|
124
|
+
onMounted(() => {
|
|
125
|
+
void getBrightnessAsync().then(value => (brightness.value = value));
|
|
126
|
+
subscription = addBrightnessListener(event => (brightness.value = event.brightness));
|
|
127
|
+
});
|
|
128
|
+
onUnmounted(() => subscription?.remove());
|
|
129
|
+
</script>
|
|
130
|
+
|
|
131
|
+
<template>
|
|
132
|
+
<View>
|
|
133
|
+
<Text>{{ brightness === null ? 'checking…' : `${Math.round(brightness * 100)}%` }}</Text>
|
|
134
|
+
<Pressable @press="setBrightnessAsync(0.5)">
|
|
135
|
+
<Text>Set to 50%</Text>
|
|
136
|
+
</Pressable>
|
|
137
|
+
<Text>{{ permissionStatus?.status ?? 'checking…' }}</Text>
|
|
138
|
+
<Pressable @press="requestPermission()">
|
|
139
|
+
<Text>Request permission</Text>
|
|
140
|
+
</Pressable>
|
|
141
|
+
</View>
|
|
142
|
+
</template>
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
```ts
|
|
146
|
+
// Angular — trimmed from examples/expo-angular/src/screens/BrightnessScreen.ts
|
|
147
|
+
import { Component, inject, signal } from '@angular/core';
|
|
148
|
+
import { Pressable, Text, View } from '@symbiote-native/angular';
|
|
149
|
+
import {
|
|
150
|
+
PermissionsService,
|
|
151
|
+
addBrightnessListener,
|
|
152
|
+
getBrightnessAsync,
|
|
153
|
+
setBrightnessAsync,
|
|
154
|
+
} from '@symbiote-native/brightness/angular';
|
|
155
|
+
|
|
156
|
+
@Component({
|
|
157
|
+
selector: 'BrightnessScreen',
|
|
158
|
+
standalone: true,
|
|
159
|
+
imports: [Pressable, Text, View],
|
|
160
|
+
template: `
|
|
161
|
+
<View>
|
|
162
|
+
<Text>{{ brightnessLabel() }}</Text>
|
|
163
|
+
<Pressable (press)="setBrightnessAsync(0.5)"><Text>Set to 50%</Text></Pressable>
|
|
164
|
+
<Text>{{ permissionStatus()?.status ?? 'checking…' }}</Text>
|
|
165
|
+
<Pressable (press)="permissionsService.request()"><Text>Request permission</Text></Pressable>
|
|
166
|
+
</View>
|
|
167
|
+
`,
|
|
168
|
+
})
|
|
169
|
+
export class BrightnessScreen {
|
|
170
|
+
private readonly permissionsService = inject(PermissionsService);
|
|
171
|
+
readonly permissionStatus = this.permissionsService.connect();
|
|
172
|
+
readonly brightness = signal<number | null>(null);
|
|
173
|
+
|
|
174
|
+
constructor() {
|
|
175
|
+
getBrightnessAsync().then(value => this.brightness.set(value));
|
|
176
|
+
addBrightnessListener(event => this.brightness.set(event.brightness));
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
brightnessLabel(): string {
|
|
180
|
+
const value = this.brightness();
|
|
181
|
+
return value === null ? 'checking…' : `${Math.round(value * 100)}%`;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
setBrightnessAsync(value: number): void {
|
|
185
|
+
setBrightnessAsync(value).then(() => getBrightnessAsync().then(result => this.brightness.set(result)));
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
The real demo screens (`examples/expo-react/screens/BrightnessScreen.tsx` and its Vue/Angular
|
|
191
|
+
twins in `examples/expo-vue-sfc`, `examples/expo-vue-tsx`, `examples/expo-angular`) additionally
|
|
192
|
+
gate an Android-only system-brightness-mode card behind `Platform.OS === 'android'` — see
|
|
193
|
+
[API](#api) below for that surface's full signatures.
|
|
194
|
+
|
|
195
|
+
## API
|
|
196
|
+
|
|
197
|
+
Mostly stateless async functions, plus one iOS-only listener and a permission surface that gets
|
|
198
|
+
its own lifecycle hook/composable/service (matching upstream's own `usePermissions`).
|
|
199
|
+
|
|
200
|
+
```ts
|
|
201
|
+
isAvailableAsync(): Promise<boolean>
|
|
202
|
+
// Whether the brightness API is available — !!native.getBrightnessAsync.
|
|
203
|
+
|
|
204
|
+
getBrightnessAsync(): Promise<number>
|
|
205
|
+
setBrightnessAsync(brightnessValue: number): Promise<void>
|
|
206
|
+
// The app-local screen brightness, 0..1 (clamped; NaN throws a TypeError).
|
|
207
|
+
|
|
208
|
+
getSystemBrightnessAsync(): Promise<number>
|
|
209
|
+
setSystemBrightnessAsync(brightnessValue: number): Promise<void>
|
|
210
|
+
// System-wide brightness. On iOS these delegate straight to get/setBrightnessAsync — there is
|
|
211
|
+
// no separate system-level value on that platform.
|
|
212
|
+
|
|
213
|
+
restoreSystemBrightnessAsync(): Promise<void>
|
|
214
|
+
isUsingSystemBrightnessAsync(): Promise<boolean>
|
|
215
|
+
getSystemBrightnessModeAsync(): Promise<BrightnessMode> // UNKNOWN / AUTOMATIC / MANUAL
|
|
216
|
+
setSystemBrightnessModeAsync(mode: BrightnessMode): Promise<void>
|
|
217
|
+
// Android only — every one of these is a no-op (or resolves a fixed fallback) on iOS/other
|
|
218
|
+
// platforms. setSystemBrightnessModeAsync(UNKNOWN) is also always a no-op, even on Android.
|
|
219
|
+
|
|
220
|
+
getPermissionsAsync(): Promise<PermissionResponse>
|
|
221
|
+
requestPermissionsAsync(): Promise<PermissionResponse>
|
|
222
|
+
// Plain passthroughs to the native module — no platform gating.
|
|
223
|
+
|
|
224
|
+
addBrightnessListener(listener: (event: BrightnessEvent) => void): EventSubscription
|
|
225
|
+
// @platform ios — fires only on iOS; never fires on Android or web.
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Plus `BrightnessMode` (enum: `UNKNOWN`/`AUTOMATIC`/`MANUAL`), `BrightnessEvent`, and
|
|
229
|
+
`PermissionStatus`/`PermissionResponse`/`PermissionExpiration` — the latter three re-exported
|
|
230
|
+
verbatim from `expo-modules-core`, never the `expo` meta-package.
|
|
231
|
+
|
|
232
|
+
```ts
|
|
233
|
+
import { getBrightnessAsync, setBrightnessAsync } from '@symbiote-native/brightness';
|
|
234
|
+
|
|
235
|
+
// framework-scoped entry points re-export the same free functions, plus a usePermissions
|
|
236
|
+
// hook/composable/service:
|
|
237
|
+
import { usePermissions } from '@symbiote-native/brightness/react';
|
|
238
|
+
import { usePermissions } from '@symbiote-native/brightness/vue';
|
|
239
|
+
import { PermissionsService } from '@symbiote-native/brightness/angular';
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
`usePermissions` (React/Vue) auto-fetches the current permission status on mount and returns a
|
|
243
|
+
`[status, request, get]` tuple (React) / `{ status, request, get }` object (Vue); Angular's
|
|
244
|
+
`PermissionsService.connect()` does the same, returning a readonly `Signal`:
|
|
245
|
+
|
|
246
|
+
```ts
|
|
247
|
+
// React
|
|
248
|
+
const [status, request] = usePermissions();
|
|
249
|
+
|
|
250
|
+
// Vue
|
|
251
|
+
const { status, request } = usePermissions();
|
|
252
|
+
|
|
253
|
+
// Angular
|
|
254
|
+
readonly status = inject(PermissionsService).connect();
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
## Platform notes
|
|
258
|
+
|
|
259
|
+
- **System-brightness surface is Android only.** `getSystemBrightnessAsync`/
|
|
260
|
+
`setSystemBrightnessAsync` fall back to the app-local `get`/`setBrightnessAsync` on iOS (there
|
|
261
|
+
is no separate system-level brightness there); `restoreSystemBrightnessAsync`/
|
|
262
|
+
`setSystemBrightnessModeAsync` no-op, `isUsingSystemBrightnessAsync` resolves `false`, and
|
|
263
|
+
`getSystemBrightnessModeAsync` resolves `BrightnessMode.UNKNOWN` on every other platform.
|
|
264
|
+
- **`addBrightnessListener` is iOS only.** It never fires on Android or web — an app relying on
|
|
265
|
+
it for cross-platform behavior needs its own fallback (e.g. polling `getBrightnessAsync`).
|
|
266
|
+
- **The iOS Simulator never round-trips brightness at all.** `UIScreen.main.brightness` — what
|
|
267
|
+
`getBrightnessAsync`/`setBrightnessAsync` read and write on iOS — isn't implemented by the
|
|
268
|
+
Simulator: `set` is a silent no-op and `get` always resolves a constant (observed `1.0`),
|
|
269
|
+
regardless of what was written. This is a long-standing Apple Simulator limitation reproducible
|
|
270
|
+
in any app, including stock Expo Go — not a bug in this wrapper. It only reproduces on a real
|
|
271
|
+
iOS device; Android's emulator has no equivalent gap (see the `symbiote-expo-native-module`
|
|
272
|
+
skill for the verified symptom and the underlying `BrightnessModule.swift` call).
|
|
273
|
+
|
|
274
|
+
## Test it
|
|
275
|
+
|
|
276
|
+
No Fabric/Descriptor angle at all — brightness is a pure async-function + listener + permission
|
|
277
|
+
surface, never a view. Tests inject a fake native-module object in place of the real
|
|
278
|
+
`requireNativeModule` resolution (`src/core/*.test.ts`, `src/{react,vue,angular}/**/*.test.{ts,tsx}`,
|
|
279
|
+
`vitest`), the same pattern `expo-brightness` itself uses upstream — no `installFabric()`, no
|
|
280
|
+
ViewConfig. Native rendering itself is verified on-device (see the parent
|
|
281
|
+
[README](../../README.md) for the project's testing model).
|
|
282
|
+
|
|
283
|
+
## Known gaps
|
|
284
|
+
|
|
285
|
+
- Not yet wired into the public, non-Expo `examples/react` canary — only in `examples/expo-react`
|
|
286
|
+
(and its Vue/Angular Expo twins, `examples/expo-vue-sfc`/`examples/expo-vue-tsx`/
|
|
287
|
+
`examples/expo-angular`, all of which already have full three-layer native wiring: Android
|
|
288
|
+
`MainApplication.kt` registration + `WRITE_SETTINGS`, plus the iOS Podfile/pod install).
|
|
289
|
+
- Tests exercise the JS layer only (fake native module, `vitest`) — no on-device/simulator
|
|
290
|
+
automated smoke test yet, only manual verification.
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export { PermissionsService } from './services/permissions.service';
|
|
2
|
+
export { isAvailableAsync, getBrightnessAsync, setBrightnessAsync, getSystemBrightnessAsync, setSystemBrightnessAsync, restoreSystemBrightnessAsync, isUsingSystemBrightnessAsync, getSystemBrightnessModeAsync, setSystemBrightnessModeAsync, getPermissionsAsync, requestPermissionsAsync, addBrightnessListener, BrightnessMode, PermissionStatus, type BrightnessEvent, type PermissionExpiration, type PermissionResponse, type EventSubscription, } from '../core';
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
// PermissionsService is the Angular-only lifecycle half; the free functions and event
|
|
2
|
+
// subscription plumbing all live in core, shared with React/Vue.
|
|
3
|
+
export { PermissionsService } from './services/permissions.service/index.js';
|
|
4
|
+
export { isAvailableAsync, getBrightnessAsync, setBrightnessAsync, getSystemBrightnessAsync, setSystemBrightnessAsync, restoreSystemBrightnessAsync, isUsingSystemBrightnessAsync, getSystemBrightnessModeAsync, setSystemBrightnessModeAsync, getPermissionsAsync, requestPermissionsAsync, addBrightnessListener, BrightnessMode, PermissionStatus, } from '../core/index.js';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type Signal } from '@angular/core';
|
|
2
|
+
import { type PermissionResponse } from '../../../core';
|
|
3
|
+
export declare class PermissionsService {
|
|
4
|
+
private readonly status;
|
|
5
|
+
connect(): Signal<PermissionResponse | null>;
|
|
6
|
+
get(): Promise<PermissionResponse>;
|
|
7
|
+
request(): Promise<PermissionResponse>;
|
|
8
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
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
|
+
// Angular twin of React's usePermissions hook and Vue's usePermissions composable.
|
|
36
|
+
// `connect()` auto-fetches once (no ongoing subscription to clean up, so no Injector/effect
|
|
37
|
+
// needed, unlike BatteryStateService) and returns a readonly signal; get()/request() are
|
|
38
|
+
// imperative one-shot methods.
|
|
39
|
+
//
|
|
40
|
+
// readonly status = inject(PermissionsService).connect();
|
|
41
|
+
// // template: {{ status()?.granted }}
|
|
42
|
+
import { Injectable, signal } from '@angular/core';
|
|
43
|
+
import { getPermissionsAsync, requestPermissionsAsync, } from '../../../core';
|
|
44
|
+
let PermissionsService = (() => {
|
|
45
|
+
let _classDecorators = [Injectable({ providedIn: 'root' })];
|
|
46
|
+
let _classDescriptor;
|
|
47
|
+
let _classExtraInitializers = [];
|
|
48
|
+
let _classThis;
|
|
49
|
+
var PermissionsService = class {
|
|
50
|
+
static { _classThis = this; }
|
|
51
|
+
static {
|
|
52
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
|
|
53
|
+
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
|
|
54
|
+
PermissionsService = _classThis = _classDescriptor.value;
|
|
55
|
+
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
56
|
+
__runInitializers(_classThis, _classExtraInitializers);
|
|
57
|
+
}
|
|
58
|
+
status = signal(null);
|
|
59
|
+
connect() {
|
|
60
|
+
if (this.status() === null) {
|
|
61
|
+
void this.get();
|
|
62
|
+
}
|
|
63
|
+
return this.status.asReadonly();
|
|
64
|
+
}
|
|
65
|
+
async get() {
|
|
66
|
+
const response = await getPermissionsAsync();
|
|
67
|
+
this.status.set(response);
|
|
68
|
+
return response;
|
|
69
|
+
}
|
|
70
|
+
async request() {
|
|
71
|
+
const response = await requestPermissionsAsync();
|
|
72
|
+
this.status.set(response);
|
|
73
|
+
return response;
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
return PermissionsService = _classThis;
|
|
77
|
+
})();
|
|
78
|
+
export { PermissionsService };
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { EventSubscription } from 'expo-modules-core';
|
|
2
|
+
import { BrightnessMode, type BrightnessEvent, type PermissionResponse } from './types';
|
|
3
|
+
/** Whether the brightness API is available on the current device. */
|
|
4
|
+
export declare function isAvailableAsync(): Promise<boolean>;
|
|
5
|
+
/** Gets the current brightness level of the device's main screen, between `0` and `1`. */
|
|
6
|
+
export declare function getBrightnessAsync(): Promise<number>;
|
|
7
|
+
/**
|
|
8
|
+
* Sets the current screen brightness. On iOS this only affects the screen while the app is
|
|
9
|
+
* foregrounded; on Android it persists until changed again.
|
|
10
|
+
*/
|
|
11
|
+
export declare function setBrightnessAsync(brightnessValue: number): Promise<void>;
|
|
12
|
+
/**
|
|
13
|
+
* Gets the system-wide brightness. On iOS the app-local and system brightness are the same
|
|
14
|
+
* value, so this delegates to `getBrightnessAsync`.
|
|
15
|
+
* @platform android
|
|
16
|
+
*/
|
|
17
|
+
export declare function getSystemBrightnessAsync(): Promise<number>;
|
|
18
|
+
/**
|
|
19
|
+
* Sets the system-wide brightness. On iOS this delegates to `setBrightnessAsync`, since there is
|
|
20
|
+
* no separate system-level brightness to set.
|
|
21
|
+
* @platform android
|
|
22
|
+
*/
|
|
23
|
+
export declare function setSystemBrightnessAsync(brightnessValue: number): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* Resets the system brightness to the value it had before this app started controlling it. A
|
|
26
|
+
* no-op on every platform except Android.
|
|
27
|
+
* @platform android
|
|
28
|
+
*/
|
|
29
|
+
export declare function restoreSystemBrightnessAsync(): Promise<void>;
|
|
30
|
+
/**
|
|
31
|
+
* Whether the app is currently controlling the system brightness (as opposed to only its own
|
|
32
|
+
* window's brightness). Always `false` on every platform except Android.
|
|
33
|
+
* @platform android
|
|
34
|
+
*/
|
|
35
|
+
export declare function isUsingSystemBrightnessAsync(): Promise<boolean>;
|
|
36
|
+
/**
|
|
37
|
+
* Gets the system brightness mode (`AUTOMATIC` / `MANUAL`). Always resolves `UNKNOWN` on every
|
|
38
|
+
* platform except Android.
|
|
39
|
+
* @platform android
|
|
40
|
+
*/
|
|
41
|
+
export declare function getSystemBrightnessModeAsync(): Promise<BrightnessMode>;
|
|
42
|
+
/**
|
|
43
|
+
* Sets the system brightness mode. A no-op on every platform except Android, and also a no-op
|
|
44
|
+
* when passed `BrightnessMode.UNKNOWN` (there is nothing meaningful to set it to).
|
|
45
|
+
* @platform android
|
|
46
|
+
*/
|
|
47
|
+
export declare function setSystemBrightnessModeAsync(brightnessMode: BrightnessMode): Promise<void>;
|
|
48
|
+
/** Checks user's permissions for accessing the system brightness. */
|
|
49
|
+
export declare function getPermissionsAsync(): Promise<PermissionResponse>;
|
|
50
|
+
/** Asks the user for permission to access the system brightness. */
|
|
51
|
+
export declare function requestPermissionsAsync(): Promise<PermissionResponse>;
|
|
52
|
+
/**
|
|
53
|
+
* Subscribes to brightness changes. Only fires on iOS — never on Android or web.
|
|
54
|
+
* @platform ios
|
|
55
|
+
*/
|
|
56
|
+
export declare function addBrightnessListener(listener: (event: BrightnessEvent) => void): EventSubscription;
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// Hand-ported from .vendors/expo/packages/expo-brightness/src/Brightness.ts (sdk-57). The
|
|
2
|
+
// system-brightness surface (mode, "using system brightness", restore) is Android-only —
|
|
3
|
+
// upstream branches every one of those on `Platform.OS !== 'android'` before ever reaching the
|
|
4
|
+
// native module, so those branches are preserved verbatim rather than guessed.
|
|
5
|
+
import { Platform, UnavailabilityError } from 'expo-modules-core';
|
|
6
|
+
import { expoBrightness } from './native-module.js';
|
|
7
|
+
import { BrightnessMode } from './types.js';
|
|
8
|
+
const NATIVE_MODULE_NAME = 'expo-brightness';
|
|
9
|
+
function clampBrightness(brightnessValue, callerName) {
|
|
10
|
+
const clamped = Math.max(0, Math.min(brightnessValue, 1));
|
|
11
|
+
if (Number.isNaN(clamped)) {
|
|
12
|
+
throw new TypeError(`${callerName} cannot be called with ${brightnessValue}`);
|
|
13
|
+
}
|
|
14
|
+
return clamped;
|
|
15
|
+
}
|
|
16
|
+
/** Whether the brightness API is available on the current device. */
|
|
17
|
+
export async function isAvailableAsync() {
|
|
18
|
+
return !!expoBrightness.getBrightnessAsync;
|
|
19
|
+
}
|
|
20
|
+
/** Gets the current brightness level of the device's main screen, between `0` and `1`. */
|
|
21
|
+
export async function getBrightnessAsync() {
|
|
22
|
+
if (!expoBrightness.getBrightnessAsync) {
|
|
23
|
+
throw new UnavailabilityError(NATIVE_MODULE_NAME, 'getBrightnessAsync');
|
|
24
|
+
}
|
|
25
|
+
return expoBrightness.getBrightnessAsync();
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Sets the current screen brightness. On iOS this only affects the screen while the app is
|
|
29
|
+
* foregrounded; on Android it persists until changed again.
|
|
30
|
+
*/
|
|
31
|
+
export async function setBrightnessAsync(brightnessValue) {
|
|
32
|
+
const clamped = clampBrightness(brightnessValue, 'setBrightnessAsync');
|
|
33
|
+
if (!expoBrightness.setBrightnessAsync) {
|
|
34
|
+
throw new UnavailabilityError(NATIVE_MODULE_NAME, 'setBrightnessAsync');
|
|
35
|
+
}
|
|
36
|
+
await expoBrightness.setBrightnessAsync(clamped);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Gets the system-wide brightness. On iOS the app-local and system brightness are the same
|
|
40
|
+
* value, so this delegates to `getBrightnessAsync`.
|
|
41
|
+
* @platform android
|
|
42
|
+
*/
|
|
43
|
+
export async function getSystemBrightnessAsync() {
|
|
44
|
+
if (Platform.OS !== 'android') {
|
|
45
|
+
return getBrightnessAsync();
|
|
46
|
+
}
|
|
47
|
+
if (!expoBrightness.getSystemBrightnessAsync) {
|
|
48
|
+
throw new UnavailabilityError(NATIVE_MODULE_NAME, 'getSystemBrightnessAsync');
|
|
49
|
+
}
|
|
50
|
+
return expoBrightness.getSystemBrightnessAsync();
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Sets the system-wide brightness. On iOS this delegates to `setBrightnessAsync`, since there is
|
|
54
|
+
* no separate system-level brightness to set.
|
|
55
|
+
* @platform android
|
|
56
|
+
*/
|
|
57
|
+
export async function setSystemBrightnessAsync(brightnessValue) {
|
|
58
|
+
const clamped = clampBrightness(brightnessValue, 'setSystemBrightnessAsync');
|
|
59
|
+
if (Platform.OS !== 'android') {
|
|
60
|
+
return setBrightnessAsync(clamped);
|
|
61
|
+
}
|
|
62
|
+
if (!expoBrightness.setSystemBrightnessAsync) {
|
|
63
|
+
throw new UnavailabilityError(NATIVE_MODULE_NAME, 'setSystemBrightnessAsync');
|
|
64
|
+
}
|
|
65
|
+
await expoBrightness.setSystemBrightnessAsync(clamped);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Resets the system brightness to the value it had before this app started controlling it. A
|
|
69
|
+
* no-op on every platform except Android.
|
|
70
|
+
* @platform android
|
|
71
|
+
*/
|
|
72
|
+
export async function restoreSystemBrightnessAsync() {
|
|
73
|
+
if (Platform.OS !== 'android') {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (!expoBrightness.restoreSystemBrightnessAsync) {
|
|
77
|
+
throw new UnavailabilityError(NATIVE_MODULE_NAME, 'restoreSystemBrightnessAsync');
|
|
78
|
+
}
|
|
79
|
+
await expoBrightness.restoreSystemBrightnessAsync();
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Whether the app is currently controlling the system brightness (as opposed to only its own
|
|
83
|
+
* window's brightness). Always `false` on every platform except Android.
|
|
84
|
+
* @platform android
|
|
85
|
+
*/
|
|
86
|
+
export async function isUsingSystemBrightnessAsync() {
|
|
87
|
+
if (Platform.OS !== 'android') {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
if (!expoBrightness.isUsingSystemBrightnessAsync) {
|
|
91
|
+
throw new UnavailabilityError(NATIVE_MODULE_NAME, 'isUsingSystemBrightnessAsync');
|
|
92
|
+
}
|
|
93
|
+
return expoBrightness.isUsingSystemBrightnessAsync();
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Gets the system brightness mode (`AUTOMATIC` / `MANUAL`). Always resolves `UNKNOWN` on every
|
|
97
|
+
* platform except Android.
|
|
98
|
+
* @platform android
|
|
99
|
+
*/
|
|
100
|
+
export async function getSystemBrightnessModeAsync() {
|
|
101
|
+
if (Platform.OS !== 'android') {
|
|
102
|
+
return BrightnessMode.UNKNOWN;
|
|
103
|
+
}
|
|
104
|
+
if (!expoBrightness.getSystemBrightnessModeAsync) {
|
|
105
|
+
throw new UnavailabilityError(NATIVE_MODULE_NAME, 'getSystemBrightnessModeAsync');
|
|
106
|
+
}
|
|
107
|
+
return expoBrightness.getSystemBrightnessModeAsync();
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Sets the system brightness mode. A no-op on every platform except Android, and also a no-op
|
|
111
|
+
* when passed `BrightnessMode.UNKNOWN` (there is nothing meaningful to set it to).
|
|
112
|
+
* @platform android
|
|
113
|
+
*/
|
|
114
|
+
export async function setSystemBrightnessModeAsync(brightnessMode) {
|
|
115
|
+
if (Platform.OS !== 'android' || brightnessMode === BrightnessMode.UNKNOWN) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
if (!expoBrightness.setSystemBrightnessModeAsync) {
|
|
119
|
+
throw new UnavailabilityError(NATIVE_MODULE_NAME, 'setSystemBrightnessModeAsync');
|
|
120
|
+
}
|
|
121
|
+
await expoBrightness.setSystemBrightnessModeAsync(brightnessMode);
|
|
122
|
+
}
|
|
123
|
+
/** Checks user's permissions for accessing the system brightness. */
|
|
124
|
+
export async function getPermissionsAsync() {
|
|
125
|
+
return expoBrightness.getPermissionsAsync();
|
|
126
|
+
}
|
|
127
|
+
/** Asks the user for permission to access the system brightness. */
|
|
128
|
+
export async function requestPermissionsAsync() {
|
|
129
|
+
return expoBrightness.requestPermissionsAsync();
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Subscribes to brightness changes. Only fires on iOS — never on Android or web.
|
|
133
|
+
* @platform ios
|
|
134
|
+
*/
|
|
135
|
+
export function addBrightnessListener(listener) {
|
|
136
|
+
return expoBrightness.addListener('Expo.brightnessDidChange', listener);
|
|
137
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { isAvailableAsync, getBrightnessAsync, setBrightnessAsync, getSystemBrightnessAsync, setSystemBrightnessAsync, restoreSystemBrightnessAsync, isUsingSystemBrightnessAsync, getSystemBrightnessModeAsync, setSystemBrightnessModeAsync, getPermissionsAsync, requestPermissionsAsync, addBrightnessListener, } from './brightness';
|
|
2
|
+
export { BrightnessMode, PermissionStatus, type BrightnessEvent, type PermissionExpiration, type PermissionResponse, } from './types';
|
|
3
|
+
export type { EventSubscription } from 'expo-modules-core';
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export { isAvailableAsync, getBrightnessAsync, setBrightnessAsync, getSystemBrightnessAsync, setSystemBrightnessAsync, restoreSystemBrightnessAsync, isUsingSystemBrightnessAsync, getSystemBrightnessModeAsync, setSystemBrightnessModeAsync, getPermissionsAsync, requestPermissionsAsync, addBrightnessListener, } from './brightness.js';
|
|
2
|
+
export { BrightnessMode, PermissionStatus, } from './types.js';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { EventSubscription } from 'expo-modules-core';
|
|
2
|
+
import type { BrightnessEvent, BrightnessMode, PermissionResponse } from './types';
|
|
3
|
+
export type INativeBrightnessModule = {
|
|
4
|
+
getBrightnessAsync?(): Promise<number>;
|
|
5
|
+
setBrightnessAsync?(value: number): Promise<void>;
|
|
6
|
+
getSystemBrightnessAsync?(): Promise<number>;
|
|
7
|
+
setSystemBrightnessAsync?(value: number): Promise<void>;
|
|
8
|
+
restoreSystemBrightnessAsync?(): Promise<void>;
|
|
9
|
+
isUsingSystemBrightnessAsync?(): Promise<boolean>;
|
|
10
|
+
getSystemBrightnessModeAsync?(): Promise<BrightnessMode>;
|
|
11
|
+
setSystemBrightnessModeAsync?(mode: BrightnessMode): Promise<void>;
|
|
12
|
+
getPermissionsAsync(): Promise<PermissionResponse>;
|
|
13
|
+
requestPermissionsAsync(): Promise<PermissionResponse>;
|
|
14
|
+
addListener(eventName: 'Expo.brightnessDidChange', listener: (event: BrightnessEvent) => void): EventSubscription;
|
|
15
|
+
};
|
|
16
|
+
export declare const expoBrightness: INativeBrightnessModule;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { PermissionStatus, type PermissionExpiration, type PermissionResponse } from 'expo-modules-core';
|
|
2
|
+
export { PermissionStatus, type PermissionExpiration, type PermissionResponse };
|
|
3
|
+
/** Enum representing the brightness mode of the device. */
|
|
4
|
+
export declare enum BrightnessMode {
|
|
5
|
+
/** Returned when brightness mode cannot be determined. */
|
|
6
|
+
UNKNOWN = 0,
|
|
7
|
+
/** Automatic brightness mode, tracking the ambient light sensor. */
|
|
8
|
+
AUTOMATIC = 1,
|
|
9
|
+
/** Manual brightness mode, set by the user or by `setSystemBrightnessAsync`. */
|
|
10
|
+
MANUAL = 2
|
|
11
|
+
}
|
|
12
|
+
/** Fired when the screen brightness changes, on iOS only. */
|
|
13
|
+
export type BrightnessEvent = {
|
|
14
|
+
/** The current brightness value between `0` and `1`, inclusive. */
|
|
15
|
+
brightness: number;
|
|
16
|
+
};
|