• v2.0.8 b2aa1c167c

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

    GPS2Audio v2.0.8 – Stream-Playlist (Atmo/Begleitmusik)

    Version: 2.0.8 · versionCode: 36 · Build type: Release (signed) · Baseline: v2.0.7 (GPS2Audio_v2_0_7_atmo_service_player_source.zip)

    Artefakte

    Pfad Inhalt
    /home/user/workspace/GPS2Audio_v2_0_8_stream_playlist_signed.apk Signiertes Release-APK (v2 APK Signing)
    /home/user/workspace/GPS2Audio_v2_0_8_stream_playlist_source.zip Vollständiger Quellbaum ohne build/, ohne Keystore, ohne keystore.properties
    /home/user/workspace/GPS2Audio_v2_0_8_stream_playlist_handoff.md Dieses Dokument

    APK-Eckdaten (aapt dump badging):

    • package: name='de.waypointaudio' versionCode='36' versionName='2.0.8'
    • compileSdk 35, minSdk 26, targetSdk 35
    • Signer SHA-256: 7B:0E:A4:D5:52:86:05:17:3D:29:9D:7B:26:8E:7A:B8:76:8D:D5:04:EC:15:B6:6A:52:D4:7B:42:57:B3:BB:46 (identisch mit Release-Keystore aus GPS2Audio_release_keystore_credentials.txt)
    • APK-SHA-256: 2b9a1f25f54fbf7b93eb4dd24c824dc429fe293e62774cd777c5902d14a13053
    • Größe: ~14.5 MB
    • apksigner verify: Verifies (APK Signature Scheme v2)

    Was ist neu in 2.0.8

    Die einzige Stream-URL pro Tour wird durch eine Stream-Playlist ersetzt. User können beliebig viele
    Internet-Stream-Einträge (URL + optionaler Name) pro Tour anlegen, umsortieren und nacheinander abspielen.
    Der service-eigene ExoPlayer aus v2.0.7 wurde unverändert übernommen – die Stream-Playlist wird einfach als
    mehrteilige MediaItem-Liste an die bestehende Player-Pipeline übergeben.

    Alle anderen v2.0.7-Funktionen bleiben unverändert: service-eigener ExoPlayer (Foreground Service
    mediaPlayback), PTT pausiert Wegpunkt + Atmo, Drag-Reorder, Wake Lock, Map-Provider, Track-Editor +
    Drafts, Backup-Export/Import, Map-Suche, Audio-Bibliothek, OSM-Attribution, TabRow-Fix, About-Dialog
    mit PayPal-Link/Versions-Notiz.

    Datenmodell + Migration

    Neu in TourAudioSettings
    (app/src/main/kotlin/de/waypointaudio/data/TourAudioSettings.kt):

    data class StreamEntry(val url: String, val name: String = "")
    
    data class TourAudioSettings(
        
        val streamPlaylist: List<StreamEntry> = emptyList(),
        val streamUrl: String? = null,   // Legacy-Feld – nur für Backward-Compat
        
    ) {
        val effectiveStreamPlaylist: List<StreamEntry> get() = when {
            streamPlaylist.isNotEmpty() -> streamPlaylist
            !streamUrl.isNullOrBlank()  -> listOf(StreamEntry(url = streamUrl, name = ""))
            else                        -> emptyList()
        }
        fun migrated(): TourAudioSettings =   // streamUrl → streamPlaylist
    }
    

    Persistenz (TourMusicStore): streamPlaylist wird als JSON-Array [{url, name}, …] neben dem
    bestehenden streamUrl-Feld gespeichert. Beim Laden:

    • Wenn streamPlaylist befüllt ist → wird direkt benutzt.
    • Wenn streamPlaylist leer ist und streamUrl befüllt ist → einmalige Migration in einen einzigen
      StreamEntry (url=streamUrl, name=""), streamUrl wird auf null gesetzt.
    • Sobald der User die Einstellungen das nächste Mal speichert, ist die Migration auf Disk persistiert.

    Backward-Compatibility:

    • Beim Lesen alter Datastore-Inhalte (v2.0.7 oder älter): streamPlaylist-Feld fehlt im JSON → wird als
      leere Liste interpretiert (Gson-Default). Anschließend migrated() aufrufen → Legacy-streamUrl wird in
      eine ein-Element-Playlist überführt. Der User sieht in v2.0.8 sofort die alte URL als ersten Eintrag der
      Stream-Playlist.
    • Beim Schreiben neuer Settings aus dem Dialog wird streamUrl=null gespeichert; die kanonische
      Quelle ist die streamPlaylist. Ältere App-Versionen können die JSON-Repräsentation immer noch lesen
      (Gson ignoriert das unbekannte streamPlaylist-Feld bei v2.0.7), zeigen aber dann keine Stream-URL,
      weil das Legacy-Feld leer ist – das wäre also ein Downgrade-Pfad und kein normaler Use-Case.

    Keine DB-Migration nötigTourMusicStore benutzt JSON in DataStore und ist tolerant gegenüber
    fehlenden Feldern (Gson-Defaults).

    UI-Flow (Begleitmusik-Dialog)

    Datei: app/src/main/kotlin/de/waypointaudio/ui/BegleitmusikDialog.kt

    Quelle „Stream-URL" wählen → die alte einzelne URL-Eingabe ist ersetzt durch:

    1. Stream-Playlist-Liste (oben): pro Eintrag eine Zeile mit Radio-Icon, Name (falls vergeben) bzw. URL,
      Buttons:
      • / (Reorder, sequentiell – kein Drag, da im Dialog overkill)
      • ✏️ Bearbeiten → lädt den Eintrag in die Eingabefelder unten
      • ✕ Entfernen (rot)
    2. Eingabesektion „Stream hinzufügen" / „Stream bearbeiten":
      • Name (optional) – Klartext, z. B. „Deutschlandfunk Kultur"
      • Stream-URL – Pflicht, validiert auf http:// oder https://, Fehleranzeige unter dem Feld
      • Buttons: Hinzufügen / Übernehmen (Edit) / Abbrechen (nur im Edit-Modus) / Playlist leeren (rot)
    3. Shuffle-Switch erscheint zusätzlich nur wenn streamPlaylist.size > 1.
    4. Der bestehende Plattform-Hinweis zu YouTube/SoundCloud/radio.de bleibt erhalten.
    5. Bei „Speichern" mit leerer Stream-Playlist und Quelle=Stream → Fehlermeldung
      music_stream_playlist_empty_error („Bitte mindestens einen Stream zur Playlist hinzufügen.")

    Alle Labels sind auf Deutsch lokalisiert (neue Strings in res/values/strings.xml Zeilen ~280–298,
    Prefix music_stream_*).

    Playback-Verhalten

    Datei: app/src/main/kotlin/de/waypointaudio/service/AtmoPlaybackService.kt

    • Neue Intent-Extras: EXTRA_STREAM_URLS (String[]), EXTRA_STREAM_NAMES (String[]).
    • Legacy EXTRA_STREAM_URL wird weiterhin gesetzt (= erstes Element der Playlist) für Re-Delivery-Fälle.
    • applySettingsFromIntent baut bei isStream=true aus EXTRA_STREAM_URLS eine Liste von MediaItems
      und übergibt sie unverändert über die bestehende ExoPlayer-Initialisierung – derselbe Code-Pfad wie für
      lokale Playlists. ExoPlayer behandelt Next/Previous, Auto-Advance und Fehler nativ über
      Player.REPEAT_MODE_ALL und die Player.Listener-Callbacks, die wir bereits hatten.
    • Wenn ein Stream-Item fehlerhaft ist (Netzwerk, falsche URL), gehört das zum Standard-ExoPlayer-Verhalten
      → der Player loggt den Fehler und überträgt den nächsten MediaItem (Repeat-All). Es gibt keinen Crash.
    • MusicPlaybackState:
      • isStream = true bleibt für alle Stream-Modi.
      • playlistIndex / playlistTotal werden bei Stream-Playlists mit > 1 Eintrag korrekt befüllt – die
        UI kann darüber „1/3" anzeigen wie für lokale Playlists.
      • supportsSkip = total > 1 gilt jetzt auch für Stream-Playlists, sodass die Next/Previous-Buttons
        funktionieren.
      • currentTitle zeigt den Stream-Namen, wenn vergeben, sonst den URL-Host.
      • sourceLabel zeigt „ Streams" bei Playlists mit > 1 Eintrag, sonst den Host.

    Background-Wiedergabe (v2.0.7-Verhalten unverändert): Der Foreground-Service AtmoPlaybackService
    besitzt den ExoPlayer. PTT/Wegpunkt-Audio pausiert die Wiedergabe wie zuvor (Verhalten PAUSE_RESUME,
    FADE_OUT_IN, DUCK_UNDERLAY, CONTINUE_UNDERLAY greifen auf Stream-Playlists wie auf lokale Playlists).
    Drag-Reorder im Wegpunkt-Listenscreen ist nicht betroffen.

    Backup-Export/Import

    Export (io/BackupExportManager.kt):
    data/music_settings.json enthält jetzt zusätzlich streamPlaylist: [{url, name}, …] pro Tour
    (effectiveStreamPlaylist – auch bei Tourdaten, die noch das Legacy-streamUrl haben). Das alte Feld
    streamUrl bleibt im Export-JSON aus Kompat-Gründen enthalten (nun typischerweise null).

    Import (io/BackupImportManager.kt):
    parseMusicSettings() liest streamPlaylist (neu); falls fehlend und streamUrl gesetzt → Migration
    in eine ein-Element-Stream-Playlist. Damit funktionieren v2.0.7-Backups uneingeschränkt – die alte
    Stream-URL erscheint nach dem Import als erster Eintrag der Stream-Playlist.

    Manuelle Tests

    Erstinstallation (Geräte-Daten vorher leer):

    1. Stream-Playlist anlegen – Tour erstellen → Begleitmusik → Quelle „Stream-URL" → zwei Streams
      eintragen (z. B. https://liveradio.swr.de/sw282p3/swr3/play.mp3 und
      https://st01.dlf.de/dlf/01/104/ogg/stream.ogg) → speichern → Play-Button → erster Stream startet im
      Foreground.
    2. Next/Previous – Skip-Vor/Zurück-Buttons springen zwischen den beiden Streams.
    3. Background – App in den Hintergrund schicken, Bildschirm aus → Musik läuft weiter (Notification
      bleibt sichtbar). App schließen (Recents wischen) → Wiedergabe läuft weiter; Stop-Action in der
      Notification beendet sie.
    4. PTT-Pause – PTT aktivieren → Atmo-Stream pausiert; PTT loslassen → Wiedergabe setzt fort, abhängig
      vom Verhalten (PAUSE_RESUME / FADE_OUT_IN / DUCK / CONTINUE).
    5. Wegpunkt-Pause – an einem Wegpunkt vorbeigehen oder „Test" → Wegpunkt-Ton spielt, Atmo verhält sich
      gemäß Behavior-Modus.
    6. Reorder – Streams in der Liste per / umsortieren → Save → Reihenfolge bleibt nach App-Restart
      erhalten.
    7. Edit & Delete – Eintrag bearbeiten (Name + URL ändern) und entfernen → Liste reagiert sofort.
    8. Shuffle – Switch erscheint bei > 1 Stream-Eintrag, aktivieren → Reihenfolge wird zufällig.
    9. Lokale Playlist – Quelle auf „Lokale Playlist" wechseln → bestehende lokale Playlist + Audio-
      Bibliothek funktionieren unverändert. Audio-Bibliothek-Picker für lokale Files wurde nicht angefasst.
    10. Validation – Stream-URL ohne http(s):// eintragen → Fehlermeldung. Stream-Playlist leer + Quelle
      Stream + Save → Fehlermeldung.

    Migration / Backward-Compatibility:
    11. v2.0.7-Backup importieren – Backup-ZIP aus v2.0.7 öffnen → Touren mit einzelner Stream-URL
    erscheinen nach Import unter „Stream-Playlist" mit genau einem Eintrag (URL-Feld gefüllt, Name leer).
    12. In-Place-Upgrade – v2.0.7-APK deinstallieren nicht notwendig (gleiche Signatur, gleiche
    applicationId). Nach Upgrade auf 2.0.8 zeigen vorhandene Touren ihre alte Stream-URL automatisch in der
    neuen Stream-Playlist.
    13. Backup-Export – Backup in v2.0.8 erstellen → ZIP enthält streamPlaylist-Feld (kann mit unzip -p backup.zip data/music_settings.json inspiziert werden).

    Regressions-Checkliste (v2.0.7-Features)

    • Service-owned ExoPlayerAtmoPlaybackService besitzt weiterhin den Player; Wiedergabe läuft
      durch App-Hintergrund, Bildschirm aus, App-Kill (Recents).
    • PTT pausiert Wegpunkt + AtmoLivePttManager und AtmoResumeManager unverändert. Stream-
      Playlist verhält sich identisch zu lokalen Playlists.
    • Drag-Reorder WegpunkteWaypointListScreen und Drag-Logic unverändert.
    • Wake Lock – ExoPlayer-Wake-Mode (WAKE_MODE_NETWORK für Streams, WAKE_MODE_LOCAL für lokale)
      bleibt erhalten.
    • Map-Provider – Map-Screen + Provider-Auswahl nicht angefasst.
    • Track-Editor + Drafts – Code in editor/TrackDraftListScreen nicht modifiziert.
    • Backup-Export/Import – Music-Settings-JSON erweitert (kompatibel mit alten Backups), alle
      anderen Sektionen unverändert.
    • Map-Suche – Code in MapScreen nur Compiler-Warnung (deprecated fillColor – schon in v2.0.7),
      keine Verhaltensänderung.
    • Audio-BibliothekLibraryPickerDialog und audioLibrary-Flow unverändert.
    • OSM-Attribution – nicht modifiziert.
    • TabRow-Fix – nicht modifiziert.
    • About-Dialog (PayPal-Link, Versions-Notiz) – nicht modifiziert; Version wird automatisch aus
      BuildConfig gelesen (2.0.8).

    Geänderte Dateien

    app/build.gradle.kts                                                versionCode 35→36, versionName "2.0.7"→"2.0.8"
    app/src/main/kotlin/de/waypointaudio/data/TourAudioSettings.kt      + StreamEntry, streamPlaylist, effectiveStreamPlaylist, migrated()
    app/src/main/kotlin/de/waypointaudio/data/TourMusicStore.kt         + StreamEntryJson, streamPlaylist-Persistenz, Migration im Read-Pfad
    app/src/main/kotlin/de/waypointaudio/service/AtmoPlaybackService.kt + EXTRA_STREAM_URLS/NAMES, buildStreamItems(List<StreamEntry>), state für mehrere Streams
    app/src/main/kotlin/de/waypointaudio/service/BackgroundMusicPlayer.kt   srcOk-Check auf effectiveStreamPlaylist
    app/src/main/kotlin/de/waypointaudio/ui/BegleitmusikDialog.kt       komplettes Stream-Playlist-UI (Liste, Add/Edit/Delete, Reorder, Validate, Shuffle)
    app/src/main/kotlin/de/waypointaudio/ui/WaypointListScreen.kt       hasSource-Check auf effectiveStreamPlaylist
    app/src/main/kotlin/de/waypointaudio/viewmodel/WaypointViewModel.kt sourceOk-Check auf effectiveStreamPlaylist
    app/src/main/kotlin/de/waypointaudio/io/BackupExportManager.kt      + streamPlaylist im JSON-Export
    app/src/main/kotlin/de/waypointaudio/io/BackupImportManager.kt      parseMusicSettings liest streamPlaylist mit Legacy-Migration
    app/src/main/res/values/strings.xml                                 + 14 neue Strings music_stream_*
    

    Build-Reproduktion

    unzip GPS2Audio_v2_0_8_stream_playlist_source.zip -d gps2audio
    cd gps2audio
    cat > keystore.properties <<EOF
    storeFile=/path/to/GPS2Audio_release_keystore.jks
    storePassword=<store password>
    keyAlias=gps2audio_release
    keyPassword=<key password>
    EOF
    export ANDROID_HOME=/path/to/android-sdk    # platforms/android-35, build-tools/35.0.0
    gradle :app:assembleRelease
    # Output: app/build/outputs/apk/release/app-release.apk
    

    Verifiziert: apksigner verify --verbose app-release.apkVerifies (v2-Scheme), Signer-CN
    CN=GPS2Audio, OU=NesoHub, O=GPS2Audio, L=Germany, C=DE.

    Downloads