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

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.