├── .gitattributes
├── .gitignore
├── Example.js
├── LICENSE
├── README.md
├── android
├── build.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ └── java
│ └── com
│ └── rnimmersivemode
│ ├── BarMode.java
│ ├── BarStyle.java
│ ├── ImmersiveEvent.java
│ ├── ImmersiveMode.java
│ ├── RNImmersiveModeModule.java
│ └── RNImmersiveModePackage.java
├── index.d.ts
├── index.js
└── package.json
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.pbxproj -text
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # OSX
3 | #
4 | .DS_Store
5 |
6 | # node.js
7 | #
8 | node_modules/
9 | npm-debug.log
10 | yarn-error.log
11 |
12 |
13 | # Xcode
14 | #
15 | build/
16 | *.pbxuser
17 | !default.pbxuser
18 | *.mode1v3
19 | !default.mode1v3
20 | *.mode2v3
21 | !default.mode2v3
22 | *.perspectivev3
23 | !default.perspectivev3
24 | xcuserdata
25 | *.xccheckout
26 | *.moved-aside
27 | DerivedData
28 | *.hmap
29 | *.ipa
30 | *.xcuserstate
31 | project.xcworkspace
32 |
33 |
34 | # Android/IntelliJ
35 | #
36 | build/
37 | .idea
38 | .gradle
39 | local.properties
40 | *.iml
41 |
42 | # BUCK
43 | buck-out/
44 | \.buckd/
45 | *.keystore
46 |
--------------------------------------------------------------------------------
/Example.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import {
3 | SafeAreaView,
4 | StyleSheet,
5 | Text,
6 | TouchableOpacity
7 | } from 'react-native';
8 | import ImmersiveMode from 'react-native-immersive-mode';
9 |
10 | export default class App extends Component {
11 |
12 | state = {
13 | color: '#ff0000',
14 | }
15 |
16 | buttonImmersiveMode = [
17 | 'Normal',
18 | 'Full',
19 | 'FullSticky',
20 | 'Bottom',
21 | 'BottomSticky',
22 | ]
23 |
24 | componentDidMount() {
25 | ImmersiveMode.fullLayout(true);
26 | this.listen = ImmersiveMode.addEventListener((e) => {
27 | /**
28 | * e = {
29 | * statusBar: boolean,
30 | * navigationBottomBar: boolean,
31 | * }
32 | */
33 | console.log(e)
34 | })
35 | }
36 |
37 | componentWillUnmount() {
38 | this.listen.remove();
39 | ImmersiveMode.fullLayout(false);
40 | }
41 |
42 | render() {
43 | return (
44 |
45 |
46 | Main
47 |
48 | {
49 | this.buttonImmersiveMode.map(v =>
50 | {
51 | ImmersiveMode.setBarMode(v);
52 | }}>
53 | {v}
54 |
55 | )
56 | }
57 |
58 | this.setState({ color: t })}
70 | maxLength={9} />
71 | {
72 | ImmersiveMode.setBarColor(this.state.color);
73 | }}>
74 | Bar color {this.state.color}
75 |
76 | {
77 | ImmersiveMode.setBarColor(null);
78 | }}>
79 | Bar color default
80 |
81 |
82 |
85 | {
86 | ImmersiveMode.setBarStyle('Dark');
87 | }}>
88 | Bar Style Dark
89 |
90 | {
91 | ImmersiveMode.setBarStyle('Light');
92 | }}>
93 | Bar Style Light
94 |
95 |
96 |
97 |
100 | {
101 | ImmersiveMode.setBarTranslucent(true);
102 | }}>
103 | Translucent On
104 |
105 | {
106 | ImmersiveMode.setBarTranslucent(false);
107 | }}>
108 | Translucent Off
109 |
110 |
111 |
112 |
113 |
114 | );
115 | }
116 | };
117 |
118 | const styles = StyleSheet.create({
119 | container: {
120 | flex: 1,
121 | justifyContent: 'center',
122 | alignItems: 'center',
123 | backgroundColor: '#ddd',
124 | },
125 | button: {
126 | margin: 8,
127 | paddingVertical: 8,
128 | paddingHorizontal: 16,
129 | borderRadius: 4,
130 | backgroundColor: '#5fba7d',
131 | }
132 | });
133 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 wrathyz
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.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # react-native-immersive-mode
3 | [](https://badge.fury.io/js/react-native-immersive-mode)
4 |
5 | ## Installation
6 |
7 | ### Mostly automatic installation
8 |
9 | ```python
10 | npm install react-native-immersive-mode --save
11 | ```
12 |
13 | ### Auto linking library (react-native < 0.60)
14 |
15 | ```python
16 | react-native link react-native-immersive-mode
17 | ```
18 |
19 | ### Manual linking library
20 |
21 | #### Android
22 |
23 | 1. Append the following lines to `android/settings.gradle`
24 | ```
25 | include ':react-native-immersive-mode'
26 | project(':react-native-immersive-mode').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-immersive-mode/android')
27 | ```
28 | 2. Insert the following lines inside the dependencies block in `android/app/build.gradle`
29 | ```
30 | implementation project(':react-native-immersive-mode')
31 | ```
32 | 3. Add it to your `MainApplication.java`
33 | ```
34 | import com.rnimmersivemode.RNImmersiveModePackage; // add this
35 |
36 | public class MainActivity extends ReactActivity {
37 | @Override
38 | protected List getPackages() {
39 | return Arrays.asList(
40 | new MainReactPackage(),
41 | new RNImmersiveModePackage() // add this
42 | );
43 | }
44 | }
45 | ```
46 | ## Usage
47 |
48 | ### Bar Mode
49 | - **Normal** - show status and navigation
50 | - **Full** - hide status and navigation
51 | - **FullSticky** - hide status and navigation with sticky
52 | - **Bottom** - hide navigation
53 | - **BottomSticky** - hide navigation with sticky
54 |
55 | ### Bar Style
56 | - **Dark**
57 | - **Light**
58 |
59 | ### Methods
60 |
61 | - [fullLayout](#fulllayout)
62 | - [setBarMode](#setbarmode)
63 | - [setBarStyle](#setbarstyle)
64 | - [setBarTranslucent](#setbartranslucent)
65 | - [setBarColor](#setbarcolor)
66 | - [setBarDefaultColor](#setBarDefaultColor)
67 | - [addEventListener](#addeventlistener)
68 |
69 | ### fullLayout
70 | `fullLayout(full: boolean): void`
71 | use all area of screen
72 |
73 | | name | type | description |
74 | | ---- | ---- | ------------|
75 | | full | boolean | `true` use all area of screen, `false` not include status and navigation bar |
76 |
77 | ```javascript
78 | import ImmersiveMode from 'react-native-immersive-mode';
79 |
80 | // should set full layout in componentDidMount
81 | componentDidMount() {
82 | ImmersiveMode.fullLayout(true);
83 | }
84 | // and should restore layout
85 | componentWillUnmount() {
86 | ImmersiveMode.fullLayout(false);
87 | }
88 | ```
89 |
90 | ### setBarMode
91 | `setBarMode(mode: string): void`
92 | change status and navigation bar mode
93 |
94 | **Note**. mode sticky will be disabled bar color.
95 |
96 | | name | type | description |
97 | | ---- | ---- | ------------|
98 | | mode | string | [Bar Mode](#bar-mode) |
99 |
100 | ```javascript
101 | import ImmersiveMode from 'react-native-immersive-mode';
102 |
103 | ImmersiveMode.setBarMode('Normal');
104 | ```
105 |
106 | ### setBarStyle
107 | `setBarStyle(style: string): void`
108 | chnage status and navigation style
109 |
110 | **Note**. To change system Navigation(bottom) to Light, must be change bar color `setBarColor` to other color first.
111 |
112 | | name | type | description |
113 | | ---- | ---- | ------------|
114 | | mode | string | [Bar Style](#bar-style) |
115 |
116 | ```javascript
117 | import ImmersiveMode from 'react-native-immersive-mode';
118 |
119 | ImmersiveMode.setBarStyle('Dark');
120 | ```
121 |
122 | ### setBarTranslucent
123 | `setBarTranslucent(translucent: boolean): void`
124 | change status and navigation bar is transparent 50%.
125 |
126 | **Note**. When `true` bar color will be disabled.
127 |
128 | | name | type | description |
129 | | ---- | ---- | ------------|
130 | | translucent | booelan | |
131 |
132 | ```javascript
133 | import ImmersiveMode from 'react-native-immersive-mode';
134 |
135 | ImmersiveMode.setBarTranslucent(true);
136 | ```
137 |
138 | ### setBarColor
139 | `setBarColor(color: string): void`
140 | change status and navigation bar is transparent 50%.
141 |
142 | | name | type | description |
143 | | ---- | ---- | ------------|
144 | | color | string | `#rgb`, `#rrggbb`, `#rrggbbaa` |
145 |
146 | ```javascript
147 | import ImmersiveMode from 'react-native-immersive-mode';
148 |
149 | ImmersiveMode.setBarColor('#ff0000');
150 | ```
151 |
152 | **Note**. still can passing `null` to set default color
153 |
154 | ### setBarDefaultColor
155 | `setBarDefaultColor(): void`
156 |
157 | > default color is color before changed by `setBarColor`
158 |
159 | ```javascript
160 | import ImmersiveMode from 'react-native-immersive-mode';
161 |
162 | ImmersiveMode.setBarDefaultColor();
163 | ```
164 |
165 | ### addEventListener
166 | `addEventListener(callback: function): EmitterSubscription`
167 | trigger event when bar visibility change (mode sticky not trigged)
168 |
169 | | name | type | params | description |
170 | | ---- | ---- | ------ | ------------|
171 | | callback | function | (statusBar: boolean, navigationBottomBar: boolean) | `true`: show, `false`: hidden |
172 |
173 | ```javascript
174 | import ImmersiveMode from 'react-native-immersive-mode';
175 |
176 | // ...
177 |
178 | componentDidMount() {
179 | this.listen = ImmersiveMode.addEventListener((e) => {
180 | console.log(e)
181 | })
182 | }
183 |
184 | componentWillUnmount() {
185 | this.listen.remove();
186 | }
187 | ```
188 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | google()
4 | mavenCentral()
5 | }
6 |
7 | dependencies {
8 | classpath "com.android.tools.build:gradle:7.2.1"
9 | }
10 | }
11 |
12 | apply plugin: 'com.android.library'
13 |
14 | android {
15 | compileSdkVersion 33
16 |
17 | defaultConfig {
18 | minSdkVersion rootProject.hasProperty('minSdkVersion') ? rootProject.minSdkVersion : 16
19 | targetSdkVersion 33
20 | versionCode 1
21 | versionName "1.0"
22 | }
23 | lintOptions {
24 | abortOnError false
25 | }
26 | }
27 |
28 | repositories {
29 | mavenCentral()
30 | }
31 |
32 | dependencies {
33 | implementation 'com.facebook.react:react-native:+'
34 | }
35 |
--------------------------------------------------------------------------------
/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/android/src/main/java/com/rnimmersivemode/BarMode.java:
--------------------------------------------------------------------------------
1 | package com.rnimmersivemode;
2 |
3 | class BarMode {
4 | static final String Normal = "Normal"; // show status and navigation
5 | static final String Full = "Full"; // hide status and navigation
6 | static final String FullSticky = "FullSticky"; // hide status and navigation with sticky
7 | static final String Bottom = "Bottom"; // hide navigation
8 | static final String BottomSticky = "BottomSticky"; // hide navigation with sticky
9 | }
10 |
--------------------------------------------------------------------------------
/android/src/main/java/com/rnimmersivemode/BarStyle.java:
--------------------------------------------------------------------------------
1 | package com.rnimmersivemode;
2 |
3 | public class BarStyle {
4 | static final String Light = "Light"; // hide navigation with sticky
5 | static final String Dark = "Dark"; // hide navigation with sticky
6 | }
7 |
--------------------------------------------------------------------------------
/android/src/main/java/com/rnimmersivemode/ImmersiveEvent.java:
--------------------------------------------------------------------------------
1 | package com.rnimmersivemode;
2 |
3 | class ImmersiveEvent {
4 | static final String OnSystemUiVisibilityChange = "OnSystemUiVisibilityChange";
5 | }
6 |
--------------------------------------------------------------------------------
/android/src/main/java/com/rnimmersivemode/ImmersiveMode.java:
--------------------------------------------------------------------------------
1 | package com.rnimmersivemode;
2 |
3 | class ImmersiveMode {
4 | static final int Normal = 1; // show status and navigation
5 | static final int Full = 2; // hide status and navigation
6 | static final int FullSticky = 3; // hide status and navigation with sticky
7 | static final int Bottom = 4; // hide navigation
8 | static final int BottomSticky = 5; // hide navigation with sticky
9 | }
10 |
--------------------------------------------------------------------------------
/android/src/main/java/com/rnimmersivemode/RNImmersiveModeModule.java:
--------------------------------------------------------------------------------
1 | package com.rnimmersivemode;
2 |
3 | import android.app.Activity;
4 | import android.graphics.Color;
5 | import android.os.Build;
6 | import android.util.Log;
7 | import android.view.View;
8 | import android.view.Window;
9 | import android.view.WindowManager;
10 |
11 | import com.facebook.react.bridge.Arguments;
12 | import com.facebook.react.bridge.ReactApplicationContext;
13 | import com.facebook.react.bridge.ReactContext;
14 | import com.facebook.react.bridge.ReactContextBaseJavaModule;
15 | import com.facebook.react.bridge.ReactMethod;
16 | import com.facebook.react.bridge.WritableMap;
17 | import com.facebook.react.modules.core.DeviceEventManagerModule;
18 |
19 | import java.util.HashMap;
20 | import java.util.Map;
21 |
22 | import javax.annotation.Nonnull;
23 |
24 | import static com.facebook.react.bridge.UiThreadUtil.runOnUiThread;
25 |
26 | public class RNImmersiveModeModule extends ReactContextBaseJavaModule {
27 |
28 | private static final String ModuleName = "RNImmersiveMode";
29 | private int currentLayout = View.VISIBLE | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
30 | private int currentMode = View.VISIBLE | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
31 | private int currentStatusStyle = View.VISIBLE | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
32 | private int currentNavigationStyle = View.VISIBLE | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
33 |
34 | private boolean hasColorChange = false;
35 | private int defaultStatusColor;
36 | private int defaultNavigationColor;
37 |
38 | RNImmersiveModeModule(ReactApplicationContext reactContext) {
39 | super(reactContext);
40 | }
41 |
42 | @Nonnull
43 | @Override
44 | public String getName() {
45 | return ModuleName;
46 | }
47 |
48 | @Override
49 | public Map getConstants() {
50 | final Map constants = new HashMap<>();
51 |
52 | //mode
53 | constants.put("Normal", ImmersiveMode.Normal);
54 | constants.put("Full", ImmersiveMode.Full);
55 | constants.put("FullSticky", ImmersiveMode.FullSticky);
56 | constants.put("Bottom", ImmersiveMode.Bottom);
57 | constants.put("BottomSticky", ImmersiveMode.BottomSticky);
58 |
59 | //event
60 | constants.put("OnSystemUiVisibilityChange", ImmersiveEvent.OnSystemUiVisibilityChange);
61 | return constants;
62 | }
63 |
64 | @ReactMethod
65 | public void fullLayout(final Boolean fullscreen) {
66 | if (fullscreen) {
67 | // set layout fullscreen (including layout navigation bar on bottom)
68 | this.currentLayout = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
69 | | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
70 | | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
71 | } else {
72 | // set layout normal (not including layout navigation bar on bottom)
73 | this.currentLayout = View.VISIBLE
74 | | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
75 | }
76 |
77 | this.setUiOnUiThread();
78 | }
79 |
80 | @ReactMethod
81 | public void setBarMode(String immersive) {
82 |
83 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
84 | Log.w(ModuleName, "Sdk Version must be >= " + Build.VERSION_CODES.KITKAT);
85 | return;
86 | }
87 |
88 | switch (immersive) {
89 | case BarMode.Normal:
90 | this.currentMode = View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
91 | break;
92 | case BarMode.Full:
93 | this.currentMode = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION // hide nav bar
94 | | View.SYSTEM_UI_FLAG_FULLSCREEN // hide status bar
95 | | View.SYSTEM_UI_FLAG_IMMERSIVE;
96 | break;
97 | case BarMode.FullSticky:
98 | this.currentMode = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
99 | | View.SYSTEM_UI_FLAG_FULLSCREEN
100 | | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
101 | break;
102 | case BarMode.Bottom:
103 | this.currentMode = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
104 | | View.SYSTEM_UI_FLAG_IMMERSIVE;
105 | break;
106 | case BarMode.BottomSticky:
107 | this.currentMode = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
108 | | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
109 | break;
110 | default:
111 | }
112 |
113 | this.setUiOnUiThread();
114 | }
115 |
116 | @ReactMethod
117 | public void setBarColor(final String hexColor) {
118 | runOnUiThread(new Runnable() {
119 | @Override
120 | public void run() {
121 | Activity activity = getCurrentActivity();
122 | if (activity != null) {
123 | Window window = activity.getWindow();
124 | if (window != null) {
125 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
126 |
127 | window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
128 | window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
129 | window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
130 |
131 | try {
132 | if (hexColor != null) {
133 | if (!hasColorChange) {
134 | hasColorChange = true;
135 | defaultStatusColor = window.getStatusBarColor();
136 | defaultNavigationColor = window.getNavigationBarColor();
137 | }
138 |
139 | window.setStatusBarColor(Color.parseColor(hexColor));
140 | window.setNavigationBarColor(Color.parseColor(hexColor));
141 | } else if (hasColorChange) {
142 | hasColorChange = false;
143 | window.setStatusBarColor(defaultStatusColor);
144 | window.setNavigationBarColor(defaultNavigationColor);
145 | }
146 | } catch (Exception e) {
147 | Log.e(ModuleName, e.getMessage());
148 | }
149 |
150 | } else {
151 | Log.w(ModuleName, "Sdk Version must be >= " + Build.VERSION_CODES.LOLLIPOP);
152 | }
153 | }
154 | }
155 | }
156 | });
157 | }
158 |
159 | @ReactMethod
160 | public void setBarStyle(final String style) {
161 | switch (style) {
162 | case BarStyle.Dark:
163 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
164 | this.currentStatusStyle = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
165 | } else {
166 | Log.w(ModuleName, "Sdk Version must be >= " + Build.VERSION_CODES.M);
167 | }
168 |
169 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
170 | this.currentNavigationStyle = View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
171 | } else {
172 | Log.w(ModuleName, "Sdk Version must be >= " + Build.VERSION_CODES.O);
173 | }
174 | break;
175 | case BarStyle.Light:
176 | this.currentStatusStyle = View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
177 | this.currentNavigationStyle = View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
178 | break;
179 | }
180 |
181 | this.setUiOnUiThread();
182 | }
183 |
184 | @ReactMethod
185 | public void setBarTranslucent(final boolean enable) {
186 | runOnUiThread(new Runnable() {
187 | @Override
188 | public void run() {
189 | Activity activity = getCurrentActivity();
190 | if (activity != null) {
191 | Window window = activity.getWindow();
192 | if (window != null) {
193 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
194 | if (enable) {
195 | window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
196 | window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
197 | } else {
198 | window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
199 | window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
200 | }
201 | }
202 | }
203 | }
204 | }
205 | });
206 | }
207 |
208 | @ReactMethod
209 | private void setOnSystemUiVisibilityChangeListener() {
210 | runOnUiThread(new Runnable() {
211 | @Override
212 | public void run() {
213 | Activity activity = getCurrentActivity();
214 | if (activity != null) {
215 | Window window = activity.getWindow();
216 | if (window != null) {
217 | View view = window.getDecorView();
218 | view.setOnSystemUiVisibilityChangeListener(
219 | new View.OnSystemUiVisibilityChangeListener() {
220 | @Override
221 | public void onSystemUiVisibilityChange(int visibility) {
222 | WritableMap params = Arguments.createMap();
223 |
224 | if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) {
225 | params.putBoolean("statusBar", true);
226 | } else {
227 | params.putBoolean("statusBar", false);
228 | }
229 |
230 | if ((visibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0) {
231 | params.putBoolean("navigationBottomBar", true);
232 | } else {
233 | params.putBoolean("navigationBottomBar", false);
234 | }
235 |
236 | sendEvent(getReactApplicationContext(),
237 | params);
238 | }
239 | });
240 | }
241 | }
242 | }
243 | });
244 | }
245 |
246 | private void setUiOnUiThread() {
247 | runOnUiThread(new Runnable() {
248 | @Override
249 | public void run() {
250 | Activity activity = getCurrentActivity();
251 | if (activity != null) {
252 | Window window = activity.getWindow();
253 | if (window != null) {
254 | View view = window.getDecorView();
255 | view.setSystemUiVisibility(currentLayout | currentMode | currentStatusStyle | currentNavigationStyle);
256 | }
257 | }
258 | }
259 | });
260 | }
261 |
262 | private void sendEvent(ReactContext reactContext,
263 | WritableMap params) {
264 | reactContext
265 | .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
266 | .emit(ImmersiveEvent.OnSystemUiVisibilityChange, params);
267 | }
268 | }
--------------------------------------------------------------------------------
/android/src/main/java/com/rnimmersivemode/RNImmersiveModePackage.java:
--------------------------------------------------------------------------------
1 | package com.rnimmersivemode;
2 |
3 | import com.facebook.react.ReactPackage;
4 | import com.facebook.react.bridge.NativeModule;
5 | import com.facebook.react.bridge.ReactApplicationContext;
6 | import com.facebook.react.uimanager.ViewManager;
7 |
8 | import java.util.Collections;
9 | import java.util.List;
10 |
11 | import javax.annotation.Nonnull;
12 |
13 | public class RNImmersiveModePackage implements ReactPackage {
14 | @Nonnull
15 | @Override
16 | public List createNativeModules(@Nonnull ReactApplicationContext reactContext) {
17 | return Collections.singletonList(new RNImmersiveModeModule(reactContext));
18 | }
19 |
20 | @Nonnull
21 | @Override
22 | public List createViewManagers(@Nonnull ReactApplicationContext reactContext) {
23 | return Collections.emptyList();
24 | }
25 | }
--------------------------------------------------------------------------------
/index.d.ts:
--------------------------------------------------------------------------------
1 | import { EmitterSubscription } from 'react-native'
2 |
3 | type BarViisibilityType = {
4 | statusBar: boolean,
5 | navigationBottomBar: boolean
6 | }
7 |
8 | type ImmersiveBarStyleType = 'Dark' | 'Light';
9 | type ImmersiveBarModeType =
10 | 'Normal' |
11 | 'Full' |
12 | 'FullSticky' |
13 | 'Bottom' |
14 | 'BottomSticky'
15 |
16 |
17 | interface ImmersiveModeStatic {
18 | fullLayout(full: boolean): void;
19 |
20 | /**
21 | * Set system ui mode.
22 | * @param mode
23 | */
24 | setBarMode(mode: ImmersiveBarModeType): void;
25 |
26 | /**
27 | * Set color of system bar.
28 | * When set color translucent will be disabled.
29 | *
30 | * @param color color hex #rrggbbaa. if color is null will set default color
31 | */
32 | setBarColor(color: string): void;
33 |
34 | /**
35 | * Set default color of system bar.
36 | * When set default color translucent will be disabled.
37 | */
38 | setBarDefaultColor(): void;
39 |
40 | /**
41 | * Set style of system bar.
42 | * System Navigation will be Light, must be change bar color `setBarColor` to other color first.
43 | *
44 | * @param style
45 | */
46 | setBarStyle(style: ImmersiveBarStyleType): void;
47 |
48 | /**
49 | * System bar background color is transparent 50%.
50 | * When `true` bar color will be disabled.
51 | *
52 | * @param enable
53 | */
54 | setBarTranslucent(enable: boolean): void;
55 |
56 | addEventListener(callback: (viisibility: BarViisibilityType) => void): EmitterSubscription;
57 | }
58 |
59 | declare const ImmersiveMode: ImmersiveModeStatic;
60 | export default ImmersiveMode;
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | import { NativeModules, DeviceEventEmitter, Platform } from 'react-native';
2 |
3 | const { RNImmersiveMode } = NativeModules;
4 |
5 | const checkModule = () => {
6 | if (Platform.OS === 'android' && !RNImmersiveMode) {
7 | throw Error('RNImmersiveMode is not properly linked');
8 | }
9 | // else: (maybe iOS)
10 |
11 | return Platform.OS === 'android' && RNImmersiveMode;
12 | }
13 |
14 | const ImmersiveMode = {
15 |
16 | fullLayout(full) {
17 | if (checkModule()) {
18 | RNImmersiveMode.fullLayout(full);
19 | }
20 | },
21 |
22 | setBarMode(mode) {
23 | if (checkModule()) {
24 | RNImmersiveMode.setBarMode(mode);
25 | }
26 | },
27 |
28 | setBarStyle(style) {
29 | if (checkModule()) {
30 | RNImmersiveMode.setBarStyle(style);
31 | }
32 | },
33 |
34 | setBarTranslucent(enable) {
35 | if (checkModule()) {
36 | RNImmersiveMode.setBarTranslucent(enable);
37 | }
38 | },
39 |
40 | setBarColor(color) {
41 | if (checkModule()) {
42 | if (typeof color === 'string') {
43 | if (color.length === 9) {
44 | // convert #rgba to #argb
45 | color = '#' + color.substr(7, 2) + color.substr(1, 6);
46 | } else if (color.length === 4) {
47 | color = '#' + color[1] + color[1] + color[2] + color[2] + color[3] + color[3];
48 | }
49 | }
50 |
51 | RNImmersiveMode.setBarColor(color);
52 | }
53 | },
54 |
55 | setBarDefaultColor() {
56 | if (checkModule()) {
57 | RNImmersiveMode.setBarColor(null);
58 | }
59 | },
60 |
61 | addEventListener(callback) {
62 | if (checkModule()) {
63 | if (typeof callback !== 'function') return;
64 |
65 | RNImmersiveMode.setOnSystemUiVisibilityChangeListener();
66 |
67 | const subscription = DeviceEventEmitter.addListener(
68 | RNImmersiveMode.OnSystemUiVisibilityChange,
69 | (e) => callback(e));
70 |
71 | return subscription;
72 | }
73 | }
74 | }
75 |
76 | export default ImmersiveMode;
77 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-native-immersive-mode",
3 | "version": "2.0.2",
4 | "description": "Set Immersive mode for Android (hide status bar or navigation bar bottom)",
5 | "main": "index.js",
6 | "keywords": [
7 | "react-native",
8 | "android",
9 | "immersive",
10 | "navigation-bar-bottom",
11 | "status-bar"
12 | ],
13 | "homepage": "https://github.com/wrathyz/react-native-immersive-mode",
14 | "bugs": {
15 | "url": "https://github.com/wrathyz/react-native-immersive-mode/issues"
16 | },
17 | "repository": {
18 | "type": "git",
19 | "url": "git://github.com/wrathyz/react-native-immersive-mode.git"
20 | },
21 | "author": "Wrathyz",
22 | "license": "MIT",
23 | "peerDependencies": {
24 | "react-native": ">=0.60.5"
25 | },
26 | "types": "./index.d.ts",
27 | "scripts": {
28 | "test": "echo \"Error: no test specified\" && exit 1"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------