Files
pluriwave/openspec/changes/archive/2026-07-19-android-auto-local-music-paging/proposal.md
T
FreeTLab e030a0975d docs(openspec): archive android-auto-local-music-paging
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.
2026-07-19 22:21:45 +02:00

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.