react-native-nitro-location-tracking 0.1.0 → 0.1.2

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.
@@ -26,13 +26,15 @@ class LocationForegroundService : Service() {
26
26
  (getSystemService(NOTIFICATION_SERVICE) as NotificationManager)
27
27
  .createNotificationChannel(channel)
28
28
  }
29
+ // Must call startForeground immediately to avoid ForegroundServiceDidNotStartInTimeException
30
+ startForeground(NOTIFICATION_ID, buildNotification("Location Active", "Tracking your location"))
29
31
  }
30
32
 
31
33
  override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
32
34
  val title = intent?.getStringExtra("title") ?: "Location Active"
33
35
  val text = intent?.getStringExtra("text") ?: "Tracking your location"
34
36
  when (intent?.action) {
35
- ACTION_START -> startForeground(
37
+ ACTION_START, null -> startForeground(
36
38
  NOTIFICATION_ID, buildNotification(title, text))
37
39
  ACTION_STOP -> {
38
40
  stopForeground(STOP_FOREGROUND_REMOVE)
@@ -48,9 +48,26 @@ class LocationEngine: NSObject, CLLocationManagerDelegate {
48
48
  var isTracking: Bool { tracking }
49
49
 
50
50
  func getCurrentLocation(completion: @escaping (LocationData?) -> Void) {
51
- locationManager.requestLocation()
52
- // Store completion for delegate callback
51
+ // If we already have a recent location, return it immediately
52
+ if let lastLocation = locationManager.location {
53
+ let age: TimeInterval = Swift.abs(lastLocation.timestamp.timeIntervalSinceNow)
54
+ if age < 10.0 {
55
+ let data = LocationData(
56
+ latitude: lastLocation.coordinate.latitude,
57
+ longitude: lastLocation.coordinate.longitude,
58
+ altitude: lastLocation.altitude,
59
+ speed: max(lastLocation.speed, 0),
60
+ bearing: max(lastLocation.course, 0),
61
+ accuracy: lastLocation.horizontalAccuracy,
62
+ timestamp: lastLocation.timestamp.timeIntervalSince1970 * 1000
63
+ )
64
+ completion(data)
65
+ return
66
+ }
67
+ }
68
+ // Otherwise request a fresh location
53
69
  pendingLocationCompletion = completion
70
+ locationManager.requestLocation()
54
71
  }
55
72
 
56
73
  private var pendingLocationCompletion: ((LocationData?) -> Void)?
@@ -97,50 +114,3 @@ class LocationEngine: NSObject, CLLocationManagerDelegate {
97
114
  }
98
115
  }
99
116
 
100
- //// Simple struct to pass location data between components
101
- //struct LocationData {
102
- // let latitude: Double
103
- // let longitude: Double
104
- // let altitude: Double
105
- // let speed: Double
106
- // let bearing: Double
107
- // let accuracy: Double
108
- // let timestamp: Double
109
- //
110
- // var id: String = ""
111
- //
112
- // func toJSON() -> [String: Any] {
113
- // return [
114
- // "latitude": latitude,
115
- // "longitude": longitude,
116
- // "altitude": altitude,
117
- // "speed": speed,
118
- // "bearing": bearing,
119
- // "accuracy": accuracy,
120
- // "timestamp": timestamp
121
- // ]
122
- // }
123
- //}
124
- //
125
- //// Config structs
126
- //struct LocationConfig {
127
- // let desiredAccuracy: String
128
- // let distanceFilter: Double
129
- // let intervalMs: Double
130
- // let fastestIntervalMs: Double
131
- // let stopTimeout: Double
132
- // let stopOnTerminate: Bool
133
- // let startOnBoot: Bool
134
- // let foregroundNotificationTitle: String
135
- // let foregroundNotificationText: String
136
- //}
137
- //
138
- //struct ConnectionConfig {
139
- // let wsUrl: String
140
- // let restUrl: String
141
- // let authToken: String
142
- // let reconnectIntervalMs: Double
143
- // let maxReconnectAttempts: Double
144
- // let batchSize: Double
145
- // let syncIntervalMs: Double
146
- //}
@@ -4,10 +4,18 @@ const sleep = ms => new Promise(r => setTimeout(r, ms));
4
4
  export class LocationSmoother {
5
5
  queue = [];
6
6
  isAnimating = false;
7
+ hasInitialCoordinate = false;
7
8
  constructor(markerRef) {
8
9
  this.markerRef = markerRef;
9
10
  }
10
11
  feed(location) {
12
+ // Skip the first location — let the Marker mount with it as its
13
+ // initial `coordinate` prop. Calling animateMarkerToCoordinate before
14
+ // the native marker has a position causes a NullPointerException.
15
+ if (!this.hasInitialCoordinate) {
16
+ this.hasInitialCoordinate = true;
17
+ return;
18
+ }
11
19
  this.queue.push(location);
12
20
  if (this.queue.length > 10) this.queue = this.queue.slice(-3);
13
21
  if (!this.isAnimating) this.drain();
@@ -28,6 +36,7 @@ export class LocationSmoother {
28
36
  clear() {
29
37
  this.queue = [];
30
38
  this.isAnimating = false;
39
+ this.hasInitialCoordinate = false;
31
40
  }
32
41
  }
33
42
  //# sourceMappingURL=LocationSmoother.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["sleep","ms","Promise","r","setTimeout","LocationSmoother","queue","isAnimating","constructor","markerRef","feed","location","push","length","slice","drain","next","shift","dur","current","animateMarkerToCoordinate","latitude","longitude","clear"],"sourceRoot":"../../src","sources":["LocationSmoother.ts"],"mappings":";;AAEA,MAAMA,KAAK,GAAIC,EAAU,IAAK,IAAIC,OAAO,CAAQC,CAAC,IAAKC,UAAU,CAACD,CAAC,EAAEF,EAAE,CAAC,CAAC;AASzE,OAAO,MAAMI,gBAAgB,CAAC;EACpBC,KAAK,GAAmB,EAAE;EAC1BC,WAAW,GAAG,KAAK;EAG3BC,WAAWA,CAACC,SAAwC,EAAE;IACpD,IAAI,CAACA,SAAS,GAAGA,SAAS;EAC5B;EAEAC,IAAIA,CAACC,QAAsB,EAAE;IAC3B,IAAI,CAACL,KAAK,CAACM,IAAI,CAACD,QAAQ,CAAC;IACzB,IAAI,IAAI,CAACL,KAAK,CAACO,MAAM,GAAG,EAAE,EAAE,IAAI,CAACP,KAAK,GAAG,IAAI,CAACA,KAAK,CAACQ,KAAK,CAAC,CAAC,CAAC,CAAC;IAC7D,IAAI,CAAC,IAAI,CAACP,WAAW,EAAE,IAAI,CAACQ,KAAK,CAAC,CAAC;EACrC;EAEA,MAAcA,KAAKA,CAAA,EAAG;IACpB,IAAI,CAACR,WAAW,GAAG,IAAI;IACvB,OAAO,IAAI,CAACD,KAAK,CAACO,MAAM,GAAG,CAAC,EAAE;MAC5B,MAAMG,IAAI,GAAG,IAAI,CAACV,KAAK,CAACW,KAAK,CAAC,CAAE;MAChC,MAAMC,GAAG,GACP,IAAI,CAACZ,KAAK,CAACO,MAAM,GAAG,CAAC,GAAG,GAAG,GAAG,IAAI,CAACP,KAAK,CAACO,MAAM,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI;MACnE,IAAI,CAACJ,SAAS,CAACU,OAAO,EAAEC,yBAAyB,CAC/C;QAAEC,QAAQ,EAAEL,IAAI,CAACK,QAAQ;QAAEC,SAAS,EAAEN,IAAI,CAACM;MAAU,CAAC,EACtDJ,GACF,CAAC;MACD,MAAMlB,KAAK,CAACkB,GAAG,GAAG,GAAG,CAAC;IACxB;IACA,IAAI,CAACX,WAAW,GAAG,KAAK;EAC1B;EAEAgB,KAAKA,CAAA,EAAG;IACN,IAAI,CAACjB,KAAK,GAAG,EAAE;IACf,IAAI,CAACC,WAAW,GAAG,KAAK;EAC1B;AACF","ignoreList":[]}
1
+ {"version":3,"names":["sleep","ms","Promise","r","setTimeout","LocationSmoother","queue","isAnimating","hasInitialCoordinate","constructor","markerRef","feed","location","push","length","slice","drain","next","shift","dur","current","animateMarkerToCoordinate","latitude","longitude","clear"],"sourceRoot":"../../src","sources":["LocationSmoother.ts"],"mappings":";;AAEA,MAAMA,KAAK,GAAIC,EAAU,IAAK,IAAIC,OAAO,CAAQC,CAAC,IAAKC,UAAU,CAACD,CAAC,EAAEF,EAAE,CAAC,CAAC;AASzE,OAAO,MAAMI,gBAAgB,CAAC;EACpBC,KAAK,GAAmB,EAAE;EAC1BC,WAAW,GAAG,KAAK;EAEnBC,oBAAoB,GAAG,KAAK;EAEpCC,WAAWA,CAACC,SAAwC,EAAE;IACpD,IAAI,CAACA,SAAS,GAAGA,SAAS;EAC5B;EAEAC,IAAIA,CAACC,QAAsB,EAAE;IAC3B;IACA;IACA;IACA,IAAI,CAAC,IAAI,CAACJ,oBAAoB,EAAE;MAC9B,IAAI,CAACA,oBAAoB,GAAG,IAAI;MAChC;IACF;IAEA,IAAI,CAACF,KAAK,CAACO,IAAI,CAACD,QAAQ,CAAC;IACzB,IAAI,IAAI,CAACN,KAAK,CAACQ,MAAM,GAAG,EAAE,EAAE,IAAI,CAACR,KAAK,GAAG,IAAI,CAACA,KAAK,CAACS,KAAK,CAAC,CAAC,CAAC,CAAC;IAC7D,IAAI,CAAC,IAAI,CAACR,WAAW,EAAE,IAAI,CAACS,KAAK,CAAC,CAAC;EACrC;EAEA,MAAcA,KAAKA,CAAA,EAAG;IACpB,IAAI,CAACT,WAAW,GAAG,IAAI;IACvB,OAAO,IAAI,CAACD,KAAK,CAACQ,MAAM,GAAG,CAAC,EAAE;MAC5B,MAAMG,IAAI,GAAG,IAAI,CAACX,KAAK,CAACY,KAAK,CAAC,CAAE;MAChC,MAAMC,GAAG,GACP,IAAI,CAACb,KAAK,CAACQ,MAAM,GAAG,CAAC,GAAG,GAAG,GAAG,IAAI,CAACR,KAAK,CAACQ,MAAM,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI;MACnE,IAAI,CAACJ,SAAS,CAACU,OAAO,EAAEC,yBAAyB,CAC/C;QAAEC,QAAQ,EAAEL,IAAI,CAACK,QAAQ;QAAEC,SAAS,EAAEN,IAAI,CAACM;MAAU,CAAC,EACtDJ,GACF,CAAC;MACD,MAAMnB,KAAK,CAACmB,GAAG,GAAG,GAAG,CAAC;IACxB;IACA,IAAI,CAACZ,WAAW,GAAG,KAAK;EAC1B;EAEAiB,KAAKA,CAAA,EAAG;IACN,IAAI,CAAClB,KAAK,GAAG,EAAE;IACf,IAAI,CAACC,WAAW,GAAG,KAAK;IACxB,IAAI,CAACC,oBAAoB,GAAG,KAAK;EACnC;AACF","ignoreList":[]}
@@ -4,6 +4,7 @@ import { useState, useEffect, useCallback, useRef } from 'react';
4
4
  import { NitroModules } from 'react-native-nitro-modules';
5
5
  const NitroLocationModule = NitroModules.createHybridObject('NitroLocationTracking');
6
6
  export default NitroLocationModule;
7
+ export { requestLocationPermission } from "./requestPermission.js";
7
8
  export { LocationSmoother } from "./LocationSmoother.js";
8
9
  export { shortestRotation, calculateBearing } from "./bearing.js";
9
10
  // export * from './db'
@@ -1 +1 @@
1
- {"version":3,"names":["useState","useEffect","useCallback","useRef","NitroModules","NitroLocationModule","createHybridObject","LocationSmoother","shortestRotation","calculateBearing","useDriverLocation","config","location","setLocation","isMoving","setIsMoving","isTracking","setIsTracking","configJson","JSON","stringify","trackingRef","parsed","parse","configure","onLocation","onMotionChange","current","stopTracking","startTracking","useRideConnection","connectionState","setConnectionState","lastMessage","setLastMessage","configureConnection","onConnectionStateChange","onMessage","disconnectWebSocket","connect","connectWebSocket","disconnect","send","m","sendMessage"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["useState","useEffect","useCallback","useRef","NitroModules","NitroLocationModule","createHybridObject","requestLocationPermission","LocationSmoother","shortestRotation","calculateBearing","useDriverLocation","config","location","setLocation","isMoving","setIsMoving","isTracking","setIsTracking","configJson","JSON","stringify","trackingRef","parsed","parse","configure","onLocation","onMotionChange","current","stopTracking","startTracking","useRideConnection","connectionState","setConnectionState","lastMessage","setLastMessage","configureConnection","onConnectionStateChange","onMessage","disconnectWebSocket","connect","connectWebSocket","disconnect","send","m","sendMessage"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+
3
+ import { PermissionsAndroid, Platform } from 'react-native';
4
+ export async function requestLocationPermission(locationPermissionMessage, locationPermissionBackground) {
5
+ if (Platform.OS === 'ios') {
6
+ // iOS permissions are handled via Info.plist + system prompt on first access
7
+ return true;
8
+ }
9
+ const fineGranted = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION, {
10
+ title: locationPermissionMessage?.title ?? 'Location Permission',
11
+ message: locationPermissionMessage?.message ?? 'This app needs access to your location to track your ride.',
12
+ buttonPositive: locationPermissionMessage?.buttonPositive ?? 'Allow',
13
+ buttonNegative: locationPermissionMessage?.buttonNegative ?? 'Deny'
14
+ });
15
+ if (fineGranted !== PermissionsAndroid.RESULTS.GRANTED) {
16
+ return false;
17
+ }
18
+
19
+ // Request background location (required for Android 10+)
20
+ if (Number(Platform.Version) >= 29) {
21
+ const bgGranted = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.ACCESS_BACKGROUND_LOCATION, {
22
+ title: locationPermissionBackground?.title ?? 'Background Location Permission',
23
+ message: locationPermissionBackground?.message ?? 'This app needs background location access to continue tracking while the app is minimized.',
24
+ buttonPositive: locationPermissionBackground?.buttonPositive ?? 'Allow',
25
+ buttonNegative: locationPermissionBackground?.buttonNegative ?? 'Deny'
26
+ });
27
+ if (bgGranted !== PermissionsAndroid.RESULTS.GRANTED) {
28
+ return false;
29
+ }
30
+ }
31
+
32
+ // Request notification permission (required for Android 13+)
33
+ if (Number(Platform.Version) >= 33) {
34
+ await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS);
35
+ }
36
+ return true;
37
+ }
38
+ //# sourceMappingURL=requestPermission.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["PermissionsAndroid","Platform","requestLocationPermission","locationPermissionMessage","locationPermissionBackground","OS","fineGranted","request","PERMISSIONS","ACCESS_FINE_LOCATION","title","message","buttonPositive","buttonNegative","RESULTS","GRANTED","Number","Version","bgGranted","ACCESS_BACKGROUND_LOCATION","POST_NOTIFICATIONS"],"sourceRoot":"../../src","sources":["requestPermission.ts"],"mappings":";;AAAA,SAASA,kBAAkB,EAAEC,QAAQ,QAAQ,cAAc;AAE3D,OAAO,eAAeC,yBAAyBA,CAC7CC,yBAOC,EACDC,4BAOC,EACiB;EAClB,IAAIH,QAAQ,CAACI,EAAE,KAAK,KAAK,EAAE;IACzB;IACA,OAAO,IAAI;EACb;EAEA,MAAMC,WAAW,GAAG,MAAMN,kBAAkB,CAACO,OAAO,CAClDP,kBAAkB,CAACQ,WAAW,CAACC,oBAAoB,EACnD;IACEC,KAAK,EAAEP,yBAAyB,EAAEO,KAAK,IAAI,qBAAqB;IAChEC,OAAO,EACLR,yBAAyB,EAAEQ,OAAO,IAClC,4DAA4D;IAC9DC,cAAc,EAAET,yBAAyB,EAAES,cAAc,IAAI,OAAO;IACpEC,cAAc,EAAEV,yBAAyB,EAAEU,cAAc,IAAI;EAC/D,CACF,CAAC;EAED,IAAIP,WAAW,KAAKN,kBAAkB,CAACc,OAAO,CAACC,OAAO,EAAE;IACtD,OAAO,KAAK;EACd;;EAEA;EACA,IAAIC,MAAM,CAACf,QAAQ,CAACgB,OAAO,CAAC,IAAI,EAAE,EAAE;IAClC,MAAMC,SAAS,GAAG,MAAMlB,kBAAkB,CAACO,OAAO,CAChDP,kBAAkB,CAACQ,WAAW,CAACW,0BAA0B,EACzD;MACET,KAAK,EACHN,4BAA4B,EAAEM,KAAK,IACnC,gCAAgC;MAClCC,OAAO,EACLP,4BAA4B,EAAEO,OAAO,IACrC,4FAA4F;MAC9FC,cAAc,EAAER,4BAA4B,EAAEQ,cAAc,IAAI,OAAO;MACvEC,cAAc,EAAET,4BAA4B,EAAES,cAAc,IAAI;IAClE,CACF,CAAC;IAED,IAAIK,SAAS,KAAKlB,kBAAkB,CAACc,OAAO,CAACC,OAAO,EAAE;MACpD,OAAO,KAAK;IACd;EACF;;EAEA;EACA,IAAIC,MAAM,CAACf,QAAQ,CAACgB,OAAO,CAAC,IAAI,EAAE,EAAE;IAClC,MAAMjB,kBAAkB,CAACO,OAAO,CAC9BP,kBAAkB,CAACQ,WAAW,CAACY,kBACjC,CAAC;EACH;EAEA,OAAO,IAAI;AACb","ignoreList":[]}
@@ -9,6 +9,7 @@ export declare class LocationSmoother {
9
9
  private queue;
10
10
  private isAnimating;
11
11
  private markerRef;
12
+ private hasInitialCoordinate;
12
13
  constructor(markerRef: {
13
14
  current: MarkerRef | null;
14
15
  });
@@ -1 +1 @@
1
- {"version":3,"file":"LocationSmoother.d.ts","sourceRoot":"","sources":["../../../src/LocationSmoother.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAIlE,MAAM,WAAW,SAAS;IACxB,yBAAyB,CACvB,UAAU,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,EACnD,QAAQ,EAAE,MAAM,GACf,IAAI,CAAC;CACT;AAED,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,KAAK,CAAsB;IACnC,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,SAAS,CAAgC;gBAErC,SAAS,EAAE;QAAE,OAAO,EAAE,SAAS,GAAG,IAAI,CAAA;KAAE;IAIpD,IAAI,CAAC,QAAQ,EAAE,YAAY;YAMb,KAAK;IAenB,KAAK;CAIN"}
1
+ {"version":3,"file":"LocationSmoother.d.ts","sourceRoot":"","sources":["../../../src/LocationSmoother.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAIlE,MAAM,WAAW,SAAS;IACxB,yBAAyB,CACvB,UAAU,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,EACnD,QAAQ,EAAE,MAAM,GACf,IAAI,CAAC;CACT;AAED,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,KAAK,CAAsB;IACnC,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,SAAS,CAAgC;IACjD,OAAO,CAAC,oBAAoB,CAAS;gBAEzB,SAAS,EAAE;QAAE,OAAO,EAAE,SAAS,GAAG,IAAI,CAAA;KAAE;IAIpD,IAAI,CAAC,QAAQ,EAAE,YAAY;YAcb,KAAK;IAenB,KAAK;CAKN"}
@@ -1,6 +1,7 @@
1
1
  import type { NitroLocationTracking, LocationData, LocationConfig, ConnectionConfig } from './NitroLocationTracking.nitro';
2
2
  declare const NitroLocationModule: NitroLocationTracking;
3
3
  export default NitroLocationModule;
4
+ export { requestLocationPermission } from './requestPermission';
4
5
  export { LocationSmoother } from './LocationSmoother';
5
6
  export { shortestRotation, calculateBearing } from './bearing';
6
7
  export type { NitroLocationTracking, LocationData, LocationConfig, ConnectionConfig, } from './NitroLocationTracking.nitro';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,qBAAqB,EACrB,YAAY,EACZ,cAAc,EACd,gBAAgB,EACjB,MAAM,+BAA+B,CAAC;AAEvC,QAAA,MAAM,mBAAmB,uBAGtB,CAAC;AAEJ,eAAe,mBAAmB,CAAC;AACnC,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAE/D,YAAY,EACV,qBAAqB,EACrB,YAAY,EACZ,cAAc,EACd,gBAAgB,GACjB,MAAM,+BAA+B,CAAC;AAEvC,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,cAAc;;;;;;EAuCvD;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,gBAAgB;;;;;cAuBhC,MAAM;EAE/B"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,qBAAqB,EACrB,YAAY,EACZ,cAAc,EACd,gBAAgB,EACjB,MAAM,+BAA+B,CAAC;AAEvC,QAAA,MAAM,mBAAmB,uBAGtB,CAAC;AAEJ,eAAe,mBAAmB,CAAC;AACnC,OAAO,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAE/D,YAAY,EACV,qBAAqB,EACrB,YAAY,EACZ,cAAc,EACd,gBAAgB,GACjB,MAAM,+BAA+B,CAAC;AAEvC,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,cAAc;;;;;;EAuCvD;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,gBAAgB;;;;;cAuBhC,MAAM;EAE/B"}
@@ -0,0 +1,12 @@
1
+ export declare function requestLocationPermission(locationPermissionMessage?: {
2
+ title: string | 'Location Permission';
3
+ message: string | 'This app needs access to your location to track your ride.';
4
+ buttonPositive: string | 'Allow';
5
+ buttonNegative: string | 'Deny';
6
+ }, locationPermissionBackground?: {
7
+ title: string | 'Background Location Permission';
8
+ message: string | 'This app needs background location access to continue tracking while the app is minimized.';
9
+ buttonPositive: string | 'Allow';
10
+ buttonNegative: string | 'Deny';
11
+ }): Promise<boolean>;
12
+ //# sourceMappingURL=requestPermission.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"requestPermission.d.ts","sourceRoot":"","sources":["../../../src/requestPermission.ts"],"names":[],"mappings":"AAEA,wBAAsB,yBAAyB,CAC7C,yBAAyB,CAAC,EAAE;IAC1B,KAAK,EAAE,MAAM,GAAG,qBAAqB,CAAC;IACtC,OAAO,EACH,MAAM,GACN,4DAA4D,CAAC;IACjE,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC;IACjC,cAAc,EAAE,MAAM,GAAG,MAAM,CAAC;CACjC,EACD,4BAA4B,CAAC,EAAE;IAC7B,KAAK,EAAE,MAAM,GAAG,gCAAgC,CAAC;IACjD,OAAO,EACH,MAAM,GACN,4FAA4F,CAAC;IACjG,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC;IACjC,cAAc,EAAE,MAAM,GAAG,MAAM,CAAC;CACjC,GACA,OAAO,CAAC,OAAO,CAAC,CAmDlB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-nitro-location-tracking",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "A React Native Nitro module for location tracking",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
@@ -13,12 +13,21 @@ export class LocationSmoother {
13
13
  private queue: LocationData[] = [];
14
14
  private isAnimating = false;
15
15
  private markerRef: { current: MarkerRef | null };
16
+ private hasInitialCoordinate = false;
16
17
 
17
18
  constructor(markerRef: { current: MarkerRef | null }) {
18
19
  this.markerRef = markerRef;
19
20
  }
20
21
 
21
22
  feed(location: LocationData) {
23
+ // Skip the first location — let the Marker mount with it as its
24
+ // initial `coordinate` prop. Calling animateMarkerToCoordinate before
25
+ // the native marker has a position causes a NullPointerException.
26
+ if (!this.hasInitialCoordinate) {
27
+ this.hasInitialCoordinate = true;
28
+ return;
29
+ }
30
+
22
31
  this.queue.push(location);
23
32
  if (this.queue.length > 10) this.queue = this.queue.slice(-3);
24
33
  if (!this.isAnimating) this.drain();
@@ -42,5 +51,6 @@ export class LocationSmoother {
42
51
  clear() {
43
52
  this.queue = [];
44
53
  this.isAnimating = false;
54
+ this.hasInitialCoordinate = false;
45
55
  }
46
56
  }
package/src/index.tsx CHANGED
@@ -13,6 +13,7 @@ const NitroLocationModule =
13
13
  );
14
14
 
15
15
  export default NitroLocationModule;
16
+ export { requestLocationPermission } from './requestPermission';
16
17
  export { LocationSmoother } from './LocationSmoother';
17
18
  export { shortestRotation, calculateBearing } from './bearing';
18
19
  // export * from './db'
@@ -0,0 +1,71 @@
1
+ import { PermissionsAndroid, Platform } from 'react-native';
2
+
3
+ export async function requestLocationPermission(
4
+ locationPermissionMessage?: {
5
+ title: string | 'Location Permission';
6
+ message:
7
+ | string
8
+ | 'This app needs access to your location to track your ride.';
9
+ buttonPositive: string | 'Allow';
10
+ buttonNegative: string | 'Deny';
11
+ },
12
+ locationPermissionBackground?: {
13
+ title: string | 'Background Location Permission';
14
+ message:
15
+ | string
16
+ | 'This app needs background location access to continue tracking while the app is minimized.';
17
+ buttonPositive: string | 'Allow';
18
+ buttonNegative: string | 'Deny';
19
+ }
20
+ ): Promise<boolean> {
21
+ if (Platform.OS === 'ios') {
22
+ // iOS permissions are handled via Info.plist + system prompt on first access
23
+ return true;
24
+ }
25
+
26
+ const fineGranted = await PermissionsAndroid.request(
27
+ PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
28
+ {
29
+ title: locationPermissionMessage?.title ?? 'Location Permission',
30
+ message:
31
+ locationPermissionMessage?.message ??
32
+ 'This app needs access to your location to track your ride.',
33
+ buttonPositive: locationPermissionMessage?.buttonPositive ?? 'Allow',
34
+ buttonNegative: locationPermissionMessage?.buttonNegative ?? 'Deny',
35
+ }
36
+ );
37
+
38
+ if (fineGranted !== PermissionsAndroid.RESULTS.GRANTED) {
39
+ return false;
40
+ }
41
+
42
+ // Request background location (required for Android 10+)
43
+ if (Number(Platform.Version) >= 29) {
44
+ const bgGranted = await PermissionsAndroid.request(
45
+ PermissionsAndroid.PERMISSIONS.ACCESS_BACKGROUND_LOCATION,
46
+ {
47
+ title:
48
+ locationPermissionBackground?.title ??
49
+ 'Background Location Permission',
50
+ message:
51
+ locationPermissionBackground?.message ??
52
+ 'This app needs background location access to continue tracking while the app is minimized.',
53
+ buttonPositive: locationPermissionBackground?.buttonPositive ?? 'Allow',
54
+ buttonNegative: locationPermissionBackground?.buttonNegative ?? 'Deny',
55
+ }
56
+ );
57
+
58
+ if (bgGranted !== PermissionsAndroid.RESULTS.GRANTED) {
59
+ return false;
60
+ }
61
+ }
62
+
63
+ // Request notification permission (required for Android 13+)
64
+ if (Number(Platform.Version) >= 33) {
65
+ await PermissionsAndroid.request(
66
+ PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS
67
+ );
68
+ }
69
+
70
+ return true;
71
+ }