Behavior-preserving cleanup of the app-lock UI plumbing:
- SettingsScreen: replace the app's only Toast with the canonical
SnackbarHostState + Scaffold(snackbarHost) + consume pattern for the
app-lock rejection message (matches MailboxScreen); the ViewModel keeps
the @StringRes id, resolved via LocalResources at the display boundary.
- AppLockGateHost: replace the hand-rolled DisposableEffect +
LifecycleEventObserver with LifecycleEventEffect, and the ContextWrapper
findFragmentActivity() walk with LocalActivity; remember the derived
activity and the authenticate lambda.
- AppLockManager: delete the dead availability() API and the four-value
AppLockAvailability enum (no production caller; the
BIOMETRIC_STRONG or DEVICE_CREDENTIAL canAuthenticate combo is
unsupported on minSdk 29). Keep isDeviceSecure() and AUTHENTICATORS.
- AppLockViewModel: derive the gated uiState from the injected gate via a
single publish() helper instead of hand-mirroring gate.state at each
auth site; the transient Checking cover and app-lock-off unlocked states
stay explicit (settings/lifecycle-driven, not session-gate-driven).
Extend SettingsScreenTest with a Compose test for the rejection snackbar
(now visible to Compose semantics) and drop availability() from its fake.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>