mirror of
https://github.com/Novaloop-AG/element-x-android.git
synced 2026-11-02 12:07:28 +00:00
Adapt location feature to updated maplibre-compose API
This commit is contained in:
-10
@@ -9,11 +9,9 @@
|
||||
package io.element.android.features.location.impl.common
|
||||
|
||||
import android.Manifest
|
||||
import androidx.compose.ui.Alignment
|
||||
import org.maplibre.compose.camera.CameraPosition
|
||||
import org.maplibre.compose.map.GestureOptions
|
||||
import org.maplibre.compose.map.MapOptions
|
||||
import org.maplibre.compose.map.OrnamentOptions
|
||||
import org.maplibre.compose.map.RenderOptions
|
||||
import org.maplibre.spatialk.geojson.Position
|
||||
|
||||
@@ -24,14 +22,6 @@ object MapDefaults {
|
||||
val options = MapOptions(
|
||||
renderOptions = RenderOptions.Standard,
|
||||
gestureOptions = GestureOptions.Standard,
|
||||
ornamentOptions = OrnamentOptions(
|
||||
isLogoEnabled = true,
|
||||
logoAlignment = Alignment.BottomStart,
|
||||
isAttributionEnabled = true,
|
||||
attributionAlignment = Alignment.BottomEnd,
|
||||
isCompassEnabled = false,
|
||||
isScaleBarEnabled = false,
|
||||
)
|
||||
)
|
||||
|
||||
val defaultCameraPosition = CameraPosition(
|
||||
|
||||
+21
-7
@@ -7,16 +7,19 @@
|
||||
|
||||
package io.element.android.features.location.impl.common.ui
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.BoxScope
|
||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.WindowInsetsSides
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.navigationBars
|
||||
import androidx.compose.foundation.layout.only
|
||||
@@ -32,7 +35,6 @@ import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.material3.rememberBottomSheetScaffoldState
|
||||
import androidx.compose.material3.rememberBottomSheetState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
@@ -55,6 +57,9 @@ import org.maplibre.compose.camera.CameraState
|
||||
import org.maplibre.compose.camera.rememberCameraState
|
||||
import org.maplibre.compose.map.MapOptions
|
||||
import org.maplibre.compose.map.MaplibreMap
|
||||
import org.maplibre.compose.overlay.ExpandingAttributionButton
|
||||
import org.maplibre.compose.overlay.MapOverlay
|
||||
import org.maplibre.compose.overlay.MaplibreLogo
|
||||
import org.maplibre.compose.style.BaseStyle
|
||||
import org.maplibre.compose.util.MaplibreComposable
|
||||
import kotlin.math.roundToInt
|
||||
@@ -115,10 +120,6 @@ fun MapBottomSheetScaffold(
|
||||
PaddingValues(bottom = bottomPadding)
|
||||
}
|
||||
}
|
||||
// Update camera position when sheet padding changes
|
||||
LaunchedEffect(sheetPadding) {
|
||||
cameraState.position = cameraState.position.copy(padding = sheetPadding)
|
||||
}
|
||||
BottomSheetScaffold(
|
||||
modifier = Modifier,
|
||||
sheetPeekHeight = sheetPeekHeight,
|
||||
@@ -135,14 +136,27 @@ fun MapBottomSheetScaffold(
|
||||
snackbarHost = snackbarHost,
|
||||
topBar = topBar,
|
||||
) {
|
||||
val ornamentOptions = mapOptions.ornamentOptions.copy(padding = sheetPadding)
|
||||
val mapOptions = mapOptions.copy(ornamentOptions = ornamentOptions)
|
||||
Box {
|
||||
when (customMapTilerConfig) {
|
||||
is AsyncData.Success -> {
|
||||
MaplibreMap(
|
||||
options = mapOptions,
|
||||
cameraPadding = sheetPadding,
|
||||
contentWindowInsets = WindowInsets(bottom = sheetPadding.calculateBottomPadding()),
|
||||
baseStyle = BaseStyle.Uri(rememberTileStyleUrl(customMapTilerConfig.data)),
|
||||
overlay = MapOverlay {
|
||||
val style = styleState
|
||||
Row(
|
||||
Modifier
|
||||
.align(Alignment.BottomStart)
|
||||
.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
MaplibreLogo()
|
||||
ExpandingAttributionButton(cameraState = cameraState, styleState = style)
|
||||
}
|
||||
},
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
cameraState = cameraState,
|
||||
content = mapContent,
|
||||
|
||||
+24
-12
@@ -7,14 +7,16 @@
|
||||
|
||||
package io.element.android.features.location.impl.common.userlocation
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import dev.zacsweers.metro.ContributesBinding
|
||||
import io.element.android.libraries.di.RoomScope
|
||||
import org.maplibre.compose.location.DesiredAccuracy
|
||||
import org.maplibre.compose.location.rememberNullLocationProvider
|
||||
import kotlinx.coroutines.flow.mapNotNull
|
||||
import org.maplibre.compose.location.LocationAccuracy
|
||||
import org.maplibre.compose.location.LocationEvent
|
||||
import org.maplibre.compose.location.LocationRequest
|
||||
import org.maplibre.spatialk.units.extensions.meters
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
@@ -22,17 +24,27 @@ import kotlin.time.Duration.Companion.seconds
|
||||
class DefaultUserLocationStateFactory : UserLocationState.Factory {
|
||||
@Composable
|
||||
override fun create(hasLocationPermission: Boolean): UserLocationState {
|
||||
val locationProvider = if (!hasLocationPermission) {
|
||||
rememberNullLocationProvider()
|
||||
val locationProvider = if (hasLocationPermission) {
|
||||
rememberPlatformLocationProvider()
|
||||
} else {
|
||||
@SuppressLint("MissingPermission")
|
||||
rememberPlatformLocationProvider(
|
||||
updateInterval = 5.seconds,
|
||||
desiredAccuracy = DesiredAccuracy.High,
|
||||
minDistance = 5.meters,
|
||||
)
|
||||
rememberNotGrantedLocationProvider()
|
||||
}
|
||||
val location by locationProvider.location.collectAsState()
|
||||
val locationFlow = remember(locationProvider) {
|
||||
val locationRequest = LocationRequest(
|
||||
accuracy = LocationAccuracy.High,
|
||||
minimumInterval = 5.seconds,
|
||||
minimumDistance = 5.meters,
|
||||
)
|
||||
locationProvider
|
||||
.updates(locationRequest)
|
||||
.mapNotNull { event ->
|
||||
when (event) {
|
||||
is LocationEvent.Fix -> event.location
|
||||
is LocationEvent.Unavailable -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
val location by locationFlow.collectAsState(null)
|
||||
return UserLocationState(location)
|
||||
}
|
||||
}
|
||||
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Element Creations Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.location.impl.common.userlocation
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import org.maplibre.compose.location.LocationEvent
|
||||
import org.maplibre.compose.location.LocationProvider
|
||||
import org.maplibre.compose.location.LocationRequest
|
||||
import org.maplibre.compose.location.LocationUnavailableReason
|
||||
|
||||
class NotGrantedLocationProvider : LocationProvider {
|
||||
override fun updates(request: LocationRequest): Flow<LocationEvent> {
|
||||
return flowOf(LocationEvent.Unavailable(LocationUnavailableReason.PermissionDenied))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun rememberNotGrantedLocationProvider(): NotGrantedLocationProvider {
|
||||
return remember { NotGrantedLocationProvider() }
|
||||
}
|
||||
+112
-78
@@ -9,83 +9,126 @@ package io.element.android.features.location.impl.common.userlocation
|
||||
|
||||
import android.Manifest
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.content.pm.PackageManager
|
||||
import android.location.Location
|
||||
import android.location.LocationManager
|
||||
import android.os.Handler
|
||||
import android.os.HandlerThread
|
||||
import androidx.annotation.RequiresPermission
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.location.LocationListenerCompat
|
||||
import androidx.core.location.LocationManagerCompat
|
||||
import androidx.core.location.LocationRequestCompat
|
||||
import androidx.core.os.ExecutorCompat
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.callbackFlow
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import org.maplibre.compose.location.DesiredAccuracy
|
||||
import org.maplibre.compose.location.Location
|
||||
import org.maplibre.compose.location.LocationAccuracy
|
||||
import org.maplibre.compose.location.LocationEvent
|
||||
import org.maplibre.compose.location.LocationProvider
|
||||
import org.maplibre.compose.location.PermissionException
|
||||
import org.maplibre.compose.location.LocationRequest
|
||||
import org.maplibre.compose.location.LocationUnavailableReason
|
||||
import org.maplibre.compose.location.asMapLibreLocation
|
||||
import org.maplibre.spatialk.units.Length
|
||||
import org.maplibre.spatialk.units.extensions.inMeters
|
||||
import kotlin.time.Duration
|
||||
|
||||
@SuppressLint("InlinedApi")
|
||||
class PlatformLocationProvider(
|
||||
context: Context,
|
||||
private val updateInterval: Duration,
|
||||
private val minDistance: Length,
|
||||
private val desiredAccuracy: DesiredAccuracy = DesiredAccuracy.High,
|
||||
coroutineScope: CoroutineScope,
|
||||
sharingStarted: SharingStarted = SharingStarted.WhileSubscribed(stopTimeoutMillis = 1000),
|
||||
private val context: Context,
|
||||
) : LocationProvider {
|
||||
override val location: StateFlow<Location?>
|
||||
|
||||
init {
|
||||
if (!handlerThread.isAlive) handlerThread.start()
|
||||
if (
|
||||
context.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED &&
|
||||
context.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
throw PermissionException()
|
||||
@SuppressLint("MissingPermission")
|
||||
override fun updates(request: LocationRequest): Flow<LocationEvent> = callbackFlow {
|
||||
if (!context.hasLocationPermission()) {
|
||||
trySend(LocationEvent.Unavailable(LocationUnavailableReason.PermissionDenied))
|
||||
close()
|
||||
return@callbackFlow
|
||||
}
|
||||
val locationManager = context.getSystemService(LocationManager::class.java)
|
||||
val provider = PROVIDERS_BY_PRIORITY.firstOrNull { LocationManagerCompat.hasProvider(locationManager, it) }
|
||||
val locationFlow = if (provider != null) {
|
||||
createProviderFlow(locationManager, provider)
|
||||
} else {
|
||||
emptyFlow()
|
||||
}
|
||||
location = locationFlow.stateIn(coroutineScope, sharingStarted, null)
|
||||
}
|
||||
val listener = object : LocationListenerCompat {
|
||||
override fun onLocationChanged(location: Location) {
|
||||
trySend(LocationEvent.Fix(location.asMapLibreLocation()))
|
||||
}
|
||||
|
||||
@RequiresPermission(
|
||||
anyOf = [Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION]
|
||||
)
|
||||
private fun createProviderFlow(locationManager: LocationManager, provider: String) = callbackFlow {
|
||||
send(locationManager.getLastKnownLocation(provider)?.asMapLibreLocation())
|
||||
val listener = LocationListenerCompat { trySend(it.asMapLibreLocation()) }
|
||||
val request = LocationRequestCompat.Builder(updateInterval.inWholeMilliseconds)
|
||||
.setQuality(desiredAccuracy.toLocationRequestQuality())
|
||||
.setMinUpdateDistanceMeters(minDistance.inMeters.toFloat())
|
||||
.build()
|
||||
LocationManagerCompat.requestLocationUpdates(
|
||||
locationManager,
|
||||
provider,
|
||||
request,
|
||||
ExecutorCompat.create(Handler(handlerThread.looper)),
|
||||
listener,
|
||||
)
|
||||
awaitClose { LocationManagerCompat.removeUpdates(locationManager, listener) }
|
||||
override fun onProviderDisabled(provider: String) {
|
||||
trySend(LocationEvent.Unavailable(LocationUnavailableReason.ServicesDisabled))
|
||||
}
|
||||
}
|
||||
var registered = false
|
||||
|
||||
fun refreshRegistration() {
|
||||
if (registered) {
|
||||
LocationManagerCompat.removeUpdates(locationManager, listener)
|
||||
registered = false
|
||||
}
|
||||
if (!LocationManagerCompat.isLocationEnabled(locationManager)) {
|
||||
trySend(LocationEvent.Unavailable(LocationUnavailableReason.ServicesDisabled))
|
||||
return
|
||||
}
|
||||
val provider = PROVIDERS_BY_PRIORITY.firstOrNull { LocationManagerCompat.hasProvider(locationManager, it) }
|
||||
if (provider == null) {
|
||||
trySend(LocationEvent.Unavailable(LocationUnavailableReason.Unsupported))
|
||||
return
|
||||
}
|
||||
locationManager.getLastKnownLocation(provider)?.let { location ->
|
||||
trySend(LocationEvent.Fix(location.asMapLibreLocation()))
|
||||
}
|
||||
val locationRequestCompat = LocationRequestCompat.Builder(request.minimumInterval.inWholeMilliseconds)
|
||||
.setQuality(request.accuracy.toLocationRequestQuality())
|
||||
.setMinUpdateDistanceMeters(request.minimumDistance.inMeters.toFloat())
|
||||
.build()
|
||||
LocationManagerCompat.requestLocationUpdates(
|
||||
locationManager,
|
||||
provider,
|
||||
locationRequestCompat,
|
||||
ExecutorCompat.create(Handler(handlerThread.looper)),
|
||||
listener,
|
||||
)
|
||||
registered = true
|
||||
}
|
||||
|
||||
// Re-select and re-register when the user toggles location settings, so updates resume
|
||||
// automatically once a usable provider comes back.
|
||||
val settingsReceiver = object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
try {
|
||||
refreshRegistration()
|
||||
} catch (error: SecurityException) {
|
||||
trySend(LocationEvent.Unavailable(LocationUnavailableReason.PermissionDenied, error))
|
||||
close()
|
||||
} catch (error: IllegalArgumentException) {
|
||||
trySend(LocationEvent.Unavailable(LocationUnavailableReason.UnexpectedFailure, error))
|
||||
close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
ContextCompat.registerReceiver(
|
||||
context,
|
||||
settingsReceiver,
|
||||
IntentFilter().apply {
|
||||
addAction(LocationManager.MODE_CHANGED_ACTION)
|
||||
addAction(LocationManager.PROVIDERS_CHANGED_ACTION)
|
||||
},
|
||||
ContextCompat.RECEIVER_NOT_EXPORTED,
|
||||
)
|
||||
refreshRegistration()
|
||||
} catch (error: SecurityException) {
|
||||
trySend(LocationEvent.Unavailable(LocationUnavailableReason.PermissionDenied, error))
|
||||
close()
|
||||
} catch (error: IllegalArgumentException) {
|
||||
trySend(LocationEvent.Unavailable(LocationUnavailableReason.UnexpectedFailure, error))
|
||||
close()
|
||||
}
|
||||
awaitClose {
|
||||
runCatching { context.unregisterReceiver(settingsReceiver) }
|
||||
LocationManagerCompat.removeUpdates(locationManager, listener)
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
@@ -94,36 +137,27 @@ class PlatformLocationProvider(
|
||||
LocationManager.GPS_PROVIDER,
|
||||
LocationManager.NETWORK_PROVIDER,
|
||||
)
|
||||
private val handlerThread by lazy { HandlerThread("PlatformLocationProvider") }
|
||||
private val handlerThread by lazy {
|
||||
HandlerThread("PlatformLocationProvider").apply { start() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun DesiredAccuracy.toLocationRequestQuality(): Int = when (this) {
|
||||
DesiredAccuracy.Highest, DesiredAccuracy.High -> LocationRequestCompat.QUALITY_HIGH_ACCURACY
|
||||
DesiredAccuracy.Balanced -> LocationRequestCompat.QUALITY_BALANCED_POWER_ACCURACY
|
||||
DesiredAccuracy.Low, DesiredAccuracy.Lowest -> LocationRequestCompat.QUALITY_LOW_POWER
|
||||
private fun Context.hasLocationPermission(): Boolean =
|
||||
checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) ==
|
||||
PackageManager.PERMISSION_GRANTED ||
|
||||
checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) ==
|
||||
PackageManager.PERMISSION_GRANTED
|
||||
|
||||
private fun LocationAccuracy.toLocationRequestQuality(): Int = when (this) {
|
||||
LocationAccuracy.BestForNavigation, LocationAccuracy.High -> LocationRequestCompat.QUALITY_HIGH_ACCURACY
|
||||
LocationAccuracy.Balanced -> LocationRequestCompat.QUALITY_BALANCED_POWER_ACCURACY
|
||||
LocationAccuracy.Low, LocationAccuracy.Lowest -> LocationRequestCompat.QUALITY_LOW_POWER
|
||||
}
|
||||
|
||||
@Composable
|
||||
@RequiresPermission(
|
||||
anyOf = [Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION]
|
||||
)
|
||||
fun rememberPlatformLocationProvider(
|
||||
updateInterval: Duration,
|
||||
minDistance: Length,
|
||||
desiredAccuracy: DesiredAccuracy = DesiredAccuracy.High,
|
||||
context: Context = LocalContext.current,
|
||||
coroutineScope: CoroutineScope = rememberCoroutineScope(),
|
||||
sharingStarted: SharingStarted = SharingStarted.WhileSubscribed(stopTimeoutMillis = 1000),
|
||||
): PlatformLocationProvider {
|
||||
return remember(context, updateInterval, minDistance, desiredAccuracy, coroutineScope, sharingStarted) {
|
||||
PlatformLocationProvider(
|
||||
context = context,
|
||||
updateInterval = updateInterval,
|
||||
minDistance = minDistance,
|
||||
desiredAccuracy = desiredAccuracy,
|
||||
coroutineScope = coroutineScope,
|
||||
sharingStarted = sharingStarted,
|
||||
)
|
||||
fun rememberPlatformLocationProvider(context: Context = LocalContext.current): PlatformLocationProvider {
|
||||
return remember(context) {
|
||||
PlatformLocationProvider(context = context)
|
||||
}
|
||||
}
|
||||
|
||||
+32
-26
@@ -30,15 +30,14 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import org.maplibre.compose.location.DesiredAccuracy
|
||||
import org.maplibre.compose.location.PermissionException
|
||||
import org.maplibre.compose.location.LocationAccuracy
|
||||
import org.maplibre.compose.location.LocationEvent
|
||||
import org.maplibre.compose.location.LocationRequest
|
||||
import org.maplibre.compose.location.LocationUnavailableReason
|
||||
import org.maplibre.spatialk.units.extensions.inMeters
|
||||
import org.maplibre.spatialk.units.extensions.meters
|
||||
import timber.log.Timber
|
||||
@@ -91,31 +90,38 @@ class LiveLocationSharingService : Service() {
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
private fun startLocationUpdatesListener() {
|
||||
Timber.d("LiveLocationSharingService listening to location updates")
|
||||
val locationProvider = PlatformLocationProvider(context = applicationContext)
|
||||
appPreferencesStore.getLiveLocationMinimumDistanceInMetersUpdateFlow()
|
||||
.flatMapLatest { minDistanceMeters ->
|
||||
try {
|
||||
PlatformLocationProvider(
|
||||
context = applicationContext,
|
||||
updateInterval = UPDATE_INTERVAL_IN_SECOND.seconds,
|
||||
minDistance = minDistanceMeters.meters,
|
||||
desiredAccuracy = DesiredAccuracy.Balanced,
|
||||
coroutineScope = coroutineScope
|
||||
).location
|
||||
} catch (exception: PermissionException) {
|
||||
Timber.e(exception, "Failed to create PlatformLocationProvider")
|
||||
coordinator.dispatchUnrecoverableError()
|
||||
emptyFlow()
|
||||
val locationRequest = LocationRequest(
|
||||
accuracy = LocationAccuracy.Balanced,
|
||||
minimumInterval = UPDATE_INTERVAL_IN_SECOND.seconds,
|
||||
minimumDistance = minDistanceMeters.meters
|
||||
)
|
||||
locationProvider.updates(locationRequest)
|
||||
}
|
||||
.onEach { locationEvent ->
|
||||
when (locationEvent) {
|
||||
is LocationEvent.Fix -> {
|
||||
val location = ApiLocation(
|
||||
lat = locationEvent.location.position.value.latitude,
|
||||
lon = locationEvent.location.position.value.longitude,
|
||||
accuracy = locationEvent.location.position.accuracy?.inMeters?.toFloat(),
|
||||
)
|
||||
coordinator.dispatch(location)
|
||||
}
|
||||
is LocationEvent.Unavailable -> {
|
||||
when (locationEvent.reason) {
|
||||
LocationUnavailableReason.ServicesDisabled,
|
||||
LocationUnavailableReason.TemporarilyUnavailable -> Unit
|
||||
LocationUnavailableReason.Unsupported,
|
||||
LocationUnavailableReason.Misconfigured,
|
||||
LocationUnavailableReason.PermissionDenied,
|
||||
LocationUnavailableReason.UnexpectedFailure -> coordinator.dispatchUnrecoverableError()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.filterNotNull()
|
||||
.map { location ->
|
||||
ApiLocation(
|
||||
lat = location.position.value.latitude,
|
||||
lon = location.position.value.longitude,
|
||||
accuracy = location.position.accuracy?.inMeters?.toFloat(),
|
||||
)
|
||||
}
|
||||
.onEach(coordinator::dispatch)
|
||||
.launchIn(coroutineScope)
|
||||
}
|
||||
|
||||
|
||||
-1
@@ -154,7 +154,6 @@ fun ShowLocationView(
|
||||
modifier = Modifier.clickable {
|
||||
state.eventSink(ShowLocationEvent.TrackMyLocation(false))
|
||||
val position = CameraPosition(
|
||||
padding = sheetPaddings,
|
||||
target = Position(locationShare.location.lon, locationShare.location.lat),
|
||||
// Force pointing to NORTH
|
||||
bearing = 0.0,
|
||||
|
||||
Reference in New Issue
Block a user