GPS: lyssna på alla platsproviders (gps/fused/network) + kartmarkör
All checks were successful
release / build-release (push) Successful in 4m13s

Fälttest visade noll callbacks från råa GPS_PROVIDER på wheatek WP19 —
telefonen levererar via fused/network. Nu:
- Lyssning på gps + fused + network (FUSED_PROVIDER finns fr.o.m. API 31);
  loggen visar vilka providers som finns/är på och varje fix per provider
- Kartan centreras direkt på senast kända position (lastKnownLocation) och
  visar en positionsmarkör — ingen blå 0°,0°-ocean längre
- Spårpunkter filtreras på noggrannhet (≤35 m) istället för provider;
  positionen på kartan uppdateras oavsett kvalitet
Version 0.8.1

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C2GA94E1f3cmrvdQLQhYdg
This commit is contained in:
2026-08-25 09:25:41 +02:00
parent 2f6c55e967
commit dcb91f587d
3 changed files with 71 additions and 20 deletions

View File

@@ -29,7 +29,7 @@ android {
minSdk = 31
targetSdk = 35
versionCode = ciRunNumber ?: 1
versionName = "0.8.0" + (ciRunNumber?.let { "+build.$it" } ?: "-dev")
versionName = "0.8.1" + (ciRunNumber?.let { "+build.$it" } ?: "-dev")
}
signingConfigs {

View File

@@ -48,6 +48,8 @@ data class TrackingState(
val elevationGainMeters: Double = 0.0,
/** Senaste GPS-position (lat, lon) + hela spåret */
val points: List<Pair<Double, Double>> = emptyList(),
/** Senast kända position oavsett kvalitet — för kartcentrering/markör */
val currentPosition: Pair<Double, Double>? = null,
val currentSpeedKmh: Double? = null,
val kcal: Double? = null,
val gpsFix: Boolean = false,
@@ -164,15 +166,34 @@ class TrackingService : Service(), LocationListener {
TrackLog.log("FEL: platsbehörighet saknas — ingen GPS-lyssning")
return
}
locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager
TrackLog.log("GPS-lyssning begärd (GPS_PROVIDER, 2 s / 3 m)")
runCatching {
locationManager?.requestLocationUpdates(
LocationManager.GPS_PROVIDER,
2000L, // ms
3f, // meter
this,
Looper.getMainLooper(),
val lm = getSystemService(Context.LOCATION_SERVICE) as LocationManager
locationManager = lm
// Billiga telefoner levererar ofta bara via fused/network — lyssna på
// allt som finns och filtrera på noggrannhet istället för provider.
val providers = listOf(
LocationManager.GPS_PROVIDER,
LocationManager.FUSED_PROVIDER,
LocationManager.NETWORK_PROVIDER,
)
for (provider in providers) {
val exists = runCatching { lm.allProviders.contains(provider) }.getOrDefault(false)
val enabled = exists && runCatching { lm.isProviderEnabled(provider) }.getOrDefault(false)
TrackLog.log("provider $provider: ${if (!exists) "saknas" else if (enabled) "på" else "AVSTÄNGD"}")
if (!enabled) continue
runCatching {
lm.requestLocationUpdates(provider, 2000L, 0f, this, Looper.getMainLooper())
TrackLog.log("lyssnar på $provider (2 s)")
}.onFailure { TrackLog.log("FEL: kunde inte lyssna på $provider: ${it.message}") }
}
// Senast kända position → omedelbar kartcentrering (läggs inte i spåret)
providers.firstNotNullOfOrNull { p ->
runCatching { lm.getLastKnownLocation(p) }.getOrNull()
}?.let { last ->
TrackLog.log("lastKnown: ${last.provider} acc=${if (last.hasAccuracy()) "%.0f".format(last.accuracy) else "?"} m")
_state.value = _state.value.copy(
currentPosition = last.latitude to last.longitude,
)
}
}
@@ -192,16 +213,21 @@ class TrackingService : Service(), LocationListener {
val s = _state.value
if (!s.isActive || !s.isDistanceBased) return
val acc = if (location.hasAccuracy()) location.accuracy else -1f
// Släng riktigt dåliga fixar
if (location.hasAccuracy() && location.accuracy > 30f) {
TrackLog.log("fix förkastad: noggrannhet ${"%.0f".format(acc)} m (>30)")
// Position för kartan uppdateras alltid, oavsett kvalitet
_state.value = _state.value.copy(currentPosition = location.latitude to location.longitude)
// Spårpunkter kräver hygglig noggrannhet
if (location.hasAccuracy() && location.accuracy > 35f) {
TrackLog.log("${location.provider}: förkastad för spåret, acc=${"%.0f".format(acc)} m (>35)")
return
}
TrackLog.log(
"fix: acc=${"%.0f".format(acc)} m alt=${if (location.hasAltitude()) "%.0f".format(location.altitude) else "?"}" +
"${location.provider}: fix acc=${"%.0f".format(acc)} m" +
" alt=${if (location.hasAltitude()) "%.0f".format(location.altitude) else "?"}" +
" fart=${if (location.hasSpeed()) "%.1f".format(location.speed * 3.6) else "?"} km/h"
)
if (s.phase == TrackingPhase.SEARCHING_GPS) activate("GPS-fix")
if (s.phase == TrackingPhase.SEARCHING_GPS) activate("fix från ${location.provider}")
var distance = s.distanceMeters
var elevGain = s.elevationGainMeters

View File

@@ -339,6 +339,7 @@ private fun LiveContent(
if (tracking.isDistanceBased) {
TrackMap(
points = tracking.points,
currentPosition = tracking.currentPosition,
modifier = Modifier
.fillMaxWidth()
.weight(1f),
@@ -484,7 +485,11 @@ private fun StatBox(label: String, value: String, modifier: Modifier = Modifier)
}
@Composable
private fun TrackMap(points: List<Pair<Double, Double>>, modifier: Modifier = Modifier) {
private fun TrackMap(
points: List<Pair<Double, Double>>,
currentPosition: Pair<Double, Double>?,
modifier: Modifier = Modifier,
) {
val context = LocalContext.current
val lineColor = MaterialTheme.colorScheme.primary.toArgb()
@@ -506,6 +511,14 @@ private fun TrackMap(points: List<Pair<Double, Double>>, modifier: Modifier = Mo
outlinePaint.strokeWidth = 10f
}.also { mapView.overlays.add(it) }
}
val positionMarker = remember {
org.osmdroid.views.overlay.Marker(mapView).apply {
setAnchor(org.osmdroid.views.overlay.Marker.ANCHOR_CENTER, org.osmdroid.views.overlay.Marker.ANCHOR_CENTER)
setInfoWindow(null)
}.also { mapView.overlays.add(it) }
}
// Centrera bara automatiskt tills användaren själv panorerat
var hasCentered by remember { mutableStateOf(false) }
DisposableEffect(Unit) {
mapView.onResume()
@@ -518,13 +531,25 @@ private fun TrackMap(points: List<Pair<Double, Double>>, modifier: Modifier = Mo
AndroidView(
factory = { mapView },
modifier = modifier,
update = {
update = { map ->
val pos = points.lastOrNull() ?: currentPosition
if (pos != null) {
positionMarker.position = GeoPoint(pos.first, pos.second)
positionMarker.isEnabled = true
if (!hasCentered) {
map.controller.setZoom(17.0)
map.controller.setCenter(GeoPoint(pos.first, pos.second))
hasCentered = true
} else {
map.controller.animateTo(GeoPoint(pos.first, pos.second))
}
} else {
positionMarker.isEnabled = false
}
if (points.isNotEmpty()) {
polyline.setPoints(points.map { (lat, lon) -> GeoPoint(lat, lon) })
val last = points.last()
it.controller.animateTo(GeoPoint(last.first, last.second))
it.invalidate()
}
map.invalidate()
},
)
}