# GPS2Audio v2.7.37 — Handoff Small internal feature on top of v2.7.36: a short **silent pause between items during manual sequence playback**, so tour playback feels like an audiobook. No UI, no setting, no persistence, no data-model change. ## Goal In manual (UI-driven) sequence playback, insert a fixed ~1.7 s silence before automatically starting the next item — both **clip → clip within a waypoint** and **last clip of one waypoint → first clip of the next**. The delay must not apply to GPS-triggered playback, PTT, Atmo, or isolated single-clip preview, and must be fully cancellable. ## Where the delay lives Everything is in one method: `WaypointViewModel.playFromCurrentPlayable(autoAdvance)`. The manual sequence auto-advance was already centralized in the `onCompletion` callback of `manualPlayer.play(ctx, item.uri) { … }`. Both clip→clip and waypoint→waypoint advance flow through the **same** branch (it just takes the next entry of the flat `playableSequence()`), so a single insertion point covers both cases the requirement asked for. Previously that branch computed the next item and called `playFromCurrentPlayable(autoAdvance = true)` **immediately**. Now it instead launches a cancellable gap job: ```kotlin cancelManualGap() manualGapJob = viewModelScope.launch { delay(MANUAL_GAP_MS) // MANUAL_GAP_MS = 1700L manualGapJob = null if (pttManager.pttActive.value) { … bail, stay paused … } val seqAfterGap = playableSequence() // re-evaluate val resolvedIdx = seqAfterGap.indexOfFirst { it.compoundId == nextItem.compoundId } if (resolvedIdx < 0) { … sequence changed → stop cleanly … } _currentPlayable.value = seqAfterGap[resolvedIdx] _manualIndex.value = … playFromCurrentPlayable(autoAdvance = true) } ``` Scope guarantees (unchanged from before, verified): - The delay is **only** in the `autoAdvance = true` branch. The `autoAdvance = false` branch (used internally) returns early before reaching it. - **Single-clip preview** uses `manualPlayer.playSingle(...)` via `manualPlaySingle` / `manualPlaySingleClip` — a different code path that never calls `playFromCurrentPlayable(autoAdvance = true)`, so it gets no gap. - **GPS playback** uses the separate `AudioPlayer` in the location service, not `ManualAudioPlayer`, and is untouched. - **PTT / Atmo** are unaffected; PTT is additionally re-checked after the delay so a PTT press during the gap suppresses the next clip. ## Cancellation behavior A single `private var manualGapJob: Job?` tracks the pending pause; `cancelManualGap()` cancels and clears it. It is cancelled at every point that makes a new playback decision or stops playback, so **no delayed auto-start ever fires after the user acts**: - `startPlayableAt(...)` — the common entry for **Next/Skip, Previous, Play-at, and Resume**. Pressing Next/Skip during the gap cancels it and starts the chosen item immediately (skip-the-delay requirement). - `manualPlayPause()` — if a gap is pending, the player shows "playing" but no MediaPlayer is running; a pause tap now explicitly cancels the gap and stays paused (does **not** restart). This is a new guard added for the gap. - `manualStop()` — stop cancels the gap. - `manualPlaySingleClip(...)` — single-clip preview cancels the gap before taking over, so a pending sequence advance can't overwrite the preview. - Tour create/delete reset blocks (`addTour` / `deleteTour` selected-tour reset) — cancel the gap alongside `manualPlayer.stop()`. - `onCleared()` — the gap runs in `viewModelScope`, which is cancelled automatically when the ViewModel is cleared (no explicit call needed). After the delay elapses, the next item is **re-resolved** from a fresh `playableSequence()` rather than trusting the pre-delay snapshot, so a waypoint or clip deleted/deactivated during the pause is handled cleanly (stops instead of crashing). ## What was NOT changed Backup/import, license gate, GPS playback, PTT, Atmo, multi-clip, background playback, the v2.7.32 player auto-open / tour ordering, and all UI (v2.7.33–35). No new imports were needed (`Job`, `delay`, `viewModelScope`, `launch` already imported). No UI files touched. Note on background playback: between clips the `ManualPlaybackService` foreground service already stops on each clip's completion and restarts on the next clip's load — this was the existing per-clip behavior. The gap simply lengthens that existing inter-clip moment by ~1.7 s; it introduces no new service lifecycle. ## Changed files 1. `app/src/main/kotlin/de/waypointaudio/viewmodel/WaypointViewModel.kt` - Added `manualGapJob` field + `cancelManualGap()` helper; `MANUAL_GAP_MS = 1700L` constant in the companion object. - `playFromCurrentPlayable()`: auto-advance now goes through a cancellable `delay(MANUAL_GAP_MS)` job that re-evaluates the sequence and respects PTT. - Added `cancelManualGap()` calls in `startPlayableAt`, `manualPlayPause` (with a new during-gap pause guard), `manualStop`, `manualPlaySingleClip`, and the `addTour`/`deleteTour` manual-player reset blocks. 2. `app/build.gradle.kts` - `versionCode 142 → 143`, `versionName 2.7.36 → 2.7.37`, changelog comment. ## Build & test - Command: `./gradlew :app:assembleRelease --no-daemon` - Result: **BUILD SUCCESSFUL** (~53s). Only pre-existing deprecation warnings elsewhere; none from the changed code. - Signed with the release config from `keystore.properties`. Verified signer SHA-256 `490511f3…d4e5` (same key as prior releases). - `aapt2 badging`: `versionCode='143' versionName='2.7.37'`. - No unit/instrumented test suite in the project. Verification was build + signature + version + code-path review. **Not** exercised on a device/emulator in this environment. ### How to verify on device Open a tour with multiple clips/waypoints and start manual playback (player bar Play / a waypoint's Play). Expected: ~1.7 s of silence before each next clip and each next waypoint begins. During that silence: pressing **Next** jumps to the next item immediately; pressing **Pause/Stop** keeps it silent and does not auto-start. Single-clip preview (Play on a single clip in the edit dialog) plays exactly one clip with no gap and no advance. GPS-triggered playback is unchanged. ## Artifacts - `GPS2Audio_v2.7.37_universal-release-signed.apk` — signed universal release APK (single APK; no ABI splits configured). - `GPS2Audio_v2.7.37_src.zip` — clean source. Excludes `.gradle`, `.idea`, `.kotlin`, all `build/` dirs, `.git`, and binaries (`.apk/.aab/.jks/.keystore/.so` and raster images). Verified: changed files present, no excluded artifacts inside. **Note:** `keystore.properties` and `local.properties` text files are still included. `keystore.properties` carries the signing path/passwords — **strip it before distributing the source externally.**