From 4451693dc8073afce1e5587900a4eb18f37c16ce Mon Sep 17 00:00:00 2001 From: Jordon de Hoog Date: Fri, 24 Jan 2025 12:03:20 -0500 Subject: [PATCH 1/2] update Android's `locationEnabled` to use SettingsClient --- .../browser/internal/DefaultBrowserLocator.kt | 2 +- .../mobile/MobileLocator.android.kt | 2 +- .../mobile/internal/LocationManager.kt | 42 +++++++++++++++++-- .../geolocation/mobile/MobileLocator.ios.kt | 2 +- .../api/android/compass-geolocation.api | 6 +-- .../api/jvm/compass-geolocation.api | 6 +-- .../jordond/compass/geolocation/Geolocator.kt | 2 +- .../jordond/compass/geolocation/Locator.kt | 4 +- .../geolocation/internal/DefaultGeolocator.kt | 2 +- .../kotlin/geolocation/GeolocationModel.kt | 6 ++- 10 files changed, 55 insertions(+), 19 deletions(-) diff --git a/compass-geolocation-browser/src/commonMain/kotlin/dev/jordond/compass/geolocation/browser/internal/DefaultBrowserLocator.kt b/compass-geolocation-browser/src/commonMain/kotlin/dev/jordond/compass/geolocation/browser/internal/DefaultBrowserLocator.kt index f22cae06..38b7c497 100644 --- a/compass-geolocation-browser/src/commonMain/kotlin/dev/jordond/compass/geolocation/browser/internal/DefaultBrowserLocator.kt +++ b/compass-geolocation-browser/src/commonMain/kotlin/dev/jordond/compass/geolocation/browser/internal/DefaultBrowserLocator.kt @@ -40,7 +40,7 @@ internal class DefaultBrowserLocator : BrowserLocator { ) override val locationUpdates: Flow = _locationUpdates - override fun isAvailable(): Boolean { + override suspend fun isAvailable(): Boolean { return navigator?.geolocation != null } diff --git a/compass-geolocation-mobile/src/androidMain/kotlin/dev/jordond/compass/geolocation/mobile/MobileLocator.android.kt b/compass-geolocation-mobile/src/androidMain/kotlin/dev/jordond/compass/geolocation/mobile/MobileLocator.android.kt index 0865d3dd..a215d201 100644 --- a/compass-geolocation-mobile/src/androidMain/kotlin/dev/jordond/compass/geolocation/mobile/MobileLocator.android.kt +++ b/compass-geolocation-mobile/src/androidMain/kotlin/dev/jordond/compass/geolocation/mobile/MobileLocator.android.kt @@ -33,7 +33,7 @@ internal class AndroidLocator( override val locationUpdates: Flow = locationManager.locationUpdates .mapNotNull { result -> result.lastLocation?.toModel() } - override fun isAvailable(): Boolean { + override suspend fun isAvailable(): Boolean { return locationManager.locationEnabled() } diff --git a/compass-geolocation-mobile/src/androidMain/kotlin/dev/jordond/compass/geolocation/mobile/internal/LocationManager.kt b/compass-geolocation-mobile/src/androidMain/kotlin/dev/jordond/compass/geolocation/mobile/internal/LocationManager.kt index 4e0492fa..b3237c48 100644 --- a/compass-geolocation-mobile/src/androidMain/kotlin/dev/jordond/compass/geolocation/mobile/internal/LocationManager.kt +++ b/compass-geolocation-mobile/src/androidMain/kotlin/dev/jordond/compass/geolocation/mobile/internal/LocationManager.kt @@ -3,11 +3,19 @@ package dev.jordond.compass.geolocation.mobile.internal import android.content.Context import android.location.Location import android.location.LocationManager +import android.os.Build.VERSION +import android.os.Build.VERSION_CODES import android.os.Looper 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.LocationSettingsRequest +import com.google.android.gms.location.Priority.PRIORITY_BALANCED_POWER_ACCURACY +import com.google.android.gms.location.Priority.PRIORITY_HIGH_ACCURACY +import com.google.android.gms.location.Priority.PRIORITY_LOW_POWER +import com.google.android.gms.location.Priority.PRIORITY_PASSIVE +import com.google.android.gms.location.SettingsClient import com.google.android.gms.tasks.CancellationTokenSource import dev.jordond.compass.exception.NotFoundException import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -33,11 +41,25 @@ internal class LocationManager( LocationServices.getFusedLocationProviderClient(context) } - fun locationEnabled(): Boolean { - val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager + private val settingsClient: SettingsClient by lazy { + LocationServices.getSettingsClient(context) + } + + suspend fun locationEnabled(): Boolean { + val request = LocationSettingsRequest.Builder().addAllLocationRequests( + listOf( + LocationRequest.Builder(PRIORITY_HIGH_ACCURACY, 100).build(), + LocationRequest.Builder(PRIORITY_BALANCED_POWER_ACCURACY, 100).build(), + LocationRequest.Builder(PRIORITY_PASSIVE, 100).build(), + LocationRequest.Builder(PRIORITY_LOW_POWER, 100).build(), + ) + ).build() - return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) - || locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER) + val result = runCatching { + settingsClient.checkLocationSettings(request).await() + }.isSuccess + + return result || legacyLocationEnabled() } @OptIn(ExperimentalCoroutinesApi::class) @@ -75,4 +97,16 @@ internal class LocationManager( locationCallback = null } } + + private fun legacyLocationEnabled(): Boolean { + val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager + + val providers = listOfNotNull( + LocationManager.GPS_PROVIDER, + LocationManager.NETWORK_PROVIDER, + if (VERSION.SDK_INT >= VERSION_CODES.S) LocationManager.FUSED_PROVIDER else null, + ) + + return providers.any { locationManager.isProviderEnabled(it) } + } } diff --git a/compass-geolocation-mobile/src/iosMain/kotlin/dev/jordond/compass/geolocation/mobile/MobileLocator.ios.kt b/compass-geolocation-mobile/src/iosMain/kotlin/dev/jordond/compass/geolocation/mobile/MobileLocator.ios.kt index a8653669..73aeab65 100644 --- a/compass-geolocation-mobile/src/iosMain/kotlin/dev/jordond/compass/geolocation/mobile/MobileLocator.ios.kt +++ b/compass-geolocation-mobile/src/iosMain/kotlin/dev/jordond/compass/geolocation/mobile/MobileLocator.ios.kt @@ -43,7 +43,7 @@ internal class IosLocator( } } - override fun isAvailable(): Boolean { + override suspend fun isAvailable(): Boolean { return CLLocationManager.locationServicesEnabled() } diff --git a/compass-geolocation/api/android/compass-geolocation.api b/compass-geolocation/api/android/compass-geolocation.api index bfb062ba..7e04c494 100644 --- a/compass-geolocation/api/android/compass-geolocation.api +++ b/compass-geolocation/api/android/compass-geolocation.api @@ -10,7 +10,7 @@ public abstract interface class dev/jordond/compass/geolocation/Geolocator { public abstract fun current (Ldev/jordond/compass/Priority;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public abstract fun getLocationUpdates ()Lkotlinx/coroutines/flow/Flow; public abstract fun getTrackingStatus ()Lkotlinx/coroutines/flow/Flow; - public abstract fun isAvailable ()Z + public abstract fun isAvailable (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public abstract fun startTracking (Ldev/jordond/compass/geolocation/LocationRequest;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public abstract fun stopTracking ()V public abstract fun track (Ldev/jordond/compass/geolocation/LocationRequest;)Lkotlinx/coroutines/flow/Flow; @@ -136,7 +136,7 @@ public abstract interface class dev/jordond/compass/geolocation/Locator { public static final field Companion Ldev/jordond/compass/geolocation/Locator$Companion; public abstract fun current (Ldev/jordond/compass/Priority;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public abstract fun getLocationUpdates ()Lkotlinx/coroutines/flow/Flow; - public abstract fun isAvailable ()Z + public abstract fun isAvailable (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public abstract fun stopTracking ()V public abstract fun track (Ldev/jordond/compass/geolocation/LocationRequest;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } @@ -153,7 +153,7 @@ public final class dev/jordond/compass/geolocation/NotSupportedLocator : dev/jor public static final field INSTANCE Ldev/jordond/compass/geolocation/NotSupportedLocator; public fun current (Ldev/jordond/compass/Priority;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun getLocationUpdates ()Lkotlinx/coroutines/flow/Flow; - public fun isAvailable ()Z + public fun isAvailable (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun stopTracking ()V public fun track (Ldev/jordond/compass/geolocation/LocationRequest;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } diff --git a/compass-geolocation/api/jvm/compass-geolocation.api b/compass-geolocation/api/jvm/compass-geolocation.api index bfb062ba..7e04c494 100644 --- a/compass-geolocation/api/jvm/compass-geolocation.api +++ b/compass-geolocation/api/jvm/compass-geolocation.api @@ -10,7 +10,7 @@ public abstract interface class dev/jordond/compass/geolocation/Geolocator { public abstract fun current (Ldev/jordond/compass/Priority;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public abstract fun getLocationUpdates ()Lkotlinx/coroutines/flow/Flow; public abstract fun getTrackingStatus ()Lkotlinx/coroutines/flow/Flow; - public abstract fun isAvailable ()Z + public abstract fun isAvailable (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public abstract fun startTracking (Ldev/jordond/compass/geolocation/LocationRequest;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public abstract fun stopTracking ()V public abstract fun track (Ldev/jordond/compass/geolocation/LocationRequest;)Lkotlinx/coroutines/flow/Flow; @@ -136,7 +136,7 @@ public abstract interface class dev/jordond/compass/geolocation/Locator { public static final field Companion Ldev/jordond/compass/geolocation/Locator$Companion; public abstract fun current (Ldev/jordond/compass/Priority;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public abstract fun getLocationUpdates ()Lkotlinx/coroutines/flow/Flow; - public abstract fun isAvailable ()Z + public abstract fun isAvailable (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public abstract fun stopTracking ()V public abstract fun track (Ldev/jordond/compass/geolocation/LocationRequest;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } @@ -153,7 +153,7 @@ public final class dev/jordond/compass/geolocation/NotSupportedLocator : dev/jor public static final field INSTANCE Ldev/jordond/compass/geolocation/NotSupportedLocator; public fun current (Ldev/jordond/compass/Priority;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun getLocationUpdates ()Lkotlinx/coroutines/flow/Flow; - public fun isAvailable ()Z + public fun isAvailable (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun stopTracking ()V public fun track (Ldev/jordond/compass/geolocation/LocationRequest;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } diff --git a/compass-geolocation/src/commonMain/kotlin/dev/jordond/compass/geolocation/Geolocator.kt b/compass-geolocation/src/commonMain/kotlin/dev/jordond/compass/geolocation/Geolocator.kt index 7f6e0f87..6e8e6919 100644 --- a/compass-geolocation/src/commonMain/kotlin/dev/jordond/compass/geolocation/Geolocator.kt +++ b/compass-geolocation/src/commonMain/kotlin/dev/jordond/compass/geolocation/Geolocator.kt @@ -46,7 +46,7 @@ public interface Geolocator { * * @return `true` if location services are available, `false` otherwise. */ - public fun isAvailable(): Boolean + public suspend fun isAvailable(): Boolean /** * Get the current location. diff --git a/compass-geolocation/src/commonMain/kotlin/dev/jordond/compass/geolocation/Locator.kt b/compass-geolocation/src/commonMain/kotlin/dev/jordond/compass/geolocation/Locator.kt index dba98dcf..925d359a 100644 --- a/compass-geolocation/src/commonMain/kotlin/dev/jordond/compass/geolocation/Locator.kt +++ b/compass-geolocation/src/commonMain/kotlin/dev/jordond/compass/geolocation/Locator.kt @@ -28,7 +28,7 @@ public interface Locator { /** * Check if the platform supports geolocation. */ - public fun isAvailable(): Boolean + public suspend fun isAvailable(): Boolean /** * Get the current location. @@ -86,7 +86,7 @@ public interface PermissionLocator : Locator { public object NotSupportedLocator : Locator { override val locationUpdates: Flow = emptyFlow() - override fun isAvailable(): Boolean = false + override suspend fun isAvailable(): Boolean = false override suspend fun current(priority: Priority): Location = throw NotSupportedException() override suspend fun track(request: LocationRequest): Flow = emptyFlow() override fun stopTracking() {} diff --git a/compass-geolocation/src/commonMain/kotlin/dev/jordond/compass/geolocation/internal/DefaultGeolocator.kt b/compass-geolocation/src/commonMain/kotlin/dev/jordond/compass/geolocation/internal/DefaultGeolocator.kt index 197cb4c3..83e82f9e 100644 --- a/compass-geolocation/src/commonMain/kotlin/dev/jordond/compass/geolocation/internal/DefaultGeolocator.kt +++ b/compass-geolocation/src/commonMain/kotlin/dev/jordond/compass/geolocation/internal/DefaultGeolocator.kt @@ -43,7 +43,7 @@ internal class DefaultGeolocator( override val trackingStatus: Flow = status - override fun isAvailable(): Boolean = locator.isAvailable() + override suspend fun isAvailable(): Boolean = locator.isAvailable() override suspend fun current(priority: Priority): GeolocatorResult { return handleResult { locator.current(priority) } diff --git a/demo/composeApp/src/commonMain/kotlin/geolocation/GeolocationModel.kt b/demo/composeApp/src/commonMain/kotlin/geolocation/GeolocationModel.kt index f85a9712..7a087deb 100644 --- a/demo/composeApp/src/commonMain/kotlin/geolocation/GeolocationModel.kt +++ b/demo/composeApp/src/commonMain/kotlin/geolocation/GeolocationModel.kt @@ -17,8 +17,10 @@ import kotlinx.coroutines.launch class GeolocationModel(private val geolocator: Geolocator) : StateScreenModel(State()) { init { - val isAvailable = geolocator.isAvailable() - updateState { it.copy(locationServiceAvailable = isAvailable) } + screenModelScope.launch { + val isAvailable = geolocator.isAvailable() + updateState { it.copy(locationServiceAvailable = isAvailable) } + } geolocator.trackingStatus .onEach { status -> From a6980b10ad2889f8a2e08bf0de806c750ebf4de4 Mon Sep 17 00:00:00 2001 From: Jordon de Hoog Date: Fri, 24 Jan 2025 12:11:16 -0500 Subject: [PATCH 2/2] update interval millis --- .../geolocation/mobile/internal/LocationManager.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compass-geolocation-mobile/src/androidMain/kotlin/dev/jordond/compass/geolocation/mobile/internal/LocationManager.kt b/compass-geolocation-mobile/src/androidMain/kotlin/dev/jordond/compass/geolocation/mobile/internal/LocationManager.kt index b3237c48..51a60e20 100644 --- a/compass-geolocation-mobile/src/androidMain/kotlin/dev/jordond/compass/geolocation/mobile/internal/LocationManager.kt +++ b/compass-geolocation-mobile/src/androidMain/kotlin/dev/jordond/compass/geolocation/mobile/internal/LocationManager.kt @@ -48,10 +48,10 @@ internal class LocationManager( suspend fun locationEnabled(): Boolean { val request = LocationSettingsRequest.Builder().addAllLocationRequests( listOf( - LocationRequest.Builder(PRIORITY_HIGH_ACCURACY, 100).build(), - LocationRequest.Builder(PRIORITY_BALANCED_POWER_ACCURACY, 100).build(), - LocationRequest.Builder(PRIORITY_PASSIVE, 100).build(), - LocationRequest.Builder(PRIORITY_LOW_POWER, 100).build(), + LocationRequest.Builder(PRIORITY_HIGH_ACCURACY, 1000).build(), + LocationRequest.Builder(PRIORITY_BALANCED_POWER_ACCURACY, 1000).build(), + LocationRequest.Builder(PRIORITY_PASSIVE, 1000).build(), + LocationRequest.Builder(PRIORITY_LOW_POWER, 1000).build(), ) ).build()