# 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 (A–F, 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.