@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.
Files changed (132) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +396 -0
  3. package/android/build.gradle +71 -0
  4. package/android/gradle.properties +7 -0
  5. package/android/src/main/AndroidManifest.xml +40 -0
  6. package/android/src/main/java/com/livetracking/LiveTrackingModuleImpl.kt +728 -0
  7. package/android/src/main/java/com/livetracking/LiveTrackingPackage.kt +16 -0
  8. package/android/src/main/java/com/livetracking/location/LocationEngine.kt +93 -0
  9. package/android/src/main/java/com/livetracking/network/NetworkListener.kt +127 -0
  10. package/android/src/main/java/com/livetracking/optimizer/ActivityRecognitionHandler.kt +248 -0
  11. package/android/src/main/java/com/livetracking/optimizer/MotionSleepManager.kt +130 -0
  12. package/android/src/main/java/com/livetracking/permissions/PermissionHandler.kt +145 -0
  13. package/android/src/main/java/com/livetracking/queue/QueueEngine.kt +167 -0
  14. package/android/src/main/java/com/livetracking/queue/QueuedLocation.kt +16 -0
  15. package/android/src/main/java/com/livetracking/queue/TrackingDatabase.kt +239 -0
  16. package/android/src/main/java/com/livetracking/receiver/BootReceiver.kt +53 -0
  17. package/android/src/main/java/com/livetracking/service/TrackingForegroundService.kt +145 -0
  18. package/android/src/main/java/com/livetracking/sync/FirebaseSyncEngine.kt +277 -0
  19. package/android/src/main/java/com/livetracking/sync/LocationDataPoint.kt +31 -0
  20. package/android/src/main/java/com/livetracking/sync/SyncEngineController.kt +220 -0
  21. package/android/src/main/java/com/livetracking/sync/SyncTargetConfig.kt +20 -0
  22. package/android/src/main/java/com/livetracking/sync/TargetHandler.kt +601 -0
  23. package/android/src/newarch/java/com/livetracking/LiveTrackingModule.kt +64 -0
  24. package/android/src/oldarch/java/com/livetracking/LiveTrackingModule.kt +70 -0
  25. package/android/src/test/java/com/livetracking/BackoffCalculationTest.kt +216 -0
  26. package/android/src/test/java/com/livetracking/BatchAccumulatorTest.kt +391 -0
  27. package/android/src/test/java/com/livetracking/BootReceiverTest.kt +247 -0
  28. package/android/src/test/java/com/livetracking/FirebaseSyncEngineTest.kt +337 -0
  29. package/android/src/test/java/com/livetracking/LocationEngineTest.kt +202 -0
  30. package/android/src/test/java/com/livetracking/MotionSleepManagerTest.kt +420 -0
  31. package/android/src/test/java/com/livetracking/OfflineQueueTest.kt +462 -0
  32. package/android/src/test/java/com/livetracking/PermissionHandlerTest.kt +200 -0
  33. package/android/src/test/java/com/livetracking/QueueEngineTest.kt +335 -0
  34. package/android/src/test/java/com/livetracking/SyncEngineControllerTest.kt +855 -0
  35. package/ios/ActivityRecognitionHandler.swift +196 -0
  36. package/ios/BackgroundModeHelper.swift +132 -0
  37. package/ios/FirebaseSyncEngine.swift +276 -0
  38. package/ios/LiveTracking-Bridging-Header.h +2 -0
  39. package/ios/LiveTracking.m +37 -0
  40. package/ios/LiveTracking.swift +773 -0
  41. package/ios/LocationDataPoint.swift +56 -0
  42. package/ios/LocationEngine.swift +160 -0
  43. package/ios/MotionSleepManager.swift +151 -0
  44. package/ios/NetworkListener.swift +105 -0
  45. package/ios/OfflineQueueManager.swift +503 -0
  46. package/ios/PermissionHandler.swift +148 -0
  47. package/ios/QueueEngine.swift +249 -0
  48. package/ios/SyncEngineController.swift +396 -0
  49. package/ios/SyncTargetConfig.swift +36 -0
  50. package/ios/TargetHandler.swift +715 -0
  51. package/ios/Tests/ActivityRecognitionHandlerTests.swift +259 -0
  52. package/ios/Tests/FirebaseSyncEngineTests.swift +303 -0
  53. package/ios/Tests/LocationEngineTests.swift +244 -0
  54. package/ios/Tests/MotionSleepManagerTests.swift +355 -0
  55. package/ios/Tests/NetworkListenerTests.swift +188 -0
  56. package/ios/Tests/OfflineQueueFlushTests.swift +375 -0
  57. package/ios/Tests/PermissionHandlerTests.swift +238 -0
  58. package/ios/Tests/QueueEngineTests.swift +346 -0
  59. package/ios/TrackingCleanup.swift +93 -0
  60. package/ios/TrackingNotificationManager.swift +187 -0
  61. package/lib/commonjs/EventEmitter.js +113 -0
  62. package/lib/commonjs/EventEmitter.js.map +1 -0
  63. package/lib/commonjs/LiveTracking.js +134 -0
  64. package/lib/commonjs/LiveTracking.js.map +1 -0
  65. package/lib/commonjs/NativeLiveTracking.js +21 -0
  66. package/lib/commonjs/NativeLiveTracking.js.map +1 -0
  67. package/lib/commonjs/filters/distanceTimeFilter.js +63 -0
  68. package/lib/commonjs/filters/distanceTimeFilter.js.map +1 -0
  69. package/lib/commonjs/index.js +103 -0
  70. package/lib/commonjs/index.js.map +1 -0
  71. package/lib/commonjs/serialization/locationSerializer.js +51 -0
  72. package/lib/commonjs/serialization/locationSerializer.js.map +1 -0
  73. package/lib/commonjs/types.js +77 -0
  74. package/lib/commonjs/types.js.map +1 -0
  75. package/lib/commonjs/utils/distance.js +63 -0
  76. package/lib/commonjs/utils/distance.js.map +1 -0
  77. package/lib/commonjs/utils/retry.js +80 -0
  78. package/lib/commonjs/utils/retry.js.map +1 -0
  79. package/lib/commonjs/validation.js +463 -0
  80. package/lib/commonjs/validation.js.map +1 -0
  81. package/lib/module/EventEmitter.js +105 -0
  82. package/lib/module/EventEmitter.js.map +1 -0
  83. package/lib/module/LiveTracking.js +127 -0
  84. package/lib/module/LiveTracking.js.map +1 -0
  85. package/lib/module/NativeLiveTracking.js +16 -0
  86. package/lib/module/NativeLiveTracking.js.map +1 -0
  87. package/lib/module/filters/distanceTimeFilter.js +58 -0
  88. package/lib/module/filters/distanceTimeFilter.js.map +1 -0
  89. package/lib/module/index.js +32 -0
  90. package/lib/module/index.js.map +1 -0
  91. package/lib/module/serialization/locationSerializer.js +45 -0
  92. package/lib/module/serialization/locationSerializer.js.map +1 -0
  93. package/lib/module/types.js +94 -0
  94. package/lib/module/types.js.map +1 -0
  95. package/lib/module/utils/distance.js +56 -0
  96. package/lib/module/utils/distance.js.map +1 -0
  97. package/lib/module/utils/retry.js +72 -0
  98. package/lib/module/utils/retry.js.map +1 -0
  99. package/lib/module/validation.js +456 -0
  100. package/lib/module/validation.js.map +1 -0
  101. package/lib/typescript/EventEmitter.d.ts +65 -0
  102. package/lib/typescript/EventEmitter.d.ts.map +1 -0
  103. package/lib/typescript/LiveTracking.d.ts +23 -0
  104. package/lib/typescript/LiveTracking.d.ts.map +1 -0
  105. package/lib/typescript/NativeLiveTracking.d.ts +25 -0
  106. package/lib/typescript/NativeLiveTracking.d.ts.map +1 -0
  107. package/lib/typescript/filters/distanceTimeFilter.d.ts +44 -0
  108. package/lib/typescript/filters/distanceTimeFilter.d.ts.map +1 -0
  109. package/lib/typescript/index.d.ts +21 -0
  110. package/lib/typescript/index.d.ts.map +1 -0
  111. package/lib/typescript/serialization/locationSerializer.d.ts +39 -0
  112. package/lib/typescript/serialization/locationSerializer.d.ts.map +1 -0
  113. package/lib/typescript/types.d.ts +217 -0
  114. package/lib/typescript/types.d.ts.map +1 -0
  115. package/lib/typescript/utils/distance.d.ts +38 -0
  116. package/lib/typescript/utils/distance.d.ts.map +1 -0
  117. package/lib/typescript/utils/retry.d.ts +60 -0
  118. package/lib/typescript/utils/retry.d.ts.map +1 -0
  119. package/lib/typescript/validation.d.ts +26 -0
  120. package/lib/typescript/validation.d.ts.map +1 -0
  121. package/package.json +126 -0
  122. package/react-native-live-tracking.podspec +47 -0
  123. package/src/EventEmitter.ts +118 -0
  124. package/src/LiveTracking.ts +159 -0
  125. package/src/NativeLiveTracking.ts +29 -0
  126. package/src/filters/distanceTimeFilter.ts +75 -0
  127. package/src/index.ts +51 -0
  128. package/src/serialization/locationSerializer.ts +57 -0
  129. package/src/types.ts +252 -0
  130. package/src/utils/distance.ts +68 -0
  131. package/src/utils/retry.ts +75 -0
  132. package/src/validation.ts +552 -0
package/src/types.ts ADDED
@@ -0,0 +1,252 @@
1
+ /**
2
+ * TypeScript type definitions for react-native-live-tracking.
3
+ *
4
+ * @packageDocumentation
5
+ */
6
+
7
+ // ─── Enums ───────────────────────────────────────────────────────────────────
8
+
9
+ /**
10
+ * Internal state machine for tracking lifecycle.
11
+ */
12
+ export enum TrackingState {
13
+ IDLE = 'idle',
14
+ CONFIGURED = 'configured',
15
+ TRACKING = 'tracking',
16
+ MOTION_SLEEP = 'motion_sleep',
17
+ PAUSED_GPS = 'paused_gps',
18
+ }
19
+
20
+ // ─── Configuration Interfaces ────────────────────────────────────────────────
21
+
22
+ /**
23
+ * Strategy used by the distance/time filter.
24
+ * - 'interval': accept when enough time has elapsed
25
+ * - 'distance': accept when enough distance has been covered
26
+ * - 'both': accept only when both conditions are met (default)
27
+ */
28
+ export type OptimizationMode = 'interval' | 'distance' | 'both';
29
+
30
+ /**
31
+ * Optimization parameters for location tracking.
32
+ */
33
+ export interface OptimizationConfig {
34
+ /** Minimum time interval between location updates in milliseconds. Default: 10000 */
35
+ intervalMs?: number;
36
+ /** Minimum distance change in meters to trigger an update. Default: 10 */
37
+ distanceFilterMeters?: number;
38
+ /** Whether to reduce GPS accuracy when device is still. Default: true */
39
+ stopWhenStill?: boolean;
40
+ /** Filter strategy: 'interval', 'distance', or 'both'. Default: 'both' */
41
+ mode?: OptimizationMode;
42
+ }
43
+
44
+ /**
45
+ * Android foreground service notification configuration.
46
+ */
47
+ export interface AndroidNotificationConfig {
48
+ /** Whether to show the foreground service notification. Default: true */
49
+ enabled?: boolean;
50
+ /** Notification title */
51
+ title: string;
52
+ /** Notification body text */
53
+ text: string;
54
+ /** Notification icon resource name */
55
+ icon?: string;
56
+ /** Notification channel ID */
57
+ channelId?: string;
58
+ /** Notification channel name */
59
+ channelName?: string;
60
+ }
61
+
62
+ /**
63
+ * iOS persistent notification configuration.
64
+ * Shows a local notification while tracking is active (similar to Android's foreground service notification).
65
+ */
66
+ export interface IOSNotificationConfig {
67
+ /** Whether to show the persistent local notification. Default: true */
68
+ enabled?: boolean;
69
+ /** Notification title */
70
+ title: string;
71
+ /** Notification body text */
72
+ text: string;
73
+ }
74
+
75
+ /**
76
+ * A user-defined sync target specifying a Firebase path, write method,
77
+ * and optional batching/offline queue settings.
78
+ */
79
+ export interface SyncTarget {
80
+ /** Firebase path to write to */
81
+ path: string;
82
+ /** Write method: 'set' (overwrite), 'push' (append), 'update' (merge) */
83
+ method: 'set' | 'push' | 'update';
84
+ /** Number of points to accumulate before writing. Default: 1 (immediate) */
85
+ batchSize?: number;
86
+ /** Whether to persist data offline when device has no connectivity */
87
+ offlineQueue?: boolean;
88
+ }
89
+
90
+ /**
91
+ * Firebase connection and sync target configuration.
92
+ */
93
+ export interface FirebaseConfig {
94
+ /** Firebase service type: Realtime Database or Firestore */
95
+ service: 'RTDB' | 'Firestore';
96
+ /** Array of sync targets (at least one required) */
97
+ targets: [SyncTarget, ...SyncTarget[]];
98
+ }
99
+
100
+ /**
101
+ * Main configuration object passed to `configure()`.
102
+ */
103
+ export interface TrackingConfig {
104
+ /** Optimization settings for battery and update frequency */
105
+ optimization: OptimizationConfig;
106
+ /** Android foreground service notification settings (Android only) */
107
+ androidNotification?: AndroidNotificationConfig;
108
+ /** iOS persistent notification settings (iOS only) */
109
+ iosNotification?: IOSNotificationConfig;
110
+ /** Firebase connection and path configuration */
111
+ firebase: FirebaseConfig;
112
+ }
113
+
114
+ // ─── Data Interfaces ─────────────────────────────────────────────────────────
115
+
116
+ /**
117
+ * Location data emitted by the library on each valid location update.
118
+ */
119
+ export interface LocationData {
120
+ /** Latitude in degrees (-90 to 90) */
121
+ latitude: number;
122
+ /** Longitude in degrees (-180 to 180) */
123
+ longitude: number;
124
+ /** Unix timestamp in milliseconds */
125
+ timestamp: number;
126
+ /** Horizontal accuracy in meters */
127
+ accuracy: number;
128
+ /** Speed in meters per second, or null if unavailable */
129
+ speed: number | null;
130
+ /** Altitude in meters above sea level, or null if unavailable */
131
+ altitude: number | null;
132
+ /** Bearing/heading in degrees (0-360), or null if unavailable */
133
+ bearing: number | null;
134
+ }
135
+
136
+ // ─── Error & Status Interfaces ───────────────────────────────────────────────
137
+
138
+ /**
139
+ * Error object emitted by the library when tracking issues occur.
140
+ */
141
+ export interface TrackingError {
142
+ /** Error code identifier (e.g., 'PERMISSION_DENIED', 'GPS_DISABLED') */
143
+ code: string;
144
+ /** Human-readable error message */
145
+ message: string;
146
+ /** Whether the error is recoverable (auto-retry or user action can fix) */
147
+ recoverable: boolean;
148
+ /** Additional error context */
149
+ details?: Record<string, unknown>;
150
+ }
151
+
152
+ /**
153
+ * Current tracking status snapshot.
154
+ */
155
+ export interface TrackingStatus {
156
+ /** Current state of the tracking lifecycle */
157
+ state: TrackingState;
158
+ /** Whether the device currently has network connectivity */
159
+ isOnline: boolean;
160
+ /** Number of locations waiting in the offline queue */
161
+ queuedLocations: number;
162
+ /** Last known location, or null if no location has been received */
163
+ lastLocation: LocationData | null;
164
+ /** Current battery optimization mode */
165
+ batteryOptimization: 'full_accuracy' | 'low_power' | 'disabled';
166
+ }
167
+
168
+ // ─── Validation Interfaces ───────────────────────────────────────────────────
169
+
170
+ /**
171
+ * Single configuration validation error.
172
+ */
173
+ export interface ConfigError {
174
+ /** Dot-notation path to the invalid field (e.g., 'firebase.service') */
175
+ field: string;
176
+ /** Human-readable description of the validation error */
177
+ message: string;
178
+ /** Machine-readable error code */
179
+ code: string;
180
+ }
181
+
182
+ /**
183
+ * Result of configuration validation.
184
+ */
185
+ export interface ConfigValidationResult {
186
+ /** Whether the configuration is valid */
187
+ valid: boolean;
188
+ /** List of validation errors (empty if valid) */
189
+ errors: ConfigError[];
190
+ }
191
+
192
+ // ─── Utility Types ───────────────────────────────────────────────────────────
193
+
194
+ /**
195
+ * Subscription handle returned by event listener registrations.
196
+ * Call `remove()` to unsubscribe from the event.
197
+ */
198
+ export interface Subscription {
199
+ /** Unsubscribe from the event */
200
+ remove(): void;
201
+ }
202
+
203
+ // ─── Module Interface ────────────────────────────────────────────────────────
204
+
205
+ /**
206
+ * Main public API interface for the react-native-live-tracking library.
207
+ */
208
+ export interface LiveTrackingModule {
209
+ /**
210
+ * Configure the tracking library with the given parameters.
211
+ * Must be called before `start()`.
212
+ */
213
+ configure(config: TrackingConfig): Promise<void>;
214
+
215
+ /**
216
+ * Start location tracking. Requires `configure()` to have been called first.
217
+ */
218
+ start(): Promise<void>;
219
+
220
+ /**
221
+ * Stop location tracking and clean up resources.
222
+ */
223
+ stop(): Promise<void>;
224
+
225
+ /**
226
+ * Register a callback for location updates.
227
+ * Returns a Subscription that can be used to unsubscribe.
228
+ */
229
+ onLocationUpdate(callback: (location: LocationData) => void): Subscription;
230
+
231
+ /**
232
+ * Register a callback for tracking errors.
233
+ * Returns a Subscription that can be used to unsubscribe.
234
+ */
235
+ onError(callback: (error: TrackingError) => void): Subscription;
236
+
237
+ /**
238
+ * Get the current tracking status.
239
+ */
240
+ getStatus(): Promise<TrackingStatus>;
241
+
242
+ /**
243
+ * Get the number of locations currently queued for sync.
244
+ */
245
+ getQueuedLocations(): Promise<number>;
246
+
247
+ /**
248
+ * Get the number of queued locations per target path.
249
+ * Returns a record mapping each configured target path to its queued location count.
250
+ */
251
+ getQueuedLocationsByTarget(): Promise<Record<string, number>>;
252
+ }
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Distance calculation utility using the Haversine formula.
3
+ *
4
+ * The Haversine formula determines the great-circle distance between two points
5
+ * on a sphere given their longitudes and latitudes. This is used by the
6
+ * Distance/Time Matrix filter to determine if a new location update has moved
7
+ * far enough from the last known position.
8
+ *
9
+ * @packageDocumentation
10
+ */
11
+
12
+ /**
13
+ * Earth's mean radius in meters.
14
+ * Used as the sphere radius in the Haversine formula.
15
+ */
16
+ export const EARTH_RADIUS_METERS = 6371000;
17
+
18
+ /**
19
+ * Converts degrees to radians.
20
+ *
21
+ * @param degrees - Angle in degrees
22
+ * @returns Angle in radians
23
+ */
24
+ function toRadians(degrees: number): number {
25
+ return degrees * (Math.PI / 180);
26
+ }
27
+
28
+ /**
29
+ * Calculates the distance in meters between two geographic coordinates
30
+ * using the Haversine formula.
31
+ *
32
+ * The Haversine formula accounts for the curvature of the Earth and provides
33
+ * accurate results for short and medium distances. It assumes a spherical Earth
34
+ * with a mean radius of 6,371,000 meters.
35
+ *
36
+ * @param lat1 - Latitude of the first point in degrees (-90 to 90)
37
+ * @param lng1 - Longitude of the first point in degrees (-180 to 180)
38
+ * @param lat2 - Latitude of the second point in degrees (-90 to 90)
39
+ * @param lng2 - Longitude of the second point in degrees (-180 to 180)
40
+ * @returns Distance between the two points in meters
41
+ *
42
+ * @example
43
+ * ```typescript
44
+ * // Distance between Jakarta and Bandung (approximately 120 km)
45
+ * const distance = calculateDistance(-6.2088, 106.8456, -6.9175, 107.6191);
46
+ * console.log(distance); // ~120,000 meters
47
+ * ```
48
+ */
49
+ export function calculateDistance(
50
+ lat1: number,
51
+ lng1: number,
52
+ lat2: number,
53
+ lng2: number
54
+ ): number {
55
+ const dLat = toRadians(lat2 - lat1);
56
+ const dLng = toRadians(lng2 - lng1);
57
+
58
+ const a =
59
+ Math.sin(dLat / 2) * Math.sin(dLat / 2) +
60
+ Math.cos(toRadians(lat1)) *
61
+ Math.cos(toRadians(lat2)) *
62
+ Math.sin(dLng / 2) *
63
+ Math.sin(dLng / 2);
64
+
65
+ const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
66
+
67
+ return EARTH_RADIUS_METERS * c;
68
+ }
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Exponential backoff retry utilities for Firebase write operations.
3
+ *
4
+ * Used by the Firebase Sync Engine to determine delay between retry attempts
5
+ * when writes fail due to network issues or transient errors.
6
+ *
7
+ * @module utils/retry
8
+ */
9
+
10
+ /** Default base delay in milliseconds for exponential backoff */
11
+ export const DEFAULT_BASE_DELAY = 1000;
12
+
13
+ /** Default maximum jitter in milliseconds (±) to avoid thundering herd */
14
+ export const DEFAULT_MAX_JITTER = 200;
15
+
16
+ /** Maximum retry attempts for sync targets using 'set' or 'update' write methods */
17
+ export const MAX_RETRIES_SET_UPDATE = 3;
18
+
19
+ /** Maximum retry attempts for sync targets using 'push' write method */
20
+ export const MAX_RETRIES_PUSH = 5;
21
+
22
+ /**
23
+ * Calculates the delay before the next retry attempt using exponential backoff
24
+ * with random jitter.
25
+ *
26
+ * Formula: delay = baseDelay × 2^(attempt - 1) + random(-maxJitter, +maxJitter)
27
+ *
28
+ * The result is clamped to a minimum of 0 to prevent negative delays.
29
+ *
30
+ * @param attempt - The retry attempt number (1-indexed, first retry = 1)
31
+ * @param baseDelay - The base delay in milliseconds (default: 1000ms)
32
+ * @param maxJitter - The maximum jitter offset in milliseconds (default: 200ms).
33
+ * A random value between -maxJitter and +maxJitter is added to the delay.
34
+ * @returns The calculated delay in milliseconds (never negative)
35
+ *
36
+ * @example
37
+ * ```typescript
38
+ * // First retry: ~1000ms ± 200ms
39
+ * const delay1 = calculateBackoffDelay(1);
40
+ *
41
+ * // Second retry: ~2000ms ± 200ms
42
+ * const delay2 = calculateBackoffDelay(2);
43
+ *
44
+ * // Third retry: ~4000ms ± 200ms
45
+ * const delay3 = calculateBackoffDelay(3);
46
+ * ```
47
+ */
48
+ export function calculateBackoffDelay(
49
+ attempt: number,
50
+ baseDelay: number = DEFAULT_BASE_DELAY,
51
+ maxJitter: number = DEFAULT_MAX_JITTER
52
+ ): number {
53
+ const exponentialDelay = baseDelay * Math.pow(2, attempt - 1);
54
+ const jitter = (Math.random() * 2 - 1) * maxJitter;
55
+ return Math.max(0, exponentialDelay + jitter);
56
+ }
57
+
58
+ /**
59
+ * Determines whether a retry should be attempted based on the current attempt
60
+ * number and the maximum allowed retries.
61
+ *
62
+ * @param attempt - The current attempt number (1-indexed)
63
+ * @param maxRetries - The maximum number of retries allowed
64
+ * @returns `true` if the attempt is within the allowed retry limit, `false` otherwise
65
+ *
66
+ * @example
67
+ * ```typescript
68
+ * shouldRetry(1, 3); // true - first retry is allowed
69
+ * shouldRetry(3, 3); // true - third retry is allowed
70
+ * shouldRetry(4, 3); // false - exceeds max retries
71
+ * ```
72
+ */
73
+ export function shouldRetry(attempt: number, maxRetries: number): boolean {
74
+ return attempt <= maxRetries;
75
+ }