Skip to content

UI density: compact mode, flat placeholders, Settings tab, shelf play - #656

Closed
PylotLight wants to merge 37 commits into
timusus:mainfrom
PylotLight:pr/ui-density
Closed

PylotLight wants to merge 37 commits into
timusus:mainfrom
PylotLight:pr/ui-density

Conversation

@PylotLight

Copy link
Copy Markdown
Contributor

Stacked on #655 (retarget once it merges; the diff below includes it until then). Slims the M3 Expressive chrome and adds a density option; no behaviour changes to playback, scanning or backup.

  • Removes wavy progress indicators (flat LinearProgressIndicator everywhere)
  • Slim-down: transparent settings rows, standard nav bar, smaller hero/tiles/mini-player
  • Compact mode toggle (Appearance, off by default): 44dp row art, 36dp headers, 96dp tiles, pinned one-row Library/Settings bars, smaller detail heros, tighter grids
  • Missing-artwork placeholders flattened to bare icons (was MaterialShapes cookie/blob containers); design-language.md updated
  • 4th bottom tab: Settings, with its own back stack
  • Home shelf headers gain play buttons that queue the whole shelf in order
  • Fixes: resume hero actually renders at 64dp (a Modifier.size vs size conflict left it at 160dp, which also made the first compact pass a no-op)

Verification: home/settings/shell/library/theme suites green (incl. new shelf-play tests), assembleDebug green. Roborazzi goldens under docs/design/ need re-recording (placeholders/headers/tabs changed).

Remaining 8 modules still targeted JVM 11 with stale comments about
pre-KMP inline constraints (core, networking). All consumers are now
on JVM 17 (app, designsystem, fixtures), AGP 9.4.1 + Kotlin 2.4.20
fully support 17, desugaring is on. Bump compileOptions +
jvmTarget together so inline functions can't mix bytecode levels.

Majors verified current as of 2026-09-27, intentionally untouched:
AGP 9.4.1, Kotlin 2.4.20, Media3 1.11.1, Compose BOM 2026.09.00,
Coil 3.6.2, Ktor 3.6.0, Room 2.8.3. R8 fullMode=false left as-is
(Retrofit-adapter crash needs root-causing before AGP 10).

Needs ./gradlew :android:app:assembleDebug on a machine with
Android SDK + JDK 21 to verify.
…torage flag

- enableOnBackInvokedCallback=true (targetSdk 36)
- localeConfig locales_config.xml (en seed for Settings per-app picker)
- remove requestLegacyExternalStorage (ignored since API 30)

M3 Expressive stays on 1.5.0-alpha29 (latest as of 2026-09-27;
stable 1.4.0, 1.5.0 stable later in 2026). M2 styles.xml +
google.material dep stay: Cast route chooser + server sign-in
dialog fragments still need the View theme (see MainActivity KDoc).
Port OffBookPlus k8s flow for standalone k3s: tar+kubectl-cp seed pod
instead of rsync/hostPath, SDK bootstraps at runtime from temurin
JDK 21 into a local-path PVC (sdk+gradle+workspace survive across
builds). Keeps Dockerfile for docker-capable hosts. OffBookPlus
scripts unusable here: no docker/colima/rsync on host, cluster is
remote (node luxai, amd64) so hostPath sync can't work.
…rops

Remote build failed two ways: KMP requests a Java 17 toolchain but the
temurin image ships only 21 (no auto-provisioning configured), and
app/build.gradle.kts shells to git for versioning (binary missing
broke config-cache store). Bootstrap now fetches Temurin 17 once into
the PVC and registers installations.paths, ensures git exists, and
build-remote.sh forwards local git-describe as -PversionCode/Name
since .git (714M) is excluded from the sync.
TaglibMediaProvider.findSongs ignored its existingSongs and re-parsed
every file with an FD + TagLib open per row. Build an existingByPath
map and emit the stored song directly when its lastModified + size
match (plus duration within 1s, for DATE_MODIFIED's 1-second
granularity), preserving id, play counts and artworkVersion.
…r tuning

MediaStore ContentObserver in the media-provider startup initializer
(debounced 20s, gated on music permission + already-scanned-once)
reuses MediaImporter.import's own coalescing. New exported
LibraryScanReceiver (RESCAN_LIBRARY, 60s debounce, no DUMP gate) for
automation apps via goAsync. Worker drops the never-firing
setRequiresDeviceIdle, gains setRequiresStorageNotLow, and staggers
its first run 15-30min instead of a full interval. Extra SAF trees
documented as manual/periodic-only (no DocumentsProvider observer).
The success-only kubectl-wait burned the full deadline on failed
builds. Poll succeeded/failed explicitly, break on either, dump the
last 40 log lines on failure.
Previous build OOMKilled at mergeProjectDexDebug under 6Gi. Node has
headroom (32GB, ~21% used): 4 CPU / 10Gi limit, Gradle heap 6g.
S2Sheet pulls expressive internals; the new caller missed the
@OptIn. Caught by :android:app:compileDebugKotlin (OPT_IN_USAGE_ERROR).
Leftover from the dialog→S2Dialog extraction. Caught by
:android:app:compileDebugKotlin (SYNTAX error).
S2ActionsSheet still needs it; the BasicAlertDialog removal took the
annotation with it.
The success-path kubectl cp from the build pod silently fetched
nothing. Route through the alpine seed pod like the source sync.
Settings -> Library gains Back up library / Restore library backup.
Export writes play counts, positions, favourites, exclusions,
dates and playlists (identity: path, remote id, tag fingerprint;
never Room ids, tokens or grant URIs). Restore matches exact,
relative-path, remote-id then fuzzy, and merges field-by-field
(max counts, latest times, earliest favourite) so neither side
loses newer activity. Includes matcher/schema unit tests.
SettingsViewModel gained a LibraryBackupManager dependency; the
direct constructions in SettingsViewModelTest/SettingsIntegrationTest
now pass a relaxed mock (backup flows have their own tests).
…, remote test

- relativePath now strips /storage/emulated/<n>/ as well as physical
  volume ids (primary storage is the common reinstall case).
- latestOf/earliestOf replace maxOf/minOf helpers that recursed via
  overload resolution (StackOverflowError in merge).
- remote-id test now uses differing paths so exact match can't win.
The optional-group regex ate one path segment too many on emulated
storage. (emulated/N | volume) alternatives are now exclusive.
Restore now overwrites playCount, timestamps, position, excluded and favouritedAt from the backup snapshot instead of merging field-by-field (max counts, latest times). dateAdded keeps its current value when the backup has none. Adds SongDataDao.setFavouritedAt; matcher tests pin the snapshot behaviour.
…library density

Replaces wavy progress indicators with flat ones; slims Expressive chrome (settings rows, nav bar, hero, tiles, mini-player); adds an Appearance compact-mode toggle (dense rows, 36dp headers, 96dp tiles, pinned one-row library/settings bars, smaller detail heros); flattens missing-artwork placeholders to bare icons; adds a Settings bottom tab; adds play buttons to Home shelf headers.
Flat resume row (no hero card), flat shadowless tiles, icon-only nav bar, 28/40dp row art, 32dp headers, 3-column grids, tighter detail heros. Renames the toggle to Classic mode; pref key unchanged.
Settings lives in the bottom tab: removes the Home/Library top-bar gears and their plumbing. Restores the old home's shortcut row (History, Recently Added, Favorites, Shuffle) opening the smart playlists, two-line shelf headers with subtitles, and play-count badges on most-played tiles (count out of the subtitle). Resume hero gains a test tag so its actions stay tappable beside the shortcuts.
Drops Home's empty top bar (Shuffle lives in the shortcut row), adds classic vertical spacing between shortcuts and shelves, shrinks classic shortcut circles to 48dp, and densifies classic tile subtitles.
Each one-shot builder minted its own ~/.android debug key, so every APK had a new signature and Android refused to install it over the previous build. The debug build type now signs with a checked-in debug key (not a secret); release signing is untouched.
Brand row with the launcher logo and Shuttle name tops Home. Shortcut circles go solid classic colors (orange/green/red/blue) with white glyphs. Shelf tiles grow (128dp classic, 144dp modern) so rows fill the screen vertically.
Brand logo 40/44dp with headlineSmall name. Shortcuts keep their 48/56dp circles but swap Rounded for the sharp Filled glyphs the classic app used. Dual debug+release now the default cluster build; dev release signs with the shared debug key under the .dev id.
Brand mark, shortcut row, two-line shelf headers and play-count badges render in classic mode only; modern returns to the plain top bar. Settings > About shows version (code/sha) so dev builds are told apart; build-remote.sh forwards the local sha as -PGitSha.
Restores ic_history, ic_playlist_add and ic_favorite_border from before the legacy-resource deletion, pairing with the surviving shuffle glyph. Classic shortcuts render them tinted in the reference accent colors on dark circles; modern keeps filled white glyphs. Brand splits by mode: centered Shuttle Music Player in classic, small row in modern.
@timusus

timusus commented Oct 1, 2026

Copy link
Copy Markdown
Owner

Holding this one for now. It stacks on #655 (see the split request there), and several of its changes are design-language decisions rather than density tweaks: removing the wavy progress indicators, flattening the missing-artwork placeholders, and adding a fourth Settings tab with its own back stack. Those need a call from Tim against docs/design/design-language.md before any review of the code makes sense.

Once #655 is split and landed, a rebased PR with just the compact-mode toggle (off by default) and the resume-hero size fix would be the easiest piece to take first; the indicator, placeholder and Settings-tab changes are better raised as separate issues so each can be decided on its own.

@timusus

timusus commented Oct 3, 2026 •

Copy link
Copy Markdown
Owner

🤖 Claude here (an AI agent), reviewing for Tim.

A follow-up on the hold above. #655 is being finished on the maintainer side (see the note there), so this one no longer needs to wait for it. To move forward:

  1. Rebase on main with only the UI commits. The branch still carries k8s/, the JDK 21 / minSdk 26 / wrapper changes, the scanner commits, the theme consolidation and a committed android/app/debug.keystore. None of those can come in through this PR, and the keystore shouldn't be in the repo at all.

  2. Split it in two:

    • (a) the compact-mode toggle (off by default) plus the resume-hero size fix. These change nothing for existing users, so this part can be reviewed straight away.
    • (b) the design-language changes, one at a time.
  3. Attach before/after screenshots for each design change, with light and dark mode where it matters:

    • flat progress indicators instead of wavy
    • bare-icon artwork placeholders
    • the fourth Settings tab
    • the classic home layout and brand header
    • compact mode on and off

    These changes get decided against docs/design/design-language.md, and screenshots are the quickest way to decide each one.

I can review (a) as soon as it's up.

@PylotLight

PylotLight commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor Author

started on the split as suggested. opened #860 with just the (a) slice: compact-mode toggle plumbing off current main, no readers yet so zero visual change, none of the old stack (no k8s/build/scanner/theme/keystore).

one note: the resume-hero size fix does not carry over. main's Home is a JumpBackIn grid now and the old ResumeHero card is gone. happy to fold in a sizing fix if one still applies to the new layout.

keeping this PR open until #860 is up for review, then we can close/supersede. design-language pieces to follow one at a time with screenshots.

timusus added a commit that referenced this pull request Oct 4, 2026
timusus added a commit that referenced this pull request Oct 4, 2026
A play icon beside See all plays every song of the shelf in order, replacing the queue. Not on Jump back in or Genre picks.

Idea-by: PylotLight (#656)
@timusus

timusus commented Oct 4, 2026

Copy link
Copy Markdown
Owner

🤖 Claude here (an AI agent), reviewing for Tim. Closing in favour of the summary on #860. Thanks!

@timusus timusus closed this Oct 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants