mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
Better biometric view message implementation
This commit is contained in:
@@ -29,7 +29,6 @@ import androidx.appcompat.app.AlertDialog
|
||||
import android.view.View
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.biometric.FingerPrintAnimatedVector
|
||||
import com.kunzisoft.keepass.settings.SettingsActivity
|
||||
import com.kunzisoft.keepass.settings.SettingsAdvancedUnlockActivity
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
@@ -53,7 +52,7 @@ class FingerPrintExplanationDialog : DialogFragment() {
|
||||
}
|
||||
|
||||
fingerPrintAnimatedVector = FingerPrintAnimatedVector(activity,
|
||||
rootView.findViewById(R.id.fingerprint_image))
|
||||
rootView.findViewById(R.id.biometric_image))
|
||||
|
||||
builder.setView(rootView)
|
||||
.setPositiveButton(android.R.string.ok) { _, _ -> }
|
||||
|
||||
@@ -114,13 +114,13 @@ class AdvancedUnlockedViewManager(var context: FragmentActivity,
|
||||
override fun onAuthenticationError(
|
||||
errorCode: Int,
|
||||
errString: CharSequence) {
|
||||
Log.e(TAG, "Fingerprint authentication error. Code : $errorCode Error : $errString")
|
||||
setAdvancedUnlockedView(errString.toString())
|
||||
Log.e(TAG, "Biometric authentication error. Code : $errorCode Error : $errString")
|
||||
setAdvancedUnlockedMessageView(errString.toString())
|
||||
}
|
||||
|
||||
override fun onAuthenticationFailed() {
|
||||
Log.e(TAG, "Fingerprint authentication failed, fingerprint not recognized")
|
||||
setAdvancedUnlockedView(R.string.fingerprint_not_recognized)
|
||||
Log.e(TAG, "Biometric authentication failed, biometric not recognized")
|
||||
setAdvancedUnlockedMessageView(R.string.fingerprint_not_recognized)
|
||||
}
|
||||
|
||||
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
|
||||
@@ -155,21 +155,21 @@ class AdvancedUnlockedViewManager(var context: FragmentActivity,
|
||||
|
||||
private fun initNotConfigured() {
|
||||
showFingerPrintViews(true)
|
||||
setAdvancedUnlockedView(R.string.configure_biometric)
|
||||
setAdvancedUnlockedTitleView(R.string.configure_biometric)
|
||||
|
||||
advancedUnlockInfoView?.setIconViewClickListener(null)
|
||||
}
|
||||
|
||||
private fun initWaitData() {
|
||||
showFingerPrintViews(true)
|
||||
setAdvancedUnlockedView(R.string.no_credentials_stored)
|
||||
setAdvancedUnlockedTitleView(R.string.no_credentials_stored)
|
||||
|
||||
advancedUnlockInfoView?.setIconViewClickListener(null)
|
||||
}
|
||||
|
||||
private fun initEncryptData() {
|
||||
showFingerPrintViews(true)
|
||||
setAdvancedUnlockedView(R.string.open_biometric_prompt_store_credential)
|
||||
setAdvancedUnlockedTitleView(R.string.open_biometric_prompt_store_credential)
|
||||
|
||||
biometricHelper?.initEncryptData { biometricPrompt, cryptoObject, promptInfo ->
|
||||
|
||||
@@ -185,7 +185,7 @@ class AdvancedUnlockedViewManager(var context: FragmentActivity,
|
||||
|
||||
private fun initDecryptData() {
|
||||
showFingerPrintViews(true)
|
||||
setAdvancedUnlockedView(R.string.open_biometric_prompt_unlock_database)
|
||||
setAdvancedUnlockedTitleView(R.string.open_biometric_prompt_unlock_database)
|
||||
|
||||
if (biometricHelper != null) {
|
||||
prefsNoBackup.getString(preferenceKeyIvSpec, null)?.let {
|
||||
@@ -244,22 +244,6 @@ class AdvancedUnlockedViewManager(var context: FragmentActivity,
|
||||
menuInflater.inflate(R.menu.advanced_unlock, menu)
|
||||
}
|
||||
|
||||
private fun showFingerPrintViews(show: Boolean) {
|
||||
context.runOnUiThread { advancedUnlockInfoView?.hide = !show }
|
||||
}
|
||||
|
||||
private fun setAdvancedUnlockedView(textId: Int) {
|
||||
context.runOnUiThread {
|
||||
advancedUnlockInfoView?.setText(textId)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setAdvancedUnlockedView(text: CharSequence) {
|
||||
context.runOnUiThread {
|
||||
advancedUnlockInfoView?.text = text
|
||||
}
|
||||
}
|
||||
|
||||
private fun removePrefsNoBackupKey() {
|
||||
prefsNoBackup.edit()
|
||||
?.remove(preferenceKeyValue)
|
||||
@@ -267,6 +251,13 @@ class AdvancedUnlockedViewManager(var context: FragmentActivity,
|
||||
?.apply()
|
||||
}
|
||||
|
||||
fun deleteEntryKey() {
|
||||
biometricHelper?.deleteEntryKey()
|
||||
removePrefsNoBackupKey()
|
||||
biometricMode = Mode.NOT_CONFIGURED
|
||||
checkBiometricAvailability()
|
||||
}
|
||||
|
||||
override fun handleEncryptedResult(
|
||||
value: String,
|
||||
ivSpec: String) {
|
||||
@@ -275,7 +266,7 @@ class AdvancedUnlockedViewManager(var context: FragmentActivity,
|
||||
?.putString(preferenceKeyIvSpec, ivSpec)
|
||||
?.apply()
|
||||
loadDatabase.invoke(null)
|
||||
setAdvancedUnlockedView(R.string.encrypted_value_stored)
|
||||
setAdvancedUnlockedMessageView(R.string.encrypted_value_stored)
|
||||
}
|
||||
|
||||
override fun handleDecryptedResult(value: String) {
|
||||
@@ -284,22 +275,36 @@ class AdvancedUnlockedViewManager(var context: FragmentActivity,
|
||||
}
|
||||
|
||||
override fun onInvalidKeyException(e: Exception) {
|
||||
setAdvancedUnlockedView(R.string.biometric_invalid_key)
|
||||
deleteEntryKey()
|
||||
setAdvancedUnlockedMessageView(R.string.biometric_invalid_key)
|
||||
}
|
||||
|
||||
override fun onBiometricException(e: Exception) {
|
||||
// Don't show error here;
|
||||
// showError(getString(R.string.fingerprint_error, e.getMessage()));
|
||||
// Can be uninit in Activity and init in fragment
|
||||
setAdvancedUnlockedView(e.localizedMessage)
|
||||
setAdvancedUnlockedMessageView(e.localizedMessage)
|
||||
}
|
||||
|
||||
fun deleteEntryKey() {
|
||||
biometricHelper?.deleteEntryKey()
|
||||
removePrefsNoBackupKey()
|
||||
biometricMode = Mode.NOT_CONFIGURED
|
||||
checkBiometricAvailability()
|
||||
private fun showFingerPrintViews(show: Boolean) {
|
||||
context.runOnUiThread { advancedUnlockInfoView?.hide = !show }
|
||||
}
|
||||
|
||||
private fun setAdvancedUnlockedTitleView(textId: Int) {
|
||||
context.runOnUiThread {
|
||||
advancedUnlockInfoView?.setTitle(textId)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setAdvancedUnlockedMessageView(textId: Int) {
|
||||
context.runOnUiThread {
|
||||
advancedUnlockInfoView?.setMessage(textId)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setAdvancedUnlockedMessageView(text: CharSequence) {
|
||||
context.runOnUiThread {
|
||||
advancedUnlockInfoView?.message = text
|
||||
}
|
||||
}
|
||||
|
||||
enum class Mode {
|
||||
|
||||
@@ -62,12 +62,14 @@ class BiometricHelper(private val context: FragmentActivity, private val biometr
|
||||
|
||||
private val promptInfoStoreCredential = BiometricPrompt.PromptInfo.Builder()
|
||||
.setTitle(context.getString(R.string.biometric_prompt_store_credential_title))
|
||||
//.setDeviceCredentialAllowed(true)
|
||||
.setDescription(context.getString(R.string.biometric_prompt_store_credential_message))
|
||||
//.setDeviceCredentialAllowed(true) TODO device credential
|
||||
.setNegativeButtonText(context.getString(android.R.string.cancel))
|
||||
.build()
|
||||
|
||||
private val promptInfoExtractCredential = BiometricPrompt.PromptInfo.Builder()
|
||||
.setTitle(context.getString(R.string.biometric_prompt_extract_credential_title))
|
||||
.setDescription(context.getString(R.string.biometric_prompt_extract_credential_message))
|
||||
//.setDeviceCredentialAllowed(true)
|
||||
.setNegativeButtonText(context.getString(android.R.string.cancel))
|
||||
.build()
|
||||
@@ -294,7 +296,7 @@ class BiometricHelper(private val context: FragmentActivity, private val biometr
|
||||
/**
|
||||
* Remove entry key in keystore
|
||||
*/
|
||||
fun deleteEntryKeyInKeystoreForFingerprints(context: FragmentActivity,
|
||||
fun deleteEntryKeyInKeystoreForBiometric(context: FragmentActivity,
|
||||
biometricUnlockCallback: BiometricUnlockErrorCallback) {
|
||||
val fingerPrintHelper = BiometricHelper(context, object : BiometricUnlockCallback {
|
||||
|
||||
|
||||
@@ -252,7 +252,7 @@ class NestedSettingsFragment : PreferenceFragmentCompat(), Preference.OnPreferen
|
||||
.setPositiveButton(resources.getString(android.R.string.yes)
|
||||
) { _, _ ->
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
BiometricHelper.deleteEntryKeyInKeystoreForFingerprints(
|
||||
BiometricHelper.deleteEntryKeyInKeystoreForBiometric(
|
||||
activity,
|
||||
object : BiometricHelper.BiometricUnlockErrorCallback {
|
||||
override fun onInvalidKeyException(e: Exception) {}
|
||||
|
||||
@@ -19,7 +19,8 @@ class AdvancedUnlockInfoView @JvmOverloads constructor(context: Context,
|
||||
|
||||
private val unlockContainerView: View
|
||||
private var unlockAnimatedVector: FingerPrintAnimatedVector? = null
|
||||
private var unlockTextView: TextView? = null
|
||||
private var unlockTitleTextView: TextView? = null
|
||||
private var unlockMessageTextView: TextView? = null
|
||||
var unlockIconImageView: ImageView? = null
|
||||
|
||||
init {
|
||||
@@ -30,8 +31,9 @@ class AdvancedUnlockInfoView @JvmOverloads constructor(context: Context,
|
||||
unlockContainerView = findViewById(R.id.fingerprint_container)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
unlockTextView = findViewById(R.id.fingerprint_label)
|
||||
unlockIconImageView = findViewById(R.id.fingerprint_image)
|
||||
unlockTitleTextView = findViewById(R.id.biometric_title)
|
||||
unlockMessageTextView = findViewById(R.id.biometric_message)
|
||||
unlockIconImageView = findViewById(R.id.biometric_image)
|
||||
// Init the fingerprint animation
|
||||
unlockAnimatedVector = FingerPrintAnimatedVector(context, unlockIconImageView!!)
|
||||
}
|
||||
@@ -58,16 +60,28 @@ class AdvancedUnlockInfoView @JvmOverloads constructor(context: Context,
|
||||
unlockIconImageView?.setOnClickListener(listener)
|
||||
}
|
||||
|
||||
var text: CharSequence
|
||||
var title: CharSequence
|
||||
get() {
|
||||
return unlockTextView?.text?.toString() ?: ""
|
||||
return unlockTitleTextView?.text?.toString() ?: ""
|
||||
}
|
||||
set(value) {
|
||||
unlockTextView?.text = value
|
||||
unlockTitleTextView?.text = value
|
||||
}
|
||||
|
||||
fun setText(@StringRes textId: Int) {
|
||||
text = context.getString(textId)
|
||||
fun setTitle(@StringRes textId: Int) {
|
||||
title = context.getString(textId)
|
||||
}
|
||||
|
||||
var message: CharSequence
|
||||
get() {
|
||||
return unlockMessageTextView?.text?.toString() ?: ""
|
||||
}
|
||||
set(value) {
|
||||
unlockMessageTextView?.text = value
|
||||
}
|
||||
|
||||
fun setMessage(@StringRes textId: Int) {
|
||||
message = context.getString(textId)
|
||||
}
|
||||
|
||||
var hide: Boolean
|
||||
|
||||
@@ -98,7 +98,7 @@
|
||||
android:text="@string/fingerprint_open_biometric_prompt"/>
|
||||
</LinearLayout>
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/fingerprint_image"
|
||||
android:id="@+id/biometric_image"
|
||||
android:layout_gravity="center"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
|
||||
@@ -1,37 +1,55 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/fingerprint_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
tools:visibility="gone">
|
||||
android:layout_height="wrap_content">
|
||||
<View
|
||||
android:id="@+id/biometric_delimiter"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="?attr/colorPrimaryDark"/>
|
||||
android:background="?attr/colorPrimaryDark"
|
||||
app:layout_constraintTop_toTopOf="parent"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/fingerprint_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginStart="24dp"
|
||||
android:layout_marginEnd="18dp"
|
||||
android:layout_toStartOf="@+id/fingerprint_image"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center_vertical"
|
||||
app:layout_constraintTop_toTopOf="@+id/biometric_delimiter"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/biometric_image" >
|
||||
<TextView
|
||||
android:id="@+id/biometric_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
tools:text="@string/biometric_prompt_store_credential_title"
|
||||
style="@style/KeepassDXStyle.TextAppearance.Default.TextOnPrimary"
|
||||
android:gravity="center_vertical|start" />
|
||||
android:gravity="center_vertical|end" />
|
||||
<TextView
|
||||
android:id="@+id/biometric_message"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
tools:text="Sample error"
|
||||
style="@style/KeepassDXStyle.TextAppearance.Secondary.TextOnPrimary"
|
||||
android:gravity="center_vertical|end" />
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/fingerprint_image"
|
||||
android:id="@+id/biometric_image"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginTop="@dimen/default_margin"
|
||||
android:layout_marginBottom="@dimen/default_margin"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="@dimen/default_margin"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_margin="@dimen/default_margin"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:elevation="8dp"
|
||||
android:src="@drawable/fingerprint"
|
||||
android:background="@drawable/background_image"
|
||||
android:backgroundTint="?attr/colorAccent" />
|
||||
</RelativeLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
@@ -229,8 +229,10 @@
|
||||
<string name="configure_biometric">Biometric prompt is supported but not set up.</string>
|
||||
<string name="open_biometric_prompt_unlock_database">Open the biometric prompt to unlock the database</string>
|
||||
<string name="open_biometric_prompt_store_credential">Open the biometric prompt to store credentials</string>
|
||||
<string name="biometric_prompt_store_credential_title">Store database credentials with biometric data</string>
|
||||
<string name="biometric_prompt_extract_credential_title">Extract database credential with biometric data</string>
|
||||
<string name="biometric_prompt_store_credential_title">Save biometric recognition</string>
|
||||
<string name="biometric_prompt_store_credential_message">Store database credentials with biometric data</string>
|
||||
<string name="biometric_prompt_extract_credential_title">Open database with biometric recognition</string>
|
||||
<string name="biometric_prompt_extract_credential_message">Extract database credential with biometric data</string>
|
||||
<string name="encrypted_value_stored">Encrypted password stored</string>
|
||||
<string name="biometric_invalid_key">Could not read biometric key. Restore your credential.</string>
|
||||
<string name="fingerprint_not_recognized">Could not recognize fingerprint</string>
|
||||
|
||||
@@ -226,6 +226,7 @@
|
||||
<item name="android:textColor">?attr/textColorInverse</item>
|
||||
</style>
|
||||
<style name="KeepassDXStyle.TextAppearance.Secondary.TextOnPrimary" parent="KeepassDXStyle.TextAppearance.Default.TextOnPrimary">
|
||||
<item name="android:textColor">?android:attr/textColorHintInverse</item>
|
||||
<item name="android:textSize">14sp</item>
|
||||
<item name="android:textStyle">italic</item>
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user