# GPS2Audio v2.5.38 — Handoff: Map Attribution, POI-Layer, Persistenz **Datum:** 2026-05-29 **Version:** 2.5.38 / versionCode 92 **applicationId:** de.waypointaudio **Basis:** v2.5.37 (GPS2Audio_v2_5_37_explorer_map_styles_source.zip) --- ## Artefakte | Datei | Beschreibung | |---|---| | `GPS2Audio_v2_5_38_map_attribution_poi_persistence_universal_signed.apk` | Universal-APK (57 MB), alle 4 ABIs, Release-Signatur | | `GPS2Audio_v2_5_38_map_attribution_poi_persistence_source.zip` | Quellpaket (ohne Keystore, ohne Build-Artefakte) | | `GPS2Audio_v2_5_38_map_attribution_poi_persistence_handoff.md` | dieses Dokument | --- ## 1. Kartenattribution (rechtlich sauber) ### Umsetzung **Kompaktes Badge auf der Karte (bottom-left):** - Neues Composable `MapCompactAttributionBadge` in `ExplorerScreenMapLibre.kt` (ersetzt altes `OsmAttributionBadge`) - Text: `© OpenStreetMap · ODbL` - Klickbar → öffnet `https://www.openstreetmap.org/copyright` - Semi-transparent (alpha 0.80), harmoniert mit Allen Kartenstilen - Position: bottom-left, padding 6dp — wird nicht von Buttons überdeckt (Layer-FAB top-right, MapLibre-Badge bottom-right) **Vollständiger Info-Dialog aus dem Layer-Sheet:** - Neues Composable `MapAttributionDialog` in `MapLayerPanel.kt` - Erreichbar: Layer-Sheet öffnen → unterster Abschnitt "Karteninfo" → Button "Karteninfo" - Zeigt: - **Kartenstil:** OpenFreeMap / OpenMapTiles - **Kartendaten:** © OpenStreetMap-Mitwirkende, ODbL - **Quellen (anklickbar):** - ↗ openfreemap.org → `https://openfreemap.org` - ↗ openmaptiles.org → `https://openmaptiles.org` - ↗ openstreetmap.org/copyright → `https://www.openstreetmap.org/copyright` - Hinweis: ODbL, Namensnennung erforderlich **Rechtlicher Status:** - OSM ODbL verlangt: sichtbare Namensnennung + ODbL-Hinweis + Link. Beides erfüllt. - OpenFreeMap: Attribution "OpenFreeMap © OpenMapTiles Data from OpenStreetMap" — vollständig im Info-Dialog. OFM sagt, OFM-Namensnennung ist "nice to have", OSM/ODbL ist Pflicht. Beide sind jetzt drin. - OpenMapTiles: Attribution im Dialog erwähnt. - MapLibre eigene Attribution: `isAttributionEnabled = false` bleibt (wird durch eigenes UI ersetzt). ### Geänderte Dateien - `app/src/main/kotlin/de/waypointaudio/ui/ExplorerScreenMapLibre.kt`: `MapCompactAttributionBadge` hinzugefügt, `OsmAttributionBadge`-Aufruf ersetzt - `app/src/main/kotlin/de/waypointaudio/ui/MapLayerPanel.kt`: `MapAttributionDialog` + Karteninfo-Surface im Layer-Sheet - `app/src/main/res/values/strings.xml`: neue Attribution-Strings (`map_info_*`, `map_attribution_compact`) --- ## 2. POI-Layer-Kategorien ### Umsetzung **Architektur:** - Neues Enum `PoiCategory` und `data class PoiLayerSettings` in `MapStyleSettings.kt` - `PoiLayerSettings`: 4 Boolean-Felder (`showSights`, `showInfrastructure`, `showGastronomy`, `showMobility`) - Persistenz via DataStore (4 neue Keys in `map_style_settings`) **Kategorien und Icons (im Layer-Sheet):** | Kategorie | Icon | Default | Heuristik-Präfixe | |---|---|---|---| | Sehenswürdigkeiten | Place | **AN** | poi, tourism, historic, attraction, landmark, monument, museum, zoo, castle, church… | | Infrastruktur | LocationOn | aus | health, hospital, pharmacy, police, post, bank, atm, fuel… | | Gastronomie | Nightlife | aus | food, catering, restaurant, cafe, fast-food, bar, pub… | | Mobilität | Route | aus | transit, transport, railway, bus, subway, parking, bicycle, station… | **UI:** Zwei Chip-Reihen (2×2) unter dem Ebenen-Abschnitt im Layer-Sheet. Visuell: secondaryContainer (aktiv) vs. surfaceVariant (inaktiv). **Technischer Mechanismus:** - Funktion `applyPoiLayerVisibility(style, poiLayers)` in `ExplorerScreenMapLibre.kt` - Iteriert über alle `SymbolLayer` des geladenen MapLibre-Styles - Layer-ID-Heuristik (lowercase-contains) ordnet jeden Layer einer Kategorie zu - Setzt `visibility = VISIBLE` oder `NONE` via `PropertyFactory.visibility()` - Wird aufgerufen: nach Style-Load (`onStyleReady`), bei Kategorienänderung (`LaunchedEffect(layerState.poiLayers, ...)`), bei Stilwechsel **Einschränkungen (transparent dokumentiert):** - Layer-IDs sind style-abhängig. Wenn OpenFreeMap den Style-Schema ändert, kann die Heuristik Abweichungen zeigen. - Im CLASSIC-Raster-Modus (OSM-Rastertiles) sind keine Vektor-Layer vorhanden — die Chips werden im Sheet angezeigt, aber deaktiviert mit Hinweistext ("POI-Layer nur bei Vektor-Stilen verfügbar"). - Die Heuristik deckt die häufigsten Symbol-Layer ab. Feingranulare Filter (z. B. nur "museum", nicht alle "tourism") sind mit dieser Methode nicht möglich, ohne Style-JSON vollständig zu analysieren. - Es gibt keine Overpass-API-Abfrage — ausschließlich Vektor-Tile-Layer-Sichtbarkeit. - Kein Crash wenn Layer-ID nicht existiert (jede `.setProperties()`-Änderung ist in `runCatching {}` gewrappt). ### Geänderte Dateien - `app/src/main/kotlin/de/waypointaudio/data/MapStyleSettings.kt`: `PoiLayerSettings`, `PoiCategory`, DataStore-Keys, `setPoiLayers()` - `app/src/main/kotlin/de/waypointaudio/ui/MapLayerPanel.kt`: POI-Abschnitt mit Chip-UI, `PoiCategoryChip`-Composable - `app/src/main/kotlin/de/waypointaudio/ui/ExplorerScreenMapLibre.kt`: `applyPoiLayerVisibility()`, LaunchedEffects für POI --- ## 3. Kartenstil-Persistenz ### Status in v2.5.37 - `MapStyleStore.setBaseStyle()` bereits vorhanden und funktionstüchtig - `LaunchedEffect(mapStyleSettings)` hydratete `layerState.baseStyle` — aber **nur einmal** beim ersten Laden, **nicht bei App-Neustart** wenn der erste Compose-Rendering-Pass vor dem DataStore-Emit stattfand ### Fix in v2.5.38 - `layerState` Initialisierung startet weiterhin mit `MapLayerState(followLocation = persistedSettings.followLocation)` (sofortiger Wert) - `LaunchedEffect(mapStyleSettings)` hydratiert jetzt **zusätzlich `poiLayers`**: ```kotlin layerState = layerState.copy( baseStyle = mapStyleSettings.baseStyle, showAudioLength = mapStyleSettings.showAudioLength, showAudioLines = mapStyleSettings.showAudioLines, poiLayers = mapStyleSettings.poiLayers, // NEU ) ``` - `styleHydrated`-Guard verhindert Überschreiben nach initialem Load - Neuer `LaunchedEffect(layerState.poiLayers)` persistiert POI-Auswahl wenn geändert - Die Hydration funktioniert weil DataStore-Flow in Coroutine gesammelt wird — erster Emit kommt kurz nach Compose-Start, triggert LaunchedEffect ### Persistenz-Zyklus (vollständig) ``` App-Start → DataStore emit mapStyleSettings (baseStyle=X, poiLayers=Y) → LaunchedEffect(mapStyleSettings): layerState.copy(baseStyle=X, poiLayers=Y) → MapLibre lädt Style-URL für X → onStyleReady: applyPoiLayerVisibility(style, Y) → styleReady=true → Karte zeigt Stil X mit POI-Sichtbarkeit Y Nutzer wechselt Stil → layerState.copy(baseStyle=Z) → LaunchedEffect(layerState.baseStyle): mapStyleStore.setBaseStyle(Z) → LaunchedEffect(baseStyle, styleReady): setStyle(Z), POI neu anwenden → DataStore gespeichert → Neustart: Stil Z Nutzer schaltet POI → layerState.copy(poiLayers=P) → LaunchedEffect(poiLayers): mapStyleStore.setPoiLayers(P) → LaunchedEffect(poiLayers, styleReady): applyPoiLayerVisibility(style, P) → DataStore gespeichert → Neustart: POI-Auswahl P ``` --- ## 4. Keine Regressionen Folgende Funktionen wurden **nicht verändert** und sind in v2.5.38 unberührt: | Funktion | Status | |---|---| | Phantom-Layer-Fix (v2.5.36) | ✓ unverändert — `clearAllAnnotationsCaches()` vor Stilwechsel | | v2.5.37 Stilwechsel (5 Stile) | ✓ unverändert — gleiche Style-URLs | | Lizenzsystem (ECDSA, Import/Export) | ✓ unverändert | | Multi-Clip-Waypoints | ✓ unverändert | | PTT / Live-Aufnahme | ✓ unverändert | | Tour-Zähler | ✓ unverändert | | Backup/Cover (ZIP-Export/Import) | ✓ unverändert | | GPX-Import (additiv) | ✓ unverändert | | Explorer/Routing (OSRM, Planlinie) | ✓ unverändert | | Tour-/Waypoint-Kopien | ✓ unverändert | | Hintergrundwiedergabe | ✓ unverändert | | OsmAttributionBadge (MapScreen, TrackDraftEditor) | ✓ unverändert — nur ExplorerScreenMapLibre nutzt neues Badge | --- ## 5. Signatur & ABI-Status ``` APK: GPS2Audio_v2_5_38_map_attribution_poi_persistence_universal_signed.apk Größe: 57 MB (58.954.154 Bytes) Signaturprüfung (apksigner verify): Verified using v2 scheme (APK Signature Scheme v2): true Number of signers: 1 Package: de.waypointaudio versionCode: 92 versionName: 2.5.38 ABIs: lib/arm64-v8a/libmaplibre.so ✓ lib/armeabi-v7a/libmaplibre.so ✓ lib/x86/libmaplibre.so ✓ lib/x86_64/libmaplibre.so ✓ Keystore: GPS2Audio_release_keystore_v2.jks Alias: gps2audio_release_v2 ``` --- ## 6. Testfälle ### Attribution - [ ] Karte öffnen → Badge bottom-left zeigt "© OpenStreetMap · ODbL" - [ ] Badge antippen → Browser öffnet openstreetmap.org/copyright - [ ] Layer-Sheet öffnen → unterster Bereich "Karteninfo" sichtbar - [ ] "Karteninfo"-Button antippen → Dialog öffnet sich - [ ] Dialog: Links zu openfreemap.org, openmaptiles.org, openstreetmap.org/copyright antippen - [ ] Kartenstil wechseln → Attribution-Badge bleibt sichtbar ### POI-Layer - [ ] Layer-Sheet öffnen → Abschnitt "POI anzeigen" sichtbar (unter Ebenen-Switches) - [ ] Standard: Sehenswürdigkeiten-Chip ist aktiv (farbig), andere 3 inaktiv - [ ] Sehenswürdigkeiten-Chip deaktivieren → POI-Icons auf Karte ausgeblendet (bei ausreichend Zoom, Vektor-Stil) - [ ] Gastronomie aktivieren → Gastronomie-Icons eingeblendet - [ ] App schließen und neu öffnen → POI-Auswahl wiederhergestellt - [ ] Auf Klassisch wechseln → Hinweis "POI-Layer nur bei Vektor-Stilen verfügbar" erscheint - [ ] Stilwechsel → POI-Sichtbarkeit wird neu angewendet (kein Reset der Auswahl) ### Kartenstil-Persistenz - [ ] Auf "Dunkel" wechseln → App schließen → App öffnen → "Dunkel" ist aktiv - [ ] Auf "Outdoor" wechseln → App schließen → App öffnen → "Outdoor" ist aktiv - [ ] Auf "3D Stadt" wechseln, Pitch auf 50° → App schließen → Pitch ist korrekt (Pitch wird separat gespeichert) - [ ] Migration von v2.5.37: alte Einstellungen ("modern_2d", "dark" etc.) werden korrekt geladen ### Keine Regressionen - [ ] GPX-Import fügt Wegpunkte hinzu ohne bestehende zu löschen - [ ] ZIP-Backup erstellen und importieren → alle Touren, Covers, Waypoints wiederhergestellt - [ ] PTT-Aufnahme funktioniert - [ ] Routing (OSRM) berechnet Route - [ ] Phantom-Layer: aus HomeScreen zum Explorer navigieren → keine alten Linien sichtbar --- ## 7. Geänderte Dateien (Zusammenfassung) | Datei | Änderung | |---|---| | `app/build.gradle.kts` | versionCode 91→92, versionName 2.5.37→2.5.38 | | `app/src/main/res/values/strings.xml` | Attribution-Strings, POI-Strings, v2.5.38-Version-Notice | | `data/MapStyleSettings.kt` | `PoiLayerSettings`, `PoiCategory`, DataStore-Keys, `setPoiLayers()` | | `ui/MapLayerPanel.kt` | POI-Chip-UI, `MapAttributionDialog`, `PoiCategoryChip`, Karteninfo-Surface | | `ui/ExplorerScreenMapLibre.kt` | `MapCompactAttributionBadge`, `applyPoiLayerVisibility()`, POI-LaunchedEffects, poiLayers-Hydration | | `ui/VersionNoticeDialog.kt` | Version Notice auf v2538 aktualisiert | --- *Gitea-Veröffentlichung wurde nicht durchgeführt (gemäß Aufgabe).*