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.
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.
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.
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.
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.
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.
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.
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.
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
Move every exposed UI string into res/values/strings.xml (+ plurals) and
read it via stringResource/pluralStringResource or getString, with 27
locale files carried over from the Flutter app's Weblate translations.
Only strings whose English is unchanged reuse an old translation; reworded
ones get fresh keys.
Persisted download failures become a DownloadError enum mapped to string
resources (stored by name, unknown legacy values degrade to Failed), the
per-app locale config is generated by AGP, and StringResourcesTest guards
locale keys and placeholders.
PlaybackController.now exposes awaitStopped() so the tests that own its DB can join its scope before db.close(); close() stays cancel-only so AppModuleTest (Main dispatcher) cannot deadlock. Restores db.close() in the tests that have no leaking scope, closes the SyncManager AddSourceViewModelTest was leaking, replaces the duplicated poll with the existing awaitCoverArt helper, and trims the testLogging block to the one line that does anything.
The ThemeTest failure was cross-test contamination, not a theme bug. A Room invalidation-tracker query keeps running on a cancelled flow collector after the in-memory DB is closed or finalised, throws "no such table: sources", and kotlinx-coroutines-test records it and fails the next Compose test with UncaughtExceptionsBeforeTest. Which test it lands on is just ordering/load luck.
Close repositories the way DownloadRepository already did: cancel and join the scope, so Room work cannot outlive close. Stop closing the in-memory test DBs; the DB is dropped and collected once the test releases it, instead of being torn down under a pending query. Wait for the stored ref in offlineModeStillServesDownloadedArtwork instead of asserting once, since it was waiting on a different flow than the state coverArt reads. Print full stack traces for failed tests in CI.
Run the integration harness from its own devshell so the server packages stay out of the everyday shell. Add lms and a Nextcloud Music instance to the harness and extend the suite with auth-fallback, media, error, write round-trip, multi-source and prune tests.
The auth test caught that lms answers OpenSubsonic error 42 (not 41) when it refuses token auth, so the client now falls back on both codes.
desugar_jdk_libs is bundled by core library desugaring rather than the
runtime classpath, so it never reached the generated licence list. Add it
plus its GPL-2.0-with-Classpath-Exception text via the plugin's manual
config directory.
Add AboutLibraries, whose Android Gradle plugin collects the licence and
attribution of every dependency into a generated resource at build time,
and surface it under a new About section in Settings.
Verify the Subsonic token hash and salt freshness, and fall back to per-album
songs when the empty-search probe fails with an I/O error instead of
aborting the whole sync.
Add a real-server sync test that mirrors navidrome and gonic into Room and
reads the rows back, plus SyncManager coverage for the success, coalescing
and mid-sync-failure paths.
Cover the sort mapping and delete/ping ViewModels, the download engine's
request flags, the queue helpers, download prefs and migration data
preservation.
Drop the leaked collectors in the ViewModel/queue tests, document the
screenshot tests as render smoke tests, bind a real search term in the
trigram plan test, and assert a positive in-flight invariant alongside the
concurrency timing checks.
AGP 9.4.1 / Gradle 9.7.1 / Kotlin 2.3.10 (AGP built-in Kotlin), compileSdk and targetSdk 37, Room 3 with KSP schema export, and nushell/navidrome/gonic in the devshell plus a direnv .envrc. Adds a dedicated :app:integrationTest task and excludes the integration class from the unit test tasks.
- flake: pin build-tools and platform-tools to 37.0.0 instead of "latest", matching build.gradle.kts
- robolectric: run at sdk 35 (targetSdk) and re-record the golden
- manifest: allowBackup off, drop not-yet-used permissions
- theme: drop no-op status/nav bar colors (edge-to-edge)
- deps: kotlin 2.1.20, drop unused deps and test runner
- wrapper: regenerate jar from 8.11.1, add distributionSha256Sum
- remove stale README
replace the flutter project with a gradle/compose android app:
- nix flake dev shell (jdk21, android sdk 35, build-tools 37, gradle)
- material3 app with a placeholder screen
- roborazzi/robolectric screenshot tests with one golden
- carry over the existing launcher/notification assets