react-native-geo-activity-kit 3.0.3 → 4.0.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/README.md CHANGED
@@ -1,24 +1,43 @@
1
- # react-native-geo-activity-kit 📍 (Android Only)
1
+ # React Native Geo Activity Kit
2
2
 
3
- Battery-efficient **background location & motion tracker** for React Native.
3
+ A **production-grade, battery-efficient background tracking and activity recognition library for React Native**.
4
4
 
5
- Unlike standard GPS libraries that drain battery in hours, this library uses the **Accelerometer (Low Power)** to intelligently toggle the **GPS (High Power)**. It includes a robust **Foreground Service** implementation to ensure background execution on modern Android versions (12/13/14+).
5
+ This library is designed for **workforce management and tracking apps**. It combines **Google Fused Location Provider** with the **Activity Recognition Transition API** to create a **Smart Tracking Engine** that automatically adjusts GPS frequency based on user movement ensuring **high accuracy when moving** and **near-zero battery drain when stationary**.
6
6
 
7
7
  ---
8
8
 
9
- ## 🧠 The "Smart Switch" Logic
9
+ ## 🚀 Key Features
10
10
 
11
- ### Stationary Mode (Zero/Low Drain)
11
+ ### 🧠 Smart Battery Saver
12
+ - **Stationary Mode**: Defaults to **5-minute intervals** when the device is still.
13
+ - **High-Speed Mode**: Automatically switches to **30-second intervals** the millisecond movement is detected.
12
14
 
13
- - When the user is sitting still, the accelerometer detects no movement.
14
- - GPS is switched to _Balanced Power Mode_ (low frequency) or effectively paused.
15
- - Battery drain is negligible.
15
+ ### Zero-Latency Wake Up
16
+ - Uses **EXIT STILL** transitions to trigger GPS immediately when a user stands up or picks up the phone.
16
17
 
17
- ### Moving Mode (High Accuracy)
18
+ ### 🛡️ Foreground Service
19
+ - Runs reliably in the background with a persistent notification.
20
+ - Fully compatible with **Android 14** foreground service requirements.
18
21
 
19
- - When movement is detected (walking, driving), the library wakes up.
20
- - GPS switches to _High Accuracy Mode_.
21
- - Locations are logged at your configured interval.
22
+ ### 🔔 Local Notification System
23
+ - **Geofence Alerts**
24
+ - **Entry (Green)** and **Exit (Red)** pre-styled notifications.
25
+ - **Generic Alerts**
26
+ - Trigger custom notifications for background push messages or updates.
27
+
28
+ ### 🏃 Motion Detection
29
+ Detects the following activities:
30
+ - STILL
31
+ - WALKING
32
+ - RUNNING
33
+ - IN_VEHICLE
34
+ - ON_BICYCLE
35
+
36
+ ### 🛰️ GPS Status Monitoring
37
+ - Detects when the user physically toggles the **System Location (GPS)** switch ON/OFF.
38
+
39
+ ### 🚫 Fake GPS Detection
40
+ - Every location update includes an `is_mock` flag to detect spoofed locations.
22
41
 
23
42
  ---
24
43
 
@@ -32,132 +51,156 @@ yarn add react-native-geo-activity-kit
32
51
 
33
52
  ---
34
53
 
35
- ## 🤖 Android Setup
54
+ ## ⚙️ Android Configuration (Required)
55
+
56
+ Because this library runs continuously in the background, **AndroidManifest.xml configuration is mandatory**.
36
57
 
37
- ### 1. Update `AndroidManifest.xml`
58
+ ### 1️⃣ Add Permissions
38
59
 
39
- Open `android/app/src/main/AndroidManifest.xml` and add the following permissions inside the `<manifest>` tag:
60
+ Open `android/app/src/main/AndroidManifest.xml` and add:
40
61
 
41
62
  ```xml
42
- <manifest xmlns:android="http://schemas.android.com/apk/res/android">
63
+ <manifest ...>
43
64
 
44
- <!-- Location -->
45
65
  <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
46
66
  <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
47
67
  <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
48
68
 
49
- <!-- Foreground Service (Crucial for Background Tasks) -->
50
69
  <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
51
70
  <uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
52
-
53
- <!-- Notifications (Android 13+) -->
71
+ <uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
72
+ <uses-permission android:name="android.permission.WAKE_LOCK" />
54
73
  <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
55
74
 
56
- <!-- System -->
57
- <uses-permission android:name="android.permission.WAKE_LOCK" />
75
+ <application ...>
76
+ ```
58
77
 
59
- </manifest>
78
+ ---
79
+
80
+ ### 2️⃣ Register Services & Receivers
81
+
82
+ Inside the `<application>` tag:
83
+
84
+ ```xml
85
+ <application ...>
86
+
87
+ <service
88
+ android:name="com.rngeoactivitykit.TrackingService"
89
+ android:enabled="true"
90
+ android:exported="false"
91
+ android:foregroundServiceType="location" />
92
+
93
+ <receiver
94
+ android:name="com.rngeoactivitykit.ActivityTransitionReceiver"
95
+ android:exported="false"
96
+ android:permission="com.google.android.gms.permission.ACTIVITY_RECOGNITION">
97
+ <intent-filter>
98
+ <action android:name="com.rngeoactivitykit.ACTION_PROCESS_ACTIVITY_TRANSITIONS" />
99
+ </intent-filter>
100
+ </receiver>
101
+
102
+ </application>
60
103
  ```
61
104
 
62
- > 💡 You may not strictly need `ACCESS_BACKGROUND_LOCATION` if you always run as a Foreground Service, but it's included here for maximum compatibility. Adjust based on your Play Store policy needs.
105
+ ---
106
+
107
+ ## 📖 Usage Guide
108
+
109
+ ### 1️⃣ Request Permissions
110
+
111
+ ```javascript
112
+ import { PermissionsAndroid, Platform } from 'react-native';
113
+
114
+ async function requestPermissions() {
115
+ if (Platform.OS === 'android') {
116
+ await PermissionsAndroid.requestMultiple([
117
+ PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
118
+ PermissionsAndroid.PERMISSIONS.ACCESS_BACKGROUND_LOCATION,
119
+ PermissionsAndroid.PERMISSIONS.ACTIVITY_RECOGNITION,
120
+ PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS,
121
+ ]);
122
+ }
123
+ }
124
+ ```
63
125
 
64
126
  ---
65
127
 
66
- ## 🚀 Usage Guide
128
+ ### 2️⃣ Start the Smart Tracking Service
129
+
130
+ ```javascript
131
+ import GeoKit from 'react-native-geo-activity-kit';
132
+
133
+ const startTracking = async () => {
134
+ await GeoKit.startForegroundService(
135
+ 'Workforce Tracking',
136
+ 'Monitoring location in background...',
137
+ 9991
138
+ );
67
139
 
68
- ### 1. Request Permissions
140
+ await GeoKit.startMotionDetector();
141
+ };
142
+ ```
69
143
 
70
- Before starting, ensure you request the necessary runtime permissions (Location & Notifications).
144
+ ---
71
145
 
72
- ### 2. Implementation Example
146
+ ### 3️⃣ Listen for Data
73
147
 
74
- ```tsx
75
- import React, { useEffect, useState } from 'react';
76
- import { View, Text, Button, PermissionsAndroid, Platform } from 'react-native';
148
+ ```javascript
149
+ import { useEffect } from 'react';
77
150
  import GeoKit from 'react-native-geo-activity-kit';
78
151
 
79
- const App = () => {
80
- const [status, setStatus] = useState('Off');
81
- const [logs, setLogs] = useState<string[]>([]);
82
-
83
- useEffect(() => {
84
- // 1. Motion State Listener (STATIONARY vs MOVING)
85
- const motionSub = GeoKit.addMotionListener((event) => {
86
- console.log('Motion State:', event.state);
87
- setStatus(event.state);
88
- });
89
-
90
- // 2. Location Listener
91
- const locationSub = GeoKit.addLocationLogListener((loc) => {
92
- console.log('Location:', loc.latitude, loc.longitude);
93
- setLogs((prev) => [
94
- `${loc.timestamp}: ${loc.latitude}, ${loc.longitude}`,
95
- ...prev,
96
- ]);
97
- });
98
-
99
- // 3. Error Listener
100
- const errorSub = GeoKit.addLocationErrorListener((err) => {
101
- console.error('GeoKit Error:', err.message);
102
- });
103
-
104
- return () => {
105
- motionSub.remove();
106
- locationSub.remove();
107
- errorSub.remove();
108
- };
109
- }, []);
110
-
111
- const startService = async () => {
112
- try {
113
- // 1. Ask for permissions first (Standard Android Code)
114
- if (Platform.OS === 'android') {
115
- await PermissionsAndroid.requestMultiple([
116
- PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
117
- PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS,
118
- ]);
119
- }
120
-
121
- // 2. Start the Foreground Service (REQUIRED for background persistence)
122
- await GeoKit.startForegroundService(
123
- 'Location Tracker',
124
- 'Tracking your trips in the background...'
125
- );
126
-
127
- // 3. Start the Motion Detector (This activates the Smart Switch)
128
- // Threshold 0.8 is recommended for walking/driving detection.
129
- await GeoKit.startMotionDetector(0.8);
130
-
131
- setStatus('Started');
132
- } catch (e) {
133
- console.error(e);
152
+ useEffect(() => {
153
+ const locationSub = GeoKit.addLocationLogListener((event) => {
154
+ console.log(event.latitude, event.longitude);
155
+
156
+ if (event.is_mock) {
157
+ console.warn('FAKE GPS DETECTED');
134
158
  }
135
- };
159
+ });
160
+
161
+ const motionSub = GeoKit.addMotionListener((event) => {
162
+ console.log(event.activity, event.isMoving);
163
+ });
136
164
 
137
- const stopService = async () => {
138
- await GeoKit.stopMotionDetector();
139
- await GeoKit.stopForegroundService();
140
- setStatus('Stopped');
165
+ const gpsSub = GeoKit.addGpsStatusListener((event) => {
166
+ if (!event.enabled) {
167
+ console.error('GPS turned OFF');
168
+ }
169
+ });
170
+
171
+ return () => {
172
+ locationSub.remove();
173
+ motionSub.remove();
174
+ gpsSub.remove();
141
175
  };
176
+ }, []);
177
+ ```
142
178
 
143
- return (
144
- <View style={{ padding: 20 }}>
145
- <Text style={{ fontSize: 18, marginBottom: 10 }}>Status: {status}</Text>
146
- <Button title="Start Tracking" onPress={startService} />
147
- <View style={{ height: 10 }} />
148
- <Button title="Stop Tracking" color="red" onPress={stopService} />
149
-
150
- <Text style={{ marginTop: 20, fontWeight: 'bold' }}>Logs:</Text>
151
- {logs.map((l, i) => (
152
- <Text key={i} style={{ fontSize: 10 }}>
153
- {l}
154
- </Text>
155
- ))}
156
- </View>
157
- );
158
- };
179
+ ---
180
+
181
+ ## 🔔 Notification API
182
+
183
+ ### Geofence Alerts
184
+
185
+ ```javascript
186
+ GeoKit.fireGeofenceAlert('OUT', 'John Doe');
187
+ GeoKit.fireGeofenceAlert('IN', 'John Doe');
188
+ ```
189
+
190
+ ---
191
+
192
+ ### Generic Alerts
193
+
194
+ ```javascript
195
+ GeoKit.fireGenericAlert('New Task', 'You have a new site visit.', 1001);
196
+ ```
197
+
198
+ ---
199
+
200
+ ### Cancel Notification
159
201
 
160
- export default App;
202
+ ```javascript
203
+ GeoKit.cancelGenericAlert(1001);
161
204
  ```
162
205
 
163
206
  ---
@@ -166,39 +209,53 @@ export default App;
166
209
 
167
210
  ### Service Control
168
211
 
169
- These methods control the sticky notification that keeps your app alive in the background.
212
+ | Method | Description |
213
+ |------|------------|
214
+ | startForegroundService | Starts persistent background service |
215
+ | stopForegroundService | Stops service |
216
+ | updateServiceNotification | Updates notification text |
217
+
218
+ ---
219
+
220
+ ### Motion & Location
170
221
 
171
- | Method | Description |
172
- | ----------------------------------- | ------------------------------------------------------------------- |
173
- | `startForegroundService(title, body)` | Starts the Android Foreground Service. Required for background tracking. |
174
- | `stopForegroundService()` | Stops the service and removes the notification. |
175
- | `updateServiceNotification(title, body)` | Updates the text of the running notification without restarting the service. |
222
+ | Method | Description |
223
+ |------|------------|
224
+ | startMotionDetector | Enables activity recognition |
225
+ | stopMotionDetector | Stops motion detection |
226
+ | setLocationUpdateInterval | Manual GPS override |
227
+ | registerGpsListener | Monitor GPS toggle |
176
228
 
177
229
  ---
178
230
 
179
- ### Configuration & Sensors
231
+ ## 📱 Supported Platforms
180
232
 
181
- | Method | Default | Description |
182
- | --------------------------------- | ------- | --------------------------------------------------------------------------- |
183
- | `startMotionDetector(threshold)` | `0.8` | Starts the accelerometer and GPS logic. `threshold` = force (G) to mark MOVING. |
184
- | `stopMotionDetector()` | - | Stops the sensors and GPS updates. |
185
- | `setUpdateInterval(ms)` | `100` | How often the accelerometer checks for force, in milliseconds. |
186
- | `setLocationUpdateInterval(ms)` | `90000` | How often GPS logs are captured when MOVING (90 seconds default). |
187
- | `setStabilityThresholds(start, stop)` | `20, 3000` | `start`: consecutive checks > threshold to become MOVING; `stop`: consecutive checks < threshold to become STATIONARY. |
233
+ - **Android**: SDK 29 (Android 10) to SDK 34 (Android 14)
234
+ - **iOS**: Not supported
188
235
 
189
236
  ---
190
237
 
191
- ### 🔔 Native Notifications
238
+ ## 🧩 Troubleshooting
192
239
 
193
- These methods trigger local notifications directly from the native module, ensuring they fire even if the JS thread is busy or backgrounded.
240
+ ### Service crashes on start
241
+ Ensure notification ID is an integer.
194
242
 
195
- | Method | Description |
196
- | ------------------------------ | --------------------------------------------------------------------------- |
197
- | `fireGenericAlert(title, body, id)` | Triggers a standard push notification immediately. `id` is a unique number. |
198
- | `fireGeofenceAlert(type, userName)` | Triggers a pre-formatted Geofence alert. `type` must be `"IN"` or `"OUT"`. |
199
- | `cancelGenericAlert(id)` | Cancels a specific notification by its ID. |
243
+ ### Location stops after 20 minutes
244
+ This is expected behavior in stationary mode.
245
+
246
+ ### Fake GPS not detected
247
+ Ensure a mock location app is actively spoofing.
200
248
 
201
249
  ---
202
250
 
203
251
  ## 📄 License
252
+
204
253
  MIT
254
+
255
+
256
+ ---
257
+
258
+ ## 👤 Author
259
+
260
+ **Kartikey Mishra**
261
+ Creator & Maintainer of React Native Geo Activity Kit
@@ -7,6 +7,7 @@ import android.util.Log
7
7
  import com.facebook.react.bridge.Arguments
8
8
  import com.facebook.react.bridge.ReactApplicationContext
9
9
  import com.facebook.react.modules.core.DeviceEventManagerModule
10
+ import com.google.android.gms.location.ActivityTransition
10
11
  import com.google.android.gms.location.ActivityTransitionResult
11
12
  import com.google.android.gms.location.DetectedActivity
12
13
 
@@ -17,41 +18,42 @@ class ActivityTransitionReceiver : BroadcastReceiver() {
17
18
  val result = ActivityTransitionResult.extractResult(intent) ?: return
18
19
 
19
20
  for (event in result.transitionEvents) {
20
- val activityType = toActivityString(event.activityType)
21
- val transitionType = toTransitionString(event.transitionType)
21
+ val activityTypeStr = toActivityString(event.activityType)
22
+ val transitionTypeStr = toTransitionString(event.transitionType)
22
23
 
23
- Log.d("ActivityReceiver", "🏃 Motion Event: $activityType ($transitionType)")
24
+ Log.d("ActivityReceiver", "🏃 Motion Event: $activityTypeStr ($transitionTypeStr)")
24
25
 
25
- val isMoving = when(event.activityType) {
26
- DetectedActivity.WALKING,
27
- DetectedActivity.RUNNING,
28
- DetectedActivity.ON_BICYCLE,
29
- DetectedActivity.IN_VEHICLE -> true
30
- else -> false
31
- }
26
+ // PROD GRADE LOGIC:
27
+ // We are "Moving" if we ENTER a moving state OR if we EXIT the Still state.
28
+ // Exiting "Still" is the fastest way to detect movement start.
29
+ val isMoving = (event.transitionType == ActivityTransition.ACTIVITY_TRANSITION_ENTER &&
30
+ (event.activityType == DetectedActivity.WALKING ||
31
+ event.activityType == DetectedActivity.IN_VEHICLE ||
32
+ event.activityType == DetectedActivity.ON_BICYCLE ||
33
+ event.activityType == DetectedActivity.RUNNING)) ||
34
+ (event.activityType == DetectedActivity.STILL && event.transitionType == ActivityTransition.ACTIVITY_TRANSITION_EXIT)
32
35
 
33
- // --- NEW: DIRECT NATIVE CONTROL (No JS required) ---
34
36
  try {
35
37
  if (isMoving) {
36
- // Fast Updates (30s) when moving
38
+ // User is moving: Speed up to 30 seconds
37
39
  LocationHelper.shared?.setLocationUpdateInterval(30000)
38
- } else {
39
- // Slow Updates (5 mins) when STILL - BATTERY SAVER
40
- LocationHelper.shared?.setLocationUpdateInterval(5 * 60 * 1000)
40
+ } else if (event.activityType == DetectedActivity.STILL && event.transitionType == ActivityTransition.ACTIVITY_TRANSITION_ENTER) {
41
+ // User stopped: Slow down to 5 minutes
42
+ LocationHelper.shared?.setLocationUpdateInterval(300000)
41
43
  }
42
44
  } catch (e: Exception) {
43
- Log.e("ActivityReceiver", "Failed to update location interval directly")
45
+ Log.e("ActivityReceiver", "Failed to update location interval directly: ${e.message}")
44
46
  }
45
- // ---------------------------------------------------
46
47
 
48
+ // Send to JS
47
49
  try {
48
50
  val reactContext = context.applicationContext as? ReactApplicationContext
49
51
  ?: TrackingService.instance?.application as? ReactApplicationContext
50
52
 
51
53
  if (reactContext != null && reactContext.hasActiveCatalystInstance()) {
52
54
  val params = Arguments.createMap()
53
- params.putString("activity", activityType)
54
- params.putString("transition", transitionType)
55
+ params.putString("activity", activityTypeStr)
56
+ params.putString("transition", transitionTypeStr)
55
57
  params.putBoolean("isMoving", isMoving)
56
58
  params.putString("state", if (isMoving) "MOVING" else "STATIONARY")
57
59
 
@@ -80,8 +82,8 @@ class ActivityTransitionReceiver : BroadcastReceiver() {
80
82
 
81
83
  private fun toTransitionString(type: Int): String {
82
84
  return when (type) {
83
- com.google.android.gms.location.ActivityTransition.ACTIVITY_TRANSITION_ENTER -> "ENTER"
84
- com.google.android.gms.location.ActivityTransition.ACTIVITY_TRANSITION_EXIT -> "EXIT"
85
+ ActivityTransition.ACTIVITY_TRANSITION_ENTER -> "ENTER"
86
+ ActivityTransition.ACTIVITY_TRANSITION_EXIT -> "EXIT"
85
87
  else -> "UNKNOWN"
86
88
  }
87
89
  }
@@ -3,6 +3,7 @@ package com.rngeoactivitykit
3
3
  import android.Manifest
4
4
  import android.annotation.SuppressLint
5
5
  import android.content.pm.PackageManager
6
+ import android.os.Build
6
7
  import android.os.Looper
7
8
  import android.util.Log
8
9
  import androidx.core.content.ContextCompat
@@ -16,12 +17,10 @@ import java.util.TimeZone
16
17
 
17
18
  class LocationHelper(private val context: ReactApplicationContext) {
18
19
 
19
- // --- ADDED THIS BLOCK (Singleton Pattern) ---
20
20
  companion object {
21
21
  @SuppressLint("StaticFieldLeak")
22
22
  var shared: LocationHelper? = null
23
23
  }
24
- // --------------------------------------------
25
24
 
26
25
  private val fusedLocationClient: FusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(context)
27
26
  private var locationCallback: LocationCallback
@@ -35,13 +34,15 @@ class LocationHelper(private val context: ReactApplicationContext) {
35
34
  }
36
35
 
37
36
  init {
38
- // --- ADDED THIS LINE ---
39
- shared = this
40
- // -----------------------
37
+ shared = this
41
38
 
39
+ // PROD GRADE: Start with 5 Minutes (Battery Saver)
40
+ // We assume the user is stationary until the Motion Detector proves otherwise.
41
+ val defaultInterval = 300000L // 5 Minutes
42
+
42
43
  locationRequest = LocationRequest.create().apply {
43
- interval = 30000
44
- fastestInterval = 30000
44
+ interval = defaultInterval
45
+ fastestInterval = defaultInterval
45
46
  priority = Priority.PRIORITY_BALANCED_POWER_ACCURACY
46
47
  }
47
48
 
@@ -50,10 +51,13 @@ class LocationHelper(private val context: ReactApplicationContext) {
50
51
  locationResult.lastLocation ?: return
51
52
  val location = locationResult.lastLocation!!
52
53
 
54
+ // PROD GRADE: Filter noise. If accuracy is very bad (>200m), ignore it to save processing.
55
+ if (location.accuracy > 200) return
56
+
53
57
  Log.d("LocationHelper", "📍 New Location: ${location.latitude}, ${location.longitude} (Acc: ${location.accuracy}m)")
54
58
 
55
59
  var isMock = false
56
- if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
60
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
57
61
  isMock = location.isMock
58
62
  } else {
59
63
  isMock = location.isFromMockProvider
@@ -72,15 +76,22 @@ class LocationHelper(private val context: ReactApplicationContext) {
72
76
  }
73
77
 
74
78
  fun setLocationUpdateInterval(intervalMs: Long) {
75
- val newPriority = if (intervalMs < 10000) {
79
+ val newPriority = if (intervalMs < 60000) {
80
+ // High accuracy for intervals < 1 min
76
81
  Priority.PRIORITY_HIGH_ACCURACY
77
82
  } else {
83
+ // Balanced power for stationary/slow updates
78
84
  Priority.PRIORITY_BALANCED_POWER_ACCURACY
79
85
  }
80
86
  updateLocationRequest(newPriority, intervalMs)
81
87
  }
82
88
 
83
89
  fun updateLocationRequest(priority: Int, intervalMs: Long) {
90
+ // PROD GRADE: Prevent restarting the hardware if nothing changed
91
+ if (locationRequest.interval == intervalMs && locationRequest.priority == priority && isLocationClientRunning) {
92
+ return
93
+ }
94
+
84
95
  Log.d("LocationHelper", "🔄 Updating Request: Priority=$priority, Interval=${intervalMs}ms")
85
96
 
86
97
  locationRequest = LocationRequest.create().apply {
@@ -93,7 +104,7 @@ class LocationHelper(private val context: ReactApplicationContext) {
93
104
  stopLocationUpdates()
94
105
  startLocationUpdates()
95
106
  } else {
96
- startLocationUpdates()
107
+ // Do not auto-start if it wasn't running. Wait for explicit start.
97
108
  }
98
109
  }
99
110
 
@@ -18,28 +18,26 @@ class MotionDetector(private val context: ReactApplicationContext) {
18
18
  private val activityClient = ActivityRecognition.getClient(context)
19
19
  private var pendingIntent: PendingIntent? = null
20
20
 
21
- // We define which activities trigger a wake-up
21
+ // Monitor Enter AND Exit for precise state management
22
22
  private val transitions = listOf(
23
- // Detect when user STOPS moving
24
- ActivityTransition.Builder()
25
- .setActivityType(DetectedActivity.STILL)
26
- .setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_ENTER)
27
- .build(),
23
+ // STILL
24
+ ActivityTransition.Builder().setActivityType(DetectedActivity.STILL).setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_ENTER).build(),
25
+ ActivityTransition.Builder().setActivityType(DetectedActivity.STILL).setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_EXIT).build(),
28
26
 
29
- // Detect when user STARTS Walking
30
- ActivityTransition.Builder()
31
- .setActivityType(DetectedActivity.WALKING)
32
- .setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_ENTER)
33
- .build(),
27
+ // WALKING
28
+ ActivityTransition.Builder().setActivityType(DetectedActivity.WALKING).setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_ENTER).build(),
29
+ ActivityTransition.Builder().setActivityType(DetectedActivity.WALKING).setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_EXIT).build(),
34
30
 
35
- // Detect when user STARTS Driving
36
- ActivityTransition.Builder()
37
- .setActivityType(DetectedActivity.IN_VEHICLE)
38
- .setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_ENTER)
39
- .build()
31
+ // VEHICLE
32
+ ActivityTransition.Builder().setActivityType(DetectedActivity.IN_VEHICLE).setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_ENTER).build(),
33
+ ActivityTransition.Builder().setActivityType(DetectedActivity.IN_VEHICLE).setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_EXIT).build(),
34
+
35
+ // RUNNING
36
+ ActivityTransition.Builder().setActivityType(DetectedActivity.RUNNING).setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_ENTER).build(),
37
+ ActivityTransition.Builder().setActivityType(DetectedActivity.RUNNING).setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_EXIT).build()
40
38
  )
41
39
 
42
- @SuppressLint("MissingPermission") // Checked in start()
40
+ @SuppressLint("MissingPermission")
43
41
  fun start(): Boolean {
44
42
  if (!hasPermission()) {
45
43
  return false
@@ -13,12 +13,8 @@ class SensorModule(reactContext: ReactApplicationContext) : ReactContextBaseJava
13
13
 
14
14
  private val notificationHelper = NotificationHelper(reactContext)
15
15
  private val locationHelper = LocationHelper(reactContext)
16
-
17
- // NEW: MotionDetector no longer needs a callback because
18
- // the ActivityTransitionReceiver handles the events.
19
16
  private val motionDetector = MotionDetector(reactContext)
20
17
 
21
- // --- GPS STATUS RECEIVER (Detects if user turns off Location Toggle) ---
22
18
  private val gpsStatusReceiver = object : BroadcastReceiver() {
23
19
  override fun onReceive(context: Context?, intent: Intent?) {
24
20
  if (intent?.action == LocationManager.PROVIDERS_CHANGED_ACTION) {
@@ -42,22 +38,18 @@ class SensorModule(reactContext: ReactApplicationContext) : ReactContextBaseJava
42
38
  }
43
39
 
44
40
  init {
45
- // Register GPS Status Receiver
46
41
  val filter = IntentFilter(LocationManager.PROVIDERS_CHANGED_ACTION)
47
42
  reactContext.registerReceiver(gpsStatusReceiver, filter)
48
43
  }
49
44
 
50
- // --- SERVICE CONTROL ---
51
-
52
45
  @ReactMethod
53
- // ✅ ADD "id: Int" here. Now it matches JavaScript's 3 arguments.
54
46
  fun startForegroundService(title: String, body: String, id: Int, promise: Promise) {
55
47
  try {
56
48
  val intent = Intent(reactApplicationContext, TrackingService::class.java)
57
49
  intent.action = TrackingService.ACTION_START
58
50
  intent.putExtra("title", title)
59
51
  intent.putExtra("body", body)
60
- // intent.putExtra("id", id) // Optional: Pass it if you want to use it later
52
+ // intent.putExtra("id", id) // Pass ID if you want to make the persistent notification dynamic later
61
53
 
62
54
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
63
55
  reactApplicationContext.startForegroundService(intent)
@@ -75,7 +67,7 @@ class SensorModule(reactContext: ReactApplicationContext) : ReactContextBaseJava
75
67
  try {
76
68
  val intent = Intent(reactApplicationContext, TrackingService::class.java)
77
69
  intent.action = TrackingService.ACTION_STOP
78
- reactApplicationContext.startService(intent) // Triggers stop logic in Service
70
+ reactApplicationContext.startService(intent)
79
71
  promise.resolve(true)
80
72
  } catch (e: Exception) {
81
73
  promise.reject("STOP_SERVICE_FAILED", e.message)
@@ -101,18 +93,13 @@ class SensorModule(reactContext: ReactApplicationContext) : ReactContextBaseJava
101
93
  }
102
94
  }
103
95
 
104
- // --- MOTION DETECTOR (Updated for Activity Recognition) ---
105
-
106
96
  @ReactMethod
107
97
  fun startMotionDetector(threshold: Double, promise: Promise) {
108
- // NOTE: 'threshold' is ignored now because Activity Recognition
109
- // handles sensitivity automatically using Machine Learning.
110
98
  try {
111
99
  val success = motionDetector.start()
112
100
  if (success) {
113
101
  promise.resolve(true)
114
102
  } else {
115
- // This usually means Permissions are missing
116
103
  promise.reject("PERMISSION_DENIED", "ACTIVITY_RECOGNITION permission is required")
117
104
  }
118
105
  } catch (e: Exception) {
@@ -130,8 +117,6 @@ class SensorModule(reactContext: ReactApplicationContext) : ReactContextBaseJava
130
117
  }
131
118
  }
132
119
 
133
- // --- LOCATION CONTROL ---
134
-
135
120
  @ReactMethod
136
121
  fun setLocationUpdateInterval(intervalMs: Int) {
137
122
  try {
@@ -141,8 +126,6 @@ class SensorModule(reactContext: ReactApplicationContext) : ReactContextBaseJava
141
126
  }
142
127
  }
143
128
 
144
- // --- ALERTS & NOTIFICATIONS ---
145
-
146
129
  @ReactMethod
147
130
  fun fireGeofenceAlert(type: String, userName: String, promise: Promise) {
148
131
  try {
@@ -175,7 +158,6 @@ class SensorModule(reactContext: ReactApplicationContext) : ReactContextBaseJava
175
158
 
176
159
  @ReactMethod
177
160
  fun registerGpsListener(promise: Promise) {
178
- // Already registered in init, just checking permission state
179
161
  try {
180
162
  val locationManager = reactApplicationContext.getSystemService(Context.LOCATION_SERVICE) as LocationManager
181
163
  val isEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)
@@ -185,17 +167,11 @@ class SensorModule(reactContext: ReactApplicationContext) : ReactContextBaseJava
185
167
  }
186
168
  }
187
169
 
188
- // --- EVENT EMITTER BOILERPLATE ---
189
-
190
170
  @ReactMethod
191
- fun addListener(eventName: String) {
192
- // Required for NativeEventEmitter
193
- }
171
+ fun addListener(eventName: String) {}
194
172
 
195
173
  @ReactMethod
196
- fun removeListeners(count: Int) {
197
- // Required for NativeEventEmitter
198
- }
174
+ fun removeListeners(count: Int) {}
199
175
 
200
176
  private fun sendEvent(eventName: String, params: Any?) {
201
177
  try {
@@ -209,22 +185,12 @@ class SensorModule(reactContext: ReactApplicationContext) : ReactContextBaseJava
209
185
  }
210
186
  }
211
187
 
212
- // --- CLEANUP ---
213
-
214
188
  override fun onCatalystInstanceDestroy() {
215
189
  super.onCatalystInstanceDestroy()
216
190
  try {
217
- // 1. Unregister Receivers
218
191
  reactApplicationContext.unregisterReceiver(gpsStatusReceiver)
219
-
220
- // 2. Stop Sensors
221
192
  motionDetector.stop()
222
193
  locationHelper.stopLocationUpdates()
223
-
224
- // 3. Stop Service (Optional: Depending on whether you want it to persist after app kill)
225
- // Ideally, we DO NOT stop the service here if we want it running in background.
226
- // But we do clean up listeners.
227
-
228
194
  } catch (e: Exception) {
229
195
  e.printStackTrace()
230
196
  }
@@ -38,11 +38,15 @@ class TrackingService : Service() {
38
38
  try {
39
39
  val powerManager = getSystemService(POWER_SERVICE) as PowerManager
40
40
  wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "GeoKit::TrackingLock")
41
+ wakeLock?.setReferenceCounted(false) // PROD: Ensure we don't over-release
41
42
  wakeLock?.acquire()
42
43
  Log.d("TrackingService", "🔒 WakeLock Acquired (Permanent)")
43
44
  } catch (e: Exception) {
44
45
  Log.e("TrackingService", "❌ Failed to acquire WakeLock: ${e.message}")
45
46
  }
47
+
48
+ // Start Location Updates on Create via Helper
49
+ LocationHelper.shared?.startLocationUpdates()
46
50
  }
47
51
 
48
52
  override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
@@ -54,6 +58,9 @@ class TrackingService : Service() {
54
58
  val title = intent.getStringExtra("title") ?: "Location Active"
55
59
  val body = intent.getStringExtra("body") ?: "Monitoring in background..."
56
60
  startForegroundService(title, body)
61
+
62
+ // Ensure sensors are running
63
+ LocationHelper.shared?.startLocationUpdates()
57
64
  }
58
65
  ACTION_STOP -> {
59
66
  Log.d("TrackingService", "⏹️ Action: STOP")
@@ -83,6 +90,7 @@ class TrackingService : Service() {
83
90
 
84
91
  private fun stopForegroundService() {
85
92
  try {
93
+ LocationHelper.shared?.stopLocationUpdates()
86
94
  stopForeground(true)
87
95
  stopSelf()
88
96
  } catch (e: Exception) {
@@ -1,8 +1,6 @@
1
1
  "use strict";
2
2
 
3
3
  import { NativeModules, NativeEventEmitter, Platform } from 'react-native';
4
- // ✅ Fixed: Modern Type & Correct Import
5
-
6
4
  const LINKING_ERROR = `The package 'react-native-geo-activity-kit' doesn't seem to be linked. Make sure: \n\n` + Platform.select({
7
5
  ios: "- You have run 'pod install'\n",
8
6
  default: ''
@@ -13,33 +11,36 @@ const RNSensorModule = NativeModules.RNSensorModule ? NativeModules.RNSensorModu
13
11
  }
14
12
  });
15
13
  const emitter = new NativeEventEmitter(RNSensorModule);
14
+ export const startForegroundService = (title, body, id) => RNSensorModule.startForegroundService(title, body, id);
15
+ export const stopForegroundService = () => RNSensorModule.stopForegroundService();
16
+ export const updateServiceNotification = (title, body) => RNSensorModule.updateServiceNotification(title, body);
17
+ export const startMotionDetector = (confidence = 75) => RNSensorModule.startMotionDetector(confidence);
18
+ export const stopMotionDetector = () => RNSensorModule.stopMotionDetector();
19
+ export const setLocationUpdateInterval = ms => RNSensorModule.setLocationUpdateInterval(ms);
20
+ export const fireGeofenceAlert = (type, userName) => RNSensorModule.fireGeofenceAlert(type, userName);
21
+ export const fireGenericAlert = (title, body, id) => RNSensorModule.fireGenericAlert(title, body, id);
22
+ export const cancelGenericAlert = id => RNSensorModule.cancelGenericAlert(id);
23
+ export const registerGpsListener = () => RNSensorModule.registerGpsListener();
24
+ export const addGpsStatusListener = cb => emitter.addListener('onGpsStatusChanged', event => cb(event));
25
+ export const addMotionListener = cb => emitter.addListener('onMotionStateChanged', event => cb(event));
26
+ export const addLocationLogListener = cb => emitter.addListener('onLocationLog', event => cb(event));
27
+ export const addLocationErrorListener = cb => emitter.addListener('onLocationError', event => cb(event));
16
28
 
17
- // 2. Define Types
18
-
19
- // 3. Export the Object
29
+ // Default export for backward compatibility
20
30
  export default {
21
- // --- Service Control ---
22
- startForegroundService: (title, body, id) => RNSensorModule.startForegroundService(title, body, id),
23
- stopForegroundService: () => RNSensorModule.stopForegroundService(),
24
- updateServiceNotification: (title, body) => RNSensorModule.updateServiceNotification(title, body),
25
- // --- Motion & Intelligence ---
26
- startMotionDetector: (confidence = 75) => RNSensorModule.startMotionDetector(confidence),
27
- stopMotionDetector: () => RNSensorModule.stopMotionDetector(),
28
- // --- Location Control ---
29
- setLocationUpdateInterval: ms => RNSensorModule.setLocationUpdateInterval(ms),
30
- // --- Alerts & Notifications ---
31
- fireGeofenceAlert: (type, userName) => RNSensorModule.fireGeofenceAlert(type, userName),
32
- fireGenericAlert: (title, body, id) => RNSensorModule.fireGenericAlert(title, body, id),
33
- cancelGenericAlert: id => RNSensorModule.cancelGenericAlert(id),
34
- // --- GPS Status ---
35
- registerGpsListener: () => RNSensorModule.registerGpsListener(),
36
- // --- Event Listeners ---
37
- // Note: We cast event to 'any' to avoid TS strict object checks,
38
- // and return EventSubscription (modern) instead of EmitterSubscription (deprecated).
39
-
40
- addGpsStatusListener: cb => emitter.addListener('onGpsStatusChanged', event => cb(event)),
41
- addMotionListener: cb => emitter.addListener('onMotionStateChanged', event => cb(event)),
42
- addLocationLogListener: cb => emitter.addListener('onLocationLog', event => cb(event)),
43
- addLocationErrorListener: cb => emitter.addListener('onLocationError', event => cb(event))
31
+ startForegroundService,
32
+ stopForegroundService,
33
+ updateServiceNotification,
34
+ startMotionDetector,
35
+ stopMotionDetector,
36
+ setLocationUpdateInterval,
37
+ fireGeofenceAlert,
38
+ fireGenericAlert,
39
+ cancelGenericAlert,
40
+ registerGpsListener,
41
+ addGpsStatusListener,
42
+ addMotionListener,
43
+ addLocationLogListener,
44
+ addLocationErrorListener
44
45
  };
45
46
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["NativeModules","NativeEventEmitter","Platform","LINKING_ERROR","select","ios","default","RNSensorModule","Proxy","get","Error","emitter","startForegroundService","title","body","id","stopForegroundService","updateServiceNotification","startMotionDetector","confidence","stopMotionDetector","setLocationUpdateInterval","ms","fireGeofenceAlert","type","userName","fireGenericAlert","cancelGenericAlert","registerGpsListener","addGpsStatusListener","cb","addListener","event","addMotionListener","addLocationLogListener","addLocationErrorListener"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,SAASA,aAAa,EAAEC,kBAAkB,EAAEC,QAAQ,QAAQ,cAAc;AACnB;;AAEvD,MAAMC,aAAa,GACjB,wFAAwF,GACxFD,QAAQ,CAACE,MAAM,CAAC;EAAEC,GAAG,EAAE,gCAAgC;EAAEC,OAAO,EAAE;AAAG,CAAC,CAAC,GACvE,sDAAsD,GACtD,+BAA+B;AAEjC,MAAMC,cAAc,GAAGP,aAAa,CAACO,cAAc,GAC/CP,aAAa,CAACO,cAAc,GAC5B,IAAIC,KAAK,CACT,CAAC,CAAC,EACF;EACEC,GAAGA,CAAA,EAAG;IACJ,MAAM,IAAIC,KAAK,CAACP,aAAa,CAAC;EAChC;AACF,CACF,CAAC;AAEH,MAAMQ,OAAO,GAAG,IAAIV,kBAAkB,CAACM,cAAc,CAAC;;AAEtD;;AAoBA;AACA,eAAe;EACb;EACAK,sBAAsB,EAAEA,CAACC,KAAa,EAAEC,IAAY,EAAEC,EAAU,KAC9DR,cAAc,CAACK,sBAAsB,CAACC,KAAK,EAAEC,IAAI,EAAEC,EAAE,CAAC;EAExDC,qBAAqB,EAAEA,CAAA,KACrBT,cAAc,CAACS,qBAAqB,CAAC,CAAC;EAExCC,yBAAyB,EAAEA,CAACJ,KAAa,EAAEC,IAAY,KACrDP,cAAc,CAACU,yBAAyB,CAACJ,KAAK,EAAEC,IAAI,CAAC;EAEvD;EACAI,mBAAmB,EAAEA,CAACC,UAAkB,GAAG,EAAE,KAC3CZ,cAAc,CAACW,mBAAmB,CAACC,UAAU,CAAC;EAEhDC,kBAAkB,EAAEA,CAAA,KAClBb,cAAc,CAACa,kBAAkB,CAAC,CAAC;EAErC;EACAC,yBAAyB,EAAGC,EAAU,IACpCf,cAAc,CAACc,yBAAyB,CAACC,EAAE,CAAC;EAE9C;EACAC,iBAAiB,EAAEA,CAACC,IAAkB,EAAEC,QAAgB,KACtDlB,cAAc,CAACgB,iBAAiB,CAACC,IAAI,EAAEC,QAAQ,CAAC;EAElDC,gBAAgB,EAAEA,CAACb,KAAa,EAAEC,IAAY,EAAEC,EAAU,KACxDR,cAAc,CAACmB,gBAAgB,CAACb,KAAK,EAAEC,IAAI,EAAEC,EAAE,CAAC;EAElDY,kBAAkB,EAAGZ,EAAU,IAC7BR,cAAc,CAACoB,kBAAkB,CAACZ,EAAE,CAAC;EAEvC;EACAa,mBAAmB,EAAEA,CAAA,KACnBrB,cAAc,CAACqB,mBAAmB,CAAC,CAAC;EAEtC;EACA;EACA;;EAEAC,oBAAoB,EAAGC,EAAyC,IAC9DnB,OAAO,CAACoB,WAAW,CAAC,oBAAoB,EAAGC,KAAU,IAAKF,EAAE,CAACE,KAAK,CAAC,CAAC;EAEtEC,iBAAiB,EAAGH,EAAgC,IAClDnB,OAAO,CAACoB,WAAW,CAAC,sBAAsB,EAAGC,KAAU,IAAKF,EAAE,CAACE,KAAK,CAAC,CAAC;EAExEE,sBAAsB,EAAGJ,EAAkC,IACzDnB,OAAO,CAACoB,WAAW,CAAC,eAAe,EAAGC,KAAU,IAAKF,EAAE,CAACE,KAAK,CAAC,CAAC;EAEjEG,wBAAwB,EAAGL,EAA+B,IACxDnB,OAAO,CAACoB,WAAW,CAAC,iBAAiB,EAAGC,KAAU,IAAKF,EAAE,CAACE,KAAK,CAAC;AACpE,CAAC","ignoreList":[]}
1
+ {"version":3,"names":["NativeModules","NativeEventEmitter","Platform","LINKING_ERROR","select","ios","default","RNSensorModule","Proxy","get","Error","emitter","startForegroundService","title","body","id","stopForegroundService","updateServiceNotification","startMotionDetector","confidence","stopMotionDetector","setLocationUpdateInterval","ms","fireGeofenceAlert","type","userName","fireGenericAlert","cancelGenericAlert","registerGpsListener","addGpsStatusListener","cb","addListener","event","addMotionListener","addLocationLogListener","addLocationErrorListener"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"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","ignoreList":[]}
@@ -15,6 +15,22 @@ export interface ErrorEvent {
15
15
  error: string;
16
16
  message: string;
17
17
  }
18
+ export declare const startForegroundService: (title: string, body: string, id: number) => Promise<boolean>;
19
+ export declare const stopForegroundService: () => Promise<boolean>;
20
+ export declare const updateServiceNotification: (title: string, body: string) => Promise<boolean>;
21
+ export declare const startMotionDetector: (confidence?: number) => Promise<boolean>;
22
+ export declare const stopMotionDetector: () => Promise<boolean>;
23
+ export declare const setLocationUpdateInterval: (ms: number) => Promise<void>;
24
+ export declare const fireGeofenceAlert: (type: "IN" | "OUT", userName: string) => Promise<boolean>;
25
+ export declare const fireGenericAlert: (title: string, body: string, id: number) => Promise<boolean>;
26
+ export declare const cancelGenericAlert: (id: number) => Promise<boolean>;
27
+ export declare const registerGpsListener: () => Promise<boolean>;
28
+ export declare const addGpsStatusListener: (cb: (event: {
29
+ enabled: boolean;
30
+ }) => void) => EventSubscription;
31
+ export declare const addMotionListener: (cb: (event: MotionEvent) => void) => EventSubscription;
32
+ export declare const addLocationLogListener: (cb: (event: LocationEvent) => void) => EventSubscription;
33
+ export declare const addLocationErrorListener: (cb: (event: ErrorEvent) => void) => EventSubscription;
18
34
  declare const _default: {
19
35
  startForegroundService: (title: string, body: string, id: number) => Promise<boolean>;
20
36
  stopForegroundService: () => Promise<boolean>;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAsBtD,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS,GAAG,YAAY,GAAG,YAAY,GAAG,SAAS,CAAC;IACpF,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,QAAQ,GAAG,YAAY,CAAC;CAChC;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;;oCAKiC,MAAM,QAAQ,MAAM,MAAM,MAAM,KAAG,OAAO,CAAC,OAAO,CAAC;iCAGxD,OAAO,CAAC,OAAO,CAAC;uCAGR,MAAM,QAAQ,MAAM,KAAG,OAAO,CAAC,OAAO,CAAC;uCAIxC,MAAM,KAAQ,OAAO,CAAC,OAAO,CAAC;8BAGxC,OAAO,CAAC,OAAO,CAAC;oCAIR,MAAM,KAAG,OAAO,CAAC,IAAI,CAAC;8BAI5B,IAAI,GAAG,KAAK,YAAY,MAAM,KAAG,OAAO,CAAC,OAAO,CAAC;8BAGjD,MAAM,QAAQ,MAAM,MAAM,MAAM,KAAG,OAAO,CAAC,OAAO,CAAC;6BAGpD,MAAM,KAAG,OAAO,CAAC,OAAO,CAAC;+BAIzB,OAAO,CAAC,OAAO,CAAC;+BAOd,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,KAAK,IAAI,KAAG,iBAAiB;4BAG5D,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,KAAG,iBAAiB;iCAG3C,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,KAAG,iBAAiB;mCAGhD,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,KAAG,iBAAiB;;AAjDhF,wBAmDE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-geo-activity-kit",
3
- "version": "3.0.3",
3
+ "version": "4.0.0",
4
4
  "description": "Battery-efficient location tracking with motion detection and native notifications.",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
package/src/index.tsx CHANGED
@@ -1,5 +1,5 @@
1
1
  import { NativeModules, NativeEventEmitter, Platform } from 'react-native';
2
- import type { EventSubscription } from 'react-native'; // ✅ Fixed: Modern Type & Correct Import
2
+ import type { EventSubscription } from 'react-native';
3
3
 
4
4
  const LINKING_ERROR =
5
5
  `The package 'react-native-geo-activity-kit' doesn't seem to be linked. Make sure: \n\n` +
@@ -10,17 +10,16 @@ const LINKING_ERROR =
10
10
  const RNSensorModule = NativeModules.RNSensorModule
11
11
  ? NativeModules.RNSensorModule
12
12
  : new Proxy(
13
- {},
14
- {
15
- get() {
16
- throw new Error(LINKING_ERROR);
17
- },
18
- }
19
- );
13
+ {},
14
+ {
15
+ get() {
16
+ throw new Error(LINKING_ERROR);
17
+ },
18
+ }
19
+ );
20
20
 
21
21
  const emitter = new NativeEventEmitter(RNSensorModule);
22
22
 
23
- // 2. Define Types
24
23
  export interface LocationEvent {
25
24
  latitude: number;
26
25
  longitude: number;
@@ -30,7 +29,13 @@ export interface LocationEvent {
30
29
  }
31
30
 
32
31
  export interface MotionEvent {
33
- activity: 'STILL' | 'WALKING' | 'RUNNING' | 'ON_BICYCLE' | 'IN_VEHICLE' | 'UNKNOWN';
32
+ activity:
33
+ | 'STILL'
34
+ | 'WALKING'
35
+ | 'RUNNING'
36
+ | 'ON_BICYCLE'
37
+ | 'IN_VEHICLE'
38
+ | 'UNKNOWN';
34
39
  isMoving: boolean;
35
40
  state: 'MOVING' | 'STATIONARY';
36
41
  }
@@ -40,56 +45,81 @@ export interface ErrorEvent {
40
45
  message: string;
41
46
  }
42
47
 
43
- // 3. Export the Object
44
- export default {
45
- // --- Service Control ---
46
- startForegroundService: (title: string, body: string, id: number): Promise<boolean> =>
47
- RNSensorModule.startForegroundService(title, body, id),
48
+ export const startForegroundService = (
49
+ title: string,
50
+ body: string,
51
+ id: number
52
+ ): Promise<boolean> => RNSensorModule.startForegroundService(title, body, id);
48
53
 
49
- stopForegroundService: (): Promise<boolean> =>
50
- RNSensorModule.stopForegroundService(),
54
+ export const stopForegroundService = (): Promise<boolean> =>
55
+ RNSensorModule.stopForegroundService();
51
56
 
52
- updateServiceNotification: (title: string, body: string): Promise<boolean> =>
53
- RNSensorModule.updateServiceNotification(title, body),
57
+ export const updateServiceNotification = (
58
+ title: string,
59
+ body: string
60
+ ): Promise<boolean> => RNSensorModule.updateServiceNotification(title, body);
54
61
 
55
- // --- Motion & Intelligence ---
56
- startMotionDetector: (confidence: number = 75): Promise<boolean> =>
57
- RNSensorModule.startMotionDetector(confidence),
62
+ export const startMotionDetector = (
63
+ confidence: number = 75
64
+ ): Promise<boolean> => RNSensorModule.startMotionDetector(confidence);
58
65
 
59
- stopMotionDetector: (): Promise<boolean> =>
60
- RNSensorModule.stopMotionDetector(),
66
+ export const stopMotionDetector = (): Promise<boolean> =>
67
+ RNSensorModule.stopMotionDetector();
61
68
 
62
- // --- Location Control ---
63
- setLocationUpdateInterval: (ms: number): Promise<void> =>
64
- RNSensorModule.setLocationUpdateInterval(ms),
69
+ export const setLocationUpdateInterval = (ms: number): Promise<void> =>
70
+ RNSensorModule.setLocationUpdateInterval(ms);
65
71
 
66
- // --- Alerts & Notifications ---
67
- fireGeofenceAlert: (type: 'IN' | 'OUT', userName: string): Promise<boolean> =>
68
- RNSensorModule.fireGeofenceAlert(type, userName),
72
+ export const fireGeofenceAlert = (
73
+ type: 'IN' | 'OUT',
74
+ userName: string
75
+ ): Promise<boolean> => RNSensorModule.fireGeofenceAlert(type, userName);
69
76
 
70
- fireGenericAlert: (title: string, body: string, id: number): Promise<boolean> =>
71
- RNSensorModule.fireGenericAlert(title, body, id),
77
+ export const fireGenericAlert = (
78
+ title: string,
79
+ body: string,
80
+ id: number
81
+ ): Promise<boolean> => RNSensorModule.fireGenericAlert(title, body, id);
72
82
 
73
- cancelGenericAlert: (id: number): Promise<boolean> =>
74
- RNSensorModule.cancelGenericAlert(id),
83
+ export const cancelGenericAlert = (id: number): Promise<boolean> =>
84
+ RNSensorModule.cancelGenericAlert(id);
75
85
 
76
- // --- GPS Status ---
77
- registerGpsListener: (): Promise<boolean> =>
78
- RNSensorModule.registerGpsListener(),
86
+ export const registerGpsListener = (): Promise<boolean> =>
87
+ RNSensorModule.registerGpsListener();
79
88
 
80
- // --- Event Listeners ---
81
- // Note: We cast event to 'any' to avoid TS strict object checks,
82
- // and return EventSubscription (modern) instead of EmitterSubscription (deprecated).
89
+ export const addGpsStatusListener = (
90
+ cb: (event: { enabled: boolean }) => void
91
+ ): EventSubscription =>
92
+ emitter.addListener('onGpsStatusChanged', (event: any) => cb(event));
83
93
 
84
- addGpsStatusListener: (cb: (event: { enabled: boolean }) => void): EventSubscription =>
85
- emitter.addListener('onGpsStatusChanged', (event: any) => cb(event)),
94
+ export const addMotionListener = (
95
+ cb: (event: MotionEvent) => void
96
+ ): EventSubscription =>
97
+ emitter.addListener('onMotionStateChanged', (event: any) => cb(event));
86
98
 
87
- addMotionListener: (cb: (event: MotionEvent) => void): EventSubscription =>
88
- emitter.addListener('onMotionStateChanged', (event: any) => cb(event)),
99
+ export const addLocationLogListener = (
100
+ cb: (event: LocationEvent) => void
101
+ ): EventSubscription =>
102
+ emitter.addListener('onLocationLog', (event: any) => cb(event));
89
103
 
90
- addLocationLogListener: (cb: (event: LocationEvent) => void): EventSubscription =>
91
- emitter.addListener('onLocationLog', (event: any) => cb(event)),
104
+ export const addLocationErrorListener = (
105
+ cb: (event: ErrorEvent) => void
106
+ ): EventSubscription =>
107
+ emitter.addListener('onLocationError', (event: any) => cb(event));
92
108
 
93
- addLocationErrorListener: (cb: (event: ErrorEvent) => void): EventSubscription =>
94
- emitter.addListener('onLocationError', (event: any) => cb(event)),
95
- };
109
+ // Default export for backward compatibility
110
+ export default {
111
+ startForegroundService,
112
+ stopForegroundService,
113
+ updateServiceNotification,
114
+ startMotionDetector,
115
+ stopMotionDetector,
116
+ setLocationUpdateInterval,
117
+ fireGeofenceAlert,
118
+ fireGenericAlert,
119
+ cancelGenericAlert,
120
+ registerGpsListener,
121
+ addGpsStatusListener,
122
+ addMotionListener,
123
+ addLocationLogListener,
124
+ addLocationErrorListener,
125
+ };