@kafitra/react-native-live-tracking 0.1.0
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/LICENSE +21 -0
- package/README.md +396 -0
- package/android/build.gradle +71 -0
- package/android/gradle.properties +7 -0
- package/android/src/main/AndroidManifest.xml +40 -0
- package/android/src/main/java/com/livetracking/LiveTrackingModuleImpl.kt +728 -0
- package/android/src/main/java/com/livetracking/LiveTrackingPackage.kt +16 -0
- package/android/src/main/java/com/livetracking/location/LocationEngine.kt +93 -0
- package/android/src/main/java/com/livetracking/network/NetworkListener.kt +127 -0
- package/android/src/main/java/com/livetracking/optimizer/ActivityRecognitionHandler.kt +248 -0
- package/android/src/main/java/com/livetracking/optimizer/MotionSleepManager.kt +130 -0
- package/android/src/main/java/com/livetracking/permissions/PermissionHandler.kt +145 -0
- package/android/src/main/java/com/livetracking/queue/QueueEngine.kt +167 -0
- package/android/src/main/java/com/livetracking/queue/QueuedLocation.kt +16 -0
- package/android/src/main/java/com/livetracking/queue/TrackingDatabase.kt +239 -0
- package/android/src/main/java/com/livetracking/receiver/BootReceiver.kt +53 -0
- package/android/src/main/java/com/livetracking/service/TrackingForegroundService.kt +145 -0
- package/android/src/main/java/com/livetracking/sync/FirebaseSyncEngine.kt +277 -0
- package/android/src/main/java/com/livetracking/sync/LocationDataPoint.kt +31 -0
- package/android/src/main/java/com/livetracking/sync/SyncEngineController.kt +220 -0
- package/android/src/main/java/com/livetracking/sync/SyncTargetConfig.kt +20 -0
- package/android/src/main/java/com/livetracking/sync/TargetHandler.kt +601 -0
- package/android/src/newarch/java/com/livetracking/LiveTrackingModule.kt +64 -0
- package/android/src/oldarch/java/com/livetracking/LiveTrackingModule.kt +70 -0
- package/android/src/test/java/com/livetracking/BackoffCalculationTest.kt +216 -0
- package/android/src/test/java/com/livetracking/BatchAccumulatorTest.kt +391 -0
- package/android/src/test/java/com/livetracking/BootReceiverTest.kt +247 -0
- package/android/src/test/java/com/livetracking/FirebaseSyncEngineTest.kt +337 -0
- package/android/src/test/java/com/livetracking/LocationEngineTest.kt +202 -0
- package/android/src/test/java/com/livetracking/MotionSleepManagerTest.kt +420 -0
- package/android/src/test/java/com/livetracking/OfflineQueueTest.kt +462 -0
- package/android/src/test/java/com/livetracking/PermissionHandlerTest.kt +200 -0
- package/android/src/test/java/com/livetracking/QueueEngineTest.kt +335 -0
- package/android/src/test/java/com/livetracking/SyncEngineControllerTest.kt +855 -0
- package/ios/ActivityRecognitionHandler.swift +196 -0
- package/ios/BackgroundModeHelper.swift +132 -0
- package/ios/FirebaseSyncEngine.swift +276 -0
- package/ios/LiveTracking-Bridging-Header.h +2 -0
- package/ios/LiveTracking.m +37 -0
- package/ios/LiveTracking.swift +773 -0
- package/ios/LocationDataPoint.swift +56 -0
- package/ios/LocationEngine.swift +160 -0
- package/ios/MotionSleepManager.swift +151 -0
- package/ios/NetworkListener.swift +105 -0
- package/ios/OfflineQueueManager.swift +503 -0
- package/ios/PermissionHandler.swift +148 -0
- package/ios/QueueEngine.swift +249 -0
- package/ios/SyncEngineController.swift +396 -0
- package/ios/SyncTargetConfig.swift +36 -0
- package/ios/TargetHandler.swift +715 -0
- package/ios/Tests/ActivityRecognitionHandlerTests.swift +259 -0
- package/ios/Tests/FirebaseSyncEngineTests.swift +303 -0
- package/ios/Tests/LocationEngineTests.swift +244 -0
- package/ios/Tests/MotionSleepManagerTests.swift +355 -0
- package/ios/Tests/NetworkListenerTests.swift +188 -0
- package/ios/Tests/OfflineQueueFlushTests.swift +375 -0
- package/ios/Tests/PermissionHandlerTests.swift +238 -0
- package/ios/Tests/QueueEngineTests.swift +346 -0
- package/ios/TrackingCleanup.swift +93 -0
- package/ios/TrackingNotificationManager.swift +187 -0
- package/lib/commonjs/EventEmitter.js +113 -0
- package/lib/commonjs/EventEmitter.js.map +1 -0
- package/lib/commonjs/LiveTracking.js +134 -0
- package/lib/commonjs/LiveTracking.js.map +1 -0
- package/lib/commonjs/NativeLiveTracking.js +21 -0
- package/lib/commonjs/NativeLiveTracking.js.map +1 -0
- package/lib/commonjs/filters/distanceTimeFilter.js +63 -0
- package/lib/commonjs/filters/distanceTimeFilter.js.map +1 -0
- package/lib/commonjs/index.js +103 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/serialization/locationSerializer.js +51 -0
- package/lib/commonjs/serialization/locationSerializer.js.map +1 -0
- package/lib/commonjs/types.js +77 -0
- package/lib/commonjs/types.js.map +1 -0
- package/lib/commonjs/utils/distance.js +63 -0
- package/lib/commonjs/utils/distance.js.map +1 -0
- package/lib/commonjs/utils/retry.js +80 -0
- package/lib/commonjs/utils/retry.js.map +1 -0
- package/lib/commonjs/validation.js +463 -0
- package/lib/commonjs/validation.js.map +1 -0
- package/lib/module/EventEmitter.js +105 -0
- package/lib/module/EventEmitter.js.map +1 -0
- package/lib/module/LiveTracking.js +127 -0
- package/lib/module/LiveTracking.js.map +1 -0
- package/lib/module/NativeLiveTracking.js +16 -0
- package/lib/module/NativeLiveTracking.js.map +1 -0
- package/lib/module/filters/distanceTimeFilter.js +58 -0
- package/lib/module/filters/distanceTimeFilter.js.map +1 -0
- package/lib/module/index.js +32 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/serialization/locationSerializer.js +45 -0
- package/lib/module/serialization/locationSerializer.js.map +1 -0
- package/lib/module/types.js +94 -0
- package/lib/module/types.js.map +1 -0
- package/lib/module/utils/distance.js +56 -0
- package/lib/module/utils/distance.js.map +1 -0
- package/lib/module/utils/retry.js +72 -0
- package/lib/module/utils/retry.js.map +1 -0
- package/lib/module/validation.js +456 -0
- package/lib/module/validation.js.map +1 -0
- package/lib/typescript/EventEmitter.d.ts +65 -0
- package/lib/typescript/EventEmitter.d.ts.map +1 -0
- package/lib/typescript/LiveTracking.d.ts +23 -0
- package/lib/typescript/LiveTracking.d.ts.map +1 -0
- package/lib/typescript/NativeLiveTracking.d.ts +25 -0
- package/lib/typescript/NativeLiveTracking.d.ts.map +1 -0
- package/lib/typescript/filters/distanceTimeFilter.d.ts +44 -0
- package/lib/typescript/filters/distanceTimeFilter.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +21 -0
- package/lib/typescript/index.d.ts.map +1 -0
- package/lib/typescript/serialization/locationSerializer.d.ts +39 -0
- package/lib/typescript/serialization/locationSerializer.d.ts.map +1 -0
- package/lib/typescript/types.d.ts +217 -0
- package/lib/typescript/types.d.ts.map +1 -0
- package/lib/typescript/utils/distance.d.ts +38 -0
- package/lib/typescript/utils/distance.d.ts.map +1 -0
- package/lib/typescript/utils/retry.d.ts +60 -0
- package/lib/typescript/utils/retry.d.ts.map +1 -0
- package/lib/typescript/validation.d.ts +26 -0
- package/lib/typescript/validation.d.ts.map +1 -0
- package/package.json +126 -0
- package/react-native-live-tracking.podspec +47 -0
- package/src/EventEmitter.ts +118 -0
- package/src/LiveTracking.ts +159 -0
- package/src/NativeLiveTracking.ts +29 -0
- package/src/filters/distanceTimeFilter.ts +75 -0
- package/src/index.ts +51 -0
- package/src/serialization/locationSerializer.ts +57 -0
- package/src/types.ts +252 -0
- package/src/utils/distance.ts +68 -0
- package/src/utils/retry.ts +75 -0
- package/src/validation.ts +552 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Kafitra Marna Ibrahim
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
# @kafitra/react-native-live-tracking
|
|
2
|
+
|
|
3
|
+
Real-time location tracking library for React Native with Firebase synchronization. Supports background tracking, offline caching, battery optimization, and fully flexible sync targets.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Background Location Tracking** — Android Foreground Service & iOS Background Modes
|
|
8
|
+
- **Flexible Firebase Sync** — Define unlimited sync targets with custom paths, write methods, and behavior
|
|
9
|
+
- **Offline-First** — Per-target offline queue with automatic sync when connection restores
|
|
10
|
+
- **Battery Optimization** — Distance/Time matrix filter + Motion Sleep Mode
|
|
11
|
+
- **Auto-Restart** — Resume tracking after device reboot (Android BOOT_COMPLETED)
|
|
12
|
+
- **Cross-Platform** — iOS 13+ & Android API 21+
|
|
13
|
+
- **New Architecture Ready** — Works with both TurboModules (New Arch) and legacy Bridge (Old Arch) via interop
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install @kafitra/react-native-live-tracking
|
|
19
|
+
# or
|
|
20
|
+
yarn add @kafitra/react-native-live-tracking
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### iOS
|
|
24
|
+
|
|
25
|
+
#### 1. Podfile Setup
|
|
26
|
+
|
|
27
|
+
This library depends on `FirebaseDatabase` and `FirebaseFirestore`. If you use `react-native-firebase`, add the required modular headers and permissions in your `Podfile`:
|
|
28
|
+
|
|
29
|
+
```ruby
|
|
30
|
+
$RNFirebaseAsStaticFramework = true
|
|
31
|
+
|
|
32
|
+
require Pod::Executable.execute_command('node', ['-p',
|
|
33
|
+
'require.resolve(
|
|
34
|
+
"react-native/scripts/react_native_pods.rb",
|
|
35
|
+
{paths: [process.argv[1]]},
|
|
36
|
+
)', __dir__]).strip
|
|
37
|
+
|
|
38
|
+
require_relative '../node_modules/react-native-permissions/scripts/setup'
|
|
39
|
+
|
|
40
|
+
platform :ios, min_ios_version_supported
|
|
41
|
+
prepare_react_native_project!
|
|
42
|
+
|
|
43
|
+
target 'YourApp' do
|
|
44
|
+
# Firebase modular headers (required for Swift static library integration)
|
|
45
|
+
pod 'GoogleUtilities', :modular_headers => true
|
|
46
|
+
pod 'FirebaseCore', :modular_headers => true
|
|
47
|
+
pod 'FirebaseCoreExtension', :modular_headers => true
|
|
48
|
+
pod 'FirebaseCoreInternal', :modular_headers => true
|
|
49
|
+
pod 'FirebaseAppCheckInterop', :modular_headers => true
|
|
50
|
+
pod 'FirebaseDatabase', :modular_headers => true
|
|
51
|
+
pod 'FirebaseFirestore', :modular_headers => true
|
|
52
|
+
pod 'FirebaseFirestoreInternal', :modular_headers => true
|
|
53
|
+
pod 'leveldb-library', :modular_headers => true
|
|
54
|
+
|
|
55
|
+
# Permissions required by live-tracking
|
|
56
|
+
setup_permissions([
|
|
57
|
+
'LocationAlways',
|
|
58
|
+
'LocationWhenInUse',
|
|
59
|
+
'Motion',
|
|
60
|
+
])
|
|
61
|
+
|
|
62
|
+
config = use_native_modules!
|
|
63
|
+
use_react_native!(:path => config[:reactNativePath])
|
|
64
|
+
end
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
#### 2. Install Pods
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
cd ios && pod install
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Quick Start
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
import LiveTracking from '@kafitra/react-native-live-tracking';
|
|
77
|
+
|
|
78
|
+
// 1. Configure
|
|
79
|
+
await LiveTracking.configure({
|
|
80
|
+
optimization: {
|
|
81
|
+
intervalMs: 10000, // Minimum 10 seconds between updates
|
|
82
|
+
distanceFilterMeters: 10, // Only update if moved 10+ meters
|
|
83
|
+
stopWhenStill: true, // Save battery when stationary
|
|
84
|
+
},
|
|
85
|
+
androidNotification: {
|
|
86
|
+
title: "Recording Trip",
|
|
87
|
+
text: "Your location is being shared.",
|
|
88
|
+
},
|
|
89
|
+
firebase: {
|
|
90
|
+
service: 'RTDB', // or 'Firestore'
|
|
91
|
+
targets: [
|
|
92
|
+
{
|
|
93
|
+
path: `users/${userId}/current_location`,
|
|
94
|
+
method: 'set', // overwrite — for real-time map marker
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
path: `users/${userId}/location_history`,
|
|
98
|
+
method: 'push', // append — for track history
|
|
99
|
+
batchSize: 15, // send every 15 points
|
|
100
|
+
offlineQueue: true, // persist offline if no connection
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// 2. Listen for updates
|
|
107
|
+
const subscription = LiveTracking.onLocationUpdate((location) => {
|
|
108
|
+
console.log(location.latitude, location.longitude);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// 3. Start tracking
|
|
112
|
+
await LiveTracking.start();
|
|
113
|
+
|
|
114
|
+
// 4. Stop when done
|
|
115
|
+
await LiveTracking.stop();
|
|
116
|
+
subscription.remove();
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Usage with React Native (TrackingProvider Pattern)
|
|
120
|
+
|
|
121
|
+
A common pattern is to wrap tracking in a provider that reacts to backend status changes (e.g., via Firebase RTDB):
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
import React, { useEffect } from 'react';
|
|
125
|
+
import LiveTracking from '@kafitra/react-native-live-tracking';
|
|
126
|
+
import database from '@react-native-firebase/database';
|
|
127
|
+
|
|
128
|
+
const TRACKING_STATUS = { READY: 0, ON_PROGRESS: 1, COMPLETED: 2 } as const;
|
|
129
|
+
|
|
130
|
+
async function startLiveTracking(deliveryNoteId: string | number) {
|
|
131
|
+
await LiveTracking.configure({
|
|
132
|
+
firebase: {
|
|
133
|
+
service: 'RTDB',
|
|
134
|
+
targets: [
|
|
135
|
+
{ path: `delivery/${deliveryNoteId}/current`, method: 'set' },
|
|
136
|
+
{ path: `delivery/${deliveryNoteId}/history`, method: 'push', batchSize: 10, offlineQueue: true },
|
|
137
|
+
],
|
|
138
|
+
},
|
|
139
|
+
optimization: { intervalMs: 10000, distanceFilterMeters: 10, stopWhenStill: true },
|
|
140
|
+
androidNotification: {
|
|
141
|
+
title: 'Delivery In Progress',
|
|
142
|
+
text: 'Tracking your location...',
|
|
143
|
+
},
|
|
144
|
+
});
|
|
145
|
+
await LiveTracking.start();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function TrackingProvider({ children }: { children: React.ReactNode }) {
|
|
149
|
+
useEffect(() => {
|
|
150
|
+
const statusRef = database().ref(`delivery/188/status`);
|
|
151
|
+
const onValue = statusRef.on('value', (snapshot) => {
|
|
152
|
+
const status = snapshot.val();
|
|
153
|
+
if (status === TRACKING_STATUS.ON_PROGRESS) {
|
|
154
|
+
startLiveTracking(188).catch(console.warn);
|
|
155
|
+
} else if (status === TRACKING_STATUS.COMPLETED) {
|
|
156
|
+
LiveTracking.stop().catch(console.warn);
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
return () => statusRef.off('value', onValue);
|
|
160
|
+
}, []);
|
|
161
|
+
|
|
162
|
+
return <>{children}</>;
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Sync Targets
|
|
167
|
+
|
|
168
|
+
Each sync target defines a **path**, **method**, and optional behavior. You have full control over your Firebase database structure.
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
interface SyncTarget {
|
|
172
|
+
/** Firebase path to write to */
|
|
173
|
+
path: string;
|
|
174
|
+
/** Write method: 'set' (overwrite), 'push' (append), 'update' (merge) */
|
|
175
|
+
method: 'set' | 'push' | 'update';
|
|
176
|
+
/** Number of points to accumulate before writing. Default: 1 (immediate) */
|
|
177
|
+
batchSize?: number;
|
|
178
|
+
/** Whether to persist data offline when there is no connection. Default: false */
|
|
179
|
+
offlineQueue?: boolean;
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Write Methods
|
|
184
|
+
|
|
185
|
+
| Method | Behavior | Use Case |
|
|
186
|
+
|--------|----------|----------|
|
|
187
|
+
| `set` | Overwrite data at path | Real-time map marker |
|
|
188
|
+
| `push` | Append with auto-generated key | Track history |
|
|
189
|
+
| `update` | Merge fields without removing existing ones | Partial data update |
|
|
190
|
+
|
|
191
|
+
### Example: Multiple Targets
|
|
192
|
+
|
|
193
|
+
```typescript
|
|
194
|
+
firebase: {
|
|
195
|
+
service: 'Firestore',
|
|
196
|
+
targets: [
|
|
197
|
+
// Real-time marker (overwrite, no queue)
|
|
198
|
+
{ path: `drivers/${driverId}/location`, method: 'set' },
|
|
199
|
+
|
|
200
|
+
// Trip history (batch + offline queue)
|
|
201
|
+
{ path: `trips/${tripId}/points`, method: 'push', batchSize: 20, offlineQueue: true },
|
|
202
|
+
|
|
203
|
+
// Status update (merge into existing document)
|
|
204
|
+
{ path: `drivers/${driverId}/status`, method: 'update' },
|
|
205
|
+
],
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
## Configuration
|
|
210
|
+
|
|
211
|
+
### Optimization
|
|
212
|
+
|
|
213
|
+
| Parameter | Type | Default | Description |
|
|
214
|
+
|-----------|------|---------|-------------|
|
|
215
|
+
| `optimization.intervalMs` | number | 10000 | Minimum interval between updates (ms) |
|
|
216
|
+
| `optimization.distanceFilterMeters` | number | 10 | Minimum distance to trigger update (meters) |
|
|
217
|
+
| `optimization.stopWhenStill` | boolean | true | Enable Motion Sleep Mode |
|
|
218
|
+
| `optimization.mode` | `'interval' \| 'distance' \| 'both'` | `'both'` | Filter strategy: time only, distance only, or both |
|
|
219
|
+
|
|
220
|
+
### Firebase
|
|
221
|
+
|
|
222
|
+
| Parameter | Type | Required | Description |
|
|
223
|
+
|-----------|------|----------|-------------|
|
|
224
|
+
| `firebase.service` | `'RTDB' \| 'Firestore'` | ✅ | Firebase service type |
|
|
225
|
+
| `firebase.targets` | `SyncTarget[]` | ✅ | Array of sync targets (min 1, max 20) |
|
|
226
|
+
|
|
227
|
+
### SyncTarget Options
|
|
228
|
+
|
|
229
|
+
| Parameter | Type | Default | Description |
|
|
230
|
+
|-----------|------|---------|-------------|
|
|
231
|
+
| `path` | string | — | Firebase path (1–768 characters) |
|
|
232
|
+
| `method` | `'set' \| 'push' \| 'update'` | — | Write method |
|
|
233
|
+
| `batchSize` | number | 1 | Points per batch (1–1000) |
|
|
234
|
+
| `offlineQueue` | boolean | false | Persist data offline when no connection |
|
|
235
|
+
|
|
236
|
+
### Android Notification
|
|
237
|
+
|
|
238
|
+
| Parameter | Type | Default | Description |
|
|
239
|
+
|-----------|------|---------|-------------|
|
|
240
|
+
| `androidNotification.enabled` | boolean | `true` | Show the foreground service notification |
|
|
241
|
+
| `androidNotification.title` | string | — | Foreground service notification title (required when enabled) |
|
|
242
|
+
| `androidNotification.text` | string | — | Notification text (required when enabled) |
|
|
243
|
+
| `androidNotification.icon` | string? | — | Icon resource name |
|
|
244
|
+
| `androidNotification.channelId` | string? | — | Notification channel ID |
|
|
245
|
+
| `androidNotification.channelName` | string? | — | Notification channel name |
|
|
246
|
+
|
|
247
|
+
> **Note:** Android requires a foreground service notification to run background tracking. When `enabled: false`, the service still runs but shows a minimal default notification.
|
|
248
|
+
|
|
249
|
+
### iOS Notification
|
|
250
|
+
|
|
251
|
+
| Parameter | Type | Default | Description |
|
|
252
|
+
|-----------|------|---------|-------------|
|
|
253
|
+
| `iosNotification.enabled` | boolean | `true` | Show the persistent local notification |
|
|
254
|
+
| `iosNotification.title` | string | — | Notification title (required when enabled) |
|
|
255
|
+
| `iosNotification.text` | string | — | Notification body (required when enabled) |
|
|
256
|
+
|
|
257
|
+
## How It Works
|
|
258
|
+
|
|
259
|
+
### Distance/Time Matrix
|
|
260
|
+
The filtering strategy is controlled by `optimization.mode`:
|
|
261
|
+
|
|
262
|
+
| Mode | Behavior |
|
|
263
|
+
|------|----------|
|
|
264
|
+
| `both` (default) | Update only when **both** time and distance conditions are met |
|
|
265
|
+
| `interval` | Update only when time since last update ≥ `intervalMs` |
|
|
266
|
+
| `distance` | Update only when distance from last location ≥ `distanceFilterMeters` |
|
|
267
|
+
|
|
268
|
+
### GPS Disabled During Tracking
|
|
269
|
+
The library monitors GPS/location service status. If GPS is disabled while tracking is active:
|
|
270
|
+
- Tracking is automatically paused
|
|
271
|
+
- An `onError` event with code `GPS_DISABLED` is emitted
|
|
272
|
+
- When GPS is re-enabled, tracking automatically resumes
|
|
273
|
+
- A `GPS_ENABLED` event is emitted on resume
|
|
274
|
+
|
|
275
|
+
### Motion Sleep Mode
|
|
276
|
+
When `stopWhenStill: true` and device is stationary for > 3 minutes:
|
|
277
|
+
- GPS accuracy is reduced to save battery
|
|
278
|
+
- Resumes full accuracy when movement is detected
|
|
279
|
+
|
|
280
|
+
### Per-Target Batch Processing
|
|
281
|
+
- Targets with `batchSize > 1` accumulate points in memory
|
|
282
|
+
- Written to Firebase in a single batch operation when full
|
|
283
|
+
- Partial batches are automatically flushed on `stop()` or after 30 seconds of inactivity
|
|
284
|
+
|
|
285
|
+
### Per-Target Offline Queue
|
|
286
|
+
- Targets with `offlineQueue: true` persist data to SQLite (Android) / CoreData (iOS) when offline
|
|
287
|
+
- Automatically synced when connection restores (chronological order, oldest first)
|
|
288
|
+
- Maximum 10,000 data points per target (oldest evicted when full)
|
|
289
|
+
- Data persists across app restart and process termination
|
|
290
|
+
|
|
291
|
+
### Retry with Exponential Backoff
|
|
292
|
+
- Each target retries independently (does not block other targets)
|
|
293
|
+
- `set`/`update`: max 3 retries
|
|
294
|
+
- `push`: max 5 retries
|
|
295
|
+
- Base delay 1000ms, multiplier 2x, jitter ±200ms
|
|
296
|
+
- Non-transient errors (permission denied) fail immediately without retry
|
|
297
|
+
|
|
298
|
+
## Platform Setup
|
|
299
|
+
|
|
300
|
+
### Android
|
|
301
|
+
|
|
302
|
+
#### Permissions
|
|
303
|
+
|
|
304
|
+
Add to `AndroidManifest.xml`:
|
|
305
|
+
```xml
|
|
306
|
+
<!-- Location -->
|
|
307
|
+
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
|
308
|
+
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
|
309
|
+
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
|
|
310
|
+
|
|
311
|
+
<!-- Foreground Service (required for background tracking notification) -->
|
|
312
|
+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
|
313
|
+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
|
|
314
|
+
|
|
315
|
+
<!-- Optional: Notification permission (Android 13+) -->
|
|
316
|
+
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
|
317
|
+
|
|
318
|
+
<!-- Optional: Auto-restart tracking after device reboot -->
|
|
319
|
+
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
|
320
|
+
|
|
321
|
+
<!-- Optional: Motion Sleep Mode (reduce battery when stationary) -->
|
|
322
|
+
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
|
|
323
|
+
|
|
324
|
+
<!-- Network state detection for offline queue sync -->
|
|
325
|
+
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
> **Note:** You must request runtime permissions (`ACCESS_FINE_LOCATION`, `ACCESS_BACKGROUND_LOCATION`, `POST_NOTIFICATIONS`) before calling `LiveTracking.start()`. The library will reject with `PERMISSION_DENIED` if required permissions are not granted.
|
|
329
|
+
|
|
330
|
+
#### Firebase Setup
|
|
331
|
+
|
|
332
|
+
Ensure your app has `google-services.json` in `android/app/` and the Google Services plugin applied in your `android/app/build.gradle`:
|
|
333
|
+
```groovy
|
|
334
|
+
apply plugin: 'com.google.gms.google-services'
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
### iOS
|
|
338
|
+
|
|
339
|
+
#### Info.plist
|
|
340
|
+
|
|
341
|
+
Add the following keys to your `Info.plist`:
|
|
342
|
+
```xml
|
|
343
|
+
<!-- Required: Location permission descriptions -->
|
|
344
|
+
<key>NSLocationWhenInUseUsageDescription</key>
|
|
345
|
+
<string>App needs your location to track deliveries in real-time.</string>
|
|
346
|
+
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
|
|
347
|
+
<string>App needs background location access to track deliveries while the app is in the background.</string>
|
|
348
|
+
|
|
349
|
+
<!-- Optional: Motion permission for stopWhenStill battery optimization -->
|
|
350
|
+
<key>NSMotionUsageDescription</key>
|
|
351
|
+
<string>Used to optimize battery usage when stationary.</string>
|
|
352
|
+
|
|
353
|
+
<!-- Required: Enable background location updates -->
|
|
354
|
+
<key>UIBackgroundModes</key>
|
|
355
|
+
<array>
|
|
356
|
+
<string>location</string>
|
|
357
|
+
</array>
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
#### Firebase Setup
|
|
361
|
+
|
|
362
|
+
Ensure your app has `GoogleService-Info.plist` added to the Xcode project.
|
|
363
|
+
|
|
364
|
+
## API Reference
|
|
365
|
+
|
|
366
|
+
| Method | Returns | Description |
|
|
367
|
+
|--------|---------|-------------|
|
|
368
|
+
| `configure(config)` | `Promise<void>` | Configure the library |
|
|
369
|
+
| `start()` | `Promise<void>` | Start tracking |
|
|
370
|
+
| `stop()` | `Promise<void>` | Stop tracking (flushes all batches) |
|
|
371
|
+
| `getStatus()` | `Promise<TrackingStatus>` | Get current tracking status |
|
|
372
|
+
| `getQueuedLocations()` | `Promise<number>` | Get total offline queue count |
|
|
373
|
+
| `getQueuedLocationsByTarget()` | `Promise<Record<string, number>>` | Get queue count per target path |
|
|
374
|
+
| `onLocationUpdate(cb)` | `Subscription` | Listen for location updates |
|
|
375
|
+
| `onError(cb)` | `Subscription` | Listen for errors |
|
|
376
|
+
|
|
377
|
+
## Error Codes
|
|
378
|
+
|
|
379
|
+
| Code | Description |
|
|
380
|
+
|------|-------------|
|
|
381
|
+
| `PERMISSION_DENIED` | Location permission not granted |
|
|
382
|
+
| `GPS_DISABLED` | GPS/Location services turned off |
|
|
383
|
+
| `GPS_ENABLED` | GPS/Location services turned back on (auto-resume) |
|
|
384
|
+
| `PERMISSION_REVOKED` | Permission revoked while tracking was paused |
|
|
385
|
+
| `INVALID_CONFIG` | Invalid configuration parameters |
|
|
386
|
+
| `NOT_CONFIGURED` | Method called before `configure()` |
|
|
387
|
+
| `FIREBASE_WRITE_FAILED` | Firebase write failed after max retries |
|
|
388
|
+
| `DEPRECATED_FIELD` | Using old config fields (currentLocationPath/historyPath) |
|
|
389
|
+
| `QUEUE_OVERFLOW` | Offline queue reached 10,000 cap (oldest evicted) |
|
|
390
|
+
| `LOCATION_UNKNOWN` | Location temporarily unavailable |
|
|
391
|
+
| `NETWORK_ERROR` | Location network error |
|
|
392
|
+
| `LOCATION_ERROR` | Generic location update failure |
|
|
393
|
+
|
|
394
|
+
## License
|
|
395
|
+
|
|
396
|
+
MIT
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
buildscript {
|
|
2
|
+
ext.safeExtGet = {prop, fallback ->
|
|
3
|
+
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
|
|
4
|
+
}
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
apply plugin: 'com.android.library'
|
|
8
|
+
apply plugin: 'kotlin-android'
|
|
9
|
+
apply plugin: 'com.facebook.react'
|
|
10
|
+
|
|
11
|
+
android {
|
|
12
|
+
namespace "com.livetracking"
|
|
13
|
+
|
|
14
|
+
compileSdkVersion safeExtGet('compileSdkVersion', 34)
|
|
15
|
+
|
|
16
|
+
defaultConfig {
|
|
17
|
+
minSdkVersion safeExtGet('minSdkVersion', 23)
|
|
18
|
+
targetSdkVersion safeExtGet('targetSdkVersion', 34)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
buildFeatures {
|
|
22
|
+
buildConfig true
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
buildTypes {
|
|
26
|
+
release {
|
|
27
|
+
minifyEnabled false
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
compileOptions {
|
|
32
|
+
sourceCompatibility JavaVersion.VERSION_17
|
|
33
|
+
targetCompatibility JavaVersion.VERSION_17
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
kotlinOptions {
|
|
37
|
+
jvmTarget = '17'
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
sourceSets {
|
|
41
|
+
main {
|
|
42
|
+
java.srcDirs += 'src/main/java'
|
|
43
|
+
java.srcDirs += 'src/oldarch/java'
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
testOptions {
|
|
48
|
+
unitTests {
|
|
49
|
+
includeAndroidResources = true
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
repositories {
|
|
55
|
+
mavenCentral()
|
|
56
|
+
google()
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
dependencies {
|
|
60
|
+
implementation "com.facebook.react:react-android:+"
|
|
61
|
+
implementation "org.jetbrains.kotlin:kotlin-stdlib:${safeExtGet('kotlinVersion', '1.8.22')}"
|
|
62
|
+
implementation "androidx.core:core-ktx:1.12.0"
|
|
63
|
+
implementation "com.google.android.gms:play-services-location:21.0.1"
|
|
64
|
+
implementation "com.google.firebase:firebase-database:20.3.0"
|
|
65
|
+
implementation "com.google.firebase:firebase-firestore:24.10.0"
|
|
66
|
+
|
|
67
|
+
testImplementation "junit:junit:4.13.2"
|
|
68
|
+
testImplementation "io.mockk:mockk:1.13.8"
|
|
69
|
+
testImplementation "org.robolectric:robolectric:4.11.1"
|
|
70
|
+
testImplementation "androidx.test:core:1.5.0"
|
|
71
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
LiveTracking_kotlinVersion=1.8.22
|
|
2
|
+
LiveTracking_compileSdkVersion=34
|
|
3
|
+
LiveTracking_minSdkVersion=23
|
|
4
|
+
LiveTracking_targetSdkVersion=34
|
|
5
|
+
# Enable/disable new architecture (TurboModules)
|
|
6
|
+
# Set to true in consumer app's gradle.properties to enable new architecture
|
|
7
|
+
newArchEnabled=false
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
|
2
|
+
package="com.livetracking">
|
|
3
|
+
|
|
4
|
+
<!-- Location permissions -->
|
|
5
|
+
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
|
6
|
+
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
|
7
|
+
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
|
|
8
|
+
|
|
9
|
+
<!-- Foreground service permissions -->
|
|
10
|
+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
|
11
|
+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
|
|
12
|
+
|
|
13
|
+
<!-- Network state permission for connectivity monitoring -->
|
|
14
|
+
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
|
15
|
+
|
|
16
|
+
<!-- Boot completed permission for auto-restart tracking -->
|
|
17
|
+
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
|
18
|
+
|
|
19
|
+
<!-- Activity Recognition permissions for motion detection -->
|
|
20
|
+
<uses-permission android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION" />
|
|
21
|
+
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
|
|
22
|
+
|
|
23
|
+
<application>
|
|
24
|
+
<service
|
|
25
|
+
android:name=".service.TrackingForegroundService"
|
|
26
|
+
android:enabled="true"
|
|
27
|
+
android:exported="false"
|
|
28
|
+
android:foregroundServiceType="location" />
|
|
29
|
+
|
|
30
|
+
<receiver
|
|
31
|
+
android:name=".receiver.BootReceiver"
|
|
32
|
+
android:enabled="true"
|
|
33
|
+
android:exported="true">
|
|
34
|
+
<intent-filter>
|
|
35
|
+
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
|
36
|
+
</intent-filter>
|
|
37
|
+
</receiver>
|
|
38
|
+
</application>
|
|
39
|
+
|
|
40
|
+
</manifest>
|