react-native-geo-activity-kit 1.2.4 → 1.2.5

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.
@@ -7,12 +7,25 @@
7
7
  <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
8
8
  <uses-permission android:name="android.permission.WAKE_LOCK" />
9
9
 
10
+ <uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
11
+ <uses-permission android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION" />
12
+
10
13
  <application>
11
14
  <service
12
15
  android:name=".TrackingService"
13
16
  android:enabled="true"
14
17
  android:exported="false"
15
18
  android:foregroundServiceType="location" />
19
+
20
+ <receiver
21
+ android:name=".ActivityTransitionReceiver"
22
+ android:enabled="true"
23
+ android:exported="false"
24
+ android:permission="com.google.android.gms.permission.ACTIVITY_RECOGNITION">
25
+ <intent-filter>
26
+ <action android:name="com.rngeoactivitykit.ACTION_PROCESS_ACTIVITY_TRANSITIONS" />
27
+ </intent-filter>
28
+ </receiver>
16
29
  </application>
17
30
 
18
31
  </manifest>
@@ -0,0 +1,88 @@
1
+ package com.rngeoactivitykit
2
+
3
+ import android.content.BroadcastReceiver
4
+ import android.content.Context
5
+ import android.content.Intent
6
+ import android.util.Log
7
+ import com.facebook.react.bridge.Arguments
8
+ import com.facebook.react.bridge.ReactApplicationContext
9
+ import com.facebook.react.modules.core.DeviceEventManagerModule
10
+ import com.google.android.gms.location.ActivityTransitionResult
11
+ import com.google.android.gms.location.DetectedActivity
12
+
13
+ class ActivityTransitionReceiver : BroadcastReceiver() {
14
+
15
+ override fun onReceive(context: Context, intent: Intent) {
16
+ if (ActivityTransitionResult.hasResult(intent)) {
17
+ val result = ActivityTransitionResult.extractResult(intent) ?: return
18
+
19
+ for (event in result.transitionEvents) {
20
+ val activityType = toActivityString(event.activityType)
21
+ val transitionType = toTransitionString(event.transitionType)
22
+
23
+ Log.d("ActivityReceiver", "🏃 Motion Event: $activityType ($transitionType)")
24
+
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
+ }
32
+
33
+ // --- NEW: DIRECT NATIVE CONTROL (No JS required) ---
34
+ try {
35
+ if (isMoving) {
36
+ // Fast Updates (30s) when moving
37
+ LocationHelper.shared?.setLocationUpdateInterval(30000)
38
+ } else {
39
+ // Slow Updates (5 mins) when STILL - BATTERY SAVER
40
+ LocationHelper.shared?.setLocationUpdateInterval(5 * 60 * 1000)
41
+ }
42
+ } catch (e: Exception) {
43
+ Log.e("ActivityReceiver", "Failed to update location interval directly")
44
+ }
45
+ // ---------------------------------------------------
46
+
47
+ try {
48
+ val reactContext = context.applicationContext as? ReactApplicationContext
49
+ ?: TrackingService.instance?.application as? ReactApplicationContext
50
+
51
+ if (reactContext != null && reactContext.hasActiveCatalystInstance()) {
52
+ val params = Arguments.createMap()
53
+ params.putString("activity", activityType)
54
+ params.putString("transition", transitionType)
55
+ params.putBoolean("isMoving", isMoving)
56
+ params.putString("state", if (isMoving) "MOVING" else "STATIONARY")
57
+
58
+ reactContext
59
+ .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
60
+ .emit("onMotionStateChanged", params)
61
+ }
62
+ } catch (e: Exception) {
63
+ Log.e("ActivityReceiver", "JS Bridge Error: ${e.message}")
64
+ }
65
+ }
66
+ }
67
+ }
68
+
69
+ private fun toActivityString(type: Int): String {
70
+ return when (type) {
71
+ DetectedActivity.STILL -> "STILL"
72
+ DetectedActivity.WALKING -> "WALKING"
73
+ DetectedActivity.RUNNING -> "RUNNING"
74
+ DetectedActivity.ON_BICYCLE -> "ON_BICYCLE"
75
+ DetectedActivity.IN_VEHICLE -> "IN_VEHICLE"
76
+ DetectedActivity.TILTING -> "TILTING"
77
+ else -> "UNKNOWN"
78
+ }
79
+ }
80
+
81
+ private fun toTransitionString(type: Int): String {
82
+ 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
+ else -> "UNKNOWN"
86
+ }
87
+ }
88
+ }
@@ -16,6 +16,13 @@ import java.util.TimeZone
16
16
 
17
17
  class LocationHelper(private val context: ReactApplicationContext) {
18
18
 
19
+ // --- ADDED THIS BLOCK (Singleton Pattern) ---
20
+ companion object {
21
+ @SuppressLint("StaticFieldLeak")
22
+ var shared: LocationHelper? = null
23
+ }
24
+ // --------------------------------------------
25
+
19
26
  private val fusedLocationClient: FusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(context)
20
27
  private var locationCallback: LocationCallback
21
28
  private var locationRequest: LocationRequest
@@ -28,6 +35,10 @@ class LocationHelper(private val context: ReactApplicationContext) {
28
35
  }
29
36
 
30
37
  init {
38
+ // --- ADDED THIS LINE ---
39
+ shared = this
40
+ // -----------------------
41
+
31
42
  locationRequest = LocationRequest.create().apply {
32
43
  interval = 30000
33
44
  fastestInterval = 30000
@@ -39,21 +50,38 @@ class LocationHelper(private val context: ReactApplicationContext) {
39
50
  locationResult.lastLocation ?: return
40
51
  val location = locationResult.lastLocation!!
41
52
 
53
+ Log.d("LocationHelper", "📍 New Location: ${location.latitude}, ${location.longitude} (Acc: ${location.accuracy}m)")
54
+
55
+ var isMock = false
56
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
57
+ isMock = location.isMock
58
+ } else {
59
+ isMock = location.isFromMockProvider
60
+ }
61
+
42
62
  val params = Arguments.createMap()
43
63
  params.putDouble("latitude", location.latitude)
44
64
  params.putDouble("longitude", location.longitude)
45
65
  params.putString("timestamp", isoFormatter.format(Date(location.time)))
46
66
  params.putDouble("accuracy", location.accuracy.toDouble())
67
+ params.putBoolean("is_mock", isMock)
47
68
 
48
69
  sendEvent("onLocationLog", params)
49
70
  }
50
71
  }
51
72
  }
52
73
 
53
- fun updateLocationRequest(priority: Int, intervalMs: Long) {
54
- if (locationRequest.priority == priority && locationRequest.interval == intervalMs) return
74
+ fun setLocationUpdateInterval(intervalMs: Long) {
75
+ val newPriority = if (intervalMs < 10000) {
76
+ Priority.PRIORITY_HIGH_ACCURACY
77
+ } else {
78
+ Priority.PRIORITY_BALANCED_POWER_ACCURACY
79
+ }
80
+ updateLocationRequest(newPriority, intervalMs)
81
+ }
55
82
 
56
- Log.i("LocationHelper", "Switching Mode -> Priority: $priority, Interval: $intervalMs")
83
+ fun updateLocationRequest(priority: Int, intervalMs: Long) {
84
+ Log.d("LocationHelper", "🔄 Updating Request: Priority=$priority, Interval=${intervalMs}ms")
57
85
 
58
86
  locationRequest = LocationRequest.create().apply {
59
87
  this.interval = intervalMs
@@ -61,10 +89,11 @@ class LocationHelper(private val context: ReactApplicationContext) {
61
89
  this.priority = priority
62
90
  }
63
91
 
64
- // Restart if running to apply changes
65
92
  if (isLocationClientRunning) {
66
93
  stopLocationUpdates()
67
94
  startLocationUpdates()
95
+ } else {
96
+ startLocationUpdates()
68
97
  }
69
98
  }
70
99
 
@@ -72,21 +101,15 @@ class LocationHelper(private val context: ReactApplicationContext) {
72
101
  fun startLocationUpdates() {
73
102
  if (isLocationClientRunning) return
74
103
  if (!hasLocationPermission()) {
75
- val params = Arguments.createMap()
76
- params.putString("error", "LOCATION_PERMISSION_DENIED")
77
- params.putString("message", "Location permission is not granted.")
78
- sendEvent("onLocationError", params)
104
+ Log.e("LocationHelper", "Permission Missing")
79
105
  return
80
106
  }
81
107
  try {
82
108
  fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, Looper.getMainLooper())
83
109
  isLocationClientRunning = true
84
- Log.i("LocationHelper", "Location updates started.")
110
+ Log.d("LocationHelper", "Location Updates STARTED.")
85
111
  } catch (e: Exception) {
86
- val params = Arguments.createMap()
87
- params.putString("error", "START_LOCATION_FAILED")
88
- params.putString("message", "Error starting location: ${e.message}")
89
- sendEvent("onLocationError", params)
112
+ Log.e("LocationHelper", "Error starting location: ${e.message}")
90
113
  }
91
114
  }
92
115
 
@@ -95,9 +118,9 @@ class LocationHelper(private val context: ReactApplicationContext) {
95
118
  try {
96
119
  fusedLocationClient.removeLocationUpdates(locationCallback)
97
120
  isLocationClientRunning = false
98
- Log.i("LocationHelper", "Location updates stopped.")
121
+ Log.d("LocationHelper", "🛑 Location Updates STOPPED.")
99
122
  } catch (e: Exception) {
100
- Log.e("LocationHelper", "Failed to stop location updates: " + e.message)
123
+ Log.e("LocationHelper", "Failed to stop: " + e.message)
101
124
  }
102
125
  }
103
126
 
@@ -108,8 +131,12 @@ class LocationHelper(private val context: ReactApplicationContext) {
108
131
  }
109
132
 
110
133
  private fun sendEvent(eventName: String, params: Any?) {
111
- if (context.hasActiveCatalystInstance()) {
112
- context.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java).emit(eventName, params)
134
+ try {
135
+ if (context.hasActiveCatalystInstance()) {
136
+ context.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java).emit(eventName, params)
137
+ }
138
+ } catch (e: Exception) {
139
+ Log.e("LocationHelper", "JS Error: ${e.message}")
113
140
  }
114
141
  }
115
142
  }
@@ -1,115 +1,88 @@
1
1
  package com.rngeoactivitykit
2
2
 
3
- import android.content.Context
4
- import android.hardware.Sensor
5
- import android.hardware.SensorEvent
6
- import android.hardware.SensorEventListener
7
- import android.hardware.SensorManager
8
- import com.facebook.react.bridge.Arguments
3
+ import android.Manifest
4
+ import android.annotation.SuppressLint
5
+ import android.app.PendingIntent
6
+ import android.content.Intent
7
+ import android.content.pm.PackageManager
8
+ import android.os.Build
9
+ import androidx.core.content.ContextCompat
9
10
  import com.facebook.react.bridge.ReactApplicationContext
10
- import com.facebook.react.modules.core.DeviceEventManagerModule
11
- import kotlin.math.sqrt
12
-
13
- class MotionDetector(private val context: ReactApplicationContext, private val onStateChange: (String) -> Unit) : SensorEventListener {
14
-
15
- private val sensorManager: SensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
16
- private var accelerometer: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
11
+ import com.google.android.gms.location.ActivityRecognition
12
+ import com.google.android.gms.location.ActivityTransition
13
+ import com.google.android.gms.location.ActivityTransitionRequest
14
+ import com.google.android.gms.location.DetectedActivity
15
+
16
+ class MotionDetector(private val context: ReactApplicationContext) {
17
+
18
+ private val activityClient = ActivityRecognition.getClient(context)
19
+ private var pendingIntent: PendingIntent? = null
20
+
21
+ // We define which activities trigger a wake-up
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(),
28
+
29
+ // Detect when user STARTS Walking
30
+ ActivityTransition.Builder()
31
+ .setActivityType(DetectedActivity.WALKING)
32
+ .setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_ENTER)
33
+ .build(),
34
+
35
+ // Detect when user STARTS Driving
36
+ ActivityTransition.Builder()
37
+ .setActivityType(DetectedActivity.IN_VEHICLE)
38
+ .setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_ENTER)
39
+ .build()
40
+ )
41
+
42
+ @SuppressLint("MissingPermission") // Checked in start()
43
+ fun start(): Boolean {
44
+ if (!hasPermission()) {
45
+ return false
46
+ }
17
47
 
18
- private val gravity = floatArrayOf(0f, 0f, 0f)
19
- private val linearAcceleration = floatArrayOf(0f, 0f, 0f)
20
- private val alpha: Float = 0.8f
48
+ val request = ActivityTransitionRequest(transitions)
49
+ val intent = Intent(context, ActivityTransitionReceiver::class.java)
50
+ intent.action = "com.rngeoactivitykit.ACTION_PROCESS_ACTIVITY_TRANSITIONS"
21
51
 
22
- var motionThreshold: Float = 0.8f
23
- var startStabilityThreshold: Int = 20
24
- var stopStabilityThreshold: Int = 3000
25
- var samplingPeriodUs: Int = 100_000
52
+ val flags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
53
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
54
+ } else {
55
+ PendingIntent.FLAG_UPDATE_CURRENT
56
+ }
26
57
 
27
- private var currentState: String = "STATIONARY"
28
- private var potentialState: String = "STATIONARY"
29
- private var consecutiveCount = 0
30
- private var isStarted = false
58
+ pendingIntent = PendingIntent.getBroadcast(context, 0, intent, flags)
31
59
 
32
- fun start(threshold: Double): Boolean {
33
- if (accelerometer == null) return false
34
-
35
- motionThreshold = threshold.toFloat()
36
- currentState = "STATIONARY"
37
- potentialState = "STATIONARY"
38
- consecutiveCount = 0
39
- isStarted = true
60
+ activityClient.requestActivityTransitionUpdates(request, pendingIntent!!)
61
+ .addOnSuccessListener {
62
+ // Success
63
+ }
64
+ .addOnFailureListener { e ->
65
+ e.printStackTrace()
66
+ }
40
67
 
41
- sensorManager.registerListener(this, accelerometer, samplingPeriodUs)
42
68
  return true
43
69
  }
44
70
 
45
71
  fun stop() {
46
- isStarted = false
47
- sensorManager.unregisterListener(this)
48
- }
49
-
50
- fun setUpdateInterval(ms: Int) {
51
- samplingPeriodUs = ms * 1000
52
- if (isStarted) {
53
- stop()
54
- sensorManager.registerListener(this, accelerometer, samplingPeriodUs)
55
- isStarted = true
72
+ pendingIntent?.let {
73
+ activityClient.removeActivityTransitionUpdates(it)
74
+ pendingIntent = null
56
75
  }
57
76
  }
58
77
 
59
- fun isSensorAvailable(): Boolean = accelerometer != null
60
-
61
- override fun onSensorChanged(event: SensorEvent?) {
62
- event ?: return
63
- if (event.sensor.type == Sensor.TYPE_ACCELEROMETER) {
64
-
65
- // Isolate Gravity
66
- gravity[0] = alpha * gravity[0] + (1 - alpha) * event.values[0]
67
- gravity[1] = alpha * gravity[1] + (1 - alpha) * event.values[1]
68
- gravity[2] = alpha * gravity[2] + (1 - alpha) * event.values[2]
69
-
70
- // Remove Gravity
71
- linearAcceleration[0] = event.values[0] - gravity[0]
72
- linearAcceleration[1] = event.values[1] - gravity[1]
73
- linearAcceleration[2] = event.values[2] - gravity[2]
74
-
75
- val magnitude = sqrt(
76
- (linearAcceleration[0] * linearAcceleration[0] +
77
- linearAcceleration[1] * linearAcceleration[1] +
78
- linearAcceleration[2] * linearAcceleration[2]).toDouble()
79
- ).toFloat()
80
-
81
- val newState = if (magnitude > motionThreshold) "MOVING" else "STATIONARY"
82
-
83
- if (newState == potentialState) {
84
- consecutiveCount++
85
- } else {
86
- potentialState = newState
87
- consecutiveCount = 1
88
- }
89
-
90
- var stabilityMet = false
91
- if (potentialState == "MOVING" && consecutiveCount >= startStabilityThreshold) {
92
- stabilityMet = true
93
- } else if (potentialState == "STATIONARY" && consecutiveCount >= stopStabilityThreshold) {
94
- stabilityMet = true
95
- }
96
-
97
- if (stabilityMet && potentialState != currentState) {
98
- currentState = potentialState
99
-
100
- // Notify Listener (SensorModule)
101
- onStateChange(currentState)
102
-
103
- // Emit to JS
104
- val params = Arguments.createMap()
105
- params.putString("state", currentState)
106
- if (context.hasActiveCatalystInstance()) {
107
- context.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
108
- .emit("onMotionStateChanged", params)
109
- }
110
- }
78
+ private fun hasPermission(): Boolean {
79
+ return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
80
+ ContextCompat.checkSelfPermission(
81
+ context,
82
+ Manifest.permission.ACTIVITY_RECOGNITION
83
+ ) == PackageManager.PERMISSION_GRANTED
84
+ } else {
85
+ true // Not required at runtime below Android 10
111
86
  }
112
87
  }
113
-
114
- override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {}
115
88
  }
@@ -8,66 +8,46 @@ import android.location.LocationManager
8
8
  import android.os.Build
9
9
  import com.facebook.react.bridge.*
10
10
  import com.facebook.react.modules.core.DeviceEventManagerModule
11
- import com.google.android.gms.location.Priority
12
11
 
13
12
  class SensorModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
14
13
 
15
14
  private val notificationHelper = NotificationHelper(reactContext)
16
15
  private val locationHelper = LocationHelper(reactContext)
17
16
 
18
- private val motionDetector = MotionDetector(reactContext) { newState ->
19
- onMotionStateChanged(newState)
20
- }
21
-
22
- private var locationInterval: Long = 30000
17
+ // NEW: MotionDetector no longer needs a callback because
18
+ // the ActivityTransitionReceiver handles the events.
19
+ private val motionDetector = MotionDetector(reactContext)
23
20
 
24
- // --- NEW: Hardware GPS Toggle Receiver ---
21
+ // --- GPS STATUS RECEIVER (Detects if user turns off Location Toggle) ---
25
22
  private val gpsStatusReceiver = object : BroadcastReceiver() {
26
23
  override fun onReceive(context: Context?, intent: Intent?) {
27
24
  if (intent?.action == LocationManager.PROVIDERS_CHANGED_ACTION) {
28
- val locationManager = context?.getSystemService(Context.LOCATION_SERVICE) as LocationManager
29
- val isGpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)
30
-
31
- val params = Arguments.createMap()
32
- params.putBoolean("enabled", isGpsEnabled)
33
-
34
- // Emit event to React Native
35
- sendEvent("onGpsStatusChanged", params)
25
+ try {
26
+ val locationManager = context?.getSystemService(Context.LOCATION_SERVICE) as LocationManager
27
+ val isGpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)
28
+
29
+ val params = Arguments.createMap()
30
+ params.putBoolean("enabled", isGpsEnabled)
31
+
32
+ sendEvent("onGpsStatusChanged", params)
33
+ } catch (e: Exception) {
34
+ e.printStackTrace()
35
+ }
36
36
  }
37
37
  }
38
38
  }
39
39
 
40
- override fun getName(): String = "RNSensorModule"
41
-
42
- private fun sendEvent(eventName: String, params: WritableMap) {
43
- if (reactApplicationContext.hasActiveCatalystInstance()) {
44
- reactApplicationContext
45
- .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
46
- .emit(eventName, params)
47
- }
40
+ override fun getName(): String {
41
+ return "RNSensorModule"
48
42
  }
49
43
 
50
- private fun onMotionStateChanged(state: String) {
51
- if (state == "MOVING") {
52
- locationHelper.updateLocationRequest(Priority.PRIORITY_HIGH_ACCURACY, locationInterval)
53
- locationHelper.startLocationUpdates()
54
- } else {
55
- locationHelper.updateLocationRequest(Priority.PRIORITY_BALANCED_POWER_ACCURACY, 180000)
56
- locationHelper.startLocationUpdates()
57
- }
44
+ init {
45
+ // Register GPS Status Receiver
46
+ val filter = IntentFilter(LocationManager.PROVIDERS_CHANGED_ACTION)
47
+ reactContext.registerReceiver(gpsStatusReceiver, filter)
58
48
  }
59
49
 
60
- // --- NEW: Native Method to Start GPS Listener ---
61
- @ReactMethod
62
- fun registerGpsListener(promise: Promise) {
63
- try {
64
- val filter = IntentFilter(LocationManager.PROVIDERS_CHANGED_ACTION)
65
- reactApplicationContext.registerReceiver(gpsStatusReceiver, filter)
66
- promise.resolve(true)
67
- } catch (e: Exception) {
68
- promise.reject("REGISTER_FAILED", e.message)
69
- }
70
- }
50
+ // --- SERVICE CONTROL ---
71
51
 
72
52
  @ReactMethod
73
53
  fun startForegroundService(title: String, body: String, promise: Promise) {
@@ -84,7 +64,7 @@ class SensorModule(reactContext: ReactApplicationContext) : ReactContextBaseJava
84
64
  }
85
65
  promise.resolve(true)
86
66
  } catch (e: Exception) {
87
- promise.reject("START_FAILED", e.message)
67
+ promise.reject("START_SERVICE_FAILED", e.message)
88
68
  }
89
69
  }
90
70
 
@@ -93,83 +73,82 @@ class SensorModule(reactContext: ReactApplicationContext) : ReactContextBaseJava
93
73
  try {
94
74
  val intent = Intent(reactApplicationContext, TrackingService::class.java)
95
75
  intent.action = TrackingService.ACTION_STOP
96
- reactApplicationContext.startService(intent)
76
+ reactApplicationContext.startService(intent) // Triggers stop logic in Service
97
77
  promise.resolve(true)
98
78
  } catch (e: Exception) {
99
- promise.reject("STOP_FAILED", e.message)
79
+ promise.reject("STOP_SERVICE_FAILED", e.message)
100
80
  }
101
81
  }
102
82
 
103
83
  @ReactMethod
104
84
  fun updateServiceNotification(title: String, body: String, promise: Promise) {
105
85
  try {
106
- if (TrackingService.instance != null) {
107
- val intent = Intent(reactApplicationContext, TrackingService::class.java)
108
- intent.action = TrackingService.ACTION_UPDATE
109
- intent.putExtra("title", title)
110
- intent.putExtra("body", body)
111
- reactApplicationContext.startService(intent)
112
- promise.resolve(true)
86
+ val intent = Intent(reactApplicationContext, TrackingService::class.java)
87
+ intent.action = TrackingService.ACTION_UPDATE
88
+ intent.putExtra("title", title)
89
+ intent.putExtra("body", body)
90
+
91
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
92
+ reactApplicationContext.startForegroundService(intent)
113
93
  } else {
114
- promise.resolve(false)
94
+ reactApplicationContext.startService(intent)
115
95
  }
96
+ promise.resolve(true)
116
97
  } catch (e: Exception) {
117
98
  promise.reject("UPDATE_FAILED", e.message)
118
99
  }
119
100
  }
120
101
 
102
+ // --- MOTION DETECTOR (Updated for Activity Recognition) ---
103
+
121
104
  @ReactMethod
122
105
  fun startMotionDetector(threshold: Double, promise: Promise) {
123
- val success = motionDetector.start(threshold)
124
- if (!success) {
125
- promise.reject("NO_SENSOR", "Accelerometer not available")
126
- return
106
+ // NOTE: 'threshold' is ignored now because Activity Recognition
107
+ // handles sensitivity automatically using Machine Learning.
108
+ try {
109
+ val success = motionDetector.start()
110
+ if (success) {
111
+ promise.resolve(true)
112
+ } else {
113
+ // This usually means Permissions are missing
114
+ promise.reject("PERMISSION_DENIED", "ACTIVITY_RECOGNITION permission is required")
115
+ }
116
+ } catch (e: Exception) {
117
+ promise.reject("START_MOTION_FAILED", e.message)
127
118
  }
128
- locationHelper.updateLocationRequest(Priority.PRIORITY_BALANCED_POWER_ACCURACY, 180000)
129
- locationHelper.startLocationUpdates()
130
- promise.resolve(true)
131
119
  }
132
120
 
133
121
  @ReactMethod
134
122
  fun stopMotionDetector(promise: Promise) {
135
- motionDetector.stop()
136
- locationHelper.stopLocationUpdates()
137
- promise.resolve(true)
138
- }
139
-
140
- @ReactMethod
141
- fun setLocationUpdateInterval(interval: Double, promise: Promise) {
142
- locationInterval = interval.toLong()
143
- promise.resolve(true)
144
- }
145
-
146
- @ReactMethod
147
- fun setStabilityThresholds(startThreshold: Int, stopThreshold: Int, promise: Promise) {
148
- motionDetector.startStabilityThreshold = startThreshold
149
- motionDetector.stopStabilityThreshold = stopThreshold
150
- promise.resolve(true)
123
+ try {
124
+ motionDetector.stop()
125
+ promise.resolve(true)
126
+ } catch (e: Exception) {
127
+ promise.reject("STOP_MOTION_FAILED", e.message)
128
+ }
151
129
  }
152
130
 
153
- @ReactMethod
154
- fun setUpdateInterval(ms: Int, promise: Promise) {
155
- motionDetector.setUpdateInterval(ms)
156
- promise.resolve(true)
157
- }
131
+ // --- LOCATION CONTROL ---
158
132
 
159
133
  @ReactMethod
160
- fun isAvailable(promise: Promise) {
161
- val map = Arguments.createMap()
162
- map.putBoolean("accelerometer", motionDetector.isSensorAvailable())
163
- map.putBoolean("gyroscope", false)
164
- promise.resolve(map)
134
+ fun setLocationUpdateInterval(intervalMs: Int) {
135
+ try {
136
+ locationHelper.setLocationUpdateInterval(intervalMs.toLong())
137
+ } catch (e: Exception) {
138
+ e.printStackTrace()
139
+ }
165
140
  }
141
+
142
+ // --- ALERTS & NOTIFICATIONS ---
166
143
 
167
144
  @ReactMethod
168
145
  fun fireGeofenceAlert(type: String, userName: String, promise: Promise) {
169
146
  try {
170
147
  notificationHelper.fireGeofenceAlert(type, userName)
171
148
  promise.resolve(true)
172
- } catch (e: Exception) { promise.reject("NOTIFY_FAILED", e.message) }
149
+ } catch (e: Exception) {
150
+ promise.reject("NOTIFY_FAILED", e.message)
151
+ }
173
152
  }
174
153
 
175
154
  @ReactMethod
@@ -177,7 +156,9 @@ class SensorModule(reactContext: ReactApplicationContext) : ReactContextBaseJava
177
156
  try {
178
157
  notificationHelper.fireGenericAlert(title, body, id)
179
158
  promise.resolve(true)
180
- } catch (e: Exception) { promise.reject("NOTIFY_FAILED", e.message) }
159
+ } catch (e: Exception) {
160
+ promise.reject("NOTIFY_FAILED", e.message)
161
+ }
181
162
  }
182
163
 
183
164
  @ReactMethod
@@ -185,23 +166,65 @@ class SensorModule(reactContext: ReactApplicationContext) : ReactContextBaseJava
185
166
  try {
186
167
  notificationHelper.cancelGenericAlert(id)
187
168
  promise.resolve(true)
188
- } catch (e: Exception) { promise.reject("CANCEL_FAILED", e.message) }
169
+ } catch (e: Exception) {
170
+ promise.reject("CANCEL_FAILED", e.message)
171
+ }
189
172
  }
190
173
 
191
- @ReactMethod fun addListener(eventName: String) {}
192
- @ReactMethod fun removeListeners(count: Int) {}
174
+ @ReactMethod
175
+ fun registerGpsListener(promise: Promise) {
176
+ // Already registered in init, just checking permission state
177
+ try {
178
+ val locationManager = reactApplicationContext.getSystemService(Context.LOCATION_SERVICE) as LocationManager
179
+ val isEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)
180
+ promise.resolve(isEnabled)
181
+ } catch (e: Exception) {
182
+ promise.reject("GPS_CHECK_FAILED", e.message)
183
+ }
184
+ }
185
+
186
+ // --- EVENT EMITTER BOILERPLATE ---
187
+
188
+ @ReactMethod
189
+ fun addListener(eventName: String) {
190
+ // Required for NativeEventEmitter
191
+ }
192
+
193
+ @ReactMethod
194
+ fun removeListeners(count: Int) {
195
+ // Required for NativeEventEmitter
196
+ }
197
+
198
+ private fun sendEvent(eventName: String, params: Any?) {
199
+ try {
200
+ if (reactApplicationContext.hasActiveCatalystInstance()) {
201
+ reactApplicationContext
202
+ .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
203
+ .emit(eventName, params)
204
+ }
205
+ } catch (e: Exception) {
206
+ e.printStackTrace()
207
+ }
208
+ }
209
+
210
+ // --- CLEANUP ---
193
211
 
194
212
  override fun onCatalystInstanceDestroy() {
213
+ super.onCatalystInstanceDestroy()
195
214
  try {
215
+ // 1. Unregister Receivers
196
216
  reactApplicationContext.unregisterReceiver(gpsStatusReceiver)
197
- } catch (e: Exception) {}
198
-
199
- super.onCatalystInstanceDestroy()
200
- motionDetector.stop()
201
- locationHelper.stopLocationUpdates()
202
-
203
- val intent = Intent(reactApplicationContext, TrackingService::class.java)
204
- intent.action = TrackingService.ACTION_STOP
205
- reactApplicationContext.startService(intent)
217
+
218
+ // 2. Stop Sensors
219
+ motionDetector.stop()
220
+ locationHelper.stopLocationUpdates()
221
+
222
+ // 3. Stop Service (Optional: Depending on whether you want it to persist after app kill)
223
+ // Ideally, we DO NOT stop the service here if we want it running in background.
224
+ // But we do clean up listeners.
225
+
226
+ } catch (e: Exception) {
227
+ e.printStackTrace()
228
+ }
206
229
  }
207
230
  }
@@ -11,6 +11,7 @@ import android.content.pm.ServiceInfo
11
11
  import android.os.Build
12
12
  import android.os.IBinder
13
13
  import android.os.PowerManager
14
+ import android.util.Log
14
15
  import androidx.core.app.NotificationCompat
15
16
 
16
17
  class TrackingService : Service() {
@@ -19,7 +20,6 @@ class TrackingService : Service() {
19
20
  var instance: TrackingService? = null
20
21
  const val NOTIFICATION_ID = 9991
21
22
  const val CHANNEL_ID = "geo_activity_kit_channel"
22
-
23
23
  const val ACTION_START = "ACTION_START"
24
24
  const val ACTION_STOP = "ACTION_STOP"
25
25
  const val ACTION_UPDATE = "ACTION_UPDATE"
@@ -32,15 +32,16 @@ class TrackingService : Service() {
32
32
  override fun onCreate() {
33
33
  super.onCreate()
34
34
  instance = this
35
+ Log.d("TrackingService", "✅ Service Created")
35
36
  createNotificationChannel()
36
37
 
37
- // Acquire WakeLock to keep CPU running
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?.acquire(10 * 60 * 1000L /* 10 minutes timeout safety */)
41
+ wakeLock?.acquire()
42
+ Log.d("TrackingService", "🔒 WakeLock Acquired (Permanent)")
42
43
  } catch (e: Exception) {
43
- e.printStackTrace()
44
+ Log.e("TrackingService", "❌ Failed to acquire WakeLock: ${e.message}")
44
45
  }
45
46
  }
46
47
 
@@ -49,11 +50,13 @@ class TrackingService : Service() {
49
50
 
50
51
  when (intent.action) {
51
52
  ACTION_START -> {
53
+ Log.d("TrackingService", "➡️ Action: START")
52
54
  val title = intent.getStringExtra("title") ?: "Location Active"
53
55
  val body = intent.getStringExtra("body") ?: "Monitoring in background..."
54
56
  startForegroundService(title, body)
55
57
  }
56
58
  ACTION_STOP -> {
59
+ Log.d("TrackingService", "⏹️ Action: STOP")
57
60
  stopForegroundService()
58
61
  }
59
62
  ACTION_UPDATE -> {
@@ -67,11 +70,14 @@ class TrackingService : Service() {
67
70
 
68
71
  private fun startForegroundService(title: String, body: String) {
69
72
  val notification = buildNotification(title, body)
70
-
71
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
72
- startForeground(NOTIFICATION_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION)
73
- } else {
74
- startForeground(NOTIFICATION_ID, notification)
73
+ try {
74
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
75
+ startForeground(NOTIFICATION_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION)
76
+ } else {
77
+ startForeground(NOTIFICATION_ID, notification)
78
+ }
79
+ } catch (e: Exception) {
80
+ Log.e("TrackingService", "Error starting foreground: ${e.message}")
75
81
  }
76
82
  }
77
83
 
@@ -127,6 +133,7 @@ class TrackingService : Service() {
127
133
 
128
134
  override fun onDestroy() {
129
135
  super.onDestroy()
136
+ Log.d("TrackingService", "❌ Service Destroyed")
130
137
  instance = null
131
138
  if (wakeLock?.isHeld == true) {
132
139
  try { wakeLock?.release() } catch (_: Exception) {}
@@ -9,26 +9,43 @@ const RNSensorModule = NativeModules.RNSensorModule ? NativeModules.RNSensorModu
9
9
  });
10
10
  const emitter = new NativeEventEmitter(RNSensorModule);
11
11
  export default {
12
- // Service Control
12
+ // --- SERVICE CONTROL ---
13
13
  startForegroundService: (title, body) => RNSensorModule.startForegroundService(title, body),
14
14
  stopForegroundService: () => RNSensorModule.stopForegroundService(),
15
15
  updateServiceNotification: (title, body) => RNSensorModule.updateServiceNotification(title, body),
16
- // Sensors & Configuration
16
+ // --- MOTION DETECTION (ACTIVITY RECOGNITION) ---
17
+ // Note: Threshold is ignored by the new native engine, but kept for compatibility
17
18
  startMotionDetector: (threshold = 0.8) => RNSensorModule.startMotionDetector(threshold),
18
19
  stopMotionDetector: () => RNSensorModule.stopMotionDetector(),
19
- setUpdateInterval: (ms = 100) => RNSensorModule.setUpdateInterval(ms),
20
+ // --- LOCATION CONTROL ---
20
21
  setLocationUpdateInterval: (ms = 90000) => RNSensorModule.setLocationUpdateInterval(ms),
21
- setStabilityThresholds: (start = 20, stop = 3000) => RNSensorModule.setStabilityThresholds(start, stop),
22
- // Alerts
22
+ // --- ALERTS ---
23
23
  fireGeofenceAlert: (type, userName) => RNSensorModule.fireGeofenceAlert(type, userName),
24
24
  fireGenericAlert: (title, body, id) => RNSensorModule.fireGenericAlert(title, body, id),
25
25
  cancelGenericAlert: id => RNSensorModule.cancelGenericAlert(id),
26
- isAvailable: () => RNSensorModule.isAvailable(),
27
- // --- NEW GPS HARDWARE METHODS ---
26
+ // --- GPS STATUS ---
28
27
  registerGpsListener: () => RNSensorModule.registerGpsListener(),
29
28
  addGpsStatusListener: cb => emitter.addListener('onGpsStatusChanged', event => cb(event)),
30
- // Listeners
29
+ // --- EVENT LISTENERS ---
30
+
31
+ /**
32
+ * Returns:
33
+ * {
34
+ * activity: "WALKING" | "STILL" | "IN_VEHICLE" ...,
35
+ * isMoving: boolean,
36
+ * state: "MOVING" | "STATIONARY" (Legacy support)
37
+ * }
38
+ */
31
39
  addMotionListener: cb => emitter.addListener('onMotionStateChanged', cb),
40
+ /**
41
+ * Returns:
42
+ * {
43
+ * latitude: number,
44
+ * longitude: number,
45
+ * accuracy: number,
46
+ * is_mock: boolean (NEW: Detects Fake GPS)
47
+ * }
48
+ */
32
49
  addLocationLogListener: cb => emitter.addListener('onLocationLog', cb),
33
50
  addLocationErrorListener: cb => emitter.addListener('onLocationError', cb)
34
51
  };
@@ -1 +1 @@
1
- {"version":3,"names":["NativeModules","NativeEventEmitter","LINKING_ERROR","RNSensorModule","Proxy","get","Error","emitter","startForegroundService","title","body","stopForegroundService","updateServiceNotification","startMotionDetector","threshold","stopMotionDetector","setUpdateInterval","ms","setLocationUpdateInterval","setStabilityThresholds","start","stop","fireGeofenceAlert","type","userName","fireGenericAlert","id","cancelGenericAlert","isAvailable","registerGpsListener","addGpsStatusListener","cb","addListener","event","addMotionListener","addLocationLogListener","addLocationErrorListener"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,SAASA,aAAa,EAAEC,kBAAkB,QAAQ,cAAc;AAEhE,MAAMC,aAAa,GACjB,wFAAwF,GACxF,sDAAsD,GACtD,+BAA+B;AAEjC,MAAMC,cAAc,GAAGH,aAAa,CAACG,cAAc,GAC/CH,aAAa,CAACG,cAAc,GAC5B,IAAIC,KAAK,CACP,CAAC,CAAC,EACF;EACEC,GAAGA,CAAA,EAAG;IACJ,MAAM,IAAIC,KAAK,CAACJ,aAAa,CAAC;EAChC;AACF,CACF,CAAC;AAEL,MAAMK,OAAO,GAAG,IAAIN,kBAAkB,CAACE,cAAc,CAAC;AAEtD,eAAe;EACb;EACAK,sBAAsB,EAAEA,CAACC,KAAa,EAAEC,IAAY,KAClDP,cAAc,CAACK,sBAAsB,CAACC,KAAK,EAAEC,IAAI,CAAC;EAEpDC,qBAAqB,EAAEA,CAAA,KACrBR,cAAc,CAACQ,qBAAqB,CAAC,CAAC;EAExCC,yBAAyB,EAAEA,CAACH,KAAa,EAAEC,IAAY,KACrDP,cAAc,CAACS,yBAAyB,CAACH,KAAK,EAAEC,IAAI,CAAC;EAEvD;EACAG,mBAAmB,EAAEA,CAACC,SAAiB,GAAG,GAAG,KAC3CX,cAAc,CAACU,mBAAmB,CAACC,SAAS,CAAC;EAE/CC,kBAAkB,EAAEA,CAAA,KAAMZ,cAAc,CAACY,kBAAkB,CAAC,CAAC;EAE7DC,iBAAiB,EAAEA,CAACC,EAAU,GAAG,GAAG,KAAKd,cAAc,CAACa,iBAAiB,CAACC,EAAE,CAAC;EAE7EC,yBAAyB,EAAEA,CAACD,EAAU,GAAG,KAAK,KAC5Cd,cAAc,CAACe,yBAAyB,CAACD,EAAE,CAAC;EAE9CE,sBAAsB,EAAEA,CAACC,KAAa,GAAG,EAAE,EAAEC,IAAY,GAAG,IAAI,KAC9DlB,cAAc,CAACgB,sBAAsB,CAACC,KAAK,EAAEC,IAAI,CAAC;EAEpD;EACAC,iBAAiB,EAAEA,CAACC,IAAY,EAAEC,QAAgB,KAChDrB,cAAc,CAACmB,iBAAiB,CAACC,IAAI,EAAEC,QAAQ,CAAC;EAElDC,gBAAgB,EAAEA,CAAChB,KAAa,EAAEC,IAAY,EAAEgB,EAAU,KACxDvB,cAAc,CAACsB,gBAAgB,CAAChB,KAAK,EAAEC,IAAI,EAAEgB,EAAE,CAAC;EAElDC,kBAAkB,EAAGD,EAAU,IAAKvB,cAAc,CAACwB,kBAAkB,CAACD,EAAE,CAAC;EAEzEE,WAAW,EAAEA,CAAA,KAAMzB,cAAc,CAACyB,WAAW,CAAC,CAAC;EAE/C;EACAC,mBAAmB,EAAEA,CAAA,KACnB1B,cAAc,CAAC0B,mBAAmB,CAAC,CAAC;EAEtCC,oBAAoB,EAAGC,EAAyC,IAC9DxB,OAAO,CAACyB,WAAW,CAAC,oBAAoB,EAAGC,KAAU,IAAKF,EAAE,CAACE,KAAK,CAAC,CAAC;EAEtE;EACAC,iBAAiB,EAAGH,EAAwB,IAC1CxB,OAAO,CAACyB,WAAW,CAAC,sBAAsB,EAAED,EAAE,CAAC;EAEjDI,sBAAsB,EAAGJ,EAAwB,IAC/CxB,OAAO,CAACyB,WAAW,CAAC,eAAe,EAAED,EAAE,CAAC;EAE1CK,wBAAwB,EAAGL,EAAwB,IACjDxB,OAAO,CAACyB,WAAW,CAAC,iBAAiB,EAAED,EAAE;AAC7C,CAAC","ignoreList":[]}
1
+ {"version":3,"names":["NativeModules","NativeEventEmitter","LINKING_ERROR","RNSensorModule","Proxy","get","Error","emitter","startForegroundService","title","body","stopForegroundService","updateServiceNotification","startMotionDetector","threshold","stopMotionDetector","setLocationUpdateInterval","ms","fireGeofenceAlert","type","userName","fireGenericAlert","id","cancelGenericAlert","registerGpsListener","addGpsStatusListener","cb","addListener","event","addMotionListener","addLocationLogListener","addLocationErrorListener"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,SAASA,aAAa,EAAEC,kBAAkB,QAAQ,cAAc;AAEhE,MAAMC,aAAa,GACjB,wFAAwF,GACxF,sDAAsD,GACtD,+BAA+B;AAEjC,MAAMC,cAAc,GAAGH,aAAa,CAACG,cAAc,GAC/CH,aAAa,CAACG,cAAc,GAC5B,IAAIC,KAAK,CACP,CAAC,CAAC,EACF;EACEC,GAAGA,CAAA,EAAG;IACJ,MAAM,IAAIC,KAAK,CAACJ,aAAa,CAAC;EAChC;AACF,CACF,CAAC;AAEL,MAAMK,OAAO,GAAG,IAAIN,kBAAkB,CAACE,cAAc,CAAC;AAEtD,eAAe;EACb;EACAK,sBAAsB,EAAEA,CAACC,KAAa,EAAEC,IAAY,KAClDP,cAAc,CAACK,sBAAsB,CAACC,KAAK,EAAEC,IAAI,CAAC;EAEpDC,qBAAqB,EAAEA,CAAA,KACrBR,cAAc,CAACQ,qBAAqB,CAAC,CAAC;EAExCC,yBAAyB,EAAEA,CAACH,KAAa,EAAEC,IAAY,KACrDP,cAAc,CAACS,yBAAyB,CAACH,KAAK,EAAEC,IAAI,CAAC;EAEvD;EACA;EACAG,mBAAmB,EAAEA,CAACC,SAAiB,GAAG,GAAG,KAC3CX,cAAc,CAACU,mBAAmB,CAACC,SAAS,CAAC;EAE/CC,kBAAkB,EAAEA,CAAA,KAClBZ,cAAc,CAACY,kBAAkB,CAAC,CAAC;EAErC;EACAC,yBAAyB,EAAEA,CAACC,EAAU,GAAG,KAAK,KAC5Cd,cAAc,CAACa,yBAAyB,CAACC,EAAE,CAAC;EAE9C;EACAC,iBAAiB,EAAEA,CAACC,IAAY,EAAEC,QAAgB,KAChDjB,cAAc,CAACe,iBAAiB,CAACC,IAAI,EAAEC,QAAQ,CAAC;EAElDC,gBAAgB,EAAEA,CAACZ,KAAa,EAAEC,IAAY,EAAEY,EAAU,KACxDnB,cAAc,CAACkB,gBAAgB,CAACZ,KAAK,EAAEC,IAAI,EAAEY,EAAE,CAAC;EAElDC,kBAAkB,EAAGD,EAAU,IAC7BnB,cAAc,CAACoB,kBAAkB,CAACD,EAAE,CAAC;EAEvC;EACAE,mBAAmB,EAAEA,CAAA,KACnBrB,cAAc,CAACqB,mBAAmB,CAAC,CAAC;EAEtCC,oBAAoB,EAAGC,EAAyC,IAC9DnB,OAAO,CAACoB,WAAW,CAAC,oBAAoB,EAAGC,KAAU,IAAKF,EAAE,CAACE,KAAK,CAAC,CAAC;EAEtE;;EAEA;AACF;AACA;AACA;AACA;AACA;AACA;AACA;EACEC,iBAAiB,EAAGH,EAAwB,IAC1CnB,OAAO,CAACoB,WAAW,CAAC,sBAAsB,EAAED,EAAE,CAAC;EAEjD;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;EACEI,sBAAsB,EAAGJ,EAAwB,IAC/CnB,OAAO,CAACoB,WAAW,CAAC,eAAe,EAAED,EAAE,CAAC;EAE1CK,wBAAwB,EAAGL,EAAwB,IACjDnB,OAAO,CAACoB,WAAW,CAAC,iBAAiB,EAAED,EAAE;AAC7C,CAAC","ignoreList":[]}
@@ -2,20 +2,34 @@ declare const _default: {
2
2
  startForegroundService: (title: string, body: string) => Promise<boolean>;
3
3
  stopForegroundService: () => Promise<boolean>;
4
4
  updateServiceNotification: (title: string, body: string) => Promise<boolean>;
5
- startMotionDetector: (threshold?: number) => any;
6
- stopMotionDetector: () => any;
7
- setUpdateInterval: (ms?: number) => any;
5
+ startMotionDetector: (threshold?: number) => Promise<boolean>;
6
+ stopMotionDetector: () => Promise<boolean>;
8
7
  setLocationUpdateInterval: (ms?: number) => any;
9
- setStabilityThresholds: (start?: number, stop?: number) => any;
10
- fireGeofenceAlert: (type: string, userName: string) => any;
11
- fireGenericAlert: (title: string, body: string, id: number) => any;
12
- cancelGenericAlert: (id: number) => any;
13
- isAvailable: () => any;
8
+ fireGeofenceAlert: (type: string, userName: string) => Promise<boolean>;
9
+ fireGenericAlert: (title: string, body: string, id: number) => Promise<boolean>;
10
+ cancelGenericAlert: (id: number) => Promise<boolean>;
14
11
  registerGpsListener: () => Promise<boolean>;
15
12
  addGpsStatusListener: (cb: (event: {
16
13
  enabled: boolean;
17
14
  }) => void) => import("react-native").EventSubscription;
15
+ /**
16
+ * Returns:
17
+ * {
18
+ * activity: "WALKING" | "STILL" | "IN_VEHICLE" ...,
19
+ * isMoving: boolean,
20
+ * state: "MOVING" | "STATIONARY" (Legacy support)
21
+ * }
22
+ */
18
23
  addMotionListener: (cb: (event: any) => void) => import("react-native").EventSubscription;
24
+ /**
25
+ * Returns:
26
+ * {
27
+ * latitude: number,
28
+ * longitude: number,
29
+ * accuracy: number,
30
+ * is_mock: boolean (NEW: Detects Fake GPS)
31
+ * }
32
+ */
19
33
  addLocationLogListener: (cb: (event: any) => void) => import("react-native").EventSubscription;
20
34
  addLocationErrorListener: (cb: (event: any) => void) => import("react-native").EventSubscription;
21
35
  };
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":";oCAsBkC,MAAM,QAAQ,MAAM,KAAG,OAAO,CAAC,OAAO,CAAC;iCAG5C,OAAO,CAAC,OAAO,CAAC;uCAGR,MAAM,QAAQ,MAAM,KAAG,OAAO,CAAC,OAAO,CAAC;sCAIzC,MAAM;;6BAKf,MAAM;qCAEE,MAAM;qCAGN,MAAM,SAAa,MAAM;8BAI/B,MAAM,YAAY,MAAM;8BAGxB,MAAM,QAAQ,MAAM,MAAM,MAAM;6BAGjC,MAAM;;+BAKN,OAAO,CAAC,OAAO,CAAC;+BAGd,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,KAAK,IAAI;4BAIxC,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI;iCAGf,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI;mCAGlB,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI;;AAlDrD,wBAoDE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":";oCAsBkC,MAAM,QAAQ,MAAM,KAAG,OAAO,CAAC,OAAO,CAAC;iCAG5C,OAAO,CAAC,OAAO,CAAC;uCAGR,MAAM,QAAQ,MAAM,KAAG,OAAO,CAAC,OAAO,CAAC;sCAKzC,MAAM,KAAS,OAAO,CAAC,OAAO,CAAC;8BAGxC,OAAO,CAAC,OAAO,CAAC;qCAIR,MAAM;8BAIZ,MAAM,YAAY,MAAM,KAAG,OAAO,CAAC,OAAO,CAAC;8BAG3C,MAAM,QAAQ,MAAM,MAAM,MAAM,KAAG,OAAO,CAAC,OAAO,CAAC;6BAGpD,MAAM,KAAG,OAAO,CAAC,OAAO,CAAC;+BAIzB,OAAO,CAAC,OAAO,CAAC;+BAGd,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,KAAK,IAAI;IAKhE;;;;;;;OAOG;4BACqB,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI;IAG5C;;;;;;;;OAQG;iCAC0B,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI;mCAGlB,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI;;AAjErD,wBAmEE"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-geo-activity-kit",
3
- "version": "1.2.4",
3
+ "version": "1.2.5",
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
@@ -19,7 +19,7 @@ const RNSensorModule = NativeModules.RNSensorModule
19
19
  const emitter = new NativeEventEmitter(RNSensorModule);
20
20
 
21
21
  export default {
22
- // Service Control
22
+ // --- SERVICE CONTROL ---
23
23
  startForegroundService: (title: string, body: string): Promise<boolean> =>
24
24
  RNSensorModule.startForegroundService(title, body),
25
25
 
@@ -29,45 +29,60 @@ export default {
29
29
  updateServiceNotification: (title: string, body: string): Promise<boolean> =>
30
30
  RNSensorModule.updateServiceNotification(title, body),
31
31
 
32
- // Sensors & Configuration
33
- startMotionDetector: (threshold: number = 0.8) =>
32
+ // --- MOTION DETECTION (ACTIVITY RECOGNITION) ---
33
+ // Note: Threshold is ignored by the new native engine, but kept for compatibility
34
+ startMotionDetector: (threshold: number = 0.8): Promise<boolean> =>
34
35
  RNSensorModule.startMotionDetector(threshold),
35
36
 
36
- stopMotionDetector: () => RNSensorModule.stopMotionDetector(),
37
-
38
- setUpdateInterval: (ms: number = 100) => RNSensorModule.setUpdateInterval(ms),
37
+ stopMotionDetector: (): Promise<boolean> =>
38
+ RNSensorModule.stopMotionDetector(),
39
39
 
40
+ // --- LOCATION CONTROL ---
40
41
  setLocationUpdateInterval: (ms: number = 90000) =>
41
42
  RNSensorModule.setLocationUpdateInterval(ms),
42
43
 
43
- setStabilityThresholds: (start: number = 20, stop: number = 3000) =>
44
- RNSensorModule.setStabilityThresholds(start, stop),
45
-
46
- // Alerts
47
- fireGeofenceAlert: (type: string, userName: string) =>
44
+ // --- ALERTS ---
45
+ fireGeofenceAlert: (type: string, userName: string): Promise<boolean> =>
48
46
  RNSensorModule.fireGeofenceAlert(type, userName),
49
47
 
50
- fireGenericAlert: (title: string, body: string, id: number) =>
48
+ fireGenericAlert: (title: string, body: string, id: number): Promise<boolean> =>
51
49
  RNSensorModule.fireGenericAlert(title, body, id),
52
50
 
53
- cancelGenericAlert: (id: number) => RNSensorModule.cancelGenericAlert(id),
54
-
55
- isAvailable: () => RNSensorModule.isAvailable(),
51
+ cancelGenericAlert: (id: number): Promise<boolean> =>
52
+ RNSensorModule.cancelGenericAlert(id),
56
53
 
57
- // --- NEW GPS HARDWARE METHODS ---
54
+ // --- GPS STATUS ---
58
55
  registerGpsListener: (): Promise<boolean> =>
59
56
  RNSensorModule.registerGpsListener(),
60
57
 
61
58
  addGpsStatusListener: (cb: (event: { enabled: boolean }) => void) =>
62
59
  emitter.addListener('onGpsStatusChanged', (event: any) => cb(event)),
63
60
 
64
- // Listeners
61
+ // --- EVENT LISTENERS ---
62
+
63
+ /**
64
+ * Returns:
65
+ * {
66
+ * activity: "WALKING" | "STILL" | "IN_VEHICLE" ...,
67
+ * isMoving: boolean,
68
+ * state: "MOVING" | "STATIONARY" (Legacy support)
69
+ * }
70
+ */
65
71
  addMotionListener: (cb: (event: any) => void) =>
66
72
  emitter.addListener('onMotionStateChanged', cb),
67
73
 
74
+ /**
75
+ * Returns:
76
+ * {
77
+ * latitude: number,
78
+ * longitude: number,
79
+ * accuracy: number,
80
+ * is_mock: boolean (NEW: Detects Fake GPS)
81
+ * }
82
+ */
68
83
  addLocationLogListener: (cb: (event: any) => void) =>
69
84
  emitter.addListener('onLocationLog', cb),
70
85
 
71
86
  addLocationErrorListener: (cb: (event: any) => void) =>
72
87
  emitter.addListener('onLocationError', cb),
73
- };
88
+ };
@@ -1,5 +0,0 @@
1
- "use strict";
2
-
3
- import { TurboModuleRegistry } from 'react-native';
4
- export default TurboModuleRegistry.getEnforcing('GeoActivityKit');
5
- //# sourceMappingURL=NativeGeoActivityKit.js.map
@@ -1 +0,0 @@
1
- {"version":3,"names":["TurboModuleRegistry","getEnforcing"],"sourceRoot":"../../src","sources":["NativeGeoActivityKit.ts"],"mappings":";;AAAA,SAASA,mBAAmB,QAA0B,cAAc;AAMpE,eAAeA,mBAAmB,CAACC,YAAY,CAAO,gBAAgB,CAAC","ignoreList":[]}
@@ -1,7 +0,0 @@
1
- import { type TurboModule } from 'react-native';
2
- export interface Spec extends TurboModule {
3
- multiply(a: number, b: number): number;
4
- }
5
- declare const _default: Spec;
6
- export default _default;
7
- //# sourceMappingURL=NativeGeoActivityKit.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"NativeGeoActivityKit.d.ts","sourceRoot":"","sources":["../../../src/NativeGeoActivityKit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAErE,MAAM,WAAW,IAAK,SAAQ,WAAW;IACvC,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CACxC;;AAED,wBAAwE"}
@@ -1,7 +0,0 @@
1
- import { TurboModuleRegistry, type TurboModule } from 'react-native';
2
-
3
- export interface Spec extends TurboModule {
4
- multiply(a: number, b: number): number;
5
- }
6
-
7
- export default TurboModuleRegistry.getEnforcing<Spec>('GeoActivityKit');