android: detekt's return limit, on two functions I wrote after it caught me once
Desktop (Tauri) / Windows installer (cross-compiled) (push) Successful in 3m12s
Desktop (Tauri) / Tauri desktop (Linux) (push) Successful in 5m26s
Desktop (Tauri) / Update manifest (push) Successful in 9s
Android / Kotlin + Rust (APK) (push) Successful in 8m33s

`dev` went red on 1e2b42a and nobody was watching — the CI wait was killed with the
session, so the push was never confirmed. Checked on the way back in.

Both findings are ReturnCount: four exits against a limit of two. The same rule
caught continueChecklist earlier the same day, which is the annoying part — I had
the lesson and wrote two more guard-clause ladders anyway.

checkInBackground becomes a `when`, which it wanted to be regardless: it is four
mutually exclusive situations and one action, and the ladder made that read like a
sequence of unrelated escapes.

onWifi folds its three null checks into one nullable chain. Same behaviour, and the
`caps != null &&` reads as what it is — an uncertain answer being treated as no.
This commit is contained in:
2026-08-26 10:01:24 -04:00
parent 1e2b42af25
commit f92a3d0a99
2 changed files with 30 additions and 27 deletions
@@ -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,30 +88,31 @@ 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 {
lastCheckAt = now state.busy -> Unit
runCheck(fetch = true)
// 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
runCheck(fetch = true)
}
}
} }
private fun runCheck(fetch: Boolean) { private fun runCheck(fetch: Boolean) {