Checklists in the body, colour from tags, and commit-derived CalVer #4

Merged
bvandeusen merged 73 commits from dev into main 2026-08-29 13:39:45 -04:00
2 changed files with 30 additions and 27 deletions
Showing only changes of commit f92a3d0a99 - Show all commits
@@ -79,10 +79,12 @@ object AppUpdate {
* Every uncertain answer is `false`: the cautious one costs nothing. * Every uncertain answer is `false`: the cautious one costs nothing.
*/ */
fun onWifi(context: Context): Boolean { fun onWifi(context: Context): Boolean {
val manager = context.getSystemService(ConnectivityManager::class.java) ?: return false val caps =
val active = manager.activeNetwork ?: return false context
val caps = manager.getNetworkCapabilities(active) ?: return false .getSystemService(ConnectivityManager::class.java)
return caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) && ?.let { manager -> manager.activeNetwork?.let(manager::getNetworkCapabilities) }
return caps != null &&
caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) &&
caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED) caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)
} }
@@ -88,31 +88,32 @@ class UpdateViewModel(
* when one ran recently: flicking between two apps is not a request to re-check. * when one ran recently: flicking between two apps is not a request to re-check.
*/ */
fun checkInBackground() { fun checkInBackground() {
if (state.busy) return
// Already fetched and waved away — say so again. "Later" is for that sitting,
// not forever, and without this the early return below would mean a single
// dismissal silenced the update permanently. Which is precisely the "lost"
// this whole path exists to prevent.
if (state.ready) {
if (state.nagDismissed) state = state.copy(nagDismissed = false)
return
}
// Already found one and never fetched it — almost always because the last look
// happened on mobile data. Retry the FETCH rather than the check, and ignore
// the interval: this is what makes an update found on the train arrive when
// the person gets home, instead of waiting out six hours first.
if (state.available != null) {
if (AppUpdate.onWifi(context)) viewModelScope.launch { download() }
return
}
val now = System.currentTimeMillis() val now = System.currentTimeMillis()
if (now - lastCheckAt < CHECK_INTERVAL_MS) return when {
state.busy -> Unit
// Already fetched and waved away — say so again. "Later" is for that
// sitting, not forever, and without this branch a single dismissal would
// silence the update permanently. Which is precisely the "lost" this whole
// path exists to prevent.
state.ready -> if (state.nagDismissed) state = state.copy(nagDismissed = false)
// Found one and never fetched it — almost always because the last look
// happened on mobile data. Retry the FETCH rather than the check, and
// ignore the interval: this is what makes an update found on the train
// arrive when the person gets home instead of waiting out six hours.
state.available != null ->
if (AppUpdate.onWifi(context)) viewModelScope.launch { download() }
// Flicking between two apps is not a request to re-check.
now - lastCheckAt < CHECK_INTERVAL_MS -> Unit
else -> {
lastCheckAt = now lastCheckAt = now
runCheck(fetch = true) runCheck(fetch = true)
} }
}
}
private fun runCheck(fetch: Boolean) { private fun runCheck(fetch: Boolean) {
viewModelScope.launch { viewModelScope.launch {