react-native-nitro-location-tracking 0.1.5 → 0.1.6

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.
Files changed (76) hide show
  1. package/README.md +677 -7
  2. package/android/src/main/java/com/margelo/nitro/nitrolocationtracking/GeofenceManager.kt +148 -0
  3. package/android/src/main/java/com/margelo/nitro/nitrolocationtracking/LocationEngine.kt +55 -1
  4. package/android/src/main/java/com/margelo/nitro/nitrolocationtracking/NitroLocationTracking.kt +127 -0
  5. package/android/src/main/java/com/margelo/nitro/nitrolocationtracking/ProviderStatusMonitor.kt +73 -0
  6. package/android/src/main/java/com/margelo/nitro/nitrolocationtracking/SpeedMonitor.kt +38 -0
  7. package/android/src/main/java/com/margelo/nitro/nitrolocationtracking/TripCalculator.kt +85 -0
  8. package/ios/GeofenceManager.swift +69 -0
  9. package/ios/LocationEngine.swift +56 -2
  10. package/ios/NitroLocationTracking.swift +104 -0
  11. package/ios/SpeedMonitor.swift +48 -0
  12. package/ios/TripCalculator.swift +93 -0
  13. package/lib/module/index.js.map +1 -1
  14. package/lib/typescript/src/NitroLocationTracking.nitro.d.ts +44 -0
  15. package/lib/typescript/src/NitroLocationTracking.nitro.d.ts.map +1 -1
  16. package/lib/typescript/src/index.d.ts +1 -1
  17. package/lib/typescript/src/index.d.ts.map +1 -1
  18. package/lib/typescript/test.d.ts +1 -0
  19. package/lib/typescript/test.d.ts.map +1 -0
  20. package/nitrogen/generated/android/c++/JFunc_void_GeofenceEvent_std__string.hpp +78 -0
  21. package/nitrogen/generated/android/c++/JFunc_void_LocationData.hpp +1 -0
  22. package/nitrogen/generated/android/c++/JFunc_void_LocationProviderStatus_LocationProviderStatus.hpp +77 -0
  23. package/nitrogen/generated/android/c++/JFunc_void_SpeedAlertType_double.hpp +77 -0
  24. package/nitrogen/generated/android/c++/JGeofenceEvent.hpp +58 -0
  25. package/nitrogen/generated/android/c++/JGeofenceRegion.hpp +77 -0
  26. package/nitrogen/generated/android/c++/JHybridNitroLocationTrackingSpec.cpp +102 -0
  27. package/nitrogen/generated/android/c++/JHybridNitroLocationTrackingSpec.hpp +16 -0
  28. package/nitrogen/generated/android/c++/JLocationData.hpp +8 -4
  29. package/nitrogen/generated/android/c++/JLocationProviderStatus.hpp +58 -0
  30. package/nitrogen/generated/android/c++/JPermissionStatus.hpp +67 -0
  31. package/nitrogen/generated/android/c++/JSpeedAlertType.hpp +61 -0
  32. package/nitrogen/generated/android/c++/JSpeedConfig.hpp +65 -0
  33. package/nitrogen/generated/android/c++/JTripStats.hpp +73 -0
  34. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrolocationtracking/Func_void_GeofenceEvent_std__string.kt +80 -0
  35. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrolocationtracking/Func_void_LocationProviderStatus_LocationProviderStatus.kt +80 -0
  36. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrolocationtracking/Func_void_SpeedAlertType_double.kt +80 -0
  37. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrolocationtracking/GeofenceEvent.kt +23 -0
  38. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrolocationtracking/GeofenceRegion.kt +53 -0
  39. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrolocationtracking/HybridNitroLocationTrackingSpec.kt +79 -0
  40. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrolocationtracking/LocationData.kt +6 -3
  41. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrolocationtracking/LocationProviderStatus.kt +23 -0
  42. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrolocationtracking/PermissionStatus.kt +26 -0
  43. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrolocationtracking/SpeedAlertType.kt +24 -0
  44. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrolocationtracking/SpeedConfig.kt +44 -0
  45. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrolocationtracking/TripStats.kt +50 -0
  46. package/nitrogen/generated/android/nitrolocationtrackingOnLoad.cpp +6 -0
  47. package/nitrogen/generated/ios/NitroLocationTracking-Swift-Cxx-Bridge.cpp +24 -0
  48. package/nitrogen/generated/ios/NitroLocationTracking-Swift-Cxx-Bridge.hpp +124 -0
  49. package/nitrogen/generated/ios/NitroLocationTracking-Swift-Cxx-Umbrella.hpp +22 -0
  50. package/nitrogen/generated/ios/c++/HybridNitroLocationTrackingSpecSwift.hpp +130 -0
  51. package/nitrogen/generated/ios/swift/Func_void_GeofenceEvent_std__string.swift +46 -0
  52. package/nitrogen/generated/ios/swift/Func_void_LocationProviderStatus_LocationProviderStatus.swift +46 -0
  53. package/nitrogen/generated/ios/swift/Func_void_SpeedAlertType_double.swift +46 -0
  54. package/nitrogen/generated/ios/swift/GeofenceEvent.swift +40 -0
  55. package/nitrogen/generated/ios/swift/GeofenceRegion.swift +54 -0
  56. package/nitrogen/generated/ios/swift/HybridNitroLocationTrackingSpec.swift +16 -0
  57. package/nitrogen/generated/ios/swift/HybridNitroLocationTrackingSpec_cxx.swift +197 -0
  58. package/nitrogen/generated/ios/swift/LocationData.swift +20 -2
  59. package/nitrogen/generated/ios/swift/LocationProviderStatus.swift +40 -0
  60. package/nitrogen/generated/ios/swift/PermissionStatus.swift +52 -0
  61. package/nitrogen/generated/ios/swift/SpeedAlertType.swift +44 -0
  62. package/nitrogen/generated/ios/swift/SpeedConfig.swift +39 -0
  63. package/nitrogen/generated/ios/swift/TripStats.swift +49 -0
  64. package/nitrogen/generated/shared/c++/GeofenceEvent.hpp +76 -0
  65. package/nitrogen/generated/shared/c++/GeofenceRegion.hpp +103 -0
  66. package/nitrogen/generated/shared/c++/HybridNitroLocationTrackingSpec.cpp +16 -0
  67. package/nitrogen/generated/shared/c++/HybridNitroLocationTrackingSpec.hpp +37 -0
  68. package/nitrogen/generated/shared/c++/LocationData.hpp +7 -3
  69. package/nitrogen/generated/shared/c++/LocationProviderStatus.hpp +76 -0
  70. package/nitrogen/generated/shared/c++/PermissionStatus.hpp +88 -0
  71. package/nitrogen/generated/shared/c++/SpeedAlertType.hpp +80 -0
  72. package/nitrogen/generated/shared/c++/SpeedConfig.hpp +91 -0
  73. package/nitrogen/generated/shared/c++/TripStats.hpp +99 -0
  74. package/package.json +2 -2
  75. package/src/NitroLocationTracking.nitro.ts +71 -0
  76. package/src/index.tsx +10 -0
package/README.md CHANGED
@@ -1,28 +1,698 @@
1
1
  # react-native-nitro-location-tracking
2
2
 
3
-
3
+ A high-performance React Native location tracking library built with [Nitro Modules](https://nitro.margelo.com/). Designed for ride-hailing, delivery, and fleet tracking apps with background location, WebSocket connectivity, foreground service notifications, and smooth map marker animations.
4
4
 
5
- ## Installation
5
+ ## Features
6
+
7
+ - **Background location tracking** with foreground service (Android) and background modes (iOS)
8
+ - **WebSocket connection manager** with auto-reconnect and batch sync
9
+ - **Fake GPS detection** — detect mock locations and optionally reject them
10
+ - **Geofencing** — monitor enter/exit events for circular regions
11
+ - **Speed Monitoring** — configurable speed alerts with state-transition callbacks
12
+ - **Distance Calculator** — running trip stats with Haversine distance
13
+ - **Location Provider Status** — detect when GPS/location is turned on/off
14
+ - **Smooth map marker animations** via `LocationSmoother`
15
+ - **Bearing calculation** utilities for rotation/heading
16
+ - **Foreground notifications** (Android foreground service, iOS local notifications)
17
+ - **Permission helpers** for fine, background, and notification permissions
18
+ - **Built on Nitro Modules** for near-native performance via JSI
6
19
 
20
+ ## Installation
7
21
 
8
22
  ```sh
9
23
  npm install react-native-nitro-location-tracking react-native-nitro-modules
24
+ # or
25
+ yarn add react-native-nitro-location-tracking react-native-nitro-modules
26
+ ```
27
+
28
+ > `react-native-nitro-modules` is a required peer dependency.
29
+
30
+ ### iOS Setup
10
31
 
11
- > `react-native-nitro-modules` is required as this library relies on [Nitro Modules](https://nitro.margelo.com/).
32
+ 1. Install pods:
33
+
34
+ ```sh
35
+ cd ios && pod install
36
+ ```
37
+
38
+ 1. Add the following keys to your `Info.plist`:
39
+
40
+ ```xml
41
+ <key>NSLocationWhenInUseUsageDescription</key>
42
+ <string>We need your location to track your ride.</string>
43
+ <key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
44
+ <string>We need background location access to continue tracking while the app is minimized.</string>
12
45
  ```
13
46
 
47
+ 1. Enable **Background Modes** in Xcode:
48
+ - Go to your target > **Signing & Capabilities** > **+ Capability** > **Background Modes**
49
+ - Check **Location updates**
50
+
51
+ ### Android Setup
52
+
53
+ The library's `AndroidManifest.xml` automatically merges the required permissions and the foreground service declaration. No manual changes needed.
54
+
55
+ Permissions included automatically:
56
+
57
+ - `ACCESS_FINE_LOCATION`
58
+ - `ACCESS_COARSE_LOCATION`
59
+ - `ACCESS_BACKGROUND_LOCATION`
60
+ - `FOREGROUND_SERVICE`
61
+ - `FOREGROUND_SERVICE_LOCATION`
62
+ - `POST_NOTIFICATIONS`
63
+ - `INTERNET`
64
+ - `RECEIVE_BOOT_COMPLETED`
65
+ - `WAKE_LOCK`
14
66
 
15
67
  ## Usage
16
68
 
69
+ ### Request Permissions
70
+
71
+ Call this before starting location tracking, especially on Android:
72
+
73
+ ```tsx
74
+ import { requestLocationPermission } from 'react-native-nitro-location-tracking';
75
+
76
+ const granted = await requestLocationPermission();
77
+ if (!granted) {
78
+ console.warn('Location permission denied');
79
+ }
80
+ ```
81
+
82
+ You can customize the permission dialog messages:
83
+
84
+ ```tsx
85
+ const granted = await requestLocationPermission(
86
+ {
87
+ title: 'Location Access',
88
+ message: 'We need your location to show your position on the map.',
89
+ buttonPositive: 'Allow',
90
+ buttonNegative: 'Deny',
91
+ },
92
+ {
93
+ title: 'Background Location',
94
+ message: 'Allow background location to keep tracking while the app is minimized.',
95
+ buttonPositive: 'Allow',
96
+ buttonNegative: 'Deny',
97
+ }
98
+ );
99
+ ```
100
+
101
+ > On iOS, permissions are handled via `Info.plist` and the system prompt on first access.
102
+
103
+ ### Location Tracking with `useDriverLocation`
104
+
105
+ The simplest way to track location using the built-in React hook:
106
+
107
+ ```tsx
108
+ import { useDriverLocation } from 'react-native-nitro-location-tracking';
109
+ import type { LocationConfig } from 'react-native-nitro-location-tracking';
110
+
111
+ const config: LocationConfig = {
112
+ desiredAccuracy: 'high', // 'high' | 'balanced' | 'low'
113
+ distanceFilter: 10, // meters
114
+ intervalMs: 3000, // Android only
115
+ fastestIntervalMs: 1000, // Android only
116
+ stopTimeout: 5, // minutes before declaring stopped
117
+ stopOnTerminate: false, // keep tracking after app close (Android)
118
+ startOnBoot: true, // restart tracking after reboot (Android)
119
+ foregroundNotificationTitle: 'Tracking Active',
120
+ foregroundNotificationText: 'Your location is being tracked',
121
+ };
122
+
123
+ function DriverScreen() {
124
+ const { location, isMoving, isTracking, startTracking, stopTracking } =
125
+ useDriverLocation(config);
126
+
127
+ return (
128
+ <View>
129
+ <Text>Tracking: {isTracking ? 'Yes' : 'No'}</Text>
130
+ <Text>Moving: {isMoving ? 'Yes' : 'No'}</Text>
131
+ {location && (
132
+ <Text>
133
+ {location.latitude.toFixed(6)}, {location.longitude.toFixed(6)}
134
+ {'\n'}Speed: {location.speed} m/s | Bearing: {location.bearing}°
135
+ </Text>
136
+ )}
137
+ <Button title="Start" onPress={startTracking} />
138
+ <Button title="Stop" onPress={stopTracking} />
139
+ </View>
140
+ );
141
+ }
142
+ ```
143
+
144
+ ### WebSocket Connection with `useRideConnection`
145
+
146
+ Manage a WebSocket connection for real-time location sync:
147
+
148
+ ```tsx
149
+ import { useRideConnection } from 'react-native-nitro-location-tracking';
150
+ import type { ConnectionConfig } from 'react-native-nitro-location-tracking';
151
+
152
+ const connectionConfig: ConnectionConfig = {
153
+ wsUrl: 'wss://api.example.com/ws/driver',
154
+ restUrl: 'https://api.example.com/api',
155
+ authToken: 'your-auth-token',
156
+ reconnectIntervalMs: 5000,
157
+ maxReconnectAttempts: 10,
158
+ batchSize: 5, // locations per batch upload
159
+ syncIntervalMs: 10000, // flush queue every 10s
160
+ };
161
+
162
+ function RideScreen() {
163
+ const { connectionState, lastMessage, connect, disconnect, send } =
164
+ useRideConnection(connectionConfig);
165
+
166
+ return (
167
+ <View>
168
+ <Text>Connection: {connectionState}</Text>
169
+ <Text>Last message: {lastMessage}</Text>
170
+ <Button title="Connect" onPress={connect} />
171
+ <Button title="Disconnect" onPress={disconnect} />
172
+ <Button title="Send Ping" onPress={() => send('ping')} />
173
+ </View>
174
+ );
175
+ }
176
+ ```
177
+
178
+ ### Direct Module Access
179
+
180
+ For advanced use cases, access the native module directly:
181
+
182
+ ```tsx
183
+ import NitroLocationModule from 'react-native-nitro-location-tracking';
184
+
185
+ // Configure and start tracking
186
+ NitroLocationModule.configure(config);
187
+ NitroLocationModule.onLocation((location) => {
188
+ console.log('New location:', location);
189
+ });
190
+ NitroLocationModule.onMotionChange((isMoving) => {
191
+ console.log('Motion changed:', isMoving);
192
+ });
193
+ NitroLocationModule.startTracking();
194
+
195
+ // Get current location (one-shot)
196
+ const current = await NitroLocationModule.getCurrentLocation();
197
+
198
+ // Check tracking state
199
+ const tracking = NitroLocationModule.isTracking();
200
+
201
+ // Stop tracking
202
+ NitroLocationModule.stopTracking();
203
+
204
+ // Force sync queued locations
205
+ const synced = await NitroLocationModule.forceSync();
206
+
207
+ // Notifications
208
+ NitroLocationModule.showLocalNotification('Ride Started', 'Heading to pickup');
209
+ NitroLocationModule.updateForegroundNotification('En Route', '2.5 km away');
210
+
211
+ // Cleanup
212
+ NitroLocationModule.destroy();
213
+ ```
214
+
215
+ ### Fake GPS Detection
216
+
217
+ Detect mock/fake GPS locations and optionally reject them:
218
+
219
+ ```tsx
220
+ import NitroLocationModule from 'react-native-nitro-location-tracking';
221
+
222
+ // Check if device-level mock location is enabled
223
+ const fakeGpsOn = NitroLocationModule.isFakeGpsEnabled();
224
+ if (fakeGpsOn) {
225
+ console.warn('Mock location provider is active!');
226
+ }
227
+
228
+ // Auto-reject mock locations (they won't fire onLocation callbacks)
229
+ NitroLocationModule.setRejectMockLocations(true);
230
+
231
+ // Each location update includes isMockLocation flag
232
+ NitroLocationModule.onLocation((location) => {
233
+ if (location.isMockLocation) {
234
+ console.warn('This location is from a mock provider');
235
+ }
236
+ });
237
+ ```
238
+
239
+ **Platform behavior:**
240
+
241
+ | Platform | Per-location detection | Device-level detection |
242
+ |----------|----------------------|------------------------|
243
+ | Android | `Location.isMock` (API 31+) / `isFromMockProvider` (API 18+) | `AppOpsManager` mock location check |
244
+ | iOS | `CLLocation.sourceInformation.isSimulatedBySoftware` (iOS 15+) | Simulator detection |
245
+
246
+ ### Smooth Map Marker Animation
247
+
248
+ Use `LocationSmoother` to animate a map marker smoothly between location updates:
249
+
250
+ ```tsx
251
+ import { useRef, useCallback } from 'react';
252
+ import { Marker } from 'react-native-maps';
253
+ import {
254
+ useDriverLocation,
255
+ LocationSmoother,
256
+ } from 'react-native-nitro-location-tracking';
257
+
258
+ function MapScreen() {
259
+ const markerRef = useRef(null);
260
+ const smootherRef = useRef(new LocationSmoother(markerRef));
261
+
262
+ const { location, startTracking } = useDriverLocation(config);
263
+
264
+ // Feed each new location into the smoother
265
+ const onLocation = useCallback((loc) => {
266
+ smootherRef.current.feed(loc);
267
+ }, []);
268
+
269
+ return (
270
+ <MapView>
271
+ {location && (
272
+ <Marker
273
+ ref={markerRef}
274
+ coordinate={{
275
+ latitude: location.latitude,
276
+ longitude: location.longitude,
277
+ }}
278
+ />
279
+ )}
280
+ </MapView>
281
+ );
282
+ }
283
+ ```
284
+
285
+ ### Bearing Utilities
286
+
287
+ Calculate bearing between two coordinates and handle rotation smoothing:
288
+
289
+ ```tsx
290
+ import { calculateBearing, shortestRotation } from 'react-native-nitro-location-tracking';
291
+
292
+ // Calculate bearing from point A to point B (in degrees, 0-360)
293
+ const bearing = calculateBearing(
294
+ { latitude: 41.311, longitude: 69.279 },
295
+ { latitude: 41.315, longitude: 69.285 }
296
+ );
297
+
298
+ // Smooth rotation to avoid spinning the long way around
299
+ const currentRotation = 350;
300
+ const targetRotation = 10;
301
+ const smoothed = shortestRotation(currentRotation, targetRotation); // 370 (not -350)
302
+ ```
303
+
304
+ ### Geofencing
305
+
306
+ Monitor enter/exit events for circular regions:
307
+
308
+ ```tsx
309
+ import NitroLocationModule from 'react-native-nitro-location-tracking';
310
+ import type { GeofenceRegion } from 'react-native-nitro-location-tracking';
311
+
312
+ // Listen for geofence events
313
+ NitroLocationModule.onGeofenceEvent((event, regionId) => {
314
+ console.log(`Geofence ${event} for region: ${regionId}`);
315
+ });
316
+
317
+ // Add a geofence around a pickup point
318
+ NitroLocationModule.addGeofence({
319
+ id: 'pickup-zone',
320
+ latitude: 41.311,
321
+ longitude: 69.279,
322
+ radius: 100, // meters
323
+ notifyOnEntry: true,
324
+ notifyOnExit: true,
325
+ });
326
+
327
+ // Remove a specific geofence
328
+ NitroLocationModule.removeGeofence('pickup-zone');
329
+
330
+ // Remove all geofences
331
+ NitroLocationModule.removeAllGeofences();
332
+ ```
333
+
334
+ > **Note:** iOS limits geofence regions to 20 per app. Android supports up to 100.
335
+
336
+ ### Speed Monitoring
337
+
338
+ Get alerts when speed crosses configurable thresholds:
339
+
340
+ ```tsx
341
+ import NitroLocationModule from 'react-native-nitro-location-tracking';
342
+
343
+ // Configure speed thresholds
344
+ NitroLocationModule.configureSpeedMonitor({
345
+ maxSpeedKmh: 120, // alert when exceeding 120 km/h
346
+ minSpeedKmh: 5, // alert when below 5 km/h (idle detection)
347
+ checkIntervalMs: 0, // check on every location update
348
+ });
349
+
350
+ // Listen for speed state transitions
351
+ NitroLocationModule.onSpeedAlert((alert, speedKmh) => {
352
+ if (alert === 'exceeded') {
353
+ console.warn(`Speed limit exceeded: ${speedKmh.toFixed(1)} km/h`);
354
+ } else if (alert === 'below_minimum') {
355
+ console.log('Driver appears idle');
356
+ } else {
357
+ console.log('Speed normalized');
358
+ }
359
+ });
360
+
361
+ // Get current speed anytime
362
+ const speed = NitroLocationModule.getCurrentSpeed(); // km/h
363
+ ```
364
+
365
+ ### Distance Calculator
366
+
367
+ Track running trip statistics:
368
+
369
+ ```tsx
370
+ import NitroLocationModule from 'react-native-nitro-location-tracking';
17
371
 
18
- ```js
19
- import { multiply } from 'react-native-nitro-location-tracking';
372
+ // Start recording a trip
373
+ NitroLocationModule.startTripCalculation();
20
374
 
21
- // ...
375
+ // Check stats during the trip
376
+ const stats = NitroLocationModule.getTripStats();
377
+ console.log(`Distance: ${(stats.distanceMeters / 1000).toFixed(2)} km`);
378
+ console.log(`Duration: ${(stats.durationMs / 60000).toFixed(1)} min`);
379
+ console.log(`Avg speed: ${stats.averageSpeedKmh.toFixed(1)} km/h`);
380
+ console.log(`Max speed: ${stats.maxSpeedKmh.toFixed(1)} km/h`);
381
+ console.log(`Points: ${stats.pointCount}`);
22
382
 
23
- const result = multiply(3, 7);
383
+ // Stop and get final stats
384
+ const finalStats = NitroLocationModule.stopTripCalculation();
385
+
386
+ // Reset for a new trip
387
+ NitroLocationModule.resetTripCalculation();
388
+ ```
389
+
390
+ ### Location Provider Status
391
+
392
+ Detect when GPS/location services are turned on or off:
393
+
394
+ ```tsx
395
+ import NitroLocationModule from 'react-native-nitro-location-tracking';
396
+
397
+ // Check current status
398
+ const enabled = NitroLocationModule.isLocationServicesEnabled();
399
+
400
+ // Listen for changes
401
+ NitroLocationModule.onProviderStatusChange((gps, network) => {
402
+ console.log(`GPS: ${gps}, Network: ${network}`);
403
+ if (gps === 'disabled') {
404
+ console.warn('Please enable location services!');
405
+ }
406
+ });
407
+ ```
408
+
409
+ ### Permission Status
410
+
411
+ Check the current location permission status without prompting the user:
412
+
413
+ ```tsx
414
+ import NitroLocationModule from 'react-native-nitro-location-tracking';
415
+
416
+ const status = NitroLocationModule.getLocationPermissionStatus();
417
+
418
+ switch (status) {
419
+ case 'always':
420
+ console.log('Background location granted');
421
+ break;
422
+ case 'whenInUse':
423
+ console.log('Foreground only — background tracking may not work');
424
+ break;
425
+ case 'denied':
426
+ console.warn('Location permission denied');
427
+ break;
428
+ case 'restricted':
429
+ console.warn('Location restricted by parental controls or MDM');
430
+ break;
431
+ case 'notDetermined':
432
+ console.log('Permission not yet requested');
433
+ break;
434
+ }
435
+ ```
436
+
437
+ | Status | iOS | Android |
438
+ |--------|-----|----------|
439
+ | `notDetermined` | Not yet asked | N/A (returns `denied`) |
440
+ | `denied` | User denied | Fine location not granted |
441
+ | `restricted` | Parental/MDM restriction | N/A (returns `denied`) |
442
+ | `whenInUse` | Authorized when in use | Fine granted, background not |
443
+ | `always` | Authorized always | Fine + background granted |
444
+
445
+ ## API Reference
446
+
447
+ ### Types
448
+
449
+ #### `LocationData`
450
+
451
+ ```ts
452
+ interface LocationData {
453
+ latitude: number;
454
+ longitude: number;
455
+ altitude: number;
456
+ speed: number; // m/s
457
+ bearing: number; // degrees
458
+ accuracy: number; // meters
459
+ timestamp: number; // unix ms
460
+ isMockLocation?: boolean; // true when from a mock provider
461
+ }
462
+ ```
463
+
464
+ #### `LocationConfig`
465
+
466
+ ```ts
467
+ interface LocationConfig {
468
+ desiredAccuracy: 'high' | 'balanced' | 'low';
469
+ distanceFilter: number; // meters
470
+ intervalMs: number; // Android only
471
+ fastestIntervalMs: number; // Android only
472
+ stopTimeout: number; // minutes before declaring stopped
473
+ stopOnTerminate: boolean; // keep tracking after app close (Android)
474
+ startOnBoot: boolean; // restart tracking after reboot (Android)
475
+ foregroundNotificationTitle: string;
476
+ foregroundNotificationText: string;
477
+ }
478
+ ```
479
+
480
+ #### `ConnectionConfig`
481
+
482
+ ```ts
483
+ interface ConnectionConfig {
484
+ wsUrl: string;
485
+ restUrl: string;
486
+ authToken: string;
487
+ reconnectIntervalMs: number;
488
+ maxReconnectAttempts: number;
489
+ batchSize: number; // locations per batch upload
490
+ syncIntervalMs: number; // how often to flush queue
491
+ }
492
+ ```
493
+
494
+ #### `GeofenceRegion`
495
+
496
+ ```ts
497
+ interface GeofenceRegion {
498
+ id: string;
499
+ latitude: number;
500
+ longitude: number;
501
+ radius: number; // meters
502
+ notifyOnEntry: boolean;
503
+ notifyOnExit: boolean;
504
+ }
505
+ ```
506
+
507
+ #### `SpeedConfig`
508
+
509
+ ```ts
510
+ interface SpeedConfig {
511
+ maxSpeedKmh: number; // speed limit in km/h
512
+ minSpeedKmh: number; // minimum speed threshold
513
+ checkIntervalMs: number; // how often to evaluate
514
+ }
515
+ ```
516
+
517
+ #### `TripStats`
518
+
519
+ ```ts
520
+ interface TripStats {
521
+ distanceMeters: number;
522
+ durationMs: number;
523
+ averageSpeedKmh: number;
524
+ maxSpeedKmh: number;
525
+ pointCount: number;
526
+ }
527
+ ```
528
+
529
+ #### `PermissionStatus`
530
+
531
+ ```ts
532
+ type PermissionStatus =
533
+ | 'notDetermined'
534
+ | 'denied'
535
+ | 'restricted'
536
+ | 'whenInUse'
537
+ | 'always';
538
+ ```
539
+
540
+ ### Hooks
541
+
542
+ | Hook | Returns | Description |
543
+ |------|---------|-------------|
544
+ | `useDriverLocation(config)` | `{ location, isMoving, isTracking, startTracking, stopTracking }` | Manages location tracking lifecycle |
545
+ | `useRideConnection(config)` | `{ connectionState, lastMessage, connect, disconnect, send }` | Manages WebSocket connection lifecycle |
546
+
547
+ ### Native Module Methods
548
+
549
+ | Method | Returns | Description |
550
+ |--------|---------|-------------|
551
+ | `configure(config)` | `void` | Set location tracking configuration |
552
+ | `startTracking()` | `void` | Start location tracking |
553
+ | `stopTracking()` | `void` | Stop location tracking |
554
+ | `getCurrentLocation()` | `Promise<LocationData>` | Get a one-shot location |
555
+ | `isTracking()` | `boolean` | Check if tracking is active |
556
+ | `onLocation(callback)` | `void` | Register location update callback |
557
+ | `onMotionChange(callback)` | `void` | Register motion state callback |
558
+ | `configureConnection(config)` | `void` | Set WebSocket/REST configuration |
559
+ | `connectWebSocket()` | `void` | Open WebSocket connection |
560
+ | `disconnectWebSocket()` | `void` | Close WebSocket connection |
561
+ | `sendMessage(message)` | `void` | Send a message via WebSocket |
562
+ | `getConnectionState()` | `ConnectionState` | Get current connection state |
563
+ | `onConnectionStateChange(callback)` | `void` | Register connection state callback |
564
+ | `onMessage(callback)` | `void` | Register incoming message callback |
565
+ | `forceSync()` | `Promise<boolean>` | Flush queued locations to server |
566
+ | `isFakeGpsEnabled()` | `boolean` | Check if device-level mock location is enabled |
567
+ | `setRejectMockLocations(reject)` | `void` | Auto-reject mock locations when `true` |
568
+ | `addGeofence(region)` | `void` | Start monitoring a circular geofence region |
569
+ | `removeGeofence(regionId)` | `void` | Stop monitoring a specific geofence |
570
+ | `removeAllGeofences()` | `void` | Remove all active geofences |
571
+ | `onGeofenceEvent(callback)` | `void` | Register geofence enter/exit callback |
572
+ | `configureSpeedMonitor(config)` | `void` | Set speed monitoring thresholds |
573
+ | `onSpeedAlert(callback)` | `void` | Register speed state-transition callback |
574
+ | `getCurrentSpeed()` | `number` | Get current speed in km/h |
575
+ | `startTripCalculation()` | `void` | Start recording trip distance/stats |
576
+ | `stopTripCalculation()` | `TripStats` | Stop recording and get final stats |
577
+ | `getTripStats()` | `TripStats` | Get current trip stats without stopping |
578
+ | `resetTripCalculation()` | `void` | Reset trip calculator |
579
+ | `isLocationServicesEnabled()` | `boolean` | Check if GPS/location is enabled on device |
580
+ | `onProviderStatusChange(callback)` | `void` | Register GPS/network provider status callback |
581
+ | `getLocationPermissionStatus()` | `PermissionStatus` | Check current location permission without prompting |
582
+ | `showLocalNotification(title, body)` | `void` | Show a local notification |
583
+ | `updateForegroundNotification(title, body)` | `void` | Update the foreground service notification |
584
+ | `destroy()` | `void` | Stop tracking and disconnect |
585
+
586
+ ### Utility Exports
587
+
588
+ | Export | Description |
589
+ |--------|-------------|
590
+ | `LocationSmoother` | Class for smooth map marker animation between updates |
591
+ | `calculateBearing(from, to)` | Calculate bearing between two coordinates (degrees, 0-360) |
592
+ | `shortestRotation(from, to)` | Calculate shortest rotation path to avoid spinning |
593
+ | `requestLocationPermission()` | Request location + notification permissions (Android) |
594
+
595
+ ## Publishing to npm
596
+
597
+ ### Prerequisites
598
+
599
+ 1. Create an npm account at [npmjs.com](https://www.npmjs.com/signup)
600
+ 2. Log in from terminal:
601
+
602
+ ```sh
603
+ npm login
604
+ ```
605
+
606
+ ### First-time Publish
607
+
608
+ 1. Make sure the build is up to date:
609
+
610
+ ```sh
611
+ yarn prepare
612
+ ```
613
+
614
+ 1. Do a dry run to verify what will be published:
615
+
616
+ ```sh
617
+ npm pack --dry-run
618
+ ```
619
+
620
+ 1. Publish:
621
+
622
+ ```sh
623
+ npm publish
624
+ ```
625
+
626
+ > The package is configured with `"publishConfig": { "registry": "https://registry.npmjs.org/" }` so it will publish to the public npm registry.
627
+
628
+ ### Release with Versioning (Recommended)
629
+
630
+ This project uses [release-it](https://github.com/release-it/release-it) with conventional changelog. To create a proper release:
631
+
632
+ ```sh
633
+ yarn release
24
634
  ```
25
635
 
636
+ This will:
637
+
638
+ - Bump the version based on your commits
639
+ - Generate a changelog
640
+ - Create a git commit and tag
641
+ - Publish to npm
642
+ - Create a GitHub release
643
+
644
+ For a specific version bump:
645
+
646
+ ```sh
647
+ # Patch release (0.1.5 -> 0.1.6)
648
+ npx release-it patch
649
+
650
+ # Minor release (0.1.5 -> 0.2.0)
651
+ npx release-it minor
652
+
653
+ # Major release (0.1.5 -> 1.0.0)
654
+ npx release-it major
655
+ ```
656
+
657
+ ### Manual Version Bump + Publish
658
+
659
+ ```sh
660
+ # 1. Bump version
661
+ npm version patch # or minor / major
662
+
663
+ # 2. Build
664
+ yarn prepare
665
+
666
+ # 3. Publish
667
+ npm publish
668
+
669
+ # 4. Push tags
670
+ git push --follow-tags
671
+ ```
672
+
673
+ ### What Gets Published
674
+
675
+ The `files` field in `package.json` controls what is included in the npm package:
676
+
677
+ - `src/` - TypeScript source
678
+ - `lib/` - Compiled JS + type declarations
679
+ - `android/` - Android native code (excluding build artifacts)
680
+ - `ios/` - iOS native code (excluding build folder)
681
+ - `cpp/` - C++ shared code
682
+ - `nitrogen/` - Nitro generated code
683
+ - `nitro.json` - Nitro module configuration
684
+ - `*.podspec` - CocoaPods spec
685
+ - `react-native.config.js` - React Native CLI config
686
+
687
+ ### Unpublishing / Deprecating
688
+
689
+ ```sh
690
+ # Deprecate a version (users see a warning on install)
691
+ npm deprecate react-native-nitro-location-tracking@"< 0.2.0" "please upgrade to 0.2.0+"
692
+
693
+ # Unpublish a specific version (within 72 hours only)
694
+ npm unpublish react-native-nitro-location-tracking@0.1.0
695
+ ```
26
696
 
27
697
  ## Contributing
28
698