react-native-nitro-geolocation 1.2.6 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (223) hide show
  1. package/README.md +61 -272
  2. package/android/build.gradle +1 -0
  3. package/android/src/main/AndroidManifest.xml +39 -0
  4. package/android/src/main/java/com/margelo/nitro/nitrogeolocation/AndroidFusedLocationProvider.kt +310 -0
  5. package/android/src/main/java/com/margelo/nitro/nitrogeolocation/AndroidFusedRequestFactory.kt +31 -0
  6. package/android/src/main/java/com/margelo/nitro/nitrogeolocation/AndroidGeolocationConversions.kt +71 -0
  7. package/android/src/main/java/com/margelo/nitro/nitrogeolocation/AndroidGeolocationErrors.kt +85 -0
  8. package/android/src/main/java/com/margelo/nitro/nitrogeolocation/AndroidGeolocationOptions.kt +92 -0
  9. package/android/src/main/java/com/margelo/nitro/nitrogeolocation/AndroidLocationSettings.kt +3 -0
  10. package/android/src/main/java/com/margelo/nitro/nitrogeolocation/AndroidProviderRoute.kt +74 -0
  11. package/android/src/main/java/com/margelo/nitro/nitrogeolocation/NitroBackgroundLocation.kt +132 -0
  12. package/android/src/main/java/com/margelo/nitro/nitrogeolocation/NitroGeolocation.kt +219 -476
  13. package/android/src/main/java/com/margelo/nitro/nitrogeolocation/background/AndroidBackgroundHttpSync.kt +134 -0
  14. package/android/src/main/java/com/margelo/nitro/nitrogeolocation/background/AndroidBackgroundPermissions.kt +196 -0
  15. package/android/src/main/java/com/margelo/nitro/nitrogeolocation/background/AndroidBackgroundSerialization.kt +266 -0
  16. package/android/src/main/java/com/margelo/nitro/nitrogeolocation/background/NitroActivityRecognitionReceiver.kt +13 -0
  17. package/android/src/main/java/com/margelo/nitro/nitrogeolocation/background/NitroBackgroundEventHub.kt +65 -0
  18. package/android/src/main/java/com/margelo/nitro/nitrogeolocation/background/NitroBackgroundHeadlessTaskService.kt +58 -0
  19. package/android/src/main/java/com/margelo/nitro/nitrogeolocation/background/NitroBackgroundLocationController.kt +711 -0
  20. package/android/src/main/java/com/margelo/nitro/nitrogeolocation/background/NitroBackgroundLocationService.kt +48 -0
  21. package/android/src/main/java/com/margelo/nitro/nitrogeolocation/background/NitroBackgroundNotificationFactory.kt +37 -0
  22. package/android/src/main/java/com/margelo/nitro/nitrogeolocation/background/NitroBackgroundStore.kt +655 -0
  23. package/android/src/main/java/com/margelo/nitro/nitrogeolocation/background/NitroBootReceiver.kt +26 -0
  24. package/android/src/main/java/com/margelo/nitro/nitrogeolocation/background/NitroGeofenceReceiver.kt +14 -0
  25. package/android/src/main/java/com/margelo/nitro/nitrogeolocation/background/NitroLocationUpdateReceiver.kt +37 -0
  26. package/android/src/test/java/com/margelo/nitro/nitrogeolocation/AndroidFusedLocationProviderTest.kt +64 -0
  27. package/android/src/test/java/com/margelo/nitro/nitrogeolocation/AndroidProviderRouteTest.kt +116 -0
  28. package/ios/IOSBackgroundHttpSync.swift +126 -0
  29. package/ios/IOSBackgroundLocationDelegate.swift +36 -0
  30. package/ios/IOSBackgroundMotion.swift +55 -0
  31. package/ios/IOSBackgroundSerialization.swift +539 -0
  32. package/ios/IOSGeolocationConversions.swift +131 -0
  33. package/ios/IOSGeolocationDelegate.swift +31 -0
  34. package/ios/IOSGeolocationErrors.swift +39 -0
  35. package/ios/IOSGeolocationOptions.swift +135 -0
  36. package/ios/NitroBackgroundLocation.swift +849 -0
  37. package/ios/NitroGeolocation.swift +14 -347
  38. package/nitrogen/generated/android/c++/JActivityRecognitionOptions.hpp +69 -0
  39. package/nitrogen/generated/android/c++/JAndroidBackgroundLocationOptions.hpp +74 -0
  40. package/nitrogen/generated/android/c++/JAndroidBackgroundLocationStatus.hpp +67 -0
  41. package/nitrogen/generated/android/c++/JAndroidBackgroundProvider.hpp +61 -0
  42. package/nitrogen/generated/android/c++/JAndroidForegroundServiceOptions.hpp +90 -0
  43. package/nitrogen/generated/android/c++/JBackgroundEventEnvelope.hpp +129 -0
  44. package/nitrogen/generated/android/c++/JBackgroundEventType.hpp +70 -0
  45. package/nitrogen/generated/android/c++/JBackgroundHttpMethod.hpp +61 -0
  46. package/nitrogen/generated/android/c++/JBackgroundHttpSyncOptions.hpp +131 -0
  47. package/nitrogen/generated/android/c++/JBackgroundHttpSyncResult.hpp +111 -0
  48. package/nitrogen/generated/android/c++/JBackgroundLocation.hpp +110 -0
  49. package/nitrogen/generated/android/c++/JBackgroundLocationOptions.hpp +162 -0
  50. package/nitrogen/generated/android/c++/JBackgroundLocationSource.hpp +73 -0
  51. package/nitrogen/generated/android/c++/JBackgroundLocationState.hpp +70 -0
  52. package/nitrogen/generated/android/c++/JBackgroundLocationStatus.hpp +126 -0
  53. package/nitrogen/generated/android/c++/JBackgroundPermissionResult.hpp +79 -0
  54. package/nitrogen/generated/android/c++/JBackgroundPermissionStatus.hpp +64 -0
  55. package/nitrogen/generated/android/c++/JBackgroundTrackingMode.hpp +61 -0
  56. package/nitrogen/generated/android/c++/JBatterySnapshot.hpp +61 -0
  57. package/nitrogen/generated/android/c++/JDetectedActivity.hpp +66 -0
  58. package/nitrogen/generated/android/c++/JDetectedActivityType.hpp +76 -0
  59. package/nitrogen/generated/android/c++/JFunc_void_BackgroundEventEnvelope.hpp +114 -0
  60. package/nitrogen/generated/android/c++/JFunc_void_BackgroundLocation.hpp +95 -0
  61. package/nitrogen/generated/android/c++/JGeofenceEvent.hpp +94 -0
  62. package/nitrogen/generated/android/c++/JGeofenceRegion.hpp +112 -0
  63. package/nitrogen/generated/android/c++/JGeofenceTransition.hpp +61 -0
  64. package/nitrogen/generated/android/c++/JGeofencingOptions.hpp +82 -0
  65. package/nitrogen/generated/android/c++/JGetStoredBackgroundEventsOptions.hpp +90 -0
  66. package/nitrogen/generated/android/c++/JGetStoredBackgroundLocationsOptions.hpp +69 -0
  67. package/nitrogen/generated/android/c++/JHybridNitroBackgroundLocationSpec.cpp +662 -0
  68. package/nitrogen/generated/android/c++/JHybridNitroBackgroundLocationSpec.hpp +89 -0
  69. package/nitrogen/generated/android/c++/JIOSBackgroundActivityType.hpp +67 -0
  70. package/nitrogen/generated/android/c++/JIOSBackgroundLocationOptions.hpp +79 -0
  71. package/nitrogen/generated/android/c++/JIOSBackgroundLocationStatus.hpp +61 -0
  72. package/nitrogen/generated/android/c++/JLocationProviderStatus.hpp +5 -1
  73. package/nitrogen/generated/android/c++/JStoredBackgroundEventEnvelope.hpp +115 -0
  74. package/nitrogen/generated/android/c++/JStoredBackgroundLocation.hpp +122 -0
  75. package/nitrogen/generated/android/c++/JVariant_NullType_Boolean_String_Double.cpp +34 -0
  76. package/nitrogen/generated/android/c++/JVariant_NullType_Boolean_String_Double.hpp +100 -0
  77. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/ActivityRecognitionOptions.kt +66 -0
  78. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/AndroidBackgroundLocationOptions.kt +66 -0
  79. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/AndroidBackgroundLocationStatus.kt +61 -0
  80. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/AndroidBackgroundProvider.kt +24 -0
  81. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/AndroidForegroundServiceOptions.kt +91 -0
  82. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/BackgroundEventEnvelope.kt +96 -0
  83. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/BackgroundEventType.kt +27 -0
  84. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/BackgroundHttpMethod.kt +24 -0
  85. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/BackgroundHttpSyncOptions.kt +101 -0
  86. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/BackgroundHttpSyncResult.kt +71 -0
  87. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/BackgroundLocation.kt +96 -0
  88. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/BackgroundLocationOptions.kt +136 -0
  89. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/BackgroundLocationSource.kt +28 -0
  90. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/BackgroundLocationState.kt +27 -0
  91. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/BackgroundLocationStatus.kt +116 -0
  92. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/BackgroundPermissionResult.kt +71 -0
  93. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/BackgroundPermissionStatus.kt +25 -0
  94. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/BackgroundTrackingMode.kt +24 -0
  95. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/BatterySnapshot.kt +56 -0
  96. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/DetectedActivity.kt +61 -0
  97. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/DetectedActivityType.kt +29 -0
  98. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/Func_void_BackgroundEventEnvelope.kt +80 -0
  99. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/Func_void_BackgroundLocation.kt +80 -0
  100. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/GeofenceEvent.kt +66 -0
  101. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/GeofenceRegion.kt +96 -0
  102. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/GeofenceTransition.kt +24 -0
  103. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/GeofencingOptions.kt +56 -0
  104. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/GetStoredBackgroundEventsOptions.kt +66 -0
  105. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/GetStoredBackgroundLocationsOptions.kt +66 -0
  106. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/HybridNitroBackgroundLocationSpec.kt +174 -0
  107. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/IOSBackgroundActivityType.kt +26 -0
  108. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/IOSBackgroundLocationOptions.kt +76 -0
  109. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/IOSBackgroundLocationStatus.kt +56 -0
  110. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/LocationProviderStatus.kt +7 -2
  111. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/StoredBackgroundEventEnvelope.kt +76 -0
  112. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/StoredBackgroundLocation.kt +111 -0
  113. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/Variant_NullType_Boolean_String_Double.kt +88 -0
  114. package/nitrogen/generated/android/nitrogeolocation+autolinking.cmake +3 -0
  115. package/nitrogen/generated/android/nitrogeolocationOnLoad.cpp +20 -0
  116. package/nitrogen/generated/ios/NitroGeolocation-Swift-Cxx-Bridge.cpp +104 -15
  117. package/nitrogen/generated/ios/NitroGeolocation-Swift-Cxx-Bridge.hpp +1107 -50
  118. package/nitrogen/generated/ios/NitroGeolocation-Swift-Cxx-Umbrella.hpp +102 -0
  119. package/nitrogen/generated/ios/NitroGeolocationAutolinking.mm +8 -0
  120. package/nitrogen/generated/ios/NitroGeolocationAutolinking.swift +15 -3
  121. package/nitrogen/generated/ios/c++/HybridNitroBackgroundLocationSpecSwift.cpp +11 -0
  122. package/nitrogen/generated/ios/c++/HybridNitroBackgroundLocationSpecSwift.hpp +416 -0
  123. package/nitrogen/generated/ios/swift/ActivityRecognitionOptions.swift +96 -0
  124. package/nitrogen/generated/ios/swift/AndroidBackgroundLocationOptions.swift +76 -0
  125. package/nitrogen/generated/ios/swift/AndroidBackgroundLocationStatus.swift +58 -0
  126. package/nitrogen/generated/ios/swift/AndroidBackgroundProvider.swift +44 -0
  127. package/nitrogen/generated/ios/swift/AndroidForegroundServiceOptions.swift +160 -0
  128. package/nitrogen/generated/ios/swift/BackgroundEventEnvelope.swift +110 -0
  129. package/nitrogen/generated/ios/swift/BackgroundEventType.swift +56 -0
  130. package/nitrogen/generated/ios/swift/BackgroundHttpMethod.swift +44 -0
  131. package/nitrogen/generated/ios/swift/BackgroundHttpSyncOptions.swift +259 -0
  132. package/nitrogen/generated/ios/swift/BackgroundHttpSyncResult.swift +87 -0
  133. package/nitrogen/generated/ios/swift/BackgroundLocation.swift +118 -0
  134. package/nitrogen/generated/ios/swift/BackgroundLocationOptions.swift +292 -0
  135. package/nitrogen/generated/ios/swift/BackgroundLocationSource.swift +60 -0
  136. package/nitrogen/generated/ios/swift/BackgroundLocationState.swift +56 -0
  137. package/nitrogen/generated/ios/swift/BackgroundLocationStatus.swift +124 -0
  138. package/nitrogen/generated/ios/swift/BackgroundPermissionResult.swift +81 -0
  139. package/nitrogen/generated/ios/swift/BackgroundPermissionStatus.swift +48 -0
  140. package/nitrogen/generated/ios/swift/BackgroundTrackingMode.swift +44 -0
  141. package/nitrogen/generated/ios/swift/BatterySnapshot.swift +60 -0
  142. package/nitrogen/generated/ios/swift/DetectedActivity.swift +39 -0
  143. package/nitrogen/generated/ios/swift/DetectedActivityType.swift +64 -0
  144. package/nitrogen/generated/ios/swift/Func_void_BackgroundEventEnvelope.swift +46 -0
  145. package/nitrogen/generated/ios/swift/Func_void_BackgroundHttpSyncResult.swift +46 -0
  146. package/nitrogen/generated/ios/swift/Func_void_BackgroundLocation.swift +46 -0
  147. package/nitrogen/generated/ios/swift/Func_void_BackgroundLocationStatus.swift +46 -0
  148. package/nitrogen/generated/ios/swift/Func_void_BackgroundPermissionResult.swift +46 -0
  149. package/nitrogen/generated/ios/swift/Func_void_std__optional_BackgroundLocationOptions_.swift +46 -0
  150. package/nitrogen/generated/ios/swift/Func_void_std__vector_GeofenceRegion_.swift +46 -0
  151. package/nitrogen/generated/ios/swift/Func_void_std__vector_StoredBackgroundEventEnvelope_.swift +46 -0
  152. package/nitrogen/generated/ios/swift/Func_void_std__vector_StoredBackgroundLocation_.swift +46 -0
  153. package/nitrogen/generated/ios/swift/GeofenceEvent.swift +50 -0
  154. package/nitrogen/generated/ios/swift/GeofenceRegion.swift +195 -0
  155. package/nitrogen/generated/ios/swift/GeofenceTransition.swift +44 -0
  156. package/nitrogen/generated/ios/swift/GeofencingOptions.swift +66 -0
  157. package/nitrogen/generated/ios/swift/GetStoredBackgroundEventsOptions.swift +102 -0
  158. package/nitrogen/generated/ios/swift/GetStoredBackgroundLocationsOptions.swift +96 -0
  159. package/nitrogen/generated/ios/swift/HybridNitroBackgroundLocationSpec.swift +81 -0
  160. package/nitrogen/generated/ios/swift/HybridNitroBackgroundLocationSpec_cxx.swift +654 -0
  161. package/nitrogen/generated/ios/swift/IOSBackgroundActivityType.swift +52 -0
  162. package/nitrogen/generated/ios/swift/IOSBackgroundLocationOptions.swift +125 -0
  163. package/nitrogen/generated/ios/swift/IOSBackgroundLocationStatus.swift +34 -0
  164. package/nitrogen/generated/ios/swift/LocationProviderStatus.swift +19 -1
  165. package/nitrogen/generated/ios/swift/StoredBackgroundEventEnvelope.swift +54 -0
  166. package/nitrogen/generated/ios/swift/StoredBackgroundLocation.swift +120 -0
  167. package/nitrogen/generated/ios/swift/Variant_NullType_Bool_String_Double.swift +34 -0
  168. package/nitrogen/generated/shared/c++/ActivityRecognitionOptions.hpp +95 -0
  169. package/nitrogen/generated/shared/c++/AndroidBackgroundLocationOptions.hpp +100 -0
  170. package/nitrogen/generated/shared/c++/AndroidBackgroundLocationStatus.hpp +93 -0
  171. package/nitrogen/generated/shared/c++/AndroidBackgroundProvider.hpp +80 -0
  172. package/nitrogen/generated/shared/c++/AndroidForegroundServiceOptions.hpp +116 -0
  173. package/nitrogen/generated/shared/c++/BackgroundEventEnvelope.hpp +140 -0
  174. package/nitrogen/generated/shared/c++/BackgroundEventType.hpp +92 -0
  175. package/nitrogen/generated/shared/c++/BackgroundHttpMethod.hpp +80 -0
  176. package/nitrogen/generated/shared/c++/BackgroundHttpSyncOptions.hpp +129 -0
  177. package/nitrogen/generated/shared/c++/BackgroundHttpSyncResult.hpp +101 -0
  178. package/nitrogen/generated/shared/c++/BackgroundLocation.hpp +134 -0
  179. package/nitrogen/generated/shared/c++/BackgroundLocationOptions.hpp +174 -0
  180. package/nitrogen/generated/shared/c++/BackgroundLocationSource.hpp +96 -0
  181. package/nitrogen/generated/shared/c++/BackgroundLocationState.hpp +92 -0
  182. package/nitrogen/generated/shared/c++/BackgroundLocationStatus.hpp +158 -0
  183. package/nitrogen/generated/shared/c++/BackgroundPermissionResult.hpp +107 -0
  184. package/nitrogen/generated/shared/c++/BackgroundPermissionStatus.hpp +84 -0
  185. package/nitrogen/generated/shared/c++/BackgroundTrackingMode.hpp +80 -0
  186. package/nitrogen/generated/shared/c++/BatterySnapshot.hpp +87 -0
  187. package/nitrogen/generated/shared/c++/DetectedActivity.hpp +92 -0
  188. package/nitrogen/generated/shared/c++/DetectedActivityType.hpp +100 -0
  189. package/nitrogen/generated/shared/c++/GeofenceEvent.hpp +103 -0
  190. package/nitrogen/generated/shared/c++/GeofenceRegion.hpp +123 -0
  191. package/nitrogen/generated/shared/c++/GeofenceTransition.hpp +80 -0
  192. package/nitrogen/generated/shared/c++/GeofencingOptions.hpp +90 -0
  193. package/nitrogen/generated/shared/c++/GetStoredBackgroundEventsOptions.hpp +98 -0
  194. package/nitrogen/generated/shared/c++/GetStoredBackgroundLocationsOptions.hpp +95 -0
  195. package/nitrogen/generated/shared/c++/HybridNitroBackgroundLocationSpec.cpp +47 -0
  196. package/nitrogen/generated/shared/c++/HybridNitroBackgroundLocationSpec.hpp +133 -0
  197. package/nitrogen/generated/shared/c++/IOSBackgroundActivityType.hpp +88 -0
  198. package/nitrogen/generated/shared/c++/IOSBackgroundLocationOptions.hpp +105 -0
  199. package/nitrogen/generated/shared/c++/IOSBackgroundLocationStatus.hpp +87 -0
  200. package/nitrogen/generated/shared/c++/LocationProviderStatus.hpp +5 -1
  201. package/nitrogen/generated/shared/c++/StoredBackgroundEventEnvelope.hpp +108 -0
  202. package/nitrogen/generated/shared/c++/StoredBackgroundLocation.hpp +146 -0
  203. package/package.json +17 -6
  204. package/src/NitroGeolocation.nitro.ts +4 -4
  205. package/src/api/getCurrentPosition.ts +1 -1
  206. package/src/api/requestLocationSettings.ts +3 -2
  207. package/src/background/NitroBackgroundLocation.nitro.ts +83 -0
  208. package/src/background/index.ts +200 -0
  209. package/src/background/index.web.ts +120 -0
  210. package/src/background/task.ts +13 -0
  211. package/src/background/types.ts +328 -0
  212. package/src/compat/index.web.ts +141 -0
  213. package/src/index.tsx +3 -0
  214. package/src/index.web.tsx +41 -0
  215. package/src/publicTypes.ts +6 -5
  216. package/src/types.ts +3 -0
  217. package/src/web/browser.ts +171 -0
  218. package/src/web/index.ts +167 -0
  219. package/src/web/useWatchPosition.ts +76 -0
  220. package/src/web/watch.ts +81 -0
  221. package/src/devtools/index.test.ts +0 -54
  222. package/src/utils/errors.test.ts +0 -65
  223. package/src/utils/provider.test.ts +0 -303
@@ -0,0 +1,711 @@
1
+ package com.margelo.nitro.nitrogeolocation.background
2
+
3
+ import android.Manifest
4
+ import android.annotation.SuppressLint
5
+ import android.app.PendingIntent
6
+ import android.content.Context
7
+ import android.content.Intent
8
+ import android.content.pm.PackageManager
9
+ import android.location.Location
10
+ import android.location.LocationManager
11
+ import android.os.Build
12
+ import androidx.core.content.ContextCompat
13
+ import com.facebook.react.HeadlessJsTaskService
14
+ import com.facebook.react.bridge.ReactApplicationContext
15
+ import com.google.android.gms.location.Geofence
16
+ import com.google.android.gms.location.ActivityRecognition
17
+ import com.google.android.gms.location.ActivityRecognitionResult
18
+ import com.google.android.gms.location.GeofencingEvent
19
+ import com.google.android.gms.location.GeofencingRequest
20
+ import com.google.android.gms.location.LocationRequest
21
+ import com.google.android.gms.location.LocationServices
22
+ import com.google.android.gms.location.Priority
23
+ import com.google.android.gms.tasks.Task
24
+ import com.margelo.nitro.nitrogeolocation.*
25
+ import java.util.concurrent.CountDownLatch
26
+ import java.util.concurrent.Executors
27
+ import java.util.concurrent.TimeUnit
28
+ import java.util.UUID
29
+
30
+ private const val ACTION_LOCATION_UPDATE =
31
+ "com.margelo.nitro.nitrogeolocation.background.LOCATION_UPDATE"
32
+ private const val ACTION_GEOFENCE_UPDATE =
33
+ "com.margelo.nitro.nitrogeolocation.background.GEOFENCE_UPDATE"
34
+ private const val ACTION_ACTIVITY_UPDATE =
35
+ "com.margelo.nitro.nitrogeolocation.background.ACTIVITY_UPDATE"
36
+
37
+ class NitroBackgroundLocationController private constructor(
38
+ private val context: Context
39
+ ) {
40
+ val eventHub = NitroBackgroundEventHub()
41
+ val store = NitroBackgroundStore(context)
42
+
43
+ private val appContext = context.applicationContext
44
+ private val fusedLocationClient by lazy {
45
+ LocationServices.getFusedLocationProviderClient(appContext)
46
+ }
47
+ private val geofencingClient by lazy {
48
+ LocationServices.getGeofencingClient(appContext)
49
+ }
50
+ private val activityRecognitionClient by lazy {
51
+ ActivityRecognition.getClient(appContext)
52
+ }
53
+ private val platformLocationManager by lazy {
54
+ appContext.getSystemService(Context.LOCATION_SERVICE) as LocationManager
55
+ }
56
+ private val prefs =
57
+ appContext.getSharedPreferences("nitro_background_location", Context.MODE_PRIVATE)
58
+ private val permissions = AndroidBackgroundPermissions(appContext) { getConfigOrNull() }
59
+ private val httpSync = AndroidBackgroundHttpSync()
60
+ private val taskExecutor = Executors.newSingleThreadExecutor()
61
+
62
+ @Volatile
63
+ private var config: BackgroundLocationOptions? = null
64
+
65
+ @Volatile
66
+ private var state = BackgroundLocationState.IDLE
67
+
68
+ fun checkBackgroundPermission(): BackgroundPermissionResult {
69
+ return permissions.checkBackgroundPermission()
70
+ }
71
+
72
+ fun requestBackgroundPermission(reactContext: ReactApplicationContext): BackgroundPermissionResult {
73
+ return permissions.requestBackgroundPermission(reactContext)
74
+ }
75
+
76
+ fun openAppLocationSettings() {
77
+ permissions.openAppLocationSettings()
78
+ }
79
+
80
+ fun configure(options: BackgroundLocationOptions) {
81
+ validate(options)
82
+ config = options
83
+ persistConfig(options)
84
+ }
85
+
86
+ fun getConfigOrNull(): BackgroundLocationOptions? {
87
+ return config ?: restoreConfig()?.also { config = it }
88
+ }
89
+
90
+ fun requireConfig(): BackgroundLocationOptions {
91
+ return getConfigOrNull() ?: throw IllegalStateException(
92
+ "Background location is not configured. Call configureBackgroundLocation() or startBackgroundLocation(options) first."
93
+ )
94
+ }
95
+
96
+ fun start(options: BackgroundLocationOptions?) {
97
+ options?.let(::configure)
98
+ val current = requireConfig()
99
+ validate(current)
100
+ if (permissions.foregroundPermission() != PermissionStatus.GRANTED) {
101
+ throw SecurityException("Foreground location permission is required")
102
+ }
103
+ if (current.android?.foregroundService == null &&
104
+ permissions.backgroundPermission() != BackgroundPermissionStatus.GRANTED) {
105
+ throw SecurityException("Background location permission is required")
106
+ }
107
+ state = BackgroundLocationState.STARTING
108
+ prefs.edit().putBoolean("running", true).apply()
109
+ ContextCompat.startForegroundService(
110
+ appContext,
111
+ Intent(appContext, NitroBackgroundLocationService::class.java)
112
+ )
113
+ state = BackgroundLocationState.RUNNING
114
+ }
115
+
116
+ fun stop() {
117
+ state = BackgroundLocationState.STOPPING
118
+ stopNativeLocationUpdates()
119
+ stopActivityRecognition()
120
+ appContext.stopService(Intent(appContext, NitroBackgroundLocationService::class.java))
121
+ prefs.edit().putBoolean("running", false).apply()
122
+ state = BackgroundLocationState.STOPPED
123
+ }
124
+
125
+ fun reset() {
126
+ stop()
127
+ removeGeofences(null)
128
+ config = null
129
+ prefs.edit().clear().apply()
130
+ store.clearEvents(null)
131
+ store.clearLocations(null)
132
+ }
133
+
134
+ fun getStatus(): BackgroundLocationStatus {
135
+ val providerEnabled = runCatching {
136
+ val manager = appContext.getSystemService(Context.LOCATION_SERVICE) as LocationManager
137
+ manager.isProviderEnabled(LocationManager.GPS_PROVIDER) ||
138
+ manager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
139
+ }.getOrDefault(false)
140
+
141
+ return BackgroundLocationStatus(
142
+ state,
143
+ prefs.getBoolean("running", false),
144
+ config != null || prefs.getBoolean("configured", false),
145
+ permissions.foregroundPermission(),
146
+ permissions.backgroundPermission(),
147
+ permissions.accuracyAuthorization(),
148
+ providerEnabled,
149
+ null,
150
+ store.count("background_locations"),
151
+ store.count("background_events"),
152
+ store.count("geofences"),
153
+ AndroidBackgroundLocationStatus(
154
+ prefs.getBoolean("running", false),
155
+ null,
156
+ permissions.notificationPermission()
157
+ ),
158
+ null,
159
+ null
160
+ )
161
+ }
162
+
163
+ @SuppressLint("MissingPermission")
164
+ fun startNativeLocationUpdates() {
165
+ val current = requireConfig()
166
+ if (current.android?.locationProvider == AndroidBackgroundProvider.ANDROID_PLATFORM) {
167
+ startPlatformLocationUpdates(current)
168
+ return
169
+ }
170
+ val request = LocationRequest.Builder(
171
+ resolvePriority(current),
172
+ current.interval?.toLong() ?: 10_000L
173
+ )
174
+ .setMinUpdateIntervalMillis(current.fastestInterval?.toLong() ?: 5_000L)
175
+ .setMinUpdateDistanceMeters((current.distanceFilter ?: 0.0).toFloat())
176
+ .setWaitForAccurateLocation(current.waitForAccurateLocation == true)
177
+ .setMaxUpdateDelayMillis(current.maxUpdateDelay?.toLong() ?: 0L)
178
+ .build()
179
+
180
+ fusedLocationClient.requestLocationUpdates(request, locationPendingIntent())
181
+ }
182
+
183
+ fun stopNativeLocationUpdates() {
184
+ fusedLocationClient.removeLocationUpdates(locationPendingIntent())
185
+ runCatching { platformLocationManager.removeUpdates(locationPendingIntent()) }
186
+ }
187
+
188
+ fun handleNativeLocation(location: Location, source: BackgroundLocationSource) {
189
+ val id = UUID.randomUUID().toString()
190
+ val backgroundLocation = BackgroundLocation(
191
+ id,
192
+ source,
193
+ true,
194
+ backgroundProviderFrom(location.provider),
195
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) location.isMock else location.isFromMockProvider,
196
+ System.currentTimeMillis().toDouble(),
197
+ null,
198
+ null,
199
+ GeolocationCoordinates(
200
+ location.latitude,
201
+ location.longitude,
202
+ location.takeIf { it.hasAltitude() }?.altitude?.let { NullableDouble.create(it) },
203
+ location.accuracy.toDouble(),
204
+ null,
205
+ location.takeIf { it.hasBearing() }?.bearing?.toDouble()?.let { NullableDouble.create(it) },
206
+ location.takeIf { it.hasSpeed() }?.speed?.toDouble()?.let { NullableDouble.create(it) }
207
+ ),
208
+ location.time.toDouble()
209
+ )
210
+ val event = BackgroundEventEnvelope(
211
+ backgroundLocation,
212
+ null,
213
+ null,
214
+ null,
215
+ null,
216
+ null,
217
+ UUID.randomUUID().toString(),
218
+ BackgroundEventType.LOCATION,
219
+ System.currentTimeMillis().toDouble(),
220
+ false
221
+ )
222
+ if (shouldPersist()) {
223
+ store.insertLocation(backgroundLocation)
224
+ store.pruneLocations(currentMaxStoredLocations())
225
+ store.insertEvent(event)
226
+ store.pruneEvents(currentMaxStoredEvents())
227
+ }
228
+ dispatchEvent(event)
229
+ scheduleSyncIfNeeded()
230
+ }
231
+
232
+ fun handleGeofenceEvent(geofencingEvent: GeofencingEvent) {
233
+ val transition = when (geofencingEvent.geofenceTransition) {
234
+ Geofence.GEOFENCE_TRANSITION_ENTER -> GeofenceTransition.ENTER
235
+ Geofence.GEOFENCE_TRANSITION_EXIT -> GeofenceTransition.EXIT
236
+ Geofence.GEOFENCE_TRANSITION_DWELL -> GeofenceTransition.DWELL
237
+ else -> return
238
+ }
239
+ val regions = store.getGeofences().associateBy { it.identifier }
240
+ for (trigger in geofencingEvent.triggeringGeofences ?: emptyList()) {
241
+ val region = regions[trigger.requestId] ?: continue
242
+ val now = System.currentTimeMillis().toDouble()
243
+ val event = BackgroundEventEnvelope(
244
+ null,
245
+ GeofenceEvent(region, transition, null, now),
246
+ null,
247
+ null,
248
+ null,
249
+ null,
250
+ UUID.randomUUID().toString(),
251
+ BackgroundEventType.GEOFENCE,
252
+ now,
253
+ false
254
+ )
255
+ persistEventIfNeeded(event)
256
+ dispatchEvent(event)
257
+ }
258
+ }
259
+
260
+ @SuppressLint("MissingPermission")
261
+ fun startActivityRecognition(options: ActivityRecognitionOptions?) {
262
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q &&
263
+ ContextCompat.checkSelfPermission(
264
+ appContext,
265
+ Manifest.permission.ACTIVITY_RECOGNITION
266
+ ) != PackageManager.PERMISSION_GRANTED) {
267
+ throw SecurityException("Activity recognition permission is required")
268
+ }
269
+ activityRecognitionClient.requestActivityUpdates(
270
+ (options?.interval ?: 10_000.0).toLong(),
271
+ activityPendingIntent()
272
+ )
273
+ }
274
+
275
+ fun stopActivityRecognition() {
276
+ activityRecognitionClient.removeActivityUpdates(activityPendingIntent())
277
+ }
278
+
279
+ fun handleActivityRecognition(intent: Intent) {
280
+ val result = ActivityRecognitionResult.extractResult(intent) ?: return
281
+ val activity = result.mostProbableActivity ?: return
282
+ val detected = DetectedActivity(
283
+ activity.toNitroActivityType(),
284
+ activity.confidence.toDouble(),
285
+ System.currentTimeMillis().toDouble()
286
+ )
287
+ val event = BackgroundEventEnvelope(
288
+ null,
289
+ null,
290
+ detected,
291
+ null,
292
+ null,
293
+ null,
294
+ UUID.randomUUID().toString(),
295
+ BackgroundEventType.ACTIVITY,
296
+ detected.timestamp,
297
+ false
298
+ )
299
+ persistEventIfNeeded(event)
300
+ dispatchEvent(event)
301
+ applyActivityAwareTracking(detected)
302
+ }
303
+
304
+ fun addGeofences(regions: Array<GeofenceRegion>, options: GeofencingOptions?) {
305
+ if (permissions.backgroundPermission() != BackgroundPermissionStatus.GRANTED) {
306
+ throw SecurityException("Background location permission is required to register geofences")
307
+ }
308
+ val geofences = regions.map { region ->
309
+ Geofence.Builder()
310
+ .setRequestId(region.identifier)
311
+ .setCircularRegion(region.latitude, region.longitude, region.radius.toFloat())
312
+ .setTransitionTypes(region.toTransitionTypes())
313
+ .setLoiteringDelay(region.loiteringDelay?.toInt() ?: 0)
314
+ .setExpirationDuration(region.expirationDuration?.toLong() ?: Geofence.NEVER_EXPIRE)
315
+ .apply {
316
+ options?.notificationResponsiveness?.toInt()?.takeIf { it >= 0 }?.let {
317
+ setNotificationResponsiveness(it)
318
+ }
319
+ }
320
+ .build()
321
+ }
322
+ if (geofences.isEmpty()) return
323
+
324
+ waitForTask(geofencingClient.addGeofences(
325
+ GeofencingRequest.Builder()
326
+ .setInitialTrigger(options.toInitialTrigger())
327
+ .addGeofences(geofences)
328
+ .build(),
329
+ geofencePendingIntent()
330
+ ))
331
+ store.saveGeofences(regions)
332
+ }
333
+
334
+ fun removeGeofences(identifiers: Array<String>?) {
335
+ waitForTask(if (identifiers == null) {
336
+ geofencingClient.removeGeofences(geofencePendingIntent())
337
+ } else {
338
+ geofencingClient.removeGeofences(identifiers.toList())
339
+ })
340
+ store.removeGeofences(identifiers)
341
+ }
342
+
343
+ fun registerPersistedGeofencesIfNeeded() {
344
+ val geofences = store.getGeofences()
345
+ if (geofences.isNotEmpty()) {
346
+ addGeofences(geofences, null)
347
+ }
348
+ }
349
+
350
+ fun syncStoredLocations(): BackgroundHttpSyncResult {
351
+ val sync = requireConfig().sync
352
+ val locations = store.getLocations(
353
+ GetStoredBackgroundLocationsOptions(
354
+ sync?.batchSize ?: 50.0,
355
+ null,
356
+ true,
357
+ false
358
+ )
359
+ )
360
+ val ids = locations.map { it.id }
361
+ if (ids.isEmpty()) {
362
+ return BackgroundHttpSyncResult(true, null, emptyArray(), emptyArray(), null)
363
+ }
364
+ if (sync == null) {
365
+ return BackgroundHttpSyncResult(true, null, emptyArray(), emptyArray(), null)
366
+ }
367
+ val result = httpSync.uploadLocationsWithRetry(sync, locations)
368
+ store.markSynced(result.syncedLocationIds.toList())
369
+ if (sync.autoClear == true) {
370
+ store.clearLocations(result.syncedLocationIds)
371
+ }
372
+ return result
373
+ }
374
+
375
+ private fun scheduleSyncIfNeeded() {
376
+ val sync = getConfigOrNull()?.sync ?: return
377
+ val threshold = sync.syncThreshold?.toInt()?.takeIf { it > 0 } ?: 1
378
+ val unsynced = store.getLocations(
379
+ GetStoredBackgroundLocationsOptions(
380
+ threshold.toDouble(),
381
+ null,
382
+ true,
383
+ false
384
+ )
385
+ )
386
+ if (unsynced.size < threshold) return
387
+
388
+ val now = System.currentTimeMillis()
389
+ val interval = sync.syncInterval?.toLong()?.takeIf { it > 0 } ?: 0L
390
+ val lastSyncAt = prefs.getLong("lastSyncAt", 0L)
391
+ if (interval > 0 && now - lastSyncAt < interval) return
392
+ prefs.edit().putLong("lastSyncAt", now).apply()
393
+
394
+ Thread {
395
+ val result = runCatching { syncStoredLocations() }.getOrElse { error ->
396
+ BackgroundHttpSyncResult(
397
+ false,
398
+ null,
399
+ emptyArray(),
400
+ unsynced.map { it.id }.toTypedArray(),
401
+ error.message ?: "HTTP sync failed"
402
+ )
403
+ }
404
+ val event = BackgroundEventEnvelope(
405
+ null,
406
+ null,
407
+ null,
408
+ null,
409
+ result,
410
+ null,
411
+ UUID.randomUUID().toString(),
412
+ BackgroundEventType.HTTPSYNC,
413
+ System.currentTimeMillis().toDouble(),
414
+ false
415
+ )
416
+ persistEventIfNeeded(event)
417
+ dispatchEvent(event)
418
+ }.start()
419
+ }
420
+
421
+ private fun shouldPersist(): Boolean {
422
+ return getConfigOrNull()?.persist != false
423
+ }
424
+
425
+ private fun persistEventIfNeeded(event: BackgroundEventEnvelope) {
426
+ if (!shouldPersist()) return
427
+ store.insertEvent(event)
428
+ store.pruneEvents(currentMaxStoredEvents())
429
+ }
430
+
431
+ private fun currentMaxStoredLocations(): Int? {
432
+ return getConfigOrNull()?.maxStoredLocations?.toInt()?.takeIf { it > 0 }
433
+ }
434
+
435
+ private fun currentMaxStoredEvents(): Int? {
436
+ return getConfigOrNull()?.maxStoredEvents?.toInt()?.takeIf { it > 0 }
437
+ }
438
+
439
+ @SuppressLint("MissingPermission")
440
+ private fun startPlatformLocationUpdates(options: BackgroundLocationOptions) {
441
+ val interval = options.interval?.toLong() ?: 10_000L
442
+ val distance = (options.distanceFilter ?: 0.0).toFloat()
443
+ val providers = listOf(LocationManager.GPS_PROVIDER, LocationManager.NETWORK_PROVIDER)
444
+ .filter { provider -> runCatching { platformLocationManager.isProviderEnabled(provider) }.getOrDefault(false) }
445
+ .ifEmpty { listOf(LocationManager.GPS_PROVIDER) }
446
+ providers.forEach { provider ->
447
+ platformLocationManager.requestLocationUpdates(
448
+ provider,
449
+ interval,
450
+ distance,
451
+ locationPendingIntent()
452
+ )
453
+ }
454
+ }
455
+
456
+ private fun applyActivityAwareTracking(activity: DetectedActivity) {
457
+ val current = getConfigOrNull() ?: return
458
+ val options = current.activityRecognition
459
+ if (current.trackingMode != BackgroundTrackingMode.ACTIVITYAWARE &&
460
+ options?.stopOnStill != true) {
461
+ return
462
+ }
463
+ val stopOnStill = options?.stopOnStill ?: (current.trackingMode == BackgroundTrackingMode.ACTIVITYAWARE)
464
+ val minimumConfidence = options?.minimumConfidence ?: 0.0
465
+ if (activity.confidence < minimumConfidence) return
466
+ if (activity.type == DetectedActivityType.STILL && stopOnStill) {
467
+ stopNativeLocationUpdates()
468
+ return
469
+ }
470
+ if (activity.type != DetectedActivityType.STILL &&
471
+ activity.type != DetectedActivityType.UNKNOWN &&
472
+ prefs.getBoolean("running", false)) {
473
+ runCatching { startNativeLocationUpdates() }
474
+ }
475
+ }
476
+
477
+ private fun validate(options: BackgroundLocationOptions) {
478
+ if (options.android?.foregroundService == null) {
479
+ throw IllegalArgumentException(
480
+ "Android background tracking requires android.foregroundService notification options"
481
+ )
482
+ }
483
+ }
484
+
485
+ private fun dispatchEvent(event: BackgroundEventEnvelope) {
486
+ eventHub.emit(event)
487
+ val intent = Intent(appContext, NitroBackgroundHeadlessTaskService::class.java)
488
+ .putExtra("event", event.toJson().toString())
489
+ appContext.startService(intent)
490
+ HeadlessJsTaskService.acquireWakeLockNow(appContext)
491
+ }
492
+
493
+ private fun waitForTask(task: Task<Void>) {
494
+ val latch = CountDownLatch(1)
495
+ var failure: Exception? = null
496
+ task.addOnCompleteListener(taskExecutor) {
497
+ failure = it.exception
498
+ latch.countDown()
499
+ }
500
+ latch.await(30, TimeUnit.SECONDS)
501
+ failure?.let { throw it }
502
+ if (!task.isSuccessful) {
503
+ throw IllegalStateException("Google Play services task failed")
504
+ }
505
+ }
506
+
507
+ private fun persistConfig(options: BackgroundLocationOptions) {
508
+ val service = options.android?.foregroundService
509
+ prefs.edit()
510
+ .putBoolean("configured", true)
511
+ .putBoolean("running", prefs.getBoolean("running", false))
512
+ .putBoolean("startOnBoot", options.startOnBoot == true)
513
+ .putBoolean("stopOnTerminate", options.stopOnTerminate != false)
514
+ .putString("trackingMode", options.trackingMode?.name)
515
+ .putString("accuracyAndroid", options.accuracy?.android?.name)
516
+ .putString("accuracyIos", options.accuracy?.ios?.name)
517
+ .putString("granularity", options.granularity?.name)
518
+ .putString("androidLocationProvider", options.android?.locationProvider?.name)
519
+ .putBoolean(
520
+ "androidRequestNotificationPermission",
521
+ options.android?.requestNotificationPermission != false
522
+ )
523
+ .putBoolean(
524
+ "androidRequestIgnoreBatteryOptimizations",
525
+ options.android?.requestIgnoreBatteryOptimizations == true
526
+ )
527
+ .putFloat("interval", (options.interval ?: 10_000.0).toFloat())
528
+ .putFloat("fastestInterval", (options.fastestInterval ?: 5_000.0).toFloat())
529
+ .putFloat("distanceFilter", (options.distanceFilter ?: 0.0).toFloat())
530
+ .putFloat("maxUpdateDelay", (options.maxUpdateDelay ?: 0.0).toFloat())
531
+ .putBoolean("waitForAccurateLocation", options.waitForAccurateLocation == true)
532
+ .putBoolean("persist", options.persist != false)
533
+ .putFloat("maxStoredLocations", (options.maxStoredLocations ?: 0.0).toFloat())
534
+ .putFloat("maxStoredEvents", (options.maxStoredEvents ?: 0.0).toFloat())
535
+ .putBoolean("activityConfigured", options.activityRecognition != null)
536
+ .putBoolean("activityEnabled", options.activityRecognition?.enabled == true)
537
+ .putFloat("activityInterval", (options.activityRecognition?.interval ?: 10_000.0).toFloat())
538
+ .putBoolean("activityStopOnStill", options.activityRecognition?.stopOnStill == true)
539
+ .putFloat(
540
+ "activityMinimumConfidence",
541
+ (options.activityRecognition?.minimumConfidence ?: 0.0).toFloat()
542
+ )
543
+ .putFloat("notificationId", (service?.notificationId ?: 9471.0).toFloat())
544
+ .putString("notificationTitle", service?.notificationTitle)
545
+ .putString("notificationText", service?.notificationText)
546
+ .putString("notificationChannelId", service?.notificationChannelId)
547
+ .putString("notificationChannelName", service?.notificationChannelName)
548
+ .putString("notificationChannelDescription", service?.notificationChannelDescription)
549
+ .putString("notificationIcon", service?.notificationIcon)
550
+ .putString("notificationColor", service?.notificationColor)
551
+ .putString("stopActionTitle", service?.stopActionTitle)
552
+ .putString("syncUrl", options.sync?.url)
553
+ .putString("syncMethod", options.sync?.method?.name)
554
+ .putString("syncHeaders", options.sync?.headers?.let(::stringMapToJson))
555
+ .putString("syncBodyTemplate", options.sync?.bodyTemplate?.let(::variantMapToJson))
556
+ .putBoolean("syncBatchConfigured", options.sync?.batch != null)
557
+ .putFloat("syncBatchSize", (options.sync?.batchSize ?: 50.0).toFloat())
558
+ .putFloat("syncThreshold", (options.sync?.syncThreshold ?: 1.0).toFloat())
559
+ .putFloat("syncInterval", (options.sync?.syncInterval ?: 0.0).toFloat())
560
+ .putBoolean("syncBatch", options.sync?.batch == true)
561
+ .putBoolean("syncRetry", options.sync?.retry == true)
562
+ .putFloat("syncMaxRetries", (options.sync?.maxRetries ?: 3.0).toFloat())
563
+ .putBoolean("syncAutoClear", options.sync?.autoClear == true)
564
+ .apply()
565
+ }
566
+
567
+ private fun restoreConfig(): BackgroundLocationOptions? {
568
+ if (!prefs.getBoolean("configured", false)) return null
569
+ val title = prefs.getString("notificationTitle", null) ?: return null
570
+ val text = prefs.getString("notificationText", null) ?: return null
571
+ val service = AndroidForegroundServiceOptions(
572
+ prefs.getFloat("notificationId", 9471f).toDouble(),
573
+ title,
574
+ text,
575
+ prefs.getString("notificationChannelId", null),
576
+ prefs.getString("notificationChannelName", null),
577
+ prefs.getString("notificationChannelDescription", null),
578
+ prefs.getString("notificationIcon", null),
579
+ prefs.getString("notificationColor", null),
580
+ prefs.getString("stopActionTitle", null)
581
+ )
582
+ val sync = prefs.getString("syncUrl", null)?.let { url ->
583
+ BackgroundHttpSyncOptions(
584
+ url,
585
+ prefs.getString("syncMethod", null)?.let {
586
+ runCatching { enumValueOf<BackgroundHttpMethod>(it) }.getOrNull()
587
+ },
588
+ prefs.getString("syncHeaders", null)?.let(::jsonToStringMap),
589
+ if (prefs.getBoolean("syncBatchConfigured", false)) {
590
+ prefs.getBoolean("syncBatch", false)
591
+ } else {
592
+ null
593
+ },
594
+ prefs.getFloat("syncBatchSize", 50f).toDouble(),
595
+ prefs.getFloat("syncThreshold", 1f).toDouble(),
596
+ prefs.getFloat("syncInterval", 0f).toDouble(),
597
+ prefs.getBoolean("syncRetry", false),
598
+ prefs.getFloat("syncMaxRetries", 3f).toDouble(),
599
+ prefs.getString("syncBodyTemplate", null)?.let(::jsonToVariantMap),
600
+ prefs.getBoolean("syncAutoClear", false)
601
+ )
602
+ }
603
+ val accuracyAndroid = prefs.getString("accuracyAndroid", null)?.let {
604
+ runCatching { enumValueOf<AndroidAccuracyPreset>(it) }.getOrNull()
605
+ }
606
+ val accuracyIos = prefs.getString("accuracyIos", null)?.let {
607
+ runCatching { enumValueOf<IOSAccuracyPreset>(it) }.getOrNull()
608
+ }
609
+ val accuracy = if (accuracyAndroid != null || accuracyIos != null) {
610
+ LocationAccuracyOptions(accuracyAndroid, accuracyIos)
611
+ } else {
612
+ null
613
+ }
614
+ val activityRecognition = if (prefs.getBoolean("activityConfigured", false)) {
615
+ ActivityRecognitionOptions(
616
+ prefs.getBoolean("activityEnabled", false),
617
+ prefs.getFloat("activityInterval", 10_000f).toDouble(),
618
+ prefs.getBoolean("activityStopOnStill", false),
619
+ prefs.getFloat("activityMinimumConfidence", 0f).toDouble()
620
+ )
621
+ } else {
622
+ null
623
+ }
624
+ return BackgroundLocationOptions(
625
+ prefs.getString("trackingMode", null)?.let {
626
+ runCatching { enumValueOf<BackgroundTrackingMode>(it) }.getOrNull()
627
+ },
628
+ accuracy,
629
+ prefs.getString("granularity", null)?.let {
630
+ runCatching { enumValueOf<AndroidGranularity>(it) }.getOrNull()
631
+ },
632
+ prefs.getFloat("interval", 10_000f).toDouble(),
633
+ prefs.getFloat("fastestInterval", 5_000f).toDouble(),
634
+ prefs.getFloat("distanceFilter", 0f).toDouble(),
635
+ prefs.getFloat("maxUpdateDelay", 0f).toDouble(),
636
+ prefs.getBoolean("waitForAccurateLocation", false),
637
+ prefs.getBoolean("persist", true),
638
+ prefs.getFloat("maxStoredLocations", 0f).toDouble().takeIf { it > 0 },
639
+ prefs.getFloat("maxStoredEvents", 0f).toDouble().takeIf { it > 0 },
640
+ prefs.getBoolean("stopOnTerminate", true),
641
+ prefs.getBoolean("startOnBoot", false),
642
+ AndroidBackgroundLocationOptions(
643
+ prefs.getString("androidLocationProvider", null)?.let {
644
+ runCatching { enumValueOf<AndroidBackgroundProvider>(it) }.getOrNull()
645
+ } ?: AndroidBackgroundProvider.AUTO,
646
+ service,
647
+ prefs.getBoolean("androidRequestNotificationPermission", true),
648
+ prefs.getBoolean("androidRequestIgnoreBatteryOptimizations", false)
649
+ ),
650
+ null,
651
+ null,
652
+ activityRecognition,
653
+ sync
654
+ )
655
+ }
656
+
657
+ private fun locationPendingIntent(): PendingIntent {
658
+ val intent = Intent(appContext, NitroLocationUpdateReceiver::class.java)
659
+ .setAction(ACTION_LOCATION_UPDATE)
660
+ return PendingIntent.getBroadcast(
661
+ appContext,
662
+ 1001,
663
+ intent,
664
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
665
+ )
666
+ }
667
+
668
+ private fun geofencePendingIntent(): PendingIntent {
669
+ val intent = Intent(appContext, NitroGeofenceReceiver::class.java)
670
+ .setAction(ACTION_GEOFENCE_UPDATE)
671
+ return PendingIntent.getBroadcast(
672
+ appContext,
673
+ 1002,
674
+ intent,
675
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
676
+ )
677
+ }
678
+
679
+ private fun activityPendingIntent(): PendingIntent {
680
+ val intent = Intent(appContext, NitroActivityRecognitionReceiver::class.java)
681
+ .setAction(ACTION_ACTIVITY_UPDATE)
682
+ return PendingIntent.getBroadcast(
683
+ appContext,
684
+ 1003,
685
+ intent,
686
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
687
+ )
688
+ }
689
+
690
+ private fun resolvePriority(options: BackgroundLocationOptions): Int {
691
+ return when (options.accuracy?.android) {
692
+ AndroidAccuracyPreset.HIGH -> Priority.PRIORITY_HIGH_ACCURACY
693
+ AndroidAccuracyPreset.LOW -> Priority.PRIORITY_LOW_POWER
694
+ AndroidAccuracyPreset.PASSIVE -> Priority.PRIORITY_PASSIVE
695
+ else -> Priority.PRIORITY_BALANCED_POWER_ACCURACY
696
+ }
697
+ }
698
+
699
+ companion object {
700
+ @Volatile
701
+ private var instance: NitroBackgroundLocationController? = null
702
+
703
+ fun getInstance(context: Context): NitroBackgroundLocationController {
704
+ return instance ?: synchronized(this) {
705
+ instance ?: NitroBackgroundLocationController(context.applicationContext).also {
706
+ instance = it
707
+ }
708
+ }
709
+ }
710
+ }
711
+ }