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.
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
# Archive Report: Android Auto Local Music — Phase 1
|
||||
|
||||
**Date Archived**: 2026-07-19
|
||||
**Change**: android-auto-local-music
|
||||
**Verification Status**: PASS WITH WARNINGS (0 CRITICAL, 1 WARNING, 1 SUGGESTION)
|
||||
**Delivery Mode**: Single PR with `size:exception` (commit 6ae7e37, all changes staged)
|
||||
|
||||
## Summary
|
||||
|
||||
Phase 1 of the local-music browsing feature (foundational plumbing) has been successfully implemented, verified, and archived. The feature enables users to pick a local-music root folder via SAF, browse nested folders recursively in Android Auto, and play audio files through the existing pipeline. All Phase 1 scope (SAF access, recursive tree traversal, playback integration) is complete and tested. Phases 2 and 3 (metadata extraction, sort/filter, subfolder refinements, shuffle) remain explicitly deferred as planned future work.
|
||||
|
||||
## Verification Results
|
||||
|
||||
**Test Evidence**: 91/91 tests passing (66 navigation tests + 4 audio-file tests + 21 regression tests)
|
||||
|
||||
**Spec Compliance**: All 4 ADDED and 1 MODIFIED requirements implemented and verified:
|
||||
- Local Music Root Access and Permission Persistence: cold-start safe, graceful degradation on revoked permission
|
||||
- Local Music Browsable Tree: 5-folder root (new `Musica Local` folder 4th, before `Ecualizador`), recursive `carpeta_local:`/`pista:` browsing, empty folders return `[]`
|
||||
- Local Track Playback Reuses Existing Pipeline: `pista:` playback routed through same `playMediaItem` seam as stations, EQ regression guard passed
|
||||
- Local Music Folder Item Cap: 50-item cap enforced, alphabetically sorted
|
||||
- Browsable Media Tree (MODIFIED): root now returns 5 folders (was 4)
|
||||
|
||||
**Native Code**: Static-review only (Android build/DHU not available in this environment, per project precedent). `MainActivity.kt` additions validated structurally: SAF APIs correct, cursor management sound, result handling explicit, no resource leaks detected.
|
||||
|
||||
**Files Changed**: 8 tracked files (1046 insertions, 29 deletions), 3 new untracked files (267 lines), within estimate.
|
||||
|
||||
## Warnings and Follow-up
|
||||
|
||||
**WARNING (FIXED)**: L10n locale completeness — 11 non-English/non-Spanish `.arb` files were initially skipped with an inaccurate justification. This was **flagged in verify-report.md and subsequently fixed in commit 6ae7e37**, which added all 13 locale translations before archival. All 13 locale files now contain the new `localMusicSectionTitle`, `localMusicSectionDescription`, etc. keys. Status: RESOLVED.
|
||||
|
||||
**SUGGESTION (LOW-PRIORITY FOLLOW-UP)**: Settings UI folder-path display shows raw SAF URI (`content://com.android.externalstorage.documents/tree/...`) instead of a human-friendly path, unlike the `_SeccionGrabaciones` precedent. This is a cosmetic UX issue only — Phase 1 spec does not require friendly display. Recommended for Phase 2 polish, does not block archival.
|
||||
|
||||
**No CRITICAL issues found.**
|
||||
|
||||
## Merged Specification
|
||||
|
||||
The delta spec requirements (4 ADDED + 1 MODIFIED) have been merged into the base capability spec at `openspec/specs/android-auto-media/spec.md`. The four new requirements (Local Music Root Access, Browsable Tree, Playback Pipeline Reuse, Folder Item Cap) are now part of the permanent specification. The modified "Browsable Media Tree" requirement now documents the 5-folder root structure as the new norm.
|
||||
|
||||
## Archived Artifacts
|
||||
|
||||
| Artifact | Location | Purpose |
|
||||
|----------|----------|---------|
|
||||
| proposal.md | `openspec/changes/archive/2026-07-19-android-auto-local-music/` | Original proposal, scope, risks, success criteria |
|
||||
| design.md | Same | Architecture decisions, technical approach, interfaces |
|
||||
| tasks.md | Same | 12 task groups, grounding notes, TDD cycles, review workload forecast |
|
||||
| apply-progress.md | Same | Completion record, TDD evidence table, deviations, risks |
|
||||
| verify-report.md | Same | Test results, spec compliance verification, hygiene checks |
|
||||
| spec.md (delta) | `openspec/changes/archive/2026-07-19-android-auto-local-music/specs/android-auto-media/` | Delta spec: 4 ADDED + 1 MODIFIED requirements |
|
||||
|
||||
**Change folder archival**: Original change folder at `openspec/changes/android-auto-local-music/` remains in place and must be manually deleted by orchestrator (this tool cannot delete directories). All working changes are staged for commit in git.
|
||||
|
||||
## Phase 1 Scope: Complete
|
||||
|
||||
**Implemented**:
|
||||
- SAF folder picking via `ACTION_OPEN_DOCUMENT_TREE`, persisted URI permission
|
||||
- Graceful cold-start handling (no re-prompt if permission lost or never granted)
|
||||
- Recursive `DocumentFile` traversal: pure Dart `PistaLocal` model, lazy per-folder native enumeration
|
||||
- Android Auto browse tree extension: new `Musica Local` root folder, `carpeta_local:` folder ids, `pista:` track ids
|
||||
- Playback integration: local tracks play through existing pipeline, EQ applies, fallback art rotates
|
||||
- 50-item folder cap, alphabetical sort, filename-based titles (extension stripped for tracks only)
|
||||
- Phone-side settings UI: `_SeccionMusicaLocal` folder picker, SharedPreferences persistence
|
||||
- L10n support: all 13 locale files translated (English, Spanish, Russian, Chinese, Japanese, Portuguese, French, Hindi, Indonesian, Italian, German, Arabic, Bengali)
|
||||
|
||||
**Not in Phase 1** (explicitly deferred):
|
||||
- Metadata extraction: no title/artist/album art/bitrate/sample-rate beyond raw filename
|
||||
- Sort/filter: no user-controlled ordering or filtering
|
||||
- Subfolder scoping refinements: simple recursive model only
|
||||
- Shuffle: no shuffle implementation
|
||||
- Embedded album art: fallback rotation only
|
||||
- Phone-side waveform: `visualizador_audio.dart` reuse deferred
|
||||
|
||||
## Risk Summary
|
||||
|
||||
**Unverified at runtime** (expected per project precedent for native Android work):
|
||||
- `MainActivity.onActivityResult` new override: carefully written against documented SAF patterns, structurally sound, reviewer attention flagged
|
||||
- `DocumentsContract` MIME filtering and permission persistence: standard API usage, unexercised against real SAF provider in this environment
|
||||
|
||||
**Mitigated**:
|
||||
- Cold-start/revoked-permission handling: explicit tests + try/catch wrapping, validated to degrade safely
|
||||
- EQ regression: structural proof that local playback uses same seam as station playback
|
||||
- Collision-free media ids: explicit tests covering `:` in documentId, proving length-based stripping
|
||||
|
||||
## Next Recommended
|
||||
|
||||
**Phase 2** (future work, out of scope for this delta): metadata extraction (title/artist/embedded art/bitrate from audio files), sort by title/quality, embedded album art support.
|
||||
|
||||
**Phase 3** (future work): subfolder scoping refinements (e.g., exclude certain folders), shuffle support.
|
||||
|
||||
**Cosmetic follow-ups** (low priority, non-blocking):
|
||||
- Friendly path display in settings UI (e.g., last folder name instead of raw SAF URI)
|
||||
- Phone-side waveform visualization reuse (separate architectural decision, documented in proposal)
|
||||
|
||||
## Traceability
|
||||
|
||||
All phase artifacts (proposal, spec, design, tasks, apply-progress, verify-report) are archived in `openspec/changes/archive/2026-07-19-android-auto-local-music/`. Base spec has been permanently updated at `openspec/specs/android-auto-media/spec.md`. Git commit 6ae7e37 contains all implementation changes, staged for merge.
|
||||
|
||||
---
|
||||
|
||||
**Archived by**: SDD Archive Executor
|
||||
**Time**: 2026-07-19
|
||||
**Status**: READY FOR ORCHESTRATOR MERGE AND CLEANUP
|
||||
|
||||
Orchestrator action items:
|
||||
1. Commit/push git changes (all staged in commit 6ae7e37)
|
||||
2. Delete original change folder at `openspec/changes/android-auto-local-music/` (tool cannot delete)
|
||||
3. Next phase: plan Phase 2 if metadata extraction is ready for detailed exploration
|
||||
@@ -62,14 +62,14 @@ The system MUST produce a browsable tree that never presents a user-selectable f
|
||||
|
||||
### Requirement: Browsable Media Tree
|
||||
|
||||
`getChildren` MUST return a browsable tree rooted at `AudioService.browsableRootId`, organized into non-playable folders (Favoritos, Todas las emisoras, Mis emisoras, Ecualizador) containing playable items. Playable station items SHOULD carry an audio-quality subtitle when known. The `Favoritos` folder additionally MAY contain non-playable favorite-group sub-folders (see "Favorite Group Sub-Folders"); `Todas las emisoras` and `Mis emisoras` remain flat. The `Ecualizador` folder is flat, non-playable, and contains only the 6 fixed EQ preset items (see "EQ Preset Browsable Folder").
|
||||
(Previously: root contained exactly 3 folders — Favoritos, Todas las emisoras, Mis emisoras — with no EQ folder; Favoritos was a flat folder of playable station items only, with no sub-folder nesting.)
|
||||
`getChildren` MUST return a browsable tree rooted at `AudioService.browsableRootId`, organized into non-playable folders (Favoritos, Todas las emisoras, Mis emisoras, Ecualizador, and the local-music root) containing playable items. Playable station items SHOULD carry an audio-quality subtitle when known. The `Favoritos` folder additionally MAY contain non-playable favorite-group sub-folders (see "Favorite Group Sub-Folders"); `Todas las emisoras` and `Mis emisoras` remain flat. The `Ecualizador` folder is flat, non-playable, and contains only the 6 fixed EQ preset items (see "EQ Preset Browsable Folder"). The local-music root folder is non-playable and may itself be nested (see "Local Music Browsable Tree").
|
||||
(Previously: root contained exactly 3 folders — Favoritos, Todas las emisoras, Mis emisoras — with no EQ or local-music folder; Favoritos was a flat folder of playable station items only, with no sub-folder nesting.)
|
||||
|
||||
#### Scenario: Car requests the root
|
||||
|
||||
- GIVEN the car head unit connects and requests the root (`AudioService.browsableRootId`)
|
||||
- WHEN `getChildren` is called with the root id
|
||||
- THEN it returns four folder `MediaItem`s (Favoritos, Todas las emisoras, Mis emisoras, Ecualizador), each with `playable: false`
|
||||
- THEN it returns five folder `MediaItem`s (Favoritos, Todas las emisoras, Mis emisoras, Ecualizador, and the local-music root), each with `playable: false`
|
||||
|
||||
#### Scenario: Car requests a folder with no stations
|
||||
|
||||
@@ -171,6 +171,82 @@ The system MAY indicate the active/principal preset within the `Ecualizador` fol
|
||||
- WHEN the `Ecualizador` folder's children are built
|
||||
- THEN all 6 preset rows are returned with their plain preset names, with no partial or inconsistent marking applied
|
||||
|
||||
### Requirement: Local Music Root Access and Permission Persistence
|
||||
|
||||
The system MUST let the user select a local-music root folder via SAF (`ACTION_OPEN_DOCUMENT_TREE`), persist the granted URI permission, and MUST NOT crash or leave the local-music root folder in a broken state if that permission is later lost or was never granted.
|
||||
|
||||
#### Scenario: User picks a local music root folder
|
||||
|
||||
- GIVEN the user opens the local-music setup flow
|
||||
- WHEN they complete the SAF folder picker and grant access
|
||||
- THEN the selected folder's URI permission is persisted
|
||||
- AND the local-music root becomes browsable in Android Auto without re-prompting the picker
|
||||
|
||||
#### Scenario: Persisted permission survives app restart
|
||||
|
||||
- GIVEN a local music root was previously picked and its permission persisted
|
||||
- WHEN the app is restarted (cold start)
|
||||
- THEN the local-music root remains browsable in Android Auto without re-prompting the SAF picker
|
||||
|
||||
#### Scenario: Permission revoked or never granted
|
||||
|
||||
- GIVEN the SAF permission for the local-music root was revoked outside the app, or no root was ever picked
|
||||
- WHEN the local-music root folder is browsed in Android Auto
|
||||
- THEN `getChildren` returns an empty or explanatory list, not an error
|
||||
- AND the audio handler does not throw or crash
|
||||
|
||||
### Requirement: Local Music Browsable Tree
|
||||
|
||||
The Android Auto browse tree MUST expose a new non-playable local-music root folder alongside the existing station folders. Browsing that root and any nested subfolder MUST recursively mirror the picked SAF folder's structure: subfolders as non-playable `carpeta_local:<id>` items and audio files as playable `pista:<id>` items resolving to `PistaLocal` instances, using the raw filename as the displayed title.
|
||||
|
||||
#### Scenario: Car browses the local-music root
|
||||
|
||||
- GIVEN a local music root folder was picked and its permission is valid
|
||||
- WHEN `getChildren` is called with the local-music root folder id
|
||||
- THEN it returns the root's immediate subfolders as `carpeta_local:<id>` items and audio files as `pista:<id>` playable items
|
||||
- AND each playable item's title is the file's raw filename
|
||||
|
||||
#### Scenario: Car browses a nested subfolder
|
||||
|
||||
- GIVEN a `carpeta_local:<id>` folder returned from a prior browse call
|
||||
- WHEN `getChildren` is called with that folder id
|
||||
- THEN it returns that subfolder's own contents (nested folders and/or tracks), recursively mirroring the on-device structure
|
||||
- AND browsing an empty subfolder returns an empty list, not an error
|
||||
|
||||
### Requirement: Local Track Playback Reuses Existing Pipeline
|
||||
|
||||
Selecting a `pista:<id>` item MUST resolve to the corresponding `PistaLocal` and play it through the existing `playMediaItem`/`PluriWaveAudioHandler` pipeline, unchanged, including the shared EQ signal chain.
|
||||
|
||||
#### Scenario: User selects a local track
|
||||
|
||||
- GIVEN the user taps a `pista:<id>` playable item on the car head unit
|
||||
- WHEN `playFromMediaId(id)` is called
|
||||
- THEN the id resolves to a `PistaLocal` and playback starts through the existing `playMediaItem` internal path
|
||||
- AND standard transport (play/pause/stop) and fallback album art work as they do for stations
|
||||
|
||||
#### Scenario: EQ still applies to local track playback (regression guard)
|
||||
|
||||
- GIVEN a non-flat EQ preset is currently the active/principal preset
|
||||
- WHEN a `pista:<id>` item is played
|
||||
- THEN the audible output passes through the same shared EQ signal chain used for station playback, with no separate or bypassed path for local tracks
|
||||
|
||||
#### Scenario: Unknown or stale track id
|
||||
|
||||
- GIVEN `playFromMediaId` is called with a `pista:<id>` that no longer resolves to a known `PistaLocal`
|
||||
- WHEN resolution fails
|
||||
- THEN playback does not start and no unhandled exception propagates from the handler
|
||||
|
||||
### Requirement: Local Music Folder Item Cap
|
||||
|
||||
A local-music folder (root or nested) whose immediate children exceed `_maxItemsPorCarpeta` (50) MUST be capped to that limit rather than returning unbounded results; pagination is out of scope for this delta.
|
||||
|
||||
#### Scenario: Folder exceeds the item cap
|
||||
|
||||
- GIVEN a picked or nested local-music folder contains more than 50 immediate children (subfolders and/or tracks combined)
|
||||
- WHEN `getChildren` is called with that folder id
|
||||
- THEN at most 50 items are returned, consistent with the existing `_maxItemsPorCarpeta` cap applied to station folders
|
||||
- AND no error or crash occurs as a result of the truncation
|
||||
|
||||
### Requirement: Playable Item Metadata
|
||||
|
||||
Every playable `MediaItem` (station) MUST include a non-empty `title` and a loadable `artUri`. Stations without a logo MUST fall back to on-brand artwork, and stations whose logo URL cannot actually be loaded MUST degrade to the same on-brand fallback instead of rendering broken or blank art. The fallback MUST be visually consistent with the phone UI's per-station rotation rather than a generic launcher-icon copy.
|
||||
|
||||
Reference in New Issue
Block a user