react-native-nitro-geolocation 1.1.4 → 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 +74 -8
- 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
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/react-native-nitro-geolocation)
|
|
4
4
|
|
|
5
|
-
**Simple
|
|
5
|
+
**Simple Native Geolocation for React Native** — Powered by Nitro Modules with JSI
|
|
6
6
|
|
|
7
7
|
A complete reimplementation of [`@react-native-community/geolocation`](https://github.com/michalchudziak/react-native-geolocation) for the React Native New Architecture, featuring:
|
|
8
8
|
|
|
@@ -36,7 +36,17 @@ React Native Nitro Geolocation provides **two APIs** to fit your needs:
|
|
|
36
36
|
import {
|
|
37
37
|
setConfiguration,
|
|
38
38
|
requestPermission,
|
|
39
|
+
requestLocationSettings,
|
|
40
|
+
getLocationAvailability,
|
|
39
41
|
getCurrentPosition,
|
|
42
|
+
getLastKnownPosition,
|
|
43
|
+
geocode,
|
|
44
|
+
reverseGeocode,
|
|
45
|
+
getHeading,
|
|
46
|
+
watchHeading,
|
|
47
|
+
unwatch,
|
|
48
|
+
getAccuracyAuthorization,
|
|
49
|
+
requestTemporaryFullAccuracy,
|
|
40
50
|
useWatchPosition,
|
|
41
51
|
} from "react-native-nitro-geolocation";
|
|
42
52
|
|
|
@@ -49,16 +59,50 @@ setConfiguration({
|
|
|
49
59
|
// Request permission
|
|
50
60
|
const status = await requestPermission();
|
|
51
61
|
|
|
62
|
+
// Android, v1.2+: ask the user to enable settings required for accurate location
|
|
63
|
+
await requestLocationSettings({ accuracy: { android: "high" } });
|
|
64
|
+
|
|
65
|
+
// v1.2+: check whether the platform can currently provide locations
|
|
66
|
+
const availability = await getLocationAvailability();
|
|
67
|
+
|
|
52
68
|
// Get current location
|
|
53
69
|
const position = await getCurrentPosition({
|
|
54
|
-
|
|
70
|
+
accuracy: { android: "high", ios: "best" },
|
|
71
|
+
granularity: "permission",
|
|
72
|
+
waitForAccurateLocation: true,
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// v1.2+: read cached location explicitly without starting a fresh request
|
|
76
|
+
const cached = await getLastKnownPosition({
|
|
77
|
+
maximumAge: 60_000,
|
|
78
|
+
accuracy: { android: "balanced", ios: "hundredMeters" },
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// v1.2+: convert between addresses and coordinates with native geocoders
|
|
82
|
+
const locations = await geocode("City Hall, Seoul, South Korea");
|
|
83
|
+
const addresses = await reverseGeocode({
|
|
84
|
+
latitude: 37.5665,
|
|
85
|
+
longitude: 126.978,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// v1.2+: inspect precise/reduced accuracy authorization
|
|
89
|
+
const accuracyAuthorization = await getAccuracyAuthorization();
|
|
90
|
+
if (accuracyAuthorization === "reduced") {
|
|
91
|
+
await requestTemporaryFullAccuracy("TurnByTurnNavigation");
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// v1.2+: read and watch compass heading
|
|
95
|
+
const heading = await getHeading();
|
|
96
|
+
const headingToken = watchHeading((nextHeading) => {
|
|
97
|
+
console.log(nextHeading.magneticHeading);
|
|
55
98
|
});
|
|
99
|
+
unwatch(headingToken);
|
|
56
100
|
|
|
57
101
|
// Continuous tracking with hook
|
|
58
102
|
function LocationTracker() {
|
|
59
103
|
const { position, error, isWatching } = useWatchPosition({
|
|
60
104
|
enabled: true,
|
|
61
|
-
|
|
105
|
+
accuracy: { android: "high", ios: "bestForNavigation" },
|
|
62
106
|
distanceFilter: 10,
|
|
63
107
|
});
|
|
64
108
|
|
|
@@ -75,7 +119,7 @@ function LocationTracker() {
|
|
|
75
119
|
|
|
76
120
|
**Benefits**:
|
|
77
121
|
|
|
78
|
-
### 2.
|
|
122
|
+
### 2. Compat API (Compatibility)
|
|
79
123
|
|
|
80
124
|
**Drop-in replacement** for `@react-native-community/geolocation`:
|
|
81
125
|
|
|
@@ -150,7 +194,7 @@ Optional (for background):
|
|
|
150
194
|
|
|
151
195
|
> **Prerequisites**: Requires [Rozenite DevTools](https://github.com/rozenite/rozenite) to be installed.
|
|
152
196
|
>
|
|
153
|
-
> **API Compatibility**: Only works with the Modern API. Does not support the
|
|
197
|
+
> **API Compatibility**: Only works with the Modern API. Does not support the Compat API (`/compat`).
|
|
154
198
|
|
|
155
199
|
Mock geolocation data during development with an interactive map interface:
|
|
156
200
|
|
|
@@ -200,16 +244,38 @@ function App() {
|
|
|
200
244
|
|
|
201
245
|
```tsx
|
|
202
246
|
// Get current location
|
|
203
|
-
const position = await getCurrentPosition({
|
|
247
|
+
const position = await getCurrentPosition({
|
|
248
|
+
accuracy: { android: "high", ios: "best" },
|
|
249
|
+
});
|
|
204
250
|
|
|
205
251
|
// Real-time tracking with hook
|
|
206
252
|
const { position, error } = useWatchPosition({
|
|
207
253
|
enabled: true,
|
|
254
|
+
accuracy: { android: "balanced", ios: "nearestTenMeters" },
|
|
208
255
|
distanceFilter: 10
|
|
209
256
|
});
|
|
210
257
|
```
|
|
211
258
|
|
|
212
|
-
|
|
259
|
+
Accuracy presets are available since `v1.2`.
|
|
260
|
+
|
|
261
|
+
`getLastKnownPosition(options?)`, `getLocationAvailability()`, `getHeading()`,
|
|
262
|
+
`watchHeading()`, `geocode(address)`, `reverseGeocode(coords)`, selected
|
|
263
|
+
Android request options (`granularity`, `waitForAccurateLocation`,
|
|
264
|
+
`maxUpdateAge`, `maxUpdateDelay`, and `maxUpdates`), iOS tuning options
|
|
265
|
+
(`activityType`, `pausesLocationUpdatesAutomatically`, and
|
|
266
|
+
`showsBackgroundLocationIndicator`), `getAccuracyAuthorization()`, and
|
|
267
|
+
`requestTemporaryFullAccuracy(purposeKey)` are available since `v1.2`.
|
|
268
|
+
|
|
269
|
+
`enableHighAccuracy` is deprecated in the Modern API and remains supported only
|
|
270
|
+
for v1 compatibility. Prefer `accuracy`; when `accuracy.android` or
|
|
271
|
+
`accuracy.ios` is provided for the current platform, that explicit preset takes
|
|
272
|
+
precedence over the boolean. `enableHighAccuracy` is expected to be removed from
|
|
273
|
+
the Modern API in v2.
|
|
274
|
+
|
|
275
|
+
The `/compat` API keeps `enableHighAccuracy` for drop-in compatibility with
|
|
276
|
+
`@react-native-community/geolocation`.
|
|
277
|
+
|
|
278
|
+
#### Compat API (Compatibility)
|
|
213
279
|
|
|
214
280
|
```tsx
|
|
215
281
|
import Geolocation from "react-native-nitro-geolocation/compat";
|
|
@@ -238,7 +304,7 @@ Change the import to use `/compat` — 100% API compatible:
|
|
|
238
304
|
- [Introduction](https://react-native-nitro-geolocation.pages.dev/guide/)
|
|
239
305
|
- [Quick Start Guide](https://react-native-nitro-geolocation.pages.dev/guide/quick-start)
|
|
240
306
|
- [Modern API Reference](https://react-native-nitro-geolocation.pages.dev/guide/modern-api)
|
|
241
|
-
- [
|
|
307
|
+
- [Compat API Reference](https://react-native-nitro-geolocation.pages.dev/guide/compat-api)
|
|
242
308
|
- [DevTools Plugin Guide](https://react-native-nitro-geolocation.pages.dev/guide/devtools)
|
|
243
309
|
- [Why Nitro Module?](https://react-native-nitro-geolocation.pages.dev/guide/why-nitro-module)
|
|
244
310
|
- [Benchmark Results](https://react-native-nitro-geolocation.pages.dev/guide/benchmark)
|
package/android/build.gradle
CHANGED
|
@@ -30,6 +30,10 @@ def getExtOrIntegerDefault(name) {
|
|
|
30
30
|
return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["NitroGeolocation_" + name]).toInteger()
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
def getExtOrDefaultValue(name, defaultValue) {
|
|
34
|
+
return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["NitroGeolocation_" + name] ?: defaultValue)
|
|
35
|
+
}
|
|
36
|
+
|
|
33
37
|
android {
|
|
34
38
|
namespace "com.margelo.nitro.nitrogeolocation"
|
|
35
39
|
|
|
@@ -123,6 +127,8 @@ def kotlin_version = getExtOrDefault("kotlinVersion")
|
|
|
123
127
|
|
|
124
128
|
dependencies {
|
|
125
129
|
implementation "com.facebook.react:react-android"
|
|
130
|
+
implementation "com.google.android.gms:play-services-base:${getExtOrDefaultValue('playServicesBaseVersion', '18.5.0')}"
|
|
131
|
+
implementation "com.google.android.gms:play-services-location:${getExtOrDefaultValue('playServicesLocationVersion', '21.3.0')}"
|
|
126
132
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
|
127
133
|
implementation project(":react-native-nitro-modules")
|
|
128
134
|
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
package com.margelo.nitro.nitrogeolocation
|
|
2
|
+
|
|
3
|
+
import android.location.LocationManager
|
|
4
|
+
import com.google.android.gms.location.Granularity
|
|
5
|
+
import com.google.android.gms.location.Priority
|
|
6
|
+
|
|
7
|
+
internal enum class AndroidAccuracyMode {
|
|
8
|
+
HIGH,
|
|
9
|
+
BALANCED,
|
|
10
|
+
LOW,
|
|
11
|
+
PASSIVE
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
internal data class AndroidAccuracyResolution(
|
|
15
|
+
val mode: AndroidAccuracyMode,
|
|
16
|
+
val explicitPreset: AndroidAccuracyPreset?
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
internal fun resolveAndroidAccuracy(
|
|
20
|
+
accuracy: LocationAccuracyOptions?,
|
|
21
|
+
enableHighAccuracy: Boolean
|
|
22
|
+
): AndroidAccuracyResolution {
|
|
23
|
+
val preset = accuracy?.android
|
|
24
|
+
val mode = when (preset) {
|
|
25
|
+
AndroidAccuracyPreset.HIGH -> AndroidAccuracyMode.HIGH
|
|
26
|
+
AndroidAccuracyPreset.BALANCED -> AndroidAccuracyMode.BALANCED
|
|
27
|
+
AndroidAccuracyPreset.LOW -> AndroidAccuracyMode.LOW
|
|
28
|
+
AndroidAccuracyPreset.PASSIVE -> AndroidAccuracyMode.PASSIVE
|
|
29
|
+
null -> if (enableHighAccuracy) AndroidAccuracyMode.HIGH else AndroidAccuracyMode.BALANCED
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return AndroidAccuracyResolution(mode = mode, explicitPreset = preset)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
internal fun AndroidAccuracyResolution.providerOrder(): List<String> {
|
|
36
|
+
return when (mode) {
|
|
37
|
+
AndroidAccuracyMode.HIGH -> listOf(
|
|
38
|
+
LocationManager.GPS_PROVIDER,
|
|
39
|
+
LocationManager.NETWORK_PROVIDER
|
|
40
|
+
)
|
|
41
|
+
AndroidAccuracyMode.BALANCED -> if (explicitPreset == null) {
|
|
42
|
+
listOf(LocationManager.NETWORK_PROVIDER, LocationManager.GPS_PROVIDER)
|
|
43
|
+
} else {
|
|
44
|
+
listOf(LocationManager.NETWORK_PROVIDER)
|
|
45
|
+
}
|
|
46
|
+
AndroidAccuracyMode.LOW -> listOf(
|
|
47
|
+
LocationManager.NETWORK_PROVIDER,
|
|
48
|
+
LocationManager.PASSIVE_PROVIDER
|
|
49
|
+
)
|
|
50
|
+
AndroidAccuracyMode.PASSIVE -> listOf(LocationManager.PASSIVE_PROVIDER)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
internal fun AndroidAccuracyResolution.gmsPriority(): Int {
|
|
55
|
+
return when (mode) {
|
|
56
|
+
AndroidAccuracyMode.HIGH -> Priority.PRIORITY_HIGH_ACCURACY
|
|
57
|
+
AndroidAccuracyMode.BALANCED -> Priority.PRIORITY_BALANCED_POWER_ACCURACY
|
|
58
|
+
AndroidAccuracyMode.LOW -> Priority.PRIORITY_LOW_POWER
|
|
59
|
+
AndroidAccuracyMode.PASSIVE -> Priority.PRIORITY_PASSIVE
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
internal fun AndroidGranularity?.gmsGranularity(): Int {
|
|
64
|
+
return when (this) {
|
|
65
|
+
AndroidGranularity.COARSE -> Granularity.GRANULARITY_COARSE
|
|
66
|
+
AndroidGranularity.FINE -> Granularity.GRANULARITY_FINE
|
|
67
|
+
AndroidGranularity.PERMISSION,
|
|
68
|
+
null -> Granularity.GRANULARITY_PERMISSION_LEVEL
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
internal fun AndroidGranularity?.allowsProvider(provider: String): Boolean {
|
|
73
|
+
return when (this) {
|
|
74
|
+
AndroidGranularity.COARSE -> provider != LocationManager.GPS_PROVIDER
|
|
75
|
+
AndroidGranularity.FINE,
|
|
76
|
+
AndroidGranularity.PERMISSION,
|
|
77
|
+
null -> true
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
internal fun mostDemandingAndroidAccuracy(
|
|
82
|
+
current: AndroidAccuracyResolution?,
|
|
83
|
+
next: AndroidAccuracyResolution
|
|
84
|
+
): AndroidAccuracyResolution {
|
|
85
|
+
if (current == null) return next
|
|
86
|
+
|
|
87
|
+
val currentRank = current.mode.demandRank()
|
|
88
|
+
val nextRank = next.mode.demandRank()
|
|
89
|
+
|
|
90
|
+
return when {
|
|
91
|
+
nextRank > currentRank -> next
|
|
92
|
+
nextRank < currentRank -> current
|
|
93
|
+
current.explicitPreset == null && next.explicitPreset != null -> next
|
|
94
|
+
else -> current
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
private fun AndroidAccuracyMode.demandRank(): Int {
|
|
99
|
+
return when (this) {
|
|
100
|
+
AndroidAccuracyMode.PASSIVE -> 0
|
|
101
|
+
AndroidAccuracyMode.LOW -> 1
|
|
102
|
+
AndroidAccuracyMode.BALANCED -> 2
|
|
103
|
+
AndroidAccuracyMode.HIGH -> 3
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -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
|
+
}
|