rn-system-bar 3.0.3 → 3.1.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/android/src/main/AndroidManifest.xml +9 -0
- package/android/src/main/java/com/systembar/SystemBarModule.kt +145 -56
- package/android/src/main/java/com/systembar/SystemBarPackage.kt +2 -1
- package/index.ts +2 -0
- package/ios/SystemBarModule.m +32 -12
- package/ios/SystemBarModule.swift +241 -83
- package/lib/index.d.ts +4 -0
- package/lib/index.js +25 -0
- package/lib/specs/NativeSystemBar.d.ts +21 -0
- package/lib/specs/NativeSystemBar.js +8 -0
- package/lib/src/SystemBar.d.ts +20 -0
- package/lib/src/SystemBar.js +176 -0
- package/lib/src/types.d.ts +11 -0
- package/lib/src/types.js +5 -0
- package/lib/src/useSystemBar.d.ts +19 -0
- package/lib/src/useSystemBar.js +102 -0
- package/package.json +15 -4
- package/src/SystemBar.ts +82 -135
- package/src/types.ts +10 -48
- package/src/useSystemBar.ts +90 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { NavigationBarBehavior, NavigationBarButtonStyle, NavigationBarStyle, NavigationBarVisibility, Orientation, ScreencastInfo, StatusBarStyle } from "./types";
|
|
2
|
+
export interface SystemBarConfig {
|
|
3
|
+
navigationBarColor?: string;
|
|
4
|
+
navigationBarVisibility?: NavigationBarVisibility;
|
|
5
|
+
navigationBarButtonStyle?: NavigationBarButtonStyle;
|
|
6
|
+
navigationBarStyle?: NavigationBarStyle;
|
|
7
|
+
navigationBarBehavior?: NavigationBarBehavior;
|
|
8
|
+
statusBarColor?: string;
|
|
9
|
+
statusBarStyle?: StatusBarStyle;
|
|
10
|
+
statusBarVisible?: boolean;
|
|
11
|
+
statusBarAnimated?: boolean;
|
|
12
|
+
keepScreenOn?: boolean;
|
|
13
|
+
immersiveMode?: boolean;
|
|
14
|
+
brightness?: number;
|
|
15
|
+
orientation?: Orientation;
|
|
16
|
+
secureScreen?: boolean;
|
|
17
|
+
}
|
|
18
|
+
export declare const useSystemBar: (config: SystemBarConfig) => void;
|
|
19
|
+
export declare const useScreencast: () => ScreencastInfo;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ─────────────────────────────────────────────
|
|
3
|
+
// rn-system-bar · useSystemBar.ts
|
|
4
|
+
// ─────────────────────────────────────────────
|
|
5
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
8
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
9
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
10
|
+
}
|
|
11
|
+
Object.defineProperty(o, k2, desc);
|
|
12
|
+
}) : (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
o[k2] = m[k];
|
|
15
|
+
}));
|
|
16
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
17
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
18
|
+
}) : function(o, v) {
|
|
19
|
+
o["default"] = v;
|
|
20
|
+
});
|
|
21
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
22
|
+
var ownKeys = function(o) {
|
|
23
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
24
|
+
var ar = [];
|
|
25
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
26
|
+
return ar;
|
|
27
|
+
};
|
|
28
|
+
return ownKeys(o);
|
|
29
|
+
};
|
|
30
|
+
return function (mod) {
|
|
31
|
+
if (mod && mod.__esModule) return mod;
|
|
32
|
+
var result = {};
|
|
33
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
34
|
+
__setModuleDefault(result, mod);
|
|
35
|
+
return result;
|
|
36
|
+
};
|
|
37
|
+
})();
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.useScreencast = exports.useSystemBar = void 0;
|
|
40
|
+
const react_1 = require("react");
|
|
41
|
+
const SystemBar = __importStar(require("./SystemBar"));
|
|
42
|
+
const useSystemBar = (config) => {
|
|
43
|
+
const configRef = (0, react_1.useRef)("");
|
|
44
|
+
const configStr = JSON.stringify(config);
|
|
45
|
+
(0, react_1.useEffect)(() => {
|
|
46
|
+
if (configRef.current === configStr)
|
|
47
|
+
return;
|
|
48
|
+
configRef.current = configStr;
|
|
49
|
+
const apply = async () => {
|
|
50
|
+
const p = [];
|
|
51
|
+
if (config.navigationBarColor !== undefined)
|
|
52
|
+
SystemBar.setNavigationBarColor(config.navigationBarColor);
|
|
53
|
+
if (config.navigationBarVisibility !== undefined)
|
|
54
|
+
p.push(SystemBar.setNavigationBarVisibility(config.navigationBarVisibility));
|
|
55
|
+
if (config.navigationBarButtonStyle !== undefined)
|
|
56
|
+
p.push(SystemBar.setNavigationBarButtonStyle(config.navigationBarButtonStyle));
|
|
57
|
+
if (config.navigationBarStyle !== undefined)
|
|
58
|
+
p.push(SystemBar.setNavigationBarStyle(config.navigationBarStyle));
|
|
59
|
+
if (config.navigationBarBehavior !== undefined)
|
|
60
|
+
p.push(SystemBar.setNavigationBarBehavior(config.navigationBarBehavior));
|
|
61
|
+
await Promise.allSettled(p);
|
|
62
|
+
if (config.statusBarColor !== undefined)
|
|
63
|
+
SystemBar.setStatusBarColor(config.statusBarColor, config.statusBarAnimated);
|
|
64
|
+
if (config.statusBarStyle !== undefined)
|
|
65
|
+
SystemBar.setStatusBarStyle(config.statusBarStyle, config.statusBarAnimated);
|
|
66
|
+
if (config.statusBarVisible !== undefined)
|
|
67
|
+
SystemBar.setStatusBarVisibility(config.statusBarVisible, config.statusBarAnimated);
|
|
68
|
+
if (config.keepScreenOn !== undefined)
|
|
69
|
+
SystemBar.keepScreenOn(config.keepScreenOn);
|
|
70
|
+
if (config.immersiveMode !== undefined)
|
|
71
|
+
SystemBar.immersiveMode(config.immersiveMode);
|
|
72
|
+
if (config.brightness !== undefined)
|
|
73
|
+
SystemBar.setBrightness(config.brightness);
|
|
74
|
+
if (config.orientation !== undefined)
|
|
75
|
+
SystemBar.setOrientation(config.orientation);
|
|
76
|
+
if (config.secureScreen !== undefined)
|
|
77
|
+
SystemBar.setSecureScreen(config.secureScreen);
|
|
78
|
+
};
|
|
79
|
+
apply().catch(() => {
|
|
80
|
+
if (__DEV__)
|
|
81
|
+
console.warn("[rn-system-bar] useSystemBar: one or more settings failed.");
|
|
82
|
+
});
|
|
83
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
84
|
+
}, [configStr]);
|
|
85
|
+
};
|
|
86
|
+
exports.useSystemBar = useSystemBar;
|
|
87
|
+
// ─────────────────────────────────────────────
|
|
88
|
+
// useScreencast
|
|
89
|
+
// ─────────────────────────────────────────────
|
|
90
|
+
const useScreencast = () => {
|
|
91
|
+
const [info, setInfo] = (0, react_1.useState)({
|
|
92
|
+
isCasting: false,
|
|
93
|
+
displayName: null,
|
|
94
|
+
});
|
|
95
|
+
(0, react_1.useEffect)(() => {
|
|
96
|
+
SystemBar.getScreencastInfo().then(setInfo).catch(() => { });
|
|
97
|
+
const unsub = SystemBar.onScreencastChange(setInfo);
|
|
98
|
+
return () => unsub();
|
|
99
|
+
}, []);
|
|
100
|
+
return info;
|
|
101
|
+
};
|
|
102
|
+
exports.useScreencast = useScreencast;
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rn-system-bar",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.1.1",
|
|
4
4
|
"description": "Control Android & iOS system bars, brightness, volume, orientation and screen flags from React Native.",
|
|
5
|
-
"main": "index.
|
|
6
|
-
"react-native": "index.
|
|
7
|
-
"types": "
|
|
5
|
+
"main": "lib/index.js",
|
|
6
|
+
"react-native": "lib/index.js",
|
|
7
|
+
"types": "lib/index.d.ts",
|
|
8
8
|
"homepage": "https://github.com/your-org/rn-system-bar",
|
|
9
9
|
"license": "MIT",
|
|
10
10
|
"author": "Your Name",
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
"files": [
|
|
24
24
|
"index.ts",
|
|
25
25
|
"src/",
|
|
26
|
+
"lib/",
|
|
26
27
|
"specs/",
|
|
27
28
|
"android/",
|
|
28
29
|
"ios/",
|
|
@@ -31,5 +32,15 @@
|
|
|
31
32
|
"peerDependencies": {
|
|
32
33
|
"react": ">=18.0.0",
|
|
33
34
|
"react-native": ">=0.73.0"
|
|
35
|
+
},
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "rimraf lib && npx tsc",
|
|
38
|
+
"npm:prepublish": "npm publish --access public",
|
|
39
|
+
"installing": "cd .. && npm install rn-system-bar@latest && cd rn-system-bar"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@types/react": "^19.2.14",
|
|
43
|
+
"@types/react-native": "^0.72.8",
|
|
44
|
+
"typescript": "^5.9.3"
|
|
34
45
|
}
|
|
35
46
|
}
|
package/src/SystemBar.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
// ─────────────────────────────────────────────
|
|
2
2
|
// rn-system-bar · SystemBar.ts
|
|
3
|
-
// v4 — expo-navigation-bar based, zero deprecated APIs
|
|
4
3
|
// ─────────────────────────────────────────────
|
|
5
4
|
|
|
6
|
-
import * as NavigationBar from "expo-navigation-bar";
|
|
7
5
|
import { NativeModules, Platform, StatusBar } from "react-native";
|
|
8
6
|
|
|
9
7
|
import type {
|
|
@@ -12,222 +10,171 @@ import type {
|
|
|
12
10
|
NavigationBarStyle,
|
|
13
11
|
NavigationBarVisibility,
|
|
14
12
|
Orientation,
|
|
13
|
+
ScreencastInfo,
|
|
15
14
|
StatusBarStyle,
|
|
16
15
|
VolumeStream,
|
|
17
16
|
} from "./types";
|
|
18
17
|
|
|
19
|
-
const
|
|
18
|
+
const NavBar = (() => {
|
|
19
|
+
try { return require("expo-navigation-bar"); }
|
|
20
|
+
catch { return null; }
|
|
21
|
+
})();
|
|
22
|
+
|
|
23
|
+
const { SystemBar: Native } = NativeModules;
|
|
20
24
|
|
|
21
25
|
const isAndroid = Platform.OS === "android";
|
|
22
26
|
|
|
23
27
|
const androidOnly = (name: string): boolean => {
|
|
24
28
|
if (!isAndroid) {
|
|
25
|
-
if (__DEV__) {
|
|
26
|
-
console.warn(
|
|
27
|
-
`[rn-system-bar] ${name}() is Android-only and is a no-op on iOS.`,
|
|
28
|
-
);
|
|
29
|
-
}
|
|
29
|
+
if (__DEV__) console.warn(`[rn-system-bar] ${name}() is Android-only, no-op on iOS.`);
|
|
30
30
|
return false;
|
|
31
31
|
}
|
|
32
32
|
return true;
|
|
33
33
|
};
|
|
34
34
|
|
|
35
35
|
const checkNative = () => {
|
|
36
|
-
if (!
|
|
37
|
-
throw new Error(
|
|
38
|
-
"[rn-system-bar] Native module not found. Rebuild your Android/iOS project.",
|
|
39
|
-
);
|
|
40
|
-
}
|
|
36
|
+
if (!Native) throw new Error("[rn-system-bar] Native module not found. Rebuild your project.");
|
|
41
37
|
};
|
|
42
38
|
|
|
43
39
|
// ═══════════════════════════════════════════════
|
|
44
|
-
// NAVIGATION BAR
|
|
45
|
-
//
|
|
46
|
-
//
|
|
40
|
+
// NAVIGATION BAR (Android-only)
|
|
41
|
+
// Color → native Window API (edge-to-edge safe)
|
|
42
|
+
// Others → expo-navigation-bar (work in edge-to-edge)
|
|
47
43
|
// ═══════════════════════════════════════════════
|
|
48
44
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
return NavigationBar.setBackgroundColorAsync(color);
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Show or hide the navigation bar.
|
|
61
|
-
* @platform android
|
|
62
|
-
*/
|
|
63
|
-
export const setNavigationBarVisibility = (
|
|
64
|
-
mode: NavigationBarVisibility,
|
|
65
|
-
): Promise<void> => {
|
|
45
|
+
export const setNavigationBarColor = (color: string): void => {
|
|
46
|
+
if (!androidOnly("setNavigationBarColor")) return;
|
|
47
|
+
checkNative();
|
|
48
|
+
Native.setNavigationBarColor(color);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export const setNavigationBarVisibility = (mode: NavigationBarVisibility): Promise<void> => {
|
|
66
52
|
if (!androidOnly("setNavigationBarVisibility")) return Promise.resolve();
|
|
67
|
-
return
|
|
53
|
+
return NavBar?.setVisibilityAsync(mode) ?? Promise.resolve();
|
|
68
54
|
};
|
|
69
55
|
|
|
70
|
-
|
|
71
|
-
* Set navigation bar icon/button style.
|
|
72
|
-
* @param style "light" = white icons | "dark" = black icons
|
|
73
|
-
* @platform android
|
|
74
|
-
*/
|
|
75
|
-
export const setNavigationBarButtonStyle = (
|
|
76
|
-
style: NavigationBarButtonStyle,
|
|
77
|
-
): Promise<void> => {
|
|
56
|
+
export const setNavigationBarButtonStyle = (style: NavigationBarButtonStyle): Promise<void> => {
|
|
78
57
|
if (!androidOnly("setNavigationBarButtonStyle")) return Promise.resolve();
|
|
79
|
-
return
|
|
58
|
+
return NavBar?.setButtonStyleAsync(style) ?? Promise.resolve();
|
|
80
59
|
};
|
|
81
60
|
|
|
82
|
-
|
|
83
|
-
* Set navigation bar visual style.
|
|
84
|
-
* @platform android
|
|
85
|
-
*/
|
|
86
|
-
export const setNavigationBarStyle = (
|
|
87
|
-
style: NavigationBarStyle,
|
|
88
|
-
): Promise<void> => {
|
|
61
|
+
export const setNavigationBarStyle = (style: NavigationBarStyle): Promise<void> => {
|
|
89
62
|
if (!androidOnly("setNavigationBarStyle")) return Promise.resolve();
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Set navigation bar gesture behavior.
|
|
97
|
-
* @platform android
|
|
98
|
-
*/
|
|
99
|
-
export const setNavigationBarBehavior = (
|
|
100
|
-
behavior: NavigationBarBehavior,
|
|
101
|
-
): Promise<void> => {
|
|
63
|
+
const btn: NavigationBarButtonStyle = style === "dark" || style === "auto" ? "dark" : "light";
|
|
64
|
+
return NavBar?.setButtonStyleAsync(btn) ?? Promise.resolve();
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export const setNavigationBarBehavior = (behavior: NavigationBarBehavior): Promise<void> => {
|
|
102
68
|
if (!androidOnly("setNavigationBarBehavior")) return Promise.resolve();
|
|
103
|
-
return
|
|
69
|
+
return NavBar?.setBehaviorAsync(behavior) ?? Promise.resolve();
|
|
104
70
|
};
|
|
105
71
|
|
|
106
72
|
// ═══════════════════════════════════════════════
|
|
107
73
|
// STATUS BAR
|
|
108
|
-
// React Native StatusBar — cross-platform, no deprecated APIs.
|
|
109
74
|
// ═══════════════════════════════════════════════
|
|
110
75
|
|
|
111
|
-
/**
|
|
112
|
-
* Set status bar background color.
|
|
113
|
-
* @platform android
|
|
114
|
-
*/
|
|
115
76
|
export const setStatusBarColor = (color: string, animated = false): void => {
|
|
116
77
|
if (!androidOnly("setStatusBarColor")) return;
|
|
117
78
|
StatusBar.setBackgroundColor(color, animated);
|
|
118
79
|
};
|
|
119
80
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
export const
|
|
125
|
-
style: StatusBarStyle,
|
|
126
|
-
animated = false,
|
|
127
|
-
): void => {
|
|
128
|
-
StatusBar.setBarStyle(
|
|
129
|
-
style === "light" ? "light-content" : "dark-content",
|
|
130
|
-
animated,
|
|
131
|
-
);
|
|
132
|
-
};
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Show or hide the status bar.
|
|
136
|
-
*/
|
|
137
|
-
export const setStatusBarVisibility = (
|
|
138
|
-
visible: boolean,
|
|
139
|
-
animated = false,
|
|
140
|
-
): void => {
|
|
81
|
+
export const setStatusBarStyle = (style: StatusBarStyle, animated = false): void => {
|
|
82
|
+
StatusBar.setBarStyle(style === "light" ? "light-content" : "dark-content", animated);
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export const setStatusBarVisibility = (visible: boolean, animated = false): void => {
|
|
141
86
|
StatusBar.setHidden(!visible, animated ? "slide" : "none");
|
|
142
87
|
};
|
|
143
88
|
|
|
144
89
|
// ═══════════════════════════════════════════════
|
|
145
|
-
// BRIGHTNESS
|
|
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.
|
|
146
98
|
// ═══════════════════════════════════════════════
|
|
147
99
|
|
|
148
|
-
/**
|
|
149
|
-
* Set screen brightness (0.0 – 1.0).
|
|
150
|
-
*/
|
|
151
100
|
export const setBrightness = (level: number): void => {
|
|
152
101
|
checkNative();
|
|
153
|
-
|
|
102
|
+
Native.setBrightness(Math.max(0.01, Math.min(1, level)));
|
|
154
103
|
};
|
|
155
104
|
|
|
156
|
-
/**
|
|
157
|
-
* Get current screen brightness.
|
|
158
|
-
* @returns Promise<number> 0.0 – 1.0
|
|
159
|
-
*/
|
|
160
105
|
export const getBrightness = (): Promise<number> => {
|
|
161
106
|
checkNative();
|
|
162
|
-
return
|
|
107
|
+
return Native.getBrightness();
|
|
163
108
|
};
|
|
164
109
|
|
|
165
110
|
// ═══════════════════════════════════════════════
|
|
166
|
-
// VOLUME
|
|
111
|
+
// VOLUME
|
|
167
112
|
// ═══════════════════════════════════════════════
|
|
168
113
|
|
|
169
|
-
|
|
170
|
-
* Set volume level (0.0 – 1.0).
|
|
171
|
-
* @param stream "music" | "ring" | "notification" | "alarm" | "system"
|
|
172
|
-
*/
|
|
173
|
-
export const setVolume = (
|
|
174
|
-
level: number,
|
|
175
|
-
stream: VolumeStream = "music",
|
|
176
|
-
): void => {
|
|
114
|
+
export const setVolume = (level: number, stream: VolumeStream = "music"): void => {
|
|
177
115
|
checkNative();
|
|
178
|
-
|
|
116
|
+
Native.setVolume(Math.max(0, Math.min(1, level)), stream);
|
|
179
117
|
};
|
|
180
118
|
|
|
181
|
-
/**
|
|
182
|
-
* Get current volume level (0.0 – 1.0).
|
|
183
|
-
*/
|
|
184
119
|
export const getVolume = (stream: VolumeStream = "music"): Promise<number> => {
|
|
185
120
|
checkNative();
|
|
186
|
-
return
|
|
121
|
+
return Native.getVolume(stream);
|
|
187
122
|
};
|
|
188
123
|
|
|
189
|
-
/**
|
|
190
|
-
* Show or hide the system volume popup.
|
|
191
|
-
* @platform android
|
|
192
|
-
*/
|
|
193
124
|
export const setVolumeHUDVisible = (visible: boolean): void => {
|
|
194
125
|
if (!androidOnly("setVolumeHUDVisible")) return;
|
|
195
126
|
checkNative();
|
|
196
|
-
|
|
127
|
+
Native.setVolumeHUDVisible(visible);
|
|
197
128
|
};
|
|
198
129
|
|
|
199
130
|
// ═══════════════════════════════════════════════
|
|
200
|
-
// SCREEN
|
|
131
|
+
// SCREEN
|
|
201
132
|
// ═══════════════════════════════════════════════
|
|
202
133
|
|
|
203
|
-
/**
|
|
204
|
-
* Prevent the screen from going to sleep.
|
|
205
|
-
*/
|
|
206
134
|
export const keepScreenOn = (enable: boolean): void => {
|
|
207
135
|
checkNative();
|
|
208
|
-
|
|
136
|
+
Native.keepScreenOn(enable);
|
|
209
137
|
};
|
|
210
138
|
|
|
211
|
-
/**
|
|
212
|
-
* Enable immersive fullscreen (hides status + nav bar).
|
|
213
|
-
* Uses WindowInsetsController on API 30+.
|
|
214
|
-
* @platform android
|
|
215
|
-
*/
|
|
216
139
|
export const immersiveMode = (enable: boolean): void => {
|
|
217
140
|
if (!androidOnly("immersiveMode")) return;
|
|
218
141
|
checkNative();
|
|
219
|
-
|
|
142
|
+
Native.immersiveMode(enable);
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
export const setSecureScreen = (enable: boolean): void => {
|
|
146
|
+
if (!androidOnly("setSecureScreen")) return;
|
|
147
|
+
checkNative();
|
|
148
|
+
Native.setSecureScreen(enable);
|
|
220
149
|
};
|
|
221
150
|
|
|
222
151
|
// ═══════════════════════════════════════════════
|
|
223
|
-
// ORIENTATION
|
|
152
|
+
// ORIENTATION
|
|
224
153
|
// ═══════════════════════════════════════════════
|
|
225
154
|
|
|
226
|
-
/**
|
|
227
|
-
* Lock or unlock screen orientation.
|
|
228
|
-
* "portrait" | "landscape" | "landscape-left" | "landscape-right" | "auto"
|
|
229
|
-
*/
|
|
230
155
|
export const setOrientation = (mode: Orientation): void => {
|
|
231
156
|
checkNative();
|
|
232
|
-
|
|
157
|
+
Native.setOrientation(mode);
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
// ═══════════════════════════════════════════════
|
|
161
|
+
// SCREENCAST
|
|
162
|
+
// ═══════════════════════════════════════════════
|
|
163
|
+
|
|
164
|
+
export const getScreencastInfo = (): Promise<ScreencastInfo> => {
|
|
165
|
+
checkNative();
|
|
166
|
+
return Native.getScreencastInfo();
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
export const onScreencastChange = (
|
|
170
|
+
callback: (info: ScreencastInfo) => void
|
|
171
|
+
): (() => void) => {
|
|
172
|
+
checkNative();
|
|
173
|
+
const { DeviceEventEmitter } = require("react-native");
|
|
174
|
+
Native.startScreencastListener();
|
|
175
|
+
const sub = DeviceEventEmitter.addListener("SystemBar_ScreencastChange", callback);
|
|
176
|
+
return () => {
|
|
177
|
+
sub.remove();
|
|
178
|
+
Native.stopScreencastListener();
|
|
179
|
+
};
|
|
233
180
|
};
|
package/src/types.ts
CHANGED
|
@@ -3,27 +3,14 @@
|
|
|
3
3
|
// ─────────────────────────────────────────────
|
|
4
4
|
|
|
5
5
|
export type NavigationBarBehavior =
|
|
6
|
-
| "overlay-swipe"
|
|
7
|
-
| "inset-swipe"
|
|
8
|
-
| "inset-touch";
|
|
6
|
+
| "overlay-swipe"
|
|
7
|
+
| "inset-swipe"
|
|
8
|
+
| "inset-touch";
|
|
9
9
|
|
|
10
|
-
export type NavigationBarButtonStyle =
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
export type NavigationBarStyle =
|
|
15
|
-
| "auto" // System decides
|
|
16
|
-
| "inverted" // Inverted from system
|
|
17
|
-
| "light" // Light background nav bar
|
|
18
|
-
| "dark"; // Dark background nav bar
|
|
19
|
-
|
|
20
|
-
export type NavigationBarVisibility =
|
|
21
|
-
| "visible"
|
|
22
|
-
| "hidden";
|
|
23
|
-
|
|
24
|
-
export type StatusBarStyle =
|
|
25
|
-
| "light" // Light icons (for dark backgrounds)
|
|
26
|
-
| "dark"; // Dark icons (for light backgrounds)
|
|
10
|
+
export type NavigationBarButtonStyle = "light" | "dark";
|
|
11
|
+
export type NavigationBarStyle = "auto" | "inverted" | "light" | "dark";
|
|
12
|
+
export type NavigationBarVisibility = "visible" | "hidden";
|
|
13
|
+
export type StatusBarStyle = "light" | "dark";
|
|
27
14
|
|
|
28
15
|
export type Orientation =
|
|
29
16
|
| "portrait"
|
|
@@ -39,32 +26,7 @@ export type VolumeStream =
|
|
|
39
26
|
| "alarm"
|
|
40
27
|
| "system";
|
|
41
28
|
|
|
42
|
-
export interface
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
setNavigationBarVisibility(mode: NavigationBarVisibility): void;
|
|
46
|
-
setNavigationBarButtonStyle(style: NavigationBarButtonStyle): void;
|
|
47
|
-
setNavigationBarStyle(style: NavigationBarStyle): void;
|
|
48
|
-
setNavigationBarBehavior(behavior: NavigationBarBehavior): void;
|
|
49
|
-
|
|
50
|
-
// Status Bar
|
|
51
|
-
setStatusBarColor(color: string): void;
|
|
52
|
-
setStatusBarStyle(style: StatusBarStyle): void;
|
|
53
|
-
setStatusBarVisibility(visible: boolean): void;
|
|
54
|
-
|
|
55
|
-
// Brightness
|
|
56
|
-
setBrightness(level: number): void;
|
|
57
|
-
getBrightness(): Promise<number>;
|
|
58
|
-
|
|
59
|
-
// Volume
|
|
60
|
-
setVolume(level: number, stream?: VolumeStream): void;
|
|
61
|
-
getVolume(stream?: VolumeStream): Promise<number>;
|
|
62
|
-
setVolumeHUDVisible(visible: boolean): void;
|
|
63
|
-
|
|
64
|
-
// Screen
|
|
65
|
-
keepScreenOn(enable: boolean): void;
|
|
66
|
-
immersiveMode(enable: boolean): void;
|
|
67
|
-
|
|
68
|
-
// Orientation
|
|
69
|
-
setOrientation(mode: Orientation): void;
|
|
29
|
+
export interface ScreencastInfo {
|
|
30
|
+
isCasting: boolean;
|
|
31
|
+
displayName: string | null;
|
|
70
32
|
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// ─────────────────────────────────────────────
|
|
2
|
+
// rn-system-bar · useSystemBar.ts
|
|
3
|
+
// ─────────────────────────────────────────────
|
|
4
|
+
|
|
5
|
+
import { useEffect, useRef, useState } from "react";
|
|
6
|
+
import type {
|
|
7
|
+
NavigationBarBehavior,
|
|
8
|
+
NavigationBarButtonStyle,
|
|
9
|
+
NavigationBarStyle,
|
|
10
|
+
NavigationBarVisibility,
|
|
11
|
+
Orientation,
|
|
12
|
+
ScreencastInfo,
|
|
13
|
+
StatusBarStyle,
|
|
14
|
+
} from "./types";
|
|
15
|
+
import * as SystemBar from "./SystemBar";
|
|
16
|
+
|
|
17
|
+
// ─────────────────────────────────────────────
|
|
18
|
+
// useSystemBar — apply system bar config safely
|
|
19
|
+
// All async calls run inside useEffect — no Suspense crash
|
|
20
|
+
// ─────────────────────────────────────────────
|
|
21
|
+
export interface SystemBarConfig {
|
|
22
|
+
navigationBarColor?: string;
|
|
23
|
+
navigationBarVisibility?: NavigationBarVisibility;
|
|
24
|
+
navigationBarButtonStyle?: NavigationBarButtonStyle;
|
|
25
|
+
navigationBarStyle?: NavigationBarStyle;
|
|
26
|
+
navigationBarBehavior?: NavigationBarBehavior;
|
|
27
|
+
statusBarColor?: string;
|
|
28
|
+
statusBarStyle?: StatusBarStyle;
|
|
29
|
+
statusBarVisible?: boolean;
|
|
30
|
+
statusBarAnimated?: boolean;
|
|
31
|
+
keepScreenOn?: boolean;
|
|
32
|
+
immersiveMode?: boolean;
|
|
33
|
+
brightness?: number;
|
|
34
|
+
orientation?: Orientation;
|
|
35
|
+
secureScreen?: boolean;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export const useSystemBar = (config: SystemBarConfig) => {
|
|
39
|
+
const configRef = useRef("");
|
|
40
|
+
const configStr = JSON.stringify(config);
|
|
41
|
+
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
if (configRef.current === configStr) return;
|
|
44
|
+
configRef.current = configStr;
|
|
45
|
+
|
|
46
|
+
const apply = async () => {
|
|
47
|
+
const p: Promise<void>[] = [];
|
|
48
|
+
|
|
49
|
+
if (config.navigationBarColor !== undefined) SystemBar.setNavigationBarColor(config.navigationBarColor);
|
|
50
|
+
if (config.navigationBarVisibility !== undefined) p.push(SystemBar.setNavigationBarVisibility(config.navigationBarVisibility));
|
|
51
|
+
if (config.navigationBarButtonStyle !== undefined) p.push(SystemBar.setNavigationBarButtonStyle(config.navigationBarButtonStyle));
|
|
52
|
+
if (config.navigationBarStyle !== undefined) p.push(SystemBar.setNavigationBarStyle(config.navigationBarStyle));
|
|
53
|
+
if (config.navigationBarBehavior !== undefined) p.push(SystemBar.setNavigationBarBehavior(config.navigationBarBehavior));
|
|
54
|
+
|
|
55
|
+
await Promise.allSettled(p);
|
|
56
|
+
|
|
57
|
+
if (config.statusBarColor !== undefined) SystemBar.setStatusBarColor(config.statusBarColor, config.statusBarAnimated);
|
|
58
|
+
if (config.statusBarStyle !== undefined) SystemBar.setStatusBarStyle(config.statusBarStyle, config.statusBarAnimated);
|
|
59
|
+
if (config.statusBarVisible !== undefined) SystemBar.setStatusBarVisibility(config.statusBarVisible, config.statusBarAnimated);
|
|
60
|
+
if (config.keepScreenOn !== undefined) SystemBar.keepScreenOn(config.keepScreenOn);
|
|
61
|
+
if (config.immersiveMode !== undefined) SystemBar.immersiveMode(config.immersiveMode);
|
|
62
|
+
if (config.brightness !== undefined) SystemBar.setBrightness(config.brightness);
|
|
63
|
+
if (config.orientation !== undefined) SystemBar.setOrientation(config.orientation);
|
|
64
|
+
if (config.secureScreen !== undefined) SystemBar.setSecureScreen(config.secureScreen);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
apply().catch(() => {
|
|
68
|
+
if (__DEV__) console.warn("[rn-system-bar] useSystemBar: one or more settings failed.");
|
|
69
|
+
});
|
|
70
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
71
|
+
}, [configStr]);
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// ─────────────────────────────────────────────
|
|
75
|
+
// useScreencast
|
|
76
|
+
// ─────────────────────────────────────────────
|
|
77
|
+
export const useScreencast = () => {
|
|
78
|
+
const [info, setInfo] = useState<ScreencastInfo>({
|
|
79
|
+
isCasting: false,
|
|
80
|
+
displayName: null,
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
useEffect(() => {
|
|
84
|
+
SystemBar.getScreencastInfo().then(setInfo).catch(() => {});
|
|
85
|
+
const unsub = SystemBar.onScreencastChange(setInfo);
|
|
86
|
+
return () => unsub();
|
|
87
|
+
}, []);
|
|
88
|
+
|
|
89
|
+
return info;
|
|
90
|
+
};
|