diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/UnavailableFeatureDialogFragment.kt b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/UnavailableFeatureDialogFragment.kt
index b866d72fe..9632021cd 100644
--- a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/UnavailableFeatureDialogFragment.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/UnavailableFeatureDialogFragment.kt
@@ -90,12 +90,12 @@ class UnavailableFeatureDialogFragment : DialogFragment() {
}
}
if (apiName.isEmpty()) {
- val mapper = arrayOf("ANDROID BASE", "ANDROID BASE 1.1", "CUPCAKE", "DONUT", "ECLAIR", "ECLAIR_0_1", "ECLAIR_MR1", "FROYO", "GINGERBREAD", "GINGERBREAD_MR1", "HONEYCOMB", "HONEYCOMB_MR1", "HONEYCOMB_MR2", "ICE_CREAM_SANDWICH", "ICE_CREAM_SANDWICH_MR1", "JELLY_BEAN", "JELLY_BEAN", "JELLY_BEAN", "KITKAT", "KITKAT", "LOLLIPOOP", "LOLLIPOOP_MR1", "MARSHMALLOW", "NOUGAT", "NOUGAT", "OREO", "OREO")
+ val mapper = arrayOf("ANDROID BASE", "ANDROID BASE 1.1", "CUPCAKE", "DONUT", "ECLAIR", "ECLAIR_0_1", "ECLAIR_MR1", "FROYO", "GINGERBREAD", "GINGERBREAD_MR1", "HONEYCOMB", "HONEYCOMB_MR1", "HONEYCOMB_MR2", "ICE_CREAM_SANDWICH", "ICE_CREAM_SANDWICH_MR1", "JELLY_BEAN", "JELLY_BEAN", "JELLY_BEAN", "KITKAT", "KITKAT", "LOLLIPOOP", "LOLLIPOOP_MR1", "MARSHMALLOW", "NOUGAT", "NOUGAT", "OREO", "OREO", "PIE", "", "")
val index = apiNumber - 1
apiName = if (index < mapper.size) mapper[index] else "UNKNOWN_VERSION"
}
if (version.isEmpty()) {
- val versions = arrayOf("1.0", "1.1", "1.5", "1.6", "2.0", "2.0.1", "2.1", "2.2.X", "2.3", "2.3.3", "3.0", "3.1", "3.2.0", "4.0.1", "4.0.3", "4.1.0", "4.2.0", "4.3.0", "4.4", "4.4", "5.0", "5.1", "6.0", "7.0", "7.1", "8.0.0", "8.1.0")
+ val versions = arrayOf("1.0", "1.1", "1.5", "1.6", "2.0", "2.0.1", "2.1", "2.2.X", "2.3", "2.3.3", "3.0", "3.1", "3.2.0", "4.0.1", "4.0.3", "4.1.0", "4.2.0", "4.3.0", "4.4", "4.4", "5.0", "5.1", "6.0", "7.0", "7.1", "8.0.0", "8.1.0", "9", "10", "11")
val index = apiNumber - 1
version = if (index < versions.size) versions[index] else "UNKNOWN_VERSION"
}
diff --git a/app/src/main/java/com/kunzisoft/keepass/biometric/BiometricUnlockDatabaseHelper.kt b/app/src/main/java/com/kunzisoft/keepass/biometric/BiometricUnlockDatabaseHelper.kt
index 7752ff822..bde6cf062 100644
--- a/app/src/main/java/com/kunzisoft/keepass/biometric/BiometricUnlockDatabaseHelper.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/biometric/BiometricUnlockDatabaseHelper.kt
@@ -307,21 +307,26 @@ class BiometricUnlockDatabaseHelper(private val context: FragmentActivity) {
private const val BIOMETRIC_BLOCKS_MODES = KeyProperties.BLOCK_MODE_CBC
private const val BIOMETRIC_ENCRYPTION_PADDING = KeyProperties.ENCRYPTION_PADDING_PKCS7
+ @RequiresApi(api = Build.VERSION_CODES.M)
fun canAuthenticate(context: Context): Int {
return try {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
- BiometricManager.from(context).canAuthenticate(BIOMETRIC_STRONG or DEVICE_CREDENTIAL)
- } else {
- BiometricManager.from(context).canAuthenticate(BIOMETRIC_STRONG)
- }
+ BiometricManager.from(context).canAuthenticate(
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ BIOMETRIC_STRONG or DEVICE_CREDENTIAL
+ } else {
+ BIOMETRIC_STRONG
+ }
+ )
} catch (e: Exception) {
Log.e(TAG, "Unable to authenticate with strong biometric.", e)
try {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
- BiometricManager.from(context).canAuthenticate(BIOMETRIC_WEAK or DEVICE_CREDENTIAL)
- } else {
- BiometricManager.from(context).canAuthenticate(BIOMETRIC_WEAK)
- }
+ BiometricManager.from(context).canAuthenticate(
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ BIOMETRIC_WEAK or DEVICE_CREDENTIAL
+ } else {
+ BIOMETRIC_WEAK
+ }
+ )
} catch (e: Exception) {
Log.e(TAG, "Unable to authenticate with weak biometric.", e)
BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE
@@ -329,6 +334,7 @@ class BiometricUnlockDatabaseHelper(private val context: FragmentActivity) {
}
}
+ @RequiresApi(api = Build.VERSION_CODES.M)
fun allowInitKeyStore(context: Context): Boolean {
val biometricCanAuthenticate = canAuthenticate(context)
return ( biometricCanAuthenticate == BiometricManager.BIOMETRIC_SUCCESS
@@ -336,19 +342,42 @@ class BiometricUnlockDatabaseHelper(private val context: FragmentActivity) {
)
}
- fun unlockSupported(context: Context): Boolean {
- val biometricCanAuthenticate = canAuthenticate(context)
- return ( biometricCanAuthenticate == BiometricManager.BIOMETRIC_SUCCESS
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ fun biometricUnlockSupported(context: Context): Boolean {
+ val biometricCanAuthenticate = try {
+ BiometricManager.from(context).canAuthenticate(BIOMETRIC_STRONG)
+ } catch (e: Exception) {
+ Log.e(TAG, "Unable to authenticate with strong biometric.", e)
+ try {
+ BiometricManager.from(context).canAuthenticate(BIOMETRIC_WEAK)
+ } catch (e: Exception) {
+ Log.e(TAG, "Unable to authenticate with weak biometric.", e)
+ BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE
+ }
+ }
+ return (biometricCanAuthenticate == BiometricManager.BIOMETRIC_SUCCESS
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_STATUS_UNKNOWN
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED
- )
+ )
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.R)
+ fun deviceCredentialUnlockSupported(context: Context): Boolean {
+ val biometricCanAuthenticate = BiometricManager.from(context).canAuthenticate(DEVICE_CREDENTIAL)
+ return (biometricCanAuthenticate == BiometricManager.BIOMETRIC_SUCCESS
+ || biometricCanAuthenticate == BiometricManager.BIOMETRIC_STATUS_UNKNOWN
+ || biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE
+ || biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED
+ || biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED
+ )
}
/**
* Remove entry key in keystore
*/
+ @RequiresApi(api = Build.VERSION_CODES.M)
fun deleteEntryKeyInKeystoreForBiometric(context: FragmentActivity,
biometricCallback: BiometricUnlockErrorCallback) {
BiometricUnlockDatabaseHelper(context).apply {
diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/NestedAppSettingsFragment.kt b/app/src/main/java/com/kunzisoft/keepass/settings/NestedAppSettingsFragment.kt
index d2dc148fb..247e6d779 100644
--- a/app/src/main/java/com/kunzisoft/keepass/settings/NestedAppSettingsFragment.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/settings/NestedAppSettingsFragment.kt
@@ -30,7 +30,6 @@ import android.view.autofill.AutofillManager
import android.widget.Toast
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AlertDialog
-import androidx.biometric.BiometricManager
import androidx.preference.ListPreference
import androidx.preference.Preference
import androidx.preference.SwitchPreference
@@ -209,14 +208,15 @@ class NestedAppSettingsFragment : NestedSettingsFragment() {
activity?.let { activity ->
val biometricUnlockEnablePreference: SwitchPreference? = findPreference(getString(R.string.biometric_unlock_enable_key))
- val deleteKeysFingerprints: Preference? = findPreference(getString(R.string.biometric_delete_all_key_key))
- // < M solve verifyError exception
+ val deviceCredentialUnlockEnablePreference: SwitchPreference? = findPreference(getString(R.string.device_credential_unlock_enable_key))
+ val autoOpenPromptPreference: SwitchPreference? = findPreference(getString(R.string.biometric_auto_open_prompt_key))
+
val biometricUnlockSupported = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- BiometricUnlockDatabaseHelper.unlockSupported(activity)
+ BiometricUnlockDatabaseHelper.biometricUnlockSupported(activity)
} else false
- if (!biometricUnlockSupported) {
+ biometricUnlockEnablePreference?.apply {
// False if under Marshmallow
- biometricUnlockEnablePreference?.apply {
+ if (!biometricUnlockSupported) {
isChecked = false
setOnPreferenceClickListener { preference ->
(preference as SwitchPreference).isChecked = false
@@ -224,9 +224,48 @@ class NestedAppSettingsFragment : NestedSettingsFragment() {
.show(parentFragmentManager, "unavailableFeatureDialog")
false
}
+ } else {
+ setOnPreferenceChangeListener { _, newValue ->
+ val checked = (newValue as Boolean)
+ autoOpenPromptPreference?.isEnabled = checked
+ || deviceCredentialUnlockEnablePreference?.isChecked == true
+ if (checked)
+ deviceCredentialUnlockEnablePreference?.isChecked = false
+ true
+ }
}
- deleteKeysFingerprints?.isEnabled = false
- } else {
+ }
+
+
+ val deviceCredentialUnlockSupported = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ BiometricUnlockDatabaseHelper.deviceCredentialUnlockSupported(activity)
+ } else false
+ deviceCredentialUnlockEnablePreference?.apply {
+ if (!deviceCredentialUnlockSupported) {
+ isChecked = false
+ setOnPreferenceClickListener { preference ->
+ (preference as SwitchPreference).isChecked = false
+ UnavailableFeatureDialogFragment.getInstance(Build.VERSION_CODES.R)
+ .show(parentFragmentManager, "unavailableFeatureDialog")
+ false
+ }
+ } else {
+ setOnPreferenceChangeListener { _, newValue ->
+ val checked = (newValue as Boolean)
+ autoOpenPromptPreference?.isEnabled = checked ||
+ biometricUnlockEnablePreference?.isChecked == true
+ if (checked)
+ biometricUnlockEnablePreference?.isChecked = false
+ true
+ }
+ }
+ }
+
+ autoOpenPromptPreference?.isEnabled = biometricUnlockEnablePreference?.isChecked == true
+ || deviceCredentialUnlockEnablePreference?.isChecked == true
+
+ val deleteKeysFingerprints: Preference? = findPreference(getString(R.string.biometric_delete_all_key_key))
+ if (biometricUnlockSupported || deviceCredentialUnlockSupported) {
deleteKeysFingerprints?.setOnPreferenceClickListener {
context?.let { context ->
AlertDialog.Builder(context)
@@ -260,6 +299,8 @@ class NestedAppSettingsFragment : NestedSettingsFragment() {
}
false
}
+ } else {
+ deleteKeysFingerprints?.isEnabled = false
}
}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 1b34e09f2..34aa42078 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -324,8 +324,8 @@
Lets you scan your biometric to open the database
Device credential unlocking
Lets you use your device credential to open the database
- Auto-open biometric prompt
- Automatically ask for biometric if the database is set up to use it
+ Auto-open prompt
+ Automatically request advanced unlock if the database is set up to use it
Delete encryption keys
Delete all encryption keys related to advanced unlock recognition
Delete all encryption keys related to advanced unlock recognition?
diff --git a/app/src/main/res/xml/preferences_advanced_unlock.xml b/app/src/main/res/xml/preferences_advanced_unlock.xml
index 618ff9b15..9ce2402fd 100644
--- a/app/src/main/res/xml/preferences_advanced_unlock.xml
+++ b/app/src/main/res/xml/preferences_advanced_unlock.xml
@@ -18,34 +18,27 @@
along with KeePassDX. If not, see .
-->
-
+ android:title="@string/general">
+
-
-
-
-
-
+