├── example
├── .node-version
├── .watchmanconfig
├── .bundle
│ └── config
├── app.json
├── android
│ ├── app
│ │ ├── debug.keystore
│ │ ├── src
│ │ │ ├── main
│ │ │ │ ├── res
│ │ │ │ │ ├── values
│ │ │ │ │ │ ├── strings.xml
│ │ │ │ │ │ └── styles.xml
│ │ │ │ │ ├── mipmap-hdpi
│ │ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ │ ├── mipmap-mdpi
│ │ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ │ ├── mipmap-xhdpi
│ │ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ │ └── drawable
│ │ │ │ │ │ └── rn_edit_text_material.xml
│ │ │ │ ├── AndroidManifest.xml
│ │ │ │ └── java
│ │ │ │ │ └── com
│ │ │ │ │ └── systemnavigationbarexample
│ │ │ │ │ ├── MainActivity.java
│ │ │ │ │ └── MainApplication.java
│ │ │ ├── debug
│ │ │ │ ├── AndroidManifest.xml
│ │ │ │ └── java
│ │ │ │ │ └── com
│ │ │ │ │ └── systemnavigationbarexample
│ │ │ │ │ └── ReactNativeFlipper.java
│ │ │ └── release
│ │ │ │ └── java
│ │ │ │ └── com
│ │ │ │ └── systemnavigationbarexample
│ │ │ │ └── ReactNativeFlipper.java
│ │ ├── proguard-rules.pro
│ │ └── build.gradle
│ ├── gradle
│ │ └── wrapper
│ │ │ ├── gradle-wrapper.jar
│ │ │ └── gradle-wrapper.properties
│ ├── settings.gradle
│ ├── build.gradle
│ ├── gradle.properties
│ ├── gradlew.bat
│ └── gradlew
├── index.js
├── react-native.config.js
├── babel.config.js
├── package.json
├── metro.config.js
└── src
│ └── App.tsx
├── src
├── __tests__
│ └── index.test.tsx
└── index.tsx
├── .gitattributes
├── tsconfig.build.json
├── babel.config.js
├── screenshots
├── hide.gif
├── bar-mode.gif
├── immersive.gif
├── lean-back.gif
├── low-profile.gif
├── divider-color.gif
├── navigation-color.gif
├── sticky-immersive.gif
└── fits-systems-windows.gif
├── android
├── src
│ └── main
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ └── com
│ │ └── reactnativesystemnavigationbar
│ │ ├── SystemNavigationBarPackage.java
│ │ └── SystemNavigationBarModule.java
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── build.gradle
├── gradlew.bat
└── gradlew
├── .yarnrc
├── .editorconfig
├── .gitignore
├── scripts
└── bootstrap.js
├── tsconfig.json
├── lefthook.yml
├── LICENSE
├── .github
└── ISSUE_TEMPLATE
│ └── bug-report.yml
├── package.json
└── README.md
/example/.node-version:
--------------------------------------------------------------------------------
1 | 18
2 |
--------------------------------------------------------------------------------
/example/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/src/__tests__/index.test.tsx:
--------------------------------------------------------------------------------
1 | it.todo('write a test');
2 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.pbxproj -text
2 | # specific for windows script files
3 | *.bat text eol=crlf
--------------------------------------------------------------------------------
/example/.bundle/config:
--------------------------------------------------------------------------------
1 | BUNDLE_PATH: "vendor/bundle"
2 | BUNDLE_FORCE_RUBY_PLATFORM: 1
3 |
--------------------------------------------------------------------------------
/tsconfig.build.json:
--------------------------------------------------------------------------------
1 |
2 | {
3 | "extends": "./tsconfig",
4 | "exclude": ["example"]
5 | }
6 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: ['module:metro-react-native-babel-preset'],
3 | };
4 |
--------------------------------------------------------------------------------
/example/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "SystemNavigationBarExample",
3 | "displayName": "SystemNavigationBarExample"
4 | }
--------------------------------------------------------------------------------
/screenshots/hide.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kadiraydinli/react-native-system-navigation-bar/HEAD/screenshots/hide.gif
--------------------------------------------------------------------------------
/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/screenshots/bar-mode.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kadiraydinli/react-native-system-navigation-bar/HEAD/screenshots/bar-mode.gif
--------------------------------------------------------------------------------
/screenshots/immersive.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kadiraydinli/react-native-system-navigation-bar/HEAD/screenshots/immersive.gif
--------------------------------------------------------------------------------
/screenshots/lean-back.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kadiraydinli/react-native-system-navigation-bar/HEAD/screenshots/lean-back.gif
--------------------------------------------------------------------------------
/.yarnrc:
--------------------------------------------------------------------------------
1 | # Override Yarn command so we can automatically setup the repo on running `yarn`
2 |
3 | yarn-path "scripts/bootstrap.js"
4 |
--------------------------------------------------------------------------------
/screenshots/low-profile.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kadiraydinli/react-native-system-navigation-bar/HEAD/screenshots/low-profile.gif
--------------------------------------------------------------------------------
/screenshots/divider-color.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kadiraydinli/react-native-system-navigation-bar/HEAD/screenshots/divider-color.gif
--------------------------------------------------------------------------------
/screenshots/navigation-color.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kadiraydinli/react-native-system-navigation-bar/HEAD/screenshots/navigation-color.gif
--------------------------------------------------------------------------------
/screenshots/sticky-immersive.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kadiraydinli/react-native-system-navigation-bar/HEAD/screenshots/sticky-immersive.gif
--------------------------------------------------------------------------------
/example/android/app/debug.keystore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kadiraydinli/react-native-system-navigation-bar/HEAD/example/android/app/debug.keystore
--------------------------------------------------------------------------------
/example/android/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | SystemNavigationBarExample
3 |
4 |
--------------------------------------------------------------------------------
/screenshots/fits-systems-windows.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kadiraydinli/react-native-system-navigation-bar/HEAD/screenshots/fits-systems-windows.gif
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kadiraydinli/react-native-system-navigation-bar/HEAD/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/example/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kadiraydinli/react-native-system-navigation-bar/HEAD/example/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kadiraydinli/react-native-system-navigation-bar/HEAD/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kadiraydinli/react-native-system-navigation-bar/HEAD/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kadiraydinli/react-native-system-navigation-bar/HEAD/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kadiraydinli/react-native-system-navigation-bar/HEAD/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/index.js:
--------------------------------------------------------------------------------
1 | import { AppRegistry } from 'react-native';
2 | import App from './src/App';
3 | import { name as appName } from './app.json';
4 |
5 | AppRegistry.registerComponent(appName, () => App);
6 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kadiraydinli/react-native-system-navigation-bar/HEAD/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kadiraydinli/react-native-system-navigation-bar/HEAD/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kadiraydinli/react-native-system-navigation-bar/HEAD/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kadiraydinli/react-native-system-navigation-bar/HEAD/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kadiraydinli/react-native-system-navigation-bar/HEAD/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kadiraydinli/react-native-system-navigation-bar/HEAD/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/example/react-native.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const pak = require('../package.json');
3 |
4 | module.exports = {
5 | dependencies: {
6 | [pak.name]: {
7 | root: path.join(__dirname, '..'),
8 | },
9 | },
10 | };
11 |
--------------------------------------------------------------------------------
/example/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-all.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/example/android/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'SystemNavigationBarExample'
2 | apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
3 | include ':app'
4 | includeBuild('../node_modules/react-native-gradle-plugin')
5 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig helps developers define and maintain consistent
2 | # coding styles between different editors and IDEs
3 | # editorconfig.org
4 |
5 | root = true
6 |
7 | [*]
8 |
9 | indent_style = space
10 | indent_size = 2
11 |
12 | end_of_line = lf
13 | charset = utf-8
14 | trim_trailing_whitespace = true
15 | insert_final_newline = true
16 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/example/babel.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const pak = require('../package.json');
3 |
4 | module.exports = {
5 | presets: ['module:metro-react-native-babel-preset'],
6 | plugins: [
7 | [
8 | 'module-resolver',
9 | {
10 | extensions: ['.tsx', '.ts', '.js', '.json'],
11 | alias: {
12 | [pak.name]: path.join(__dirname, '..', pak.source),
13 | },
14 | },
15 | ],
16 | ],
17 | };
18 |
--------------------------------------------------------------------------------
/example/android/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
--------------------------------------------------------------------------------
/example/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # OSX
2 | #
3 | .DS_Store
4 |
5 | # XDE
6 | .expo/
7 |
8 | # VSCode
9 | .vscode/
10 | jsconfig.json
11 |
12 | # Android/IJ
13 | #
14 | .classpath
15 | .cxx
16 | .gradle
17 | .idea
18 | .project
19 | .settings
20 | local.properties
21 | android.iml
22 | *.hprof
23 |
24 | # Ruby
25 | example/vendor/
26 |
27 | # node.js
28 | #
29 | node_modules/
30 | npm-debug.log
31 | yarn-debug.log
32 | yarn-error.log
33 |
34 | # BUCK
35 | buck-out/
36 | \.buckd/
37 | example/android/app/build
38 | android/build
39 | android/app/libs
40 | android/keystores/debug.keystore
41 |
42 | # Expo
43 | .expo/*
44 |
45 | # generated by bob
46 | lib/
47 |
--------------------------------------------------------------------------------
/example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "SystemNavigationBarExample",
3 | "description": "Example app for react-native-system-navigation-bar",
4 | "version": "0.0.1",
5 | "private": true,
6 | "scripts": {
7 | "android": "react-native run-android",
8 | "start": "react-native start"
9 | },
10 | "dependencies": {
11 | "react": "18.2.0",
12 | "react-native": "^0.71.10"
13 | },
14 | "devDependencies": {
15 | "@babel/core": "^7.22.1",
16 | "@babel/preset-env": "^7.22.4",
17 | "@babel/runtime": "^7.22.3",
18 | "babel-plugin-module-resolver": "^5.0.0",
19 | "metro-react-native-babel-preset": "0.73.9"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/example/android/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | ext {
5 | buildToolsVersion = "33.0.0"
6 | minSdkVersion = 21
7 | compileSdkVersion = 33
8 | targetSdkVersion = 33
9 |
10 | // We use NDK 23 which has both M1 support and is the side-by-side NDK version from AGP.
11 | ndkVersion = "23.1.7779620"
12 | }
13 | repositories {
14 | google()
15 | mavenCentral()
16 | }
17 | dependencies {
18 | classpath("com.android.tools.build:gradle:7.3.1")
19 | classpath("com.facebook.react:react-native-gradle-plugin")
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/scripts/bootstrap.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const child_process = require('child_process');
3 |
4 | const root = path.resolve(__dirname, '..');
5 | const args = process.argv.slice(2);
6 | const options = {
7 | cwd: process.cwd(),
8 | env: process.env,
9 | stdio: 'inherit',
10 | encoding: 'utf-8',
11 | };
12 |
13 | let result;
14 |
15 | if (process.cwd() !== root || args.length) {
16 | // We're not in the root of the project, or additional arguments were passed
17 | // In this case, forward the command to `yarn`
18 | result = child_process.spawnSync('yarn', args, options);
19 | } else {
20 | // If `yarn` is run without arguments, perform bootstrap
21 | result = child_process.spawnSync('yarn', ['bootstrap'], options);
22 | }
23 |
24 | process.exitCode = result.status;
25 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": "./",
4 | "paths": {
5 | "react-native-system-navigation-bar": ["./src/index"]
6 | },
7 | "allowUnreachableCode": false,
8 | "allowUnusedLabels": false,
9 | "esModuleInterop": true,
10 | "importsNotUsedAsValues": "error",
11 | "forceConsistentCasingInFileNames": true,
12 | "jsx": "react",
13 | "lib": ["esnext"],
14 | "module": "esnext",
15 | "moduleResolution": "node",
16 | "noFallthroughCasesInSwitch": true,
17 | "noImplicitReturns": true,
18 | "noImplicitUseStrict": false,
19 | "noStrictGenericChecks": false,
20 | "noUnusedLocals": true,
21 | "noUnusedParameters": true,
22 | "resolveJsonModule": true,
23 | "skipLibCheck": true,
24 | "strict": true,
25 | "target": "esnext"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/example/android/app/src/release/java/com/systemnavigationbarexample/ReactNativeFlipper.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) Meta Platforms, Inc. and affiliates.
3 | *
4 | *
This source code is licensed under the MIT license found in the LICENSE file in the root
5 | * directory of this source tree.
6 | */
7 | package com.systemnavigationbarexample;
8 |
9 | import android.content.Context;
10 | import com.facebook.react.ReactInstanceManager;
11 |
12 | /**
13 | * Class responsible of loading Flipper inside your React Native application. This is the release
14 | * flavor of it so it's empty as we don't want to load Flipper.
15 | */
16 | public class ReactNativeFlipper {
17 | public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
18 | // Do nothing as we don't want to initialize Flipper on Release.
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/lefthook.yml:
--------------------------------------------------------------------------------
1 | # EXAMPLE USAGE
2 | # Refer for explanation to following link:
3 | # https://github.com/evilmartians/lefthook/blob/master/docs/full_guide.md
4 | #
5 | # pre-push:
6 | # commands:
7 | # packages-audit:
8 | # tags: frontend security
9 | # run: yarn audit
10 | # gems-audit:
11 | # tags: backend security
12 | # run: bundle audit
13 | #
14 | # pre-commit:
15 | # parallel: true
16 | # commands:
17 | # eslint:
18 | # glob: "*.{js,ts}"
19 | # run: yarn eslint {staged_files}
20 | # rubocop:
21 | # tags: backend style
22 | # glob: "*.rb"
23 | # exclude: "application.rb|routes.rb"
24 | # run: bundle exec rubocop --force-exclusion {all_files}
25 | # govet:
26 | # tags: backend style
27 | # files: git ls-files -m
28 | # glob: "*.go"
29 | # run: go vet {files}
30 | # scripts:
31 | # "hello.js":
32 | # runner: node
33 | # "any.go":
34 | # runner: go run
35 |
--------------------------------------------------------------------------------
/android/src/main/java/com/reactnativesystemnavigationbar/SystemNavigationBarPackage.java:
--------------------------------------------------------------------------------
1 | package com.reactnativesystemnavigationbar;
2 |
3 | import androidx.annotation.NonNull;
4 |
5 | import com.facebook.react.ReactPackage;
6 | import com.facebook.react.bridge.NativeModule;
7 | import com.facebook.react.bridge.ReactApplicationContext;
8 | import com.facebook.react.uimanager.ViewManager;
9 |
10 | import java.util.ArrayList;
11 | import java.util.Collections;
12 | import java.util.List;
13 |
14 | public class SystemNavigationBarPackage implements ReactPackage {
15 | @NonNull
16 | @Override
17 | public List createNativeModules(@NonNull ReactApplicationContext reactContext) {
18 | List modules = new ArrayList<>();
19 | modules.add(new SystemNavigationBarModule(reactContext));
20 | return modules;
21 | }
22 |
23 | @NonNull
24 | @Override
25 | public List createViewManagers(@NonNull ReactApplicationContext reactContext) {
26 | return Collections.emptyList();
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/example/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
12 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Kadir Aydınlı
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 |
--------------------------------------------------------------------------------
/example/metro.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const escape = require('escape-string-regexp');
3 | const exclusionList = require('metro-config/src/defaults/exclusionList');
4 | const pak = require('../package.json');
5 |
6 | const root = path.resolve(__dirname, '..');
7 |
8 | const modules = Object.keys({
9 | ...pak.peerDependencies,
10 | });
11 |
12 | module.exports = {
13 | projectRoot: __dirname,
14 | watchFolders: [root],
15 |
16 | // We need to make sure that only one version is loaded for peerDependencies
17 | // So we block them at the root, and alias them to the versions in example's node_modules
18 | resolver: {
19 | blacklistRE: exclusionList(
20 | modules.map(
21 | (m) =>
22 | new RegExp(`^${escape(path.join(root, 'node_modules', m))}\\/.*$`)
23 | )
24 | ),
25 |
26 | extraNodeModules: modules.reduce((acc, name) => {
27 | acc[name] = path.join(__dirname, 'node_modules', name);
28 | return acc;
29 | }, {}),
30 | },
31 |
32 | transformer: {
33 | getTransformOptions: async () => ({
34 | transform: {
35 | experimentalImportSupport: false,
36 | inlineRequires: true,
37 | },
38 | }),
39 | },
40 | };
41 |
--------------------------------------------------------------------------------
/example/android/app/src/main/java/com/systemnavigationbarexample/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.systemnavigationbarexample;
2 |
3 | import com.facebook.react.ReactActivity;
4 | import com.facebook.react.ReactActivityDelegate;
5 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
6 | import com.facebook.react.defaults.DefaultReactActivityDelegate;
7 |
8 | public class MainActivity extends ReactActivity {
9 |
10 | /**
11 | * Returns the name of the main component registered from JavaScript. This is used to schedule
12 | * rendering of the component.
13 | */
14 | @Override
15 | protected String getMainComponentName() {
16 | return "SystemNavigationBarExample";
17 | }
18 |
19 | /**
20 | * Returns the instance of the {@link ReactActivityDelegate}. Here we use a util class {@link
21 | * DefaultReactActivityDelegate} which allows you to easily enable Fabric and Concurrent React
22 | * (aka React 18) with two boolean flags.
23 | */
24 | @Override
25 | protected ReactActivityDelegate createReactActivityDelegate() {
26 | return new DefaultReactActivityDelegate(
27 | this,
28 | getMainComponentName(),
29 | // If you opted-in for the New Architecture, we enable the Fabric Renderer.
30 | DefaultNewArchitectureEntryPoint.getFabricEnabled(), // fabricEnabled
31 | // If you opted-in for the New Architecture, we enable Concurrent React (i.e. React 18).
32 | DefaultNewArchitectureEntryPoint.getConcurrentReactEnabled() // concurrentRootEnabled
33 | );
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | if (project == rootProject) {
3 | repositories {
4 | google()
5 | mavenCentral()
6 | }
7 |
8 | dependencies {
9 | classpath "com.android.tools.build:gradle:8.1.4"
10 | }
11 | }
12 | }
13 |
14 | apply plugin: 'com.android.library'
15 |
16 | def safeExtGet(prop, fallback) {
17 | rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
18 | }
19 |
20 | android {
21 | namespace "com.reactnativesystemnavigationbar"
22 | compileSdkVersion safeExtGet('compileSdkVersion', 30)
23 | defaultConfig {
24 | minSdkVersion safeExtGet('minSdkVersion', 16)
25 | targetSdkVersion safeExtGet('targetSdkVersion', 30)
26 | versionCode 1
27 | versionName "1.0"
28 | }
29 |
30 | buildTypes {
31 | release {
32 | minifyEnabled false
33 | }
34 | }
35 | lintOptions {
36 | disable 'GradleCompatible'
37 | }
38 | compileOptions {
39 | sourceCompatibility JavaVersion.VERSION_1_8
40 | targetCompatibility JavaVersion.VERSION_1_8
41 | }
42 | }
43 |
44 | repositories {
45 | mavenLocal()
46 | maven {
47 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
48 | url("$rootDir/../node_modules/react-native/android")
49 | }
50 | google()
51 | mavenCentral()
52 | }
53 |
54 | dependencies {
55 | //noinspection GradleDynamicVersion
56 | implementation "com.facebook.react:react-native:+" // From node_modules
57 | }
58 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug-report.yml:
--------------------------------------------------------------------------------
1 | name: 🐛 Bug Report
2 | description: Report a reproducible or regression bug.
3 | labels: ["Needs: Triage :mag:"]
4 | body:
5 | - type: markdown
6 | attributes:
7 | value: Thanks for taking the time to fill out this bug report!
8 | - type: textarea
9 | id: description
10 | attributes:
11 | label: Description
12 | description: Please provide a clear and concise description of what the bug is. Include screenshots if needed. Test using the [latest react-native-system-navigation-bar release](https://github.com/kadiraydinli/react-native-system-navigation-bar/releases/latest) to make sure your issue has not already been fixed.
13 | validations:
14 | required: true
15 | - type: input
16 | id: package-version
17 | attributes:
18 | label: react-native-system-navigation-bar version
19 | description: What is the latest version of react-native-system-navigation-bar that this issue reproduces on? Please only list the highest version you tested.
20 | placeholder: ex. 2.5.0
21 | validations:
22 | required: true
23 | - type: input
24 | id: react-native-version
25 | attributes:
26 | label: React Native version
27 | description: What is the latest version of react-native that this issue reproduces on? Please only list the highest version you tested.
28 | placeholder: ex. 0.71.0
29 | validations:
30 | required: true
31 | - type: textarea
32 | id: extra
33 | attributes:
34 | label: Snack, code example, screenshot, or link to a repository
35 | description: |
36 | Please provide a Snack (https://snack.expo.dev/), a link to a repository on GitHub, or provide a minimal code example that reproduces the problem.
37 | You may provide a screenshot of the application if you think it is relevant to your bug report.
38 | validations:
39 | required: false
--------------------------------------------------------------------------------
/example/android/app/src/main/res/drawable/rn_edit_text_material.xml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
21 |
22 |
23 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/example/android/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx512m -XX:MaxMetaspaceSize=256m
13 | org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
19 |
20 | # AndroidX package structure to make it clearer which packages are bundled with the
21 | # Android operating system, and which are packaged with your app's APK
22 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
23 | android.useAndroidX=true
24 | # Automatically convert third-party libraries to use AndroidX
25 | android.enableJetifier=true
26 |
27 | # Version of flipper SDK to use with React Native
28 | FLIPPER_VERSION=0.125.0
29 |
30 | # Use this property to specify which architecture you want to build.
31 | # You can also override it from the CLI using
32 | # ./gradlew -PreactNativeArchitectures=x86_64
33 | reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
34 |
35 | # Use this property to enable support to the new architecture.
36 | # This will allow you to use TurboModules and the Fabric render in
37 | # your application. You should enable this flag either if you want
38 | # to write custom TurboModules/Fabric components OR use libraries that
39 | # are providing them.
40 | newArchEnabled=false
41 |
42 | # Use this property to enable or disable the Hermes JS engine.
43 | # If set to false, you will be using JSC instead.
44 | hermesEnabled=true
45 |
--------------------------------------------------------------------------------
/example/android/app/src/main/java/com/systemnavigationbarexample/MainApplication.java:
--------------------------------------------------------------------------------
1 | package com.systemnavigationbarexample;
2 |
3 | import android.app.Application;
4 | import com.facebook.react.PackageList;
5 | import com.facebook.react.ReactApplication;
6 | import com.facebook.react.ReactNativeHost;
7 | import com.facebook.react.ReactPackage;
8 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
9 | import com.facebook.react.defaults.DefaultReactNativeHost;
10 | import com.facebook.soloader.SoLoader;
11 | import java.util.List;
12 |
13 | public class MainApplication extends Application implements ReactApplication {
14 |
15 | private final ReactNativeHost mReactNativeHost =
16 | new DefaultReactNativeHost(this) {
17 | @Override
18 | public boolean getUseDeveloperSupport() {
19 | return BuildConfig.DEBUG;
20 | }
21 |
22 | @Override
23 | protected List getPackages() {
24 | @SuppressWarnings("UnnecessaryLocalVariable")
25 | List packages = new PackageList(this).getPackages();
26 | // Packages that cannot be autolinked yet can be added manually here, for example:
27 | // packages.add(new MyReactNativePackage());
28 | return packages;
29 | }
30 |
31 | @Override
32 | protected String getJSMainModuleName() {
33 | return "index";
34 | }
35 |
36 | @Override
37 | protected boolean isNewArchEnabled() {
38 | return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
39 | }
40 |
41 | @Override
42 | protected Boolean isHermesEnabled() {
43 | return BuildConfig.IS_HERMES_ENABLED;
44 | }
45 | };
46 |
47 | @Override
48 | public ReactNativeHost getReactNativeHost() {
49 | return mReactNativeHost;
50 | }
51 |
52 | @Override
53 | public void onCreate() {
54 | super.onCreate();
55 | SoLoader.init(this, /* native exopackage */ false);
56 | if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
57 | // If you opted-in for the New Architecture, we load the native entry point for this app.
58 | DefaultNewArchitectureEntryPoint.load();
59 | }
60 | ReactNativeFlipper.initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/example/android/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/android/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%"=="" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%"=="" set DIRNAME=.
29 | @rem This is normally unused
30 | set APP_BASE_NAME=%~n0
31 | set APP_HOME=%DIRNAME%
32 |
33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
35 |
36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
38 |
39 | @rem Find java.exe
40 | if defined JAVA_HOME goto findJavaFromJavaHome
41 |
42 | set JAVA_EXE=java.exe
43 | %JAVA_EXE% -version >NUL 2>&1
44 | if %ERRORLEVEL% equ 0 goto execute
45 |
46 | echo.
47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
48 | echo.
49 | echo Please set the JAVA_HOME variable in your environment to match the
50 | echo location of your Java installation.
51 |
52 | goto fail
53 |
54 | :findJavaFromJavaHome
55 | set JAVA_HOME=%JAVA_HOME:"=%
56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
57 |
58 | if exist "%JAVA_EXE%" goto execute
59 |
60 | echo.
61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
62 | echo.
63 | echo Please set the JAVA_HOME variable in your environment to match the
64 | echo location of your Java installation.
65 |
66 | goto fail
67 |
68 | :execute
69 | @rem Setup the command line
70 |
71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
72 |
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if %ERRORLEVEL% equ 0 goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | set EXIT_CODE=%ERRORLEVEL%
85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
87 | exit /b %EXIT_CODE%
88 |
89 | :mainEnd
90 | if "%OS%"=="Windows_NT" endlocal
91 |
92 | :omega
93 |
--------------------------------------------------------------------------------
/example/android/app/src/debug/java/com/systemnavigationbarexample/ReactNativeFlipper.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) Meta Platforms, Inc. and affiliates.
3 | *
4 | * This source code is licensed under the MIT license found in the LICENSE file in the root
5 | * directory of this source tree.
6 | */
7 | package com.systemnavigationbarexample;
8 |
9 | import android.content.Context;
10 | import com.facebook.flipper.android.AndroidFlipperClient;
11 | import com.facebook.flipper.android.utils.FlipperUtils;
12 | import com.facebook.flipper.core.FlipperClient;
13 | import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin;
14 | import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin;
15 | import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin;
16 | import com.facebook.flipper.plugins.inspector.DescriptorMapping;
17 | import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin;
18 | import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor;
19 | import com.facebook.flipper.plugins.network.NetworkFlipperPlugin;
20 | import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin;
21 | import com.facebook.react.ReactInstanceEventListener;
22 | import com.facebook.react.ReactInstanceManager;
23 | import com.facebook.react.bridge.ReactContext;
24 | import com.facebook.react.modules.network.NetworkingModule;
25 | import okhttp3.OkHttpClient;
26 |
27 | /**
28 | * Class responsible of loading Flipper inside your React Native application. This is the debug
29 | * flavor of it. Here you can add your own plugins and customize the Flipper setup.
30 | */
31 | public class ReactNativeFlipper {
32 | public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
33 | if (FlipperUtils.shouldEnableFlipper(context)) {
34 | final FlipperClient client = AndroidFlipperClient.getInstance(context);
35 |
36 | client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults()));
37 | client.addPlugin(new DatabasesFlipperPlugin(context));
38 | client.addPlugin(new SharedPreferencesFlipperPlugin(context));
39 | client.addPlugin(CrashReporterPlugin.getInstance());
40 |
41 | NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin();
42 | NetworkingModule.setCustomClientBuilder(
43 | new NetworkingModule.CustomClientBuilder() {
44 | @Override
45 | public void apply(OkHttpClient.Builder builder) {
46 | builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin));
47 | }
48 | });
49 | client.addPlugin(networkFlipperPlugin);
50 | client.start();
51 |
52 | // Fresco Plugin needs to ensure that ImagePipelineFactory is initialized
53 | // Hence we run if after all native modules have been initialized
54 | ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
55 | if (reactContext == null) {
56 | reactInstanceManager.addReactInstanceEventListener(
57 | new ReactInstanceEventListener() {
58 | @Override
59 | public void onReactContextInitialized(ReactContext reactContext) {
60 | reactInstanceManager.removeReactInstanceEventListener(this);
61 | reactContext.runOnNativeModulesQueueThread(
62 | new Runnable() {
63 | @Override
64 | public void run() {
65 | client.addPlugin(new FrescoFlipperPlugin());
66 | }
67 | });
68 | }
69 | });
70 | } else {
71 | client.addPlugin(new FrescoFlipperPlugin());
72 | }
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/example/src/App.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import { StyleSheet, View, Button } from 'react-native';
4 | import SystemNavigationBar, {
5 | GetBarColorType,
6 | } from 'react-native-system-navigation-bar';
7 |
8 | export default function App() {
9 | return (
10 |
11 | {
14 | const result = await SystemNavigationBar.navigationHide();
15 |
16 | console.log('Hide: ', result);
17 | }}
18 | />
19 |
20 | {
23 | const result = await SystemNavigationBar.navigationShow();
24 |
25 | console.log('Show: ', result);
26 | }}
27 | />
28 |
29 | {
32 | const result = await SystemNavigationBar.leanBack();
33 |
34 | console.log('leanBack: ', result);
35 | }}
36 | />
37 |
38 | {
41 | const result = await SystemNavigationBar.immersive();
42 |
43 | console.log('immersive: ', result);
44 | }}
45 | />
46 |
47 | {
50 | const result = await SystemNavigationBar.stickyImmersive();
51 |
52 | console.log('stickyImmersive: ', result);
53 | }}
54 | />
55 |
56 | {
59 | const result = await SystemNavigationBar.setBarMode('dark');
60 |
61 | console.log('setBarMode: ', result);
62 | }}
63 | />
64 |
65 | {
68 | const result = await SystemNavigationBar.fullScreen();
69 |
70 | console.log('fullScreen: ', result);
71 | }}
72 | />
73 |
74 | {
77 | const result = await SystemNavigationBar.lowProfile();
78 |
79 | console.log('lowProfile: ', result);
80 | }}
81 | />
82 |
83 | {
86 | const result = await SystemNavigationBar.setNavigationColor('red');
87 |
88 | console.log('Color: ', result);
89 | }}
90 | />
91 |
92 | {
95 | const result = await SystemNavigationBar.setNavigationBarDividerColor(
96 | 'red'
97 | );
98 |
99 | console.log('Divider Color: ', result);
100 | }}
101 | />
102 |
103 | {
106 | const result =
107 | await SystemNavigationBar.setNavigationBarContrastEnforced(true);
108 |
109 | console.log('Contrast Enforced: ', result);
110 | }}
111 | />
112 |
113 | {
116 | const result: GetBarColorType = await SystemNavigationBar.getBarColor(
117 | 'both'
118 | );
119 | console.log(
120 | 'Get Bar Color: ',
121 | JSON.stringify(result as GetBarColorType)
122 | );
123 | }}
124 | />
125 |
126 | {
129 | const result = await SystemNavigationBar.setFitsSystemWindows(false);
130 | console.log('setFitsSystemWindows: ', result);
131 | }}
132 | />
133 |
134 | );
135 | }
136 |
137 | const styles = StyleSheet.create({
138 | container: {
139 | flex: 1,
140 | justifyContent: 'space-evenly',
141 | alignItems: 'center',
142 | backgroundColor: '#FFFF',
143 | },
144 | });
145 |
--------------------------------------------------------------------------------
/src/index.tsx:
--------------------------------------------------------------------------------
1 | import { NativeModules, Platform, processColor } from 'react-native';
2 |
3 | const { NavigationBar } = NativeModules;
4 |
5 | const navigationHide = async () => {
6 | if (Platform.OS === 'android') {
7 | return await NavigationBar.navigationHide();
8 | }
9 | };
10 |
11 | const navigationShow = async () => {
12 | if (Platform.OS === 'android') {
13 | return await NavigationBar.navigationShow();
14 | }
15 | };
16 |
17 | const leanBack = async (enabled?: boolean) => {
18 | if (Platform.OS === 'android') {
19 | const state = typeof enabled === 'boolean' ? enabled : true;
20 | return await NavigationBar.leanBack(state);
21 | }
22 | };
23 |
24 | const immersive = async () => {
25 | if (Platform.OS === 'android') {
26 | return await NavigationBar.immersive();
27 | }
28 | };
29 |
30 | const stickyImmersive = async (enabled?: boolean) => {
31 | if (Platform.OS === 'android') {
32 | const state = typeof enabled === 'boolean' ? enabled : true;
33 | return await NavigationBar.stickyImmersive(state);
34 | }
35 | };
36 |
37 | const lowProfile = async (enabled?: boolean) => {
38 | if (Platform.OS === 'android') {
39 | const state = typeof enabled === 'boolean' ? enabled : true;
40 | return await NavigationBar.lowProfile(state);
41 | }
42 | };
43 |
44 | const getBarModeTypes = (
45 | style?: 'light' | 'dark',
46 | bar?: 'navigation' | 'status' | 'both'
47 | ) => {
48 | const modeStyle =
49 | style === 'light'
50 | ? NavigationBar.LIGHT
51 | : style === 'dark'
52 | ? NavigationBar.DARK
53 | : NavigationBar.NO_MODE;
54 |
55 | const mode =
56 | bar === 'navigation'
57 | ? NavigationBar.NAVIGATION_BAR
58 | : bar === 'status'
59 | ? NavigationBar.STATUS_BAR
60 | : NavigationBar.NAVIGATION_BAR_STATUS_BAR;
61 |
62 | return {
63 | modeStyle,
64 | mode,
65 | };
66 | };
67 |
68 | const setBarMode = async (
69 | style?: 'light' | 'dark',
70 | bar?: 'navigation' | 'status' | 'both'
71 | ) => {
72 | if (Platform.OS === 'android') {
73 | const { modeStyle, mode } = getBarModeTypes(style, bar);
74 | return await NavigationBar.setBarMode(modeStyle, mode);
75 | }
76 | };
77 |
78 | const setNavigationColor = async (
79 | color: string | number,
80 | style?: 'light' | 'dark',
81 | bar?: 'navigation' | 'status' | 'both'
82 | ) => {
83 | if (Platform.OS === 'android') {
84 | const { modeStyle, mode } = getBarModeTypes(style, bar);
85 | return await NavigationBar.setNavigationColor(
86 | color === 'translucent' ? 0 : processColor(color),
87 | color === 'translucent',
88 | modeStyle,
89 | mode
90 | );
91 | }
92 | };
93 |
94 | const setNavigationBarDividerColor = async (color: string | number) => {
95 | if (Platform.OS === 'android') {
96 | return await NavigationBar.setNavigationBarDividerColor(
97 | processColor(color)
98 | );
99 | }
100 | };
101 |
102 | const setNavigationBarContrastEnforced = async (enabled?: boolean) => {
103 | if (Platform.OS === 'android') {
104 | const state = typeof enabled === 'boolean' ? enabled : true;
105 | return await NavigationBar.setNavigationBarContrastEnforced(state);
106 | }
107 | };
108 |
109 | const fullScreen = async (enabled?: boolean) => {
110 | if (Platform.OS === 'android') {
111 | const state = typeof enabled === 'boolean' ? enabled : true;
112 | return await NavigationBar.fullScreen(state);
113 | }
114 | };
115 |
116 | export type GetBarColorType = string | { status: string; navigation: string };
117 |
118 | const getBarColor = async (
119 | bar?: 'navigation' | 'status' | 'both'
120 | ): Promise => {
121 | if (Platform.OS === 'android') {
122 | const { mode } = getBarModeTypes('light', bar || 'both');
123 | const result = await NavigationBar.getBarColor(mode);
124 |
125 | if (mode === NavigationBar.NAVIGATION_BAR_STATUS_BAR) {
126 | return JSON.parse(result);
127 | } else {
128 | return result;
129 | }
130 | }
131 | return '';
132 | };
133 |
134 | const setFitsSystemWindows = async (enabled?: boolean) => {
135 | if (Platform.OS === 'android') {
136 | const state = typeof enabled === 'boolean' ? enabled : true;
137 | return await NavigationBar.setFitsSystemWindows(state);
138 | }
139 | };
140 |
141 | var SystemNavigationBar = {
142 | navigationHide,
143 | navigationShow,
144 | leanBack,
145 | immersive,
146 | stickyImmersive,
147 | lowProfile,
148 | setBarMode,
149 | setNavigationColor,
150 | setNavigationBarDividerColor,
151 | setNavigationBarContrastEnforced,
152 | fullScreen,
153 | getBarColor,
154 | setFitsSystemWindows,
155 | };
156 |
157 | export default SystemNavigationBar;
158 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-native-system-navigation-bar",
3 | "version": "2.8.0",
4 | "description": "React Native lets you customize the navigation bar for Android.",
5 | "main": "lib/commonjs/index",
6 | "module": "lib/module/index",
7 | "types": "lib/typescript/index.d.ts",
8 | "react-native": "src/index",
9 | "source": "src/index",
10 | "files": [
11 | "src",
12 | "lib",
13 | "android",
14 | "cpp",
15 | "!lib/typescript/example",
16 | "!android/build",
17 | "!**/__tests__",
18 | "!**/__fixtures__",
19 | "!**/__mocks__"
20 | ],
21 | "scripts": {
22 | "test": "jest",
23 | "typescript": "tsc --noEmit",
24 | "lint": "eslint \"**/*.{js,ts,tsx}\"",
25 | "prepare": "bob build",
26 | "release": "release-it",
27 | "example": "yarn --cwd example",
28 | "bootstrap": "yarn example && yarn"
29 | },
30 | "keywords": [
31 | "react-native",
32 | "system-navigation-bar",
33 | "android",
34 | "navigation",
35 | "navigation-bar",
36 | "bar",
37 | "color",
38 | "bar-color",
39 | "navigation-bar-color",
40 | "hide",
41 | "show",
42 | "lean-back",
43 | "immersive",
44 | "stickyImmersive",
45 | "lowProfile",
46 | "barMode",
47 | "setBarMode",
48 | "light",
49 | "dark",
50 | "light-style",
51 | "dark-style",
52 | "status-bar-mode",
53 | "divider-color",
54 | "navigation-bar-divider-color",
55 | "contrast-enforced",
56 | "navigation-bar-contrast-enforced",
57 | "full-screen",
58 | "setFitsSystemWindows",
59 | "fitsSystemWindows",
60 | "decorFitsSystemWindows",
61 | "setDecorFitsSystemWindows"
62 | ],
63 | "repository": {
64 | "type": "git",
65 | "url": "git+https://github.com/kadiraydinli/react-native-system-navigation-bar.git"
66 | },
67 | "author": "Kadir Aydınlı (https://github.com/kadiraydinli)",
68 | "license": "MIT",
69 | "bugs": {
70 | "url": "https://github.com/kadiraydinli/react-native-system-navigation-bar/issues"
71 | },
72 | "homepage": "https://github.com/kadiraydinli/react-native-system-navigation-bar#readme",
73 | "publishConfig": {
74 | "registry": "https://registry.npmjs.org/"
75 | },
76 | "devDependencies": {
77 | "@babel/eslint-parser": "^7.21.8",
78 | "@commitlint/config-conventional": "^17.6.3",
79 | "@react-native-community/eslint-config": "^3.0.2",
80 | "@release-it/conventional-changelog": "^5.0.0",
81 | "@types/jest": "^28.1.2",
82 | "@types/react": "~17.0.21",
83 | "@types/react-native": "0.68.0",
84 | "commitlint": "^17.6.3",
85 | "eslint": "^8.40.0",
86 | "eslint-config-prettier": "^8.5.0",
87 | "eslint-plugin-prettier": "^4.0.0",
88 | "jest": "^28.1.1",
89 | "prettier": "^2.8.8",
90 | "react": "17.0.2",
91 | "react-native": "0.68.2",
92 | "react-native-builder-bob": "^0.18.3",
93 | "release-it": "^15.10.3",
94 | "typescript": "^4.5.2"
95 | },
96 | "resolutions": {
97 | "@types/react": "17.0.21"
98 | },
99 | "peerDependencies": {
100 | "react": "*",
101 | "react-native": "*"
102 | },
103 | "jest": {
104 | "preset": "react-native",
105 | "modulePathIgnorePatterns": [
106 | "/example/node_modules",
107 | "/lib/"
108 | ]
109 | },
110 | "husky": {
111 | "hooks": {
112 | "commit-msg": "commitlint -E HUSKY_GIT_PARAMS",
113 | "pre-commit": "yarn lint && yarn typescript"
114 | }
115 | },
116 | "commitlint": {
117 | "extends": [
118 | "@commitlint/config-conventional"
119 | ]
120 | },
121 | "release-it": {
122 | "git": {
123 | "commitMessage": "chore: release ${version}",
124 | "tagName": "v${version}"
125 | },
126 | "npm": {
127 | "publish": true
128 | },
129 | "github": {
130 | "release": true
131 | },
132 | "plugins": {
133 | "@release-it/conventional-changelog": {
134 | "preset": "angular"
135 | }
136 | }
137 | },
138 | "eslintConfig": {
139 | "root": true,
140 | "parser": "@babel/eslint-parser",
141 | "extends": [
142 | "@react-native-community",
143 | "prettier"
144 | ],
145 | "rules": {
146 | "prettier/prettier": [
147 | "error",
148 | {
149 | "quoteProps": "consistent",
150 | "singleQuote": true,
151 | "tabWidth": 2,
152 | "trailingComma": "es5",
153 | "useTabs": false
154 | }
155 | ]
156 | }
157 | },
158 | "eslintIgnore": [
159 | "node_modules/",
160 | "lib/"
161 | ],
162 | "prettier": {
163 | "quoteProps": "consistent",
164 | "singleQuote": true,
165 | "tabWidth": 2,
166 | "trailingComma": "es5",
167 | "useTabs": false
168 | },
169 | "react-native-builder-bob": {
170 | "source": "src",
171 | "output": "lib",
172 | "targets": [
173 | "commonjs",
174 | "module",
175 | [
176 | "typescript",
177 | {
178 | "project": "tsconfig.build.json"
179 | }
180 | ]
181 | ]
182 | }
183 | }
--------------------------------------------------------------------------------
/example/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: "com.android.application"
2 | apply plugin: "com.facebook.react"
3 |
4 | import com.android.build.OutputFile
5 |
6 | /**
7 | * This is the configuration block to customize your React Native Android app.
8 | * By default you don't need to apply any configuration, just uncomment the lines you need.
9 | */
10 | react {
11 | /* Folders */
12 | // The root of your project, i.e. where "package.json" lives. Default is '..'
13 | // root = file("../")
14 | // The folder where the react-native NPM package is. Default is ../node_modules/react-native
15 | // reactNativeDir = file("../node_modules/react-native")
16 | // The folder where the react-native Codegen package is. Default is ../node_modules/react-native-codegen
17 | // codegenDir = file("../node_modules/react-native-codegen")
18 | // The cli.js file which is the React Native CLI entrypoint. Default is ../node_modules/react-native/cli.js
19 | // cliFile = file("../node_modules/react-native/cli.js")
20 |
21 | /* Variants */
22 | // The list of variants to that are debuggable. For those we're going to
23 | // skip the bundling of the JS bundle and the assets. By default is just 'debug'.
24 | // If you add flavors like lite, prod, etc. you'll have to list your debuggableVariants.
25 | // debuggableVariants = ["liteDebug", "prodDebug"]
26 |
27 | /* Bundling */
28 | // A list containing the node command and its flags. Default is just 'node'.
29 | // nodeExecutableAndArgs = ["node"]
30 | //
31 | // The command to run when bundling. By default is 'bundle'
32 | // bundleCommand = "ram-bundle"
33 | //
34 | // The path to the CLI configuration file. Default is empty.
35 | // bundleConfig = file(../rn-cli.config.js)
36 | //
37 | // The name of the generated asset file containing your JS bundle
38 | // bundleAssetName = "MyApplication.android.bundle"
39 | //
40 | // The entry file for bundle generation. Default is 'index.android.js' or 'index.js'
41 | // entryFile = file("../js/MyApplication.android.js")
42 | //
43 | // A list of extra flags to pass to the 'bundle' commands.
44 | // See https://github.com/react-native-community/cli/blob/main/docs/commands.md#bundle
45 | // extraPackagerArgs = []
46 |
47 | /* Hermes Commands */
48 | // The hermes compiler command to run. By default it is 'hermesc'
49 | // hermesCommand = "$rootDir/my-custom-hermesc/bin/hermesc"
50 | //
51 | // The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map"
52 | // hermesFlags = ["-O", "-output-source-map"]
53 | }
54 |
55 | /**
56 | * Set this to true to create four separate APKs instead of one,
57 | * one for each native architecture. This is useful if you don't
58 | * use App Bundles (https://developer.android.com/guide/app-bundle/)
59 | * and want to have separate APKs to upload to the Play Store.
60 | */
61 | def enableSeparateBuildPerCPUArchitecture = false
62 |
63 | /**
64 | * Set this to true to Run Proguard on Release builds to minify the Java bytecode.
65 | */
66 | def enableProguardInReleaseBuilds = false
67 |
68 | /**
69 | * The preferred build flavor of JavaScriptCore (JSC)
70 | *
71 | * For example, to use the international variant, you can use:
72 | * `def jscFlavor = 'org.webkit:android-jsc-intl:+'`
73 | *
74 | * The international variant includes ICU i18n library and necessary data
75 | * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
76 | * give correct results when using with locales other than en-US. Note that
77 | * this variant is about 6MiB larger per architecture than default.
78 | */
79 | def jscFlavor = 'org.webkit:android-jsc:+'
80 |
81 | /**
82 | * Private function to get the list of Native Architectures you want to build.
83 | * This reads the value from reactNativeArchitectures in your gradle.properties
84 | * file and works together with the --active-arch-only flag of react-native run-android.
85 | */
86 | def reactNativeArchitectures() {
87 | def value = project.getProperties().get("reactNativeArchitectures")
88 | return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
89 | }
90 |
91 | android {
92 | ndkVersion rootProject.ext.ndkVersion
93 |
94 | compileSdkVersion rootProject.ext.compileSdkVersion
95 |
96 | namespace "com.systemnavigationbarexample"
97 | defaultConfig {
98 | applicationId "com.systemnavigationbarexample"
99 | minSdkVersion rootProject.ext.minSdkVersion
100 | targetSdkVersion rootProject.ext.targetSdkVersion
101 | versionCode 1
102 | versionName "1.0"
103 | }
104 |
105 | splits {
106 | abi {
107 | reset()
108 | enable enableSeparateBuildPerCPUArchitecture
109 | universalApk false // If true, also generate a universal APK
110 | include (*reactNativeArchitectures())
111 | }
112 | }
113 | signingConfigs {
114 | debug {
115 | storeFile file('debug.keystore')
116 | storePassword 'android'
117 | keyAlias 'androiddebugkey'
118 | keyPassword 'android'
119 | }
120 | }
121 | buildTypes {
122 | debug {
123 | signingConfig signingConfigs.debug
124 | }
125 | release {
126 | // Caution! In production, you need to generate your own keystore file.
127 | // see https://reactnative.dev/docs/signed-apk-android.
128 | signingConfig signingConfigs.debug
129 | minifyEnabled enableProguardInReleaseBuilds
130 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
131 | }
132 | }
133 |
134 | // applicationVariants are e.g. debug, release
135 | applicationVariants.all { variant ->
136 | variant.outputs.each { output ->
137 | // For each separate APK per architecture, set a unique version code as described here:
138 | // https://developer.android.com/studio/build/configure-apk-splits.html
139 | // Example: versionCode 1 will generate 1001 for armeabi-v7a, 1002 for x86, etc.
140 | def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4]
141 | def abi = output.getFilter(OutputFile.ABI)
142 | if (abi != null) { // null for the universal-debug, universal-release variants
143 | output.versionCodeOverride =
144 | defaultConfig.versionCode * 1000 + versionCodes.get(abi)
145 | }
146 |
147 | }
148 | }
149 | }
150 |
151 | dependencies {
152 | // The version of react-native is set by the React Native Gradle Plugin
153 | implementation("com.facebook.react:react-android")
154 |
155 | implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.0.0")
156 |
157 | debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}")
158 | debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") {
159 | exclude group:'com.squareup.okhttp3', module:'okhttp'
160 | }
161 |
162 | debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}")
163 | if (hermesEnabled.toBoolean()) {
164 | implementation("com.facebook.react:hermes-android")
165 | } else {
166 | implementation jscFlavor
167 | }
168 | }
169 |
170 | apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
171 |
--------------------------------------------------------------------------------
/example/android/gradlew:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #
4 | # Copyright © 2015-2021 the original authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | #
21 | # Gradle start up script for POSIX generated by Gradle.
22 | #
23 | # Important for running:
24 | #
25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
26 | # noncompliant, but you have some other compliant shell such as ksh or
27 | # bash, then to run this script, type that shell name before the whole
28 | # command line, like:
29 | #
30 | # ksh Gradle
31 | #
32 | # Busybox and similar reduced shells will NOT work, because this script
33 | # requires all of these POSIX shell features:
34 | # * functions;
35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»;
37 | # * compound commands having a testable exit status, especially «case»;
38 | # * various built-in commands including «command», «set», and «ulimit».
39 | #
40 | # Important for patching:
41 | #
42 | # (2) This script targets any POSIX shell, so it avoids extensions provided
43 | # by Bash, Ksh, etc; in particular arrays are avoided.
44 | #
45 | # The "traditional" practice of packing multiple parameters into a
46 | # space-separated string is a well documented source of bugs and security
47 | # problems, so this is (mostly) avoided, by progressively accumulating
48 | # options in "$@", and eventually passing that to Java.
49 | #
50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
52 | # see the in-line comments for details.
53 | #
54 | # There are tweaks for specific operating systems such as AIX, CygWin,
55 | # Darwin, MinGW, and NonStop.
56 | #
57 | # (3) This script is generated from the Groovy template
58 | # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
59 | # within the Gradle project.
60 | #
61 | # You can find Gradle at https://github.com/gradle/gradle/.
62 | #
63 | ##############################################################################
64 |
65 | # Attempt to set APP_HOME
66 |
67 | # Resolve links: $0 may be a link
68 | app_path=$0
69 |
70 | # Need this for daisy-chained symlinks.
71 | while
72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
73 | [ -h "$app_path" ]
74 | do
75 | ls=$( ls -ld "$app_path" )
76 | link=${ls#*' -> '}
77 | case $link in #(
78 | /*) app_path=$link ;; #(
79 | *) app_path=$APP_HOME$link ;;
80 | esac
81 | done
82 |
83 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
84 |
85 | APP_NAME="Gradle"
86 | APP_BASE_NAME=${0##*/}
87 |
88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
90 |
91 | # Use the maximum available, or set MAX_FD != -1 to use that value.
92 | MAX_FD=maximum
93 |
94 | warn () {
95 | echo "$*"
96 | } >&2
97 |
98 | die () {
99 | echo
100 | echo "$*"
101 | echo
102 | exit 1
103 | } >&2
104 |
105 | # OS specific support (must be 'true' or 'false').
106 | cygwin=false
107 | msys=false
108 | darwin=false
109 | nonstop=false
110 | case "$( uname )" in #(
111 | CYGWIN* ) cygwin=true ;; #(
112 | Darwin* ) darwin=true ;; #(
113 | MSYS* | MINGW* ) msys=true ;; #(
114 | NONSTOP* ) nonstop=true ;;
115 | esac
116 |
117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
118 |
119 |
120 | # Determine the Java command to use to start the JVM.
121 | if [ -n "$JAVA_HOME" ] ; then
122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
123 | # IBM's JDK on AIX uses strange locations for the executables
124 | JAVACMD=$JAVA_HOME/jre/sh/java
125 | else
126 | JAVACMD=$JAVA_HOME/bin/java
127 | fi
128 | if [ ! -x "$JAVACMD" ] ; then
129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
130 |
131 | Please set the JAVA_HOME variable in your environment to match the
132 | location of your Java installation."
133 | fi
134 | else
135 | JAVACMD=java
136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
137 |
138 | Please set the JAVA_HOME variable in your environment to match the
139 | location of your Java installation."
140 | fi
141 |
142 | # Increase the maximum file descriptors if we can.
143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
144 | case $MAX_FD in #(
145 | max*)
146 | MAX_FD=$( ulimit -H -n ) ||
147 | warn "Could not query maximum file descriptor limit"
148 | esac
149 | case $MAX_FD in #(
150 | '' | soft) :;; #(
151 | *)
152 | ulimit -n "$MAX_FD" ||
153 | warn "Could not set maximum file descriptor limit to $MAX_FD"
154 | esac
155 | fi
156 |
157 | # Collect all arguments for the java command, stacking in reverse order:
158 | # * args from the command line
159 | # * the main class name
160 | # * -classpath
161 | # * -D...appname settings
162 | # * --module-path (only if needed)
163 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
164 |
165 | # For Cygwin or MSYS, switch paths to Windows format before running java
166 | if "$cygwin" || "$msys" ; then
167 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
168 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
169 |
170 | JAVACMD=$( cygpath --unix "$JAVACMD" )
171 |
172 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
173 | for arg do
174 | if
175 | case $arg in #(
176 | -*) false ;; # don't mess with options #(
177 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
178 | [ -e "$t" ] ;; #(
179 | *) false ;;
180 | esac
181 | then
182 | arg=$( cygpath --path --ignore --mixed "$arg" )
183 | fi
184 | # Roll the args list around exactly as many times as the number of
185 | # args, so each arg winds up back in the position where it started, but
186 | # possibly modified.
187 | #
188 | # NB: a `for` loop captures its iteration list before it begins, so
189 | # changing the positional parameters here affects neither the number of
190 | # iterations, nor the values presented in `arg`.
191 | shift # remove old arg
192 | set -- "$@" "$arg" # push replacement arg
193 | done
194 | fi
195 |
196 | # Collect all arguments for the java command;
197 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
198 | # shell script including quotes and variable substitutions, so put them in
199 | # double quotes to make sure that they get re-expanded; and
200 | # * put everything else in single quotes, so that it's not re-expanded.
201 |
202 | set -- \
203 | "-Dorg.gradle.appname=$APP_BASE_NAME" \
204 | -classpath "$CLASSPATH" \
205 | org.gradle.wrapper.GradleWrapperMain \
206 | "$@"
207 |
208 | # Use "xargs" to parse quoted args.
209 | #
210 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed.
211 | #
212 | # In Bash we could simply go:
213 | #
214 | # readarray ARGS < <( xargs -n1 <<<"$var" ) &&
215 | # set -- "${ARGS[@]}" "$@"
216 | #
217 | # but POSIX shell has neither arrays nor command substitution, so instead we
218 | # post-process each arg (as a line of input to sed) to backslash-escape any
219 | # character that might be a shell metacharacter, then use eval to reverse
220 | # that process (while maintaining the separation between arguments), and wrap
221 | # the whole thing up as a single "set" statement.
222 | #
223 | # This will of course break if any of these variables contains a newline or
224 | # an unmatched quote.
225 | #
226 |
227 | eval "set -- $(
228 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
229 | xargs -n1 |
230 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
231 | tr '\n' ' '
232 | )" '"$@"'
233 |
234 | exec "$JAVACMD" "$@"
235 |
--------------------------------------------------------------------------------
/android/gradlew:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #
4 | # Copyright © 2015-2021 the original authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | #
21 | # Gradle start up script for POSIX generated by Gradle.
22 | #
23 | # Important for running:
24 | #
25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
26 | # noncompliant, but you have some other compliant shell such as ksh or
27 | # bash, then to run this script, type that shell name before the whole
28 | # command line, like:
29 | #
30 | # ksh Gradle
31 | #
32 | # Busybox and similar reduced shells will NOT work, because this script
33 | # requires all of these POSIX shell features:
34 | # * functions;
35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»;
37 | # * compound commands having a testable exit status, especially «case»;
38 | # * various built-in commands including «command», «set», and «ulimit».
39 | #
40 | # Important for patching:
41 | #
42 | # (2) This script targets any POSIX shell, so it avoids extensions provided
43 | # by Bash, Ksh, etc; in particular arrays are avoided.
44 | #
45 | # The "traditional" practice of packing multiple parameters into a
46 | # space-separated string is a well documented source of bugs and security
47 | # problems, so this is (mostly) avoided, by progressively accumulating
48 | # options in "$@", and eventually passing that to Java.
49 | #
50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
52 | # see the in-line comments for details.
53 | #
54 | # There are tweaks for specific operating systems such as AIX, CygWin,
55 | # Darwin, MinGW, and NonStop.
56 | #
57 | # (3) This script is generated from the Groovy template
58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
59 | # within the Gradle project.
60 | #
61 | # You can find Gradle at https://github.com/gradle/gradle/.
62 | #
63 | ##############################################################################
64 |
65 | # Attempt to set APP_HOME
66 |
67 | # Resolve links: $0 may be a link
68 | app_path=$0
69 |
70 | # Need this for daisy-chained symlinks.
71 | while
72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
73 | [ -h "$app_path" ]
74 | do
75 | ls=$( ls -ld "$app_path" )
76 | link=${ls#*' -> '}
77 | case $link in #(
78 | /*) app_path=$link ;; #(
79 | *) app_path=$APP_HOME$link ;;
80 | esac
81 | done
82 |
83 | # This is normally unused
84 | # shellcheck disable=SC2034
85 | APP_BASE_NAME=${0##*/}
86 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
87 | APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
88 |
89 | # Use the maximum available, or set MAX_FD != -1 to use that value.
90 | MAX_FD=maximum
91 |
92 | warn () {
93 | echo "$*"
94 | } >&2
95 |
96 | die () {
97 | echo
98 | echo "$*"
99 | echo
100 | exit 1
101 | } >&2
102 |
103 | # OS specific support (must be 'true' or 'false').
104 | cygwin=false
105 | msys=false
106 | darwin=false
107 | nonstop=false
108 | case "$( uname )" in #(
109 | CYGWIN* ) cygwin=true ;; #(
110 | Darwin* ) darwin=true ;; #(
111 | MSYS* | MINGW* ) msys=true ;; #(
112 | NONSTOP* ) nonstop=true ;;
113 | esac
114 |
115 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
116 |
117 |
118 | # Determine the Java command to use to start the JVM.
119 | if [ -n "$JAVA_HOME" ] ; then
120 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
121 | # IBM's JDK on AIX uses strange locations for the executables
122 | JAVACMD=$JAVA_HOME/jre/sh/java
123 | else
124 | JAVACMD=$JAVA_HOME/bin/java
125 | fi
126 | if [ ! -x "$JAVACMD" ] ; then
127 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
128 |
129 | Please set the JAVA_HOME variable in your environment to match the
130 | location of your Java installation."
131 | fi
132 | else
133 | JAVACMD=java
134 | if ! command -v java >/dev/null 2>&1
135 | then
136 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
137 |
138 | Please set the JAVA_HOME variable in your environment to match the
139 | location of your Java installation."
140 | fi
141 | fi
142 |
143 | # Increase the maximum file descriptors if we can.
144 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
145 | case $MAX_FD in #(
146 | max*)
147 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
148 | # shellcheck disable=SC2039,SC3045
149 | MAX_FD=$( ulimit -H -n ) ||
150 | warn "Could not query maximum file descriptor limit"
151 | esac
152 | case $MAX_FD in #(
153 | '' | soft) :;; #(
154 | *)
155 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
156 | # shellcheck disable=SC2039,SC3045
157 | ulimit -n "$MAX_FD" ||
158 | warn "Could not set maximum file descriptor limit to $MAX_FD"
159 | esac
160 | fi
161 |
162 | # Collect all arguments for the java command, stacking in reverse order:
163 | # * args from the command line
164 | # * the main class name
165 | # * -classpath
166 | # * -D...appname settings
167 | # * --module-path (only if needed)
168 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
169 |
170 | # For Cygwin or MSYS, switch paths to Windows format before running java
171 | if "$cygwin" || "$msys" ; then
172 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
173 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
174 |
175 | JAVACMD=$( cygpath --unix "$JAVACMD" )
176 |
177 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
178 | for arg do
179 | if
180 | case $arg in #(
181 | -*) false ;; # don't mess with options #(
182 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
183 | [ -e "$t" ] ;; #(
184 | *) false ;;
185 | esac
186 | then
187 | arg=$( cygpath --path --ignore --mixed "$arg" )
188 | fi
189 | # Roll the args list around exactly as many times as the number of
190 | # args, so each arg winds up back in the position where it started, but
191 | # possibly modified.
192 | #
193 | # NB: a `for` loop captures its iteration list before it begins, so
194 | # changing the positional parameters here affects neither the number of
195 | # iterations, nor the values presented in `arg`.
196 | shift # remove old arg
197 | set -- "$@" "$arg" # push replacement arg
198 | done
199 | fi
200 |
201 |
202 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
203 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
204 |
205 | # Collect all arguments for the java command:
206 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
207 | # and any embedded shellness will be escaped.
208 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
209 | # treated as '${Hostname}' itself on the command line.
210 |
211 | set -- \
212 | "-Dorg.gradle.appname=$APP_BASE_NAME" \
213 | -classpath "$CLASSPATH" \
214 | org.gradle.wrapper.GradleWrapperMain \
215 | "$@"
216 |
217 | # Stop when "xargs" is not available.
218 | if ! command -v xargs >/dev/null 2>&1
219 | then
220 | die "xargs is not available"
221 | fi
222 |
223 | # Use "xargs" to parse quoted args.
224 | #
225 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed.
226 | #
227 | # In Bash we could simply go:
228 | #
229 | # readarray ARGS < <( xargs -n1 <<<"$var" ) &&
230 | # set -- "${ARGS[@]}" "$@"
231 | #
232 | # but POSIX shell has neither arrays nor command substitution, so instead we
233 | # post-process each arg (as a line of input to sed) to backslash-escape any
234 | # character that might be a shell metacharacter, then use eval to reverse
235 | # that process (while maintaining the separation between arguments), and wrap
236 | # the whole thing up as a single "set" statement.
237 | #
238 | # This will of course break if any of these variables contains a newline or
239 | # an unmatched quote.
240 | #
241 |
242 | eval "set -- $(
243 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
244 | xargs -n1 |
245 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
246 | tr '\n' ' '
247 | )" '"$@"'
248 |
249 | exec "$JAVACMD" "$@"
250 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-native-system-navigation-bar
2 |
3 | 
4 | 
5 |
6 | React Native lets you customize the navigation bar for Android.
7 |
8 |
9 |
10 |
11 | Hide
12 |
13 |
14 |
15 | Lean Back
16 |
17 |
18 |
19 | Immersive
20 |
21 |
22 |
23 |
24 |
25 | Sticky Immersive
26 |
27 |
28 |
29 | Low Profile
30 |
31 |
32 |
33 | Navigation Color
34 |
35 |
36 |
37 |
38 |
39 | Navigation Bar Divider Color
40 |
41 |
42 |
43 | Bar Mode
44 |
45 |
46 |
47 | Fits System Windows
48 |
49 |
50 |
51 |
52 |
53 | ## Installation
54 |
55 | ```
56 | yarn add react-native-system-navigation-bar
57 | ```
58 |
59 | ## Usage
60 |
61 | #### `navigationHide()`
62 |
63 | Hides the navigation bar.
64 |
65 | ```js
66 | import SystemNavigationBar from 'react-native-system-navigation-bar';
67 |
68 | SystemNavigationBar.navigationHide();
69 | ```
70 |
71 | #### `navigationShow()`
72 |
73 | Shows the navigation bar.
74 |
75 | ```js
76 | import SystemNavigationBar from 'react-native-system-navigation-bar';
77 |
78 | SystemNavigationBar.navigationShow();
79 | ```
80 |
81 | #### `leanBack()`
82 |
83 | For full screen experience where the user will not interact heavily with the screen. You can browse the [documentation](https://developer.android.com/training/system-ui/immersive#leanback 'documentation') for more information.
84 |
85 | | Type | Required | Default |
86 | | ------- | -------- | ------- |
87 | | boolean | No | true |
88 |
89 | ```js
90 | import SystemNavigationBar from 'react-native-system-navigation-bar';
91 |
92 | SystemNavigationBar.leanBack();
93 | ```
94 |
95 | #### `immersive()`
96 |
97 | The immersive mode is intended for apps in which the user will be heavily interacting with the screen. You can browse the [documentation](https://developer.android.com/training/system-ui/immersive#immersive 'documentation') for more information.
98 |
99 | | Type | Required | Default |
100 | | ------- | -------- | ------- |
101 | | boolean | No | true |
102 |
103 | ```js
104 | import SystemNavigationBar from 'react-native-system-navigation-bar';
105 |
106 | SystemNavigationBar.immersive();
107 | ```
108 |
109 | #### `stickyImmersive()`
110 |
111 | In the regular immersive mode, any time a user swipes from an edge, the system takes care of revealing the system bars—your app won't even be aware that the gesture occurred. You can browse the [documentation](https://developer.android.com/training/system-ui/immersive#sticky-immersive 'documentation') for more information.
112 |
113 | | Type | Required | Default |
114 | | ------- | -------- | ------- |
115 | | boolean | No | true |
116 |
117 | ```js
118 | import SystemNavigationBar from 'react-native-system-navigation-bar';
119 |
120 | SystemNavigationBar.stickyImmersive();
121 | ```
122 |
123 | #### `setBarMode()`
124 |
125 | Navigation bar and status changes to bar style.
126 |
127 | | Name | Type | Required | Default |
128 | | -------------- | -------------------------- | -------- | ------- |
129 | | Bar Mode Style | light - dark | No | |
130 | | Bar Mode | status - navigation - both | No | both |
131 |
132 | ```js
133 | import SystemNavigationBar from 'react-native-system-navigation-bar';
134 |
135 | SystemNavigationBar.setBarMode('light');
136 | SystemNavigationBar.setBarMode('dark', 'navigation');
137 | ```
138 |
139 | #### `fullScreen()`
140 |
141 | Hide or show the navigation bar and the status bar.
142 |
143 | | Type | Required | Default |
144 | | ------- | -------- | ------- |
145 | | boolean | No | true |
146 |
147 | ```js
148 | import SystemNavigationBar from 'react-native-system-navigation-bar';
149 |
150 | SystemNavigationBar.fullScreen(true);
151 | ```
152 |
153 | **Note:** For notched devices, add the code below to `/android/app/src/main/res/values/styles.xml` in your project to include the cutout for the notch.
154 |
155 | ```xml
156 | - shortEdges
157 | ```
158 |
159 | After adding the code, the estimated content of the `styles.xml` file will be as follows.
160 |
161 | ```xml
162 |
163 |
167 |
168 | ```
169 |
170 | Check out the [documentation](https://developer.android.com/develop/ui/views/layout/display-cutout) for more information about this code.
171 |
172 | #### `lowProfile()`
173 |
174 | The icons in the system and navigation bar are visually retracted. You can browse the [documentation](https://developer.android.com/training/system-ui/dim 'documentation') for more information.
175 |
176 | | Type | Required | Default |
177 | | ------- | -------- | ------- |
178 | | boolean | No | true |
179 |
180 | ```js
181 | import SystemNavigationBar from 'react-native-system-navigation-bar';
182 |
183 | SystemNavigationBar.lowProfile();
184 | ```
185 |
186 | #### `setNavigationColor()`
187 |
188 | Changes the color of the navigation bar. It also changes the style of the status bar and navigation bar to dark or light.
189 |
190 | | Name | Type | Required | Default |
191 | | -------------- | -------------------------- | -------- | ------- |
192 | | Color | RGB - HSL - Color Ints | Yes | |
193 | | Bar Mode Style | light - dark | No | |
194 | | Bar Mode | status - navigation - both | No | both |
195 |
196 | ```js
197 | import SystemNavigationBar from 'react-native-system-navigation-bar';
198 |
199 | SystemNavigationBar.setNavigationColor('red');
200 | SystemNavigationBar.setNavigationColor('translucent');
201 | SystemNavigationBar.setNavigationColor('#FF0000', 'light');
202 | SystemNavigationBar.setNavigationColor(0xff00ff00, 'dark');
203 | SystemNavigationBar.setNavigationColor('blue', 'dark', 'status');
204 | SystemNavigationBar.setNavigationColor('green', 'light', 'navigation');
205 | SystemNavigationBar.setNavigationColor('yellow', 'light', 'both');
206 | SystemNavigationBar.setNavigationColor('hsla(110, 56%, 49%, 0.5)');
207 | ```
208 |
209 | #### `getBarColor()`
210 |
211 | It allows you to access the color of the navigation bar, status bar or both bars as hex color.
212 |
213 | | Name | Type | Required | Default |
214 | | ---- | -------------------------- | -------- | ------- |
215 | | Bar | status - navigation - both | No | both |
216 |
217 | ```js
218 | import SystemNavigationBar from 'react-native-system-navigation-bar';
219 |
220 | const statusBarColor: string = await SystemNavigationBar.getBarColor('status'); // #757575
221 | const navigationBarColor: string = await SystemNavigationBar.getBarColor(
222 | 'navigation'
223 | ); // #FF0000
224 | ```
225 |
226 | If the `both` option is selected, the colors of both the status bar and the navigation bar will return as Object.
227 |
228 | ```js
229 | import SystemNavigationBar, {
230 | GetBarColorType,
231 | } from 'react-native-system-navigation-bar';
232 |
233 | const barColors: GetBarColorType = await SystemNavigationBar.getBarColor(
234 | 'both'
235 | );
236 | // { "status": "#757575", "navigation": "#FF0000" }
237 | ```
238 |
239 | #### `setFitsSystemWindows()`
240 |
241 | Boolean internal attribute to adjust view layout based on system windows such as the navigation bar. You can browse the [documentation](https://developer.android.com/reference/android/view/View#attr_android:fitsSystemWindows 'documentation') for more information.
242 |
243 | | Type | Required | Default |
244 | | ------- | -------- | ------- |
245 | | boolean | No | true |
246 |
247 | ```js
248 | import SystemNavigationBar from 'react-native-system-navigation-bar';
249 |
250 | SystemNavigationBar.setFitsSystemWindows();
251 | SystemNavigationBar.setFitsSystemWindows(false);
252 | ```
253 |
254 | #### `setNavigationBarDividerColor()`
255 |
256 | > Only API Level 28 (Android 9) and higher is supported.
257 |
258 | Shows a thin line of the specified color between the navigation bar and the app content. You can browse the [documentation](https://developer.android.com/reference/android/view/Window#attr_android:navigationBarDividerColor 'documentation') for more information.
259 |
260 | | Name | Type | Required |
261 | | ----- | ---------------------- | -------- |
262 | | Color | RGB - HSL - Color Ints | Yes |
263 |
264 | ```js
265 | import SystemNavigationBar from 'react-native-system-navigation-bar';
266 |
267 | SystemNavigationBar.setNavigationBarDividerColor('red');
268 | SystemNavigationBar.setNavigationBarDividerColor('#FF0000');
269 | SystemNavigationBar.setNavigationBarDividerColor(0xff00ff00);
270 | SystemNavigationBar.setNavigationBarDividerColor('hsla(110, 56%, 49%, 0.5)');
271 | ```
272 |
273 | #### `setNavigationBarContrastEnforced()`
274 |
275 | > Only API Level 29 (Android 10) and higher is supported.
276 |
277 | Sets whether the system should ensure that the navigation bar has enough contrast when a fully transparent background is requested. You can browse the [documentation]( 'documentation') for more information.
278 |
279 | | Type | Required | Default |
280 | | ------- | -------- | ------- |
281 | | boolean | No | true |
282 |
283 | ```js
284 | import SystemNavigationBar from 'react-native-system-navigation-bar';
285 |
286 | SystemNavigationBar.setNavigationBarContrastEnforced(true);
287 | ```
288 |
289 | ### And
290 |
291 | All functions have callbacks.
292 |
293 | #### Usage
294 |
295 | ```js
296 | import SystemNavigationBar from 'react-native-system-navigation-bar';
297 |
298 | const show = async () => {
299 | const result = await SystemNavigationBar.navigationShow();
300 |
301 | console.log('Show: ', result); // true or Error Message
302 | };
303 | ```
304 |
305 | ## License
306 |
307 | MIT
308 |
--------------------------------------------------------------------------------
/android/src/main/java/com/reactnativesystemnavigationbar/SystemNavigationBarModule.java:
--------------------------------------------------------------------------------
1 | package com.reactnativesystemnavigationbar;
2 |
3 | import static com.facebook.react.bridge.UiThreadUtil.runOnUiThread;
4 |
5 | import android.app.Activity;
6 | import android.graphics.Color;
7 | import android.os.Build;
8 | import android.view.View;
9 | import android.view.Window;
10 | import android.view.WindowInsets;
11 | import android.view.WindowInsetsController;
12 | import android.view.WindowManager;
13 | import androidx.annotation.NonNull;
14 |
15 | import com.facebook.react.bridge.Promise;
16 | import com.facebook.react.bridge.ReactApplicationContext;
17 | import com.facebook.react.bridge.ReactContextBaseJavaModule;
18 | import com.facebook.react.bridge.ReactMethod;
19 | import com.facebook.react.module.annotations.ReactModule;
20 | import com.facebook.react.uimanager.IllegalViewOperationException;
21 | import java.util.HashMap;
22 | import java.util.Map;
23 | import java.util.Objects;
24 |
25 | @ReactModule(name = SystemNavigationBarModule.NAME)
26 | public class SystemNavigationBarModule extends ReactContextBaseJavaModule {
27 |
28 | public static final String NAME = "NavigationBar";
29 | public static final Integer NO_MODE = -1;
30 | public static final Integer LIGHT = 0;
31 | public static final Integer DARK = 1;
32 | public static final Integer NAVIGATION_BAR = 2;
33 | public static final Integer STATUS_BAR = 3;
34 | public static final Integer NAVIGATION_BAR_STATUS_BAR = 4;
35 | private static final Integer INSETS_TYPE_HIDE = 5;
36 | private static final Integer INSETS_TYPE_SHOW = 6;
37 | private static final Integer INSETS_TYPE_APPEARANCE = 7;
38 | private static final Integer INSETS_TYPE_APPEARANCE_CLEAR = 8;
39 | private static final Integer INSETS_TYPE_BEHAVIOR = 9;
40 |
41 | public SystemNavigationBarModule(ReactApplicationContext reactContext) {
42 | super(reactContext);
43 | }
44 |
45 | @Override
46 | @NonNull
47 | public String getName() {
48 | return NAME;
49 | }
50 |
51 | @Override
52 | public Map getConstants() {
53 | final Map constants = new HashMap<>();
54 | constants.put("NO_MODE", NO_MODE);
55 | constants.put("LIGHT", LIGHT);
56 | constants.put("DARK", DARK);
57 | constants.put("NAVIGATION_BAR", NAVIGATION_BAR);
58 | constants.put("STATUS_BAR", STATUS_BAR);
59 | constants.put("NAVIGATION_BAR_STATUS_BAR", NAVIGATION_BAR_STATUS_BAR);
60 | return constants;
61 | }
62 |
63 | /* Navigation Hide */
64 | @ReactMethod
65 | public void navigationHide(Promise promise) {
66 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
67 | setSystemInsetsController(WindowInsets.Type.navigationBars(), INSETS_TYPE_HIDE, promise);
68 | } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
69 | setSystemUIFlags(
70 | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
71 | | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
72 | | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION,
73 | promise
74 | );
75 | }
76 | }
77 |
78 | /* Navigation Show */
79 | @ReactMethod
80 | public void navigationShow(Promise promise) {
81 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
82 | setSystemInsetsController(WindowInsets.Type.navigationBars(), INSETS_TYPE_SHOW, promise);
83 | } else {
84 | setSystemUIFlags(View.SYSTEM_UI_FLAG_VISIBLE, promise);
85 | }
86 | }
87 |
88 | @ReactMethod
89 | public void fullScreen(Boolean enabled, Promise promise) {
90 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
91 | int visibility = WindowInsets.Type.navigationBars() | WindowInsets.Type.statusBars();
92 | int type = enabled ? INSETS_TYPE_HIDE : INSETS_TYPE_SHOW;
93 | setSystemInsetsController(visibility, type, promise);
94 | } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
95 | if (enabled) {
96 | setSystemUIFlags(
97 | View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
98 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
99 | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
100 | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
101 | View.SYSTEM_UI_FLAG_IMMERSIVE |
102 | View.SYSTEM_UI_FLAG_FULLSCREEN,
103 | promise
104 | );
105 | } else {
106 | setSystemUIFlags(
107 | View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
108 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
109 | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN,
110 | promise
111 | );
112 | }
113 | }
114 |
115 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
116 | final Activity currentActivity = getCurrentActivity();
117 | if (currentActivity == null) {
118 | promise.reject("Error: ", "current activity is null");
119 | return;
120 | }
121 | final Window view = currentActivity.getWindow();
122 | runOnUiThread(
123 | () -> {
124 | view.clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
125 | view.clearFlags(
126 | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION
127 | );
128 |
129 | if (enabled) {
130 | view.setFlags(
131 | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION,
132 | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION
133 | );
134 | }
135 | }
136 | );
137 | }
138 | }
139 |
140 | /* Lean Back */
141 | @ReactMethod
142 | public void leanBack(Boolean enabled, Promise promise) {
143 | if (enabled) {
144 | setSystemUIFlags(
145 | View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION,
146 | promise
147 | );
148 | } else {
149 | setSystemUIFlags(View.SYSTEM_UI_FLAG_VISIBLE, promise);
150 | }
151 | }
152 |
153 | /* Immersive */
154 | @ReactMethod
155 | public void immersive(Promise promise) {
156 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
157 | int visibility = WindowInsets.Type.navigationBars() | WindowInsets.Type.statusBars();
158 | setSystemInsetsController(visibility, INSETS_TYPE_HIDE, promise);
159 | setSystemInsetsController(WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_SWIPE, INSETS_TYPE_BEHAVIOR, promise);
160 | } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
161 | setSystemUIFlags(View.SYSTEM_UI_FLAG_IMMERSIVE |
162 | View.SYSTEM_UI_FLAG_FULLSCREEN |
163 | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION, promise);
164 | }
165 | }
166 |
167 | /* Sticky Immersive */
168 | @ReactMethod
169 | public void stickyImmersive(Boolean enabled, Promise promise) {
170 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
171 | int visibility = WindowInsets.Type.navigationBars() | WindowInsets.Type.statusBars();
172 | if (enabled) {
173 | setSystemInsetsController(visibility, INSETS_TYPE_HIDE, promise);
174 | setSystemInsetsController(WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE, INSETS_TYPE_BEHAVIOR, promise);
175 | } else {
176 | setSystemInsetsController(visibility, INSETS_TYPE_SHOW, promise);
177 | setSystemInsetsController(WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_SWIPE, INSETS_TYPE_APPEARANCE_CLEAR, promise);
178 | }
179 | } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
180 | if (enabled) {
181 | setSystemUIFlags(
182 | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY |
183 | View.SYSTEM_UI_FLAG_FULLSCREEN |
184 | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION,
185 | promise
186 | );
187 | } else {
188 | setSystemUIFlags(View.SYSTEM_UI_FLAG_VISIBLE, promise);
189 | }
190 | }
191 | }
192 |
193 | /* Low Profile */
194 | @ReactMethod
195 | public void lowProfile(Boolean enabled, Promise promise) {
196 | if (enabled) {
197 | setSystemUIFlags(View.SYSTEM_UI_FLAG_LOW_PROFILE, promise);
198 | } else {
199 | setSystemUIFlags(View.SYSTEM_UI_FLAG_VISIBLE, promise);
200 | }
201 | }
202 |
203 | @ReactMethod
204 | public void setBarMode(Integer modeStyle, Integer bar, Promise promise) {
205 | boolean isLight = modeStyle.equals(LIGHT);
206 | setModeStyle(!isLight, bar, promise);
207 | }
208 |
209 | /* Set Navigation Color */
210 | @ReactMethod
211 | public void setNavigationColor(
212 | Integer color,
213 | Boolean isTranslucent,
214 | Integer modeStyle,
215 | Integer bar,
216 | Promise promise
217 | ) {
218 | try {
219 | int requiredVersion = Build.VERSION_CODES.LOLLIPOP;
220 | if (Build.VERSION.SDK_INT < requiredVersion) {
221 | promise.reject("Error: ", errorMessage(requiredVersion));
222 | return;
223 | }
224 | final Activity currentActivity = getCurrentActivity();
225 | if (currentActivity == null) {
226 | promise.reject("Error: ", "current activity is null");
227 | return;
228 | }
229 | final Window view = currentActivity.getWindow();
230 | runOnUiThread(
231 | () -> {
232 | view.clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
233 | view.clearFlags(
234 | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION
235 | );
236 |
237 | if (isTranslucent) {
238 | view.setFlags(
239 | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION,
240 | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION
241 | );
242 | }
243 |
244 | boolean isTransparent = color.equals(Color.TRANSPARENT) && !isTranslucent;
245 | if (isTransparent) {
246 | view.setFlags(
247 | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
248 | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
249 | );
250 | view.setFlags(
251 | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION,
252 | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION
253 | );
254 | }
255 |
256 | view.setNavigationBarColor(color);
257 |
258 | if (!Objects.equals(modeStyle, NO_MODE)) {
259 | boolean isLight = modeStyle.equals(LIGHT);
260 | setModeStyle(!isLight, bar);
261 | }
262 | }
263 | );
264 | promise.resolve("true");
265 | } catch (IllegalViewOperationException e) {
266 | e.printStackTrace();
267 | promise.reject("Error: ", e.getMessage());
268 | }
269 | }
270 |
271 | @ReactMethod
272 | public void setFitsSystemWindows(
273 | Boolean enabled,
274 | Promise promise
275 | ) {
276 | try {
277 | int requiredVersion = Build.VERSION_CODES.LOLLIPOP;
278 | if (Build.VERSION.SDK_INT < requiredVersion) {
279 | promise.reject("Error: ", errorMessage(requiredVersion));
280 | return;
281 | }
282 | final Activity currentActivity = getCurrentActivity();
283 | if (currentActivity == null) {
284 | promise.reject("Error: ", "current activity is null");
285 | return;
286 | }
287 | final Window view = currentActivity.getWindow();
288 | runOnUiThread(
289 | () -> {
290 | if (!enabled) {
291 | view.getDecorView().setSystemUiVisibility(
292 | View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
293 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
294 | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
295 | );
296 | } else {
297 | view.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
298 | }
299 | }
300 | );
301 | promise.resolve("true");
302 | } catch (IllegalViewOperationException e) {
303 | e.printStackTrace();
304 | promise.reject("Error: ", e.getMessage());
305 | }
306 | }
307 |
308 | @ReactMethod
309 | public void getBarColor(Integer bar, Promise promise) {
310 | runOnUiThread(() -> {
311 | try {
312 | int requiredVersion = Build.VERSION_CODES.LOLLIPOP;
313 | if (Build.VERSION.SDK_INT < requiredVersion) {
314 | promise.reject("Error: ", errorMessage(requiredVersion));
315 | return;
316 | }
317 | final Activity currentActivity = getCurrentActivity();
318 | if (currentActivity == null) {
319 | promise.reject("Error: ", "current activity is null");
320 | return;
321 | }
322 | final Window view = currentActivity.getWindow();
323 |
324 | int navigationColor = view.getNavigationBarColor();
325 | int statusColor = view.getStatusBarColor();
326 |
327 | if (bar.equals(STATUS_BAR)) {
328 | promise.resolve(convertColorToHexCode(statusColor));
329 | } else if (bar.equals(NAVIGATION_BAR)) {
330 | promise.resolve(convertColorToHexCode(navigationColor));
331 | } else if (bar.equals(NAVIGATION_BAR_STATUS_BAR)) {
332 | String hexStatusColor = convertColorToHexCode(statusColor);
333 | String hexNavigationColor = convertColorToHexCode(navigationColor);
334 | String result = String.format("{ \"status\": \"%s\", \"navigation\": \"%s\" }", hexStatusColor, hexNavigationColor);
335 | promise.resolve(result);
336 | }
337 | } catch (IllegalViewOperationException e) {
338 | e.printStackTrace();
339 | promise.reject("Error: ", e.getMessage());
340 | }
341 | });
342 | }
343 |
344 | /* Set NavigationBar Divider Color */
345 | @ReactMethod
346 | public void setNavigationBarDividerColor(Integer color, Promise promise) {
347 | try {
348 | int requiredVersion = Build.VERSION_CODES.P;
349 | if (Build.VERSION.SDK_INT < requiredVersion) {
350 | promise.reject("Error: ", errorMessage(requiredVersion));
351 | return;
352 | }
353 | final Activity currentActivity = getCurrentActivity();
354 | if (currentActivity == null) {
355 | promise.reject("Error: ", "current activity is null");
356 | return;
357 | }
358 | final Window view = currentActivity.getWindow();
359 | runOnUiThread(
360 | () -> {
361 | view.setNavigationBarDividerColor(color);
362 | view
363 | .getDecorView()
364 | .setSystemUiVisibility(
365 | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS |
366 | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION
367 | );
368 | }
369 | );
370 | promise.resolve("true");
371 | } catch (IllegalViewOperationException e) {
372 | e.printStackTrace();
373 | promise.reject("Error: ", e.getMessage());
374 | }
375 | }
376 |
377 | /* Set NavigationBar Contrast Enforced */
378 | @ReactMethod
379 | public void setNavigationBarContrastEnforced(
380 | Boolean enforceContrast,
381 | Promise promise
382 | ) {
383 | try {
384 | int requiredVersion = Build.VERSION_CODES.Q;
385 | if (Build.VERSION.SDK_INT < requiredVersion) {
386 | promise.reject("Error: ", errorMessage(requiredVersion));
387 | return;
388 | }
389 | final Activity currentActivity = getCurrentActivity();
390 | if (currentActivity == null) {
391 | promise.reject("Error: ", "current activity is null");
392 | return;
393 | }
394 | final Window view = currentActivity.getWindow();
395 | runOnUiThread(
396 | () -> view.setNavigationBarContrastEnforced(enforceContrast)
397 | );
398 | promise.resolve("true");
399 | } catch (IllegalViewOperationException e) {
400 | e.printStackTrace();
401 | promise.reject("Error: ", e.getMessage());
402 | }
403 | }
404 |
405 | /* Private Method */
406 | private void setSystemInsetsController(int visibility, Integer insetsType, Promise promise) {
407 | try {
408 | runOnUiThread(
409 | () -> {
410 | int requiredVersion = Build.VERSION_CODES.R;
411 | if (Build.VERSION.SDK_INT < requiredVersion) {
412 | promise.reject("Error: ", errorMessage(requiredVersion));
413 | return;
414 | }
415 | Activity currentActivity = getCurrentActivity();
416 | if (currentActivity == null) {
417 | promise.reject("Error: ", "current activity is null");
418 | return;
419 | }
420 | WindowInsetsController insetsController = currentActivity.getWindow().getInsetsController();
421 |
422 | if (insetsController != null) {
423 | if (insetsType.equals(INSETS_TYPE_HIDE)) {
424 | insetsController.hide(visibility);
425 | } else if (insetsType.equals(INSETS_TYPE_SHOW)) {
426 | insetsController.show(visibility);
427 | } else if (insetsType.equals(INSETS_TYPE_APPEARANCE)) {
428 | insetsController.setSystemBarsAppearance(visibility, visibility);
429 | } else if (insetsType.equals(INSETS_TYPE_APPEARANCE_CLEAR)) {
430 | insetsController.setSystemBarsAppearance(0, visibility);
431 | } else if (insetsType.equals(INSETS_TYPE_BEHAVIOR)) {
432 | insetsController.setSystemBarsBehavior(visibility);
433 | }
434 | }
435 | promise.resolve("true");
436 | }
437 | );
438 | } catch (IllegalViewOperationException e) {
439 | e.printStackTrace();
440 | promise.reject("Error: ", e.getMessage());
441 | }
442 | }
443 |
444 | /* Private Method */
445 | private void setSystemUIFlags(int visibility, Promise promise) {
446 | try {
447 | runOnUiThread(
448 | () -> {
449 | int requiredVersion = Build.VERSION_CODES.LOLLIPOP;
450 | if (Build.VERSION.SDK_INT < requiredVersion) {
451 | promise.reject("Error: ", errorMessage(requiredVersion));
452 | return;
453 | }
454 | Activity currentActivity = getCurrentActivity();
455 | if (currentActivity == null) {
456 | promise.reject("Error: ", "current activity is null");
457 | return;
458 | }
459 | View decorView = currentActivity.getWindow().getDecorView();
460 | decorView.setSystemUiVisibility(visibility);
461 | promise.resolve("true");
462 | }
463 | );
464 | } catch (IllegalViewOperationException e) {
465 | e.printStackTrace();
466 | promise.reject("Error: ", e.getMessage());
467 | }
468 | }
469 |
470 | private void setModeStyle(Boolean light, Integer bar) {
471 | Activity currentActivity = getCurrentActivity();
472 | if (currentActivity == null) {
473 | return;
474 | }
475 |
476 | int visibility = 0;
477 |
478 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
479 | WindowInsetsController insetsController = currentActivity.getWindow().getInsetsController();
480 | int navigationBarAppearance = WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
481 | int statusBarAppearance = WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
482 | int bothBarAppearance = statusBarAppearance | navigationBarAppearance;
483 |
484 | if (insetsController != null) {
485 | if (bar.equals(NAVIGATION_BAR)) {
486 | visibility = navigationBarAppearance;
487 | } else if (bar.equals(STATUS_BAR)) {
488 | visibility = statusBarAppearance;
489 | } else if (bar.equals(NAVIGATION_BAR_STATUS_BAR)) {
490 | visibility = bothBarAppearance;
491 | }
492 |
493 | if (light) {
494 | insetsController.setSystemBarsAppearance(visibility, visibility);
495 | } else {
496 | insetsController.setSystemBarsAppearance(0, visibility);
497 | }
498 | }
499 | } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
500 | View decorView = getCurrentActivity().getWindow().getDecorView();
501 | int bit = decorView.getSystemUiVisibility();
502 |
503 | if (bar.equals(NAVIGATION_BAR)) {
504 | visibility = View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
505 | } else if (bar.equals(STATUS_BAR)) {
506 | visibility = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
507 | } else if (bar.equals(NAVIGATION_BAR_STATUS_BAR)) {
508 | visibility = View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
509 | }
510 |
511 | if (light) {
512 | bit |= visibility;
513 | } else {
514 | bit &= ~visibility;
515 | }
516 |
517 | decorView.setSystemUiVisibility(bit);
518 | }
519 | }
520 |
521 | private void setModeStyle(Boolean light, Integer bar, Promise promise) {
522 | try {
523 | runOnUiThread(
524 | () -> {
525 | if (getCurrentActivity() == null) {
526 | promise.reject("Error: ", "current activity is null");
527 | return;
528 | }
529 | setModeStyle(light, bar);
530 | promise.resolve("true");
531 | }
532 | );
533 | } catch (IllegalViewOperationException e) {
534 | promise.reject("Error: ", e.getMessage());
535 | }
536 | }
537 |
538 | private String errorMessage(int version) {
539 | return "Your device version: " + Build.VERSION.SDK_INT + ". Supported API Level: " + version;
540 | }
541 |
542 | private String convertColorToHexCode(int color) {
543 | return String.format("#%06X", (0xFFFFFF & color));
544 | }
545 | }
546 |
--------------------------------------------------------------------------------