react-native-nitro-geolocation 1.2.6 → 1.3.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.
Files changed (223) hide show
  1. package/README.md +52 -295
  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 +710 -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,710 @@
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 (permissions.backgroundPermission() != BackgroundPermissionStatus.GRANTED) {
104
+ throw SecurityException("Background location permission is required")
105
+ }
106
+ state = BackgroundLocationState.STARTING
107
+ prefs.edit().putBoolean("running", true).apply()
108
+ ContextCompat.startForegroundService(
109
+ appContext,
110
+ Intent(appContext, NitroBackgroundLocationService::class.java)
111
+ )
112
+ state = BackgroundLocationState.RUNNING
113
+ }
114
+
115
+ fun stop() {
116
+ state = BackgroundLocationState.STOPPING
117
+ stopNativeLocationUpdates()
118
+ stopActivityRecognition()
119
+ appContext.stopService(Intent(appContext, NitroBackgroundLocationService::class.java))
120
+ prefs.edit().putBoolean("running", false).apply()
121
+ state = BackgroundLocationState.STOPPED
122
+ }
123
+
124
+ fun reset() {
125
+ stop()
126
+ removeGeofences(null)
127
+ config = null
128
+ prefs.edit().clear().apply()
129
+ store.clearEvents(null)
130
+ store.clearLocations(null)
131
+ }
132
+
133
+ fun getStatus(): BackgroundLocationStatus {
134
+ val providerEnabled = runCatching {
135
+ val manager = appContext.getSystemService(Context.LOCATION_SERVICE) as LocationManager
136
+ manager.isProviderEnabled(LocationManager.GPS_PROVIDER) ||
137
+ manager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
138
+ }.getOrDefault(false)
139
+
140
+ return BackgroundLocationStatus(
141
+ state,
142
+ prefs.getBoolean("running", false),
143
+ config != null || prefs.getBoolean("configured", false),
144
+ permissions.foregroundPermission(),
145
+ permissions.backgroundPermission(),
146
+ permissions.accuracyAuthorization(),
147
+ providerEnabled,
148
+ null,
149
+ store.count("background_locations"),
150
+ store.count("background_events"),
151
+ store.count("geofences"),
152
+ AndroidBackgroundLocationStatus(
153
+ prefs.getBoolean("running", false),
154
+ null,
155
+ permissions.notificationPermission()
156
+ ),
157
+ null,
158
+ null
159
+ )
160
+ }
161
+
162
+ @SuppressLint("MissingPermission")
163
+ fun startNativeLocationUpdates() {
164
+ val current = requireConfig()
165
+ if (current.android?.locationProvider == AndroidBackgroundProvider.ANDROID_PLATFORM) {
166
+ startPlatformLocationUpdates(current)
167
+ return
168
+ }
169
+ val request = LocationRequest.Builder(
170
+ resolvePriority(current),
171
+ current.interval?.toLong() ?: 10_000L
172
+ )
173
+ .setMinUpdateIntervalMillis(current.fastestInterval?.toLong() ?: 5_000L)
174
+ .setMinUpdateDistanceMeters((current.distanceFilter ?: 0.0).toFloat())
175
+ .setWaitForAccurateLocation(current.waitForAccurateLocation == true)
176
+ .setMaxUpdateDelayMillis(current.maxUpdateDelay?.toLong() ?: 0L)
177
+ .build()
178
+
179
+ fusedLocationClient.requestLocationUpdates(request, locationPendingIntent())
180
+ }
181
+
182
+ fun stopNativeLocationUpdates() {
183
+ fusedLocationClient.removeLocationUpdates(locationPendingIntent())
184
+ runCatching { platformLocationManager.removeUpdates(locationPendingIntent()) }
185
+ }
186
+
187
+ fun handleNativeLocation(location: Location, source: BackgroundLocationSource) {
188
+ val id = UUID.randomUUID().toString()
189
+ val backgroundLocation = BackgroundLocation(
190
+ id,
191
+ source,
192
+ true,
193
+ backgroundProviderFrom(location.provider),
194
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) location.isMock else location.isFromMockProvider,
195
+ System.currentTimeMillis().toDouble(),
196
+ null,
197
+ null,
198
+ GeolocationCoordinates(
199
+ location.latitude,
200
+ location.longitude,
201
+ location.takeIf { it.hasAltitude() }?.altitude?.let { NullableDouble.create(it) },
202
+ location.accuracy.toDouble(),
203
+ null,
204
+ location.takeIf { it.hasBearing() }?.bearing?.toDouble()?.let { NullableDouble.create(it) },
205
+ location.takeIf { it.hasSpeed() }?.speed?.toDouble()?.let { NullableDouble.create(it) }
206
+ ),
207
+ location.time.toDouble()
208
+ )
209
+ val event = BackgroundEventEnvelope(
210
+ backgroundLocation,
211
+ null,
212
+ null,
213
+ null,
214
+ null,
215
+ null,
216
+ UUID.randomUUID().toString(),
217
+ BackgroundEventType.LOCATION,
218
+ System.currentTimeMillis().toDouble(),
219
+ false
220
+ )
221
+ if (shouldPersist()) {
222
+ store.insertLocation(backgroundLocation)
223
+ store.pruneLocations(currentMaxStoredLocations())
224
+ store.insertEvent(event)
225
+ store.pruneEvents(currentMaxStoredEvents())
226
+ }
227
+ dispatchEvent(event)
228
+ scheduleSyncIfNeeded()
229
+ }
230
+
231
+ fun handleGeofenceEvent(geofencingEvent: GeofencingEvent) {
232
+ val transition = when (geofencingEvent.geofenceTransition) {
233
+ Geofence.GEOFENCE_TRANSITION_ENTER -> GeofenceTransition.ENTER
234
+ Geofence.GEOFENCE_TRANSITION_EXIT -> GeofenceTransition.EXIT
235
+ Geofence.GEOFENCE_TRANSITION_DWELL -> GeofenceTransition.DWELL
236
+ else -> return
237
+ }
238
+ val regions = store.getGeofences().associateBy { it.identifier }
239
+ for (trigger in geofencingEvent.triggeringGeofences ?: emptyList()) {
240
+ val region = regions[trigger.requestId] ?: continue
241
+ val now = System.currentTimeMillis().toDouble()
242
+ val event = BackgroundEventEnvelope(
243
+ null,
244
+ GeofenceEvent(region, transition, null, now),
245
+ null,
246
+ null,
247
+ null,
248
+ null,
249
+ UUID.randomUUID().toString(),
250
+ BackgroundEventType.GEOFENCE,
251
+ now,
252
+ false
253
+ )
254
+ persistEventIfNeeded(event)
255
+ dispatchEvent(event)
256
+ }
257
+ }
258
+
259
+ @SuppressLint("MissingPermission")
260
+ fun startActivityRecognition(options: ActivityRecognitionOptions?) {
261
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q &&
262
+ ContextCompat.checkSelfPermission(
263
+ appContext,
264
+ Manifest.permission.ACTIVITY_RECOGNITION
265
+ ) != PackageManager.PERMISSION_GRANTED) {
266
+ throw SecurityException("Activity recognition permission is required")
267
+ }
268
+ activityRecognitionClient.requestActivityUpdates(
269
+ (options?.interval ?: 10_000.0).toLong(),
270
+ activityPendingIntent()
271
+ )
272
+ }
273
+
274
+ fun stopActivityRecognition() {
275
+ activityRecognitionClient.removeActivityUpdates(activityPendingIntent())
276
+ }
277
+
278
+ fun handleActivityRecognition(intent: Intent) {
279
+ val result = ActivityRecognitionResult.extractResult(intent) ?: return
280
+ val activity = result.mostProbableActivity ?: return
281
+ val detected = DetectedActivity(
282
+ activity.toNitroActivityType(),
283
+ activity.confidence.toDouble(),
284
+ System.currentTimeMillis().toDouble()
285
+ )
286
+ val event = BackgroundEventEnvelope(
287
+ null,
288
+ null,
289
+ detected,
290
+ null,
291
+ null,
292
+ null,
293
+ UUID.randomUUID().toString(),
294
+ BackgroundEventType.ACTIVITY,
295
+ detected.timestamp,
296
+ false
297
+ )
298
+ persistEventIfNeeded(event)
299
+ dispatchEvent(event)
300
+ applyActivityAwareTracking(detected)
301
+ }
302
+
303
+ fun addGeofences(regions: Array<GeofenceRegion>, options: GeofencingOptions?) {
304
+ if (permissions.backgroundPermission() != BackgroundPermissionStatus.GRANTED) {
305
+ throw SecurityException("Background location permission is required to register geofences")
306
+ }
307
+ val geofences = regions.map { region ->
308
+ Geofence.Builder()
309
+ .setRequestId(region.identifier)
310
+ .setCircularRegion(region.latitude, region.longitude, region.radius.toFloat())
311
+ .setTransitionTypes(region.toTransitionTypes())
312
+ .setLoiteringDelay(region.loiteringDelay?.toInt() ?: 0)
313
+ .setExpirationDuration(region.expirationDuration?.toLong() ?: Geofence.NEVER_EXPIRE)
314
+ .apply {
315
+ options?.notificationResponsiveness?.toInt()?.takeIf { it >= 0 }?.let {
316
+ setNotificationResponsiveness(it)
317
+ }
318
+ }
319
+ .build()
320
+ }
321
+ if (geofences.isEmpty()) return
322
+
323
+ waitForTask(geofencingClient.addGeofences(
324
+ GeofencingRequest.Builder()
325
+ .setInitialTrigger(options.toInitialTrigger())
326
+ .addGeofences(geofences)
327
+ .build(),
328
+ geofencePendingIntent()
329
+ ))
330
+ store.saveGeofences(regions)
331
+ }
332
+
333
+ fun removeGeofences(identifiers: Array<String>?) {
334
+ waitForTask(if (identifiers == null) {
335
+ geofencingClient.removeGeofences(geofencePendingIntent())
336
+ } else {
337
+ geofencingClient.removeGeofences(identifiers.toList())
338
+ })
339
+ store.removeGeofences(identifiers)
340
+ }
341
+
342
+ fun registerPersistedGeofencesIfNeeded() {
343
+ val geofences = store.getGeofences()
344
+ if (geofences.isNotEmpty()) {
345
+ addGeofences(geofences, null)
346
+ }
347
+ }
348
+
349
+ fun syncStoredLocations(): BackgroundHttpSyncResult {
350
+ val sync = requireConfig().sync
351
+ val locations = store.getLocations(
352
+ GetStoredBackgroundLocationsOptions(
353
+ sync?.batchSize ?: 50.0,
354
+ null,
355
+ true,
356
+ false
357
+ )
358
+ )
359
+ val ids = locations.map { it.id }
360
+ if (ids.isEmpty()) {
361
+ return BackgroundHttpSyncResult(true, null, emptyArray(), emptyArray(), null)
362
+ }
363
+ if (sync == null) {
364
+ return BackgroundHttpSyncResult(true, null, emptyArray(), emptyArray(), null)
365
+ }
366
+ val result = httpSync.uploadLocationsWithRetry(sync, locations)
367
+ store.markSynced(result.syncedLocationIds.toList())
368
+ if (sync.autoClear == true) {
369
+ store.clearLocations(result.syncedLocationIds)
370
+ }
371
+ return result
372
+ }
373
+
374
+ private fun scheduleSyncIfNeeded() {
375
+ val sync = getConfigOrNull()?.sync ?: return
376
+ val threshold = sync.syncThreshold?.toInt()?.takeIf { it > 0 } ?: 1
377
+ val unsynced = store.getLocations(
378
+ GetStoredBackgroundLocationsOptions(
379
+ threshold.toDouble(),
380
+ null,
381
+ true,
382
+ false
383
+ )
384
+ )
385
+ if (unsynced.size < threshold) return
386
+
387
+ val now = System.currentTimeMillis()
388
+ val interval = sync.syncInterval?.toLong()?.takeIf { it > 0 } ?: 0L
389
+ val lastSyncAt = prefs.getLong("lastSyncAt", 0L)
390
+ if (interval > 0 && now - lastSyncAt < interval) return
391
+ prefs.edit().putLong("lastSyncAt", now).apply()
392
+
393
+ Thread {
394
+ val result = runCatching { syncStoredLocations() }.getOrElse { error ->
395
+ BackgroundHttpSyncResult(
396
+ false,
397
+ null,
398
+ emptyArray(),
399
+ unsynced.map { it.id }.toTypedArray(),
400
+ error.message ?: "HTTP sync failed"
401
+ )
402
+ }
403
+ val event = BackgroundEventEnvelope(
404
+ null,
405
+ null,
406
+ null,
407
+ null,
408
+ result,
409
+ null,
410
+ UUID.randomUUID().toString(),
411
+ BackgroundEventType.HTTPSYNC,
412
+ System.currentTimeMillis().toDouble(),
413
+ false
414
+ )
415
+ persistEventIfNeeded(event)
416
+ dispatchEvent(event)
417
+ }.start()
418
+ }
419
+
420
+ private fun shouldPersist(): Boolean {
421
+ return getConfigOrNull()?.persist != false
422
+ }
423
+
424
+ private fun persistEventIfNeeded(event: BackgroundEventEnvelope) {
425
+ if (!shouldPersist()) return
426
+ store.insertEvent(event)
427
+ store.pruneEvents(currentMaxStoredEvents())
428
+ }
429
+
430
+ private fun currentMaxStoredLocations(): Int? {
431
+ return getConfigOrNull()?.maxStoredLocations?.toInt()?.takeIf { it > 0 }
432
+ }
433
+
434
+ private fun currentMaxStoredEvents(): Int? {
435
+ return getConfigOrNull()?.maxStoredEvents?.toInt()?.takeIf { it > 0 }
436
+ }
437
+
438
+ @SuppressLint("MissingPermission")
439
+ private fun startPlatformLocationUpdates(options: BackgroundLocationOptions) {
440
+ val interval = options.interval?.toLong() ?: 10_000L
441
+ val distance = (options.distanceFilter ?: 0.0).toFloat()
442
+ val providers = listOf(LocationManager.GPS_PROVIDER, LocationManager.NETWORK_PROVIDER)
443
+ .filter { provider -> runCatching { platformLocationManager.isProviderEnabled(provider) }.getOrDefault(false) }
444
+ .ifEmpty { listOf(LocationManager.GPS_PROVIDER) }
445
+ providers.forEach { provider ->
446
+ platformLocationManager.requestLocationUpdates(
447
+ provider,
448
+ interval,
449
+ distance,
450
+ locationPendingIntent()
451
+ )
452
+ }
453
+ }
454
+
455
+ private fun applyActivityAwareTracking(activity: DetectedActivity) {
456
+ val current = getConfigOrNull() ?: return
457
+ val options = current.activityRecognition
458
+ if (current.trackingMode != BackgroundTrackingMode.ACTIVITYAWARE &&
459
+ options?.stopOnStill != true) {
460
+ return
461
+ }
462
+ val stopOnStill = options?.stopOnStill ?: (current.trackingMode == BackgroundTrackingMode.ACTIVITYAWARE)
463
+ val minimumConfidence = options?.minimumConfidence ?: 0.0
464
+ if (activity.confidence < minimumConfidence) return
465
+ if (activity.type == DetectedActivityType.STILL && stopOnStill) {
466
+ stopNativeLocationUpdates()
467
+ return
468
+ }
469
+ if (activity.type != DetectedActivityType.STILL &&
470
+ activity.type != DetectedActivityType.UNKNOWN &&
471
+ prefs.getBoolean("running", false)) {
472
+ runCatching { startNativeLocationUpdates() }
473
+ }
474
+ }
475
+
476
+ private fun validate(options: BackgroundLocationOptions) {
477
+ if (options.android?.foregroundService == null) {
478
+ throw IllegalArgumentException(
479
+ "Android background tracking requires android.foregroundService notification options"
480
+ )
481
+ }
482
+ }
483
+
484
+ private fun dispatchEvent(event: BackgroundEventEnvelope) {
485
+ eventHub.emit(event)
486
+ val intent = Intent(appContext, NitroBackgroundHeadlessTaskService::class.java)
487
+ .putExtra("event", event.toJson().toString())
488
+ appContext.startService(intent)
489
+ HeadlessJsTaskService.acquireWakeLockNow(appContext)
490
+ }
491
+
492
+ private fun waitForTask(task: Task<Void>) {
493
+ val latch = CountDownLatch(1)
494
+ var failure: Exception? = null
495
+ task.addOnCompleteListener(taskExecutor) {
496
+ failure = it.exception
497
+ latch.countDown()
498
+ }
499
+ latch.await(30, TimeUnit.SECONDS)
500
+ failure?.let { throw it }
501
+ if (!task.isSuccessful) {
502
+ throw IllegalStateException("Google Play services task failed")
503
+ }
504
+ }
505
+
506
+ private fun persistConfig(options: BackgroundLocationOptions) {
507
+ val service = options.android?.foregroundService
508
+ prefs.edit()
509
+ .putBoolean("configured", true)
510
+ .putBoolean("running", prefs.getBoolean("running", false))
511
+ .putBoolean("startOnBoot", options.startOnBoot == true)
512
+ .putBoolean("stopOnTerminate", options.stopOnTerminate != false)
513
+ .putString("trackingMode", options.trackingMode?.name)
514
+ .putString("accuracyAndroid", options.accuracy?.android?.name)
515
+ .putString("accuracyIos", options.accuracy?.ios?.name)
516
+ .putString("granularity", options.granularity?.name)
517
+ .putString("androidLocationProvider", options.android?.locationProvider?.name)
518
+ .putBoolean(
519
+ "androidRequestNotificationPermission",
520
+ options.android?.requestNotificationPermission != false
521
+ )
522
+ .putBoolean(
523
+ "androidRequestIgnoreBatteryOptimizations",
524
+ options.android?.requestIgnoreBatteryOptimizations == true
525
+ )
526
+ .putFloat("interval", (options.interval ?: 10_000.0).toFloat())
527
+ .putFloat("fastestInterval", (options.fastestInterval ?: 5_000.0).toFloat())
528
+ .putFloat("distanceFilter", (options.distanceFilter ?: 0.0).toFloat())
529
+ .putFloat("maxUpdateDelay", (options.maxUpdateDelay ?: 0.0).toFloat())
530
+ .putBoolean("waitForAccurateLocation", options.waitForAccurateLocation == true)
531
+ .putBoolean("persist", options.persist != false)
532
+ .putFloat("maxStoredLocations", (options.maxStoredLocations ?: 0.0).toFloat())
533
+ .putFloat("maxStoredEvents", (options.maxStoredEvents ?: 0.0).toFloat())
534
+ .putBoolean("activityConfigured", options.activityRecognition != null)
535
+ .putBoolean("activityEnabled", options.activityRecognition?.enabled == true)
536
+ .putFloat("activityInterval", (options.activityRecognition?.interval ?: 10_000.0).toFloat())
537
+ .putBoolean("activityStopOnStill", options.activityRecognition?.stopOnStill == true)
538
+ .putFloat(
539
+ "activityMinimumConfidence",
540
+ (options.activityRecognition?.minimumConfidence ?: 0.0).toFloat()
541
+ )
542
+ .putFloat("notificationId", (service?.notificationId ?: 9471.0).toFloat())
543
+ .putString("notificationTitle", service?.notificationTitle)
544
+ .putString("notificationText", service?.notificationText)
545
+ .putString("notificationChannelId", service?.notificationChannelId)
546
+ .putString("notificationChannelName", service?.notificationChannelName)
547
+ .putString("notificationChannelDescription", service?.notificationChannelDescription)
548
+ .putString("notificationIcon", service?.notificationIcon)
549
+ .putString("notificationColor", service?.notificationColor)
550
+ .putString("stopActionTitle", service?.stopActionTitle)
551
+ .putString("syncUrl", options.sync?.url)
552
+ .putString("syncMethod", options.sync?.method?.name)
553
+ .putString("syncHeaders", options.sync?.headers?.let(::stringMapToJson))
554
+ .putString("syncBodyTemplate", options.sync?.bodyTemplate?.let(::variantMapToJson))
555
+ .putBoolean("syncBatchConfigured", options.sync?.batch != null)
556
+ .putFloat("syncBatchSize", (options.sync?.batchSize ?: 50.0).toFloat())
557
+ .putFloat("syncThreshold", (options.sync?.syncThreshold ?: 1.0).toFloat())
558
+ .putFloat("syncInterval", (options.sync?.syncInterval ?: 0.0).toFloat())
559
+ .putBoolean("syncBatch", options.sync?.batch == true)
560
+ .putBoolean("syncRetry", options.sync?.retry == true)
561
+ .putFloat("syncMaxRetries", (options.sync?.maxRetries ?: 3.0).toFloat())
562
+ .putBoolean("syncAutoClear", options.sync?.autoClear == true)
563
+ .apply()
564
+ }
565
+
566
+ private fun restoreConfig(): BackgroundLocationOptions? {
567
+ if (!prefs.getBoolean("configured", false)) return null
568
+ val title = prefs.getString("notificationTitle", null) ?: return null
569
+ val text = prefs.getString("notificationText", null) ?: return null
570
+ val service = AndroidForegroundServiceOptions(
571
+ prefs.getFloat("notificationId", 9471f).toDouble(),
572
+ title,
573
+ text,
574
+ prefs.getString("notificationChannelId", null),
575
+ prefs.getString("notificationChannelName", null),
576
+ prefs.getString("notificationChannelDescription", null),
577
+ prefs.getString("notificationIcon", null),
578
+ prefs.getString("notificationColor", null),
579
+ prefs.getString("stopActionTitle", null)
580
+ )
581
+ val sync = prefs.getString("syncUrl", null)?.let { url ->
582
+ BackgroundHttpSyncOptions(
583
+ url,
584
+ prefs.getString("syncMethod", null)?.let {
585
+ runCatching { enumValueOf<BackgroundHttpMethod>(it) }.getOrNull()
586
+ },
587
+ prefs.getString("syncHeaders", null)?.let(::jsonToStringMap),
588
+ if (prefs.getBoolean("syncBatchConfigured", false)) {
589
+ prefs.getBoolean("syncBatch", false)
590
+ } else {
591
+ null
592
+ },
593
+ prefs.getFloat("syncBatchSize", 50f).toDouble(),
594
+ prefs.getFloat("syncThreshold", 1f).toDouble(),
595
+ prefs.getFloat("syncInterval", 0f).toDouble(),
596
+ prefs.getBoolean("syncRetry", false),
597
+ prefs.getFloat("syncMaxRetries", 3f).toDouble(),
598
+ prefs.getString("syncBodyTemplate", null)?.let(::jsonToVariantMap),
599
+ prefs.getBoolean("syncAutoClear", false)
600
+ )
601
+ }
602
+ val accuracyAndroid = prefs.getString("accuracyAndroid", null)?.let {
603
+ runCatching { enumValueOf<AndroidAccuracyPreset>(it) }.getOrNull()
604
+ }
605
+ val accuracyIos = prefs.getString("accuracyIos", null)?.let {
606
+ runCatching { enumValueOf<IOSAccuracyPreset>(it) }.getOrNull()
607
+ }
608
+ val accuracy = if (accuracyAndroid != null || accuracyIos != null) {
609
+ LocationAccuracyOptions(accuracyAndroid, accuracyIos)
610
+ } else {
611
+ null
612
+ }
613
+ val activityRecognition = if (prefs.getBoolean("activityConfigured", false)) {
614
+ ActivityRecognitionOptions(
615
+ prefs.getBoolean("activityEnabled", false),
616
+ prefs.getFloat("activityInterval", 10_000f).toDouble(),
617
+ prefs.getBoolean("activityStopOnStill", false),
618
+ prefs.getFloat("activityMinimumConfidence", 0f).toDouble()
619
+ )
620
+ } else {
621
+ null
622
+ }
623
+ return BackgroundLocationOptions(
624
+ prefs.getString("trackingMode", null)?.let {
625
+ runCatching { enumValueOf<BackgroundTrackingMode>(it) }.getOrNull()
626
+ },
627
+ accuracy,
628
+ prefs.getString("granularity", null)?.let {
629
+ runCatching { enumValueOf<AndroidGranularity>(it) }.getOrNull()
630
+ },
631
+ prefs.getFloat("interval", 10_000f).toDouble(),
632
+ prefs.getFloat("fastestInterval", 5_000f).toDouble(),
633
+ prefs.getFloat("distanceFilter", 0f).toDouble(),
634
+ prefs.getFloat("maxUpdateDelay", 0f).toDouble(),
635
+ prefs.getBoolean("waitForAccurateLocation", false),
636
+ prefs.getBoolean("persist", true),
637
+ prefs.getFloat("maxStoredLocations", 0f).toDouble().takeIf { it > 0 },
638
+ prefs.getFloat("maxStoredEvents", 0f).toDouble().takeIf { it > 0 },
639
+ prefs.getBoolean("stopOnTerminate", true),
640
+ prefs.getBoolean("startOnBoot", false),
641
+ AndroidBackgroundLocationOptions(
642
+ prefs.getString("androidLocationProvider", null)?.let {
643
+ runCatching { enumValueOf<AndroidBackgroundProvider>(it) }.getOrNull()
644
+ } ?: AndroidBackgroundProvider.AUTO,
645
+ service,
646
+ prefs.getBoolean("androidRequestNotificationPermission", true),
647
+ prefs.getBoolean("androidRequestIgnoreBatteryOptimizations", false)
648
+ ),
649
+ null,
650
+ null,
651
+ activityRecognition,
652
+ sync
653
+ )
654
+ }
655
+
656
+ private fun locationPendingIntent(): PendingIntent {
657
+ val intent = Intent(appContext, NitroLocationUpdateReceiver::class.java)
658
+ .setAction(ACTION_LOCATION_UPDATE)
659
+ return PendingIntent.getBroadcast(
660
+ appContext,
661
+ 1001,
662
+ intent,
663
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
664
+ )
665
+ }
666
+
667
+ private fun geofencePendingIntent(): PendingIntent {
668
+ val intent = Intent(appContext, NitroGeofenceReceiver::class.java)
669
+ .setAction(ACTION_GEOFENCE_UPDATE)
670
+ return PendingIntent.getBroadcast(
671
+ appContext,
672
+ 1002,
673
+ intent,
674
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
675
+ )
676
+ }
677
+
678
+ private fun activityPendingIntent(): PendingIntent {
679
+ val intent = Intent(appContext, NitroActivityRecognitionReceiver::class.java)
680
+ .setAction(ACTION_ACTIVITY_UPDATE)
681
+ return PendingIntent.getBroadcast(
682
+ appContext,
683
+ 1003,
684
+ intent,
685
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
686
+ )
687
+ }
688
+
689
+ private fun resolvePriority(options: BackgroundLocationOptions): Int {
690
+ return when (options.accuracy?.android) {
691
+ AndroidAccuracyPreset.HIGH -> Priority.PRIORITY_HIGH_ACCURACY
692
+ AndroidAccuracyPreset.LOW -> Priority.PRIORITY_LOW_POWER
693
+ AndroidAccuracyPreset.PASSIVE -> Priority.PRIORITY_PASSIVE
694
+ else -> Priority.PRIORITY_BALANCED_POWER_ACCURACY
695
+ }
696
+ }
697
+
698
+ companion object {
699
+ @Volatile
700
+ private var instance: NitroBackgroundLocationController? = null
701
+
702
+ fun getInstance(context: Context): NitroBackgroundLocationController {
703
+ return instance ?: synchronized(this) {
704
+ instance ?: NitroBackgroundLocationController(context.applicationContext).also {
705
+ instance = it
706
+ }
707
+ }
708
+ }
709
+ }
710
+ }