# GPS2Audio v2.7.29 — Overlay Rehydrate Fix **Version:** 2.7.29 **versionCode:** 135 **Basis:** v2.7.28 Map Style Apply Fix **Datei:** `GPS2Audio_v2_7_29_overlay_rehydrate_fix_universal_signed.apk` --- ## Root Cause: Warum Marker nach Style-Wechsel hell/falsch waren Nach einem `setStyle()`-Aufruf in MapLibre Native löscht die Bibliothek intern alle registrierten Annotations — Marker, Polylines und Polygone werden aus dem Rendering-Graph entfernt. Das ist MapLibre-Standardverhalten. ### Waypoint-Marker (behoben seit v2.7.12/v2.7.13) Waypoint-Marker werden über `drawExplorerOverlaysMapLibre()` aus dem Haupt-LaunchedEffect gezeichnet. Dieser Effekt hat `overlayRenderNonce` als Key — nach `setStyle()` wird `overlayRenderNonce++` ausgelöst, was den Effekt sofort neu feuert. Die Waypoint-Marker wurden also bereits korrekt gezeichnet. Dennoch war das Erlebnis gestört, weil der **Live-Standort-Marker** noch fehlte (siehe unten). ### Live-Standort-Marker (Hauptbug in v2.7.29) Der Live-Standort-Puck (`myLocationMarker`) wird im `DisposableEffect(hasPermission)` über einen `LocationCallback` gesetzt — d.h. **nur wenn ein neues GPS-Fix eintrifft**. Nach `setStyle()`: 1. `clearAllAnnotationsCaches()` setzt `myLocationMarker = null` und `accuracyPolygon = null` 2. `overlayRenderNonce++` triggert Waypoint- und Routen-Redraw — aber **nicht** den LocationCallback 3. Der LocationCallback läuft erst beim nächsten GPS-Update (typisch: 1–2 Sekunden, im schlimmsten Fall länger) 4. **In diesem Zeitfenster** fehlte der Puck, oder der erste Re-Draw verwendete `IconFactory.defaultMarker()` (heller roter Standardmarker) statt des Corporate-Pucks 5. Erst der nächste GPS-Fix zeichnete den Puck korrekt — und genau das beschrieb der Nutzer: „Erst nach einer weiteren Interaktion kommt die korrekte Darstellung zurück" ### POI-Marker (sekundär) Die Explorer-POI-Marker (`explorerPoiMarkers`) hatten `styleReady` als LaunchedEffect-Key — technisch würden sie nach `styleReady` false→true neu gezeichnet. Dennoch fehlte `overlayRenderNonce` als expliziter Key für den Fall, dass sich `explorerPoiResults` zwischen dem Style-Wechsel nicht änderte. --- ## Was jetzt nach Style-Load neu registriert wird ### 1. Waypoint-Marker, Radien, Tracks, Clip-Linien, Planlinien, Routen Über den bestehenden `LaunchedEffect(waypointRenderKey, ..., overlayRenderNonce, ...)`: - Alle Waypoint-Marker mit korrekter Corporate-Farbe (`primaryArgb`) - Trigger-Radien als Polygone - GPS-Track-Polylines und -Marker - Audio-Clip-Linien - Planlinien und OSRM-Routen (separater LaunchedEffect) - Manuelle Tourverlauf-Linie (separater LaunchedEffect) - Such-Pin-Marker ### 2. Live-Standort-Marker und Genauigkeitskreis (NEU v2.7.29) Über den neuen `LaunchedEffect(overlayRenderNonce, styleReady, mapLibreMap)`: - Zeichnet `myLocationMarker` sofort aus `lastLat`/`lastLng` (letzte bekannte Koordinaten) - Zeichnet `accuracyPolygon` aus `lastAccuracyM` (letzter bekannter Genauigkeitsradius) - Verwendet `puckBitmap` (Corporate-Design-Puck, niemals `defaultMarker()`) - Läuft mit `kotlinx.coroutines.yield()` nach `styleReady`-Guard — gibt MapLibre-AnnotationManager Zeit - Feuert **ohne GPS-Fix**, sofort nach Style-Load - Guard: `lastLat == null` → kein Redraw (kein Fix je erhalten → kein alter Marker vorhanden) ### 3. Explorer-POI-Marker (NEU v2.7.29) `overlayRenderNonce` als zusätzlicher Key im `LaunchedEffect(explorerPoiResults, styleReady, showPoiNames, overlayRenderNonce)`: - Stellt sicher, dass POI-Marker nach Style-Wechsel neu gezeichnet werden - Auch wenn `explorerPoiResults` sich nicht geändert hat --- ## Wie doppelte Layers vermieden werden Die gesamte Overlay-Architektur basiert auf **Remove-before-Redraw** (idempotente Listen-Caches): - `waypointMarkers`, `waypointRadiusPolygons`, `trackMarkers`, `trackPolylines`, `routePolylines`, `planlinePolylines`, `audioLinePolylines`, `manualRoutePolylines`, `manualRouteMarkers` — jeweils `removeMarker()/removePolyline()/removePolygon()` am Anfang jedes LaunchedEffect-Aufrufs, dann `clear()`, dann Neudraw. - `myLocationMarker` / `accuracyPolygon` — im neuen Rehydrate-Effekt: `myLocationMarker?.let { removeMarker(it) }` vor `addMarker()`. Der LocationCallback tut dasselbe. - Kein `addImage`/`addSource`/`addLayer`-Overhead: GPS2Audio nutzt die klassische MapLibre Annotations API (Marker/Polyline/Polygon), keine Symbol-Layer oder GeoJSON-Sources. Nach `setStyle()` sind diese Annotations automatisch weg und werden sauber neu erstellt. - `appliedBaseStyle`/`appliedPerspectiveTilt` (v2.7.28): Verhindert Doppel-Apply desselben Stils durch den Style-Change-LaunchedEffect und den Factory-Callback. --- ## Beibehaltene Fixes (Regressions-Schutz) | Version | Fix | Status | |---------|-----|--------| | v2.7.28 | Map Style Apply Fix (`appliedBaseStyle`/`appliedPerspectiveTilt`) | Vollständig erhalten | | v2.7.27 | Kartenpräferenz-Persistenz (DataStore null-initial) | Vollständig erhalten | | v2.7.26 | Standort-Folgen-Fix | Vollständig erhalten | | v2.7.25 | Initial-Render-Fix (savedRouteLine-Guard) | Vollständig erhalten | | v2.7.24 | UX Polish | Vollständig erhalten | | v2.7.23 | Tourverlauf übernehmen | Vollständig erhalten | | v2.7.21 | Waypoint Preservation Guard | Vollständig erhalten | | v2.7.12 | overlayRenderNonce (Waypoint nach Style-Load) | Vollständig erhalten | | v2.7.13 | routeRenderKey | Vollständig erhalten | | v2.7.11 | waypointRenderKey | Vollständig erhalten | --- ## Testliste ### Grundlegender Rehydrate-Test 1. Explorer öffnen, GPS-Fix abwarten (Puck erscheint) 2. Kartenstil wechseln: Outdoor → Dunkel → 3D → Modern 3. **Erwartung nach jedem Wechsel:** Waypoint-Marker sofort in Corporate-Primärfarbe, Puck sofort sichtbar — **ohne** Tap, Clip-Start oder andere Interaktion 4. Kartenstil zurückwechseln: Klassisch → Modern 5. **Erwartung:** Keine hellen/weißen Default-Punkte ### DataStore-Restore-Test 1. Kartenstil auf "Dunkel" setzen, Explorer schließen 2. Explorer erneut öffnen (DataStore-Restore läuft, styleHydrated-Logik) 3. **Erwartung:** Puck und Waypoints sofort korrekt gefärbt, keine Interaktion nötig ### Tourverlauf/Route-Test 1. Tour mit mehreren Waypoints und gespeicherter Route öffnen 2. Kartenstil wechseln 3. **Erwartung:** Tourlinie (grün/orange) und Waypoints bleiben sichtbar und korrekt ### POI-Test 1. POIs laden, Kartenstil wechseln 2. **Erwartung:** POI-Marker nach Style-Load sofort wieder sichtbar ### Regressionstest - Clip abspielen: kein Crash, kein Einfluss auf Overlay-Darstellung - GPS-Aufzeichnung starten/stoppen: Track-Linie korrekt gezeichnet - Tourverlauf-Editor öffnen/schließen: Manuelle Linie bleibt stabil - Backup/Restore: Waypoints korrekt wiederhergestellt --- ## Geänderte Dateien ### `app/src/main/kotlin/de/waypointaudio/ui/ExplorerScreenMapLibre.kt` **Änderung 1 (ca. Zeile 875):** `overlayRenderNonce` als zusätzlicher Key im POI-LaunchedEffect ```kotlin // VORHER: LaunchedEffect(explorerPoiResults, styleReady, showPoiNames) { // NACHHER: LaunchedEffect(explorerPoiResults, styleReady, showPoiNames, overlayRenderNonce) { ``` **Änderung 2 (nach Waypoint-LaunchedEffect, vor DisposableEffect):** Neuer Rehydrate-LaunchedEffect für Live-Standort ```kotlin // v2.7.29 — Overlay-Rehydrate-Fix: Live-Standort-Marker nach Style-Load neu setzen. LaunchedEffect(overlayRenderNonce, styleReady, mapLibreMap) { if (!styleReady) return@LaunchedEffect val ml = mapLibreMap ?: return@LaunchedEffect val lat = lastLat ?: return@LaunchedEffect val lng = lastLng ?: return@LaunchedEffect kotlinx.coroutines.yield() val gp = LatLng(lat, lng) myLocationMarker?.let { runCatching { ml.removeMarker(it) } } myLocationMarker = ml.addMarker( MarkerOptions() .position(gp) .title("Mein Standort") .icon( puckBitmap?.let { IconFactory.getInstance(context).fromBitmap(it) } ?: IconFactory.getInstance(context).defaultMarker() ) ) accuracyPolygon?.let { runCatching { ml.removePolygon(it) } } val radius = lastAccuracyM?.toDouble() ?: 0.0 accuracyPolygon = if (radius > 0.0) { ml.addPolygon( PolygonOptions() .addAll(circleLatLng(lat, lng, radius, 60)) .fillColor((primaryArgb and 0x00FFFFFF) or 0x33000000) .strokeColor(primaryArgb) .alpha(0.4f) ) } else null } ``` ### `app/build.gradle.kts` - `versionCode`: 134 → 135 - `versionName`: "2.7.28" → "2.7.29" - Versionskommentar hinzugefügt --- ## Ausgabedateien | Datei | Beschreibung | |-------|-------------| | `GPS2Audio_v2_7_29_overlay_rehydrate_fix_universal_signed.apk` | Signierte Release-APK (ca. 57 MB) | | `GPS2Audio_v2_7_29_overlay_rehydrate_fix_source.zip` | Vollständiger Quellcode | | `GPS2Audio_v2_7_29_overlay_rehydrate_fix_handoff.md` | Diese Datei | **Signatur:** CN=GPS2Audio, OU=NesoHub, O=GPS2Audio, L=Germany, C=DE **Keystore:** GPS2Audio_release_keystore_v2.jks / Alias: gps2audio_release_v2