aboutsummaryrefslogtreecommitdiffstats
path: root/app/src/main/java
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-11-03 18:43:34 +0100
committerDanilo M. <danix@danix.xyz>2026-11-03 18:43:34 +0100
commitbf58bcfd32160f55b5fb28cde15a93691e624da5 (patch)
tree4075585c114e1fc3e3bb1008675fa6e9761474f9 /app/src/main/java
parentd6b0c910e065fd51481eea1ab5819d7c8f57438e (diff)
downloadHardenedPass-bf58bcfd32160f55b5fb28cde15a93691e624da5.tar.gz
HardenedPass-bf58bcfd32160f55b5fb28cde15a93691e624da5.zip
fix(app): end the screen lock prompt on sensor errors
BiometricAuthenticator mapped onAuthenticationError(ERROR_UNABLE_TO_PROCESS) to Retry, but an error callback always closes the prompt. The settings screen keeps its one-prompt-at-a-time guard set on Retry, so the gated switches stayed locked until the section was left. The error now maps to Failure; Retry comes only from onAuthenticationFailed, while the prompt is still open. Once the device screen lock is removed, the biometric app lock could not be turned off: the prompt returns HardwareUnavailableOrDisabled and nothing was written. Turning it off without a usable screen lock now writes the value directly, as the passphrase cache row already does. The settings category rows and PreferenceAction rows get Role.Button for accessibility services. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'app/src/main/java')
-rw-r--r--app/src/main/java/app/passwordstore/ui/settings/PreferenceRows.kt3
-rw-r--r--app/src/main/java/app/passwordstore/ui/settings/SettingsDestination.kt22
-rw-r--r--app/src/main/java/app/passwordstore/ui/settings/SettingsScreen.kt3
-rw-r--r--app/src/main/java/app/passwordstore/util/auth/BiometricAuthenticator.kt5
4 files changed, 21 insertions, 12 deletions
diff --git a/app/src/main/java/app/passwordstore/ui/settings/PreferenceRows.kt b/app/src/main/java/app/passwordstore/ui/settings/PreferenceRows.kt
index f40db8b0..cfaa741d 100644
--- a/app/src/main/java/app/passwordstore/ui/settings/PreferenceRows.kt
+++ b/app/src/main/java/app/passwordstore/ui/settings/PreferenceRows.kt
@@ -60,7 +60,8 @@ fun PreferenceAction(
) {
ListItem(
headlineContent = { Text(title) },
- modifier = modifier.clickable(enabled = enabled, onClick = onClick).dimmed(enabled),
+ modifier =
+ modifier.clickable(enabled = enabled, role = Role.Button, onClick = onClick).dimmed(enabled),
supportingContent =
if (summary != null) {
{ Text(summary) }
diff --git a/app/src/main/java/app/passwordstore/ui/settings/SettingsDestination.kt b/app/src/main/java/app/passwordstore/ui/settings/SettingsDestination.kt
index 1885c1ee..9fb25e29 100644
--- a/app/src/main/java/app/passwordstore/ui/settings/SettingsDestination.kt
+++ b/app/src/main/java/app/passwordstore/ui/settings/SettingsDestination.kt
@@ -133,7 +133,8 @@ fun SettingsSectionDestination(
if (prompting) return
prompting = true
BiometricAuthenticator.authenticate(activity, title) { result ->
- // Retry: a finger was not recognized, the prompt stays open.
+ // Retry: a finger was not recognized, the prompt stays open. Errors close it and are
+ // terminal (Failure, Canceled*, HardwareUnavailableOrDisabled).
if (result !is Result.Retry) prompting = false
onResult(result)
}
@@ -149,14 +150,19 @@ fun SettingsSectionDestination(
activity.autofillManager?.disableAutofillServices()
refresh()
}
- PreferenceKeys.BIOMETRIC_AUTH_2 ->
- authenticate(R.string.biometric_prompt_title) { result ->
- if (result is Result.Success) {
- viewModel.putBoolean(key, value)
- // Shortcuts were built for the old lock setting, as the old screen knew.
- activity.getSystemService<ShortcutManager>()?.removeAllDynamicShortcuts()
- }
+ PreferenceKeys.BIOMETRIC_AUTH_2 -> {
+ fun write() {
+ viewModel.putBoolean(key, value)
+ // Shortcuts were built for the old lock setting, as the old screen knew.
+ activity.getSystemService<ShortcutManager>()?.removeAllDynamicShortcuts()
}
+ // Without a screen lock no prompt can open: turning the app lock off needs none.
+ if (!value && !BiometricAuthenticator.canAuthenticate(activity)) write()
+ else
+ authenticate(R.string.biometric_prompt_title) { result ->
+ if (result is Result.Success) write()
+ }
+ }
Feature.EnablePGPPassphraseCache.configKey ->
if (!BiometricAuthenticator.canAuthenticate(activity)) viewModel.setPassphraseCache(false)
else
diff --git a/app/src/main/java/app/passwordstore/ui/settings/SettingsScreen.kt b/app/src/main/java/app/passwordstore/ui/settings/SettingsScreen.kt
index 2327551c..7d45ad74 100644
--- a/app/src/main/java/app/passwordstore/ui/settings/SettingsScreen.kt
+++ b/app/src/main/java/app/passwordstore/ui/settings/SettingsScreen.kt
@@ -29,6 +29,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringArrayResource
import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.semantics.Role
import androidx.compose.ui.text.input.KeyboardType
import app.passwordstore.BuildConfig
import app.passwordstore.R
@@ -102,7 +103,7 @@ fun SettingsScreen(
SettingsSection.entries.forEach { section ->
ListItem(
headlineContent = { Text(stringResource(section.title)) },
- modifier = Modifier.clickable { onSection(section) },
+ modifier = Modifier.clickable(role = Role.Button) { onSection(section) },
leadingContent = { Icon(painterResource(section.icon), contentDescription = null) },
colors = rowColors(),
)
diff --git a/app/src/main/java/app/passwordstore/util/auth/BiometricAuthenticator.kt b/app/src/main/java/app/passwordstore/util/auth/BiometricAuthenticator.kt
index 4aad4588..98f2229a 100644
--- a/app/src/main/java/app/passwordstore/util/auth/BiometricAuthenticator.kt
+++ b/app/src/main/java/app/passwordstore/util/auth/BiometricAuthenticator.kt
@@ -46,7 +46,7 @@ object BiometricAuthenticator {
/**
* An incorrect biometric was entered, but the prompt UI is offering the option to retry the
- * operation.
+ * operation. The prompt is still open; a terminal result follows.
*/
data object Retry : Result()
@@ -100,7 +100,8 @@ object BiometricAuthenticator {
when (errorCode) {
/** Keep in sync with [androidx.biometric.BiometricPrompt.AuthenticationError] */
BiometricPrompt.ERROR_HW_UNAVAILABLE -> callback(Result.HardwareUnavailableOrDisabled)
- BiometricPrompt.ERROR_UNABLE_TO_PROCESS -> callback(Result.Retry)
+ // An error always closes the prompt, so none of them is a Retry.
+ BiometricPrompt.ERROR_UNABLE_TO_PROCESS,
BiometricPrompt.ERROR_TIMEOUT ->
callback(
Result.Failure(