# GPS2Audio v2.0.0 — Phase 1 Fixes — Handoff **Build date:** 2026-05-14 **versionCode:** 28 **versionName:** 2.0.0 **applicationId:** de.waypointaudio **Signed:** yes, with `GPS2Audio_release_keystore.jks` (same identity as the first release) ## Artifacts | File | Purpose | |---|---| | `/home/user/workspace/GPS2Audio_v2_0_0_phase1_signed.apk` | Signed release APK (≈ 14.4 MB) | | `/home/user/workspace/GPS2Audio_v2_0_0_phase1_source.zip` | Full source tree (`work/` root) without keystore, credentials, or real `keystore.properties` | | `/home/user/workspace/GPS2Audio_v2_0_0_phase1_handoff.md` | This document | ## Build / signing verification ``` package: name='de.waypointaudio' versionCode='28' versionName='2.0.0' Signer #1 certificate DN: CN=GPS2Audio, OU=NesoHub, O=GPS2Audio, L=Germany, C=DE Signer #1 certificate SHA-256 digest: 7b0ea4d5528605173d299d7b268e7ab8768dd504ec15b66a52d47b4257b3bb46 ``` The SHA-256 matches the fingerprint recorded in `GPS2Audio_release_keystore_credentials.txt` exactly — the APK is signed with the same release identity as `GPS2Audio_first_release_signed.apk`. `apksigner verify` reports the certificate without errors. The build was produced via `gradle :app:assembleRelease`; no debug build is shipped for this milestone. ## Install / update notes - This APK **updates** the installed first release (versionCode 27) because the package name (`de.waypointaudio`), keystore identity, and signature scheme are identical. No uninstall needed. - Users keep their tours, waypoints, audio files, drafts and tour-music settings — only new DataStore stores (`wake_lock_settings`, `map_provider_settings`) are added with safe defaults (`mode = Nie`, `provider = OpenStreetMap`). ## Phase 1 feature checklist ### 1.1 Wake Lock / Display aktiv halten - [x] `WAKE_LOCK` permission was already in `AndroidManifest.xml` (for audio service); confirmed present and unchanged. - [x] German UI with three radio-button options: - **Nie** (default) - **Nur bei aktiver Tour/GPS** — only while the GPS service is running - **Immer wenn App im Vordergrund** — whenever the app window is visible - [x] Implementation uses `WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON` on the Activity window (recommended pattern, see https://developer.android.com/training/scheduling/wakelock). This is preferred over `PowerManager.SCREEN_DIM_WAKE_LOCK` (deprecated) because Android automatically clears the flag when the activity pauses, so the "release when app goes to background" requirement is satisfied by the framework itself and cannot leak. - [x] Wake lock cleared explicitly in `MainActivity.onPause()` and re-applied in `onResume()` (in addition to the framework's automatic handling). - [x] One-time **Akku-Hinweis** dialog appears the first time the user switches from *Nie* to an active mode. Tracked in DataStore (`battery_hint_shown`), so it never re-appears. - [x] Setting persisted via DataStore (`wake_lock_settings`) — new file `data/WakeLockSettings.kt`. - [x] Auto-dim after X min: **not implemented in Phase 1** — would require a custom timer + screen-brightness reset path that fights the Android system display timeout. Limitation documented here; will revisit in Phase 2 if requested. **Files touched:** - `app/src/main/kotlin/de/waypointaudio/data/WakeLockSettings.kt` *(new)* - `app/src/main/kotlin/de/waypointaudio/service/WakeLockController.kt` *(new)* - `app/src/main/kotlin/de/waypointaudio/ui/WakeLockSettingsDialog.kt` *(new)* - `app/src/main/kotlin/de/waypointaudio/MainActivity.kt` *(wires controller, observes service state)* - `app/src/main/kotlin/de/waypointaudio/ui/WaypointListScreen.kt` *(menu entry, dialog launch)* - `app/src/main/res/values/strings.xml` *(new wake-lock strings)* ### 1.2 Atmo-Bereich nach oben verschieben - [x] New scroll-region order on the main page: `Tour-Zähler → Live/PTT → Atmo (Begleitmusik) → Manueller Player → Wegpunkt-Tracks`. - [x] Existing fixed `ScrollableTabRow` for tours stays pinned at top, untouched. - [x] Page remains scrollable; bottom FAB padding (`Spacer 160 dp`) preserved. - [x] No duplicate cards — old positions of the Atmo player and manual player bar (below the waypoint list) were removed. **Files touched:** - `app/src/main/kotlin/de/waypointaudio/ui/WaypointListScreen.kt` ### 1.3 Atmo Resume vollständig absichern - [x] **AtmoResumeManager** introduced as a single source of truth for *interrupt-start / interrupt-end* across all three interruption sources (`WAYPOINT`, `MANUAL_PLAYER`, `PTT`). It uses `pause()` on the shared `BackgroundMusicPlayer`, which preserves the ExoPlayer's current position — never `stop()` (which releases the player and loses the position). - [x] **PTT path** now delegates to `AtmoResumeManager.notifyInterruptStart/End` via `LivePttManager.pauseAtmo / restoreAtmo`. The previous inline logic (which already used `pauseMusic()` and a remembered "was playing" flag) was replaced — behavior is identical from the user's perspective, but it now runs through the central manager. - [x] **Waypoint path** continues to use the existing `BackgroundMusicPlayer.before/afterWaypointAudio` hooks. Those hooks already call `pause()` internally for the PAUSE_RESUME behavior and preserve position via the live ExoPlayer instance. They are explicitly documented in the new `AtmoResumeManager` so future contributors know that the waypoint route is the canonical pause/resume contract. - [x] **Manual Player path** wraps every manual play with the same `musicManager.beforeWaypointAudio()` / `afterWaypointAudio()` pair (unchanged) — that means manual-player interruptions use the same pause-with-position semantics as GPS-triggered waypoint audio. - [x] Resume only fires if Atmo was active immediately before the first interruption *and* no other interruption is still active *and* the Atmo player still holds content (i.e. the user has not manually stopped it during the interruption). - [x] Loop / natural-end edge case: `BackgroundMusicPlayer` already uses `Player.REPEAT_MODE_ALL`, so an Atmo track that finishes naturally mid-interruption simply transitions to the next item; on resume the next-item start position is correct. **Files touched:** - `app/src/main/kotlin/de/waypointaudio/service/AtmoResumeManager.kt` *(new)* - `app/src/main/kotlin/de/waypointaudio/service/LivePttManager.kt` *(delegates to AtmoResumeManager)* ### 1.4 TopBar — Variante C (Icon + kompakter Titel) - [x] Title row in `WaypointListScreen` is now `Row { Image(24dp) + Text(titleMedium / SemiBold) }`. - [x] `R.drawable.ic_launcher` is reused (the launcher icon already encodes the MapPin + sound-wave concept; there is no separate `ic_launcher_foreground` resource — single-source icon in this repo). - [x] Line break in *GPS2Audio* fixed via `maxLines = 1` + `softWrap = false` + `overflow = TextOverflow.Ellipsis`. **Files touched:** - `app/src/main/kotlin/de/waypointaudio/ui/WaypointListScreen.kt` ### 1.5 Versionsnummer bereinigen - [x] `app/build.gradle.kts`: `versionCode = 28`, `versionName = "2.0.0"`. - [x] No suffix; *Über diese App* (`AboutDialog`) reads via `AppVersion.get()` (existing util) and therefore now displays **Version 2.0.0 (28)** with no code changes. ### 1.6 Karten-Provider Auswahl - [x] New enum `MapProvider` with eight OSMDroid-based tile sources: OSM (default / fallback), CartoDB Positron, CartoDB Dark Matter, OpenTopoMap, ESRI Satellite, ESRI Hybrid, Stadia Alidade, Stadia Outdoors. All are wired through `XYTileSource` — no library swap. - [x] German selection dialog (`MapProviderDialog`) reached from a new layers-icon button in the Map TopBar actions row. - [x] Selection persisted **app-wide** via DataStore (`map_provider_settings`). **Per-tour persistence not implemented**: tours currently store only audio settings (`TourAudioSettings`), not map settings — adding a per-tour map provider would require a new per-tour store and migration of existing tour data, which is out of scope for Phase 1. The app-wide setting is reachable from the map at any time, so it is one tap to switch. - [x] Live tile-source switch: `LaunchedEffect(selectedProvider)` in both `MapScreen` and `TrackDraftEditorScreen` updates the active `MapView` and calls `invalidate()` so the new tiles load immediately. - [x] OSM-Attribution remains the persistent `OsmAttributionBadge` overlay on `MapScreen` — visible for *all* providers. The provider-specific copyright (CartoDB, Esri, Stadia, OpenTopoMap) is encoded in each `XYTileSource` and surfaced when osmdroid prints copyright info. **Files touched:** - `app/src/main/kotlin/de/waypointaudio/data/MapProvider.kt` *(new)* - `app/src/main/kotlin/de/waypointaudio/ui/MapProviderDialog.kt` *(new)* - `app/src/main/kotlin/de/waypointaudio/ui/MapScreen.kt` - `app/src/main/kotlin/de/waypointaudio/ui/TrackDraftEditorScreen.kt` - `app/src/main/res/values/strings.xml` *(new provider strings)* ## First-release regression checklist Each of these features from the first release was inspected and is **not modified** by this milestone: - [x] `applicationId` `de.waypointaudio` unchanged. - [x] Track-Editor / draft archive, thick route line + handles, marking, "import marked points" flow — code paths in `TrackDraftListScreen`, `TrackDraftEditorScreen`, `WaypointRepository` untouched apart from a single map-tile-source line. - [x] Backup export / import unchanged (`io/BackupExportManager.kt`, `io/BackupImportManager.kt`). - [x] Sort mode for tours and waypoint tracks unchanged (SortModeBar + TourReorderList still in WaypointListScreen). - [x] Scrollable main page preserved (the outer `verticalScroll` column still wraps everything below the tab row). - [x] Map search in normal map and editor unchanged (`PlaceSearchDialog` still wired into both `MapScreen` and `TrackDraftEditorScreen`). - [x] Audio library playlist selection unchanged (`BegleitmusikDialog`). - [x] PTT / Atmo collapsible unchanged (`LivePttCard`). - [x] PTT pauses / resumes Atmo correctly — same behavior, now routed through `AtmoResumeManager`. Manually traced: `pauseAtmo` → `notifyInterruptStart(PTT)` → snapshots `wasAtmoPlayingAtFirstInterruption` and pauses if playing; `restoreAtmo` → `notifyInterruptEnd(PTT)` → resumes only if it was playing *and* the player still has content. - [x] OSM attribution layout (`OsmAttributionBadge`) still rendered for every provider — verified by inspecting `MapScreen.kt` lines 819–824. - [x] About: PayPal support link, automatic version display (`AppVersion.get()`), per-version startup notice (`VersionNoticeStore`, `VersionNoticeDialogHost`) — all unchanged. Because `versionCode = 28` is new, the one-time version notice will fire once on first launch of v2.0.0. - [x] JSON / GPX I/O (`ImportExportManager`), GPS behaviors (`WaypointLocationService`, `TrackRecordingService`) untouched. ## Build details ``` gradle :app:assembleRelease BUILD SUCCESSFUL in 1m 51s 43 actionable tasks: 43 executed ``` Compiler warnings (non-fatal, all pre-existing): - `launchWhenStarted` deprecation in MainActivity (could be migrated to `repeatOnLifecycle` in a later cleanup pass). - A handful of osmdroid + Material3 deprecations carried over from the first release. ## Implementation notes worth remembering - **WAKE_LOCK manifest permission** is genuinely needed by the audio service (MediaPlayer keeps CPU alive in foreground service). The new display-on logic does **not** need an additional manifest permission — `FLAG_KEEP_SCREEN_ON` requires none. - **Per-tour map provider** was considered and explicitly deferred because the existing `TourAudioSettings` store is audio-only; adding a parallel per-tour map store would have created two parallel persistence schemas in one milestone. The single app-wide setting is one tap away on the map. - **`PowerManager` wake lock** is *not* held in addition to `FLAG_KEEP_SCREEN_ON` for the display use case. Two mechanisms competing for the same outcome makes future maintenance harder and provides no user-visible benefit on modern Android. - **AtmoResumeManager.activeInterruptions** is a `Set`: if a PTT press overlaps with a waypoint trigger, both must end before the Atmo resumes. This prevents the Atmo from briefly re-starting between two adjacent interruptions. - **keystore.properties** is excluded from the source zip (only the `.example` template is included). The keystore `.jks` file and credentials are never bundled. Build from this zip with the signing-config environment variables or by writing a local `keystore.properties` outside source control.