Release GPS2Audio v2.7.39 remove manual playback gap

This commit is contained in:
Perplexity Computer
2026-06-22 11:07:32 +00:00
parent a3382bb0aa
commit 8ba9adbe48
6 changed files with 125 additions and 119 deletions
+11 -33
View File
@@ -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.37Hö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.39Audio-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")
@@ -197,18 +197,14 @@ class WaypointViewModel(application: Application) : AndroidViewModel(application
private val _manualPlaying = MutableStateFlow(false)
val manualPlaying: StateFlow<Boolean> = _manualPlaying.asStateFlow()
// v2.7.37Kurze 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.39Die 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<String?>(null)
@@ -601,10 +597,9 @@ class WaypointViewModel(application: Application) : AndroidViewModel(application
if (safeTarget != de.waypointaudio.data.Waypoint.DEFAULT_TOUR_NAME) {
_neutralExplorerMode = false
}
// v2.7.38Audio-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.39Legacy-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.38Auch 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.39Legacy-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
}
}