Promotes the android-auto-media capability spec to openspec/specs/ and moves both completed changes into openspec/changes/archive/.
108 lines
4.6 KiB
Markdown
108 lines
4.6 KiB
Markdown
# Android Auto Media Specification
|
|
|
|
## Purpose
|
|
|
|
PluriWave MUST expose a browsable station tree and play-by-media-id interface to Android Auto (projected) via the existing `PluriWaveAudioHandler` (`audio_service` MediaBrowserService/MediaSession), so a driver can browse and play stations from the car head unit without rebuilding the audio pipeline.
|
|
|
|
## Requirements
|
|
|
|
### Requirement: Android Auto Discovery Declaration
|
|
|
|
The app MUST declare itself as a media app to Android Auto so the car head unit discovers it.
|
|
|
|
#### Scenario: Android Auto scans installed apps
|
|
|
|
- GIVEN PluriWave is installed on the phone
|
|
- WHEN Android Auto scans installed apps for car-app support
|
|
- THEN it finds `res/xml/automotive_app_desc.xml` declaring `<uses name="media"/>`
|
|
- AND the `AndroidManifest.xml` `<application>` block declares `<meta-data android:name="com.google.android.gms.car.application" android:resource="@xml/automotive_app_desc"/>`
|
|
- AND PluriWave appears in the car's media app list
|
|
|
|
### 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) containing playable station items.
|
|
|
|
#### 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 three folder `MediaItem`s (Favoritos, Todas las emisoras, Mis emisoras), each with `playable: false`
|
|
|
|
#### Scenario: Car requests a folder with no stations
|
|
|
|
- GIVEN the user has zero favorite stations
|
|
- WHEN `getChildren` is 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 `getChildren` is called (root or any folder)
|
|
- THEN it returns a valid, possibly empty, list without throwing and without blocking or crashing the service
|
|
|
|
### 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 a bundled default artwork.
|
|
|
|
#### Scenario: Station has a remote logo
|
|
|
|
- GIVEN a station has a valid remote logo URL
|
|
- WHEN it is mapped to a `MediaItem`
|
|
- THEN `title` is the station name and `artUri` is the station's logo URL
|
|
|
|
#### Scenario: Station has no logo
|
|
|
|
- GIVEN a station has no logo (null or empty)
|
|
- WHEN it is mapped to a `MediaItem`
|
|
- THEN `artUri` is set to the bundled default artwork asset instead of being empty or null
|
|
|
|
### Requirement: Media Item Resolution by ID
|
|
|
|
`getMediaItem` MUST resolve a single station id to its full `MediaItem`, returning `null` for unknown ids instead of throwing.
|
|
|
|
#### Scenario: Known station id
|
|
|
|
- GIVEN a valid station id that exists in current state
|
|
- WHEN `getMediaItem(id)` is called
|
|
- THEN it returns the corresponding `MediaItem` with `title` and `artUri` populated
|
|
|
|
#### Scenario: Unknown station id
|
|
|
|
- GIVEN an id that does not match any known station or folder
|
|
- WHEN `getMediaItem(id)` is called
|
|
- THEN it returns `null` without throwing
|
|
|
|
### Requirement: Play by Media ID Reuses Existing Playback Path
|
|
|
|
`playFromMediaId` MUST resolve the id to an `Emisora` and invoke the existing internal `playMediaItem` path; it MUST NOT duplicate playback or reconnection logic.
|
|
|
|
#### Scenario: User selects a station in the car
|
|
|
|
- GIVEN the user taps a playable station item on the car head unit
|
|
- WHEN `playFromMediaId(id)` is called
|
|
- THEN the id is resolved to an `Emisora` via the existing resolution logic (`_emisoraDesdeMediaItem`)
|
|
- AND playback starts through the existing `playMediaItem` internal path
|
|
- AND no separate or duplicated playback logic is executed
|
|
|
|
#### Scenario: Unknown or stale media id
|
|
|
|
- GIVEN `playFromMediaId` is called with an id that no longer resolves to a known station
|
|
- WHEN resolution fails
|
|
- THEN playback does not start and no unhandled exception propagates from the handler
|
|
|
|
### Requirement: Playback State Synchronization
|
|
|
|
Play/pause/stop state changes MUST remain synchronized between the car head unit and the phone UI, regardless of which side initiated the change.
|
|
|
|
#### Scenario: User pauses from the car
|
|
|
|
- GIVEN a station is playing and projected to the car
|
|
- WHEN the user pauses from the car head unit
|
|
- THEN the phone UI reflects the paused state via the shared `MediaSession`/`PlaybackState`
|
|
|
|
#### Scenario: User pauses from the phone
|
|
|
|
- GIVEN a station is playing and projected to the car
|
|
- WHEN the user pauses from the phone UI
|
|
- THEN the car head unit reflects the paused state via the same shared session
|