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.
7.4 KiB
Delta for Android Auto Media
Scope: Phase 1 (foundational plumbing) of android-auto-local-music only. See "Not in this delta" for explicit exclusions.
ADDED Requirements
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
getChildrenreturns 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
getChildrenis called with the local-music root folder id - THEN it returns the root's immediate subfolders as
carpeta_local:<id>items and audio files aspista:<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
getChildrenis 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
PistaLocaland playback starts through the existingplayMediaIteminternal 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
playFromMediaIdis called with apista:<id>that no longer resolves to a knownPistaLocal - 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
getChildrenis called with that folder id - THEN at most 50 items are returned, consistent with the existing
_maxItemsPorCarpetacap applied to station folders - AND no error or crash occurs as a result of the truncation
MODIFIED Requirements
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, and the new 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; Todas las emisoras and Mis emisoras remain flat. The Ecualizador folder is flat, non-playable, and contains only the 6 fixed EQ preset items. The local-music root folder is non-playable and may itself be nested (see "Local Music Browsable Tree").
(Previously: root contained exactly four folders — Favoritos, Todas las emisoras, Mis emisoras, Ecualizador — with no local-music root.)
Scenario: Car requests the root
- GIVEN the car head unit connects and requests the root (
AudioService.browsableRootId) - WHEN
getChildrenis called with the root id - THEN it returns five folder
MediaItems (Favoritos, Todas las emisoras, Mis emisoras, Ecualizador, and the local-music root), each withplayable: false
Scenario: Car requests a folder with no stations
- GIVEN the user has zero favorite stations
- WHEN
getChildrenis called with the Favoritos folder id - THEN it returns an empty list, not an error
Scenario: Browse requested before app state is loaded
- GIVEN the audio handler starts cold and station/favorites Provider state has not finished loading
- WHEN
getChildrenis called (root or any folder) - THEN it returns a valid, possibly empty, list without throwing and without blocking or crashing the service
Scenario: Station has known codec and bitrate
- GIVEN a station's
Emisora.codecandEmisora.bitrateare both known (non-null) - WHEN it is mapped to a playable
MediaItem - THEN
displaySubtitleSHALL contain a human-readable quality hint combining bitrate and codec
Scenario: Station has unknown codec or bitrate
- GIVEN a station's
Emisora.codecorEmisora.bitrate(or both) is null/unknown - WHEN it is mapped to a playable
MediaItem - THEN
displaySubtitleSHALL omit the quality hint gracefully, and MUST NOT render literal placeholder text such as "null kbps"
Scenario: Ungrouped station appears exactly as before (regression guard)
- GIVEN a station's
Emisora.grupoFavoritosIdequalsGrupoFavoritos.sinAsignarId - WHEN the
Favoritos,Todas las emisoras, orMis emisorasfolders are browsed - THEN that station appears as a playable
emisora:<uuid>item exactly as before, unaffected by the local-music root's existence or content
Not in this delta
Phase 1 does NOT specify: local-track metadata display beyond the raw filename (no title/artist tagging), album art beyond the existing generic on-brand fallback, sort/filter of local tracks or folders, or shuffle. These are deferred to Phase 2/3 per the proposal and MUST NOT be implemented against this spec.