commit 918b05e820c94cfa2db1ec6f19a50e50b4f5a5dc
parent bb89662a54c8d4d020ba99ce6404c4c52748090a
Author: Iván Ávalos <avalos@disroot.org>
Date: Mon, 26 Oct 2026 15:25:21 +0100
[wallet] disable wallet lock when device authentication is unavailable
Diffstat:
1 file changed, 33 insertions(+), 2 deletions(-)
diff --git a/wallet/src/main/java/net/taler/wallet/main/MainActivity.kt b/wallet/src/main/java/net/taler/wallet/main/MainActivity.kt
@@ -16,6 +16,8 @@
package net.taler.wallet.main
+import android.app.KeyguardManager
+import android.content.Context
import android.content.Intent
import android.content.Intent.ACTION_SEND
import android.content.Intent.EXTRA_TEXT
@@ -32,6 +34,7 @@ import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.activity.result.contract.ActivityResultContracts.CreateDocument
import androidx.activity.viewModels
+import androidx.biometric.BiometricManager
import androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_STRONG
import androidx.biometric.BiometricManager.Authenticators.DEVICE_CREDENTIAL
import androidx.biometric.BiometricPrompt
@@ -75,6 +78,7 @@ import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.NavController
import androidx.navigation.compose.rememberNavController
+import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import net.taler.lib.android.TalerNfcService
import net.taler.wallet.R
@@ -124,8 +128,15 @@ class MainActivity : FragmentActivity() {
val errorSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = !devMode)
val authenticated by model.authenticated.collectAsState()
val biometricEnabled by model.settingsManager.getBiometricLockEnabled(this).collectAsState(false)
+ val biometricAvailable = remember { isDeviceAuthAvailable() }
+ LaunchedEffect(biometricEnabled, biometricAvailable) {
+ if (biometricEnabled && !biometricAvailable) {
+ model.settingsManager.setBiometricLockEnabled(this@MainActivity, false)
+ model.unlockWallet()
+ }
+ }
val databaseMigrationState by model.databaseMigrationState.collectAsState()
- val walletUnlocked = !biometricEnabled || authenticated
+ val walletUnlocked = !biometricEnabled || authenticated || !biometricAvailable
val logExportLauncher = rememberLauncherForActivityResult(CreateDocument("text/plain")) { uri ->
uri?.let { model.settingsManager.exportLogcat(it) }
@@ -169,7 +180,7 @@ class MainActivity : FragmentActivity() {
)
}
- if (!authenticated && biometricEnabled) {
+ if (!walletUnlocked) {
BiometricOverlay(
onUnlock = { biometricPrompt.authenticate(promptInfo) }
)
@@ -273,6 +284,16 @@ class MainActivity : FragmentActivity() {
}
}
+ private fun isDeviceAuthAvailable(): Boolean {
+ return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ BiometricManager.from(this).canAuthenticate(
+ BIOMETRIC_STRONG or DEVICE_CREDENTIAL,
+ ) == BiometricManager.BIOMETRIC_SUCCESS
+ } else {
+ (getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager).isDeviceSecure
+ }
+ }
+
private fun setupBiometrics() {
biometricPrompt = BiometricPrompt(
this,
@@ -281,7 +302,9 @@ class MainActivity : FragmentActivity() {
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
super.onAuthenticationError(errorCode, errString)
if (errorCode == ERROR_NO_BIOMETRICS || errorCode == ERROR_NO_DEVICE_CREDENTIAL) {
+ model.settingsManager.setBiometricLockEnabled(this@MainActivity, false)
model.unlockWallet()
+ return
}
Toast.makeText(this@MainActivity, getString(R.string.biometric_auth_error, errString), LENGTH_SHORT).show()
}
@@ -394,6 +417,14 @@ class MainActivity : FragmentActivity() {
override fun onResume() {
super.onResume()
TalerNfcService.setDefaultHandler(this)
+ if (!isDeviceAuthAvailable()) {
+ lifecycleScope.launch {
+ if (model.settingsManager.getBiometricLockEnabled(this@MainActivity).first()) {
+ model.settingsManager.setBiometricLockEnabled(this@MainActivity, false)
+ }
+ model.unlockWallet()
+ }
+ }
}
override fun onPause() {