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.
- package/android/src/main/AndroidManifest.xml +13 -0
- package/android/src/main/java/com/rngeoactivitykit/ActivityTransitionReceiver.kt +88 -0
- package/android/src/main/java/com/rngeoactivitykit/LocationHelper.kt +44 -17
- package/android/src/main/java/com/rngeoactivitykit/MotionDetector.kt +70 -97
- package/android/src/main/java/com/rngeoactivitykit/SensorModule.kt +123 -100
- package/android/src/main/java/com/rngeoactivitykit/TrackingService.kt +16 -9
- package/lib/module/index.js +55 -28
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/index.d.ts +28 -22
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/index.tsx +102 -66
- package/lib/module/NativeGeoActivityKit.js +0 -5
- package/lib/module/NativeGeoActivityKit.js.map +0 -1
- package/lib/typescript/src/NativeGeoActivityKit.d.ts +0 -7
- package/lib/typescript/src/NativeGeoActivityKit.d.ts.map +0 -1
- package/src/NativeGeoActivityKit.ts +0 -7
|
@@ -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
|
|
54
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
110
|
+
Log.d("LocationHelper", "✅ Location Updates STARTED.")
|
|
85
111
|
} catch (e: Exception) {
|
|
86
|
-
|
|
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.
|
|
121
|
+
Log.d("LocationHelper", "🛑 Location Updates STOPPED.")
|
|
99
122
|
} catch (e: Exception) {
|
|
100
|
-
Log.e("LocationHelper", "Failed to stop
|
|
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
|
-
|
|
112
|
-
context.
|
|
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.
|
|
4
|
-
import android.
|
|
5
|
-
import android.
|
|
6
|
-
import android.
|
|
7
|
-
import android.
|
|
8
|
-
import
|
|
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.
|
|
11
|
-
import
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
|
|
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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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
|
-
|
|
47
|
-
|
|
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
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
-
|
|
19
|
-
|
|
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
|
-
// ---
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
// ---
|
|
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("
|
|
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("
|
|
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
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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
|
-
|
|
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
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
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
|
-
|
|
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
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
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) {
|
|
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) {
|
|
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) {
|
|
169
|
+
} catch (e: Exception) {
|
|
170
|
+
promise.reject("CANCEL_FAILED", e.message)
|
|
171
|
+
}
|
|
189
172
|
}
|
|
190
173
|
|
|
191
|
-
@ReactMethod
|
|
192
|
-
|
|
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
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
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(
|
|
41
|
+
wakeLock?.acquire()
|
|
42
|
+
Log.d("TrackingService", "🔒 WakeLock Acquired (Permanent)")
|
|
42
43
|
} catch (e: Exception) {
|
|
43
|
-
e.
|
|
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
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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) {}
|
package/lib/module/index.js
CHANGED
|
@@ -1,35 +1,62 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
import { NativeModules,
|
|
4
|
-
const LINKING_ERROR = `The package 'react-native-geo-activity-kit' doesn't seem to be linked. Make sure: \n\n` +
|
|
5
|
-
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
package/lib/module/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["NativeModules","
|
|
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
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
export
|
|
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":";
|
|
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": "
|
|
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,
|
|
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
|
-
|
|
9
|
-
|
|
9
|
+
// 1. Get the Native Module
|
|
10
|
+
const GeoActivityKit = NativeModules.GeoActivityKit
|
|
11
|
+
? NativeModules.GeoActivityKit
|
|
10
12
|
: new Proxy(
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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 +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 +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"}
|