Files
pluriwave/openspec/changes/android-auto-local-music-paging/apply-progress.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

8.7 KiB
Raw Blame History

Apply Progress: android-auto-local-music-paging

Status: DONE — all 18 tasks across 8 phases completed in a single apply batch (first and only batch), delivered as one PR with size:exception per the resolved delivery strategy.

Tasks Completed: 18 / 18

  • Phase 1 (Paging Primitives): 1.1-1.4 — done
  • Phase 2 (Paged Media-ID Scheme): 2.1-2.2 — done
  • Phase 3 (Paged itemsLocales + "Más…" Item): 3.1-3.5 — done
  • Phase 4 (Wire Paged ID into hijosMusicaLocal): 4.1-4.2 — done
  • Phase 5 (Friendly Folder Name): 5.1-5.2 — done
  • Phase 6 (Localization): 6.1 — done (13/13 locales)
  • Phase 7 (Settings UI Wiring): 7.1 — done (implementation); widget test written but not executable in this sandbox (see Deviations)
  • Phase 8 (Regression / Manual Follow-Up): 8.1 — done (verified via targeted test re-runs); 8.2 — deviated as originally documented (manual follow-up, unchanged from tasks.md's own note)

TDD Discipline

Strict TDD mode was active. RED was confirmed via actual compile failures (missing symbols / signature mismatches) before each GREEN implementation step for:

  • paginaDe<T> / hayPaginaSiguiente (Phase 1) — implemented alongside the first test batch write (single edit pass covered both primitives), tests then run and passed; no separate RED capture for this pair specifically since both are trivial one-line pure functions added in the same edit as their tests were authored.
  • esCarpetaLocalPaginadaMediaId / paginaCarpetaLocalDesde / paged itemsLocales / paged hijosMusicaLocal (Phases 2-4) — RED confirmed via a full flutter test test/servicios/navegacion_auto_test.dart run showing ~20 compile errors (Method not found, No named parameter) before the GREEN implementation, then a second run confirming 84/84 passing.
  • nombreCarpetaDesdeUri (Phase 5) — RED confirmed via flutter test test/servicios/musica_local_auto_test.dart showing 5 Method not found: 'nombreCarpetaDesdeUri' compile errors before GREEN, then 10/10 passing after.
  • Settings UI wiring (Phase 7) — RED attempted via flutter test test/pantallas/pantalla_ajustes_test.dart, but this surfaced a PRE-EXISTING environment blocker unrelated to this change (see Deviations #1). The implementation was completed to spec regardless.

Files Changed

Production code:

  • lib/servicios/navegacion_auto.dart (+119/-lines within diff) — added top-level paginaDe<T>, hayPaginaSiguiente; added _prefijoCarpetaLocalPaginada, esCarpetaLocalPaginadaMediaId, paginaCarpetaLocalDesde, _tituloMasLocal, _itemMasLocal; extended itemsLocales signature (documentIdPadre, pagina, tamano, @visibleForTesting construirItem) with slice-then-map ordering (ADR-3); added paged branch to hijosMusicaLocal.
  • lib/servicios/musica_local_auto.dart (+47 lines) — added pure nombreCarpetaDesdeUri(String treeUri, {required String nombreGenerico}). Signature note: takes the localized fallback text as a plain String parameter rather than a BuildContext, so the function stays pure and unit-testable without a widget tree (mirrors the _formatearDuracion(AppLocalizations l10n, ...) precedent in pantalla_reproductor.dart, minus the generated-class coupling). This is a refinement of tasks.md's terse "nombreCarpetaDesdeUri(String) -> String" description, the same kind of abbreviation gap task 3.4 already had for itemsLocales's full signature.
  • lib/pantallas/pantalla_ajustes.dart (+5/-2 lines) — _SeccionMusicaLocal subtitle now renders nombreCarpetaDesdeUri(carpeta, nombreGenerico: l10n.localMusicFolderGenericName) instead of the raw persisted URI; the unconfigured branch is unchanged (l10n.localMusicFolderNotConfigured).
  • lib/l10n/app_{ar,bn,de,en,es,fr,hi,id,it,ja,pt,ru,zh}.arb (13 files, +1 key each) — added localMusicFolderGenericName, matching each locale's existing localMusicFolder*/localMusicChoosePath register. No "Más…" arb key added anywhere (ADR-5 — that label is hardcoded Spanish car-tree text, matching every other car-tree label in navegacion_auto.dart).

Tests:

  • test/servicios/navegacion_auto_test.dart (+453 lines) — new groups: paginaDe, hayPaginaSiguiente, esCarpetaLocalPaginadaMediaId / paginaCarpetaLocalDesde; rewrote/extended ConstructorArbolAuto.itemsLocales group (call-count invariant, exact-50 boundary, 51-100 two-step, 100+ chained 3+ pages, "Más…" item shape/round -trip, root paging, stale-page-beyond-range, plus all 8 pre-existing call sites updated to pass documentIdPadre); extended hijosMusicaLocal group with 4 new paged-branch tests.
  • test/servicios/musica_local_auto_test.dart (+84 lines) — new nombreCarpetaDesdeUri group: internal storage, SD card, nested folder, storage-root empty-tail fallback, unparseable-URI fallback, never-raw -URI/never-empty invariant.
  • test/pantallas/pantalla_ajustes_test.dart (+57 lines) — new group _SeccionMusicaLocal — friendly folder name (Phase 7): configured folder shows the derived friendly name (never the raw content:// URI); unconfigured folder keeps the existing "not configured" message. Written to spec but NOT executable in this sandbox (see Deviations #1).

Test Results (independently re-confirmed)

  • flutter test test/servicios/navegacion_auto_test.dart test/servicios/musica_local_auto_test.dart --concurrency=1 --timeout=60s94 / 94 passing (84 tests in navegacion_auto_test.dart + 10 tests in musica_local_auto_test.dart) — corrected during verify, the figure above (95/95, 85+10) originally reported here was a counting error.
  • test/pantallas/pantalla_ajustes_test.dart — NOT run to completion; fails to COMPILE in this sandbox for reasons that predate this change entirely (see Deviations #1). The 2 new tests in this file are structurally complete and should pass once flutter gen-l10n is run as part of the normal manual follow-up (task 8.2), which already covers regenerating l10n artifacts.

Diff Size

git diff --stat: 19 files changed, 764 insertions(+), 40 deletions(-) (≈804 changed lines). This exceeds the tasks.md forecast of ~480-600 lines, mainly because task 3.5's "mechanical" call-site update also required rewriting one pre-existing assertion (the 60-item cap test) into two new boundary tests reflecting the spec's cap→paging behavior change, plus the call-count-invariant test's verbosity (4 assertions × explicit spy wiring). Delivered as a single PR with size:exception, as directed.

Deviations

  1. pantalla_ajustes_test.dart cannot compile in this sandbox — pre-existing, not introduced by this change. The tracked lib/l10n/gen/app_localizations.dart is stale: it predates even the PARENT local-music change and is missing localMusicFolderNotConfigured, localMusicSectionTitle, localMusicFolderTitle, etc. — getters the file already referenced BEFORE this apply pass touched anything. Confirmed by running the targeted test file and observing the compiler errors point exclusively at pre-existing lines (320-383) before any new code from this change is even reached. Regenerating the l10n artifacts requires flutter gen-l10n, which is documented to hang in this environment and was explicitly out of scope for this apply run (per task 8.2, already flagged as a manual follow-up in the original tasks.md). The new localMusicFolderGenericName getter this change adds will appear automatically once that regeneration happens — no code change is needed on top of what's here.
  2. The pre-existing 60-item itemsLocales cap test was rewritten, not just signature-patched, as part of task 3.5. This was flagged in tasks.md as "mechanical, no behavior change" for the 8 call sites in aggregate, but this ONE specific test's assertion (exactly 50 items, full stop) is incompatible with the spec's Modified Requirement ("Local Music Folder Item Cap and Paging" — cap-and-truncate replaced by cap-and-page). It was split into two new tests that assert the correct NEW behavior (exactly 50 items → no "Más…"; 51-100 items → 50 + "Más…" then the remainder). This is an intentional, spec-mandated behavior change, not a regression.

Risks Carried Forward

  • Re-enumerating the folder per "Más…" tap repeats one SAF query per ADR-2 (accepted, documented, out of scope to change here).
  • Pure-Dart friendly-name derivation is brittle for exotic SAF providers (accepted per ADR-4 — degrades to the generic label, never a crash/blank /raw-URI).
  • flutter analyze / flutter gen-l10n / full flutter test were not run in this session (environment constraint) — task 8.2's original deviation note stands, now also explains Deviation #1 above.