~ knotless Search/
↑↓ move↵ open⇧↵ all results

grain.social/grain-android

1 collaborator
0
1

Builds older than the server's minimum ask to be updated

Merged
Merged chadtmiller.com asked to merge into main from grain.social/grain-android:update-gate

What changes

The gate. UpdateGate fetches client.baseUrl + /app-version.json with the app's OkHttp client (5 s call timeout) on every resume, the first included, and compares android.minimumVersionCode with the running versionCode from PackageInfo (BuildConfig is off in this project). Below it, UpdateRequiredScreen replaces 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 opening market://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 missing android key or minimumVersionCode means 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), and MainViewModelTest updated. ./gradlew :app:testDebugUnitTest on JDK 21: 684 tests, 0 failures. Not run: a release build, R8, lint, or the app on a device or emulator.

Shortcomings

  • Strings are inline in Kotlin, as every other screen's are, not in strings.xml.
  • A minimumVersionCode as a string or decimal counts as malformed, keeping a cached minimum, rather than as no minimum.
  • The debug build installs as social.grain.debug, but Update opens the social.grain listing.
  • Robolectric fails on JDK 27 ("Unsupported class file major version 71"); local runs need JDK 21.

🤖 Generated with Claude Code

chadtmiller.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 minimumVersionCode can't overwrite a good cached floor is a sound choice.
  • Concurrency: tryLock skips 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