# 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 `MediaItem`s 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.