Builds older than the server's minimum ask to be updated
Mergedchadtmiller.com added 1 commit
Approved
This adds an UpdateGate that reads /app-version.json on every resume. It caches the last good minimum in SharedPreferences. A build below the minimum gets a full-screen UpdateRequiredScreen in place of the app.
I found nothing that must change. I read the code and did not run it, and I did not look at the checks.
- Failure handling: a non-2xx response, a network error or malformed JSON leaves the cache alone, and with nothing cached the gate stays open. Parsing by hand so a mistyped
minimumVersionCodecan't overwrite a good cached floor is a sound choice. - Concurrency:
tryLockskips overlapping checks and always unlocks. - Tests: they cover the cache across a failed fetch, a lowered minimum, an offline launch and malformed bodies.
The listed shortcomings are acceptable. The debug build opening the release listing is the one to remember before you test on a debug install.
chadtmiller.com added 1 commit
Approved
Carried forward: the change this pull makes at cfedcf02909be3783d994e29f5d34e1a0d519e73 is the one reviewed at 436e52845bee67755626e22896ecd70454f9be10, so that review stands. What is new comes from its base.
Merged as 6964bff

What changes
The gate.
UpdateGatefetchesclient.baseUrl+/app-version.jsonwith the app's OkHttp client (5 s call timeout) on every resume, the first included, and comparesandroid.minimumVersionCodewith the runningversionCodefromPackageInfo(BuildConfig is off in this project). Below it,UpdateRequiredScreenreplaces the app's content, sign-in included, and swallows back: "Update Grain", "This version of Grain is no longer supported. Update to keep using it.", and an Update button openingmarket://details?id=…for a Play listing, then the URL itself.Offline and failures. The last minimum read is kept in SharedPreferences (
grain_app_version) and read at startup, so a blocked build is blocked from the first frame. Only a successful, valid response changes it: a non-2xx, a network error or malformed JSON leaves it alone, and with nothing cached the app does not block. A missingandroidkey orminimumVersionCodemeans no minimum and clears the cache.Why
grain's server is moving off hatk, and the builds installed before the new API will stop working when it does. Today the app has no way to tell anyone to update. This ships first, on the current server, so at cutover the minimum is raised and older builds show this screen instead of errors. The server half is grain.social/grain's "Serve minimum app versions at /app-version.json".
How it was checked
13 tests in
UpdateGateTest.kt(comparison, the document, missing keys, malformed bodies, a 503 and an offline client, the cache across a failed fetch, a lowered minimum, the store fallback, the market URI, the screen's copy and button), andMainViewModelTestupdated../gradlew :app:testDebugUnitTeston JDK 21: 684 tests, 0 failures. Not run: a release build, R8, lint, or the app on a device or emulator.Shortcomings
strings.xml.minimumVersionCodeas a string or decimal counts as malformed, keeping a cached minimum, rather than as no minimum.social.grain.debug, but Update opens thesocial.grainlisting.🤖 Generated with Claude Code