Make "check on launch" actually check on launch

The switch has been in Settings since the updater landed and has never
been read by anything: there was no launch-time check to turn on or off.
It is now wired to one.

It speaks only when there is a build to install. An app that is already
current says nothing, and neither does one that could not reach GitHub —
opening the app is not asking a question, and a phone with no signal
should not be told off for it once a launch. The button in Settings *is*
a question and still answers every time, up to date and failures alike.
That rule is `launchOffer`, next to the channel rule and tested with it.

Two things it is careful about:

The setting is read straight from the store rather than from the state
the screens are drawn with. That state begins as the defaults — where
checking is on — and is replaced a moment later by what is stored, so a
check driven off it would have fired on the default before ever learning
it had been switched off.

The prompt waits for the splash to clear before appearing, while the
check itself runs behind it. The look costs no time and cannot land on
top of the tartan.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01DCuRESmSZeJoJond43itw2
This commit is contained in:
Claude
2026-08-08 03:29:42 +00:00
parent 3372adf87a
commit cd7f86f47b
5 changed files with 142 additions and 0 deletions
@@ -117,6 +117,27 @@ class UpdatePolicyTest {
)
}
// -- What a check on launch is allowed to say ---------------------------
@Test
fun `a check on launch speaks only when there is something to install`() {
assertEquals(devBuild, launchOffer(UpdateVerdict.Install(devBuild)))
}
@Test
fun `a check on launch says nothing when there is nothing to say`() {
assertNull("an up-to-date app has no business interrupting", launchOffer(UpdateVerdict.UpToDate))
assertNull(
"a phone with no network has nothing to apologise for",
launchOffer(UpdateVerdict.Refused("Unable to resolve host api.github.com")),
)
// Including the refusal that this whole change exists to produce: being
// shown the other channel's build is not a thing to be told about.
assertNull(
launchOffer(verdictFor(productionBuild, UpdateChannel.DEV, installedVersionCode = 1)),
)
}
/**
* The two sequences genuinely overlap — production is on 40 while dev is on
* 69 — so a build's own number is meaningless on the other channel. This is