diff --git a/HANDOFF_v2.7.39.md b/HANDOFF_v2.7.39.md new file mode 100644 index 0000000..b022132 --- /dev/null +++ b/HANDOFF_v2.7.39.md @@ -0,0 +1,40 @@ +# GPS2Audio v2.7.39 — Handoff + +## Ziel + +Stabilitätsfix für Samsung-Geräte: Die in v2.7.37 eingeführte feste Stille-Pause zwischen manuellen Clips/Wegpunkten wurde entfernt. Der Nutzen der Pause war rein kosmetisch, während sie auf einigen Geräten offenbar zu uneindeutigen Player-Zuständen und der sichtbaren Fehlermeldung „Audio konnte nicht abgespielt werden“ beitragen konnte. + +## Geänderte Dateien + +- `app/src/main/kotlin/de/waypointaudio/viewmodel/WaypointViewModel.kt` + - `manualGapJob` und `MANUAL_GAP_MS` wurden entfernt. + - Der Sonderfall „Play/Pause während der Stille-Pause“ wurde entfernt. + - Der manuelle Auto-Advance startet den nächsten aktiven Clip/Wegpunkt jetzt wieder direkt. + - `cancelManualGap()` bleibt als No-op bestehen, damit bestehende Stop-, Skip- und Tourwechsel-Pfade ohne strukturelles Risiko gleich bleiben. + +- `app/build.gradle.kts` + - `versionCode` von `144` auf `145` erhöht. + - `versionName` von `2.7.38` auf `2.7.39` erhöht. + - Changelog-Kommentar auf v2.7.39 aktualisiert. + +- `RELEASES.md` + - v2.7.39 als Audio-Stabilitätsfix dokumentiert. + +## Entscheidungslogik + +Die Pause wurde nicht als Benutzeroption beibehalten, sondern vollständig aus dem aktiven Ablauf entfernt. Dadurch gibt es keinen verzögerten Coroutine-Job mehr, der nach einem Tourwechsel, Stop/Pause oder auf Samsung-Geräten in einen veralteten Player-Zustand laufen kann. Der manuelle Modus spielt weiterhin alle aktivierten Clips und Waypoints der Reihe nach, nur ohne künstliche Pause. + +Die GPS-Wiedergabe nutzt weiterhin ihren eigenen Pfad und wurde in diesem Fix bewusst nicht verändert. + +## Build-Status in dieser Sandbox + +Der Quellcode-Fix ist vorbereitet. In der aktuellen Sandbox fehlen jedoch Android-SDK/Gradle-Toolchain und der Release-Keystore, daher konnte hier keine signierte updatefähige APK gebaut werden. Für den finalen Release-Build muss die bekannte GPS2Audio-Release-Buildumgebung beziehungsweise der Keystore erneut verfügbar sein. + +## Empfohlene Tests + +- Manueller Tour-Play mit mehreren Waypoints und mehreren Clips pro Waypoint. +- Manuelles Weiter/Zurück zwischen Clips und Waypoints. +- GPS-Trigger bei aktiver GPS-Wiedergabe auf einem Samsung-Tablet. +- Ungültige oder gelöschte Audioquelle: Fehlermeldung darf weiterhin erscheinen. +- PTT während laufender Waypoint-Wiedergabe. +- Atmo-Pause/Resume während Waypoint-Audio. diff --git a/RELEASES.md b/RELEASES.md index 4f1ce74..78f8087 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -43,6 +43,7 @@ Release: https://gitea.nesohub.org/marcel/GPS2Audio/releases/tag/v2.1.3 | `v2.7.8` | Unbenannte Tour + Planlinie Auto-Render | (1) 'Spontane Tour' → 'Unbenannte Tour': UI-String explorer_spontane_tour und createSpontaneousTour()-Basis geändert, Schlüssel/Funktionsname intern beibehalten. (2) Root Cause Planlinie: savedRouteLine wurde korrekt befüllt, aber nicht in routeLine übertragen; routeLine ist die primäre Zeichnungsquelle im draw-LaunchedEffect. Bei Kaltstart und ohne Button-Druck fehlte der Transfer. Fix 1: Neues LaunchedEffect(savedRouteLine, selectedTour) überträgt savedRouteLine → routeLine, wenn routeLine leer und keine Session-Route vorhanden ist. Fix 2: Auto-Persistenz-Effekt (LaunchedEffect routePlanline) ruft zusätzlich setCurrentSessionRoute() auf, statt nur saveTourRouteFor() — füllt Session-Route auch ohne Button. Fix 3: activeTourRoute als zusätzlicher Key im Zeichen-LaunchedEffect. Keine Regressionen. versionCode 114. | | `v2.7.26` | Persistent Map Preferences | Globale Kartenpräferenzen dauerhaft gespeichert: Wegpunkt-Marker, Audio-Radius/Reichweite, GPS-Track/Draft-Linien, POIs-automatisch-nachladen jetzt persistent (MapStyleStore). Bestehende Persistenz (baseStyle, showAudioLength, showAudioLines, POI-Kategorien, POI-Namen, perspectiveTilt, Standort-Folgen) unverändert erweitert. Standort-Folgen entschärft: Jede manuelle Kartenverschiebung deaktiviert Follow sofort (statt nur bei >40m Distanz). Kein Auto-Zentrieren beim nächsten GPS-Fix. Beim Öffnen des Explorers: Kamera auf Tour/Wegpunkte, nicht auf Live-Standort (sofern Follow aus). Keine Regressionen. versionCode 132. | +| `v2.7.39` | Audio-Stabilitätsfix ohne manuelle Zwangspause | Die in v2.7.37 eingeführte feste Stille-Pause zwischen Clips/Wegpunkten im manuellen Tour-Play wurde entfernt, weil sie auf Samsung-Geräten zu uneindeutigem Player-State und der Meldung „Audio konnte nicht abgespielt werden“ führen konnte. Manueller Auto-Advance startet den nächsten aktiven Clip/Wegpunkt wieder direkt. GPS-Wiedergabe bleibt getrennt und wurde nicht verändert. Keine Änderung an Lizenz, Backup, Karten/Explorer, PTT, Atmo oder Hintergrundwiedergabe. versionCode 145. | ## Release-Links diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 291d2d2..e809436 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -434,40 +434,18 @@ android { // gezeigt statt abzustürzen. // • KEINE Änderung am Backup-Format/-Inhalt, an der Import-Logik oder an // anderen Funktionen. - // v2.7.37 — Hörbuch-Gefühl: kurze Stille zwischen Clips/Wegpunkten. - // • Bei der manuellen Sequenz-Wiedergabe (Tour-Play) wird vor dem - // automatischen Start des nächsten Eintrags eine feste Pause von - // 1,7 s eingefügt – sowohl Clip→Clip als auch Wegpunkt→Wegpunkt. - // • Kein UI-Setting, keine Persistenz, kein neues Datenmodell. - // • Gilt NUR für die manuelle Sequenz; NICHT für GPS-getriggerte - // Wiedergabe, PTT, Atmo oder die Einzelclip-Vorschau. - // • Die Pause ist als abbrechbarer Coroutine-Job umgesetzt: Next/Skip - // überspringt sie sofort, Pause/Stop/Tour-Wechsel brechen sie ab – - // kein verspäteter Auto-Start nach dem Stoppen. - // • KEINE Änderung an Backup/Import, Lizenz, Hintergrund-Playback oder - // am Player-Auto-Open. - // - // v2.7.38 — Audio-Zuverlässigkeit (Regressionsfix, insb. Samsung-Tablets): - // • Die v2.7.37-Stille (Auto-Advance-Gap) bleibt erhalten, ist aber jetzt - // STRIKT auf den Auto-Advance nach einem natürlich beendeten Clip - // innerhalb DERSELBEN Tour beschränkt. Der verzögerte Gap-Job prüft vor - // dem Folge-Start, dass weder die Tour gewechselt wurde noch der Nutzer - // pausiert/gestoppt hat – sonst wird er verworfen. - // • Tour-Wechsel (selectTour) und Tour-Löschen (deleteTour) brechen einen - // anstehenden Gap-Job ab, damit kein verspäteter Start in der falschen - // bzw. gelöschten Tour feuert. Aktive Wiedergabe-Persistenz bleibt. - // • ManualAudioPlayer meldet Lade-/Prepare-/Start- und asynchrone - // MediaPlayer-Fehler jetzt über onError zurück. Schlägt der Start fehl - // (z. B. Samsung-SAF-/Codec-Problem), wird der "spielt"-Zustand sauber - // zurückgesetzt und eine Fehlermeldung angezeigt – statt still ohne Ton - // auf "spielt" zu verharren. - // • Erster manueller Start wird nie durch einen Gap-Job verzögert - // (unverändert) und kann nicht von einem alten Gap-Job überschrieben - // werden. + // v2.7.39 — Audio-Stabilitätsfix: + // • Die in v2.7.37 eingeführte feste Stille-Pause zwischen Clips und + // Wegpunkten im manuellen Tour-Play wurde entfernt. + // • Manueller Auto-Advance startet den nächsten aktiven Clip/Wegpunkt + // wieder direkt. Dadurch gibt es keinen verzögerten Coroutine-Job + // mehr, der auf Samsung-Geräten oder nach Tour-/GPS-State-Wechseln in + // veraltete Player-Zustände laufen kann. // • Keine Änderung an GPS-Wiedergabe, PTT, Atmo, Einzelclip-Vorschau, - // Hintergrund-Playback, Backup/Import, Lizenz oder Player-Auto-Open. - versionCode = 144 - versionName = "2.7.38" + // Hintergrund-Playback, Backup/Import, Lizenz, Karten/Explorer oder + // Player-Auto-Open. + versionCode = 145 + versionName = "2.7.39" } val storeFilePath = resolveSigningValue("storeFile", "GPS2AUDIO_RELEASE_STORE_FILE") diff --git a/app/src/main/kotlin/de/waypointaudio/viewmodel/WaypointViewModel.kt b/app/src/main/kotlin/de/waypointaudio/viewmodel/WaypointViewModel.kt index 1a94864..6cc966b 100644 --- a/app/src/main/kotlin/de/waypointaudio/viewmodel/WaypointViewModel.kt +++ b/app/src/main/kotlin/de/waypointaudio/viewmodel/WaypointViewModel.kt @@ -197,18 +197,14 @@ class WaypointViewModel(application: Application) : AndroidViewModel(application private val _manualPlaying = MutableStateFlow(false) val manualPlaying: StateFlow = _manualPlaying.asStateFlow() - // v2.7.37 — Kurze Stille zwischen Clips/Wegpunkten bei der manuellen - // Sequenz-Wiedergabe ("Hörbuch-Gefühl"). Nur im Auto-Advance-Pfad der - // Tour-/Sequenz-Wiedergabe; NICHT bei GPS, PTT, Atmo oder Einzelclip-Vorschau. - // Bewusst kein UI-Setting und keine Persistenz — fester interner Wert. - private var manualGapJob: Job? = null - - /** Bricht eine laufende Auto-Advance-Pause ab, sodass kein verzögerter - * Start nach Stop/Pause/Skip/Tour-Wechsel mehr feuert. */ - private fun cancelManualGap() { - manualGapJob?.cancel() - manualGapJob = null - } + // v2.7.39 — Die in v2.7.37 eingeführte feste Stille-Pause zwischen + // manuellen Clips/Wegpunkten ist aus Stabilitätsgründen wieder entfernt. + // Auf einigen Samsung-Geräten führte der verzögerte Auto-Advance zu + // uneindeutigem Player-State und konnte fälschlich "Audio konnte nicht + // abgespielt werden" auslösen. Die Methode bleibt als No-op bestehen, + // damit bestehende Stop-/Skip-/Tourwechsel-Pfade ohne strukturelles Risiko + // weiter denselben Aufräumaufruf verwenden können. + private fun cancelManualGap() = Unit /** Name of the waypoint currently loaded in the manual player (null = nothing loaded). */ private val _manualCurrentName = MutableStateFlow(null) @@ -601,10 +597,9 @@ class WaypointViewModel(application: Application) : AndroidViewModel(application if (safeTarget != de.waypointaudio.data.Waypoint.DEFAULT_TOUR_NAME) { _neutralExplorerMode = false } - // v2.7.38 — Audio-Zuverlässigkeit: Ein evtl. anstehender Auto-Advance- - // Gap-Job gehört zur alten Tour. Beim Tour-Wechsel abbrechen, damit kein - // verspäteter Start in der falschen Tour feuert. Aktiv laufende - // Wiedergabe bleibt davon unberührt (Persistenz, s. u.). + // v2.7.39 — Legacy-Aufräumaufruf bleibt erhalten, ist nach Entfernen + // der festen manuellen Pause aber ein No-op. Aktiv laufende Wiedergabe + // bleibt vom bloßen Tour-Wechsel weiterhin unberührt (Persistenz, s. u.). if (safeTarget != _selectedTour.value) { cancelManualGap() } @@ -978,9 +973,8 @@ class WaypointViewModel(application: Application) : AndroidViewModel(application val nextTour = current.firstOrNull() ?: Waypoint.DEFAULT_TOUR_NAME if (_selectedTour.value == tourName) { // Reset manual player state - // v2.7.38 — Auch den anstehenden Auto-Advance-Gap-Job abbrechen, - // sonst startet er 1,7 s nach dem Stop fälschlich erneut Audio - // gegen die gerade gelöschte Tour. + // v2.7.39 — Legacy-Aufräumaufruf; nach Entfernen der festen + // manuellen Pause gibt es keinen verzögerten Auto-Advance-Job mehr. cancelManualGap() manualPlayer.stop() _manualPlaying.value = false @@ -1857,17 +1851,6 @@ class WaypointViewModel(application: Application) : AndroidViewModel(application _singlePlayingActive.value = false } - // v2.7.37 — Während der Stille-Pause läuft kein MediaPlayer, der State - // ist aber "playing". Pause-Tippen soll dann die Auto-Advance-Pause - // abbrechen und nicht neu starten. - if (manualGapJob != null) { - cancelManualGap() - _manualPlaying.value = false - stopProgressTicker(keepSnapshot = true) - musicManager.afterWaypointAudio() - return - } - if (manualPlayer.isPlaying) { manualPlayer.pause() _manualPlaying.value = false @@ -2028,57 +2011,27 @@ class WaypointViewModel(application: Application) : AndroidViewModel(application return@play } val nextItem = nextSeq[nextIdx] - // v2.7.37 — Kurze Stille vor dem nächsten Eintrag (Hörbuch-Gefühl). - // Läuft als abbrechbarer Job: Pause/Stop/Skip/Tour-Wechsel rufen - // cancelManualGap() und verhindern so einen verspäteten Auto-Start. - // Die Sequenz wird NACH der Pause erneut ausgewertet, da sich der - // Zustand (PTT, gelöschte Wegpunkte, deaktivierte Clips) während der - // Wartezeit ändern kann. - // - // v2.7.38 — Audio-Zuverlässigkeit (Samsung-Regressionsfix): - // Die Gap ist STRIKT auf den Auto-Advance NACH einem natürlich - // beendeten Clip beschränkt. Sie darf den Folge-Clip nur dann - // starten, wenn währenddessen weder die Tour gewechselt wurde noch - // der Nutzer pausiert/gestoppt hat. Andernfalls wird der verzögerte - // Start verworfen, statt den Zustand fälschlich auf "spielt" zu - // lassen oder Audio in der falschen Tour zu starten. - val gapTour = _selectedTour.value - cancelManualGap() - manualGapJob = viewModelScope.launch { - delay(MANUAL_GAP_MS) - manualGapJob = null - // Tour gewechselt während der Stille → kein Auto-Advance. - // Laufende/zukünftige Wiedergabe der anderen Tour nicht stören; - // nur den eigenen verspäteten Start verwerfen. - if (_selectedTour.value != gapTour) { - return@launch - } - // Nutzer hat während der Stille pausiert/gestoppt - // (z. B. Pause-Tippen, Stop, neuer Single-Clip) → nicht starten. - if (!_manualPlaying.value || manualPlayer.isSingleMode) { - return@launch - } - if (pttManager.pttActive.value) { - _manualPlaying.value = false - stopProgressTicker(keepSnapshot = true) - return@launch - } - val seqAfterGap = playableSequence() - val resolvedIdx = seqAfterGap.indexOfFirst { it.compoundId == nextItem.compoundId } - if (resolvedIdx < 0) { - // Nächster Eintrag existiert nicht mehr → sauber stoppen. - _manualPlaying.value = false - _manualCurrentName.value = null - _currentPlayable.value = null - stopProgressTicker(keepSnapshot = false) - musicManager.afterWaypointAudio() - return@launch - } - val resolved = seqAfterGap[resolvedIdx] - _currentPlayable.value = resolved - _manualIndex.value = resolved.waypointIndex - 1 - playFromCurrentPlayable(autoAdvance = true) + // v2.7.39 — Keine feste Stille-Pause mehr: Der nächste Eintrag wird + // direkt gestartet. Dadurch gibt es keinen verzögerten Job mehr, der + // auf Samsung-Geräten oder nach Tour-/GPS-State-Wechseln in einen + // veralteten Player-Zustand feuern könnte. + if (!_manualPlaying.value || manualPlayer.isSingleMode) { + return@play } + val seqNow = playableSequence() + val resolvedIdx = seqNow.indexOfFirst { it.compoundId == nextItem.compoundId } + if (resolvedIdx < 0) { + _manualPlaying.value = false + _manualCurrentName.value = null + _currentPlayable.value = null + stopProgressTicker(keepSnapshot = false) + musicManager.afterWaypointAudio() + return@play + } + val resolved = seqNow[resolvedIdx] + _currentPlayable.value = resolved + _manualIndex.value = resolved.waypointIndex - 1 + playFromCurrentPlayable(autoAdvance = true) } startProgressTicker() } @@ -2733,11 +2686,5 @@ class WaypointViewModel(application: Application) : AndroidViewModel(application */ private const val PROGRESS_TICK_MS = 500L - /** - * v2.7.37 — Feste Stille-Pause vor dem automatischen Start des nächsten - * Eintrags in der manuellen Sequenz-Wiedergabe (Clip→Clip und - * Wegpunkt→Wegpunkt). Gibt der Wiedergabe ein hörbuchartiges Gefühl. - */ - private const val MANUAL_GAP_MS = 1700L } } diff --git a/releases/v2.7.39/GPS2Audio_v2_7_39_audio_pause_removed_handoff.md b/releases/v2.7.39/GPS2Audio_v2_7_39_audio_pause_removed_handoff.md new file mode 100644 index 0000000..b022132 --- /dev/null +++ b/releases/v2.7.39/GPS2Audio_v2_7_39_audio_pause_removed_handoff.md @@ -0,0 +1,40 @@ +# GPS2Audio v2.7.39 — Handoff + +## Ziel + +Stabilitätsfix für Samsung-Geräte: Die in v2.7.37 eingeführte feste Stille-Pause zwischen manuellen Clips/Wegpunkten wurde entfernt. Der Nutzen der Pause war rein kosmetisch, während sie auf einigen Geräten offenbar zu uneindeutigen Player-Zuständen und der sichtbaren Fehlermeldung „Audio konnte nicht abgespielt werden“ beitragen konnte. + +## Geänderte Dateien + +- `app/src/main/kotlin/de/waypointaudio/viewmodel/WaypointViewModel.kt` + - `manualGapJob` und `MANUAL_GAP_MS` wurden entfernt. + - Der Sonderfall „Play/Pause während der Stille-Pause“ wurde entfernt. + - Der manuelle Auto-Advance startet den nächsten aktiven Clip/Wegpunkt jetzt wieder direkt. + - `cancelManualGap()` bleibt als No-op bestehen, damit bestehende Stop-, Skip- und Tourwechsel-Pfade ohne strukturelles Risiko gleich bleiben. + +- `app/build.gradle.kts` + - `versionCode` von `144` auf `145` erhöht. + - `versionName` von `2.7.38` auf `2.7.39` erhöht. + - Changelog-Kommentar auf v2.7.39 aktualisiert. + +- `RELEASES.md` + - v2.7.39 als Audio-Stabilitätsfix dokumentiert. + +## Entscheidungslogik + +Die Pause wurde nicht als Benutzeroption beibehalten, sondern vollständig aus dem aktiven Ablauf entfernt. Dadurch gibt es keinen verzögerten Coroutine-Job mehr, der nach einem Tourwechsel, Stop/Pause oder auf Samsung-Geräten in einen veralteten Player-Zustand laufen kann. Der manuelle Modus spielt weiterhin alle aktivierten Clips und Waypoints der Reihe nach, nur ohne künstliche Pause. + +Die GPS-Wiedergabe nutzt weiterhin ihren eigenen Pfad und wurde in diesem Fix bewusst nicht verändert. + +## Build-Status in dieser Sandbox + +Der Quellcode-Fix ist vorbereitet. In der aktuellen Sandbox fehlen jedoch Android-SDK/Gradle-Toolchain und der Release-Keystore, daher konnte hier keine signierte updatefähige APK gebaut werden. Für den finalen Release-Build muss die bekannte GPS2Audio-Release-Buildumgebung beziehungsweise der Keystore erneut verfügbar sein. + +## Empfohlene Tests + +- Manueller Tour-Play mit mehreren Waypoints und mehreren Clips pro Waypoint. +- Manuelles Weiter/Zurück zwischen Clips und Waypoints. +- GPS-Trigger bei aktiver GPS-Wiedergabe auf einem Samsung-Tablet. +- Ungültige oder gelöschte Audioquelle: Fehlermeldung darf weiterhin erscheinen. +- PTT während laufender Waypoint-Wiedergabe. +- Atmo-Pause/Resume während Waypoint-Audio. diff --git a/releases/v2.7.39/GPS2Audio_v2_7_39_audio_pause_removed_source.zip b/releases/v2.7.39/GPS2Audio_v2_7_39_audio_pause_removed_source.zip new file mode 100644 index 0000000..ff9ab34 Binary files /dev/null and b/releases/v2.7.39/GPS2Audio_v2_7_39_audio_pause_removed_source.zip differ