Commit Graph
1089 Commits
Author SHA1 Message Date
austinried 8aee86fc60 settings: restore the version, homepage and support links in About 2026-10-08 17:19:18 +00:00
austinried cc65f238ae docs: correct the architecture notes and drop the implementation plan 2026-10-08 14:09:52 +00:00
austinried acd087cfe0 docs: rework README and store feature list
Give each feature a bolded name with a short description, add large-library
and offline-mode points, and bring the store description's order and content
in line with the README.
2026-10-08 11:10:34 +00:00
austinried 05de10c912 clear the build warnings and fail on new ones
A clean build now emits no warnings of its own, and Kotlin
`allWarningsAsErrors` plus lint `warningsAsErrors` stop new ones from
sneaking back in.

- Kotlin: give SearchFilter's @StringRes parameter an explicit @param:
  target; use safe calls for File.parentFile in StringResourcesTest.
- Packaging: keep debug symbols for the prebuilt .so files that have no
  symbol table, which is what the "unable to strip" warning was about.
- Lint: move `modifier` to the first optional parameter across the Compose
  screens, use mutableIntStateOf for int state, read Resources via
  LocalResources, use the KTX Bitmap.get/String.toUri, drop the unused
  ic_action_name and ic_stat_name bitmaps and the dead -v24 qualifier on
  the notification vector, fix the pt-BR "album" accents and the unused
  Vietnamese/Thai plural quantities, and suppress the intentional
  cleartext/user-certificate, backup, exported-service and
  plural-candidate findings. Disable the version-update nags, which the
  dependency-update process owns.

The AboutLibraries plugin still resolves the variant classpaths during
configuration, so AGP keeps warning about that one; upstream in
mikepenz/AboutLibraries#1369.
2026-10-08 10:27:09 +00:00
austinried eef94eff63 ci: move to actions/checkout v7 and actions/cache v6, pin ubuntu-26.04
The v4 pair targets Node 20, which GitHub now forces onto Node 24 (Node 20 is
deprecated). The Forgejo runner is v13, which runs Node 24, so both copies move
together. Pin the GitHub runner to ubuntu-26.04: ubuntu-latest migrates to it
over Oct 19 - Nov 19, 2026, and Nix supplies the toolchain either way.
2026-10-05 13:50:54 +00:00
austinried 126065e705 ci: run the workflow on GitHub pull requests
Add .github/workflows/ci.yml, a copy of the Forgejo CI workflow adapted to
GitHub-hosted runners (ubuntu-latest + cachix/install-nix-action), so pull
requests on the GitHub mirror get the lint, unit-test and integration checks.
The GitHub copy uses a 90 minute timeout so a cold run can finish and warm the
caches, caps the Nix cache so it cannot evict the Gradle/music caches from
GitHub's 10 GB per-repo budget, and pins the job token to contents:read.
2026-10-04 08:07:14 +00:00
austinried cdc02be9a1 docs: restore and refresh the README badges
Add the F-Droid version badge back ahead of the GitHub release badge, match its colour to the release badge's blue, and darken the GPL-3.0 license badge.
2026-10-04 02:51:25 +00:00
austinried 628e4b75e6 update weblate translations status image 2026-10-04 02:45:15 +00:00
austinried a7d33b9448 add translations status badge 2026-10-04 02:38:55 +00:00
austinried 41f2335a2c metadata: refresh the artist store screenshot
The artist page now shows the artist name, an Artist/Album artist label
and keeps the count on the button row. Feed those fields in the demo
screenshot and re-render 05_artist.png from the Navidrome demo library.
2026-10-04 01:49:55 +00:00
austinried 174d79eeb7 artists: keep the label-to-count gap the same across artist types
The album count sits in the star/more button row, so the row is 48dp tall
only when those buttons are present, shifting the count for album artists
versus credited track artists. Pin the row to that height either way.
2026-10-04 01:49:55 +00:00
austinried 299457d8fc artists: tighten the header and drop the dead star/more on track artists
Lift the type label to the artist name's inset and centre the album count
on the star/more row. A credited track artist has no artists row, so its
star is disabled and more does nothing; hide both there.
2026-10-04 01:49:55 +00:00
austinried fd2e1cfd86 artists: show a credited track artist's name and type on their page
A track artist without an artists row had no name on the artist page, so
the header and the placeholder letter were blank. Derive the name from the
songs that credit them, and label the page 'Album artist' when they lead an
album or 'Artist' when they are only credited on tracks. The hero art keeps
the letter placeholder when there is no artist image.
2026-10-04 01:49:55 +00:00
austinried 2b08ae3b63 artists: browse and download the tracks a guest artist is credited on
A song's artist can differ from its album artist (compilations, guest
tracks). The artist page was built only from albums where the artist is
the album artist, so opening such an artist showed a blank page. Attribute
a song to both its album artist and its credited track artist across the
artist album list, shuffle queue, download status and the artists list
filter/badges, and show the listed album count on the detail page.
2026-10-04 01:49:55 +00:00
austinried d1ec74ad1e Add monochrome launcher icon layer for themed icons 2026-10-04 01:24:16 +00:00
austinried dbb6745b7b address review: replay one dismissal so a recreated screen still sees it
A notification tap can fire the dismissal before the route's collector
subscribes (activity recreated with a restored options sheet), and a
replay-zero broadcast drops it. Replaying one is a no-op on default state
and the new ContextMenuHost test guards it.
2026-10-04 00:56:53 +00:00
austinried af6979459b address review: signal transient dismissal with an event flow, not an int
An Int counter was already rejected for the now-playing launch signal; use
a broadcast Flow on the shared ContextMenuHost and collect it, so the
dismissal is an event rather than an ever-increasing number.
2026-10-04 00:56:53 +00:00
austinried 1b83a5ff93 now playing: dismiss transient UI when the notification opens it
Tapping the media notification opened now playing over whatever was on
screen, leaving the item context menu, the library options sheet, the
keyboard and any open dialog above it. On a now-playing request, clear
focus, dismiss the shared menus and close each route's dialog state.
2026-10-04 00:56:53 +00:00
austinried 9f2e14a9f9 Re-notify replay on every connectivity change, not just availability flips
conflate rather than distinctUntilChanged: switching between two
available networks (WiFi LAN server -> cellular -> WiFi) kept the signal
true, so the queue never drained until another trigger. Emit on each
callback instead, and drop the sink combine's distinctUntilChanged so a
repeated ready edge still flushes. Simplify flush to reuse the safe drain.
Add a regression test that replays on two true emissions with no false
between.
2026-10-04 00:37:29 +00:00
austinried 6662bdd514 Replay pending actions when connectivity is restored
A raw network drop and recovery never flipped the offline flag, so the
queue only drained on the next sink op, an offline-mode toggle or a
restart. Observe the default network's availability and use it, together
with the persisted offline preference, to trigger a replay. This signal
only drains pending actions; it never changes offline mode.
2026-10-04 00:37:29 +00:00
austinried f20937c83e Harden the pending-action queue after review
- Order direct stars against the queue so a successful online unstar is no
  longer overwritten by an older queued star, and serialize all queue
  mutations under one lock.
- Treat a SubsonicException with a negative code as a transport failure:
  queue it in setStar and keep it on flush instead of dropping it.
- Make coalescing atomic, keep the offline collector alive across errors,
  and drain from the persisted offline flag so a queued action flushes on
  a cold start that is already online.
- Give ServerActionSink.flush a default body and drop the no-op overrides.
2026-10-04 00:37:29 +00:00
austinried 0e70268129 Queue stars and scrobbles while the server is unreachable
Actions that cannot be delivered are persisted in a pending_actions
table and replayed once the server is reachable again. Stars coalesce so
only the latest state per item is sent, and replay happens on the next
successful action, when offline mode is turned off, or before a sync.
2026-10-04 00:37:29 +00:00
austinried eb47748ee4 fdroid: address review findings
Query the target project for an existing MR (fork->upstream MRs live upstream), pin checkout to main, use a sha256 marker instead of a duplicated upstream copy, pass GL_TOKEN via GIT_ASKPASS instead of the push URL, use sed -E and check the upstream id parsed, and abort if upstream moves between the guard and the clone.
2026-10-04 00:30:08 +00:00
austinried 9b17920f7e fdroid: split into sync and submit workflows
Sync pulls fdroiddata's current recipe into tools/fdroid/ (refusing to clobber unsubmitted moves). Submit refuses when upstream has moved since the last sync or when there is nothing to submit, then pushes this repo's copy to the GitLab fork on a branch based on current upstream and opens the MR. Drops the API-overwrite script.
2026-10-04 00:30:08 +00:00
austinried 51c8e86c97 tools: read the upstream project id anonymously
A fine-grained token can be scoped to the fork only; fdroiddata is public, so its project id needs no auth.
2026-10-04 00:30:08 +00:00
austinried 5003c9fb48 tools: fix the F-Droid submit API calls
Use PUT for the repository file (it already exists on the fork), create the MR on the fork with target_project_id (the create endpoint ignores source_project_id), and reuse an open MR instead of deleting and recreating the branch. Drop the unused --branch/--title flags and the redundant workflow concurrency/defaults.
2026-10-04 00:30:08 +00:00
austinried fdb3e7ec0a tools: read the GitLab token from GL_TOKEN 2026-10-04 00:30:08 +00:00
austinried 774ac34bae tools: add a submit script for the F-Droid recipe
The fdroiddata recipe is now kept in tools/fdroid/ as the source of truth. tools/fdroid-submit.nu pushes it to the fork and opens the merge request via the GitLab API, so no fdroiddata clone or manual web editing is needed. Also runnable from a Forgejo Actions workflow_dispatch.
2026-10-04 00:30:08 +00:00
austinried 673e492765 docs: centre the screenshots and show five 2026-10-04 00:30:08 +00:00
austinried 94498d71e4 metadata: mirror the README feature list in the store description 2026-10-04 00:30:08 +00:00
austinried 4333522256 docs: rebuild the README for the native rewrite 2026-10-04 00:30:08 +00:00
austinried 5d9d9e5d7e brand: refresh the app icon, logo and feature graphic 2026-10-04 00:30:08 +00:00
austinried cdba272f98 metadata: harden the screenshot generator and changelog test
Only cache a cover after a successful response and drop the file if decoding
fails, so a transient error cannot poison the cache permanently. Fail with a
clear message instead of a bare NoSuchElementException when a demo album has
no songs or the artist list is empty, and normalize line endings before the
changelog length check.
2026-10-04 00:30:08 +00:00
austinried e34363c1ac metadata: show the cross-library search screenshot
Render the dedicated SearchScreen with results across playlists, artists and
albums instead of the album tab's in-list search.
2026-10-04 00:30:08 +00:00
austinried 8e6c1593dc metadata: say Subsonic-compatible and use lowercase subtracks
Drop the "API" from the server compatibility wording, and lowercase the brand
in the title, description, old changelog and the app name to match the logo.
2026-10-04 00:30:08 +00:00
austinried 4bf34ea20a Add a test for the 500-char changelog limit
Both F-Droid and Google Play cap release notes at 500 characters. Check every
metadata changelog in the unit test suite (which CI already runs), and trim the
two old Flutter changelogs that were over the limit.
2026-10-04 00:30:08 +00:00
austinried 3ead9f6fb6 metadata: make the 3.0 changelog plain text under 500 chars
Neither F-Droid nor Google Play renders Markdown in release notes, and both
cap them at 500 characters. Drop the Markdown header and bullets and trim the
copy to fit.
2026-10-04 00:30:08 +00:00
austinried 5bd63d44d7 metadata: only show albums and artists that have cover art
The demo library has plenty of entries with no artwork, which render as
placeholders in the store screenshots. Filter the album and artist lists (and
the home rows) to entries the sync marked with a coverArt id, so no extra
image loads are needed to decide.
2026-10-04 00:30:08 +00:00
austinried 468451c7cb metadata: tint library screens from the playing track
The library accent follows the playback artwork (LibraryRoute derives it from
the current item), so pass the playing album instead of the first list entry,
which made the tab bar disagree with the miniplayer. Inset the miniplayer
progress bar when the tab bar is present, matching AppRoot.
2026-10-04 00:30:08 +00:00
austinried 9976fbeb20 metadata: stack the miniplayer above the library tabs
Render the miniplayer in a column below the screen instead of overlaying it, so
the library tab bar and the list-options FAB are no longer covered. Use Ugress
for the artist screen, drop Various Artists from the home artist rows, and have
the album screen play its first track so the row indicator and the miniplayer
colours match the album.
2026-10-04 00:30:08 +00:00
austinried 0e96b5872c metadata: theme each screenshot and show the miniplayer
Use a different album for the now-playing and album screens (Chillhop Winter,
My latin way) so the store shots show distinct colour schemes, tint the home
screen with the playing album so the logo pops, and render the miniplayer on
the albums, album, artists and search screens. Search now runs "chill".

The generator stages the sync into an on-disk database and caches cover bytes
under the temp dir, and only fetches full-resolution art for the covers shown
large; grid art uses the thumbnail endpoint.
2026-10-04 00:30:08 +00:00
austinried 0410ed026c metadata: show the playing album tint on the home screenshot
Tint the home screen with the artwork theme the app derives from the playing
album, so the subtracks logo and accents pick up a vivid colour (The Knife -
Shaking The Habitual) instead of the default scheme. Switch the now-playing and
album screens to The Polish Ambassador - Future, Sex, Computers, and search for
"chill" so the result grid shows the Chillhop Essentials covers.
2026-10-04 00:30:08 +00:00
austinried 7815f211e4 metadata: render phone screenshots from the Navidrome demo library
Replace the placeholder-art screenshots with renders of a real library, so the
store listing shows real album and artist art and names.

The generator (DemoScreenshotTest, run with `gradle :app:demoScreenshots`) syncs
the public Navidrome demo (demo.navidrome.org) into an in-memory database using
the normal SyncService pipeline, derives the theme colours from the fetched
cover art, and renders each screen. The synced data and cover art stay in memory
and are never written to the repository; only the resulting PNGs are committed.
The test is excluded from the unit-test tasks and is not part of CI.

Also reverts the screenshot fixture palette tweak from the previous commit,
which the real art makes unnecessary.
2026-10-04 00:30:08 +00:00
austinried e7ee6fa9a2 release: bump to 3.0.0 and refresh F-Droid metadata
Bump versionCode to 13 and versionName to 3.0.0. The Flutter app ended at
2.0.0-alpha.3+12 (versionCode 12), so 13 keeps the eventual com.subtracks
release an in-place upgrade.

Metadata was still inherited from the Flutter app:
- add the changelog for versionCode 13
- rewrite the descriptions for the native app
- replace the Flutter phone screenshots with native renders, and mute the
  screenshot fixture palette so the placeholder artwork reads as covers
2026-10-04 00:30:08 +00:00
austinried 836d0636d1 search: tighten the filter bar's vertical padding 2026-10-04 00:30:08 +00:00
austinried f6e087b6ab search: list playlists before artists, albums and songs
Order the result sections smallest-first and apply the review fixes: pad
the filter bar for the navigation bar, keep at least one filter enabled,
hide the clear button on an empty field, drop the unused autoFocus and
bar-modifier parameters, and test the filter mask and section order.
2026-10-04 00:30:08 +00:00
austinried 5d81c2dc41 library: match the list options search rounding to the search field 2026-10-04 00:30:07 +00:00
austinried f54ea54566 search: outline the shared search field to match the list options entry 2026-10-04 00:30:07 +00:00
austinried 3bef469acc search: add a type filter bar and share the search field
Add a filter chip bar above the keyboard to toggle songs, albums, artists
and playlists independently, all on by default and scrolling when it
overflows. Extract one compact 48dp search field and use it in both the
cross-library screen and the library tabs.
2026-10-04 00:30:07 +00:00
austinried 12ad62813e search: localize the cross-library search strings 2026-10-04 00:30:07 +00:00