Merge branch 'develop' into feature/Autofill_Save

This commit is contained in:
J-Jamet
2020-10-07 13:39:36 +02:00
6 changed files with 117 additions and 61 deletions

View File

@@ -101,7 +101,7 @@ dependencies {
// WARNING: Bug with extra field // WARNING: Bug with extra field
implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.documentfile:documentfile:1.0.1' implementation 'androidx.documentfile:documentfile:1.0.1'
implementation 'androidx.biometric:biometric:1.0.1' implementation 'androidx.biometric:biometric:1.1.0-alpha02'
// Lifecycle - LiveData - ViewModel - Coroutines // Lifecycle - LiveData - ViewModel - Coroutines
implementation "androidx.core:core-ktx:1.3.1" implementation "androidx.core:core-ktx:1.3.1"
implementation 'androidx.fragment:fragment-ktx:1.2.5' implementation 'androidx.fragment:fragment-ktx:1.2.5'

View File

@@ -52,6 +52,7 @@ import com.kunzisoft.keepass.activities.selection.SpecialModeActivity
import com.kunzisoft.keepass.app.database.CipherDatabaseEntity import com.kunzisoft.keepass.app.database.CipherDatabaseEntity
import com.kunzisoft.keepass.autofill.AutofillHelper import com.kunzisoft.keepass.autofill.AutofillHelper
import com.kunzisoft.keepass.biometric.AdvancedUnlockedManager import com.kunzisoft.keepass.biometric.AdvancedUnlockedManager
import com.kunzisoft.keepass.biometric.BiometricUnlockDatabaseHelper
import com.kunzisoft.keepass.database.action.ProgressDatabaseTaskProvider import com.kunzisoft.keepass.database.action.ProgressDatabaseTaskProvider
import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.exception.DuplicateUuidDatabaseException import com.kunzisoft.keepass.database.exception.DuplicateUuidDatabaseException
@@ -68,7 +69,6 @@ import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.utils.BACK_PREVIOUS_KEYBOARD_ACTION import com.kunzisoft.keepass.utils.BACK_PREVIOUS_KEYBOARD_ACTION
import com.kunzisoft.keepass.utils.MenuUtil import com.kunzisoft.keepass.utils.MenuUtil
import com.kunzisoft.keepass.utils.UriUtil import com.kunzisoft.keepass.utils.UriUtil
import com.kunzisoft.keepass.utils.closeDatabase
import com.kunzisoft.keepass.view.AdvancedUnlockInfoView import com.kunzisoft.keepass.view.AdvancedUnlockInfoView
import com.kunzisoft.keepass.view.KeyFileSelectionView import com.kunzisoft.keepass.view.KeyFileSelectionView
import com.kunzisoft.keepass.view.asError import com.kunzisoft.keepass.view.asError
@@ -698,7 +698,7 @@ open class PasswordActivity : SpecialModeActivity() {
}) })
if (!readOnlyEducationPerformed) { if (!readOnlyEducationPerformed) {
val biometricCanAuthenticate = BiometricManager.from(this).canAuthenticate() val biometricCanAuthenticate = BiometricUnlockDatabaseHelper.canAuthenticate(this)
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
&& PreferencesUtil.isBiometricUnlockEnable(applicationContext) && PreferencesUtil.isBiometricUnlockEnable(applicationContext)
&& (biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED || biometricCanAuthenticate == BiometricManager.BIOMETRIC_SUCCESS) && (biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED || biometricCanAuthenticate == BiometricManager.BIOMETRIC_SUCCESS)

View File

@@ -53,6 +53,10 @@ class AdvancedUnlockedManager(var context: FragmentActivity,
private var biometricUnlockDatabaseHelper: BiometricUnlockDatabaseHelper? = null private var biometricUnlockDatabaseHelper: BiometricUnlockDatabaseHelper? = null
private var biometricMode: Mode = Mode.UNAVAILABLE private var biometricMode: Mode = Mode.UNAVAILABLE
// Only to fix multiple fingerprint menu #332
private var mAllowAdvancedUnlockMenu = false
private var mAddBiometricMenuInProgress = false
/** /**
* Manage setting to auto open biometric prompt * Manage setting to auto open biometric prompt
*/ */
@@ -84,13 +88,15 @@ class AdvancedUnlockedManager(var context: FragmentActivity,
// biometric not supported (by API level or hardware) so keep option hidden // biometric not supported (by API level or hardware) so keep option hidden
// or manually disable // or manually disable
val biometricCanAuthenticate = BiometricManager.from(context).canAuthenticate() val biometricCanAuthenticate = BiometricUnlockDatabaseHelper.canAuthenticate(context)
allowOpenBiometricPrompt = true allowOpenBiometricPrompt = true
if (!PreferencesUtil.isBiometricUnlockEnable(context) if (!PreferencesUtil.isBiometricUnlockEnable(context)
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE || biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE) { || biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE) {
toggleMode(Mode.UNAVAILABLE) toggleMode(Mode.UNAVAILABLE)
} else if (biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED){
toggleMode(Mode.BIOMETRIC_SECURITY_UPDATE_REQUIRED)
} else { } else {
// biometric is available but not configured, show icon but in disabled state with some information // biometric is available but not configured, show icon but in disabled state with some information
if (biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED) { if (biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED) {
@@ -114,7 +120,7 @@ class AdvancedUnlockedManager(var context: FragmentActivity,
} else { } else {
cipherDatabaseAction.containsCipherDatabase(databaseFileUri) { containsCipher -> cipherDatabaseAction.containsCipherDatabase(databaseFileUri) { containsCipher ->
// biometric available but no stored password found yet for this DB so show info don't listen // biometric available but no stored password found yet for this DB so show info don't listen
toggleMode( if (containsCipher) { toggleMode(if (containsCipher) {
// listen for decryption // listen for decryption
Mode.EXTRACT_CREDENTIAL Mode.EXTRACT_CREDENTIAL
} else { } else {
@@ -156,10 +162,16 @@ class AdvancedUnlockedManager(var context: FragmentActivity,
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) { override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
context.runOnUiThread { context.runOnUiThread {
when (biometricMode) { when (biometricMode) {
Mode.UNAVAILABLE -> {} Mode.UNAVAILABLE -> {
Mode.BIOMETRIC_NOT_CONFIGURED -> {} }
Mode.KEY_MANAGER_UNAVAILABLE -> {} Mode.BIOMETRIC_SECURITY_UPDATE_REQUIRED -> {
Mode.WAIT_CREDENTIAL -> {} }
Mode.BIOMETRIC_NOT_CONFIGURED -> {
}
Mode.KEY_MANAGER_UNAVAILABLE -> {
}
Mode.WAIT_CREDENTIAL -> {
}
Mode.STORE_CREDENTIAL -> { Mode.STORE_CREDENTIAL -> {
// newly store the entered password in encrypted way // newly store the entered password in encrypted way
biometricUnlockDatabaseHelper?.encryptData(passwordView?.text.toString()) biometricUnlockDatabaseHelper?.encryptData(passwordView?.text.toString())
@@ -183,6 +195,15 @@ class AdvancedUnlockedManager(var context: FragmentActivity,
advancedUnlockInfoView?.setIconViewClickListener(false, null) advancedUnlockInfoView?.setIconViewClickListener(false, null)
} }
private fun initSecurityUpdateRequired() {
showFingerPrintViews(true)
setAdvancedUnlockedTitleView(R.string.biometric_security_update_required)
advancedUnlockInfoView?.setIconViewClickListener(false) {
context.startActivity(Intent(Settings.ACTION_SECURITY_SETTINGS))
}
}
private fun initNotConfigured() { private fun initNotConfigured() {
showFingerPrintViews(true) showFingerPrintViews(true)
setAdvancedUnlockedTitleView(R.string.configure_biometric) setAdvancedUnlockedTitleView(R.string.configure_biometric)
@@ -196,7 +217,6 @@ class AdvancedUnlockedManager(var context: FragmentActivity,
private fun initKeyManagerNotAvailable() { private fun initKeyManagerNotAvailable() {
showFingerPrintViews(true) showFingerPrintViews(true)
setAdvancedUnlockedTitleView(R.string.keystore_not_accessible) setAdvancedUnlockedTitleView(R.string.keystore_not_accessible)
setAdvancedUnlockedMessageView("")
advancedUnlockInfoView?.setIconViewClickListener(false) { advancedUnlockInfoView?.setIconViewClickListener(false) {
context.startActivity(Intent(Settings.ACTION_SECURITY_SETTINGS)) context.startActivity(Intent(Settings.ACTION_SECURITY_SETTINGS))
@@ -210,17 +230,25 @@ class AdvancedUnlockedManager(var context: FragmentActivity,
advancedUnlockInfoView?.setIconViewClickListener(false) { advancedUnlockInfoView?.setIconViewClickListener(false) {
biometricAuthenticationCallback.onAuthenticationError( biometricAuthenticationCallback.onAuthenticationError(
BiometricConstants.ERROR_UNABLE_TO_PROCESS BiometricConstants.ERROR_UNABLE_TO_PROCESS, context.getString(R.string.credential_before_click_biometric_button))
, context.getString(R.string.credential_before_click_biometric_button))
} }
} }
private fun openBiometricPrompt(biometricPrompt: BiometricPrompt?, private fun openBiometricPrompt(biometricPrompt: BiometricPrompt?,
cryptoObject: BiometricPrompt.CryptoObject, cryptoObject: BiometricPrompt.CryptoObject?,
promptInfo: BiometricPrompt.PromptInfo) { promptInfo: BiometricPrompt.PromptInfo) {
context.runOnUiThread { context.runOnUiThread {
if (allowOpenBiometricPrompt) if (allowOpenBiometricPrompt) {
biometricPrompt?.authenticate(promptInfo, cryptoObject) if (biometricPrompt != null) {
if (cryptoObject != null) {
biometricPrompt.authenticate(promptInfo, cryptoObject)
} else {
setAdvancedUnlockedTitleView(R.string.crypto_object_not_initialized)
}
} else {
setAdvancedUnlockedTitleView(R.string.biometric_prompt_not_initialized)
}
}
} }
} }
@@ -230,14 +258,10 @@ class AdvancedUnlockedManager(var context: FragmentActivity,
setAdvancedUnlockedMessageView("") setAdvancedUnlockedMessageView("")
biometricUnlockDatabaseHelper?.initEncryptData { biometricPrompt, cryptoObject, promptInfo -> biometricUnlockDatabaseHelper?.initEncryptData { biometricPrompt, cryptoObject, promptInfo ->
// Set listener to open the biometric dialog and save credential
cryptoObject?.let { crypto -> advancedUnlockInfoView?.setIconViewClickListener { _ ->
// Set listener to open the biometric dialog and save credential openBiometricPrompt(biometricPrompt, cryptoObject, promptInfo)
advancedUnlockInfoView?.setIconViewClickListener { _ ->
openBiometricPrompt(biometricPrompt, crypto, promptInfo)
}
} }
} }
} }
@@ -252,19 +276,16 @@ class AdvancedUnlockedManager(var context: FragmentActivity,
it?.specParameters?.let { specs -> it?.specParameters?.let { specs ->
biometricUnlockDatabaseHelper?.initDecryptData(specs) { biometricPrompt, cryptoObject, promptInfo -> biometricUnlockDatabaseHelper?.initDecryptData(specs) { biometricPrompt, cryptoObject, promptInfo ->
cryptoObject?.let { crypto -> // Set listener to open the biometric dialog and check credential
// Set listener to open the biometric dialog and check credential advancedUnlockInfoView?.setIconViewClickListener { _ ->
advancedUnlockInfoView?.setIconViewClickListener { _ -> openBiometricPrompt(biometricPrompt, cryptoObject, promptInfo)
openBiometricPrompt(biometricPrompt, crypto, promptInfo)
}
// Auto open the biometric prompt
if (isBiometricPromptAutoOpenEnable) {
isBiometricPromptAutoOpenEnable = false
openBiometricPrompt(biometricPrompt, crypto, promptInfo)
}
} }
// Auto open the biometric prompt
if (isBiometricPromptAutoOpenEnable) {
isBiometricPromptAutoOpenEnable = false
openBiometricPrompt(biometricPrompt, cryptoObject, promptInfo)
}
} }
} }
} }
@@ -273,16 +294,32 @@ class AdvancedUnlockedManager(var context: FragmentActivity,
@Synchronized @Synchronized
fun initBiometricMode() { fun initBiometricMode() {
mAllowAdvancedUnlockMenu = false
when (biometricMode) { when (biometricMode) {
Mode.UNAVAILABLE -> initNotAvailable() Mode.UNAVAILABLE -> initNotAvailable()
Mode.BIOMETRIC_SECURITY_UPDATE_REQUIRED -> initSecurityUpdateRequired()
Mode.BIOMETRIC_NOT_CONFIGURED -> initNotConfigured() Mode.BIOMETRIC_NOT_CONFIGURED -> initNotConfigured()
Mode.KEY_MANAGER_UNAVAILABLE -> initKeyManagerNotAvailable() Mode.KEY_MANAGER_UNAVAILABLE -> initKeyManagerNotAvailable()
Mode.WAIT_CREDENTIAL -> initWaitData() Mode.WAIT_CREDENTIAL -> initWaitData()
Mode.STORE_CREDENTIAL -> initEncryptData() Mode.STORE_CREDENTIAL -> initEncryptData()
Mode.EXTRACT_CREDENTIAL -> initDecryptData() Mode.EXTRACT_CREDENTIAL -> initDecryptData()
} }
invalidateBiometricMenu()
}
private fun invalidateBiometricMenu() {
// Show fingerprint key deletion // Show fingerprint key deletion
context.invalidateOptionsMenu() if (!mAddBiometricMenuInProgress) {
mAddBiometricMenuInProgress = true
cipherDatabaseAction.containsCipherDatabase(databaseFileUri) { containsCipher ->
mAllowAdvancedUnlockMenu = containsCipher
&& (biometricMode != Mode.UNAVAILABLE
&& biometricMode != Mode.KEY_MANAGER_UNAVAILABLE)
mAddBiometricMenuInProgress = false
context.invalidateOptionsMenu()
}
}
} }
fun destroy() { fun destroy() {
@@ -293,26 +330,19 @@ class AdvancedUnlockedManager(var context: FragmentActivity,
checkboxPasswordView?.setOnCheckedChangeListener(onCheckedPasswordChangeListener) checkboxPasswordView?.setOnCheckedChangeListener(onCheckedPasswordChangeListener)
} }
// Only to fix multiple fingerprint menu #332
private var addBiometricMenuInProgress = false
fun inflateOptionsMenu(menuInflater: MenuInflater, menu: Menu) { fun inflateOptionsMenu(menuInflater: MenuInflater, menu: Menu) {
if (!addBiometricMenuInProgress) { if (mAllowAdvancedUnlockMenu)
addBiometricMenuInProgress = true menuInflater.inflate(R.menu.advanced_unlock, menu)
cipherDatabaseAction.containsCipherDatabase(databaseFileUri) {
if ((biometricMode != Mode.UNAVAILABLE && biometricMode != Mode.BIOMETRIC_NOT_CONFIGURED)
&& it) {
menuInflater.inflate(R.menu.advanced_unlock, menu)
addBiometricMenuInProgress = false
}
}
}
} }
fun deleteEntryKey() { fun deleteEntryKey() {
allowOpenBiometricPrompt = false
advancedUnlockInfoView?.setIconViewClickListener(false, null)
biometricUnlockDatabaseHelper?.closeBiometricPrompt()
biometricUnlockDatabaseHelper?.deleteEntryKey() biometricUnlockDatabaseHelper?.deleteEntryKey()
cipherDatabaseAction.deleteByDatabaseUri(databaseFileUri) cipherDatabaseAction.deleteByDatabaseUri(databaseFileUri) {
biometricMode = Mode.BIOMETRIC_NOT_CONFIGURED checkBiometricAvailability()
checkBiometricAvailability() }
} }
override fun handleEncryptedResult(encryptedValue: String, ivSpec: String) { override fun handleEncryptedResult(encryptedValue: String, ivSpec: String) {
@@ -359,7 +389,13 @@ class AdvancedUnlockedManager(var context: FragmentActivity,
} }
enum class Mode { enum class Mode {
UNAVAILABLE, BIOMETRIC_NOT_CONFIGURED, KEY_MANAGER_UNAVAILABLE, WAIT_CREDENTIAL, STORE_CREDENTIAL, EXTRACT_CREDENTIAL UNAVAILABLE,
BIOMETRIC_SECURITY_UPDATE_REQUIRED,
BIOMETRIC_NOT_CONFIGURED,
KEY_MANAGER_UNAVAILABLE,
WAIT_CREDENTIAL,
STORE_CREDENTIAL,
EXTRACT_CREDENTIAL
} }
companion object { companion object {

View File

@@ -29,6 +29,9 @@ import android.util.Base64
import android.util.Log import android.util.Log
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.biometric.BiometricManager import androidx.biometric.BiometricManager
import androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_STRONG
import androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_WEAK
import androidx.biometric.BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE
import androidx.biometric.BiometricPrompt import androidx.biometric.BiometricPrompt
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import com.kunzisoft.keepass.R import com.kunzisoft.keepass.R
@@ -66,7 +69,7 @@ class BiometricUnlockDatabaseHelper(private val context: FragmentActivity) {
setDeviceCredentialAllowed(true) setDeviceCredentialAllowed(true)
else else
*/ */
setNegativeButtonText(context.getString(android.R.string.cancel)) setNegativeButtonText(context.getString(android.R.string.cancel))
}.build() }.build()
private val promptInfoExtractCredential = BiometricPrompt.PromptInfo.Builder().apply { private val promptInfoExtractCredential = BiometricPrompt.PromptInfo.Builder().apply {
@@ -78,8 +81,8 @@ class BiometricUnlockDatabaseHelper(private val context: FragmentActivity) {
if (keyguardManager?.isDeviceSecure == true) if (keyguardManager?.isDeviceSecure == true)
setDeviceCredentialAllowed(true) setDeviceCredentialAllowed(true)
else else
*/ */
setNegativeButtonText(context.getString(android.R.string.cancel)) setNegativeButtonText(context.getString(android.R.string.cancel))
}.build() }.build()
val isKeyManagerInitialized: Boolean val isKeyManagerInitialized: Boolean
@@ -91,7 +94,7 @@ class BiometricUnlockDatabaseHelper(private val context: FragmentActivity) {
} }
init { init {
if (BiometricManager.from(context).canAuthenticate() != BiometricManager.BIOMETRIC_SUCCESS) { if (canAuthenticate(context) != BiometricManager.BIOMETRIC_SUCCESS) {
// really not much to do when no fingerprint support found // really not much to do when no fingerprint support found
isKeyManagerInit = false isKeyManagerInit = false
} else { } else {
@@ -158,7 +161,7 @@ class BiometricUnlockDatabaseHelper(private val context: FragmentActivity) {
fun initEncryptData(actionIfCypherInit fun initEncryptData(actionIfCypherInit
: (biometricPrompt: BiometricPrompt?, : (biometricPrompt: BiometricPrompt?,
cryptoObject: BiometricPrompt.CryptoObject?, cryptoObject: BiometricPrompt.CryptoObject?,
promptInfo: BiometricPrompt.PromptInfo)->Unit) { promptInfo: BiometricPrompt.PromptInfo) -> Unit) {
if (!isKeyManagerInitialized) { if (!isKeyManagerInitialized) {
return return
} }
@@ -203,9 +206,9 @@ class BiometricUnlockDatabaseHelper(private val context: FragmentActivity) {
} }
fun initDecryptData(ivSpecValue: String, actionIfCypherInit fun initDecryptData(ivSpecValue: String, actionIfCypherInit
: (biometricPrompt: BiometricPrompt?, : (biometricPrompt: BiometricPrompt?,
cryptoObject: BiometricPrompt.CryptoObject?, cryptoObject: BiometricPrompt.CryptoObject?,
promptInfo: BiometricPrompt.PromptInfo)->Unit) { promptInfo: BiometricPrompt.PromptInfo) -> Unit) {
if (!isKeyManagerInitialized) { if (!isKeyManagerInitialized) {
return return
} }
@@ -295,6 +298,20 @@ class BiometricUnlockDatabaseHelper(private val context: FragmentActivity) {
private const val BIOMETRIC_BLOCKS_MODES = KeyProperties.BLOCK_MODE_CBC private const val BIOMETRIC_BLOCKS_MODES = KeyProperties.BLOCK_MODE_CBC
private const val BIOMETRIC_ENCRYPTION_PADDING = KeyProperties.ENCRYPTION_PADDING_PKCS7 private const val BIOMETRIC_ENCRYPTION_PADDING = KeyProperties.ENCRYPTION_PADDING_PKCS7
fun canAuthenticate(context: Context): Int {
return 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)
BIOMETRIC_ERROR_HW_UNAVAILABLE
}
}
}
/** /**
* Remove entry key in keystore * Remove entry key in keystore
*/ */

View File

@@ -47,6 +47,7 @@ import com.kunzisoft.keepass.icons.IconPackChooser
import com.kunzisoft.keepass.settings.preference.IconPackListPreference import com.kunzisoft.keepass.settings.preference.IconPackListPreference
import com.kunzisoft.keepass.utils.UriUtil import com.kunzisoft.keepass.utils.UriUtil
class NestedAppSettingsFragment : NestedSettingsFragment() { class NestedAppSettingsFragment : NestedSettingsFragment() {
override fun onCreateScreenPreference(screen: Screen, savedInstanceState: Bundle?, rootKey: String?) { override fun onCreateScreenPreference(screen: Screen, savedInstanceState: Bundle?, rootKey: String?) {
@@ -211,7 +212,7 @@ class NestedAppSettingsFragment : NestedSettingsFragment() {
// < M solve verifyError exception // < M solve verifyError exception
var biometricUnlockSupported = false var biometricUnlockSupported = false
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val biometricCanAuthenticate = BiometricManager.from(activity).canAuthenticate() val biometricCanAuthenticate = BiometricUnlockDatabaseHelper.canAuthenticate(activity)
biometricUnlockSupported = biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED biometricUnlockSupported = biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_SUCCESS || biometricCanAuthenticate == BiometricManager.BIOMETRIC_SUCCESS
} }
@@ -336,8 +337,7 @@ class NestedAppSettingsFragment : NestedSettingsFragment() {
activity?.let { activity -> activity?.let { activity ->
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
findPreference<SwitchPreference?>(getString(R.string.settings_autofill_enable_key))?.let { findPreference<SwitchPreference?>(getString(R.string.settings_autofill_enable_key))?.let { autoFillEnablePreference ->
autoFillEnablePreference ->
val autofillManager = activity.getSystemService(AutofillManager::class.java) val autofillManager = activity.getSystemService(AutofillManager::class.java)
autoFillEnablePreference.isChecked = autofillManager != null autoFillEnablePreference.isChecked = autofillManager != null
&& autofillManager.hasEnabledAutofillServices() && autofillManager.hasEnabledAutofillServices()

View File

@@ -268,6 +268,7 @@
<string name="version_label">Version %1$s</string> <string name="version_label">Version %1$s</string>
<string name="build_label">Build %1$s</string> <string name="build_label">Build %1$s</string>
<string name="configure_biometric">Biometric prompt is supported, but not set up.</string> <string name="configure_biometric">Biometric prompt is supported, but not set up.</string>
<string name="biometric_security_update_required">Biometric security update required.</string>
<string name="keystore_not_accessible">The keystore is not properly initialized.</string> <string name="keystore_not_accessible">The keystore is not properly initialized.</string>
<string name="open_biometric_prompt_unlock_database">Open the biometric prompt to unlock the database</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="open_biometric_prompt_store_credential">Open the biometric prompt to store credentials</string>
@@ -280,6 +281,8 @@
<string name="biometric_not_recognized">Could not recognize biometric</string> <string name="biometric_not_recognized">Could not recognize biometric</string>
<string name="biometric_scanning_error">Biometric error: %1$s</string> <string name="biometric_scanning_error">Biometric error: %1$s</string>
<string name="no_credentials_stored">This database does not have stored credential yet.</string> <string name="no_credentials_stored">This database does not have stored credential yet.</string>
<string name="biometric_prompt_not_initialized">Unable to initialize biometric prompt.</string>
<string name="crypto_object_not_initialized">Unable to retrieve crypto object.</string>
<string name="credential_before_click_biometric_button">Type in the password, and then click the \"Biometric\" button.</string> <string name="credential_before_click_biometric_button">Type in the password, and then click the \"Biometric\" button.</string>
<string name="database_history">History</string> <string name="database_history">History</string>
<string name="menu_appearance_settings">Appearance</string> <string name="menu_appearance_settings">Appearance</string>