Files
pluriwave/openspec/changes/archive/2026-07-19-android-auto-local-music-phase2/proposal.md
T
FreeTLab 85dd043cd4 docs(openspec): archive android-auto-local-music-phase2
Merges its delta requirements into the android-auto-media base spec.
Completes Phase 2; only Phase 3 (subfolder scoping, shuffle) remains.
On-device validation of the native metadata path is still an
outstanding pre-release gate across all local-music phases shipped
today.
2026-07-19 23:59:21 +02:00

68 lines
5.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Proposal: Android Auto Local Music — Phase 2 (Real Metadata, Sort, Name Buckets)
## Intent
Phase 1 shipped browsable SAF folders with filename-only titles and generic placeholder art. Phase 2 delivers the deferred polish: real embedded metadata (title/artist/bitrate/sample-rate/album art), sort by name and audio quality, and name-based navigation — without breaking Phase 1's lazy paging philosophy or its pure-SAF, zero-dangerous-permission stance.
## Scope
### In Scope
- **A. Real metadata** — resolve embedded title/artist/bitrate/sample-rate + embedded album art for tracks on the page being shown/played, replacing filename title and `station_art_*` placeholder.
- **B. Sort** — name (filename, already cheap) and quality (bitrate desc, reusing the `ordenarEmisoras(..., OrdenEmisoras.calidad)` comparator shape) exposed as separate navigable sort-mode entries (Android Auto cannot re-sort in place).
- **C. Name navigation** — alphabetical navigable buckets (AF, etc.) as the realistic, browse-tree-native form of "filter by name".
- New translatable strings (sort-mode labels, "unknown metadata" text) added to ALL 13 ARB files from the start.
### Out of Scope
- Phase 3: subfolder scoping, shuffle, transport-control polish.
- Live text-search box (impossible under legacy MediaBrowserService).
- Voice search via `audio_service.onSearch` — flagged as Phase 3 candidate.
## Capabilities
### New Capabilities
- None.
### Modified Capabilities
- `local-music-browse`: gains metadata resolution, quality sort, alphabetical buckets (confirm exact spec name in `openspec/specs/` during sdd-spec).
- `android-auto-navigation`: local branch gains sort-mode + bucket entries.
## Approach — Metadata Extraction Decision (the one open technical call)
**DECISION: native `android.media.MediaMetadataRetriever` on the existing `pluriwave/file_actions` channel. No new pub dependency.**
Why, versus the Dart packages:
- Phase 1 tracks are `content://` SAF URIs (`buildDocumentUriUsingTree`), NOT file paths and NOT guaranteed MediaStore-indexed. `on_audio_query` is MediaStore-based and would (a) miss files outside standard media folders reached via an arbitrary SAF tree and (b) reintroduce `READ_MEDIA_AUDIO`, the dangerous permission Phase 1 deliberately rejected — REJECT.
- `audio_metadata_reader` / `flutter_media_metadata` need a `File`/bytes; SAF `content://` gives neither, so we'd still need native byte-streaming — more complexity, no less native code.
- `MediaMetadataRetriever.setDataSource(context, uri)` accepts a `content://` URI directly, returns TITLE/ARTIST/BITRATE + `getEmbeddedPicture()` bytes, reuses the existing `contentResolver`, adds zero deps and zero permissions. Consistent with the Phase 1 hand-rolled-channel ADR.
**DECISION: parse-on-demand + bounded in-memory session cache. NOT eager-scan-and-cache** — eager scan is slow, needs a persistence store, and goes stale when the user adds/removes files. On-demand is always fresh and matches paging. Design formalizes cache bounds/eviction.
Tension to formalize in sdd-design: **sort-by-quality needs bitrate for the whole folder**, so it must resolve metadata for all tracks in that folder (not just the visible page). Mitigation: name sort stays metadata-free (filename only); quality-sort cost is paid only when the user opts into that entry.
## Affected Areas
| Area | Impact | Description |
|------|--------|-------------|
| `MainActivity.kt` (`file_actions`) | Modified | New `readAudioMetadata(treeUri, documentId)` via MediaMetadataRetriever; embedded-art delivery (cache file + existing FileProvider) — static-review-only |
| `lib/modelos/pista_local.dart` | Modified | Add artist/bitrate/sampleRate/artUri fields |
| `lib/servicios/musica_local_auto.dart` | Modified | Metadata channel call + session cache |
| `lib/servicios/navegacion_auto.dart` | Modified | Metadata-backed title/subtitle/art; sort-mode + bucket entries |
| `lib/l10n/*.arb` (13) | Modified | Sort/metadata labels |
## Risks
| Risk | Likelihood | Mitigation |
|------|------------|------------|
| MMR sample-rate key is API 31+ | High | Degrade gracefully, reuse existing "unknown" handling |
| `artUri` needs a static URI, not bytes | High | Persist embedded art to cache dir, serve via existing FileProvider |
| Native untestable here (no build/DHU) | High | Static-review-only; keep sort/bucket/mapping logic pure Dart |
| Quality-sort forces full-folder parse | Med | Opt-in cost only when that entry is chosen |
| ARB drift across 13 locales | Med | All 13 in tasks from the start |
## Rollback Plan
Additive over Phase 1. Revert by removing `readAudioMetadata`, restoring filename title + placeholder art, and dropping sort-mode/bucket entries. Phase 1 browse/play untouched.
## Dependencies
- None new (native MMR, no pub package).
## Success Criteria
- [ ] Tracks show embedded title/artist and real album art when present, filename/placeholder fallback otherwise.
- [ ] Name and quality sort available as navigable entries; quality orders by bitrate desc.
- [ ] Alphabetical buckets navigable within a folder.
- [ ] Metadata resolved on-demand only; no eager scan, no persistence staleness.
- [ ] All 13 ARB files carry new strings; no regression to Phase 1 or stations; pure Dart unit-tested, native static-reviewed.