react-native-geo-activity-kit 1.2.4 → 2.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.
@@ -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) {}
@@ -1,35 +1,62 @@
1
1
  "use strict";
2
2
 
3
- import { NativeModules, NativeEventEmitter } from 'react-native';
4
- const LINKING_ERROR = `The package 'react-native-geo-activity-kit' doesn't seem to be linked. Make sure: \n\n` + '- You rebuilt the app after installing the package\n' + '- You are not using Expo Go\n';
5
- const RNSensorModule = NativeModules.RNSensorModule ? NativeModules.RNSensorModule : new Proxy({}, {
3
+ import { NativeModules, Platform } from 'react-native';
4
+ const LINKING_ERROR = `The package 'react-native-geo-activity-kit' doesn't seem to be linked. Make sure: \n\n` + Platform.select({
5
+ ios: "- You have run 'pod install'\n",
6
+ default: ''
7
+ }) + '- You rebuilt the app after installing the package\n' + '- You are not using Expo Go\n';
8
+
9
+ // 1. Get the Native Module
10
+ const GeoActivityKit = NativeModules.GeoActivityKit ? NativeModules.GeoActivityKit : new Proxy({}, {
6
11
  get() {
7
12
  throw new Error(LINKING_ERROR);
8
13
  }
9
14
  });
10
- const emitter = new NativeEventEmitter(RNSensorModule);
11
- export default {
12
- // Service Control
13
- startForegroundService: (title, body) => RNSensorModule.startForegroundService(title, body),
14
- stopForegroundService: () => RNSensorModule.stopForegroundService(),
15
- updateServiceNotification: (title, body) => RNSensorModule.updateServiceNotification(title, body),
16
- // Sensors & Configuration
17
- startMotionDetector: (threshold = 0.8) => RNSensorModule.startMotionDetector(threshold),
18
- stopMotionDetector: () => RNSensorModule.stopMotionDetector(),
19
- setUpdateInterval: (ms = 100) => RNSensorModule.setUpdateInterval(ms),
20
- setLocationUpdateInterval: (ms = 90000) => RNSensorModule.setLocationUpdateInterval(ms),
21
- setStabilityThresholds: (start = 20, stop = 3000) => RNSensorModule.setStabilityThresholds(start, stop),
22
- // Alerts
23
- fireGeofenceAlert: (type, userName) => RNSensorModule.fireGeofenceAlert(type, userName),
24
- fireGenericAlert: (title, body, id) => RNSensorModule.fireGenericAlert(title, body, id),
25
- cancelGenericAlert: id => RNSensorModule.cancelGenericAlert(id),
26
- isAvailable: () => RNSensorModule.isAvailable(),
27
- // --- NEW GPS HARDWARE METHODS ---
28
- registerGpsListener: () => RNSensorModule.registerGpsListener(),
29
- addGpsStatusListener: cb => emitter.addListener('onGpsStatusChanged', event => cb(event)),
30
- // Listeners
31
- addMotionListener: cb => emitter.addListener('onMotionStateChanged', cb),
32
- addLocationLogListener: cb => emitter.addListener('onLocationLog', cb),
33
- addLocationErrorListener: cb => emitter.addListener('onLocationError', cb)
34
- };
15
+
16
+ // 2. Define Types for Events
17
+
18
+ // 3. Export Functions (Named Exports)
19
+
20
+ // --- Service Control ---
21
+ export function startForegroundService(title, body, id) {
22
+ return GeoActivityKit.startForegroundService(title, body, id);
23
+ }
24
+ export function stopForegroundService() {
25
+ return GeoActivityKit.stopForegroundService();
26
+ }
27
+ export function updateServiceNotification(title, body) {
28
+ return GeoActivityKit.updateServiceNotification(title, body);
29
+ }
30
+
31
+ // --- Motion & Intelligence ---
32
+ export function startMotionDetector(confidence) {
33
+ return GeoActivityKit.startMotionDetector(confidence);
34
+ }
35
+ export function stopMotionDetector() {
36
+ return GeoActivityKit.stopMotionDetector();
37
+ }
38
+
39
+ // --- Location Control ---
40
+ export function setLocationUpdateInterval(intervalMs) {
41
+ return GeoActivityKit.setLocationUpdateInterval(intervalMs);
42
+ }
43
+
44
+ // --- Alerts & Notifications ---
45
+ export function fireGeofenceAlert(type, userName) {
46
+ return GeoActivityKit.fireGeofenceAlert(type, userName);
47
+ }
48
+ export function fireGenericAlert(title, body, id) {
49
+ return GeoActivityKit.fireGenericAlert(title, body, id);
50
+ }
51
+ export function cancelGenericAlert(id) {
52
+ return GeoActivityKit.cancelGenericAlert(id);
53
+ }
54
+
55
+ // --- GPS Status ---
56
+ export function registerGpsListener() {
57
+ return GeoActivityKit.registerGpsListener();
58
+ }
59
+
60
+ // 4. Default Export
61
+ export default GeoActivityKit;
35
62
  //# sourceMappingURL=index.js.map
@@ -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","Platform","LINKING_ERROR","select","ios","default","GeoActivityKit","Proxy","get","Error","startForegroundService","title","body","id","stopForegroundService","updateServiceNotification","startMotionDetector","confidence","stopMotionDetector","setLocationUpdateInterval","intervalMs","fireGeofenceAlert","type","userName","fireGenericAlert","cancelGenericAlert","registerGpsListener"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,SAASA,aAAa,EAAEC,QAAQ,QAAQ,cAAc;AAEtD,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;AACA,MAAMC,cAAc,GAAGN,aAAa,CAACM,cAAc,GAC/CN,aAAa,CAACM,cAAc,GAC5B,IAAIC,KAAK,CACT,CAAC,CAAC,EACF;EACEC,GAAGA,CAAA,EAAG;IACJ,MAAM,IAAIC,KAAK,CAACP,aAAa,CAAC;EAChC;AACF,CACF,CAAC;;AAEH;;AA0BA;;AAEA;AACA,OAAO,SAASQ,sBAAsBA,CACpCC,KAAa,EACbC,IAAY,EACZC,EAAU,EACQ;EAClB,OAAOP,cAAc,CAACI,sBAAsB,CAACC,KAAK,EAAEC,IAAI,EAAEC,EAAE,CAAC;AAC/D;AAEA,OAAO,SAASC,qBAAqBA,CAAA,EAAqB;EACxD,OAAOR,cAAc,CAACQ,qBAAqB,CAAC,CAAC;AAC/C;AAEA,OAAO,SAASC,yBAAyBA,CACvCJ,KAAa,EACbC,IAAY,EACM;EAClB,OAAON,cAAc,CAACS,yBAAyB,CAACJ,KAAK,EAAEC,IAAI,CAAC;AAC9D;;AAEA;AACA,OAAO,SAASI,mBAAmBA,CAACC,UAAkB,EAAoB;EACxE,OAAOX,cAAc,CAACU,mBAAmB,CAACC,UAAU,CAAC;AACvD;AAEA,OAAO,SAASC,kBAAkBA,CAAA,EAAqB;EACrD,OAAOZ,cAAc,CAACY,kBAAkB,CAAC,CAAC;AAC5C;;AAEA;AACA,OAAO,SAASC,yBAAyBA,CAACC,UAAkB,EAAiB;EAC3E,OAAOd,cAAc,CAACa,yBAAyB,CAACC,UAAU,CAAC;AAC7D;;AAEA;AACA,OAAO,SAASC,iBAAiBA,CAC/BC,IAAkB,EAClBC,QAAgB,EACE;EAClB,OAAOjB,cAAc,CAACe,iBAAiB,CAACC,IAAI,EAAEC,QAAQ,CAAC;AACzD;AAEA,OAAO,SAASC,gBAAgBA,CAC9Bb,KAAa,EACbC,IAAY,EACZC,EAAU,EACQ;EAClB,OAAOP,cAAc,CAACkB,gBAAgB,CAACb,KAAK,EAAEC,IAAI,EAAEC,EAAE,CAAC;AACzD;AAEA,OAAO,SAASY,kBAAkBA,CAACZ,EAAU,EAAoB;EAC/D,OAAOP,cAAc,CAACmB,kBAAkB,CAACZ,EAAE,CAAC;AAC9C;;AAEA;AACA,OAAO,SAASa,mBAAmBA,CAAA,EAAqB;EACtD,OAAOpB,cAAc,CAACoB,mBAAmB,CAAC,CAAC;AAC7C;;AAEA;AACA,eAAepB,cAAc","ignoreList":[]}
@@ -1,23 +1,29 @@
1
- declare const _default: {
2
- startForegroundService: (title: string, body: string) => Promise<boolean>;
3
- stopForegroundService: () => Promise<boolean>;
4
- updateServiceNotification: (title: string, body: string) => Promise<boolean>;
5
- startMotionDetector: (threshold?: number) => any;
6
- stopMotionDetector: () => any;
7
- setUpdateInterval: (ms?: number) => any;
8
- 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;
14
- registerGpsListener: () => Promise<boolean>;
15
- addGpsStatusListener: (cb: (event: {
16
- enabled: boolean;
17
- }) => void) => import("react-native").EventSubscription;
18
- addMotionListener: (cb: (event: any) => void) => import("react-native").EventSubscription;
19
- addLocationLogListener: (cb: (event: any) => void) => import("react-native").EventSubscription;
20
- addLocationErrorListener: (cb: (event: any) => void) => import("react-native").EventSubscription;
21
- };
22
- export default _default;
1
+ declare const GeoActivityKit: any;
2
+ export interface LocationEvent {
3
+ latitude: number;
4
+ longitude: number;
5
+ accuracy: number;
6
+ timestamp: string;
7
+ is_mock: boolean;
8
+ }
9
+ export interface MotionEvent {
10
+ activity: 'STILL' | 'WALKING' | 'RUNNING' | 'ON_BICYCLE' | 'IN_VEHICLE' | 'UNKNOWN';
11
+ isMoving: boolean;
12
+ state: 'MOVING' | 'STATIONARY';
13
+ }
14
+ export interface ErrorEvent {
15
+ error: string;
16
+ message: string;
17
+ }
18
+ export declare function startForegroundService(title: string, body: string, id: number): Promise<boolean>;
19
+ export declare function stopForegroundService(): Promise<boolean>;
20
+ export declare function updateServiceNotification(title: string, body: string): Promise<boolean>;
21
+ export declare function startMotionDetector(confidence: number): Promise<boolean>;
22
+ export declare function stopMotionDetector(): Promise<boolean>;
23
+ export declare function setLocationUpdateInterval(intervalMs: number): Promise<void>;
24
+ export declare function fireGeofenceAlert(type: 'IN' | 'OUT', userName: string): Promise<boolean>;
25
+ export declare function fireGenericAlert(title: string, body: string, id: number): Promise<boolean>;
26
+ export declare function cancelGenericAlert(id: number): Promise<boolean>;
27
+ export declare function registerGpsListener(): Promise<boolean>;
28
+ export default GeoActivityKit;
23
29
  //# sourceMappingURL=index.d.ts.map
@@ -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":"AASA,QAAA,MAAM,cAAc,KASjB,CAAC;AAGJ,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,EACN,OAAO,GACP,SAAS,GACT,SAAS,GACT,YAAY,GACZ,YAAY,GACZ,SAAS,CAAC;IACZ,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;AAKD,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,GACT,OAAO,CAAC,OAAO,CAAC,CAElB;AAED,wBAAgB,qBAAqB,IAAI,OAAO,CAAC,OAAO,CAAC,CAExD;AAED,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,OAAO,CAAC,CAElB;AAGD,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAExE;AAED,wBAAgB,kBAAkB,IAAI,OAAO,CAAC,OAAO,CAAC,CAErD;AAGD,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE3E;AAGD,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,IAAI,GAAG,KAAK,EAClB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,OAAO,CAAC,CAElB;AAED,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,GACT,OAAO,CAAC,OAAO,CAAC,CAElB;AAED,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAE/D;AAGD,wBAAgB,mBAAmB,IAAI,OAAO,CAAC,OAAO,CAAC,CAEtD;AAGD,eAAe,cAAc,CAAC"}
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": "2.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,73 +1,109 @@
1
- import { NativeModules, NativeEventEmitter } from 'react-native';
1
+ import { NativeModules, Platform } from 'react-native';
2
2
 
3
3
  const LINKING_ERROR =
4
4
  `The package 'react-native-geo-activity-kit' doesn't seem to be linked. Make sure: \n\n` +
5
+ Platform.select({ ios: "- You have run 'pod install'\n", default: '' }) +
5
6
  '- You rebuilt the app after installing the package\n' +
6
7
  '- You are not using Expo Go\n';
7
8
 
8
- const RNSensorModule = NativeModules.RNSensorModule
9
- ? NativeModules.RNSensorModule
9
+ // 1. Get the Native Module
10
+ const GeoActivityKit = NativeModules.GeoActivityKit
11
+ ? NativeModules.GeoActivityKit
10
12
  : new Proxy(
11
- {},
12
- {
13
- get() {
14
- throw new Error(LINKING_ERROR);
15
- },
16
- }
17
- );
18
-
19
- const emitter = new NativeEventEmitter(RNSensorModule);
20
-
21
- export default {
22
- // Service Control
23
- startForegroundService: (title: string, body: string): Promise<boolean> =>
24
- RNSensorModule.startForegroundService(title, body),
25
-
26
- stopForegroundService: (): Promise<boolean> =>
27
- RNSensorModule.stopForegroundService(),
28
-
29
- updateServiceNotification: (title: string, body: string): Promise<boolean> =>
30
- RNSensorModule.updateServiceNotification(title, body),
31
-
32
- // Sensors & Configuration
33
- startMotionDetector: (threshold: number = 0.8) =>
34
- RNSensorModule.startMotionDetector(threshold),
35
-
36
- stopMotionDetector: () => RNSensorModule.stopMotionDetector(),
37
-
38
- setUpdateInterval: (ms: number = 100) => RNSensorModule.setUpdateInterval(ms),
39
-
40
- setLocationUpdateInterval: (ms: number = 90000) =>
41
- RNSensorModule.setLocationUpdateInterval(ms),
42
-
43
- setStabilityThresholds: (start: number = 20, stop: number = 3000) =>
44
- RNSensorModule.setStabilityThresholds(start, stop),
45
-
46
- // Alerts
47
- fireGeofenceAlert: (type: string, userName: string) =>
48
- RNSensorModule.fireGeofenceAlert(type, userName),
49
-
50
- fireGenericAlert: (title: string, body: string, id: number) =>
51
- RNSensorModule.fireGenericAlert(title, body, id),
52
-
53
- cancelGenericAlert: (id: number) => RNSensorModule.cancelGenericAlert(id),
54
-
55
- isAvailable: () => RNSensorModule.isAvailable(),
56
-
57
- // --- NEW GPS HARDWARE METHODS ---
58
- registerGpsListener: (): Promise<boolean> =>
59
- RNSensorModule.registerGpsListener(),
60
-
61
- addGpsStatusListener: (cb: (event: { enabled: boolean }) => void) =>
62
- emitter.addListener('onGpsStatusChanged', (event: any) => cb(event)),
63
-
64
- // Listeners
65
- addMotionListener: (cb: (event: any) => void) =>
66
- emitter.addListener('onMotionStateChanged', cb),
67
-
68
- addLocationLogListener: (cb: (event: any) => void) =>
69
- emitter.addListener('onLocationLog', cb),
70
-
71
- addLocationErrorListener: (cb: (event: any) => void) =>
72
- emitter.addListener('onLocationError', cb),
73
- };
13
+ {},
14
+ {
15
+ get() {
16
+ throw new Error(LINKING_ERROR);
17
+ },
18
+ }
19
+ );
20
+
21
+ // 2. Define Types for Events
22
+ export interface LocationEvent {
23
+ latitude: number;
24
+ longitude: number;
25
+ accuracy: number;
26
+ timestamp: string;
27
+ is_mock: boolean;
28
+ }
29
+
30
+ export interface MotionEvent {
31
+ activity:
32
+ | 'STILL'
33
+ | 'WALKING'
34
+ | 'RUNNING'
35
+ | 'ON_BICYCLE'
36
+ | 'IN_VEHICLE'
37
+ | 'UNKNOWN';
38
+ isMoving: boolean;
39
+ state: 'MOVING' | 'STATIONARY';
40
+ }
41
+
42
+ export interface ErrorEvent {
43
+ error: string;
44
+ message: string;
45
+ }
46
+
47
+ // 3. Export Functions (Named Exports)
48
+
49
+ // --- Service Control ---
50
+ export function startForegroundService(
51
+ title: string,
52
+ body: string,
53
+ id: number
54
+ ): Promise<boolean> {
55
+ return GeoActivityKit.startForegroundService(title, body, id);
56
+ }
57
+
58
+ export function stopForegroundService(): Promise<boolean> {
59
+ return GeoActivityKit.stopForegroundService();
60
+ }
61
+
62
+ export function updateServiceNotification(
63
+ title: string,
64
+ body: string
65
+ ): Promise<boolean> {
66
+ return GeoActivityKit.updateServiceNotification(title, body);
67
+ }
68
+
69
+ // --- Motion & Intelligence ---
70
+ export function startMotionDetector(confidence: number): Promise<boolean> {
71
+ return GeoActivityKit.startMotionDetector(confidence);
72
+ }
73
+
74
+ export function stopMotionDetector(): Promise<boolean> {
75
+ return GeoActivityKit.stopMotionDetector();
76
+ }
77
+
78
+ // --- Location Control ---
79
+ export function setLocationUpdateInterval(intervalMs: number): Promise<void> {
80
+ return GeoActivityKit.setLocationUpdateInterval(intervalMs);
81
+ }
82
+
83
+ // --- Alerts & Notifications ---
84
+ export function fireGeofenceAlert(
85
+ type: 'IN' | 'OUT',
86
+ userName: string
87
+ ): Promise<boolean> {
88
+ return GeoActivityKit.fireGeofenceAlert(type, userName);
89
+ }
90
+
91
+ export function fireGenericAlert(
92
+ title: string,
93
+ body: string,
94
+ id: number
95
+ ): Promise<boolean> {
96
+ return GeoActivityKit.fireGenericAlert(title, body, id);
97
+ }
98
+
99
+ export function cancelGenericAlert(id: number): Promise<boolean> {
100
+ return GeoActivityKit.cancelGenericAlert(id);
101
+ }
102
+
103
+ // --- GPS Status ---
104
+ export function registerGpsListener(): Promise<boolean> {
105
+ return GeoActivityKit.registerGpsListener();
106
+ }
107
+
108
+ // 4. Default Export
109
+ export default GeoActivityKit;
@@ -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');