• v2.1.0 b2aa1c167c

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

    GPS2Audio v2.1.0 – PTT Recorder – Handoff

    Artifacts

    Path Size Notes
    /home/user/workspace/GPS2Audio_v2_1_0_ptt_recorder_signed.apk 14.5 MB release-signed
    /home/user/workspace/GPS2Audio_v2_1_0_ptt_recorder_source.zip 230 KB source bundle
    /home/user/workspace/GPS2Audio_v2_1_0_ptt_recorder_handoff.md this document

    SHA-256 (APK): 181fa1846bc384cae8b7e2ad72db8ea1943c881b8b7878cd50d40205b929705a

    Build / verification

    package:        de.waypointaudio
    versionCode:    37            (was 36 in v2.0.8 – satisfies "> 36")
    versionName:    2.1.0
    compileSdk:     35   minSdk: 26   targetSdk: 35
    
    gradle :app:assembleRelease  →  BUILD SUCCESSFUL (1m 47s, 43 tasks)
    
    apksigner verify --print-certs
      Signer #1 DN:        CN=GPS2Audio, OU=NesoHub, O=GPS2Audio, L=Germany, C=DE
      Signer #1 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
      ← matches GPS2Audio_release_keystore.jks fingerprint (verified)
    

    What was implemented

    1. PTT Settings (AudioRoutingSettings)

    • New persisted fields on the existing DataStore (audio_routing_settings):
      • liveOutputEnabled: Boolean — default true (matches v2.0.8 PTT behaviour)
      • recordingEnabled: Boolean — default false
      • recordingFormat: PttRecordingFormat — default M4A_AAC
    • Validation guard in LivePttManager.saveRoutingSettings() and in the
      Settings dialog: if the user turns both off, Live is forced back on
      (toast hint shown in the dialog).
    • The new PttSettingsDialog (LivePttCard.kt) replaces the v2.0.8
      AudioDeviceDialog and merges both responsibilities:
      • Toggles for Live-Ausgabe and Aufnahme speichern (with German
        helper text).
      • Format selector: M4A/AAC active; WAV shown but radio button
        disabled, label reads "WAV (in v2.1.0 nicht verfügbar)" — see
        Limitations below.
      • Existing input/output device dropdowns, refresh button and hints
        kept exactly as in v2.0.8.

    2. Recording engine (PttRecorder)

    • Wraps Android MediaRecorder (MIC source, MPEG-4 container, AAC
      encoder, mono, 44.1 kHz, 96 kbps). API ≥ 31 uses
      MediaRecorder(context), below uses the deprecated default constructor.
    • Files saved to <app filesDir>/recordings/PTT_<yyyyMMdd_HHmmss>.m4a.
    • On stop() reads the duration via MediaMetadataRetriever and returns
      a fully populated PttRecording value (id, displayName, filePath,
      file:// URI, format, createdAt, durationMs, optional lat/lon).
    • cancel() releases the recorder and deletes the partial file.

    3. Recording library (PttRecording + PttRecordingStore)

    • New DataStore (ptt_recordings) with a single JSON-encoded list of
      recording metadata entries. Gson is already a project dependency.
    • Operations: recordings: Flow<List<PttRecording>> (always sorted by
      createdAt desc), add, delete, rename.

    4. PTT lifecycle (LivePttManager)

    • startPtt() now reads the saved settings and:
      1. Pauses Atmo + waypoint audio (unchanged from v2.0.8).
      2. Starts the MediaRecorder first (if recordingEnabled). Sets
        recordingActive=true.
      3. Starts the existing LivePttService foreground service (if
        liveOutputEnabled). The service still uses
        AudioSource.VOICE_COMMUNICATION so on most devices both clients
        share the mic; if either init fails the other continues and a
        status message is surfaced.
    • stopPtt() stops the live service, finalises the recording on a
      coroutine (fetches fusedLocation.lastLocation with a 1.5 s timeout
      to attach lat/lon), persists the metadata in PttRecordingStore and
      posts the result to lastRecording: StateFlow<PttRecording?>.
    • New helper methods: deleteRecording, renameRecording,
      clearLastRecording.
    • Recording state surfaced via recordingActive: StateFlow<Boolean>
      for the UI to show a red dot/pill in the PTT card.

    5. UI changes

    • LivePttCard (ui/LivePttCard.kt): adds a LibraryMusic icon
      button next to the Settings icon that opens the recordings library.
      Status row shows "Aufnahme läuft" + red FiberManualRecord dot when
      recording. A small "Live + Aufnahme" / "Nur Aufnahme" / "Nur Live"
      summary line appears under the status. Echo warning is suppressed
      when Live is off.
    • PttSettingsDialog (same file): merged dialog described above.
    • PostRecordingDialog (ui/PttRecordingDialogs.kt): shown
      automatically whenever lastRecording becomes non-null. Buttons:
      • Als Waypoint-Audio zuweisen → opens WaypointChooserDialog
      • Löschen → calls pttManager.deleteRecording and clears the
        last-recording state
      • OK → dismisses without action
    • PttRecordingsLibraryDialog: full bottom-sheet style dialog with
      scrollable list of all recordings; per-item "Zuweisen" and "Löschen"
      buttons with confirm dialog for delete.
    • WaypointChooserDialog: lists every waypoint of the currently
      selected tour; tapping a card sets waypoint.soundUri = recording.uri
      and waypoint.soundName = recording.displayName via
      WaypointViewModel.upsert(). This is the existing assignment path,
      so the audio is immediately played on waypoint entry like any other
      sound.

    6. WaypointListScreen integration

    • Passes onOpenRecordings callback to LivePttCard.
    • Renders the post-recording dialog, recordings-library dialog and
      waypoint-chooser dialog at the end of the screen so they overlay
      the main UI.

    7. Backup

    • The existing BackupExportManager already collects every
      waypoint.soundUri into media/waypoints/<tour>/... in the backup
      ZIP. Because assigned recordings are written into that exact field
      with a file:// URI rooted in <filesDir>/recordings/, assigned
      PTT recordings flow through the existing media backup unchanged.
    • Unassigned (orphan) recordings are not copied to a backup —
      there is no recordings section in data/...json. This is documented
      as a limitation; adding it would require touching the import path as
      well. See Limitations below.

    Preserved v2.0.8 features

    • Stream playlist (multi-URL Atmo, shuffle, REPEAT_MODE_ALL): untouched.
    • AtmoPlaybackService foreground media-playback architecture: untouched.
    • PTT audio priority chain (PTT pauses Atmo and waypoint audio,
      AtmoResumeManager + WaypointAudioInterruptionCoordinator) — only the
      invocation site in LivePttManager was edited; the contract is the
      same.
    • Backup format, GPS track drafts, tour reorder, waypoint editor,
      drafts archive, map provider selector, version-notice dialog — all
      untouched.

    Limitations / known gaps

    1. WAV format is intentionally not implemented. The selector in the
      PTT settings dialog shows it disabled with a German label. Reason:
      keeping the PTT live path stable was the top priority; M4A/AAC is
      the supported codec via MediaRecorder and works on every device
      from API 26 upwards.
    2. Live + Aufnahme simultaneously should work on most devices —
      AudioRecord(VOICE_COMMUNICATION) and MediaRecorder(MIC) are
      separate clients and Android typically allows both to read from the
      mic in parallel. On vendor-specific builds (some Huawei / Samsung
      devices have been reported in the wild) one of the two may fail to
      init. The manager logs the failure, surfaces a status message and
      keeps the one that did start. If a user encounters this, the
      workaround is to disable Live in the PTT settings.
    3. Orphan recordings are not auto-backed-up. Only recordings that
      the user assigned to a waypoint end up in the backup ZIP (via the
      existing soundUri enumeration). Recordings that live only in the
      library remain in <filesDir>/recordings/ on the device.
    4. Recording playback inside the library list is not implemented.
      Users can verify a recording by assigning it to a waypoint or by
      triggering the post-recording dialog directly after capture. Adding
      in-place playback would require an extra MediaPlayer instance and
      was out of scope for the "stable PTT recorder" objective.
    5. Renaming a recording is supported by the store
      (renameRecording) but no UI is wired up yet — recordings carry
      the PTT_<timestamp> default name.

    Files added / changed

    Added:
      app/src/main/kotlin/de/waypointaudio/data/PttRecording.kt
      app/src/main/kotlin/de/waypointaudio/service/PttRecorder.kt
      app/src/main/kotlin/de/waypointaudio/ui/PttRecordingDialogs.kt
    
    Changed:
      app/build.gradle.kts                  (versionCode 36→37, versionName 2.0.8→2.1.0)
      app/src/main/kotlin/de/waypointaudio/data/AudioRoutingSettings.kt
      app/src/main/kotlin/de/waypointaudio/service/LivePttManager.kt
      app/src/main/kotlin/de/waypointaudio/ui/LivePttCard.kt          (PttSettingsDialog merged)
      app/src/main/kotlin/de/waypointaudio/ui/WaypointListScreen.kt   (3 dialog hooks)
      app/src/main/res/values/strings.xml                              (~25 new strings)
    

    No manifest changes were needed: RECORD_AUDIO, FOREGROUND_SERVICE,
    FOREGROUND_SERVICE_MICROPHONE were already declared for v2.0.8 PTT.

    Test plan (manual)

    1. Open PTT settings. Verify defaults: Live = ON, Aufnahme = OFF.
    2. Try turning both off → Live should snap back ON, German hint shown.
    3. Enable Aufnahme, leave Live on, save. Trigger PTT (start), speak a
      few seconds, stop. Verify:
      • PTT card shows red mic + "Aufnahme läuft" while active.
      • Post-recording dialog appears with file name + duration (+ GPS if
        permissions granted).
      • "Als Waypoint-Audio zuweisen" → list shows waypoints of current
        tour; tap one → confirmation.
      • Enter that waypoint on the map → recorded audio plays.
    4. Open library via the headphones icon → recording is listed → delete
      it → file and metadata both gone.
    5. Disable Live (Aufnahme only), trigger PTT → only recording happens,
      no echo, status row shows red dot. Verify Atmo pauses while
      recording and resumes on stop.
    6. Trigger Atmo with stream playlist → start PTT → Atmo pauses → stop
      PTT → Atmo resumes (regression check for v2.0.8 behaviour).
    7. Backup export with "Medien/Atmo" enabled → unzip → assigned
      recording appears under media/waypoints/<tour>/....

    Build status — final

    • Result: BUILD SUCCESSFUL (gradle 8.9, AGP from project,
      compileSdk 35, JDK 17).
    • APK signature: verified, certificate matches the v2.0.x release
      keystore fingerprint 7B:0E:A4:D5:…:BB:46.
    • APK metadata: de.waypointaudio versionCode 37 versionName
      2.1.0.
    • Tests: project contains no unit/instrumentation tests; manual
      verification described above. Runtime testing on a device was not
      possible from this build host.
    Downloads