From 8fd2087c832fb5e8aa9fedb82145064cbdb3624d Mon Sep 17 00:00:00 2001 From: stelselim Date: Thu, 2 Jul 2026 10:40:05 +0200 Subject: [PATCH 1/2] migrate geolocation to react native nitro geolocation --- android/app/src/main/AndroidManifest.xml | 1 + ios/Podfile.lock | 89 ++++++++++++++++-------- package-lock.json | 37 ++++++---- package.json | 3 +- 4 files changed, 85 insertions(+), 45 deletions(-) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index e136e20..41acf61 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -20,6 +20,7 @@ + diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 1cedc3b..555b43a 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -198,6 +198,53 @@ PODS: - nanopb/encode (= 2.30910.0) - nanopb/decode (2.30910.0) - nanopb/encode (2.30910.0) + - NitroGeolocation (1.4.2): + - hermes-engine + - NitroModules + - RCTRequired + - RCTTypeSafety + - React-callinvoker + - React-Core + - React-Core-prebuilt + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-jsi + - React-NativeModulesApple + - React-RCTFabric + - React-renderercss + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - ReactNativeDependencies + - Yoga + - NitroModules (0.36.1): + - hermes-engine + - RCTRequired + - RCTTypeSafety + - React-callinvoker + - React-Core + - React-Core-prebuilt + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-jsi + - React-NativeModulesApple + - React-RCTFabric + - React-renderercss + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - ReactNativeDependencies + - Yoga - op-sqlite (15.2.5): - hermes-engine - OpenSSL-Universal @@ -1714,28 +1761,6 @@ PODS: - Yoga - react-native-geocoder (0.5.0): - React - - react-native-geolocation (3.4.0): - - hermes-engine - - RCTRequired - - RCTTypeSafety - - React-Core - - React-Core-prebuilt - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-ImageManager - - React-jsi - - React-NativeModulesApple - - React-RCTFabric - - React-renderercss - - React-rendererdebug - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - ReactNativeDependencies - - Yoga - react-native-google-maps (1.20.1): - Google-Maps-iOS-Utils (= 5.0.0) - GoogleMaps (= 8.4.0) @@ -2923,6 +2948,8 @@ DEPENDENCIES: - IQKeyboardManager - MendixNative (from `../node_modules/mendix-native`) - nanopb + - NitroGeolocation (from `../node_modules/react-native-nitro-geolocation`) + - NitroModules (from `../node_modules/react-native-nitro-modules`) - "op-sqlite (from `../node_modules/@op-engineering/op-sqlite`)" - QRCodeReader.swift (~> 10.1.0) - RCTDeprecation (from `../node_modules/react-native/ReactApple/Libraries/RCTFoundation/RCTDeprecation`) @@ -2967,7 +2994,6 @@ DEPENDENCIES: - "react-native-cameraroll (from `../node_modules/@react-native-camera-roll/camera-roll`)" - react-native-file-viewer-turbo (from `../node_modules/react-native-file-viewer-turbo`) - react-native-geocoder (from `../node_modules/react-native-geocoder`) - - "react-native-geolocation (from `../node_modules/@react-native-community/geolocation`)" - react-native-google-maps (from `../node_modules/react-native-maps`) - react-native-image-picker (from `../node_modules/react-native-image-picker`) - react-native-maps (from `../node_modules/react-native-maps`) @@ -3082,6 +3108,10 @@ EXTERNAL SOURCES: :tag: hermes-v250829098.0.9 MendixNative: :path: "../node_modules/mendix-native" + NitroGeolocation: + :path: "../node_modules/react-native-nitro-geolocation" + NitroModules: + :path: "../node_modules/react-native-nitro-modules" op-sqlite: :path: "../node_modules/@op-engineering/op-sqlite" RCTDeprecation: @@ -3166,8 +3196,6 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native-file-viewer-turbo" react-native-geocoder: :path: "../node_modules/react-native-geocoder" - react-native-geolocation: - :path: "../node_modules/@react-native-community/geolocation" react-native-google-maps: :path: "../node_modules/react-native-maps" react-native-image-picker: @@ -3331,12 +3359,14 @@ SPEC CHECKSUMS: GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a GoogleMaps: 8939898920281c649150e0af74aa291c60f2e77d GoogleUtilities: ea963c370a38a8069cc5f7ba4ca849a60b6d7d15 - hermes-engine: 48a4081d5ebf9ec3b0d6ad6a11254f1f5a7f5bae + hermes-engine: 166613ab57f3dde1bf3f24c60212b66a1d585f8b IQKeyboardManager: c8665b3396bd0b79402b4c573eac345a31c7d485 libwebp: 02b23773aedb6ff1fd38cec7a77b81414c6842a8 MendixNative: 7cba944a4e608e6ab40ec281e9b2b0f9f3cdefe0 MultiplatformBleAdapter: b1fddd0d499b96b607e00f0faa8e60648343dc1d nanopb: 438bc412db1928dac798aa6fd75726007be04262 + NitroGeolocation: 647199e12e95fa2e4030e872ede9a3598fd379d3 + NitroModules: 4d19ff59969b2c67a19175fb17b66917af6708e0 op-sqlite: e9ef65bcf95a97863874cee87841425bb71c8396 OpenSSL-Universal: 9110d21982bb7e8b22a962b6db56a8aa805afde7 PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 @@ -3350,7 +3380,7 @@ SPEC CHECKSUMS: React: 1ba7d364ade7d883a1ec055bfc3606f35fdee17b React-callinvoker: bc2a26f8d84fb01f003fc6de6c9337b64715f95b React-Core: 7840d3a80b43a95c5e80ef75146bd70925ebab0f - React-Core-prebuilt: da3a158f1e31b83bdf28f7855e22a0885cfb9d61 + React-Core-prebuilt: 2439654d53489a5609231fe950d7f4e4c641ad52 React-CoreModules: 2eb010400b63b89e53a324ffb3c112e4c7c3ce42 React-cxxreact: a558e92199d26f145afa9e62c4233cf8e7950efe React-debug: 755200a6e7f5e6e0a40ff8d215493d43cce285fc @@ -3383,7 +3413,6 @@ SPEC CHECKSUMS: react-native-cameraroll: bd1a177463acbba89365d09e81057294eba1e2f6 react-native-file-viewer-turbo: 0da5fe2575b1c2e43ce8246fcd0fa9c3b097a72d react-native-geocoder: bc977303c9c53cce36bcdb3c6f17e6832f86beb6 - react-native-geolocation: cfc39041210bd9f591ec2beb7d417291251da8e5 react-native-google-maps: c4f5c5b2dda17e7cb2cb2b37a81f140b039b3e7e react-native-image-picker: 168d6972e50ea848defe4ac4c222faa9bc52aea0 react-native-maps: 9febd31278b35cd21e4fad2cf6fa708993be5dab @@ -3430,7 +3459,7 @@ SPEC CHECKSUMS: ReactCodegen: 797de5178718324c6eba3327b07f9a423fbd5787 ReactCommon: 07572bf9e687c8a52fbe4a3641e9e3a1a477c78e ReactNativeBiometrics: 9ead8d633833fd13e5a79564af8ba7064c2a58c6 - ReactNativeDependencies: b2c3c0cdbf1823de4ac83c51bf6e64ee0ecd86ad + ReactNativeDependencies: 14132e559d3008cd261e3c00859a58fd4f7fa42c RNBootSplash: 13b7612788960d0c5446e68489ac2509e506cfcd RNCalendarEvents: f90f73666b6bcbb3cc8a491ffbb5e48c0db3de37 RNCAsyncStorage: 3a4f5e2777dae1688b781a487923a08569e27fe4 @@ -3461,6 +3490,6 @@ SPEC CHECKSUMS: VisionCamera: 7187b3dac1ff3071234ead959ce311875748e14f Yoga: c0b3f2c7e8d3e327e450223a2414ca3fa296b9a2 -PODFILE CHECKSUM: 82c9cfbd7db4666e2b937d7c62e7ff7cd5466d02 +PODFILE CHECKSUM: 3ac57498e7a5b50e63c45224f2a40431ea916f8c COCOAPODS: 1.16.2 diff --git a/package-lock.json b/package-lock.json index 6c7c0cd..37290e2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,7 +20,6 @@ "@react-native-community/cli-platform-android": "20.1.3", "@react-native-community/cli-platform-ios": "20.1.3", "@react-native-community/datetimepicker": "8.6.0", - "@react-native-community/geolocation": "3.4.0", "@react-native-community/netinfo": "11.5.2", "@react-native-firebase/analytics": "20.1.0", "@react-native-firebase/app": "20.1.0", @@ -51,6 +50,8 @@ "react-native-linear-gradient": "2.8.3", "react-native-localize": "3.7.0", "react-native-maps": "1.20.1", + "react-native-nitro-geolocation": "1.4.2", + "react-native-nitro-modules": "0.36.1", "react-native-pager-view": "6.9.1", "react-native-permissions": "5.5.1", "react-native-reanimated": "4.3.1", @@ -2273,19 +2274,6 @@ } } }, - "node_modules/@react-native-community/geolocation": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@react-native-community/geolocation/-/geolocation-3.4.0.tgz", - "integrity": "sha512-bzZH89/cwmpkPMKKveoC72C4JH0yF4St5Ceg/ZM9pA1SqX9MlRIrIrrOGZ/+yi++xAvFDiYfihtn9TvXWU9/rA==", - "license": "MIT", - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "react": "*", - "react-native": "*" - } - }, "node_modules/@react-native-community/netinfo": { "version": "11.5.2", "resolved": "https://registry.npmjs.org/@react-native-community/netinfo/-/netinfo-11.5.2.tgz", @@ -8029,6 +8017,27 @@ } } }, + "node_modules/react-native-nitro-geolocation": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/react-native-nitro-geolocation/-/react-native-nitro-geolocation-1.4.2.tgz", + "integrity": "sha512-woUfdQgzWeTFrluUkfLyHIAPCJFCX00BPWDC+1w6c0eLkkr9SNFX0UFANdEP5aOBw+LGhb4utH/LmeVl3X3MmQ==", + "license": "MIT", + "peerDependencies": { + "react": ">=18.0.0", + "react-native": ">=0.75.0", + "react-native-nitro-modules": "*" + } + }, + "node_modules/react-native-nitro-modules": { + "version": "0.36.1", + "resolved": "https://registry.npmjs.org/react-native-nitro-modules/-/react-native-nitro-modules-0.36.1.tgz", + "integrity": "sha512-kBv/VvKqAmkXAvP1DxJMC9b/fRhh7JdSO4EUnPP46hJjrIFeFR8AwKm8mYaKZEuF014M/TVdv2vomVUW0umsQQ==", + "license": "MIT", + "peerDependencies": { + "react": "*", + "react-native": "*" + } + }, "node_modules/react-native-pager-view": { "version": "6.9.1", "resolved": "https://registry.npmjs.org/react-native-pager-view/-/react-native-pager-view-6.9.1.tgz", diff --git a/package.json b/package.json index 6ab0b95..5f18d6f 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,6 @@ "@react-native-community/cli-platform-android": "20.1.3", "@react-native-community/cli-platform-ios": "20.1.3", "@react-native-community/datetimepicker": "8.6.0", - "@react-native-community/geolocation": "3.4.0", "@react-native-community/netinfo": "11.5.2", "@react-native-firebase/analytics": "20.1.0", "@react-native-firebase/app": "20.1.0", @@ -50,6 +49,8 @@ "react-native-fast-image": "8.6.3", "react-native-file-viewer-turbo": "0.7.4", "react-native-geocoder": "0.5.0", + "react-native-nitro-geolocation": "1.4.2", + "react-native-nitro-modules": "0.36.1", "react-native-gesture-handler": "2.31.2", "react-native-image-picker": "7.2.3", "react-native-inappbrowser-reborn": "3.7.0", From 2e3655345cbfafbeb8d615945a5941c4d7e1cccf Mon Sep 17 00:00:00 2001 From: stelselim Date: Wed, 8 Jul 2026 11:11:28 +0200 Subject: [PATCH 2/2] chore: add previous patcheds to Android --- ...react-native-nitro-geolocation+1.4.2.patch | 315 ++++++++++++++++++ 1 file changed, 315 insertions(+) create mode 100644 patches/react-native-nitro-geolocation+1.4.2.patch diff --git a/patches/react-native-nitro-geolocation+1.4.2.patch b/patches/react-native-nitro-geolocation+1.4.2.patch new file mode 100644 index 0000000..ad03173 --- /dev/null +++ b/patches/react-native-nitro-geolocation+1.4.2.patch @@ -0,0 +1,315 @@ +diff --git a/node_modules/react-native-nitro-geolocation/android/src/main/java/com/margelo/nitro/nitrogeolocation/GetCurrentPosition.kt b/node_modules/react-native-nitro-geolocation/android/src/main/java/com/margelo/nitro/nitrogeolocation/GetCurrentPosition.kt +index e3124f0..527e0bb 100644 +--- a/node_modules/react-native-nitro-geolocation/android/src/main/java/com/margelo/nitro/nitrogeolocation/GetCurrentPosition.kt ++++ b/node_modules/react-native-nitro-geolocation/android/src/main/java/com/margelo/nitro/nitrogeolocation/GetCurrentPosition.kt +@@ -12,6 +12,15 @@ import android.os.Looper + import android.util.Log + import androidx.core.content.ContextCompat + import com.facebook.react.bridge.ReactApplicationContext ++import com.google.android.gms.common.ConnectionResult ++import com.google.android.gms.common.GoogleApiAvailability ++import com.google.android.gms.location.FusedLocationProviderClient ++import com.google.android.gms.location.LocationCallback ++import com.google.android.gms.location.LocationRequest ++import com.google.android.gms.location.LocationResult ++import com.google.android.gms.location.LocationServices ++import com.google.android.gms.location.Priority ++import com.google.android.gms.location.LocationAvailability + + class GetCurrentPosition(private val reactContext: ReactApplicationContext) { + +@@ -19,6 +28,121 @@ class GetCurrentPosition(private val reactContext: ReactApplicationContext) { + success: (position: CompatGeolocationResponse) -> Unit, + error: ((error: CompatGeolocationError) -> Unit)?, + options: CompatGeolocationOptions? ++ ) { ++ val opts = parseOptions(options) ++ ++ // Use FusedLocationProviderClient when Play Services are available ++ if (isGooglePlayServicesAvailable()) { ++ getCurrentPositionWithFused(success, error, opts) ++ return ++ } ++ ++ // Fallback to basic LocationManager ++ getCurrentPositionWithLocationManager(success, error, opts) ++ } ++ ++ // ===== Fused Location Provider (Play Services) Path ===== ++ ++ private fun getCurrentPositionWithFused( ++ success: (CompatGeolocationResponse) -> Unit, ++ error: ((CompatGeolocationError) -> Unit)?, ++ options: ParsedOptions ++ ) { ++ val fusedLocationClient = LocationServices.getFusedLocationProviderClient(reactContext) ++ ++ try { ++ fusedLocationClient.lastLocation ++ .addOnSuccessListener { location -> ++ if (location != null && isCachedLocationValid(location, options)) { ++ success(locationToPosition(location)) ++ } else { ++ requestFreshFusedLocation(fusedLocationClient, success, error, options) ++ } ++ } ++ .addOnFailureListener { ++ requestFreshFusedLocation(fusedLocationClient, success, error, options) ++ } ++ } catch (e: SecurityException) { ++ Log.e(TAG, "Security exception: ${e.message}") ++ error?.invoke( ++ createError(PERMISSION_DENIED, "Location permission denied: ${e.message}") ++ ) ++ } ++ } ++ ++ private fun requestFreshFusedLocation( ++ fusedLocationClient: FusedLocationProviderClient, ++ success: (CompatGeolocationResponse) -> Unit, ++ error: ((CompatGeolocationError) -> Unit)?, ++ options: ParsedOptions ++ ) { ++ val handler = Handler(Looper.getMainLooper()) ++ var isResolved = false ++ ++ val locationRequest = LocationRequest.Builder(0) ++ .setPriority( ++ if (options.androidAccuracy.mode == AndroidAccuracyMode.HIGH) ++ Priority.PRIORITY_HIGH_ACCURACY ++ else ++ Priority.PRIORITY_BALANCED_POWER_ACCURACY ++ ) ++ .setMaxUpdateAgeMillis(options.maximumAge.toLong().coerceAtLeast(0L)) ++ .setMinUpdateDistanceMeters(0f) ++ .setMaxUpdates(1) ++ .build() ++ ++ val locationCallback = object : LocationCallback() { ++ override fun onLocationResult(locationResult: LocationResult) { ++ val location = locationResult.lastLocation ++ ++ if (location == null) { ++ // Don't error on null location - wait for timeout or next callback ++ return ++ } ++ ++ if (!isResolved) { ++ isResolved = true ++ handler.removeCallbacksAndMessages(null) ++ fusedLocationClient.removeLocationUpdates(this) ++ success(locationToPosition(location)) ++ } ++ } ++ ++ override fun onLocationAvailability(locationAvailability: LocationAvailability) { ++ if (!locationAvailability.isLocationAvailable) { ++ // Don't error immediately - wait for timeout or a location update ++ return ++ } ++ } ++ } ++ ++ val timeoutRunnable = Runnable { ++ if (!isResolved) { ++ isResolved = true ++ fusedLocationClient.removeLocationUpdates(locationCallback) ++ error?.invoke(createError(TIMEOUT, "Location request timed out")) ++ } ++ } ++ ++ try { ++ fusedLocationClient.requestLocationUpdates( ++ locationRequest, ++ locationCallback, ++ Looper.getMainLooper() ++ ) ++ handler.postDelayed(timeoutRunnable, options.timeout.toLong()) ++ } catch (e: SecurityException) { ++ Log.e(TAG, "SecurityException in fused request: ${e.message}") ++ error?.invoke(createError(PERMISSION_DENIED, "Permission denied: ${e.message}")) ++ } ++ } ++ ++ // ===== Basic LocationManager Path (Fallback) ===== ++ ++ private fun getCurrentPositionWithLocationManager( ++ success: (CompatGeolocationResponse) -> Unit, ++ error: ((CompatGeolocationError) -> Unit)?, ++ options: ParsedOptions + ) { + val locationManager = + reactContext.getSystemService(Context.LOCATION_SERVICE) as? LocationManager +@@ -28,8 +152,7 @@ class GetCurrentPosition(private val reactContext: ReactApplicationContext) { + return + } + +- val opts = parseOptions(options) +- val provider = getValidProvider(locationManager, opts.androidAccuracy) ++ val provider = getValidProvider(locationManager, options.androidAccuracy) + + if (provider == null) { + Log.e(TAG, "No location provider available") +@@ -41,19 +164,19 @@ class GetCurrentPosition(private val reactContext: ReactApplicationContext) { + val lastKnownLocation = locationManager.getLastKnownLocation(provider) + + // Check if cached location is fresh enough +- if (lastKnownLocation != null && isCachedLocationValid(lastKnownLocation, opts)) { ++ if (lastKnownLocation != null && isCachedLocationValid(lastKnownLocation, options)) { + success(locationToPosition(lastKnownLocation)) + return + } + + // If maximumAge is Infinity and we have a last known location, use it +- if (lastKnownLocation != null && opts.maximumAge == Double.POSITIVE_INFINITY) { ++ if (lastKnownLocation != null && options.maximumAge == Double.POSITIVE_INFINITY) { + success(locationToPosition(lastKnownLocation)) + return + } + + // Request fresh location +- requestFreshLocation(locationManager, provider, opts, success, error, lastKnownLocation) ++ requestFreshLocation(locationManager, provider, options, success, error, lastKnownLocation) + } catch (e: SecurityException) { + Log.e(TAG, "Security exception: ${e.message}") + error?.invoke( +@@ -64,6 +187,11 @@ class GetCurrentPosition(private val reactContext: ReactApplicationContext) { + + // ===== Helper Functions ===== + ++ private fun isGooglePlayServicesAvailable(): Boolean { ++ return GoogleApiAvailability.getInstance() ++ .isGooglePlayServicesAvailable(reactContext.applicationContext) == ConnectionResult.SUCCESS ++ } ++ + private fun parseOptions(options: CompatGeolocationOptions?): ParsedOptions { + return ParsedOptions( + timeout = options?.timeout ?: DEFAULT_TIMEOUT, +diff --git a/node_modules/react-native-nitro-geolocation/android/src/main/java/com/margelo/nitro/nitrogeolocation/WatchPosition.kt b/node_modules/react-native-nitro-geolocation/android/src/main/java/com/margelo/nitro/nitrogeolocation/WatchPosition.kt +index e0cbe24..4f29d39 100644 +--- a/node_modules/react-native-nitro-geolocation/android/src/main/java/com/margelo/nitro/nitrogeolocation/WatchPosition.kt ++++ b/node_modules/react-native-nitro-geolocation/android/src/main/java/com/margelo/nitro/nitrogeolocation/WatchPosition.kt +@@ -11,6 +11,14 @@ import android.os.Looper + import android.util.Log + import androidx.core.content.ContextCompat + import com.facebook.react.bridge.ReactApplicationContext ++import com.google.android.gms.common.ConnectionResult ++import com.google.android.gms.common.GoogleApiAvailability ++import com.google.android.gms.location.FusedLocationProviderClient ++import com.google.android.gms.location.LocationCallback ++import com.google.android.gms.location.LocationRequest ++import com.google.android.gms.location.LocationResult ++import com.google.android.gms.location.LocationServices ++import com.google.android.gms.location.Priority + import java.util.concurrent.ConcurrentHashMap + import java.util.concurrent.atomic.AtomicInteger + +@@ -19,8 +27,11 @@ class WatchPosition(private val reactContext: ReactApplicationContext) { + private val watchCallbacks = ConcurrentHashMap() + private val watchIdGenerator = AtomicInteger(0) + private var locationListener: LocationListener? = null ++ private var fusedLocationCallback: LocationCallback? = null ++ private var fusedLocationClient: FusedLocationProviderClient? = null + private var watchedProvider: String? = null + private var currentOptions: CompatGeolocationOptions? = null ++ private var usingFusedProvider = false + + data class WatchCallback( + val success: (CompatGeolocationResponse) -> Unit, +@@ -54,18 +65,80 @@ class WatchPosition(private val reactContext: ReactApplicationContext) { + } + + fun stopObserving() { +- val locationManager = +- reactContext.getSystemService(Context.LOCATION_SERVICE) as? LocationManager +- +- locationListener?.let { listener -> locationManager?.removeUpdates(listener) } ++ if (usingFusedProvider) { ++ fusedLocationCallback?.let { callback -> ++ fusedLocationClient?.removeLocationUpdates(callback) ++ } ++ fusedLocationCallback = null ++ fusedLocationClient = null ++ } else { ++ val locationManager = ++ reactContext.getSystemService(Context.LOCATION_SERVICE) as? LocationManager ++ locationListener?.let { listener -> locationManager?.removeUpdates(listener) } ++ } + + locationListener = null + watchedProvider = null + currentOptions = null ++ usingFusedProvider = false + watchCallbacks.clear() + } + + private fun startObserving(options: CompatGeolocationOptions?) { ++ // Use FusedLocationProviderClient when Play Services are available ++ if (isGooglePlayServicesAvailable()) { ++ startObservingWithFused(options) ++ return ++ } ++ ++ // Fallback to basic LocationManager ++ startObservingWithLocationManager(options) ++ } ++ ++ private fun startObservingWithFused(options: CompatGeolocationOptions?) { ++ val opts = parseOptions(options) ++ val client = LocationServices.getFusedLocationProviderClient(reactContext) ++ ++ val locationRequest = LocationRequest.Builder( ++ if (opts.androidAccuracy.mode == AndroidAccuracyMode.HIGH) ++ Priority.PRIORITY_HIGH_ACCURACY ++ else ++ Priority.PRIORITY_BALANCED_POWER_ACCURACY, ++ opts.interval.toLong() ++ ) ++ .setMinUpdateDistanceMeters(opts.distanceFilter.toFloat()) ++ .build() ++ ++ val callback = object : LocationCallback() { ++ override fun onLocationResult(locationResult: LocationResult) { ++ val location = locationResult.lastLocation ?: return ++ val position = locationToPosition(location) ++ watchCallbacks.values.forEach { watchCallback -> watchCallback.success(position) } ++ } ++ } ++ ++ try { ++ client.requestLocationUpdates( ++ locationRequest, ++ callback, ++ Looper.getMainLooper() ++ ) ++ fusedLocationClient = client ++ fusedLocationCallback = callback ++ usingFusedProvider = true ++ currentOptions = options ++ } catch (e: SecurityException) { ++ Log.e(TAG, "SecurityException: ${e.message}") ++ emitErrorToAll( ++ createError( ++ GetCurrentPosition.PERMISSION_DENIED, ++ "Location permission denied: ${e.message}" ++ ) ++ ) ++ } ++ } ++ ++ private fun startObservingWithLocationManager(options: CompatGeolocationOptions?) { + val locationManager = + reactContext.getSystemService(Context.LOCATION_SERVICE) as? LocationManager + +@@ -143,6 +216,11 @@ class WatchPosition(private val reactContext: ReactApplicationContext) { + } + } + ++ private fun isGooglePlayServicesAvailable(): Boolean { ++ return GoogleApiAvailability.getInstance() ++ .isGooglePlayServicesAvailable(reactContext.applicationContext) == ConnectionResult.SUCCESS ++ } ++ + private fun emitErrorToAll(error: CompatGeolocationError) { + watchCallbacks.values.forEach { callback -> callback.error?.invoke(error) } + }