Use simple custom LocationTrackingEffect waiting for a fix from maplibre

This commit is contained in:
ganfra
2026-05-28 18:19:48 +02:00
parent e115d620ca
commit 510588b93e
3 changed files with 69 additions and 24 deletions
@@ -0,0 +1,52 @@
/*
* 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.ui
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.snapshotFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import org.maplibre.compose.location.Location
import org.maplibre.compose.location.UserLocationState
import kotlin.math.abs
/**
* Drop-in replacement for the library's LocationTrackingEffect.
* TODO remove once https://github.com/maplibre/maplibre-compose/issues/808 is fixed
*/
@Composable
internal fun SimpleLocationTrackingEffect(
locationState: UserLocationState,
enabled: Boolean = true,
precision: Double = 0.00001,
onLocationChange: suspend (Location?) -> Unit,
) {
val latestOnLocationChange by rememberUpdatedState(onLocationChange)
LaunchedEffect(locationState, enabled) {
if (!enabled) return@LaunchedEffect
val locationStateFlow = snapshotFlow { locationState.location }
locationStateFlow
.distinctUntilChanged { oldLocation, newLocation ->
if (oldLocation != null && newLocation != null) {
when {
abs(oldLocation.position.value.latitude - newLocation.position.value.latitude) >= precision -> false
abs(oldLocation.position.value.longitude - newLocation.position.value.longitude) >= precision -> false
else -> true
}
} else {
false
}
}
.collect { location ->
latestOnLocationChange(location)
}
}
}
@@ -13,19 +13,15 @@ import androidx.compose.ui.platform.LocalInspectionMode
import androidx.compose.ui.unit.dp
import io.element.android.compound.theme.ElementTheme
import io.element.android.features.location.impl.common.MapDefaults
import kotlinx.coroutines.flow.SharingStarted
import org.maplibre.compose.camera.CameraState
import org.maplibre.compose.location.DesiredAccuracy
import org.maplibre.compose.location.LocationPuck
import org.maplibre.compose.location.LocationPuckColors
import org.maplibre.compose.location.LocationPuckSizes
import org.maplibre.compose.location.LocationTrackingEffect
import org.maplibre.compose.location.UserLocationState
import org.maplibre.compose.location.rememberAndroidLocationProvider
import org.maplibre.compose.location.rememberNullLocationProvider
import org.maplibre.compose.location.rememberUserLocationState
import org.maplibre.spatialk.units.Bearing
import org.maplibre.spatialk.units.extensions.inDegrees
import org.maplibre.spatialk.units.extensions.meters
import kotlin.time.Duration.Companion.seconds
@@ -35,28 +31,24 @@ fun UserLocationPuck(
locationState: UserLocationState,
trackUserLocation: Boolean,
) {
SimpleLocationTrackingEffect(
locationState = locationState,
enabled = trackUserLocation,
) { currentLocation ->
val target = currentLocation?.position?.value ?: cameraState.position.target
val newPosition = cameraState.position.copy(
target = target,
// Force pointing to NORTH
bearing = 0.0,
zoom = cameraState.position.zoom.coerceAtLeast(MapDefaults.DEFAULT_ZOOM)
)
cameraState.animateTo(newPosition)
}
val location = locationState.location
if (location != null) {
// Moved inside this block so it correctly tracks the updated locationState value
LocationTrackingEffect(
locationState = locationState,
enabled = trackUserLocation,
) {
val newTarget = currentLocation.location?.position?.value
val newBearing = currentLocation.orientation?.orientation?.value?.clockwiseRotationTo(Bearing.North)?.inDegrees
if (newTarget != null || newBearing != null) {
val finalPosition = cameraState.position.copy(
target = newTarget ?: cameraState.position.target,
bearing = newBearing ?: cameraState.position.bearing,
zoom = cameraState.position.zoom.coerceAtLeast(MapDefaults.DEFAULT_ZOOM)
)
cameraState.animateTo(finalPosition)
}
}
LocationPuck(
idPrefix = "user-location",
location = locationState.location,
location = location,
cameraState = cameraState,
accuracyThreshold = Float.POSITIVE_INFINITY,
showBearingAccuracy = false,
@@ -85,7 +77,6 @@ fun rememberUserLocationState(hasLocationPermission: Boolean): UserLocationState
updateInterval = 5.seconds,
desiredAccuracy = DesiredAccuracy.High,
minDistance = 5.meters,
sharingStarted = SharingStarted.Eagerly,
)
}
return rememberUserLocationState(locationProvider)
@@ -153,7 +153,9 @@ fun ShowLocationView(
val position = CameraPosition(
padding = sheetPaddings,
target = Position(locationShare.location.lon, locationShare.location.lat),
zoom = MapDefaults.DEFAULT_ZOOM
// Force pointing to NORTH
bearing = 0.0,
zoom = cameraState.position.zoom.coerceAtLeast(MapDefaults.DEFAULT_ZOOM),
)
coroutineScope.launch {
cameraState.animateTo(finalPosition = position)