• v2.0.0 b2aa1c167c

    marcel released this 2026-05-15 14:29:52 +02:00 | 7 commits to main since this release

    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

    • WAKE_LOCK permission was already in AndroidManifest.xml (for audio
      service); confirmed present and unchanged.
    • 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
    • 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.
    • Wake lock cleared explicitly in MainActivity.onPause() and re-applied
      in onResume() (in addition to the framework's automatic handling).
    • 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.
    • Setting persisted via DataStore (wake_lock_settings) — new file
      data/WakeLockSettings.kt.
    • 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

    • New scroll-region order on the main page:
      Tour-Zähler → Live/PTT → Atmo (Begleitmusik) → Manueller Player → Wegpunkt-Tracks.
    • Existing fixed ScrollableTabRow for tours stays pinned at top, untouched.
    • Page remains scrollable; bottom FAB padding (Spacer 160 dp) preserved.
    • 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

    • 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).
    • 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.
    • 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.
    • 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.
    • 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).
    • 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)

    • Title row in WaypointListScreen is now Row { Image(24dp) + Text(titleMedium / SemiBold) }.
    • 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).
    • 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

    • app/build.gradle.kts: versionCode = 28, versionName = "2.0.0".
    • 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

    • 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.
    • German selection dialog (MapProviderDialog) reached from a
      new layers-icon button in the Map TopBar actions row.
    • 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.
    • Live tile-source switch: LaunchedEffect(selectedProvider) in both
      MapScreen and TrackDraftEditorScreen updates the active MapView
      and calls invalidate() so the new tiles load immediately.
    • 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:

    • applicationId de.waypointaudio unchanged.
    • 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.
    • Backup export / import unchanged (io/BackupExportManager.kt,
      io/BackupImportManager.kt).
    • Sort mode for tours and waypoint tracks unchanged (SortModeBar +
      TourReorderList still in WaypointListScreen).
    • Scrollable main page preserved (the outer verticalScroll column
      still wraps everything below the tab row).
    • Map search in normal map and editor unchanged (PlaceSearchDialog
      still wired into both MapScreen and TrackDraftEditorScreen).
    • Audio library playlist selection unchanged (BegleitmusikDialog).
    • PTT / Atmo collapsible unchanged (LivePttCard).
    • PTT pauses / resumes Atmo correctly — same behavior, now routed
      through AtmoResumeManager. Manually traced: pauseAtmo
      notifyInterruptStart(PTT) → snapshots wasAtmoPlayingAtFirstInterruption
      and pauses if playing; restoreAtmonotifyInterruptEnd(PTT)
      resumes only if it was playing and the player still has content.
    • OSM attribution layout (OsmAttributionBadge) still rendered for
      every provider — verified by inspecting MapScreen.kt lines 819–824.
    • 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.
    • 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.
    Downloads