Files
pluriwave/openspec/changes/android-auto-local-music-paging/proposal.md
T
FreeTLab 725169cd31 feat(auto): page local-music folders instead of truncating at 50 [size:exception]
Folders over the 50-item cap now show a "Mas..." item that reveals
the next page on tap, instead of silently dropping the rest. Paging
slices the cheap raw list before building any MediaItem, so items
beyond the requested page are never resolved (art, title) -- proven
by a call-count test. Also swaps the raw SAF content:// URI shown in
settings for a parsed, human-readable folder name with a localized
fallback across all 13 locales.

servicio_audio.dart is untouched; this stays entirely within the
local-music tree/dispatch layer.
2026-07-19 22:14:05 +02:00

5.1 KiB

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 MediaItems (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 MediaItems (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 MediaItems 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.