Origin: code review of PR #45 (screen-lock app gate). Simplification/reuse cleanup in the app-lock UI plumbing.
Problem
Several small divergences from established codebase patterns, bundled:
SettingsScreen uses Toast (the app's only Toast) for the app-lock rejection message, instead of the SnackbarHostState + Scaffold(snackbarHost=…) + consume pattern every other screen uses (canonical: MailboxScreen). It also pre-resolves the string via LocalResources where the Toast.makeText(Context, @StringRes Int, …) overload takes the resId directly. The Toast is invisible to Compose test semantics, so this path can't be covered by the existing SettingsScreenTest style.
AppLockGateHost hand-rollsDisposableEffect + LifecycleEventObserver where androidx.lifecycle.compose.LifecycleEventEffect is already the codebase idiom (SettingsScreen, BatteryOptimizationScreen); and hand-rolls a findFragmentActivity()ContextWrapper walk where androidx.activity.compose.LocalActivity (activity-compose 1.12.4 is already on the classpath) provides it. The authenticate lambda and the activity lookup are also rebuilt on every recomposition (not remembered).
Dead API:AppLockManager.availability() and the four-value AppLockAvailability enum have no production caller (only the interface, its impl, and a test fake) — and the BIOMETRIC_STRONG or DEVICE_CREDENTIALcanAuthenticate combination they wrap is unsupported on API 29 (returns BIOMETRIC_ERROR_UNSUPPORTED), so the mapping is wrong for the minSdk if it were ever used.
_uiState is hand-mirrored from gate.state at every mutation site (they can silently disagree on PROCEED/DISABLE paths); it is derivable as f(gate.state, error).
Suggested fix
Switch the message to the snackbar pattern (add a snackbarHost to Settings' existing Scaffold); replace the hand-rolled lifecycle observer with LifecycleEventEffect and the context walk with LocalActivity, remembering the derived activity/lambda; delete availability()/AppLockAvailability (keep isDeviceSecure() and AUTHENTICATORS); derive AppLockUiState from the gate via a single publish helper.
Origin: code review of PR #45 (screen-lock app gate). Simplification/reuse cleanup in the app-lock UI plumbing.
## Problem
Several small divergences from established codebase patterns, bundled:
- **`SettingsScreen` uses `Toast`** (the app's only `Toast`) for the app-lock rejection message, instead of the `SnackbarHostState` + `Scaffold(snackbarHost=…)` + consume pattern every other screen uses (canonical: `MailboxScreen`). It also pre-resolves the string via `LocalResources` where the `Toast.makeText(Context, @StringRes Int, …)` overload takes the resId directly. The Toast is invisible to Compose test semantics, so this path can't be covered by the existing `SettingsScreenTest` style.
- **`AppLockGateHost` hand-rolls** `DisposableEffect` + `LifecycleEventObserver` where `androidx.lifecycle.compose.LifecycleEventEffect` is already the codebase idiom (`SettingsScreen`, `BatteryOptimizationScreen`); and hand-rolls a `findFragmentActivity()` `ContextWrapper` walk where `androidx.activity.compose.LocalActivity` (activity-compose 1.12.4 is already on the classpath) provides it. The `authenticate` lambda and the activity lookup are also rebuilt on every recomposition (not `remember`ed).
- **Dead API:** `AppLockManager.availability()` and the four-value `AppLockAvailability` enum have no production caller (only the interface, its impl, and a test fake) — and the `BIOMETRIC_STRONG or DEVICE_CREDENTIAL` `canAuthenticate` combination they wrap is unsupported on API 29 (returns `BIOMETRIC_ERROR_UNSUPPORTED`), so the mapping is wrong for the minSdk if it were ever used.
- **`_uiState` is hand-mirrored** from `gate.state` at every mutation site (they can silently disagree on PROCEED/DISABLE paths); it is derivable as `f(gate.state, error)`.
## Suggested fix
Switch the message to the snackbar pattern (add a `snackbarHost` to Settings' existing `Scaffold`); replace the hand-rolled lifecycle observer with `LifecycleEventEffect` and the context walk with `LocalActivity`, `remember`ing the derived activity/lambda; delete `availability()`/`AppLockAvailability` (keep `isDeviceSecure()` and `AUTHENTICATORS`); derive `AppLockUiState` from the gate via a single publish helper.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Origin: code review of PR #45 (screen-lock app gate). Simplification/reuse cleanup in the app-lock UI plumbing.
Problem
Several small divergences from established codebase patterns, bundled:
SettingsScreenusesToast(the app's onlyToast) for the app-lock rejection message, instead of theSnackbarHostState+Scaffold(snackbarHost=…)+ consume pattern every other screen uses (canonical:MailboxScreen). It also pre-resolves the string viaLocalResourceswhere theToast.makeText(Context, @StringRes Int, …)overload takes the resId directly. The Toast is invisible to Compose test semantics, so this path can't be covered by the existingSettingsScreenTeststyle.AppLockGateHosthand-rollsDisposableEffect+LifecycleEventObserverwhereandroidx.lifecycle.compose.LifecycleEventEffectis already the codebase idiom (SettingsScreen,BatteryOptimizationScreen); and hand-rolls afindFragmentActivity()ContextWrapperwalk whereandroidx.activity.compose.LocalActivity(activity-compose 1.12.4 is already on the classpath) provides it. Theauthenticatelambda and the activity lookup are also rebuilt on every recomposition (notremembered).AppLockManager.availability()and the four-valueAppLockAvailabilityenum have no production caller (only the interface, its impl, and a test fake) — and theBIOMETRIC_STRONG or DEVICE_CREDENTIALcanAuthenticatecombination they wrap is unsupported on API 29 (returnsBIOMETRIC_ERROR_UNSUPPORTED), so the mapping is wrong for the minSdk if it were ever used._uiStateis hand-mirrored fromgate.stateat every mutation site (they can silently disagree on PROCEED/DISABLE paths); it is derivable asf(gate.state, error).Suggested fix
Switch the message to the snackbar pattern (add a
snackbarHostto Settings' existingScaffold); replace the hand-rolled lifecycle observer withLifecycleEventEffectand the context walk withLocalActivity,remembering the derived activity/lambda; deleteavailability()/AppLockAvailability(keepisDeviceSecure()andAUTHENTICATORS); deriveAppLockUiStatefrom the gate via a single publish helper.