react-native-navigation-mode 1.0.4 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +221 -121
- package/android/src/main/java/com/navigationmode/NavigationModeModule.kt +41 -2
- package/lib/module/NativeNavigationMode.js.map +1 -1
- package/lib/module/index.js +18 -1
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/NativeNavigationMode.d.ts +2 -0
- package/lib/typescript/src/NativeNavigationMode.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +6 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/NativeNavigationMode.ts +2 -0
- package/src/index.tsx +17 -0
package/README.md
CHANGED
|
@@ -32,66 +32,23 @@
|
|
|
32
32
|
</table>
|
|
33
33
|
</div>
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
## 🤔 Why This Library?
|
|
38
|
-
|
|
39
|
-
Android devices can use different navigation modes, but detecting which one is active has been a major pain point for React Native developers. Most existing solutions rely on unreliable workarounds:
|
|
40
|
-
|
|
41
|
-
### ❌ Common Bad Approaches
|
|
42
|
-
|
|
43
|
-
- **Screen dimension calculations** - Breaks on different screen sizes and orientations
|
|
44
|
-
- **Safe area inset guessing** - Inconsistent across devices and Android versions
|
|
45
|
-
- **Margin-based detection** - Fragile and depends on UI layout changes
|
|
46
|
-
- **Manual device databases** - Impossible to maintain for all Android devices
|
|
35
|
+
------
|
|
47
36
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
This library uses **official Android APIs** to directly query the system's navigation configuration:
|
|
51
|
-
|
|
52
|
-
- **`config_navBarInteractionMode`** - The actual system resource Android uses internally
|
|
53
|
-
- **Settings.Secure provider** - Fallback method for reliable detection
|
|
54
|
-
- **Zero guesswork** - No calculations, no assumptions, just direct system queries
|
|
55
|
-
|
|
56
|
-
### 🚀 Critical for Edge-to-Edge Mode
|
|
57
|
-
|
|
58
|
-
With Android 15 enforcing edge-to-edge display for apps targeting API 35 and Google mandating this for Play Store updates starting August 31, 2025, proper navigation detection is now **essential**:
|
|
59
|
-
|
|
60
|
-
- **Edge-to-edge enforcement** - Android 16 will remove the opt-out entirely
|
|
61
|
-
- **Expo SDK 53+** - New projects use edge-to-edge by default
|
|
62
|
-
- **React Native 0.79+** - Built-in support for 16KB page size and edge-to-edge
|
|
63
|
-
- **Safe area management** - Critical for preventing content overlap with system bars (especially noticeable in 3-button navigation mode).
|
|
64
|
-
|
|
65
|
-
### Real-World Impact
|
|
66
|
-
|
|
67
|
-
```typescript
|
|
68
|
-
// Before: Unreliable dimension-based guessing
|
|
69
|
-
const isGesture = screenHeight === windowHeight; // 😢 Breaks easily
|
|
70
|
-
|
|
71
|
-
// After: Direct system detection
|
|
72
|
-
const isGesture = await isGestureNavigation(); // 🎯 Always accurate
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
**Perfect for:**
|
|
76
|
-
|
|
77
|
-
- 🎨 Adaptive UI layouts based on navigation type
|
|
78
|
-
- 📱 Bottom sheet positioning and safe areas
|
|
79
|
-
- 🧭 Navigation-aware component design
|
|
80
|
-
- 🔄 Edge-to-edge layout compatibility
|
|
81
|
-
- 📊 Analytics and user experience tracking
|
|
82
|
-
|
|
83
|
-
## ✨ Features
|
|
37
|
+
## ✨ Key Features
|
|
84
38
|
|
|
85
39
|
- 🎯 **Direct Native Detection** - No hacky workarounds or dimension-based guessing
|
|
86
|
-
- ⚡ **Turbo Module** - Built
|
|
40
|
+
- ⚡ **Turbo Module** - Built for React Native New Architecture
|
|
87
41
|
- 🔄 **Real-time Detection** - Accurate navigation mode identification
|
|
42
|
+
- 📏 **Navigation Bar Height** - Get exact navigation bar height in dp for precise UI calculations
|
|
88
43
|
- 📱 **Cross Platform** - Android detection + iOS compatibility
|
|
89
44
|
- 🎣 **React Hooks** - Easy integration with `useNavigationMode()`
|
|
90
45
|
- 📦 **Zero Dependencies** - Lightweight and performant
|
|
91
46
|
- 🛡️ **TypeScript** - Full type safety out of the box
|
|
92
47
|
- ↕️ **Edge To Edge Support** - Full support for `react-native-edge-to-edge`
|
|
93
48
|
|
|
94
|
-
##
|
|
49
|
+
## 🚀 Quick Start
|
|
50
|
+
|
|
51
|
+
### Installation
|
|
95
52
|
|
|
96
53
|
Using yarn:
|
|
97
54
|
|
|
@@ -105,59 +62,130 @@ Using npm:
|
|
|
105
62
|
npm install react-native-navigation-mode
|
|
106
63
|
```
|
|
107
64
|
|
|
108
|
-
|
|
65
|
+
> **Note:** Auto-linking should handle setup automatically for all newer RN versions.
|
|
109
66
|
|
|
110
|
-
|
|
67
|
+
---
|
|
111
68
|
|
|
112
|
-
|
|
69
|
+
### Basic Usage
|
|
113
70
|
|
|
114
|
-
|
|
71
|
+
```tsx
|
|
72
|
+
import { useNavigationMode } from 'react-native-navigation-mode';
|
|
115
73
|
|
|
116
|
-
|
|
117
|
-
|
|
74
|
+
export default function App() {
|
|
75
|
+
const { navigationMode, loading, error } = useNavigationMode();
|
|
118
76
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
console.log('Gesture navigation:', isGesture); // true/false
|
|
122
|
-
```
|
|
77
|
+
if (loading) return (<Text>Detecting navigation mode...</Text>);
|
|
78
|
+
if (error) return (<Text>Error: {error.message}</Text>);
|
|
123
79
|
|
|
124
|
-
|
|
80
|
+
return (
|
|
81
|
+
<View>
|
|
82
|
+
<Text>Navigation Type: {navigationMode?.type}</Text>
|
|
83
|
+
<Text>Gesture Navigation: {navigationMode?.isGestureNavigation ? 'Yes' : 'No'}</Text>
|
|
84
|
+
<Text>Navigation Bar Height: {navigationMode?.navigationBarHeight}dp</Text>
|
|
85
|
+
</View>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
```
|
|
125
89
|
|
|
126
|
-
|
|
127
|
-
import { getNavigationMode } from 'react-native-navigation-mode';
|
|
90
|
+
---
|
|
128
91
|
|
|
129
|
-
|
|
130
|
-
const navInfo = await getNavigationMode();
|
|
131
|
-
console.log('Navigation type:', navInfo.type); // '3_button', '2_button', 'gesture', or 'unknown'
|
|
132
|
-
```
|
|
92
|
+
## 🔧 API Reference
|
|
133
93
|
|
|
134
94
|
### React Hook (Recommended)
|
|
135
95
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
96
|
+
#### `useNavigationMode(): { navigationMode, loading, error }`
|
|
97
|
+
|
|
98
|
+
- Returned property types:
|
|
99
|
+
|
|
100
|
+
| Property | Type | Description |
|
|
101
|
+
| ------------- | ------------------------------ | ------------------------------------------------------------ |
|
|
102
|
+
| navigatioMode | `NavigationModeInfo` or `null` | All properties mentioned in [NavigationModeInfo](#navigationmodeinfo). |
|
|
103
|
+
| loading | `boolean` | Indicates if navigation mode info is being fetched. |
|
|
104
|
+
| error | `Error` | Typescript error object containing the cause of the error. |
|
|
105
|
+
|
|
106
|
+
The easiest way to detect navigation mode with loading and error states.
|
|
107
|
+
|
|
108
|
+
```tsx
|
|
139
109
|
import { useNavigationMode } from 'react-native-navigation-mode';
|
|
140
110
|
|
|
141
|
-
|
|
111
|
+
function MyComponent() {
|
|
142
112
|
const { navigationMode, loading, error } = useNavigationMode();
|
|
143
|
-
|
|
144
|
-
if (loading) return <Text>
|
|
113
|
+
|
|
114
|
+
if (loading) return <Text>Loading...</Text>;
|
|
145
115
|
if (error) return <Text>Error: {error.message}</Text>;
|
|
146
|
-
|
|
116
|
+
|
|
147
117
|
return (
|
|
148
118
|
<View>
|
|
149
119
|
<Text>Navigation Type: {navigationMode?.type}</Text>
|
|
150
|
-
<Text>Gesture
|
|
120
|
+
<Text>Is Gesture: {navigationMode?.isGestureNavigation ? 'Yes' : 'No'}</Text>
|
|
121
|
+
<Text>Bar Height: {navigationMode?.navigationBarHeight}dp</Text>
|
|
151
122
|
</View>
|
|
152
123
|
);
|
|
153
124
|
}
|
|
154
125
|
```
|
|
155
126
|
|
|
156
|
-
###
|
|
127
|
+
### Functions
|
|
128
|
+
|
|
129
|
+
#### `getNavigationMode(): Promise<`[NavigationModeInfo](#navigationmodeinfo)`>`
|
|
130
|
+
|
|
131
|
+
Returns comprehensive navigation mode information.
|
|
157
132
|
|
|
158
133
|
```typescript
|
|
159
|
-
import
|
|
160
|
-
|
|
134
|
+
import { getNavigationMode } from 'react-native-navigation-mode';
|
|
135
|
+
|
|
136
|
+
const navInfo = await getNavigationMode();
|
|
137
|
+
console.log('Navigation type:', navInfo.type); // '3_button', '2_button', 'gesture', or 'unknown'
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
#### `isGestureNavigation(): Promise<boolean>`
|
|
141
|
+
|
|
142
|
+
Quick check if device is using gesture navigation.
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
import { isGestureNavigation } from 'react-native-navigation-mode';
|
|
146
|
+
|
|
147
|
+
const isGesture = await isGestureNavigation();
|
|
148
|
+
console.log('Gesture navigation:', isGesture); // true/false
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
#### `getNavigationBarHeight(): Promise<number>`
|
|
152
|
+
|
|
153
|
+
Returns the navigation bar height in density-independent pixels (dp).
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
import { getNavigationBarHeight } from 'react-native-navigation-mode';
|
|
157
|
+
|
|
158
|
+
const height = await getNavigationBarHeight();
|
|
159
|
+
console.log('Navigation bar height:', height); // number (dp)
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Types
|
|
163
|
+
|
|
164
|
+
#### `NavigationModeInfo`
|
|
165
|
+
|
|
166
|
+
| Property | Type | Description |
|
|
167
|
+
| ------------------- | ----------------------------------------------------------- | --------------------------------------------------------- |
|
|
168
|
+
| type | `'3_button'` or `'2_button'` or `'gesture'` or `'unknown'` | 4 possible Android navigation modes that can be detected. |
|
|
169
|
+
| isGestureNavigation | `boolean` | Whether gesture navigation is active. |
|
|
170
|
+
| interactionMode | `number` or `undefined` | See [Navigation Mode Values](#navigation-mode-values) |
|
|
171
|
+
| navigationBarHeight | `number` or `undefined` | Navigation bar height in density-independent pixels (dp). |
|
|
172
|
+
|
|
173
|
+
### Navigation Mode Values
|
|
174
|
+
|
|
175
|
+
| Android Mode | Type | Description |
|
|
176
|
+
| ------------ | ---------- | --------------------------------------------------- |
|
|
177
|
+
| 0 | `3_button` | Traditional Android navigation (Back, Home, Recent) |
|
|
178
|
+
| 1 | `2_button` | Two-button navigation (Back, Home) |
|
|
179
|
+
| 2 | `gesture` | Full gesture navigation |
|
|
180
|
+
| -1 | `unknown` | Could not determine navigation mode |
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## 💡 Usage Examples
|
|
185
|
+
|
|
186
|
+
### Adaptive UI Layout
|
|
187
|
+
|
|
188
|
+
```tsx
|
|
161
189
|
import { useNavigationMode } from 'react-native-navigation-mode';
|
|
162
190
|
|
|
163
191
|
export default function AdaptiveUI() {
|
|
@@ -166,101 +194,173 @@ export default function AdaptiveUI() {
|
|
|
166
194
|
return (
|
|
167
195
|
<View
|
|
168
196
|
style={{
|
|
169
|
-
paddingBottom
|
|
170
|
-
|
|
171
|
-
|
|
197
|
+
// paddingBottom using real navigation bar height
|
|
198
|
+
paddingBottom: navigationMode?.navigationBarHeight || 0,
|
|
199
|
+
}}>
|
|
172
200
|
{/* Your content */}
|
|
173
201
|
</View>
|
|
174
202
|
);
|
|
175
203
|
}
|
|
176
204
|
```
|
|
177
205
|
|
|
178
|
-
|
|
206
|
+
### Conditional Rendering
|
|
179
207
|
|
|
180
|
-
|
|
208
|
+
```tsx
|
|
209
|
+
import { useNavigationMode } from 'react-native-navigation-mode';
|
|
181
210
|
|
|
182
|
-
|
|
211
|
+
export default function ConditionalUI() {
|
|
212
|
+
const { navigationMode } = useNavigationMode();
|
|
183
213
|
|
|
184
|
-
|
|
214
|
+
return (
|
|
215
|
+
<View>
|
|
216
|
+
{navigationMode?.isGestureNavigation && (
|
|
217
|
+
<Text>Swipe gestures are available!</Text>
|
|
218
|
+
)}
|
|
219
|
+
|
|
220
|
+
{navigationMode?.type === '3_button' && (
|
|
221
|
+
<Text>Traditional navigation buttons detected</Text>
|
|
222
|
+
)}
|
|
223
|
+
</View>
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
```
|
|
185
227
|
|
|
186
|
-
|
|
228
|
+
### Manual Detection
|
|
187
229
|
|
|
188
|
-
|
|
230
|
+
```typescript
|
|
231
|
+
import {
|
|
232
|
+
getNavigationMode,
|
|
233
|
+
isGestureNavigation,
|
|
234
|
+
getNavigationBarHeight
|
|
235
|
+
} from 'react-native-navigation-mode';
|
|
236
|
+
|
|
237
|
+
const checkNavigation = async () => {
|
|
238
|
+
// Get all info at once
|
|
239
|
+
const navInfo = await getNavigationMode();
|
|
240
|
+
|
|
241
|
+
// Or get specific info
|
|
242
|
+
const isGesture = await isGestureNavigation();
|
|
243
|
+
const barHeight = await getNavigationBarHeight();
|
|
244
|
+
|
|
245
|
+
console.log('Navigation info:', navInfo);
|
|
246
|
+
console.log('Is gesture:', isGesture);
|
|
247
|
+
console.log('Bar height:', barHeight);
|
|
248
|
+
};
|
|
249
|
+
```
|
|
189
250
|
|
|
190
|
-
|
|
251
|
+
---
|
|
191
252
|
|
|
192
|
-
|
|
253
|
+
## 🤔 Why This Library?
|
|
193
254
|
|
|
194
|
-
|
|
255
|
+
Android devices can use different navigation modes, but detecting which one is active has been a major pain point for React Native developers. Most existing solutions rely on unreliable workarounds:
|
|
195
256
|
|
|
196
|
-
###
|
|
257
|
+
### ❌ Common Bad Approaches
|
|
197
258
|
|
|
198
|
-
|
|
259
|
+
- **Screen dimension calculations** - Breaks on different screen sizes and orientations
|
|
260
|
+
- **Safe area inset guessing** - Inconsistent across devices and Android versions
|
|
261
|
+
- **Margin-based detection** - Fragile and depends on UI layout changes
|
|
262
|
+
- **Manual device databases** - Impossible to maintain for all Android devices
|
|
263
|
+
|
|
264
|
+
### ✅ This Library's Solution
|
|
265
|
+
|
|
266
|
+
This library uses **official Android APIs** to directly query the system's navigation configuration:
|
|
267
|
+
|
|
268
|
+
- **`config_navBarInteractionMode`** - The actual system resource Android uses internally
|
|
269
|
+
- **Settings.Secure provider** - Fallback method for reliable detection
|
|
270
|
+
- **WindowInsets API** - Accurate navigation bar height detection
|
|
271
|
+
- **Zero guesswork** - No calculations, no assumptions, just direct system queries
|
|
272
|
+
|
|
273
|
+
### 🚀 Critical for Edge-to-Edge Mode
|
|
274
|
+
|
|
275
|
+
With Android 15 enforcing edge-to-edge display for apps targeting API 35 and Google mandating this for Play Store updates starting August 31, 2025, proper navigation detection is now **essential**:
|
|
276
|
+
|
|
277
|
+
- **Edge-to-edge enforcement** - Android 16 will remove the opt-out entirely
|
|
278
|
+
- **Expo SDK 53+** - New projects use edge-to-edge by default
|
|
279
|
+
- **React Native 0.79+** - Built-in support for 16KB page size and edge-to-edge
|
|
280
|
+
- **Safe area management** - Critical for preventing content overlap with system bars
|
|
281
|
+
|
|
282
|
+
### Real-World Impact
|
|
283
|
+
|
|
284
|
+
```typescript
|
|
285
|
+
// Before: Unreliable dimension-based guessing
|
|
286
|
+
const isGesture = screenHeight === windowHeight; // 😢 Breaks easily
|
|
199
287
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
288
|
+
// After: Direct system detection
|
|
289
|
+
const isGesture = await isGestureNavigation(); // 🎯 Always accurate
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
**Perfect for:**
|
|
293
|
+
|
|
294
|
+
- 🎨 Adaptive UI layouts based on navigation type
|
|
295
|
+
- 📱 Bottom sheet positioning and safe areas
|
|
296
|
+
- 🧭 Navigation-aware component design
|
|
297
|
+
- 🔄 Edge-to-edge layout compatibility
|
|
298
|
+
- 📊 Analytics and user experience tracking
|
|
205
299
|
|
|
206
|
-
|
|
300
|
+
---
|
|
207
301
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
302
|
+
## 🛠️ Technical Details
|
|
303
|
+
|
|
304
|
+
### Platform Support
|
|
305
|
+
|
|
306
|
+
| Platform | Support | Notes |
|
|
307
|
+
| -------- | ------------ | ------------------------------------------------------------ |
|
|
308
|
+
| Android | ✅ Full | Detects all navigation modes and navigation bar height via native Android APIs |
|
|
309
|
+
| iOS | ✅ Compatible | Always returns `gesture` and `navigationBarHeight: 0` (iOS uses gesture navigation) |
|
|
212
310
|
|
|
213
311
|
### Android Compatibility
|
|
214
312
|
|
|
215
313
|
- **API 21+** - Basic navigation bar detection
|
|
216
314
|
- **API 29+** - Full navigation mode detection (`config_navBarInteractionMode`)
|
|
217
315
|
- **All versions** - Fallback detection methods included
|
|
316
|
+
- **API 30+** - WindowInsets-based navigation bar height detection
|
|
317
|
+
- **API 24-29** - Resource-based navigation bar height fallback
|
|
218
318
|
|
|
219
|
-
|
|
319
|
+
### How It Works
|
|
220
320
|
|
|
221
321
|
The library uses multiple detection methods for maximum accuracy:
|
|
222
322
|
|
|
223
323
|
1. **`config_navBarInteractionMode`** - Official Android configuration (API 29+)
|
|
224
324
|
2. **Settings Provider** - Checks `navigation_mode` system setting
|
|
225
|
-
3. **
|
|
226
|
-
4. **
|
|
227
|
-
|
|
228
|
-
### Navigation Mode Values
|
|
229
|
-
|
|
230
|
-
| Android Mode | Type | Description |
|
|
231
|
-
|--------------|------|-------------|
|
|
232
|
-
| 0 | `3_button` | Traditional Android navigation (Back, Home, Recent) |
|
|
233
|
-
| 1 | `2_button` | Two-button navigation (Back, Home) |
|
|
234
|
-
| 2 | `gesture` | Full gesture navigation |
|
|
235
|
-
| -1 | `unknown` | Could not determine navigation mode |
|
|
325
|
+
3. **WindowInsets API** - Accurate navigation bar height detection (API 30+)
|
|
326
|
+
4. **Resource-based fallback** - Navigation bar height for older devices
|
|
236
327
|
|
|
237
|
-
|
|
328
|
+
### Performance Notes
|
|
238
329
|
|
|
239
|
-
1. 🍎 **iOS Behavior** - iOS always returns `isGestureNavigation: true` since iOS doesn't have
|
|
330
|
+
1. 🍎 **iOS Behavior** - iOS always returns `isGestureNavigation: true` and `navigationBarHeight: 0` since iOS doesn't have Android-style navigation bars
|
|
240
331
|
2. ⚡ **Performance** - Turbo module ensures minimal performance impact
|
|
241
332
|
3. 🔄 **Real-time** - Navigation mode is detected at call time, reflecting current device settings
|
|
242
333
|
|
|
243
|
-
|
|
334
|
+
---
|
|
335
|
+
|
|
336
|
+
## 🐛 Troubleshooting
|
|
244
337
|
|
|
245
338
|
### Common Issues
|
|
246
339
|
|
|
247
340
|
**"TurboModuleRegistry.getEnforcing(...) is not a function"**
|
|
341
|
+
|
|
248
342
|
- Ensure you're using React Native 0.68+ with new architecture enabled
|
|
249
343
|
- For older RN versions, the module will fallback gracefully
|
|
250
344
|
|
|
251
|
-
**Always returns 'unknown' on Android**
|
|
345
|
+
**Always returns `'unknown'` on Android**
|
|
346
|
+
|
|
252
347
|
- Check if your device/emulator supports the navigation mode APIs
|
|
253
348
|
- Some custom ROMs may not expose standard Android navigation settings
|
|
254
349
|
|
|
255
|
-
|
|
350
|
+
**Navigation bar height returns `0`**
|
|
351
|
+
|
|
352
|
+
- This is normal on devices without navigation bars (some tablets)
|
|
353
|
+
- On older Android versions, fallback detection may not work on all devices
|
|
354
|
+
|
|
355
|
+
## 🤝 Contributing
|
|
256
356
|
|
|
257
357
|
See the [contributing guide](CONTRIBUTING.md) to learn how to contribute to the repository and the development workflow.
|
|
258
358
|
|
|
259
|
-
## License
|
|
359
|
+
## 📄 License
|
|
260
360
|
|
|
261
361
|
MIT
|
|
262
362
|
|
|
263
|
-
## Support the
|
|
363
|
+
## 💖 Support the Project
|
|
264
364
|
|
|
265
365
|
<p align="center" valign="center">
|
|
266
366
|
<a href="https://liberapay.com/FutureJJ/donate">
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
package com.navigationmode
|
|
2
2
|
|
|
3
|
+
import android.app.Activity
|
|
3
4
|
import android.content.Context
|
|
4
5
|
import android.content.res.Resources
|
|
5
6
|
import android.os.Build
|
|
6
7
|
import android.provider.Settings
|
|
7
|
-
import android.view.
|
|
8
|
+
import android.view.WindowInsets
|
|
9
|
+
import com.facebook.react.bridge.Arguments
|
|
8
10
|
import com.facebook.react.bridge.Promise
|
|
9
11
|
import com.facebook.react.bridge.ReactApplicationContext
|
|
10
12
|
import com.facebook.react.bridge.WritableMap
|
|
11
|
-
import com.facebook.react.bridge.Arguments
|
|
12
13
|
import com.facebook.react.module.annotations.ReactModule
|
|
13
14
|
|
|
14
15
|
@ReactModule(name = NavigationModeModule.NAME)
|
|
@@ -21,11 +22,49 @@ class NavigationModeModule(reactContext: ReactApplicationContext) :
|
|
|
21
22
|
|
|
22
23
|
override fun getName(): String = NAME
|
|
23
24
|
|
|
25
|
+
private fun getNavigationBarHeight(context: Context): Int {
|
|
26
|
+
// Try to get Activity for WindowInsets
|
|
27
|
+
val activity = if (context is Activity) context else reactApplicationContext.currentActivity
|
|
28
|
+
val density = context.resources.displayMetrics.density // Get device density
|
|
29
|
+
|
|
30
|
+
if (activity != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
|
31
|
+
val insets = activity.window.decorView.rootWindowInsets
|
|
32
|
+
if (insets != null) {
|
|
33
|
+
val navBar = insets.getInsets(WindowInsets.Type.navigationBars())
|
|
34
|
+
return (navBar.bottom / density).toInt() // Convert pixels to dp
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Fallback to resource-based approach for API < 30
|
|
39
|
+
val resources = context.resources
|
|
40
|
+
val resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android")
|
|
41
|
+
return if (resourceId > 0) {
|
|
42
|
+
// getDimensionPixelSize returns pixels, convert to dp
|
|
43
|
+
(resources.getDimensionPixelSize(resourceId) / density).toInt()
|
|
44
|
+
} else {
|
|
45
|
+
0 // Fallback for devices without a navigation bar
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
override fun getNavigationBarHeight(promise: Promise) {
|
|
50
|
+
try {
|
|
51
|
+
val context = reactApplicationContext
|
|
52
|
+
val navBarHeight = getNavigationBarHeight(context)
|
|
53
|
+
promise.resolve(navBarHeight)
|
|
54
|
+
} catch (e: Exception) {
|
|
55
|
+
promise.reject("NAV_BAR_HEIGHT_ERROR", "Failed to get navigation bar height: ${e.message}", e)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
24
59
|
override fun getNavigationMode(promise: Promise) {
|
|
25
60
|
try {
|
|
26
61
|
val result = Arguments.createMap()
|
|
27
62
|
val context = reactApplicationContext
|
|
28
63
|
|
|
64
|
+
// Use reactApplicationContext for navigation bar height
|
|
65
|
+
val navBarHeight = getNavigationBarHeight(context)
|
|
66
|
+
result.putInt("navigationBarHeight", navBarHeight)
|
|
67
|
+
|
|
29
68
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
30
69
|
val navBarInteractionMode = getNavBarInteractionMode(context)
|
|
31
70
|
result.putInt("interactionMode", navBarInteractionMode)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["TurboModuleRegistry","Platform","NativeModule","OS","getEnforcing"],"sourceRoot":"../../src","sources":["NativeNavigationMode.ts"],"mappings":";;AACA,SAASA,mBAAmB,EAAEC,QAAQ,QAAQ,cAAc;
|
|
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":[]}
|
package/lib/module/index.js
CHANGED
|
@@ -13,7 +13,8 @@ export function getNavigationMode() {
|
|
|
13
13
|
// iOS always uses gesture navigation (no 3-button navigation exists)
|
|
14
14
|
return Promise.resolve({
|
|
15
15
|
type: 'gesture',
|
|
16
|
-
isGestureNavigation: true
|
|
16
|
+
isGestureNavigation: true,
|
|
17
|
+
navigationBarHeight: 0 // iOS doesn't have a navigation bar like Android
|
|
17
18
|
});
|
|
18
19
|
}
|
|
19
20
|
|
|
@@ -36,6 +37,21 @@ export function isGestureNavigation() {
|
|
|
36
37
|
return NavigationModeModule.isGestureNavigation();
|
|
37
38
|
}
|
|
38
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Get the navigation bar height in dp
|
|
42
|
+
* @returns Promise<number> - navigation bar height in dp
|
|
43
|
+
*/
|
|
44
|
+
export function getNavigationBarHeight() {
|
|
45
|
+
// null check is redundant as it's always null for iOS but it's there to satisfy TypeScript
|
|
46
|
+
if (Platform.OS === 'ios' || NavigationModeModule === null) {
|
|
47
|
+
// iOS doesn't have a navigation bar like Android
|
|
48
|
+
return Promise.resolve(0);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Only call native module on Android
|
|
52
|
+
return NavigationModeModule.getNavigationBarHeight();
|
|
53
|
+
}
|
|
54
|
+
|
|
39
55
|
/**
|
|
40
56
|
* Hook for React components to get navigation mode
|
|
41
57
|
*/
|
|
@@ -76,6 +92,7 @@ export function useNavigationMode() {
|
|
|
76
92
|
export default {
|
|
77
93
|
getNavigationMode,
|
|
78
94
|
isGestureNavigation,
|
|
95
|
+
getNavigationBarHeight,
|
|
79
96
|
useNavigationMode
|
|
80
97
|
};
|
|
81
98
|
//# sourceMappingURL=index.js.map
|
package/lib/module/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["Platform","React","NavigationModeModule","getNavigationMode","OS","Promise","resolve","type","isGestureNavigation","useNavigationMode","navigationMode","setNavigationMode","useState","loading","setLoading","error","setError","useEffect","mounted","fetchNavigationMode","mode","err","Error"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,SAASA,QAAQ,QAAQ,cAAc;AACvC,OAAOC,KAAK,MAAM,OAAO;AACzB,OAAOC,oBAAoB,MAEpB,2BAAwB;AAI/B;AACA;AACA;AACA;AACA,OAAO,SAASC,iBAAiBA,CAAA,EAAgC;EAC/D;EACA,IAAIH,QAAQ,CAACI,EAAE,KAAK,KAAK,IAAIF,oBAAoB,KAAK,IAAI,EAAE;IAC1D;IACA,OAAOG,OAAO,CAACC,OAAO,CAAC;MACrBC,IAAI,EAAE,SAAS;MACfC,mBAAmB,EAAE;
|
|
1
|
+
{"version":3,"names":["Platform","React","NavigationModeModule","getNavigationMode","OS","Promise","resolve","type","isGestureNavigation","navigationBarHeight","getNavigationBarHeight","useNavigationMode","navigationMode","setNavigationMode","useState","loading","setLoading","error","setError","useEffect","mounted","fetchNavigationMode","mode","err","Error"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,SAASA,QAAQ,QAAQ,cAAc;AACvC,OAAOC,KAAK,MAAM,OAAO;AACzB,OAAOC,oBAAoB,MAEpB,2BAAwB;AAI/B;AACA;AACA;AACA;AACA,OAAO,SAASC,iBAAiBA,CAAA,EAAgC;EAC/D;EACA,IAAIH,QAAQ,CAACI,EAAE,KAAK,KAAK,IAAIF,oBAAoB,KAAK,IAAI,EAAE;IAC1D;IACA,OAAOG,OAAO,CAACC,OAAO,CAAC;MACrBC,IAAI,EAAE,SAAS;MACfC,mBAAmB,EAAE,IAAI;MACzBC,mBAAmB,EAAE,CAAC,CAAE;IAC1B,CAAC,CAAC;EACJ;;EAEA;EACA,OAAOP,oBAAoB,CAACC,iBAAiB,CAAC,CAAC;AACjD;;AAEA;AACA;AACA;AACA;AACA,OAAO,SAASK,mBAAmBA,CAAA,EAAqB;EACtD;EACA,IAAIR,QAAQ,CAACI,EAAE,KAAK,KAAK,IAAIF,oBAAoB,KAAK,IAAI,EAAE;IAC1D;IACA,OAAOG,OAAO,CAACC,OAAO,CAAC,IAAI,CAAC;EAC9B;;EAEA;EACA,OAAOJ,oBAAoB,CAACM,mBAAmB,CAAC,CAAC;AACnD;;AAEA;AACA;AACA;AACA;AACA,OAAO,SAASE,sBAAsBA,CAAA,EAAoB;EACxD;EACA,IAAIV,QAAQ,CAACI,EAAE,KAAK,KAAK,IAAIF,oBAAoB,KAAK,IAAI,EAAE;IAC1D;IACA,OAAOG,OAAO,CAACC,OAAO,CAAC,CAAC,CAAC;EAC3B;;EAEA;EACA,OAAOJ,oBAAoB,CAACQ,sBAAsB,CAAC,CAAC;AACtD;;AAEA;AACA;AACA;AACA,OAAO,SAASC,iBAAiBA,CAAA,EAAG;EAClC,MAAM,CAACC,cAAc,EAAEC,iBAAiB,CAAC,GACvCZ,KAAK,CAACa,QAAQ,CAA4B,IAAI,CAAC;EACjD,MAAM,CAACC,OAAO,EAAEC,UAAU,CAAC,GAAGf,KAAK,CAACa,QAAQ,CAAC,IAAI,CAAC;EAClD,MAAM,CAACG,KAAK,EAAEC,QAAQ,CAAC,GAAGjB,KAAK,CAACa,QAAQ,CAAe,IAAI,CAAC;EAE5Db,KAAK,CAACkB,SAAS,CAAC,MAAM;IACpB,IAAIC,OAAO,GAAG,IAAI;IAElB,eAAeC,mBAAmBA,CAAA,EAAG;MACnC,IAAI;QACF,MAAMC,IAAI,GAAG,MAAMnB,iBAAiB,CAAC,CAAC;QACtC,IAAIiB,OAAO,EAAE;UACXP,iBAAiB,CAACS,IAAI,CAAC;UACvBJ,QAAQ,CAAC,IAAI,CAAC;QAChB;MACF,CAAC,CAAC,OAAOK,GAAG,EAAE;QACZ,IAAIH,OAAO,EAAE;UACXF,QAAQ,CAACK,GAAG,YAAYC,KAAK,GAAGD,GAAG,GAAG,IAAIC,KAAK,CAAC,eAAe,CAAC,CAAC;QACnE;MACF,CAAC,SAAS;QACR,IAAIJ,OAAO,EAAE;UACXJ,UAAU,CAAC,KAAK,CAAC;QACnB;MACF;IACF;IAEAK,mBAAmB,CAAC,CAAC;IAErB,OAAO,MAAM;MACXD,OAAO,GAAG,KAAK;IACjB,CAAC;EACH,CAAC,EAAE,EAAE,CAAC;EAEN,OAAO;IAAER,cAAc;IAAEG,OAAO;IAAEE;EAAM,CAAC;AAC3C;AAEA,eAAe;EACbd,iBAAiB;EACjBK,mBAAmB;EACnBE,sBAAsB;EACtBC;AACF,CAAC","ignoreList":[]}
|
|
@@ -3,10 +3,12 @@ export interface NavigationModeInfo {
|
|
|
3
3
|
type: '3_button' | '2_button' | 'gesture' | 'unknown';
|
|
4
4
|
isGestureNavigation: boolean;
|
|
5
5
|
interactionMode?: number;
|
|
6
|
+
navigationBarHeight?: number;
|
|
6
7
|
}
|
|
7
8
|
export interface Spec extends TurboModule {
|
|
8
9
|
getNavigationMode(): Promise<NavigationModeInfo>;
|
|
9
10
|
isGestureNavigation(): Promise<boolean>;
|
|
11
|
+
getNavigationBarHeight(): Promise<number>;
|
|
10
12
|
}
|
|
11
13
|
declare const NativeModule: Spec | null;
|
|
12
14
|
export default NativeModule;
|
|
@@ -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;
|
|
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,CAAC,EAAE,MAAM,CAAC;CAC9B;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"}
|
|
@@ -10,6 +10,11 @@ export declare function getNavigationMode(): Promise<NavigationModeInfo>;
|
|
|
10
10
|
* @returns Promise<boolean> - true if gesture navigation is active
|
|
11
11
|
*/
|
|
12
12
|
export declare function isGestureNavigation(): Promise<boolean>;
|
|
13
|
+
/**
|
|
14
|
+
* Get the navigation bar height in dp
|
|
15
|
+
* @returns Promise<number> - navigation bar height in dp
|
|
16
|
+
*/
|
|
17
|
+
export declare function getNavigationBarHeight(): Promise<number>;
|
|
13
18
|
/**
|
|
14
19
|
* Hook for React components to get navigation mode
|
|
15
20
|
*/
|
|
@@ -21,6 +26,7 @@ export declare function useNavigationMode(): {
|
|
|
21
26
|
declare const _default: {
|
|
22
27
|
readonly getNavigationMode: typeof getNavigationMode;
|
|
23
28
|
readonly isGestureNavigation: typeof isGestureNavigation;
|
|
29
|
+
readonly getNavigationBarHeight: typeof getNavigationBarHeight;
|
|
24
30
|
readonly useNavigationMode: typeof useNavigationMode;
|
|
25
31
|
};
|
|
26
32
|
export default _default;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAEA,OAA6B,EAC3B,KAAK,kBAAkB,EACxB,MAAM,wBAAwB,CAAC;AAEhC,YAAY,EAAE,kBAAkB,EAAE,CAAC;AAEnC;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAAC,kBAAkB,CAAC,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAEA,OAA6B,EAC3B,KAAK,kBAAkB,EACxB,MAAM,wBAAwB,CAAC;AAEhC,YAAY,EAAE,kBAAkB,EAAE,CAAC;AAEnC;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAAC,kBAAkB,CAAC,CAa/D;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,IAAI,OAAO,CAAC,OAAO,CAAC,CAStD;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,OAAO,CAAC,MAAM,CAAC,CASxD;AAED;;GAEG;AACH,wBAAgB,iBAAiB;;;;EAmChC;;;;;;;AAED,wBAKW"}
|
package/package.json
CHANGED
|
@@ -5,11 +5,13 @@ export interface NavigationModeInfo {
|
|
|
5
5
|
type: '3_button' | '2_button' | 'gesture' | 'unknown';
|
|
6
6
|
isGestureNavigation: boolean;
|
|
7
7
|
interactionMode?: number;
|
|
8
|
+
navigationBarHeight?: number;
|
|
8
9
|
}
|
|
9
10
|
|
|
10
11
|
export interface Spec extends TurboModule {
|
|
11
12
|
getNavigationMode(): Promise<NavigationModeInfo>;
|
|
12
13
|
isGestureNavigation(): Promise<boolean>;
|
|
14
|
+
getNavigationBarHeight(): Promise<number>;
|
|
13
15
|
}
|
|
14
16
|
|
|
15
17
|
// Only get the native module on Android
|
package/src/index.tsx
CHANGED
|
@@ -17,6 +17,7 @@ export function getNavigationMode(): Promise<NavigationModeInfo> {
|
|
|
17
17
|
return Promise.resolve({
|
|
18
18
|
type: 'gesture',
|
|
19
19
|
isGestureNavigation: true,
|
|
20
|
+
navigationBarHeight: 0, // iOS doesn't have a navigation bar like Android
|
|
20
21
|
});
|
|
21
22
|
}
|
|
22
23
|
|
|
@@ -39,6 +40,21 @@ export function isGestureNavigation(): Promise<boolean> {
|
|
|
39
40
|
return NavigationModeModule.isGestureNavigation();
|
|
40
41
|
}
|
|
41
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Get the navigation bar height in dp
|
|
45
|
+
* @returns Promise<number> - navigation bar height in dp
|
|
46
|
+
*/
|
|
47
|
+
export function getNavigationBarHeight(): Promise<number> {
|
|
48
|
+
// null check is redundant as it's always null for iOS but it's there to satisfy TypeScript
|
|
49
|
+
if (Platform.OS === 'ios' || NavigationModeModule === null) {
|
|
50
|
+
// iOS doesn't have a navigation bar like Android
|
|
51
|
+
return Promise.resolve(0);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Only call native module on Android
|
|
55
|
+
return NavigationModeModule.getNavigationBarHeight();
|
|
56
|
+
}
|
|
57
|
+
|
|
42
58
|
/**
|
|
43
59
|
* Hook for React components to get navigation mode
|
|
44
60
|
*/
|
|
@@ -82,5 +98,6 @@ export function useNavigationMode() {
|
|
|
82
98
|
export default {
|
|
83
99
|
getNavigationMode,
|
|
84
100
|
isGestureNavigation,
|
|
101
|
+
getNavigationBarHeight,
|
|
85
102
|
useNavigationMode,
|
|
86
103
|
} as const;
|