Files
FreeTLab 977cbcd8cc docs(openspec): archive android-auto-local-music Phase 1
Merges its delta requirements into the android-auto-media base spec.
Phases 2 (metadata/sort/filter/art) and 3 (subfolder scoping/shuffle)
remain planned future work.
2026-07-19 20:37:54 +02:00

5.2 KiB

Proposal: Android Auto Local Music — Phase 1 (Foundational Plumbing)

Intent

Users want to browse and play local music files (nested device folders) through Android Auto, mirroring the proven radio-station browse tree. This introduces THREE new domains at once — SAF/scoped-storage folder access, local audio metadata, and a new track model — so it MUST ship phased. This proposal scopes Phase 1 only: the foundational, independently shippable plumbing that gets a user-picked folder tree browsable and playable in the car, verified before any metadata/UX polish is layered on.

Scope

In Scope (Phase 1)

  • SAF folder pick via ACTION_OPEN_DOCUMENT_TREE, persisted URI permission, graceful revocation handling (mirror tolerant-read precedent).
  • Android storage permission declaration + request flow (READ_MEDIA_AUDIO / SAF grant), replacing the currently-empty manifest state.
  • Recursive DocumentFile tree traversal enumerating audio files under the picked root (new dependency, e.g. shared_storage, or hand-rolled platform channel).
  • New PistaLocal model (file URI, display name from filename, folder path) — NOT an Emisora extension.
  • Android Auto browse tree extension: new root folder + folder/leaf MediaItems via the existing ConstructorArbolAuto/FuenteEmisorasAuto pattern, id-prefix routing (pista:, carpeta_local:), respecting the _maxItemsPorCarpeta = 50 cap.
  • Play-by-id + standard transport (play/pause/stop) by REUSING the existing audio_service/PluriWaveAudioHandler pipeline.
  • Album art: existing on-brand fallback rotation via artUri (no embedded art yet).

Out of Scope (deferred)

  • Phase 2: metadata extraction (title/artist/embedded art/bitrate/sample-rate), sort & filter by name/quality, embedded album art.
  • Phase 3: subfolder scoping refinements, shuffle.
  • Live/car-side waveform — architecturally impossible under legacy MediaBrowserService (confirmed). Phone-side waveform reuse of visualizador_audio.dart is a separate future follow-up.
  • EQ — already shipped, direct reuse, no new work.

Capabilities

New Capabilities

  • local-music-browse: SAF folder access, persisted permission, recursive audio-file enumeration, PistaLocal model, and Android Auto browse/play of local files.

Modified Capabilities

  • android-auto-navigation: browse tree gains a local-music root folder alongside existing station folders (confirm exact spec name in openspec/specs/ during sdd-spec).

Approach

Extend the existing screaming-architecture seams rather than fork them. Recursive traversal + PistaLocal mapping are pure Dart (fully unit-testable). SAF platform-channel/permission/manifest work is static-review-only (no Android build/DHU in this env — same precedent as all prior native Android Auto work). Browse tree reuses ConstructorArbolAuto shape; playback reuses PluriWaveAudioHandler unchanged, handing a local file URI to just_audio exactly as station URLs are handed today.

Affected Areas

Area Impact Description
pubspec.yaml Modified Add SAF/traversal dep; enable permission_handler
android/app/src/main/AndroidManifest.xml Modified Declare READ_MEDIA_AUDIO / SAF permission
android/.../MainActivity.kt Modified Extend platform channel for tree traversal (reuse file_actions pattern)
lib/modelos/pista_local.dart New PistaLocal model
lib/servicios/ (folder source) New SAF pick + persisted URI + recursive enumeration
lib/servicios/navegacion_auto.dart Modified Local-music root folder + pista:/carpeta_local: routing

Risks

Risk Likelihood Mitigation
SAF permission revoked outside app Med Tolerant reads; degrade to empty-but-valid tree (existing precedent)
Large nested trees exceed row caps Med Enforce _maxItemsPorCarpeta; defer pagination story to a later phase
SAF/native code not runtime-testable here High Static-review-only; isolate pure-Dart logic for full unit coverage
Scope creep pulling metadata/shuffle into Phase 1 Med Hard phase boundary; metadata is Phase 2

Rollback Plan

Additive change behind a new browse root. Revert by removing the local-music root folder registration + id-prefix routing in navegacion_auto.dart, the new model/service files, and the manifest/pubspec additions. Existing station browse/play is untouched, so rollback removes only new surface with no regression to shipped behavior.

Dependencies

  • A SAF tree-traversal package (e.g. shared_storage) OR a hand-rolled DocumentFile platform channel — decide in sdd-design.
  • permission_handler re-enabled in pubspec.yaml.

Success Criteria

  • User can pick a root folder; permission persists across app restarts.
  • Nested audio files enumerate recursively into PistaLocal instances (unit-tested).
  • A local-music root folder appears in Android Auto with browsable subfolders and playable track leaves.
  • Tapping a track plays it through the existing pipeline with working play/pause/stop and fallback album art.
  • No regression to existing station browse/play; pure-Dart logic fully unit-tested, native code static-reviewed.