react-native-navigation-mode 1.2.6 → 1.2.8-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  🧭 Detect Android navigation mode (3-button, 2-button, or gesture navigation) with native precision using Turbo modules.
4
4
 
5
- [![npm version](https://img.shields.io/npm/v/react-native-navigation-mode)](https://badge.fury.io/js/react-native-navigation-mode) [![License](https://img.shields.io/github/license/JairajJangle/react-native-navigation-mode)](https://github.com/JairajJangle/react-native-navigation-mode/blob/main/LICENSE) [![Workflow Status](https://github.com/JairajJangle/react-native-navigation-mode/actions/workflows/ci.yml/badge.svg)](https://github.com/JairajJangle/react-native-navigation-mode/actions/workflows/ci.yml) ![Android](https://img.shields.io/badge/-Android-555555?logo=android&logoColor=3DDC84) ![iOS](https://img.shields.io/badge/-iOS-555555?logo=apple&logoColor=white) [![GitHub issues](https://img.shields.io/github/issues/JairajJangle/react-native-navigation-mode)](https://github.com/JairajJangle/react-native-navigation-mode/issues?q=is%3Aopen+is%3Aissue) ![TS](https://img.shields.io/badge/TypeScript-strict_💪-blue) ![Turbo Module](https://img.shields.io/badge/Turbo%20Module-⚡-orange) ![Expo](https://img.shields.io/badge/Expo-SDK_52+-000020?logo=expo) ![npm bundle size](https://img.shields.io/bundlephobia/minzip/react-native-navigation-mode)
5
+ [![npm version](https://img.shields.io/npm/v/react-native-navigation-mode)](https://badge.fury.io/js/react-native-navigation-mode) [![License](https://img.shields.io/github/license/JairajJangle/react-native-navigation-mode)](https://github.com/JairajJangle/react-native-navigation-mode/blob/main/LICENSE) [![Workflow Status](https://github.com/JairajJangle/react-native-navigation-mode/actions/workflows/ci.yml/badge.svg)](https://github.com/JairajJangle/react-native-navigation-mode/actions/workflows/ci.yml) ![Android](https://img.shields.io/badge/-Android-555555?logo=android&logoColor=3DDC84) ![iOS](https://img.shields.io/badge/-iOS-555555?logo=apple&logoColor=white) [![GitHub issues](https://img.shields.io/github/issues/JairajJangle/react-native-navigation-mode)](https://github.com/JairajJangle/react-native-navigation-mode/issues?q=is%3Aopen+is%3Aissue) ![TS](https://img.shields.io/badge/TypeScript-strict_💪-blue) ![Turbo Module](https://img.shields.io/badge/Turbo%20Module-⚡-orange) ![Expo](https://img.shields.io/badge/Expo-SDK_52+-000020?logo=expo) ![npm bundle size](https://img.shields.io/bundlephobia/minzip/react-native-navigation-mode) [![Sponsor](https://img.shields.io/badge/Sponsor-GitHub-ea4aaa?style=flat&logo=github-sponsors)](https://github.com/sponsors/JairajJangle)
6
6
 
7
7
  <table align="center">
8
8
  <tr>
@@ -67,12 +67,18 @@ class NavigationModeModule(reactContext: ReactApplicationContext) :
67
67
 
68
68
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
69
69
  val navBarInteractionMode = getNavBarInteractionMode(context)
70
+ val isGesture = if (navBarInteractionMode == 2) true else isGestureNavigationBySettings(context)
70
71
  result.putInt("interactionMode", navBarInteractionMode)
71
- result.putString("type", getNavigationTypeFromInteractionMode(navBarInteractionMode))
72
- result.putBoolean("isGestureNavigation", navBarInteractionMode == 2)
72
+ result.putString(
73
+ "type",
74
+ if (isGesture) "gesture" else getNavigationTypeFromInteractionMode(navBarInteractionMode)
75
+ )
76
+ result.putBoolean("isGestureNavigation", isGesture)
73
77
  } else {
74
- val gestureNavEnabled = isGestureNavigationEnabledLegacy(context)
78
+ val gestureNavEnabled = isGestureNavigationBySettings(context)
75
79
  result.putBoolean("isGestureNavigation", gestureNavEnabled)
80
+ // Set "type" for pre-Q devices so the field is always present in the result
81
+ result.putString("type", if (gestureNavEnabled) "gesture" else "unknown")
76
82
  }
77
83
 
78
84
  promise.resolve(result)
@@ -87,9 +93,10 @@ class NavigationModeModule(reactContext: ReactApplicationContext) :
87
93
 
88
94
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
89
95
  val navBarInteractionMode = getNavBarInteractionMode(context)
90
- promise.resolve(navBarInteractionMode == 2)
96
+ val isGesture = if (navBarInteractionMode == 2) true else isGestureNavigationBySettings(context)
97
+ promise.resolve(isGesture)
91
98
  } else {
92
- val gestureEnabled = isGestureNavigationEnabledLegacy(context)
99
+ val gestureEnabled = isGestureNavigationBySettings(context)
93
100
  promise.resolve(gestureEnabled)
94
101
  }
95
102
  } catch (e: Exception) {
@@ -120,17 +127,72 @@ class NavigationModeModule(reactContext: ReactApplicationContext) :
120
127
  }
121
128
  }
122
129
 
123
- private fun isGestureNavigationEnabledLegacy(context: Context): Boolean {
124
- // Legacy fallback using Settings.Secure (for pre-Android Q devices)
125
- // or as a backup when config_navBarInteractionMode is not available
126
- return try {
127
- val navBarMode = Settings.Secure.getString(
128
- context.contentResolver,
129
- "navigation_mode"
130
- )
131
- "2" == navBarMode
132
- } catch (e: Exception) {
133
- false
130
+ // Detects gesture navigation via Settings providers. Used as:
131
+ // - The sole detection method on pre-Android Q (API < 29) devices.
132
+ // - A supplementary fallback on Android Q+ when config_navBarInteractionMode does not
133
+ // return 2, since some OEM firmwares (e.g. Huawei Mate 30 Pro) leave that resource at
134
+ // its default value of 0 even after the user enables gesture navigation.
135
+ private fun isGestureNavigationBySettings(context: Context): Boolean {
136
+ // Standard AOSP key available on Android 10+ stock and most OEMs.
137
+ // "2" = gesture, "0" = 3-button, "1" = 2-button.
138
+ try {
139
+ val navBarMode = Settings.Secure.getString(
140
+ context.contentResolver,
141
+ "navigation_mode"
142
+ )
143
+ if ("2" == navBarMode) {
144
+ return true
145
+ }
146
+ if ("0" == navBarMode || "1" == navBarMode) {
147
+ return false
148
+ }
149
+ } catch (e: Exception) {
150
+ // Ignore and continue with OEM-specific fallbacks
151
+ }
152
+
153
+ // Huawei / Honor EMUI: written to Settings.Global when the user collapses the nav bar
154
+ // into the gesture handle. Value 1 = gestures enabled.
155
+ val huaweiNavigationBarMinGlobal = getGlobalIntSetting(context, "navigationbar_is_min")
156
+ if (huaweiNavigationBarMinGlobal != null) {
157
+ return huaweiNavigationBarMinGlobal == 1
158
+ }
159
+
160
+ // Huawei / Honor EMUI (alternate key seen on some firmware versions).
161
+ // Value 1 = gesture navigation enabled.
162
+ val huaweiSecureGesture = getSecureIntSetting(context, "secure_gesture_navigation")
163
+ if (huaweiSecureGesture != null) {
164
+ return huaweiSecureGesture == 1
165
+ }
166
+
167
+ // Xiaomi MIUI: set to 1 when the "full-screen gesture" nav bar is active.
168
+ val xiaomiForceGesture = getGlobalIntSetting(context, "force_fsg_nav_bar")
169
+ if (xiaomiForceGesture != null) {
170
+ return xiaomiForceGesture == 1
171
+ }
172
+
173
+ // Samsung One UI: toggled when the user switches to "Swipe gestures" in nav bar settings.
174
+ // Value 1 = gesture navigation enabled.
175
+ val samsungNavigationGesture = getSecureIntSetting(context, "navigation_gesture_on")
176
+ if (samsungNavigationGesture != null) {
177
+ return samsungNavigationGesture == 1
178
+ }
179
+
180
+ return false
181
+ }
182
+
183
+ private fun getSecureIntSetting(context: Context, key: String): Int? {
184
+ return try {
185
+ Settings.Secure.getInt(context.contentResolver, key)
186
+ } catch (e: Exception) {
187
+ null
188
+ }
189
+ }
190
+
191
+ private fun getGlobalIntSetting(context: Context, key: String): Int? {
192
+ return try {
193
+ Settings.Global.getInt(context.contentResolver, key)
194
+ } catch (e: Exception) {
195
+ null
196
+ }
134
197
  }
135
- }
136
198
  }
@@ -3,6 +3,9 @@
3
3
  import { TurboModuleRegistry, Platform } from 'react-native';
4
4
  // Only get the native module on Android
5
5
  // On iOS, we'll handle everything in JavaScript
6
- const NativeModule = Platform.OS === 'android' ? TurboModuleRegistry.getEnforcing('NavigationMode') : null;
6
+ const NativeModule = Platform.OS === 'android' ? TurboModuleRegistry?.getEnforcing ? TurboModuleRegistry.getEnforcing('NavigationMode') : (() => {
7
+ console.error("['react-native-navigation-mode'] TurboModuleRegistry.getEnforcing is not available. Make sure you have enabled the New Architecture (TurboModules) in your project.");
8
+ return null;
9
+ })() : null;
7
10
  export default NativeModule;
8
11
  //# sourceMappingURL=NativeNavigationMode.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["TurboModuleRegistry","Platform","NativeModule","OS","getEnforcing"],"sourceRoot":"../../src","sources":["NativeNavigationMode.ts"],"mappings":";;AACA,SAASA,mBAAmB,EAAEC,QAAQ,QAAQ,cAAc;AAe5D;AACA;AACA,MAAMC,YAAY,GAChBD,QAAQ,CAACE,EAAE,KAAK,SAAS,GACrBH,mBAAmB,CAACI,YAAY,CAAO,gBAAgB,CAAC,GACxD,IAAI;AAEV,eAAeF,YAAY","ignoreList":[]}
1
+ {"version":3,"names":["TurboModuleRegistry","Platform","NativeModule","OS","getEnforcing","console","error"],"sourceRoot":"../../src","sources":["NativeNavigationMode.ts"],"mappings":";;AACA,SAASA,mBAAmB,EAAEC,QAAQ,QAAQ,cAAc;AAe5D;AACA;AACA,MAAMC,YAAY,GAChBD,QAAQ,CAACE,EAAE,KAAK,SAAS,GACrBH,mBAAmB,EAAEI,YAAY,GAC/BJ,mBAAmB,CAACI,YAAY,CAAO,gBAAgB,CAAC,GACxD,CAAC,MAAM;EACPC,OAAO,CAACC,KAAK,CACX,qKACF,CAAC;EACD,OAAO,IAAI;AACb,CAAC,EAAE,CAAC,GACJ,IAAI;AAEV,eAAeJ,YAAY","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"file":"NativeNavigationMode.d.ts","sourceRoot":"","sources":["../../../src/NativeNavigationMode.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGhD,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,UAAU,GAAG,UAAU,GAAG,SAAS,GAAG,SAAS,CAAC;IACtD,mBAAmB,EAAE,OAAO,CAAC;IAC7B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,IAAK,SAAQ,WAAW;IACvC,iBAAiB,IAAI,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACjD,mBAAmB,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IACxC,sBAAsB,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;CAC3C;AAID,QAAA,MAAM,YAAY,aAGR,CAAC;AAEX,eAAe,YAAY,CAAC"}
1
+ {"version":3,"file":"NativeNavigationMode.d.ts","sourceRoot":"","sources":["../../../src/NativeNavigationMode.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGhD,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,UAAU,GAAG,UAAU,GAAG,SAAS,GAAG,SAAS,CAAC;IACtD,mBAAmB,EAAE,OAAO,CAAC;IAC7B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,IAAK,SAAQ,WAAW;IACvC,iBAAiB,IAAI,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACjD,mBAAmB,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IACxC,sBAAsB,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;CAC3C;AAID,QAAA,MAAM,YAAY,aAUR,CAAC;AAEX,eAAe,YAAY,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-navigation-mode",
3
- "version": "1.2.6",
3
+ "version": "1.2.8-beta.1",
4
4
  "description": "Detect Android navigation mode (3-button, 2-button, or gesture)",
5
5
  "main": "./lib/module/index.js",
6
6
  "react-native": "./lib/module/index.js",
@@ -66,31 +66,34 @@
66
66
  "devDependencies": {
67
67
  "@commitlint/config-conventional": "^19.6.0",
68
68
  "@eslint/compat": "^1.2.7",
69
- "@eslint/eslintrc": "^3.3.0",
69
+ "@eslint/eslintrc": "^3.3.3",
70
70
  "@eslint/js": "^9.22.0",
71
71
  "@evilmartians/lefthook": "^1.5.0",
72
72
  "@expo/config-plugins": "^10.1.2",
73
- "@react-native/babel-preset": "0.79.5",
74
- "@react-native/eslint-config": "0.79.5",
75
- "@release-it/conventional-changelog": "^9.0.2",
73
+ "@react-native/babel-preset": "0.79.7",
74
+ "@react-native/eslint-config": "0.79.7",
75
+ "@release-it/conventional-changelog": "^9.0.4",
76
76
  "@semantic-release/changelog": "^6.0.3",
77
77
  "@semantic-release/git": "^10.0.1",
78
- "@semantic-release/github": "^11.0.1",
79
- "@semantic-release/npm": "^12.0.1",
78
+ "@semantic-release/github": "^12.0.5",
79
+ "@semantic-release/npm": "^13.1.4",
80
+ "@testing-library/react-hooks": "^8.0.1",
81
+ "@testing-library/react-native": "^13.3.3",
80
82
  "@types/jest": "^29.5.5",
81
83
  "@types/react": "^19.0.0",
82
- "commitlint": "^19.6.1",
83
- "del-cli": "^5.1.0",
84
+ "commitlint": "^20.4.1",
85
+ "del-cli": "^7.0.0",
84
86
  "eslint": "^9.22.0",
85
- "eslint-config-prettier": "^10.1.1",
87
+ "eslint-config-prettier": "^10.1.8",
86
88
  "eslint-plugin-prettier": "^5.2.3",
87
89
  "jest": "^29.7.0",
88
90
  "prettier": "^3.0.3",
89
91
  "react": "19.0.0",
90
- "react-native": "0.79.5",
91
- "react-native-builder-bob": "^0.40.11",
92
- "release-it": "^17.10.0",
93
- "semantic-release": "^24.1.0",
92
+ "react-native": "0.79.7",
93
+ "react-native-builder-bob": "^0.40.17",
94
+ "react-test-renderer": "19.0.0",
95
+ "release-it": "^19.2.4",
96
+ "semantic-release": "^25.0.3",
94
97
  "turbo": "^1.10.7",
95
98
  "typescript": "^5.8.3"
96
99
  },
@@ -107,7 +110,7 @@
107
110
  "workspaces": [
108
111
  "example"
109
112
  ],
110
- "packageManager": "yarn@3.6.1",
113
+ "packageManager": "yarn@4.9.2",
111
114
  "jest": {
112
115
  "preset": "react-native",
113
116
  "modulePathIgnorePatterns": [
@@ -18,7 +18,14 @@ export interface Spec extends TurboModule {
18
18
  // On iOS, we'll handle everything in JavaScript
19
19
  const NativeModule =
20
20
  Platform.OS === 'android'
21
- ? TurboModuleRegistry.getEnforcing<Spec>('NavigationMode')
21
+ ? TurboModuleRegistry?.getEnforcing
22
+ ? TurboModuleRegistry.getEnforcing<Spec>('NavigationMode')
23
+ : (() => {
24
+ console.error(
25
+ "['react-native-navigation-mode'] TurboModuleRegistry.getEnforcing is not available. Make sure you have enabled the New Architecture (TurboModules) in your project."
26
+ );
27
+ return null;
28
+ })()
22
29
  : null;
23
30
 
24
31
  export default NativeModule;