Merges its delta requirements into the android-auto-media base spec. This closes out Phase-1 polish for local music; Phase 2 (metadata, sort/filter, real art) and Phase 3 (subfolder scoping, shuffle) remain the only planned future work for this feature.
106 lines
5.1 KiB
Markdown
106 lines
5.1 KiB
Markdown
# Proposal: Android Auto Local Music — On-Demand Paging + Friendly Folder Name
|
|
|
|
Fast-follow polish to the archived `android-auto-local-music` Phase 1
|
|
(`archive/2026-07-19-android-auto-local-music/`). NOT Phase 2/3.
|
|
|
|
## Intent
|
|
|
|
Phase 1 capped each local-music folder at `_maxItemsCarpetaLocal = 50` and
|
|
silently truncated the rest alphabetically — items past 50 are unreachable.
|
|
The user rejects silent data loss and wants on-demand incremental loading,
|
|
with an explicit memory goal: do NOT eagerly build full `MediaItem`s (art,
|
|
title) for items beyond the page actually shown. Also fold in an archived
|
|
verify SUGGESTION: settings shows the raw SAF content URI instead of a
|
|
human-friendly folder name.
|
|
|
|
## Scope
|
|
|
|
### In Scope
|
|
- Replace silent truncation in `itemsLocales` with 50-per-page on-demand
|
|
paging for local-music folders, via a synthetic browsable non-playable
|
|
"Más…" trailing item (established `MediaBrowserService` workaround — the
|
|
car UI has no native "load more" signal).
|
|
- Memory efficiency: hold only the cheap full `NodoLocal` list per browsed
|
|
level; construct `MediaItem`s (art/title) ONLY for the requested page.
|
|
Verified by unit test.
|
|
- Friendly folder-name display in settings `_SeccionMusicaLocal` instead of
|
|
the raw `content://…/tree/…` URI.
|
|
- Update ALL 13 `app_*.arb` locale files for any new string (the "Más…"
|
|
label) — Phase 1's verify caught an en/es-only omission; do not repeat.
|
|
|
|
### Out of Scope
|
|
- Phase 2 (metadata/sort/filter/embedded art) and Phase 3 (subfolder
|
|
scoping/shuffle) — remain deferred.
|
|
- Radio-station folders (`_maxItemsPorCarpeta = 50`) keep cap-and-truncate;
|
|
paging is NOT generalized to radio (user raised local music only; no
|
|
architectural need forces sharing now).
|
|
- Native Kotlin paging: `listAudioChildren` already returns the full level;
|
|
no native offset/limit added.
|
|
|
|
## Capabilities
|
|
|
|
### New Capabilities
|
|
None.
|
|
|
|
### Modified Capabilities
|
|
- `android-auto-media`: local-music folder browse replaces silent 50-item
|
|
truncation with on-demand "Más…" paging. (sdd-spec: confirm exact
|
|
requirement location; the settings friendly-name fix is phone-UI and may
|
|
need only a light or no spec delta.)
|
|
|
|
## Approach
|
|
|
|
Pure-Dart, additive on existing seams. `itemsLocales(nodos, pagina)` sorts
|
|
the full (cheap) `NodoLocal` list deterministically, then
|
|
`.skip(pagina*50).take(50).map(_itemLocal)` — so only the page's items are
|
|
built — and appends a "Más…" item when more remain. The "Más…" media-id
|
|
encodes `(documentId, siguientePagina)` (new prefix, e.g.
|
|
`carpeta_local_pag:`); tapping it re-invokes `getChildren`, which
|
|
re-enumerates that one folder (native already returns the whole level) and
|
|
returns the next slice. Nothing is cached between taps → minimal memory;
|
|
deterministic sort keeps page boundaries stable across re-queries. Friendly
|
|
name via pure-Dart `nombreCarpetaDesdeUri(treeUri)` (decode the tree
|
|
documentId's trailing path segment; fall back to the raw URI when empty) —
|
|
zero native surface, fully unit-testable.
|
|
|
|
## Affected Areas
|
|
|
|
| Area | Impact | Description |
|
|
|------|--------|-------------|
|
|
| `lib/servicios/navegacion_auto.dart` | Modified | `itemsLocales` gains page param + "Más…" item; paged media-id predicate/parse; `hijosMusicaLocal` routes paged ids |
|
|
| `lib/servicios/servicio_audio.dart` | Modified | `getChildren` branch for the paged "Más…" media-id |
|
|
| `lib/servicios/musica_local_auto.dart` | Modified | Add `nombreCarpetaDesdeUri` helper (pure Dart) |
|
|
| `lib/pantallas/pantalla_ajustes.dart` | Modified | `_SeccionMusicaLocal` renders friendly name (line ~361) |
|
|
| `lib/l10n/app_{ar,bn,de,en,es,fr,hi,id,it,ja,pt,ru,zh}.arb` | Modified | New "Más…" label across all 13 locales |
|
|
| `MainActivity.kt` / `pubspec.yaml` | Unchanged | No native/dep change |
|
|
|
|
## Risks
|
|
|
|
| Risk | Likelihood | Mitigation |
|
|
|------|------------|------------|
|
|
| Re-enumerating the folder on each "Más…" tap repeats one SAF query | Med | Acceptable per-tap cost; deterministic sort keeps pages stable; no cache = minimal memory |
|
|
| Pure-Dart friendly-name derivation brittle for exotic SAF providers | Med | Fallback to raw URI; native `DocumentFile.getName` left as a design fallback |
|
|
| Very deep folders still hold full `NodoLocal` list per level | Low | DTOs are tiny; only paged `MediaItem` build is bounded |
|
|
| l10n omission regresses (missing locales) | Med | Scope all 13 arb files into tasks from the start |
|
|
|
|
## Rollback Plan
|
|
|
|
Behavioral/additive. Revert = restore `.take(_maxItemsCarpetaLocal)`
|
|
truncation, drop the paged media-id + "Más…" item, revert the settings
|
|
subtitle to the raw URI, remove the new arb key. Stations untouched.
|
|
|
|
## Dependencies
|
|
|
|
None (no new packages, no native changes).
|
|
|
|
## Success Criteria
|
|
|
|
- [ ] A folder with >50 items exposes every item via "Más…" paging; none
|
|
silently dropped.
|
|
- [ ] Only the requested page's `MediaItem`s are constructed — proven by a
|
|
unit test (page N+1 items not built until page N+1 is requested).
|
|
- [ ] Settings shows a human-friendly folder name, not the raw content URI.
|
|
- [ ] All 13 `app_*.arb` files carry the new label.
|
|
- [ ] No station browse/play regression; pure-Dart fully unit-tested; native
|
|
unchanged.
|