react-native-nitro-geolocation 1.1.3 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +316 -0
- package/android/build.gradle +6 -0
- package/android/src/main/java/com/margelo/nitro/nitrogeolocation/AndroidAccuracy.kt +105 -0
- package/android/src/main/java/com/margelo/nitro/nitrogeolocation/AndroidHeadingManager.kt +313 -0
- package/android/src/main/java/com/margelo/nitro/nitrogeolocation/AndroidLocationSettings.kt +313 -0
- package/android/src/main/java/com/margelo/nitro/nitrogeolocation/GetCurrentPosition.kt +46 -45
- package/android/src/main/java/com/margelo/nitro/nitrogeolocation/LocationMetadata.kt +26 -0
- package/android/src/main/java/com/margelo/nitro/nitrogeolocation/LocationValues.kt +31 -0
- package/android/src/main/java/com/margelo/nitro/nitrogeolocation/NitroGeolocation.kt +1025 -140
- package/android/src/main/java/com/margelo/nitro/nitrogeolocation/NitroGeolocationCompat.kt +11 -11
- package/android/src/main/java/com/margelo/nitro/nitrogeolocation/RequestAuthorization.kt +6 -6
- package/android/src/main/java/com/margelo/nitro/nitrogeolocation/WatchPosition.kt +46 -45
- package/ios/CLLocation+GeolocationMetadata.swift +32 -0
- package/ios/LocationManager.swift +205 -51
- package/ios/NitroGeolocation.swift +949 -110
- package/ios/NitroGeolocationCompat.swift +7 -7
- package/nitrogen/generated/android/c++/JAccuracyAuthorization.hpp +61 -0
- package/nitrogen/generated/android/c++/JAndroidAccuracyPreset.hpp +64 -0
- package/nitrogen/generated/android/c++/JAndroidGranularity.hpp +61 -0
- package/nitrogen/generated/android/c++/{JRNConfigurationInternal.hpp → JCompatGeolocationConfigurationInternal.hpp} +10 -10
- package/nitrogen/generated/android/c++/{JGeolocationError.hpp → JCompatGeolocationError.hpp} +10 -10
- package/nitrogen/generated/android/c++/JCompatGeolocationOptions.hpp +105 -0
- package/nitrogen/generated/android/c++/JCompatGeolocationResponse.hpp +67 -0
- package/nitrogen/generated/android/c++/JFunc_void_AccuracyAuthorization.hpp +77 -0
- package/nitrogen/generated/android/c++/JFunc_void_CompatGeolocationError.hpp +78 -0
- package/nitrogen/generated/android/c++/JFunc_void_CompatGeolocationResponse.hpp +84 -0
- package/nitrogen/generated/android/c++/JFunc_void_GeolocationResponse.hpp +2 -0
- package/nitrogen/generated/android/c++/JFunc_void_Heading.hpp +78 -0
- package/nitrogen/generated/android/c++/JFunc_void_LocationProviderStatus.hpp +78 -0
- package/nitrogen/generated/android/c++/JFunc_void_PermissionStatus.hpp +77 -0
- package/nitrogen/generated/android/c++/JFunc_void_std__vector_GeocodedLocation_.hpp +97 -0
- package/nitrogen/generated/android/c++/JFunc_void_std__vector_ReverseGeocodedAddress_.hpp +98 -0
- package/nitrogen/generated/android/c++/JGeocodedLocation.hpp +65 -0
- package/nitrogen/generated/android/c++/JGeocodingCoordinates.hpp +61 -0
- package/nitrogen/generated/android/c++/{JModernGeolocationConfiguration.hpp → JGeolocationConfiguration.hpp} +10 -10
- package/nitrogen/generated/android/c++/JGeolocationResponse.hpp +13 -3
- package/nitrogen/generated/android/c++/JHeading.hpp +69 -0
- package/nitrogen/generated/android/c++/JHeadingOptions.hpp +57 -0
- package/nitrogen/generated/android/c++/JHybridNitroGeolocationCompatSpec.cpp +46 -30
- package/nitrogen/generated/android/c++/JHybridNitroGeolocationCompatSpec.hpp +4 -4
- package/nitrogen/generated/android/c++/JHybridNitroGeolocationSpec.cpp +169 -33
- package/nitrogen/generated/android/c++/JHybridNitroGeolocationSpec.hpp +14 -3
- package/nitrogen/generated/android/c++/JIOSAccuracyPreset.hpp +73 -0
- package/nitrogen/generated/android/c++/JIOSActivityType.hpp +67 -0
- package/nitrogen/generated/android/c++/JLocationAccuracyOptions.hpp +65 -0
- package/nitrogen/generated/android/c++/JLocationAvailability.hpp +62 -0
- package/nitrogen/generated/android/c++/JLocationProviderStatus.hpp +77 -0
- package/nitrogen/generated/android/c++/JLocationProviderUsed.hpp +67 -0
- package/nitrogen/generated/android/c++/JLocationRequestOptions.hpp +49 -3
- package/nitrogen/generated/android/c++/{JGeolocationOptions.hpp → JLocationSettingsOptions.hpp} +28 -22
- package/nitrogen/generated/android/c++/JReverseGeocodedAddress.hpp +82 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/AccuracyAuthorization.kt +24 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/AndroidAccuracyPreset.kt +25 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/AndroidGranularity.kt +24 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/{RNConfigurationInternal.kt → CompatGeolocationConfigurationInternal.kt} +5 -5
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/{GeolocationError.kt → CompatGeolocationError.kt} +5 -5
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/CompatGeolocationOptions.kt +68 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/CompatGeolocationResponse.kt +41 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/Func_void_AccuracyAuthorization.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/{Func_void_GeolocationError.kt → Func_void_CompatGeolocationError.kt} +9 -9
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/Func_void_CompatGeolocationResponse.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/Func_void_Heading.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/Func_void_LocationProviderStatus.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/Func_void_PermissionStatus.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/Func_void_std__vector_GeocodedLocation_.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/Func_void_std__vector_ReverseGeocodedAddress_.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/GeocodedLocation.kt +44 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/GeocodingCoordinates.kt +41 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/{ModernGeolocationConfiguration.kt → GeolocationConfiguration.kt} +5 -5
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/GeolocationResponse.kt +9 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/Heading.kt +47 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/HeadingOptions.kt +38 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/HybridNitroGeolocationCompatSpec.kt +7 -7
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/HybridNitroGeolocationSpec.kt +92 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/IOSAccuracyPreset.kt +28 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/IOSActivityType.kt +26 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/LocationAccuracyOptions.kt +41 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/LocationAvailability.kt +41 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/LocationProviderStatus.kt +53 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/LocationProviderUsed.kt +26 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/LocationRequestOptions.kt +30 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/{GeolocationOptions.kt → LocationSettingsOptions.kt} +11 -11
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/ReverseGeocodedAddress.kt +56 -0
- package/nitrogen/generated/android/nitrogeolocationOnLoad.cpp +18 -4
- package/nitrogen/generated/ios/NitroGeolocation-Swift-Cxx-Bridge.cpp +76 -12
- package/nitrogen/generated/ios/NitroGeolocation-Swift-Cxx-Bridge.hpp +519 -77
- package/nitrogen/generated/ios/NitroGeolocation-Swift-Cxx-Umbrella.hpp +61 -12
- package/nitrogen/generated/ios/c++/HybridNitroGeolocationCompatSpecSwift.hpp +28 -16
- package/nitrogen/generated/ios/c++/HybridNitroGeolocationSpecSwift.hpp +131 -13
- package/nitrogen/generated/ios/swift/AccuracyAuthorization.swift +44 -0
- package/nitrogen/generated/ios/swift/AndroidAccuracyPreset.swift +48 -0
- package/nitrogen/generated/ios/swift/AndroidGranularity.swift +44 -0
- package/nitrogen/generated/ios/swift/{RNConfigurationInternal.swift → CompatGeolocationConfigurationInternal.swift} +5 -5
- package/nitrogen/generated/ios/swift/{GeolocationError.swift → CompatGeolocationError.swift} +5 -5
- package/nitrogen/generated/ios/swift/CompatGeolocationOptions.swift +208 -0
- package/nitrogen/generated/ios/swift/CompatGeolocationResponse.swift +34 -0
- package/nitrogen/generated/ios/swift/Func_void_AccuracyAuthorization.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_CompatGeolocationError.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_CompatGeolocationResponse.swift +46 -0
- package/nitrogen/generated/ios/swift/{Func_void_GeolocationError.swift → Func_void_Heading.swift} +11 -11
- package/nitrogen/generated/ios/swift/Func_void_LocationAvailability.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_LocationProviderStatus.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_bool.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_std__vector_GeocodedLocation_.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_std__vector_ReverseGeocodedAddress_.swift +46 -0
- package/nitrogen/generated/ios/swift/GeocodedLocation.swift +52 -0
- package/nitrogen/generated/ios/swift/GeocodingCoordinates.swift +34 -0
- package/nitrogen/generated/ios/swift/{ModernGeolocationConfiguration.swift → GeolocationConfiguration.swift} +5 -5
- package/nitrogen/generated/ios/swift/GeolocationResponse.swift +31 -2
- package/nitrogen/generated/ios/swift/Heading.swift +70 -0
- package/nitrogen/generated/ios/swift/HeadingOptions.swift +42 -0
- package/nitrogen/generated/ios/swift/HybridNitroGeolocationCompatSpec.swift +4 -4
- package/nitrogen/generated/ios/swift/HybridNitroGeolocationCompatSpec_cxx.swift +28 -28
- package/nitrogen/generated/ios/swift/HybridNitroGeolocationSpec.swift +14 -3
- package/nitrogen/generated/ios/swift/HybridNitroGeolocationSpec_cxx.swift +318 -15
- package/nitrogen/generated/ios/swift/IOSAccuracyPreset.swift +60 -0
- package/nitrogen/generated/ios/swift/IOSActivityType.swift +52 -0
- package/nitrogen/generated/ios/swift/LocationAccuracyOptions.swift +46 -0
- package/nitrogen/generated/ios/swift/LocationAvailability.swift +47 -0
- package/nitrogen/generated/ios/swift/LocationProviderStatus.swift +106 -0
- package/nitrogen/generated/ios/swift/LocationProviderUsed.swift +52 -0
- package/nitrogen/generated/ios/swift/LocationRequestOptions.swift +142 -1
- package/nitrogen/generated/ios/swift/{GeolocationOptions.swift → LocationSettingsOptions.swift} +39 -46
- package/nitrogen/generated/ios/swift/ReverseGeocodedAddress.swift +150 -0
- package/nitrogen/generated/shared/c++/AccuracyAuthorization.hpp +80 -0
- package/nitrogen/generated/shared/c++/AndroidAccuracyPreset.hpp +84 -0
- package/nitrogen/generated/shared/c++/AndroidGranularity.hpp +80 -0
- package/nitrogen/generated/shared/c++/{RNConfigurationInternal.hpp → CompatGeolocationConfigurationInternal.hpp} +11 -11
- package/nitrogen/generated/shared/c++/{GeolocationError.hpp → CompatGeolocationError.hpp} +11 -11
- package/nitrogen/generated/shared/c++/CompatGeolocationOptions.hpp +128 -0
- package/nitrogen/generated/shared/c++/CompatGeolocationResponse.hpp +88 -0
- package/nitrogen/generated/shared/c++/GeocodedLocation.hpp +91 -0
- package/nitrogen/generated/shared/c++/GeocodingCoordinates.hpp +87 -0
- package/nitrogen/generated/shared/c++/{ModernGeolocationConfiguration.hpp → GeolocationConfiguration.hpp} +11 -11
- package/nitrogen/generated/shared/c++/GeolocationResponse.hpp +14 -2
- package/nitrogen/generated/shared/c++/Heading.hpp +95 -0
- package/nitrogen/generated/shared/c++/HeadingOptions.hpp +83 -0
- package/nitrogen/generated/shared/c++/HybridNitroGeolocationCompatSpec.hpp +16 -16
- package/nitrogen/generated/shared/c++/HybridNitroGeolocationSpec.cpp +11 -0
- package/nitrogen/generated/shared/c++/HybridNitroGeolocationSpec.hpp +51 -12
- package/nitrogen/generated/shared/c++/IOSAccuracyPreset.hpp +96 -0
- package/nitrogen/generated/shared/c++/IOSActivityType.hpp +88 -0
- package/nitrogen/generated/shared/c++/LocationAccuracyOptions.hpp +92 -0
- package/nitrogen/generated/shared/c++/LocationAvailability.hpp +88 -0
- package/nitrogen/generated/shared/c++/LocationProviderStatus.hpp +103 -0
- package/nitrogen/generated/shared/c++/LocationProviderUsed.hpp +88 -0
- package/nitrogen/generated/shared/c++/LocationRequestOptions.hpp +47 -3
- package/nitrogen/generated/shared/c++/{GeolocationOptions.hpp → LocationSettingsOptions.hpp} +26 -24
- package/nitrogen/generated/shared/c++/ReverseGeocodedAddress.hpp +108 -0
- package/package.json +1 -1
- package/src/NitroGeolocation.nitro.ts +291 -17
- package/src/NitroGeolocationCompat.nitro.ts +12 -12
- package/src/api/geocode.ts +18 -0
- package/src/api/getAccuracyAuthorization.ts +12 -0
- package/src/api/getCurrentPosition.ts +5 -3
- package/src/api/getHeading.ts +13 -0
- package/src/api/getLastKnownPosition.ts +28 -0
- package/src/api/getLocationAvailability.ts +11 -0
- package/src/api/getProviderStatus.ts +16 -0
- package/src/api/hasServicesEnabled.ts +13 -0
- package/src/api/index.ts +11 -0
- package/src/api/requestLocationSettings.ts +29 -0
- package/src/api/requestPermission.ts +3 -1
- package/src/api/requestTemporaryFullAccuracy.ts +21 -0
- package/src/api/reverseGeocode.ts +23 -0
- package/src/api/setConfiguration.ts +8 -4
- package/src/api/watchHeading.ts +19 -0
- package/src/api/watchPosition.ts +2 -2
- package/src/compat/getCurrentPosition.ts +7 -7
- package/src/compat/index.tsx +5 -5
- package/src/compat/requestAuthorization.ts +2 -2
- package/src/compat/setRNConfiguration.ts +7 -5
- package/src/compat/watchPosition.ts +7 -7
- package/src/devtools/getCurrentPosition.ts +5 -3
- package/src/devtools/index.ts +1 -1
- package/src/devtools/watchPosition.ts +6 -7
- package/src/hooks/useWatchPosition.ts +2 -2
- package/src/index.tsx +35 -6
- package/src/publicTypes.ts +96 -0
- package/src/types.ts +113 -37
- package/src/utils/errors.test.ts +65 -0
- package/src/utils/errors.ts +45 -18
- package/src/utils/index.ts +2 -2
- package/src/utils/provider.test.ts +172 -1
- package/src/utils/provider.ts +50 -5
- package/nitrogen/generated/android/c++/JFunc_void_GeolocationError.hpp +0 -78
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
package com.margelo.nitro.nitrogeolocation
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.hardware.GeomagneticField
|
|
5
|
+
import android.hardware.Sensor
|
|
6
|
+
import android.hardware.SensorEvent
|
|
7
|
+
import android.hardware.SensorEventListener
|
|
8
|
+
import android.hardware.SensorManager
|
|
9
|
+
import android.location.Location
|
|
10
|
+
import android.os.Handler
|
|
11
|
+
import android.os.Looper
|
|
12
|
+
import java.util.UUID
|
|
13
|
+
import java.util.concurrent.ConcurrentHashMap
|
|
14
|
+
import kotlin.math.abs
|
|
15
|
+
|
|
16
|
+
private const val DEFAULT_HEADING_TIMEOUT_MS = 10_000L
|
|
17
|
+
|
|
18
|
+
internal class AndroidHeadingManager(
|
|
19
|
+
context: Context,
|
|
20
|
+
private val createLocationError: (Double, String) -> LocationError,
|
|
21
|
+
private val getReferenceLocation: () -> Location?
|
|
22
|
+
) {
|
|
23
|
+
private data class PendingHeadingRequest(
|
|
24
|
+
val id: UUID,
|
|
25
|
+
val success: (Heading) -> Unit,
|
|
26
|
+
val error: ((LocationError) -> Unit)?,
|
|
27
|
+
val timeoutRunnable: Runnable
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
private data class HeadingSubscription(
|
|
31
|
+
val token: String,
|
|
32
|
+
val success: (Heading) -> Unit,
|
|
33
|
+
val error: ((LocationError) -> Unit)?,
|
|
34
|
+
val headingFilter: Double,
|
|
35
|
+
var lastDeliveredHeading: Double?
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
private val sensorManager =
|
|
39
|
+
context.getSystemService(Context.SENSOR_SERVICE) as? SensorManager
|
|
40
|
+
private val mainHandler = Handler(Looper.getMainLooper())
|
|
41
|
+
private val pendingRequests = ConcurrentHashMap<UUID, PendingHeadingRequest>()
|
|
42
|
+
private val subscriptions = ConcurrentHashMap<String, HeadingSubscription>()
|
|
43
|
+
private val rotationVectorSensor: Sensor? =
|
|
44
|
+
sensorManager?.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR)
|
|
45
|
+
?: sensorManager?.getDefaultSensor(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR)
|
|
46
|
+
private val accelerometerSensor: Sensor? =
|
|
47
|
+
sensorManager?.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
|
|
48
|
+
private val magneticFieldSensor: Sensor? =
|
|
49
|
+
sensorManager?.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD)
|
|
50
|
+
|
|
51
|
+
private var isListening = false
|
|
52
|
+
private var sensorAccuracy: Int? = null
|
|
53
|
+
private var gravityValues: FloatArray? = null
|
|
54
|
+
private var magneticValues: FloatArray? = null
|
|
55
|
+
|
|
56
|
+
private val sensorListener = object : SensorEventListener {
|
|
57
|
+
override fun onSensorChanged(event: SensorEvent) {
|
|
58
|
+
val magneticHeading = when (event.sensor.type) {
|
|
59
|
+
Sensor.TYPE_ROTATION_VECTOR,
|
|
60
|
+
Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR -> {
|
|
61
|
+
headingFromRotationVector(event.values)
|
|
62
|
+
}
|
|
63
|
+
Sensor.TYPE_ACCELEROMETER -> {
|
|
64
|
+
gravityValues = event.values.clone()
|
|
65
|
+
headingFromAccelerationAndMagnetometer()
|
|
66
|
+
}
|
|
67
|
+
Sensor.TYPE_MAGNETIC_FIELD -> {
|
|
68
|
+
magneticValues = event.values.clone()
|
|
69
|
+
headingFromAccelerationAndMagnetometer()
|
|
70
|
+
}
|
|
71
|
+
else -> null
|
|
72
|
+
} ?: return
|
|
73
|
+
|
|
74
|
+
emitHeading(createHeading(magneticHeading))
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {
|
|
78
|
+
sensorAccuracy = accuracy
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
fun getHeading(
|
|
83
|
+
success: (Heading) -> Unit,
|
|
84
|
+
error: ((LocationError) -> Unit)?
|
|
85
|
+
) {
|
|
86
|
+
if (!hasHeadingSensor()) {
|
|
87
|
+
error?.invoke(createUnavailableError())
|
|
88
|
+
return
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
val id = UUID.randomUUID()
|
|
92
|
+
val timeoutRunnable = Runnable {
|
|
93
|
+
pendingRequests.remove(id)?.error?.invoke(
|
|
94
|
+
createLocationError(
|
|
95
|
+
TIMEOUT,
|
|
96
|
+
"Unable to fetch heading within ${DEFAULT_HEADING_TIMEOUT_MS / 1000.0}s."
|
|
97
|
+
)
|
|
98
|
+
)
|
|
99
|
+
stopIfIdle()
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
pendingRequests[id] = PendingHeadingRequest(
|
|
103
|
+
id = id,
|
|
104
|
+
success = success,
|
|
105
|
+
error = error,
|
|
106
|
+
timeoutRunnable = timeoutRunnable
|
|
107
|
+
)
|
|
108
|
+
mainHandler.postDelayed(timeoutRunnable, DEFAULT_HEADING_TIMEOUT_MS)
|
|
109
|
+
startIfNeeded()
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
fun watchHeading(
|
|
113
|
+
success: (Heading) -> Unit,
|
|
114
|
+
error: ((LocationError) -> Unit)?,
|
|
115
|
+
options: HeadingOptions?
|
|
116
|
+
): String {
|
|
117
|
+
val token = UUID.randomUUID().toString()
|
|
118
|
+
val headingFilter = options?.headingFilter ?: 0.0
|
|
119
|
+
|
|
120
|
+
if (!headingFilter.isFinite() || headingFilter < 0.0) {
|
|
121
|
+
error?.invoke(createLocationError(
|
|
122
|
+
INTERNAL_ERROR,
|
|
123
|
+
"headingFilter must be a finite number greater than or equal to 0."
|
|
124
|
+
))
|
|
125
|
+
return token
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (!hasHeadingSensor()) {
|
|
129
|
+
error?.invoke(createUnavailableError())
|
|
130
|
+
return token
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
subscriptions[token] = HeadingSubscription(
|
|
134
|
+
token = token,
|
|
135
|
+
success = success,
|
|
136
|
+
error = error,
|
|
137
|
+
headingFilter = headingFilter,
|
|
138
|
+
lastDeliveredHeading = null
|
|
139
|
+
)
|
|
140
|
+
startIfNeeded()
|
|
141
|
+
|
|
142
|
+
return token
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
fun unwatch(token: String) {
|
|
146
|
+
subscriptions.remove(token)
|
|
147
|
+
stopIfIdle()
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
fun stopObserving() {
|
|
151
|
+
pendingRequests.values.forEach { request ->
|
|
152
|
+
mainHandler.removeCallbacks(request.timeoutRunnable)
|
|
153
|
+
}
|
|
154
|
+
pendingRequests.clear()
|
|
155
|
+
subscriptions.clear()
|
|
156
|
+
stopListening()
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
fun hasActiveWork(): Boolean {
|
|
160
|
+
return pendingRequests.isNotEmpty() || subscriptions.isNotEmpty()
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
private fun hasHeadingSensor(): Boolean {
|
|
164
|
+
return sensorManager != null &&
|
|
165
|
+
(rotationVectorSensor != null || (accelerometerSensor != null && magneticFieldSensor != null))
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
private fun startIfNeeded() {
|
|
169
|
+
if (isListening) return
|
|
170
|
+
|
|
171
|
+
val manager = sensorManager ?: return
|
|
172
|
+
val didRegister = if (rotationVectorSensor != null) {
|
|
173
|
+
manager.registerListener(
|
|
174
|
+
sensorListener,
|
|
175
|
+
rotationVectorSensor,
|
|
176
|
+
SensorManager.SENSOR_DELAY_UI,
|
|
177
|
+
mainHandler
|
|
178
|
+
)
|
|
179
|
+
} else {
|
|
180
|
+
val accelerometerRegistered = manager.registerListener(
|
|
181
|
+
sensorListener,
|
|
182
|
+
accelerometerSensor!!,
|
|
183
|
+
SensorManager.SENSOR_DELAY_UI,
|
|
184
|
+
mainHandler
|
|
185
|
+
)
|
|
186
|
+
val magneticRegistered = manager.registerListener(
|
|
187
|
+
sensorListener,
|
|
188
|
+
magneticFieldSensor!!,
|
|
189
|
+
SensorManager.SENSOR_DELAY_UI,
|
|
190
|
+
mainHandler
|
|
191
|
+
)
|
|
192
|
+
accelerometerRegistered && magneticRegistered
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
isListening = didRegister
|
|
196
|
+
if (!didRegister) {
|
|
197
|
+
val error = createUnavailableError()
|
|
198
|
+
pendingRequests.values.forEach { it.error?.invoke(error) }
|
|
199
|
+
subscriptions.values.forEach { it.error?.invoke(error) }
|
|
200
|
+
stopObserving()
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
private fun stopIfIdle() {
|
|
205
|
+
if (!hasActiveWork()) {
|
|
206
|
+
stopListening()
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
private fun stopListening() {
|
|
211
|
+
if (!isListening) return
|
|
212
|
+
|
|
213
|
+
try {
|
|
214
|
+
sensorManager?.unregisterListener(sensorListener)
|
|
215
|
+
} catch (_: Exception) {
|
|
216
|
+
// Ignore unregister races.
|
|
217
|
+
}
|
|
218
|
+
isListening = false
|
|
219
|
+
gravityValues = null
|
|
220
|
+
magneticValues = null
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
private fun headingFromRotationVector(values: FloatArray): Double? {
|
|
224
|
+
val rotationMatrix = FloatArray(9)
|
|
225
|
+
val orientation = FloatArray(3)
|
|
226
|
+
SensorManager.getRotationMatrixFromVector(rotationMatrix, values)
|
|
227
|
+
SensorManager.getOrientation(rotationMatrix, orientation)
|
|
228
|
+
return normalizeHeading(Math.toDegrees(orientation[0].toDouble()))
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
private fun headingFromAccelerationAndMagnetometer(): Double? {
|
|
232
|
+
val gravity = gravityValues ?: return null
|
|
233
|
+
val magnetic = magneticValues ?: return null
|
|
234
|
+
val rotationMatrix = FloatArray(9)
|
|
235
|
+
val inclinationMatrix = FloatArray(9)
|
|
236
|
+
|
|
237
|
+
if (!SensorManager.getRotationMatrix(rotationMatrix, inclinationMatrix, gravity, magnetic)) {
|
|
238
|
+
return null
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
val orientation = FloatArray(3)
|
|
242
|
+
SensorManager.getOrientation(rotationMatrix, orientation)
|
|
243
|
+
return normalizeHeading(Math.toDegrees(orientation[0].toDouble()))
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
private fun createHeading(magneticHeading: Double): Heading {
|
|
247
|
+
val referenceLocation = getReferenceLocation()
|
|
248
|
+
val trueHeading = referenceLocation?.let { location ->
|
|
249
|
+
val field = GeomagneticField(
|
|
250
|
+
location.latitude.toFloat(),
|
|
251
|
+
location.longitude.toFloat(),
|
|
252
|
+
if (location.hasAltitude()) location.altitude.toFloat() else 0f,
|
|
253
|
+
location.time
|
|
254
|
+
)
|
|
255
|
+
normalizeHeading(magneticHeading + field.declination)
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return Heading(
|
|
259
|
+
magneticHeading = magneticHeading,
|
|
260
|
+
trueHeading = trueHeading,
|
|
261
|
+
accuracy = sensorAccuracy?.takeIf {
|
|
262
|
+
it != SensorManager.SENSOR_STATUS_UNRELIABLE
|
|
263
|
+
}?.toDouble(),
|
|
264
|
+
timestamp = System.currentTimeMillis().toDouble()
|
|
265
|
+
)
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
private fun emitHeading(heading: Heading) {
|
|
269
|
+
val pendingSnapshot = pendingRequests.values.toList()
|
|
270
|
+
pendingSnapshot.forEach { request ->
|
|
271
|
+
mainHandler.removeCallbacks(request.timeoutRunnable)
|
|
272
|
+
if (pendingRequests.remove(request.id) != null) {
|
|
273
|
+
request.success(heading)
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
subscriptions.values.forEach { subscription ->
|
|
278
|
+
val lastHeading = subscription.lastDeliveredHeading
|
|
279
|
+
if (
|
|
280
|
+
lastHeading == null ||
|
|
281
|
+
angularDistance(lastHeading, heading.magneticHeading) >= subscription.headingFilter
|
|
282
|
+
) {
|
|
283
|
+
subscription.lastDeliveredHeading = heading.magneticHeading
|
|
284
|
+
subscription.success(heading)
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
stopIfIdle()
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
private fun angularDistance(first: Double, second: Double): Double {
|
|
292
|
+
val distance = abs(first - second) % 360.0
|
|
293
|
+
return if (distance > 180.0) 360.0 - distance else distance
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
private fun normalizeHeading(value: Double): Double {
|
|
297
|
+
val normalized = value % 360.0
|
|
298
|
+
return if (normalized < 0.0) normalized + 360.0 else normalized
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
private fun createUnavailableError(): LocationError {
|
|
302
|
+
return createLocationError(
|
|
303
|
+
POSITION_UNAVAILABLE,
|
|
304
|
+
"Heading sensor is not available."
|
|
305
|
+
)
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
private companion object {
|
|
309
|
+
private const val INTERNAL_ERROR = -1.0
|
|
310
|
+
private const val POSITION_UNAVAILABLE = 2.0
|
|
311
|
+
private const val TIMEOUT = 3.0
|
|
312
|
+
}
|
|
313
|
+
}
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
package com.margelo.nitro.nitrogeolocation
|
|
2
|
+
|
|
3
|
+
import android.Manifest
|
|
4
|
+
import android.app.Activity
|
|
5
|
+
import android.content.Context
|
|
6
|
+
import android.content.Intent
|
|
7
|
+
import android.content.IntentSender
|
|
8
|
+
import android.content.pm.PackageManager
|
|
9
|
+
import android.location.LocationManager as AndroidLocationManager
|
|
10
|
+
import android.os.Build
|
|
11
|
+
import android.os.Handler
|
|
12
|
+
import android.os.Looper
|
|
13
|
+
import androidx.core.content.ContextCompat
|
|
14
|
+
import com.facebook.react.bridge.BaseActivityEventListener
|
|
15
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
16
|
+
import com.google.android.gms.common.ConnectionResult
|
|
17
|
+
import com.google.android.gms.common.GoogleApiAvailability
|
|
18
|
+
import com.google.android.gms.common.api.ResolvableApiException
|
|
19
|
+
import com.google.android.gms.location.LocationRequest as GmsLocationRequest
|
|
20
|
+
import com.google.android.gms.location.LocationServices
|
|
21
|
+
import com.google.android.gms.location.LocationSettingsRequest
|
|
22
|
+
import com.google.android.gms.location.Priority
|
|
23
|
+
import java.util.concurrent.atomic.AtomicBoolean
|
|
24
|
+
|
|
25
|
+
private const val LOCATION_SETTINGS_REQUEST_CODE = 8948
|
|
26
|
+
private const val GOOGLE_LOCATION_ACCURACY_TIMEOUT_MS = 2_000L
|
|
27
|
+
|
|
28
|
+
internal class AndroidLocationSettings(
|
|
29
|
+
private val reactContext: ReactApplicationContext,
|
|
30
|
+
private val locationManager: AndroidLocationManager,
|
|
31
|
+
private val createLocationError: (Double, String) -> LocationError,
|
|
32
|
+
private val createPlayServicesUnavailableError: () -> LocationError
|
|
33
|
+
) {
|
|
34
|
+
private data class ParsedSettingsOptions(
|
|
35
|
+
val androidAccuracy: AndroidAccuracyResolution,
|
|
36
|
+
val intervalMillis: Long,
|
|
37
|
+
val fastestIntervalMillis: Long,
|
|
38
|
+
val distanceFilterMeters: Float,
|
|
39
|
+
val alwaysShow: Boolean,
|
|
40
|
+
val needBle: Boolean
|
|
41
|
+
) {
|
|
42
|
+
companion object {
|
|
43
|
+
private const val DEFAULT_INTERVAL_MS = 5_000.0
|
|
44
|
+
private const val DEFAULT_FASTEST_INTERVAL_MS = 1_000.0
|
|
45
|
+
private const val DEFAULT_DISTANCE_FILTER_METERS = 0.0
|
|
46
|
+
|
|
47
|
+
fun parse(options: LocationSettingsOptions?): ParsedSettingsOptions {
|
|
48
|
+
val enableHighAccuracy = options?.enableHighAccuracy ?: true
|
|
49
|
+
return ParsedSettingsOptions(
|
|
50
|
+
androidAccuracy = resolveAndroidAccuracy(
|
|
51
|
+
options?.accuracy,
|
|
52
|
+
enableHighAccuracy
|
|
53
|
+
),
|
|
54
|
+
intervalMillis = coercePositiveMillis(
|
|
55
|
+
options?.interval,
|
|
56
|
+
DEFAULT_INTERVAL_MS
|
|
57
|
+
),
|
|
58
|
+
fastestIntervalMillis = coercePositiveMillis(
|
|
59
|
+
options?.fastestInterval,
|
|
60
|
+
DEFAULT_FASTEST_INTERVAL_MS
|
|
61
|
+
),
|
|
62
|
+
distanceFilterMeters = (options?.distanceFilter
|
|
63
|
+
?: DEFAULT_DISTANCE_FILTER_METERS)
|
|
64
|
+
.coerceAtLeast(0.0)
|
|
65
|
+
.toFloat(),
|
|
66
|
+
alwaysShow = options?.alwaysShow ?: true,
|
|
67
|
+
needBle = options?.needBle ?: false
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
private fun coercePositiveMillis(value: Double?, defaultValue: Double): Long {
|
|
72
|
+
val nextValue = value ?: defaultValue
|
|
73
|
+
return when {
|
|
74
|
+
nextValue.isNaN() || nextValue <= 0.0 -> defaultValue.toLong()
|
|
75
|
+
nextValue.isInfinite() || nextValue >= Long.MAX_VALUE.toDouble() -> Long.MAX_VALUE
|
|
76
|
+
else -> nextValue.toLong()
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
private data class PendingLocationSettingsRequest(
|
|
83
|
+
val success: (LocationProviderStatus) -> Unit,
|
|
84
|
+
val error: ((LocationError) -> Unit)?,
|
|
85
|
+
val options: ParsedSettingsOptions
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
private var pendingLocationSettingsRequest: PendingLocationSettingsRequest? = null
|
|
89
|
+
private val mainHandler = Handler(Looper.getMainLooper())
|
|
90
|
+
|
|
91
|
+
private val activityEventListener = object : BaseActivityEventListener() {
|
|
92
|
+
override fun onActivityResult(
|
|
93
|
+
activity: Activity,
|
|
94
|
+
requestCode: Int,
|
|
95
|
+
resultCode: Int,
|
|
96
|
+
data: Intent?
|
|
97
|
+
) {
|
|
98
|
+
if (requestCode != LOCATION_SETTINGS_REQUEST_CODE) return
|
|
99
|
+
|
|
100
|
+
val pendingRequest = pendingLocationSettingsRequest ?: return
|
|
101
|
+
pendingLocationSettingsRequest = null
|
|
102
|
+
|
|
103
|
+
if (resultCode == Activity.RESULT_OK) {
|
|
104
|
+
checkLocationSettings(pendingRequest, shouldShowResolution = false)
|
|
105
|
+
return
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
pendingRequest.error?.invoke(createLocationError(
|
|
109
|
+
SETTINGS_NOT_SATISFIED,
|
|
110
|
+
"Location settings change was cancelled."
|
|
111
|
+
))
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
init {
|
|
116
|
+
reactContext.addActivityEventListener(activityEventListener)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
fun hasServicesEnabled(): Boolean {
|
|
120
|
+
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
|
121
|
+
locationManager.isLocationEnabled
|
|
122
|
+
} else {
|
|
123
|
+
isProviderEnabled(AndroidLocationManager.GPS_PROVIDER) ||
|
|
124
|
+
isProviderEnabled(AndroidLocationManager.NETWORK_PROVIDER)
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
fun getProviderStatus(success: (LocationProviderStatus) -> Unit) {
|
|
129
|
+
getGoogleLocationAccuracyEnabled { googleLocationAccuracyEnabled ->
|
|
130
|
+
success(createProviderStatus(googleLocationAccuracyEnabled))
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
private fun createProviderStatus(
|
|
135
|
+
googleLocationAccuracyEnabled: Boolean?
|
|
136
|
+
): LocationProviderStatus {
|
|
137
|
+
return LocationProviderStatus(
|
|
138
|
+
locationServicesEnabled = hasServicesEnabled(),
|
|
139
|
+
backgroundModeEnabled = hasBackgroundLocationPermission(),
|
|
140
|
+
gpsAvailable = isProviderEnabled(AndroidLocationManager.GPS_PROVIDER),
|
|
141
|
+
networkAvailable = isProviderEnabled(AndroidLocationManager.NETWORK_PROVIDER),
|
|
142
|
+
passiveAvailable = isProviderEnabled(AndroidLocationManager.PASSIVE_PROVIDER),
|
|
143
|
+
googleLocationAccuracyEnabled = googleLocationAccuracyEnabled
|
|
144
|
+
)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
fun requestLocationSettings(
|
|
148
|
+
success: (LocationProviderStatus) -> Unit,
|
|
149
|
+
error: ((LocationError) -> Unit)?,
|
|
150
|
+
options: LocationSettingsOptions?
|
|
151
|
+
) {
|
|
152
|
+
if (!isGooglePlayServicesAvailable()) {
|
|
153
|
+
error?.invoke(createPlayServicesUnavailableError())
|
|
154
|
+
return
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (pendingLocationSettingsRequest != null) {
|
|
158
|
+
error?.invoke(createLocationError(
|
|
159
|
+
INTERNAL_ERROR,
|
|
160
|
+
"A location settings request is already in progress."
|
|
161
|
+
))
|
|
162
|
+
return
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
val pendingRequest = PendingLocationSettingsRequest(
|
|
166
|
+
success = success,
|
|
167
|
+
error = error,
|
|
168
|
+
options = ParsedSettingsOptions.parse(options)
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
checkLocationSettings(pendingRequest, shouldShowResolution = true)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
private fun checkLocationSettings(
|
|
175
|
+
pendingRequest: PendingLocationSettingsRequest,
|
|
176
|
+
shouldShowResolution: Boolean
|
|
177
|
+
) {
|
|
178
|
+
val settingsClient = LocationServices.getSettingsClient(reactContext)
|
|
179
|
+
settingsClient
|
|
180
|
+
.checkLocationSettings(buildLocationSettingsRequest(pendingRequest.options))
|
|
181
|
+
.addOnSuccessListener {
|
|
182
|
+
getProviderStatus(pendingRequest.success)
|
|
183
|
+
}
|
|
184
|
+
.addOnFailureListener { exception ->
|
|
185
|
+
if (shouldShowResolution && exception is ResolvableApiException) {
|
|
186
|
+
showResolutionDialog(exception, pendingRequest)
|
|
187
|
+
return@addOnFailureListener
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
pendingRequest.error?.invoke(createLocationError(
|
|
191
|
+
SETTINGS_NOT_SATISFIED,
|
|
192
|
+
"Location settings do not satisfy the requested options."
|
|
193
|
+
))
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
private fun showResolutionDialog(
|
|
198
|
+
exception: ResolvableApiException,
|
|
199
|
+
pendingRequest: PendingLocationSettingsRequest
|
|
200
|
+
) {
|
|
201
|
+
val activity = reactContext.currentActivity
|
|
202
|
+
if (activity == null) {
|
|
203
|
+
pendingRequest.error?.invoke(createLocationError(
|
|
204
|
+
INTERNAL_ERROR,
|
|
205
|
+
"No activity available to request location settings."
|
|
206
|
+
))
|
|
207
|
+
return
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
pendingLocationSettingsRequest = pendingRequest
|
|
211
|
+
|
|
212
|
+
try {
|
|
213
|
+
exception.startResolutionForResult(activity, LOCATION_SETTINGS_REQUEST_CODE)
|
|
214
|
+
} catch (e: IntentSender.SendIntentException) {
|
|
215
|
+
pendingLocationSettingsRequest = null
|
|
216
|
+
pendingRequest.error?.invoke(createLocationError(
|
|
217
|
+
INTERNAL_ERROR,
|
|
218
|
+
"Failed to show location settings dialog: ${e.message}"
|
|
219
|
+
))
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
private fun buildLocationSettingsRequest(
|
|
224
|
+
options: ParsedSettingsOptions
|
|
225
|
+
): LocationSettingsRequest {
|
|
226
|
+
val priority = when (options.androidAccuracy.mode) {
|
|
227
|
+
AndroidAccuracyMode.HIGH -> Priority.PRIORITY_HIGH_ACCURACY
|
|
228
|
+
AndroidAccuracyMode.BALANCED -> Priority.PRIORITY_BALANCED_POWER_ACCURACY
|
|
229
|
+
AndroidAccuracyMode.LOW -> Priority.PRIORITY_LOW_POWER
|
|
230
|
+
AndroidAccuracyMode.PASSIVE -> Priority.PRIORITY_PASSIVE
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
val request = GmsLocationRequest
|
|
234
|
+
.Builder(priority, options.intervalMillis)
|
|
235
|
+
.setMinUpdateIntervalMillis(options.fastestIntervalMillis)
|
|
236
|
+
.setMinUpdateDistanceMeters(options.distanceFilterMeters)
|
|
237
|
+
.build()
|
|
238
|
+
|
|
239
|
+
return LocationSettingsRequest
|
|
240
|
+
.Builder()
|
|
241
|
+
.addLocationRequest(request)
|
|
242
|
+
.setAlwaysShow(options.alwaysShow)
|
|
243
|
+
.setNeedBle(options.needBle)
|
|
244
|
+
.build()
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
private fun isProviderEnabled(provider: String): Boolean {
|
|
248
|
+
return try {
|
|
249
|
+
locationManager.isProviderEnabled(provider)
|
|
250
|
+
} catch (e: Exception) {
|
|
251
|
+
false
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
private fun hasBackgroundLocationPermission(): Boolean {
|
|
256
|
+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) return true
|
|
257
|
+
|
|
258
|
+
return ContextCompat.checkSelfPermission(
|
|
259
|
+
reactContext,
|
|
260
|
+
Manifest.permission.ACCESS_BACKGROUND_LOCATION
|
|
261
|
+
) == PackageManager.PERMISSION_GRANTED
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
private fun getGoogleLocationAccuracyEnabled(success: (Boolean?) -> Unit) {
|
|
265
|
+
if (!isGooglePlayServicesAvailable()) {
|
|
266
|
+
success(null)
|
|
267
|
+
return
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
val didComplete = AtomicBoolean(false)
|
|
271
|
+
val timeoutRunnable = Runnable {
|
|
272
|
+
if (didComplete.compareAndSet(false, true)) {
|
|
273
|
+
success(null)
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
fun complete(value: Boolean?) {
|
|
278
|
+
if (didComplete.compareAndSet(false, true)) {
|
|
279
|
+
mainHandler.removeCallbacks(timeoutRunnable)
|
|
280
|
+
success(value)
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
mainHandler.postDelayed(timeoutRunnable, GOOGLE_LOCATION_ACCURACY_TIMEOUT_MS)
|
|
285
|
+
|
|
286
|
+
try {
|
|
287
|
+
LocationServices
|
|
288
|
+
.getSettingsClient(reactContext)
|
|
289
|
+
.isGoogleLocationAccuracyEnabled
|
|
290
|
+
.addOnSuccessListener { enabled ->
|
|
291
|
+
complete(enabled)
|
|
292
|
+
}
|
|
293
|
+
.addOnFailureListener {
|
|
294
|
+
complete(null)
|
|
295
|
+
}
|
|
296
|
+
.addOnCanceledListener {
|
|
297
|
+
complete(null)
|
|
298
|
+
}
|
|
299
|
+
} catch (e: Exception) {
|
|
300
|
+
complete(null)
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
private fun isGooglePlayServicesAvailable(): Boolean {
|
|
305
|
+
return GoogleApiAvailability.getInstance()
|
|
306
|
+
.isGooglePlayServicesAvailable(reactContext) == ConnectionResult.SUCCESS
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
private companion object {
|
|
310
|
+
private const val INTERNAL_ERROR = -1.0
|
|
311
|
+
private const val SETTINGS_NOT_SATISFIED = 5.0
|
|
312
|
+
}
|
|
313
|
+
}
|