Files
pluriwave/openspec/changes/archive/2026-07-19-android-auto-local-music/proposal.md
T
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

72 lines
5.2 KiB
Markdown

# 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.