Commit Graph
100 Commits
Author SHA1 Message Date
austinried b019efbef9 now playing: stop clipping the cover shadow
The cover strip clipped to the 24dp content inset. A landscape cover fills
that inset, so its drop shadow was cut off on the left and right and only
the top and bottom survived, which reads as a stray band hugging the
artwork. A tile that fills the strip's height had the same problem
vertically.

Drop the clip and space the tiles by TILE_GAP instead. The shadow then
renders whole on all four sides while the swipe target and the artwork
keep the size and the content margins they had, and the spacing leaves a
gap between the tiles while dragging between tracks. It is also what keeps
the neighbours off-screen now that the strip is unclipped.

Refs: #173
2026-10-15 13:37:57 +00:00
austinried d171825b5e build: stop test results being restored from the build cache
org.gradle.caching=true plus CI restoring ~/.gradle/caches (which holds
the local build cache) let GitHub CI restore :app:testDebugUnitTest
FROM-CACHE: a re-run reported green without executing a single test. A
cached PASS is also unsound while any test is flaky. Mark test tasks
uncacheable so every invocation actually runs the tests; up-to-date and
incremental behaviour for local dev is unchanged.
2026-10-11 07:06:05 +00:00
austinried 6f997dceb9 test: dump thread stacks when an await times out
The playback/queue wall-clock awaits have flaked on the 4-vCPU GitHub
runner at exactly the 30s limit, a different test each time. That is a
stall, not slowness: the suite finishes ~80s for 795 tests, so no test is
anywhere near 30s normally. The JUnit XML holds the stacks but is not
uploaded, leaving the failures undiagnosable from the CI log. Put every
thread's stack in the failure message, which Gradle prints.
2026-10-11 06:27:42 +00:00
austinried 43ee2d9f27 ci: bump the Gradle cache key
Actions caches are immutable, so the key derived from the Gradle files is
written once and never refreshed; v2 forces a fresh save.
2026-10-11 03:22:40 +00:00
austinried 3c184a71a6 ci: run the Forgejo workflows on the ephemeral runners
The git-runner VMs' nix-docker/bookworm labels are gone, replaced by the
ci-manager's ephemeral pools, so point every Forgejo workflow at them.

The ci job needs the Nix devshell, so it runs on the nix-xlarge pool (a
4 CPU / 8 GiB NixOS VM that already has Nix) with Gradle at -Xmx2g; the
mirror and PR-import jobs don't need Nix and use the ubuntu pool. The
mirror job pins git-filter-repo 2.47.0 itself, since the image's version
may differ and another one would move every commit on GitHub.
2026-10-11 03:22:32 +00:00
austinried 8396f98a85 ci: drop checksum/signature footer from release notes 2026-10-10 14:49:47 +00:00
austinried e396671ac7 feat: auto-sync the library on a timer while the app is open
While the app is in the foreground a timer checks the auto-sync setting and
syncs when due (default every 24h; the interval is selectable from 15 minutes
to 24 hours). It stops when the app is backgrounded. "Sync automatically"
is one setting: Off, Wi-Fi only (default) or Always.

A running sync is shown by a silent, dismissible notification and a small
spinner on the library tab bar's settings button. A manual pull-to-refresh
still holds the pull spinner; only an automatic sync does not. The library
and Home sync actions no-op while a sync is running, and SyncManager
coalesces a request that arrives mid-run into a single follow-up, so repeated
pull-to-refresh cannot pile up runs while adding a server still syncs it.

The time of the last sync attempt is recorded, so a failing server is retried
once per interval rather than every poll. The sync consumer and the auto-sync
loop both survive an unexpected failure instead of silently disabling syncing
for the rest of the process.

The poll sleeps for the interval (capped at 15 minutes) instead of a fixed
minute, the network mode is collected once rather than re-registering a
connectivity callback each poll, and the decision is logged at DEBUG (visible
with Verbose logging) rather than writing to the log file every poll.
2026-10-10 03:36:16 +00:00
austinried 8e7431cc2e fix: stop a queue drag from moving the wrong rows
A drop captured the dragged row's queue position once, at drag start, and
committed on a fire-and-forget coroutine. If the queue renumbered while the
drag was in flight (up-next consumed, a reconcile) or before a prior move had
reconciled, the drop resolved its target against stale coordinates and moved
the wrong entry - the "two middle rows swap on release" symptom.

Resolve the move from the current rows at drop time, and have move() report
whether it was accepted so a refused reorder is not announced as done; a
further move is refused while one is still committing.
2026-10-09 19:01:22 +00:00
austinried 7bbc29a1d6 feat: show the build's commit hash under About
A short, faded commit hash next to the version in Settings > About, so a bug
report can name the exact build. Baked into BuildConfig at build time from
`git rev-parse HEAD` (fixed 7 chars, so the same commit always yields the
same value and the APK stays reproducible); empty, and hidden, on a build
with no git metadata such as a source tarball.
2026-10-09 18:06:38 +00:00
austinried 0a3e839da0 feat: clean up the 1.x app's leftover data on upgrade
The React Native 1.x app shipped as com.subtracks and left an AsyncStorage
database (RKStorage), an older `servers` dump and a downloaded/cached
artwork+audio tree in app-specific storage. v3 reads none of those paths, so
an update would otherwise leave the data behind, reachable only by wiping the
whole app.

Remove them once at startup, gated on one of those 1.x paths existing, so a
fresh install does nothing and an upgrade is cleaned exactly once. The cached
trees can be large, so the delete runs off the main thread. Kept in a single
file so it can be deleted once no installs can come from 1.x.
2026-10-09 17:05:19 +00:00
austinried e0a09b724b chore: drop the F-Droid sync and submit workflows
The recipe is maintained by hand now, so the two Forgejo workflows that
pulled fdroiddata into tools/fdroid, tracked a working-copy hash and
opened the GitLab merge request are more machinery than the channel
needs. Their support files and the GL_TOKEN note go with them; the
F-Droid references in the README, signing docs and architecture notes
describe the channel itself and stay.
2026-10-09 16:47:34 +00:00
austinried e806eaef89 playback: cache streamed audio to stop redundant refetches
A streamed MP3 with no Xing/Info/VBRI header gets an estimated duration
from Mp3Extractor. At end of stream the extractor rewrites it, which
refreshes the timeline; MediaPeriodQueue.updateQueuedPeriods then sees
the playing period's duration change, drops every period after it and
re-enqueues the next one, so the next track is opened from byte 0 again.
After a seek near the end that repeats a few times.

Route streamed bytes through a bounded SimpleCache (DefaultDataSource ->
CacheDataSource -> KnownLengthDataSource -> OkHttp). A re-opened next
period is served from the bytes the prefetch already committed, so the
redundant network fetches stop without changing playback length or
gapless padding. DefaultDataSource still handles file:/content:, so
downloads bypass the cache.

The cache is a process-scoped 64 MiB LRU in cacheDir/stream-cache; a
construction failure falls back to no cache so playback still starts.
2026-10-09 13:36:55 +00:00
austinried 0cef3657bc build: make the release APK reproducible
AGP's dependency-metadata signing block (0x504b4453) is encrypted per
build, so two otherwise byte-identical release APKs differed in that
block. Disable it in the APK with dependenciesInfo.includeInApk = false;
the bundle keeps its copy for Play.
2026-10-09 13:34:38 +00:00
austinried 20c6ba526f scrobble: only dedupe when the track was already announced
The now-playing suppression keyed off the item id alone, so a track loaded while paused (which sets the tracked id without announcing) never sent its first now-playing when playback then started from the beginning. Gate the suppression on nowPlayingSent as well, and cover that case plus the transition at the combine boundary.
2026-10-09 12:31:48 +00:00
austinried cfb663c250 scrobble: send now playing once per track change
A track transition resets the player position, and the scrobble policy's restart heuristic (same song, position drops to ~0) then re-announced the track we just left and, when the new track's position jittered back to zero, announced it twice. Suppress the now-playing when the restart is the same song; the submission logic still resets so a genuine replay is scrobbled again.
2026-10-09 12:31:48 +00:00
austinried ac1e62fc02 library: drop shuffle fab from album and playlist detail 2026-10-09 09:15:36 +00:00
austinried adabe21d63 library: cover starred/downloaded shuffle in tests, dedupe list predicate 2026-10-09 09:15:36 +00:00
austinried 0bdac8fa22 library: tap-to-top and shuffle fab for long song lists 2026-10-09 09:15:36 +00:00
austinried 15d77f998f build: drop the agent-specific gradle wrapper from the devshell
The flock/nice/worker-cap wrapper serialised local builds to protect the
shared box. That is now handled at the machine level (a GRADLE_OPTS cap
in nix-home), so the devshell goes back to the plain nix Gradle.
2026-10-09 09:11:54 +00:00
austinried 779131c766 logging: address review of the breadcrumbs
Drop the non-distinct song comparison (sum of album song counts double-counts compilations), skip redaction for dropped lines via a level check, defer the startup prune off the main thread and cap the writer queue, and only warn about a stall against the still-current player. Reward the scrobble info line and cover the new preferences/level toggles with tests.
2026-10-09 07:58:55 +00:00
austinried 9695d83b1a logging: make verbose capture everything, tidy the toggle text
Verbose now drops the file level to VERBOSE (including HTTP request lines) rather than DEBUG, and the switch no longer warns about larger files.
2026-10-09 07:58:55 +00:00
austinried a8d6111aaa logging: drop the noisy album count from the sync summary
sum(artist.albumCount) double-counts compilation albums, so it read higher than the albums actually synced; keep only the song total, which matches exactly.
2026-10-09 07:58:55 +00:00
austinried d4152ba3a1 logging: add a stall watchdog and server-vs-synced counts
Warn when playback stays buffering for 15s while play-wanted, and log what the server reports for album/song totals next to what was synced and pruned.
2026-10-09 07:58:55 +00:00
austinried 79fe15da91 settings: move the log actions into a Troubleshooting section
Share logs and Verbose logging move out of About into their own Troubleshooting section.
2026-10-09 07:58:55 +00:00
austinried 714440fb25 logging: add release-visible breadcrumbs and a verbose toggle
Playback now logs each track transition and end at info (title/artist/album/source/reason), sync logs per-entity counts and pruned rows, and the Subsonic client logs server error codes and the token-auth fallback. Add a Verbose logging setting that drops the file level to debug at runtime (persisted) so a support session can capture full detail without flooding normal release logs.
2026-10-09 07:58:55 +00:00
austinried a2d89f28a6 logging: add breadcrumbs for common actions
Log sync start/finish/failure, star/unstar, scrobble now-playing/submission (and failures), download queue/start/complete/fail/remove, offline-mode toggles, network-mode and active-source changes, and pending-action queue/replay/drop. Enrich playback logs with title/artist/album, file-vs-http source and the transition reason, and render C.TIME_UNSET durations as 'unset'. Make console logging non-fatal so a logcat write never breaks the caller, and never prune the file being written.
2026-10-09 07:58:55 +00:00
austinried b2b02e7c05 logging: log upsert conflicts at verbose
Room's @Upsert insert-then-update fires a uniqueness conflict on every existing-row write (e.g. each playback-cursor save), which is normal control flow and flooded the debug file at debug level. Move it below the file threshold so the shared log stays quiet.
2026-10-09 07:58:55 +00:00
austinried 9d783b2301 logging: address review of the SQL logging path
Only downgrade a uniqueness conflict on an INSERT, so genuine duplicate-key failures still surface. Clear old zips from the dump directory before exporting, roll the log file over at midnight, prune on roll/day-change instead of on every line, and drop the unused files-path from the FileProvider config.
2026-10-09 07:58:55 +00:00
austinried 713d9a976a logging: log cancelled HTTP requests at verbose
Coil cancels in-flight image requests routinely, so an IOException from a cancelled call is not a warning.
2026-10-09 07:58:55 +00:00
austinried c262e58a96 logging: fix SQL timing and upsert-conflict false alarms
Room's statement cache resets a statement when returning it to the cache, so starting the timer at reset counted idle time in the cache and reported normal queries as tens of seconds. Start timing on the first step instead, and finish on the completing step. Also downgrade Room @Upsert uniqueness-conflict errors (its insert-then-catch-then-update control flow) from ERROR to DEBUG, and log successful HTTP requests at VERBOSE so debug logs aren't flooded by cover-art requests.
2026-10-09 07:58:55 +00:00
austinried eb5019ea5e logging: use Share logs and restore its Flutter translations
Align the Settings label with the old Flutter wording and port the 19 existing settingsAboutShareLogs translations. The subtitle was new text with no prior translation, so drop it rather than ship an untranslated string.
2026-10-09 07:58:55 +00:00
austinried 8fe26a36b1 logging: make the writer total and harden export
Never let a log-write or export failure reach the uncaught handler: wrap the async writer and crash path, synchronize export with rotation, and tolerate vanished files. Redact the Logcat stack-trace copy, stop leaking rows=0 on writes, shut down the previous writer on re-init, and drop unused API. Strengthen tests for the batched write path, rotation count and throwable redaction.
2026-10-09 07:58:55 +00:00
austinried 7a890094fc logging: add file-based logging and a Send logs option
Capture logs to rotating app-private files with Subsonic credential and URL basic-auth redaction, mirror to Logcat, and capture uncaught crashes. Log slow SQL (>100ms), SQL errors, migrations and DB lifecycle via a decorating SQLiteDriver. Add a Send logs row in Settings that zips the logs and opens the share sheet through a FileProvider.
2026-10-09 07:58:54 +00:00
austinried 4012cc0272 build: harden the gradle guard wrapper
- cap workers via GRADLE_OPTS instead of a CLI flag, so a caller passing
  --max-workers no longer collides and fails the build
- keep the lock under $XDG_RUNTIME_DIR (falling back to ~/.cache) rather
  than a world-writable /tmp path
- let --stop/--status/--version bypass the lock, and announce when a build
  is waiting for it
- drop the redundant GITHUB_ACTIONS check; the CI runners all set CI
2026-10-09 07:48:00 +00:00
austinried 336fd9c49f build: guard local gradle runs against exhausting the box
The devshell `gradle` now serialises local builds with flock and runs
them under `nice` with capped workers, so several agents building at
once queue instead of piling up daemons and compile workers and taking
the machine down. With CI/GITHUB_ACTIONS set it execs Gradle directly,
leaving CI untouched. Also shorten the Gradle daemon idle timeout so
idle daemons are reaped sooner.
2026-10-09 07:42:05 +00:00
austinried 5f138bde6a artwork: cover the non-square and thumbnail reload paths
The fix keyed the request remembers in all three CoverArt branches; assert
each of them survives a re-salted URL for the same cacheKey, not just the
square grid path.
2026-10-09 07:03:49 +00:00
austinried 32360fac08 artwork: keep the cover request stable across recompositions
CoverArt keyed its remember blocks on the whole CoverArtRef, whose url is
freshly salted on every call. A recomposition therefore built a new
ImageRequest, Coil restarted the load and dropped the current drawable, so
the image flashed back to the letter placeholder before resolving from
cache. Key the request and the load-state remembers on the ref's stable
cacheKey instead.
2026-10-09 07:03:49 +00:00
austinried 206ac8988f net: include the Android release in the user agent 2026-10-09 06:33:22 +00:00
austinried 4fad6a78ec net: use the lowercase subtracks name in the user agent 2026-10-09 06:33:22 +00:00
austinried 2439368e4e net: pin the user agent value and test the shared client
Build the shared OkHttpClient through UserAgent.httpClient() so the unit test exercises the same client the app uses, assert the exact 'Subtracks (Android; <version>)' value rather than the property it writes, and run the test under the Android runner.
2026-10-09 06:33:22 +00:00
austinried 92c6f6945f net: use the AppName (platform; version) user agent form 2026-10-09 06:33:22 +00:00
austinried bcd7bbf23a net: send the subtracks user agent on every request
Only SubsonicClient tagged requests with 'subtracks/android'; playback (Media3 OkHttpDataSource), downloads (DownloadManager) and artwork (Coil/OkHttpArtworkFetcher) fell back to the OkHttp or platform default. Add a single UserAgent with the app version, apply it to the shared OkHttp client, the streaming data source factory and the DownloadManager request, and drop SubsonicClient's now-redundant header.
2026-10-09 06:33:22 +00:00
austinried ce9e3e89a7 build: pair the nightly icon background with its amber fill
Set the nightly launcher background to a dark tint of the amber mark
(#1A1200) instead of the magenta-tinted near-black, mirroring how the
standard icon's background is a dark tint of its magenta fill.
2026-10-09 05:27:05 +00:00
austinried 66ee837cbb build: give the nightly a distinct icon and drop the longer label
The 17-character 'subtracks nightly' label just truncates in the launcher,
so revert the app_name override (debug and nightly show 'subtracks' like
the release) and distinguish the nightly with a recoloured launcher icon
(amber mark) instead. The debug and nightly variants share src/nightly/res,
so installDebug and installNightly look the same.
2026-10-09 05:20:55 +00:00
austinried de4bd473f9 ci: upload the nightly APKs as separate raw artifacts
archive: false only supports a single file, so upload each of the two
APKs and SHA256SUMS as its own unzipped artifact instead of one zip.
2026-10-08 19:51:03 +00:00
austinried 7f421470b2 release: gzip the R8 mapping before attaching it 2026-10-08 19:23:56 +00:00
austinried 4bea164906 release: split the Play key write, guard the tag and attach the mapping
- write the Play service account in its own step so fastlane never has
  the secret in its environment (only the 0600 file path)
- fail the build when the tag does not match versionName
- serialize releases with a concurrency group
- attach the R8 mapping.txt to the GitHub release
2026-10-08 19:21:26 +00:00
austinried 9d43b6d104 address review: persist nightly checksums, guard the changelog, tidy CI
- write SHA256SUMS into the subtracks-nightly artifact
- assert at least one changelog exists in the metadata test
- drop the unused assembleDebug from the Forgejo CI
- only claim the AAB was uploaded to Play for stable releases
- clean up the export-certificate keystore with a trap
2026-10-08 19:12:56 +00:00
austinried 323bfde41b ci: fold the nightly artifact into the CI job
Drop the separate build-nightly workflow: on a push to main the CI job
uploads the debug and nightly APKs as the subtracks-nightly artifact, so
the nightly variant is built once instead of twice (and there is no
second Nix/cache setup).
2026-10-08 18:53:00 +00:00
austinried 18d06f750d ci: build the nightly variant in CI
Add :app:assembleNightly to the CI Gradle invocation so the R8 minify,
resource shrinking and packaging path is exercised on every PR (signed
with the committed debug key, so no secrets are needed).
2026-10-08 18:53:00 +00:00
austinried 44bc3f927e address review: guard the release pipeline and harden the metadata test
- fail the release before uploading to Play when the versionCode has no
  changelog
- rely on supply's defaults for release_status and metadata_path
- enforce 8-bit PNGs and reject tRNS chunks in MetadataConstraintsTest
- name the nightly artifact's APKs distinctly
- fix doc drift and the stale libraries uniqueId
2026-10-08 18:53:00 +00:00
austinried b0a0bc1867 build: label the nightly/debug variants distinctly 2026-10-08 18:53:00 +00:00
austinried e700c99796 docs: label the fingerprints as keytool/apksigner format 2026-10-08 18:52:44 +00:00
austinried 6af43c9c16 docs: drop the private Play Console references from signing/README 2026-10-08 18:52:44 +00:00
austinried 4f04129b0e docs: show both fingerprint formats with tool labels 2026-10-08 18:52:44 +00:00
austinried 772ab5f563 docs: note the fingerprint formats and normalize the release-notes one 2026-10-08 18:52:44 +00:00
austinried 1eb7c136bf docs: link F-Droid's signing keys and the nightly keystore 2026-10-08 18:52:44 +00:00
austinried 17ba581f88 docs: list F-Droid before nightly in the signing channels 2026-10-08 18:52:44 +00:00
austinried 76f2e4014c docs: put the release certificate before the nightly section 2026-10-08 18:52:44 +00:00
austinried 3d2ec5e931 docs: unwrap the lines in signing/README.md 2026-10-08 18:52:44 +00:00
austinried 368f53d589 release: attach the signed AAB to the GitHub release
Publish the exact bundle sent to Play alongside the APK, cover both in
SHA256SUMS and the release notes, and document verifying the AAB.
2026-10-08 18:52:44 +00:00
austinried 3f3db71f14 docs: commit the release signing certificate and fingerprint
Add the release key's public certificate (signing/release-certificate.pem)
and its SHA-256 to signing/README.md.
2026-10-08 18:52:44 +00:00
austinried e60b2d9910 rename the next build back to nightly
Rename the build type, workflow, artifact and app id (com.subtracks.nightly)
from next to nightly, and update the signing docs to match.
2026-10-08 18:52:44 +00:00
austinried f6d1d548e4 docs: correct the signing notes for Play App Signing
Google re-signs Play-delivered apps, so the release key is only the
upload key there and signs the AAB we send; the installed app verifies
against Google's app signing certificate. The release key also signs the
GitHub-release APK.
2026-10-08 18:52:44 +00:00
austinried e74360fe07 docs: link the verification steps under the download badges 2026-10-08 18:52:44 +00:00
austinried d3373261d2 release: publish checksums and the signing certificate
Print the artifact SHA-256 and the APK signer certificate in the release
log, attach SHA256SUMS and the public release certificate to the GitHub
release, list the APK and certificate SHA-256 in the release notes, print
the next build checksums, and commit the next build's public certificate
with verification steps in signing/README.md.
2026-10-08 18:52:44 +00:00
austinried 7f4e9703d4 test: cite the store docs in the metadata constraint tests
Quote and link the Play preview-asset requirements and recommendations
and the F-Droid metadata limits alongside the checks they justify.
2026-10-08 18:52:44 +00:00
austinried ff8011eaf4 release: only publish to Play for a stable tag
Gate the Play upload on a full vX.Y.Z tag and mark other tags as
prereleases. Fold the changelog length limit into MetadataConstraintsTest
and make its image checks reject anything but PNG or JPEG. Keep the
decoded keystore at 0600 and note in .forgejo/README.md that the GitHub
CI now factors the build into composite actions.
2026-10-08 18:52:44 +00:00
austinried b4b5f66d81 ci: track the build composite action
The .gitignore build/ pattern also matched .github/actions/build, so the
action was never committed even though the workflows referenced it.
Negate the rule for that path.
2026-10-08 18:52:44 +00:00
austinried 8fb930c2ce ci: share the build as a composite action
Replace the reusable workflow with .github/actions/build plus a nested
nix setup action, invoked as a step. CI and the next build stay a single
job (no extra runner or Nix restore); the release keeps its build/publish
split for credential isolation.
2026-10-08 18:52:44 +00:00
austinried af3741a549 ci: share the build and isolate the release credentials
Extract checkout, nix, signing and the Gradle run into a reusable build
workflow used by CI, the next build and the release build job. The build
job holds only the signing secrets; the publish job is the only one with
a write token and the Play credential. The Play service account is now
written to a 0600 file and passed via --json_key instead of on argv.
2026-10-08 18:52:44 +00:00
austinried fded9949af ci: stop exposing the checkout token and the keystore to later steps
Set persist-credentials: false so the contents:write token is not left
in .git/config where every later step (including the Gradle build) could
read it, and delete the decoded keystore once signing is done.
2026-10-08 18:52:44 +00:00
austinried 5c219c9f06 test: guard the store metadata against the store limits
Add metadata checks (title, short/full description, icon, feature
graphic, phone screenshots) at the stricter of the Play and F-Droid
limits, and drop the trailing period from the short description.
2026-10-08 18:52:44 +00:00
austinried 7145b9c3c7 test: write the demo screenshots as 24-bit no-alpha PNGs
Re-encode each capture to opaque RGB so the store screenshots copied
into metadata/ already satisfy Play's rule, instead of depending on a
manual palette-free re-encode.
2026-10-08 18:52:44 +00:00
austinried 4db8645691 docs: note the Play 24-bit screenshot requirement 2026-10-08 18:52:43 +00:00
austinried eaab5368d0 play: push the full store listing and make the screenshots compliant
Drop the fastlane --skip_upload_* flags so the title, descriptions,
icon, feature graphic and screenshots upload alongside the AAB, and
re-encode the phone screenshots as 24-bit no-alpha PNGs, which is what
Play requires.
2026-10-08 18:52:43 +00:00
austinried 6b6a99e51c release: create the GitHub release with the gh CLI
Drop the third-party action in favour of GitHub's own gh CLI, which the
hosted runners already provide.
2026-10-08 18:52:43 +00:00
austinried 21d6c57bc2 ci: match the repo GitHub runner and action versions
Use ubuntu-26.04, actions/checkout v7 and actions/upload-artifact v7,
and cap the cached Nix store at 6G, matching .github/workflows/ci.yml.
2026-10-08 18:52:43 +00:00
austinried 650d8c49e6 release: upload to Google Play with fastlane
Replace the unpinned third-party upload-google-play action with fastlane
supply from a small nix release devshell, and pin the remaining
third-party GitHub release action to a commit SHA.
2026-10-08 18:52:43 +00:00
austinried c76a79c3ed ci: run the GitHub builds in the nix devshell
Drop setup-java, setup-android and the Gradle wrapper in favour of the
flake's pinned JDK, Gradle and Android SDK, restoring the Nix store with
cache-nix-action, so the release and next builds share the dev toolchain.
2026-10-08 18:52:43 +00:00
austinried 23911fceb5 ci: build signed debug and release next builds on every main push
Commit a public debug keystore and point the debug signing config at it,
and add a next build type (release-like, same key). Both carry the .next
application id, so next builds install alongside the release and update
each other without exposing the release key. A GitHub workflow builds
both APKs on every push to main and uploads them as artifacts.
2026-10-08 18:52:43 +00:00
austinried f506ac0410 release: publish signed builds on v* tags
Set applicationId to com.subtracks and add a GitHub Actions workflow
that, on a v* tag, builds and signs the release APK and AAB, creates
the GitHub release with the APK, and uploads the AAB to Google Play.
Release notes come from the matching metadata changelog.

Signing reads the keystore from the environment
(RELEASE_KEYSTORE, RELEASE_KEYSTORE_PASSWORD, RELEASE_KEY_ALIAS,
RELEASE_KEY_PASSWORD) and falls back to the debug key locally.
2026-10-08 18:52:43 +00:00
austinried 293561d81f settings: guard the About links and align the support label 2026-10-08 17:56:34 +00:00
austinried 2de31735f7 settings: use leading icons for the About rows and drop the heart emoji 2026-10-08 17:19:18 +00:00
austinried 327aa08763 settings: restore the About translations from the Flutter app 2026-10-08 17:19:18 +00:00
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