rn-system-bar 3.1.0 → 3.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/src/main/AndroidManifest.xml +9 -0
- package/android/src/main/java/com/systembar/SystemBarModule.kt +120 -294
- package/index.ts +1 -8
- package/lib/index.d.ts +1 -1
- package/lib/index.js +1 -5
- package/lib/src/SystemBar.d.ts +3 -56
- package/lib/src/SystemBar.js +24 -140
- package/lib/src/types.d.ts +5 -32
- package/lib/src/types.js +1 -1
- package/lib/src/useSystemBar.d.ts +1 -31
- package/lib/src/useSystemBar.js +7 -98
- package/package.json +3 -2
- package/rn-system-bar.podspec +1 -1
- package/src/SystemBar.ts +24 -154
- package/src/types.ts +6 -64
- package/src/useSystemBar.ts +11 -111
package/src/SystemBar.ts
CHANGED
|
@@ -1,27 +1,20 @@
|
|
|
1
1
|
// ─────────────────────────────────────────────
|
|
2
|
-
// rn-system-bar · SystemBar.ts
|
|
3
|
-
// All features — Android + iOS
|
|
2
|
+
// rn-system-bar · SystemBar.ts
|
|
4
3
|
// ─────────────────────────────────────────────
|
|
5
4
|
|
|
6
|
-
import { NativeModules, Platform, StatusBar
|
|
5
|
+
import { NativeModules, Platform, StatusBar } from "react-native";
|
|
7
6
|
|
|
8
7
|
import type {
|
|
9
|
-
BatteryInfo,
|
|
10
|
-
FontScaleInfo,
|
|
11
|
-
HapticPattern,
|
|
12
8
|
NavigationBarBehavior,
|
|
13
9
|
NavigationBarButtonStyle,
|
|
14
10
|
NavigationBarStyle,
|
|
15
11
|
NavigationBarVisibility,
|
|
16
|
-
NetworkInfo,
|
|
17
12
|
Orientation,
|
|
18
13
|
ScreencastInfo,
|
|
19
14
|
StatusBarStyle,
|
|
20
15
|
VolumeStream,
|
|
21
16
|
} from "./types";
|
|
22
17
|
|
|
23
|
-
// expo-navigation-bar — peer dep, loaded at runtime
|
|
24
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
25
18
|
const NavBar = (() => {
|
|
26
19
|
try { return require("expo-navigation-bar"); }
|
|
27
20
|
catch { return null; }
|
|
@@ -30,7 +23,6 @@ const NavBar = (() => {
|
|
|
30
23
|
const { SystemBar: Native } = NativeModules;
|
|
31
24
|
|
|
32
25
|
const isAndroid = Platform.OS === "android";
|
|
33
|
-
const isIOS = Platform.OS === "ios";
|
|
34
26
|
|
|
35
27
|
const androidOnly = (name: string): boolean => {
|
|
36
28
|
if (!isAndroid) {
|
|
@@ -45,12 +37,15 @@ const checkNative = () => {
|
|
|
45
37
|
};
|
|
46
38
|
|
|
47
39
|
// ═══════════════════════════════════════════════
|
|
48
|
-
// NAVIGATION BAR (Android
|
|
40
|
+
// NAVIGATION BAR (Android-only)
|
|
41
|
+
// Color → native Window API (edge-to-edge safe)
|
|
42
|
+
// Others → expo-navigation-bar (work in edge-to-edge)
|
|
49
43
|
// ═══════════════════════════════════════════════
|
|
50
44
|
|
|
51
|
-
export const setNavigationBarColor = (color: string):
|
|
52
|
-
if (!androidOnly("setNavigationBarColor")) return
|
|
53
|
-
|
|
45
|
+
export const setNavigationBarColor = (color: string): void => {
|
|
46
|
+
if (!androidOnly("setNavigationBarColor")) return;
|
|
47
|
+
checkNative();
|
|
48
|
+
Native.setNavigationBarColor(color);
|
|
54
49
|
};
|
|
55
50
|
|
|
56
51
|
export const setNavigationBarVisibility = (mode: NavigationBarVisibility): Promise<void> => {
|
|
@@ -93,11 +88,18 @@ export const setStatusBarVisibility = (visible: boolean, animated = false): void
|
|
|
93
88
|
|
|
94
89
|
// ═══════════════════════════════════════════════
|
|
95
90
|
// BRIGHTNESS
|
|
91
|
+
//
|
|
92
|
+
// setBrightness → updates BOTH window brightness (instant)
|
|
93
|
+
// AND system brightness (persisted).
|
|
94
|
+
// On first call: opens WRITE_SETTINGS if not granted.
|
|
95
|
+
//
|
|
96
|
+
// getBrightness → reads SYSTEM brightness so slider always
|
|
97
|
+
// matches the device brightness bar.
|
|
96
98
|
// ═══════════════════════════════════════════════
|
|
97
99
|
|
|
98
100
|
export const setBrightness = (level: number): void => {
|
|
99
101
|
checkNative();
|
|
100
|
-
Native.setBrightness(Math.max(0, Math.min(1, level)));
|
|
102
|
+
Native.setBrightness(Math.max(0.01, Math.min(1, level)));
|
|
101
103
|
};
|
|
102
104
|
|
|
103
105
|
export const getBrightness = (): Promise<number> => {
|
|
@@ -140,121 +142,30 @@ export const immersiveMode = (enable: boolean): void => {
|
|
|
140
142
|
Native.immersiveMode(enable);
|
|
141
143
|
};
|
|
142
144
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
// ═══════════════════════════════════════════════
|
|
146
|
-
|
|
147
|
-
export const setOrientation = (mode: Orientation): void => {
|
|
148
|
-
checkNative();
|
|
149
|
-
Native.setOrientation(mode);
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
// ═══════════════════════════════════════════════
|
|
153
|
-
// 🆕 NETWORK INFO
|
|
154
|
-
// ═══════════════════════════════════════════════
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Get current network connection info.
|
|
158
|
-
* Includes: type, isConnected, isAirplaneMode, ssid, cellularGeneration
|
|
159
|
-
*/
|
|
160
|
-
export const getNetworkInfo = (): Promise<NetworkInfo> => {
|
|
161
|
-
checkNative();
|
|
162
|
-
return Native.getNetworkInfo();
|
|
163
|
-
};
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* Subscribe to network changes.
|
|
167
|
-
* @returns unsubscribe function — call it in useEffect cleanup
|
|
168
|
-
* @example
|
|
169
|
-
* const unsub = onNetworkChange((info) => console.log(info));
|
|
170
|
-
* return () => unsub();
|
|
171
|
-
*/
|
|
172
|
-
export const onNetworkChange = (
|
|
173
|
-
callback: (info: NetworkInfo) => void
|
|
174
|
-
): (() => void) => {
|
|
145
|
+
export const setSecureScreen = (enable: boolean): void => {
|
|
146
|
+
if (!androidOnly("setSecureScreen")) return;
|
|
175
147
|
checkNative();
|
|
176
|
-
|
|
177
|
-
const { DeviceEventEmitter } = require("react-native");
|
|
178
|
-
Native.startNetworkListener();
|
|
179
|
-
const sub = DeviceEventEmitter.addListener("SystemBar_NetworkChange", callback);
|
|
180
|
-
return () => {
|
|
181
|
-
sub.remove();
|
|
182
|
-
Native.stopNetworkListener();
|
|
183
|
-
};
|
|
148
|
+
Native.setSecureScreen(enable);
|
|
184
149
|
};
|
|
185
150
|
|
|
186
151
|
// ═══════════════════════════════════════════════
|
|
187
|
-
//
|
|
152
|
+
// ORIENTATION
|
|
188
153
|
// ═══════════════════════════════════════════════
|
|
189
154
|
|
|
190
|
-
|
|
191
|
-
* Get current battery info: level (0–100), state, isCharging, isLow.
|
|
192
|
-
*/
|
|
193
|
-
export const getBatteryInfo = (): Promise<BatteryInfo> => {
|
|
194
|
-
checkNative();
|
|
195
|
-
return Native.getBatteryInfo();
|
|
196
|
-
};
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* Subscribe to battery level / state changes.
|
|
200
|
-
* @returns unsubscribe function
|
|
201
|
-
*/
|
|
202
|
-
export const onBatteryChange = (
|
|
203
|
-
callback: (info: BatteryInfo) => void
|
|
204
|
-
): (() => void) => {
|
|
155
|
+
export const setOrientation = (mode: Orientation): void => {
|
|
205
156
|
checkNative();
|
|
206
|
-
|
|
207
|
-
Native.startBatteryListener();
|
|
208
|
-
const sub = DeviceEventEmitter.addListener("SystemBar_BatteryChange", callback);
|
|
209
|
-
return () => {
|
|
210
|
-
sub.remove();
|
|
211
|
-
Native.stopBatteryListener();
|
|
212
|
-
};
|
|
213
|
-
};
|
|
214
|
-
|
|
215
|
-
// ═══════════════════════════════════════════════
|
|
216
|
-
// 🆕 HAPTIC FEEDBACK
|
|
217
|
-
// ═══════════════════════════════════════════════
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* Trigger haptic feedback.
|
|
221
|
-
*
|
|
222
|
-
* iOS: Uses UIImpactFeedbackGenerator / UINotificationFeedbackGenerator.
|
|
223
|
-
* Android: Uses Vibrator / VibrationEffect (API 26+).
|
|
224
|
-
*
|
|
225
|
-
* @param pattern "light" | "medium" | "heavy" | "success" | "warning" | "error" | "selection"
|
|
226
|
-
*/
|
|
227
|
-
export const haptic = (pattern: HapticPattern): void => {
|
|
228
|
-
if (isIOS) {
|
|
229
|
-
checkNative();
|
|
230
|
-
Native.haptic(pattern);
|
|
231
|
-
return;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
// Android fallback using RN's built-in Vibration API
|
|
235
|
-
// when native module is unavailable
|
|
236
|
-
if (isAndroid) {
|
|
237
|
-
checkNative();
|
|
238
|
-
Native.haptic(pattern);
|
|
239
|
-
}
|
|
157
|
+
Native.setOrientation(mode);
|
|
240
158
|
};
|
|
241
159
|
|
|
242
160
|
// ═══════════════════════════════════════════════
|
|
243
|
-
//
|
|
161
|
+
// SCREENCAST
|
|
244
162
|
// ═══════════════════════════════════════════════
|
|
245
163
|
|
|
246
|
-
/**
|
|
247
|
-
* Check if screen is currently being cast or mirrored.
|
|
248
|
-
*/
|
|
249
164
|
export const getScreencastInfo = (): Promise<ScreencastInfo> => {
|
|
250
165
|
checkNative();
|
|
251
166
|
return Native.getScreencastInfo();
|
|
252
167
|
};
|
|
253
168
|
|
|
254
|
-
/**
|
|
255
|
-
* Subscribe to screencast state changes (started / stopped).
|
|
256
|
-
* @returns unsubscribe function
|
|
257
|
-
*/
|
|
258
169
|
export const onScreencastChange = (
|
|
259
170
|
callback: (info: ScreencastInfo) => void
|
|
260
171
|
): (() => void) => {
|
|
@@ -267,44 +178,3 @@ export const onScreencastChange = (
|
|
|
267
178
|
Native.stopScreencastListener();
|
|
268
179
|
};
|
|
269
180
|
};
|
|
270
|
-
|
|
271
|
-
/**
|
|
272
|
-
* Prevent screen content from appearing in screenshots / recordings.
|
|
273
|
-
* Useful for sensitive screens (payments, passwords).
|
|
274
|
-
* @platform android
|
|
275
|
-
*/
|
|
276
|
-
export const setSecureScreen = (enable: boolean): void => {
|
|
277
|
-
if (!androidOnly("setSecureScreen")) return;
|
|
278
|
-
checkNative();
|
|
279
|
-
Native.setSecureScreen(enable);
|
|
280
|
-
};
|
|
281
|
-
|
|
282
|
-
// ═══════════════════════════════════════════════
|
|
283
|
-
// 🆕 FONT SCALE / DISPLAY INFO
|
|
284
|
-
// ═══════════════════════════════════════════════
|
|
285
|
-
|
|
286
|
-
/**
|
|
287
|
-
* Get current system font scale and display density.
|
|
288
|
-
* Useful for adapting layout to accessibility settings.
|
|
289
|
-
*/
|
|
290
|
-
export const getFontScaleInfo = (): Promise<FontScaleInfo> => {
|
|
291
|
-
checkNative();
|
|
292
|
-
return Native.getFontScaleInfo();
|
|
293
|
-
};
|
|
294
|
-
|
|
295
|
-
/**
|
|
296
|
-
* Subscribe to font scale changes (user changes system text size).
|
|
297
|
-
* @returns unsubscribe function
|
|
298
|
-
*/
|
|
299
|
-
export const onFontScaleChange = (
|
|
300
|
-
callback: (info: FontScaleInfo) => void
|
|
301
|
-
): (() => void) => {
|
|
302
|
-
checkNative();
|
|
303
|
-
const { DeviceEventEmitter } = require("react-native");
|
|
304
|
-
Native.startFontScaleListener();
|
|
305
|
-
const sub = DeviceEventEmitter.addListener("SystemBar_FontScaleChange", callback);
|
|
306
|
-
return () => {
|
|
307
|
-
sub.remove();
|
|
308
|
-
Native.stopFontScaleListener();
|
|
309
|
-
};
|
|
310
|
-
};
|
package/src/types.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
// ─────────────────────────────────────────────
|
|
2
|
-
// rn-system-bar · types.ts
|
|
2
|
+
// rn-system-bar · types.ts
|
|
3
3
|
// ─────────────────────────────────────────────
|
|
4
4
|
|
|
5
|
-
// ── Navigation Bar ────────────────────────────
|
|
6
5
|
export type NavigationBarBehavior =
|
|
7
6
|
| "overlay-swipe"
|
|
8
7
|
| "inset-swipe"
|
|
@@ -11,11 +10,8 @@ export type NavigationBarBehavior =
|
|
|
11
10
|
export type NavigationBarButtonStyle = "light" | "dark";
|
|
12
11
|
export type NavigationBarStyle = "auto" | "inverted" | "light" | "dark";
|
|
13
12
|
export type NavigationBarVisibility = "visible" | "hidden";
|
|
14
|
-
|
|
15
|
-
// ── Status Bar ────────────────────────────────
|
|
16
13
|
export type StatusBarStyle = "light" | "dark";
|
|
17
14
|
|
|
18
|
-
// ── Orientation ───────────────────────────────
|
|
19
15
|
export type Orientation =
|
|
20
16
|
| "portrait"
|
|
21
17
|
| "landscape"
|
|
@@ -23,7 +19,6 @@ export type Orientation =
|
|
|
23
19
|
| "landscape-right"
|
|
24
20
|
| "auto";
|
|
25
21
|
|
|
26
|
-
// ── Volume ────────────────────────────────────
|
|
27
22
|
export type VolumeStream =
|
|
28
23
|
| "music"
|
|
29
24
|
| "ring"
|
|
@@ -31,67 +26,14 @@ export type VolumeStream =
|
|
|
31
26
|
| "alarm"
|
|
32
27
|
| "system";
|
|
33
28
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
| "ethernet"
|
|
39
|
-
| "none"
|
|
40
|
-
| "unknown";
|
|
41
|
-
|
|
42
|
-
export interface NetworkInfo {
|
|
43
|
-
/** Current connection type */
|
|
44
|
-
type: NetworkType;
|
|
45
|
-
/** true if any network is reachable */
|
|
46
|
-
isConnected: boolean;
|
|
47
|
-
/** true if in airplane mode */
|
|
48
|
-
isAirplaneMode: boolean;
|
|
49
|
-
/** WiFi SSID — Android only; null on iOS / when not connected */
|
|
50
|
-
ssid: string | null;
|
|
51
|
-
/** Cellular network generation: "2G" | "3G" | "4G" | "5G" | null */
|
|
52
|
-
cellularGeneration: "2G" | "3G" | "4G" | "5G" | null;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// ── Battery ───────────────────────────────────
|
|
56
|
-
export type BatteryState =
|
|
57
|
-
| "charging"
|
|
58
|
-
| "discharging"
|
|
59
|
-
| "full"
|
|
60
|
-
| "unknown";
|
|
61
|
-
|
|
62
|
-
export interface BatteryInfo {
|
|
63
|
-
/** 0–100 */
|
|
64
|
-
level: number;
|
|
65
|
-
/** Current charging state */
|
|
66
|
-
state: BatteryState;
|
|
67
|
-
/** Shorthand: is it plugged in? */
|
|
68
|
-
isCharging: boolean;
|
|
69
|
-
/** Low battery threshold (<= 20%) */
|
|
70
|
-
isLow: boolean;
|
|
29
|
+
export interface ScreencastDisplay {
|
|
30
|
+
id: number;
|
|
31
|
+
name: string;
|
|
32
|
+
isValid: boolean;
|
|
71
33
|
}
|
|
72
34
|
|
|
73
|
-
// ── Haptics ───────────────────────────────────
|
|
74
|
-
export type HapticPattern =
|
|
75
|
-
| "light" // subtle tap
|
|
76
|
-
| "medium" // standard tap
|
|
77
|
-
| "heavy" // strong tap
|
|
78
|
-
| "success" // double bump — success feedback
|
|
79
|
-
| "warning" // single bump — warning feedback
|
|
80
|
-
| "error" // triple bump — error feedback
|
|
81
|
-
| "selection"; // light tick — selection change
|
|
82
|
-
|
|
83
|
-
// ── Screencast ────────────────────────────────
|
|
84
35
|
export interface ScreencastInfo {
|
|
85
|
-
/** true if screen is currently being cast / mirrored */
|
|
86
36
|
isCasting: boolean;
|
|
87
|
-
/** Name of the display being cast to, or null */
|
|
88
37
|
displayName: string | null;
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
// ── Font Scale ────────────────────────────────
|
|
92
|
-
export interface FontScaleInfo {
|
|
93
|
-
/** System font scale multiplier, e.g. 1.0 = default, 1.3 = large */
|
|
94
|
-
fontScale: number;
|
|
95
|
-
/** Display density (Android: dp ratio, iOS: scale) */
|
|
96
|
-
density: number;
|
|
38
|
+
displays: ScreencastDisplay[];
|
|
97
39
|
}
|
package/src/useSystemBar.ts
CHANGED
|
@@ -1,19 +1,13 @@
|
|
|
1
1
|
// ─────────────────────────────────────────────
|
|
2
|
-
// rn-system-bar · useSystemBar.ts
|
|
3
|
-
// Hooks: useSystemBar, useBattery, useNetwork,
|
|
4
|
-
// useScreencast, useFontScale
|
|
2
|
+
// rn-system-bar · useSystemBar.ts
|
|
5
3
|
// ─────────────────────────────────────────────
|
|
6
4
|
|
|
7
5
|
import { useEffect, useRef, useState } from "react";
|
|
8
6
|
import type {
|
|
9
|
-
BatteryInfo,
|
|
10
|
-
FontScaleInfo,
|
|
11
|
-
HapticPattern,
|
|
12
7
|
NavigationBarBehavior,
|
|
13
8
|
NavigationBarButtonStyle,
|
|
14
9
|
NavigationBarStyle,
|
|
15
10
|
NavigationBarVisibility,
|
|
16
|
-
NetworkInfo,
|
|
17
11
|
Orientation,
|
|
18
12
|
ScreencastInfo,
|
|
19
13
|
StatusBarStyle,
|
|
@@ -21,7 +15,8 @@ import type {
|
|
|
21
15
|
import * as SystemBar from "./SystemBar";
|
|
22
16
|
|
|
23
17
|
// ─────────────────────────────────────────────
|
|
24
|
-
// useSystemBar — apply config safely
|
|
18
|
+
// useSystemBar — apply system bar config safely
|
|
19
|
+
// All async calls run inside useEffect — no Suspense crash
|
|
25
20
|
// ─────────────────────────────────────────────
|
|
26
21
|
export interface SystemBarConfig {
|
|
27
22
|
navigationBarColor?: string;
|
|
@@ -51,11 +46,11 @@ export const useSystemBar = (config: SystemBarConfig) => {
|
|
|
51
46
|
const apply = async () => {
|
|
52
47
|
const p: Promise<void>[] = [];
|
|
53
48
|
|
|
54
|
-
if (config.navigationBarColor
|
|
55
|
-
if (config.navigationBarVisibility
|
|
49
|
+
if (config.navigationBarColor !== undefined) SystemBar.setNavigationBarColor(config.navigationBarColor);
|
|
50
|
+
if (config.navigationBarVisibility !== undefined) p.push(SystemBar.setNavigationBarVisibility(config.navigationBarVisibility));
|
|
56
51
|
if (config.navigationBarButtonStyle !== undefined) p.push(SystemBar.setNavigationBarButtonStyle(config.navigationBarButtonStyle));
|
|
57
|
-
if (config.navigationBarStyle
|
|
58
|
-
if (config.navigationBarBehavior
|
|
52
|
+
if (config.navigationBarStyle !== undefined) p.push(SystemBar.setNavigationBarStyle(config.navigationBarStyle));
|
|
53
|
+
if (config.navigationBarBehavior !== undefined) p.push(SystemBar.setNavigationBarBehavior(config.navigationBarBehavior));
|
|
59
54
|
|
|
60
55
|
await Promise.allSettled(p);
|
|
61
56
|
|
|
@@ -77,115 +72,20 @@ export const useSystemBar = (config: SystemBarConfig) => {
|
|
|
77
72
|
};
|
|
78
73
|
|
|
79
74
|
// ─────────────────────────────────────────────
|
|
80
|
-
//
|
|
75
|
+
// useScreencast
|
|
81
76
|
// ─────────────────────────────────────────────
|
|
82
|
-
/**
|
|
83
|
-
* Reactive battery info. Auto-updates on level/state changes.
|
|
84
|
-
* @example
|
|
85
|
-
* const { level, isCharging, isLow, state } = useBattery();
|
|
86
|
-
*/
|
|
87
|
-
export const useBattery = () => {
|
|
88
|
-
const [info, setInfo] = useState<BatteryInfo>({
|
|
89
|
-
level: -1,
|
|
90
|
-
state: "unknown",
|
|
91
|
-
isCharging: false,
|
|
92
|
-
isLow: false,
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
useEffect(() => {
|
|
96
|
-
let unsub: (() => void) | undefined;
|
|
97
|
-
SystemBar.getBatteryInfo().then(setInfo).catch(() => {});
|
|
98
|
-
unsub = SystemBar.onBatteryChange(setInfo);
|
|
99
|
-
return () => unsub?.();
|
|
100
|
-
}, []);
|
|
101
|
-
|
|
102
|
-
return info;
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
// ─────────────────────────────────────────────
|
|
106
|
-
// 🆕 useNetwork
|
|
107
|
-
// ─────────────────────────────────────────────
|
|
108
|
-
/**
|
|
109
|
-
* Reactive network info. Auto-updates on connection changes.
|
|
110
|
-
* @example
|
|
111
|
-
* const { isConnected, type, isAirplaneMode } = useNetwork();
|
|
112
|
-
*/
|
|
113
|
-
export const useNetwork = () => {
|
|
114
|
-
const [info, setInfo] = useState<NetworkInfo>({
|
|
115
|
-
type: "unknown",
|
|
116
|
-
isConnected: false,
|
|
117
|
-
isAirplaneMode: false,
|
|
118
|
-
ssid: null,
|
|
119
|
-
cellularGeneration: null,
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
useEffect(() => {
|
|
123
|
-
let unsub: (() => void) | undefined;
|
|
124
|
-
SystemBar.getNetworkInfo().then(setInfo).catch(() => {});
|
|
125
|
-
unsub = SystemBar.onNetworkChange(setInfo);
|
|
126
|
-
return () => unsub?.();
|
|
127
|
-
}, []);
|
|
128
|
-
|
|
129
|
-
return info;
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
// ─────────────────────────────────────────────
|
|
133
|
-
// 🆕 useScreencast
|
|
134
|
-
// ─────────────────────────────────────────────
|
|
135
|
-
/**
|
|
136
|
-
* Reactive screencast detection.
|
|
137
|
-
* @example
|
|
138
|
-
* const { isCasting, displayName } = useScreencast();
|
|
139
|
-
*/
|
|
140
77
|
export const useScreencast = () => {
|
|
141
78
|
const [info, setInfo] = useState<ScreencastInfo>({
|
|
142
79
|
isCasting: false,
|
|
143
80
|
displayName: null,
|
|
81
|
+
displays: [],
|
|
144
82
|
});
|
|
145
83
|
|
|
146
84
|
useEffect(() => {
|
|
147
|
-
let unsub: (() => void) | undefined;
|
|
148
85
|
SystemBar.getScreencastInfo().then(setInfo).catch(() => {});
|
|
149
|
-
unsub = SystemBar.onScreencastChange(setInfo);
|
|
150
|
-
return () => unsub
|
|
86
|
+
const unsub = SystemBar.onScreencastChange(setInfo);
|
|
87
|
+
return () => unsub();
|
|
151
88
|
}, []);
|
|
152
89
|
|
|
153
90
|
return info;
|
|
154
91
|
};
|
|
155
|
-
|
|
156
|
-
// ─────────────────────────────────────────────
|
|
157
|
-
// 🆕 useFontScale
|
|
158
|
-
// ─────────────────────────────────────────────
|
|
159
|
-
/**
|
|
160
|
-
* Reactive font scale info. Updates when user changes system text size.
|
|
161
|
-
* @example
|
|
162
|
-
* const { fontScale, density } = useFontScale();
|
|
163
|
-
*/
|
|
164
|
-
export const useFontScale = () => {
|
|
165
|
-
const [info, setInfo] = useState<FontScaleInfo>({
|
|
166
|
-
fontScale: 1.0,
|
|
167
|
-
density: 1.0,
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
useEffect(() => {
|
|
171
|
-
let unsub: (() => void) | undefined;
|
|
172
|
-
SystemBar.getFontScaleInfo().then(setInfo).catch(() => {});
|
|
173
|
-
unsub = SystemBar.onFontScaleChange(setInfo);
|
|
174
|
-
return () => unsub?.();
|
|
175
|
-
}, []);
|
|
176
|
-
|
|
177
|
-
return info;
|
|
178
|
-
};
|
|
179
|
-
|
|
180
|
-
// ─────────────────────────────────────────────
|
|
181
|
-
// 🆕 useHaptic
|
|
182
|
-
// ─────────────────────────────────────────────
|
|
183
|
-
/**
|
|
184
|
-
* Returns a stable haptic trigger function.
|
|
185
|
-
* @example
|
|
186
|
-
* const triggerHaptic = useHaptic();
|
|
187
|
-
* <Button onPress={() => triggerHaptic("success")} />
|
|
188
|
-
*/
|
|
189
|
-
export const useHaptic = () => {
|
|
190
|
-
return (pattern: HapticPattern) => SystemBar.haptic(pattern);
|
|
191
|
-
};
|