Fix: Behebt den Absturz beim BiometricPrompt durch Entfernen des negativen Button-Textes.
This commit is contained in:
@@ -134,7 +134,6 @@ class BiometricAuthenticator(private val context: Context) {
|
||||
fun promptBiometricAuth(
|
||||
title: String,
|
||||
subtitle: String,
|
||||
negativeButtonText: String,
|
||||
fragmentActivity: FragmentActivity,
|
||||
onSuccess: (BiometricPrompt.AuthenticationResult) -> Unit,
|
||||
onFailed: () -> Unit,
|
||||
@@ -143,8 +142,7 @@ class BiometricAuthenticator(private val context: Context) {
|
||||
promptInfo = BiometricPrompt.PromptInfo.Builder()
|
||||
.setTitle(title)
|
||||
.setSubtitle(subtitle)
|
||||
.setNegativeButtonText(negativeButtonText)
|
||||
.setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG)
|
||||
.setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG or BiometricManager.Authenticators.DEVICE_CREDENTIAL)
|
||||
.build()
|
||||
|
||||
val executor = ContextCompat.getMainExecutor(context)
|
||||
@@ -171,7 +169,6 @@ class BiometricAuthenticator(private val context: Context) {
|
||||
fun promptBiometricAuth(
|
||||
title: String,
|
||||
subtitle: String,
|
||||
negativeButtonText: String,
|
||||
fragmentActivity: FragmentActivity,
|
||||
crypto: BiometricPrompt.CryptoObject,
|
||||
onSuccess: (BiometricPrompt.AuthenticationResult) -> Unit,
|
||||
@@ -181,8 +178,7 @@ class BiometricAuthenticator(private val context: Context) {
|
||||
promptInfo = BiometricPrompt.PromptInfo.Builder()
|
||||
.setTitle(title)
|
||||
.setSubtitle(subtitle)
|
||||
.setNegativeButtonText(negativeButtonText)
|
||||
.setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG)
|
||||
.setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG or BiometricManager.Authenticators.DEVICE_CREDENTIAL)
|
||||
.build()
|
||||
|
||||
val executor = ContextCompat.getMainExecutor(context)
|
||||
@@ -275,7 +271,6 @@ class MainActivity : FragmentActivity() {
|
||||
biometricAuthenticator.promptBiometricAuth(
|
||||
title = getString(R.string.unlock_app),
|
||||
subtitle = getString(R.string.confirm_to_unlock),
|
||||
negativeButtonText = getString(R.string.cancel),
|
||||
fragmentActivity = this,
|
||||
onSuccess = { _ -> isUnlocked = true },
|
||||
onFailed = {
|
||||
|
||||
@@ -155,7 +155,6 @@ fun AppShell(
|
||||
biometricAuthenticator.promptBiometricAuth(
|
||||
title = context.getString(R.string.remove_encryption),
|
||||
subtitle = context.getString(R.string.confirm_to_disable_encryption),
|
||||
negativeButtonText = context.getString(R.string.cancel),
|
||||
fragmentActivity = activity,
|
||||
crypto = crypto,
|
||||
onSuccess = { result ->
|
||||
@@ -217,7 +216,6 @@ fun AppShell(
|
||||
biometricAuthenticator.promptBiometricAuth(
|
||||
title = context.getString(R.string.remove_encryption),
|
||||
subtitle = context.getString(R.string.confirm_to_remove_encryption),
|
||||
negativeButtonText = context.getString(R.string.cancel),
|
||||
fragmentActivity = activity,
|
||||
crypto = crypto,
|
||||
onSuccess = { result ->
|
||||
@@ -531,7 +529,6 @@ fun AppShell(
|
||||
biometricAuthenticator.promptBiometricAuth(
|
||||
title = context.getString(R.string.unlock_app),
|
||||
subtitle = context.getString(R.string.confirm_to_unlock),
|
||||
negativeButtonText = context.getString(R.string.cancel),
|
||||
fragmentActivity = activity,
|
||||
crypto = crypto,
|
||||
onSuccess = { result ->
|
||||
@@ -642,12 +639,9 @@ fun AppShell(
|
||||
var showSetPasswordDialog by rememberSaveable { mutableStateOf(false) }
|
||||
var showSetRecipePasswordDialog by rememberSaveable { mutableStateOf(false) }
|
||||
var showSetListPasswordDialog by rememberSaveable { mutableStateOf(false) }
|
||||
var showSetPatternDialog by rememberSaveable { mutableStateOf(false) }
|
||||
var showUnlockPatternDialog by rememberSaveable { mutableStateOf(false) }
|
||||
var unlockPatternErrorMessage by remember { mutableStateOf<String?>(null) }
|
||||
|
||||
|
||||
|
||||
var showUnlockPasswordDialog by rememberSaveable { mutableStateOf(false) }
|
||||
var showUnlockPinDialog by rememberSaveable { mutableStateOf(false) }
|
||||
var unlockErrorMessage by rememberSaveable { mutableStateOf<String?>(null) }
|
||||
var itemToUnlockId by rememberSaveable { mutableStateOf<Int?>(null) }
|
||||
var itemToUnlockType by rememberSaveable { mutableStateOf<Screen?>(null) }
|
||||
@@ -658,50 +652,34 @@ fun AppShell(
|
||||
|
||||
|
||||
val onUnlockItem: (Int, Screen, Int) -> Unit = { id, type, protectionType ->
|
||||
if (protectionType == 1) { // Password protected
|
||||
itemToUnlockId = id
|
||||
itemToUnlockType = type
|
||||
showUnlockPasswordDialog = true
|
||||
} else if (protectionType == 2) { // Biometric protected
|
||||
if (canUseBiometrics) {
|
||||
biometricAuthenticator.promptBiometricAuth(
|
||||
title = context.getString(R.string.unlock_item),
|
||||
subtitle = "",
|
||||
negativeButtonText = context.getString(R.string.cancel),
|
||||
fragmentActivity = context.findActivity() as FragmentActivity,
|
||||
onSuccess = {
|
||||
when (type) {
|
||||
is Screen.ShoppingListDetail -> selectedListId = id
|
||||
is Screen.NoteDetail -> selectedNoteId = id
|
||||
is Screen.RecipeDetail -> selectedRecipeId = id
|
||||
else -> {}
|
||||
}
|
||||
itemWasInitiallyLocked = true
|
||||
currentScreen = type
|
||||
},
|
||||
onFailed = {
|
||||
// Biometric failed, offer password as fallback if desired
|
||||
itemToUnlockId = id
|
||||
itemToUnlockType = type
|
||||
showUnlockPasswordDialog = true
|
||||
},
|
||||
onError = { _, _ ->
|
||||
// Biometric error, offer password as fallback if desired
|
||||
itemToUnlockId = id
|
||||
itemToUnlockType = type
|
||||
showUnlockPasswordDialog = true
|
||||
if (protectionType == 1 || protectionType == 2) { // Password or Biometric protected
|
||||
biometricAuthenticator.promptBiometricAuth(
|
||||
title = context.getString(R.string.unlock_item),
|
||||
subtitle = "",
|
||||
fragmentActivity = context.findActivity() as FragmentActivity,
|
||||
onSuccess = {
|
||||
when (type) {
|
||||
is Screen.ShoppingListDetail -> selectedListId = id
|
||||
is Screen.NoteDetail -> selectedNoteId = id
|
||||
is Screen.RecipeDetail -> selectedRecipeId = id
|
||||
else -> {}
|
||||
}
|
||||
)
|
||||
} else {
|
||||
// Biometrics not available, offer password as fallback
|
||||
itemToUnlockId = id
|
||||
itemToUnlockType = type
|
||||
showUnlockPasswordDialog = true
|
||||
}
|
||||
} else if (protectionType == 3) { // Pattern protected
|
||||
itemToUnlockId = id
|
||||
itemToUnlockType = type
|
||||
showUnlockPatternDialog = true
|
||||
itemWasInitiallyLocked = true
|
||||
currentScreen = type
|
||||
},
|
||||
onFailed = {
|
||||
// Biometric failed, offer password as fallback if desired
|
||||
itemToUnlockId = id
|
||||
itemToUnlockType = type
|
||||
showUnlockPasswordDialog = true
|
||||
},
|
||||
onError = { _, _ ->
|
||||
// Biometric error, offer password as fallback if desired
|
||||
itemToUnlockId = id
|
||||
itemToUnlockType = type
|
||||
showUnlockPasswordDialog = true
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -915,7 +893,6 @@ fun AppShell(
|
||||
biometricAuthenticator.promptBiometricAuth(
|
||||
title = context.getString(R.string.authenticate_to_perform_action),
|
||||
subtitle = "",
|
||||
negativeButtonText = context.getString(R.string.cancel),
|
||||
fragmentActivity = activity,
|
||||
onSuccess = { _ ->
|
||||
if (isPassword) {
|
||||
@@ -1185,15 +1162,6 @@ fun AppShell(
|
||||
itemToUnlockType = null
|
||||
}
|
||||
},
|
||||
showUnlockPatternDialog = showUnlockPatternDialog,
|
||||
onShowUnlockPatternDialogChange = {
|
||||
showUnlockPatternDialog = it
|
||||
if (!it) {
|
||||
unlockPatternErrorMessage = null
|
||||
itemToUnlockId = null
|
||||
itemToUnlockType = null
|
||||
}
|
||||
},
|
||||
onUnlock = { password ->
|
||||
val hashedPassword = PasswordHasher.hashPassword(password)
|
||||
var isPasswordCorrect = false
|
||||
@@ -1230,7 +1198,6 @@ fun AppShell(
|
||||
itemToUnlockId = null
|
||||
itemToUnlockType = null
|
||||
showUnlockPasswordDialog = false
|
||||
showUnlockPatternDialog = false
|
||||
} else {
|
||||
unlockErrorMessage = context.getString(R.string.incorrect_password)
|
||||
}
|
||||
@@ -1280,11 +1247,6 @@ fun AppShell(
|
||||
LockMethod.BIOMETRIC -> {
|
||||
shoppingListsViewModel.setProtectionBiometric(id)
|
||||
}
|
||||
LockMethod.PATTERN -> {
|
||||
selectedListId = id
|
||||
showSetPatternDialog = true
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
LockableItemType.NOTE -> {
|
||||
@@ -1296,11 +1258,6 @@ fun AppShell(
|
||||
LockMethod.BIOMETRIC -> {
|
||||
notesViewModel.setProtectionBiometric(id)
|
||||
}
|
||||
LockMethod.PATTERN -> {
|
||||
selectedNoteId = id
|
||||
showSetPatternDialog = true
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
LockableItemType.RECIPE -> {
|
||||
@@ -1312,11 +1269,6 @@ fun AppShell(
|
||||
LockMethod.BIOMETRIC -> {
|
||||
recipesViewModel.setProtectionBiometric(id)
|
||||
}
|
||||
LockMethod.PATTERN -> {
|
||||
selectedRecipeId = id
|
||||
showSetPatternDialog = true
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1328,8 +1280,5 @@ fun AppShell(
|
||||
},
|
||||
canUseBiometrics = canUseBiometrics,
|
||||
itemToLockType = itemToLockType,
|
||||
showSetPatternDialog = showSetPatternDialog,
|
||||
onShowSetPatternDialogChange = { showSetPatternDialog = it },
|
||||
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -22,7 +22,7 @@ import de.lxtools.noteshop.R
|
||||
|
||||
enum class LockMethod {
|
||||
BIOMETRIC,
|
||||
PATTERN,
|
||||
|
||||
PASSWORD
|
||||
}
|
||||
|
||||
@@ -53,19 +53,7 @@ fun ChooseLockMethodDialog(
|
||||
)
|
||||
Text(text = stringResource(R.string.biometric_lock), modifier = Modifier.padding(start = 8.dp))
|
||||
}
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable { selectedMethod = LockMethod.PATTERN }
|
||||
.padding(vertical = 8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
RadioButton(
|
||||
selected = selectedMethod == LockMethod.PATTERN,
|
||||
onClick = { selectedMethod = LockMethod.PATTERN }
|
||||
)
|
||||
Text(text = stringResource(R.string.pattern), modifier = Modifier.padding(start = 8.dp))
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
|
||||
@@ -115,7 +115,6 @@ fun EncryptionPasswordDialog(
|
||||
biometricAuthenticator.promptBiometricAuth(
|
||||
title = context.getString(R.string.set_encryption_password),
|
||||
subtitle = "",
|
||||
negativeButtonText = context.getString(R.string.cancel),
|
||||
fragmentActivity = context as FragmentActivity,
|
||||
crypto = cryptoObject,
|
||||
onSuccess = { result ->
|
||||
|
||||
@@ -61,7 +61,6 @@ fun JsonImportExportDialog(
|
||||
biometricAuthenticator.promptBiometricAuth(
|
||||
title = context.getString(R.string.authenticate_for_import_export),
|
||||
subtitle = "",
|
||||
negativeButtonText = context.getString(R.string.cancel),
|
||||
fragmentActivity = activity,
|
||||
crypto = crypto,
|
||||
onSuccess = { result ->
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
package de.lxtools.noteshop.ui
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import de.lxtools.noteshop.R
|
||||
|
||||
@Composable
|
||||
fun SetPatternDialog(onDismiss: () -> Unit, onSetPattern: (String) -> Unit) {
|
||||
var pattern by remember { mutableStateOf(emptyList<Int>()) }
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismiss,
|
||||
title = { Text(text = "Set Pattern") },
|
||||
text = {
|
||||
PatternInput(onPatternComplete = {
|
||||
pattern = it
|
||||
onSetPattern(it.joinToString(""))
|
||||
})
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(onClick = { onSetPattern(pattern.joinToString("")) }) {
|
||||
Text(text = "OK")
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = onDismiss) {
|
||||
Text(text = "Cancel")
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun UnlockPatternDialog(onDismiss: () -> Unit, onUnlock: (String) -> Unit, errorMessage: String?) {
|
||||
var pattern by remember { mutableStateOf(emptyList<Int>()) }
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismiss,
|
||||
title = { Text(text = stringResource(R.string.unlock_item)) },
|
||||
text = {
|
||||
Column {
|
||||
PatternInput(onPatternComplete = {
|
||||
pattern = it
|
||||
onUnlock(it.joinToString(""))
|
||||
})
|
||||
if (errorMessage != null) {
|
||||
Text(text = errorMessage, color = MaterialTheme.colorScheme.error)
|
||||
}
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(onClick = { onUnlock(pattern.joinToString("")) }) {
|
||||
Text(text = stringResource(R.string.unlock))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = onDismiss) {
|
||||
Text(text = stringResource(R.string.cancel))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -96,7 +96,6 @@ fun SettingsScreen(
|
||||
biometricAuthenticator.promptBiometricAuth(
|
||||
title = context.getString(R.string.confirm_to_proceed),
|
||||
subtitle = context.getString(R.string.authenticate_to_perform_action),
|
||||
negativeButtonText = context.getString(R.string.cancel),
|
||||
fragmentActivity = context as FragmentActivity,
|
||||
onSuccess = { successAction() },
|
||||
onFailed = {
|
||||
|
||||
@@ -79,12 +79,8 @@ fun AppDialogs(
|
||||
onShowSetListPasswordDialogChange: (Boolean) -> Unit,
|
||||
showUnlockPasswordDialog: Boolean,
|
||||
onShowUnlockPasswordDialogChange: (Boolean) -> Unit,
|
||||
showUnlockPatternDialog: Boolean,
|
||||
onShowUnlockPatternDialogChange: (Boolean) -> Unit,
|
||||
onUnlock: (String) -> Unit,
|
||||
unlockErrorMessage: String?,
|
||||
|
||||
|
||||
showPasswordDialog: Boolean,
|
||||
onShowPasswordDialogChange: (Boolean) -> Unit,
|
||||
onHasEncryptionPasswordChange: (Boolean) -> Unit,
|
||||
@@ -103,9 +99,6 @@ fun AppDialogs(
|
||||
onConfirmLockMethod: (LockMethod) -> Unit,
|
||||
canUseBiometrics: Boolean,
|
||||
itemToLockType: de.lxtools.noteshop.ui.LockableItemType?,
|
||||
showSetPatternDialog: Boolean,
|
||||
onShowSetPatternDialogChange: (Boolean) -> Unit,
|
||||
|
||||
) {
|
||||
val scope = rememberCoroutineScope()
|
||||
val dynamicRecipeStrings = getDynamicRecipeStrings(recipesTitle)
|
||||
@@ -212,27 +205,6 @@ fun AppDialogs(
|
||||
)
|
||||
}
|
||||
|
||||
if (showSetPatternDialog) {
|
||||
de.lxtools.noteshop.ui.SetPatternDialog(
|
||||
onDismiss = { onShowSetPatternDialogChange(false) },
|
||||
onSetPattern = { pattern ->
|
||||
when (itemToLockType) {
|
||||
LockableItemType.NOTE -> notesViewModel.setProtectionPattern(pattern)
|
||||
LockableItemType.RECIPE -> recipesViewModel.setProtectionPattern(pattern)
|
||||
LockableItemType.SHOPPING_LIST -> shoppingListsViewModel.setProtectionPattern(pattern)
|
||||
else -> {}
|
||||
}
|
||||
onShowSetPatternDialogChange(false)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if (showPasswordDialog) {
|
||||
EncryptionPasswordDialog(
|
||||
onDismiss = { onShowPasswordDialogChange(false) },
|
||||
@@ -299,16 +271,6 @@ fun AppDialogs(
|
||||
)
|
||||
}
|
||||
|
||||
if (showUnlockPatternDialog) {
|
||||
de.lxtools.noteshop.ui.UnlockPatternDialog(
|
||||
onDismiss = {
|
||||
onShowUnlockPatternDialogChange(false)
|
||||
},
|
||||
onUnlock = onUnlock,
|
||||
errorMessage = unlockErrorMessage
|
||||
)
|
||||
}
|
||||
|
||||
if (showChooseLockMethodDialog) {
|
||||
ChooseLockMethodDialog(
|
||||
onDismiss = { onShowChooseLockMethodDialogChange(false) },
|
||||
@@ -318,4 +280,4 @@ fun AppDialogs(
|
||||
canUseBiometrics = canUseBiometrics
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -238,8 +238,7 @@
|
||||
<string name="unlock_item">Unlock Item</string>
|
||||
<string name="enter_password_to_unlock">Enter password to unlock this item.</string>
|
||||
<string name="incorrect_password">Incorrect password.</string>
|
||||
<string name="incorrect_pattern">Incorrect pattern.</string>
|
||||
|
||||
|
||||
<string name="data_encryption">Data Encryption</string>
|
||||
<string name="set_encryption_password">Set Encryption Password</string>
|
||||
<string name="change_encryption_password">Change Encryption Password</string>
|
||||
|
||||
Reference in New Issue
Block a user