diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt
index 6a55ac16f..17bcea82d 100644
--- a/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt
@@ -434,8 +434,8 @@ class FileDatabaseSelectActivity : SpecialModeActivity(),
when (item.itemId) {
android.R.id.home -> UriUtil.gotoUrl(this, R.string.file_manager_explanation_url)
}
-
- return MenuUtil.onDefaultMenuOptionsItemSelected(this, item) && super.onOptionsItemSelected(item)
+ MenuUtil.onDefaultMenuOptionsItemSelected(this, item)
+ return super.onOptionsItemSelected(item)
}
companion object {
diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/PasswordActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/PasswordActivity.kt
index 6f1db15b3..15753f38a 100644
--- a/app/src/main/java/com/kunzisoft/keepass/activities/PasswordActivity.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/activities/PasswordActivity.kt
@@ -37,8 +37,8 @@ import android.widget.*
import androidx.activity.viewModels
import androidx.annotation.RequiresApi
import androidx.appcompat.widget.Toolbar
-import androidx.biometric.BiometricManager
import androidx.core.app.ActivityCompat
+import androidx.fragment.app.commit
import com.google.android.material.snackbar.Snackbar
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.dialogs.DuplicateUuidDialog
@@ -50,8 +50,8 @@ import com.kunzisoft.keepass.activities.lock.LockingActivity
import com.kunzisoft.keepass.activities.selection.SpecialModeActivity
import com.kunzisoft.keepass.app.database.CipherDatabaseEntity
import com.kunzisoft.keepass.autofill.AutofillHelper
+import com.kunzisoft.keepass.biometric.AdvancedUnlockFragment
import com.kunzisoft.keepass.biometric.AdvancedUnlockManager
-import com.kunzisoft.keepass.biometric.AdvancedUnlockHelper
import com.kunzisoft.keepass.database.action.ProgressDatabaseTaskProvider
import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.exception.DuplicateUuidDatabaseException
@@ -69,14 +69,13 @@ import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.utils.BACK_PREVIOUS_KEYBOARD_ACTION
import com.kunzisoft.keepass.utils.MenuUtil
import com.kunzisoft.keepass.utils.UriUtil
-import com.kunzisoft.keepass.view.AdvancedUnlockInfoView
import com.kunzisoft.keepass.view.KeyFileSelectionView
import com.kunzisoft.keepass.view.asError
import com.kunzisoft.keepass.viewmodels.DatabaseFileViewModel
import kotlinx.android.synthetic.main.activity_password.*
import java.io.FileNotFoundException
-open class PasswordActivity : SpecialModeActivity() {
+open class PasswordActivity : SpecialModeActivity(), AdvancedUnlockManager.BuilderListener {
// Views
private var toolbar: Toolbar? = null
@@ -86,7 +85,7 @@ open class PasswordActivity : SpecialModeActivity() {
private var confirmButtonView: Button? = null
private var checkboxPasswordView: CompoundButton? = null
private var checkboxKeyFileView: CompoundButton? = null
- private var advancedUnlockInfoView: AdvancedUnlockInfoView? = null
+ private var advancedUnlockFragment: AdvancedUnlockFragment? = null
private var infoContainerView: ViewGroup? = null
private val databaseFileViewModel: DatabaseFileViewModel by viewModels()
@@ -113,7 +112,6 @@ open class PasswordActivity : SpecialModeActivity() {
private var mProgressDatabaseTaskProvider: ProgressDatabaseTaskProvider? = null
- private var advancedUnlockManager: AdvancedUnlockManager? = null
private var mAllowAutoOpenBiometricPrompt: Boolean = true
override fun onCreate(savedInstanceState: Bundle?) {
@@ -133,7 +131,6 @@ open class PasswordActivity : SpecialModeActivity() {
keyFileSelectionView = findViewById(R.id.keyfile_selection)
checkboxPasswordView = findViewById(R.id.password_checkbox)
checkboxKeyFileView = findViewById(R.id.keyfile_checkox)
- advancedUnlockInfoView = findViewById(R.id.biometric_info)
infoContainerView = findViewById(R.id.activity_password_info_container)
mPermissionAsked = savedInstanceState?.getBoolean(KEY_PERMISSION_ASKED) ?: mPermissionAsked
@@ -170,47 +167,20 @@ open class PasswordActivity : SpecialModeActivity() {
}
// Init Biometric elements
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- if (PreferencesUtil.isAdvancedUnlockEnable(this)) {
- advancedUnlockManager = AdvancedUnlockManager(this, advancedUnlockInfoView!!,
- object: AdvancedUnlockManager.BuilderListener {
-
- override fun retrieveCredentialForEncryption(): String {
- return passwordView?.text?.toString() ?: ""
- }
-
- override fun conditionToStoreCredential(): Boolean {
- return checkboxPasswordView?.isChecked == true
- }
-
- override fun onCredentialEncrypted(databaseUri: Uri,
- encryptedCredential: String,
- ivSpec: String) {
- // Load the database if password is registered with biometric
- verifyCheckboxesAndLoadDatabase(
- CipherDatabaseEntity(
- databaseUri.toString(),
- encryptedCredential,
- ivSpec)
- )
- }
-
- override fun onCredentialDecrypted(databaseUri: Uri,
- decryptedCredential: String) {
- // Load the database if password is retrieve from biometric
- // Retrieve from biometric
- verifyKeyFileCheckboxAndLoadDatabase(decryptedCredential)
- }
- }
- )
+ advancedUnlockFragment = supportFragmentManager
+ .findFragmentByTag(UNLOCK_FRAGMENT_TAG) as? AdvancedUnlockFragment?
+ if (advancedUnlockFragment == null) {
+ advancedUnlockFragment = AdvancedUnlockFragment()
+ supportFragmentManager.commit {
+ replace(R.id.fragment_advanced_unlock_container_view,
+ advancedUnlockFragment!!,
+ UNLOCK_FRAGMENT_TAG)
}
}
// Listen password checkbox to init advanced unlock and confirmation button
checkboxPasswordView?.setOnCheckedChangeListener { _, _ ->
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- advancedUnlockManager?.checkUnlockAvailability()
- }
+ advancedUnlockFragment?.checkUnlockAvailability()
enableOrNotTheConfirmationButton()
}
@@ -247,12 +217,7 @@ open class PasswordActivity : SpecialModeActivity() {
when (actionTask) {
ACTION_DATABASE_LOAD_TASK -> {
// Recheck advanced unlock if error
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- if (PreferencesUtil.isAdvancedUnlockEnable(this@PasswordActivity)) {
- // Stay with the same mode and init it
- advancedUnlockManager?.initAdvancedUnlockMode()
- }
- }
+ advancedUnlockFragment?.initAdvancedUnlockMode()
if (result.isSuccess) {
mDatabaseKeyFileUri = null
@@ -360,6 +325,33 @@ open class PasswordActivity : SpecialModeActivity() {
finish()
}
+ override fun retrieveCredentialForEncryption(): String {
+ return passwordView?.text?.toString() ?: ""
+ }
+
+ override fun conditionToStoreCredential(): Boolean {
+ return checkboxPasswordView?.isChecked == true
+ }
+
+ override fun onCredentialEncrypted(databaseUri: Uri,
+ encryptedCredential: String,
+ ivSpec: String) {
+ // Load the database if password is registered with biometric
+ verifyCheckboxesAndLoadDatabase(
+ CipherDatabaseEntity(
+ databaseUri.toString(),
+ encryptedCredential,
+ ivSpec)
+ )
+ }
+
+ override fun onCredentialDecrypted(databaseUri: Uri,
+ decryptedCredential: String) {
+ // Load the database if password is retrieve from biometric
+ // Retrieve from biometric
+ verifyKeyFileCheckboxAndLoadDatabase(decryptedCredential)
+ }
+
private val onEditorActionListener = object : TextView.OnEditorActionListener {
override fun onEditorAction(v: TextView?, actionId: Int, event: KeyEvent?): Boolean {
if (actionId == IME_ACTION_DONE) {
@@ -426,18 +418,9 @@ open class PasswordActivity : SpecialModeActivity() {
verifyCheckboxesAndLoadDatabase(password, keyFileUri)
} else {
// Init Biometric elements
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- databaseFileUri?.let { databaseUri ->
- if (PreferencesUtil.isAdvancedUnlockEnable(this)) {
- advancedUnlockManager?.connect(databaseUri)
- advancedUnlockManager?.isBiometricPromptAutoOpenEnable =
- mAllowAutoOpenBiometricPrompt
- && mProgressDatabaseTaskProvider?.isBinded() != true
- } else {
- advancedUnlockManager?.disconnect()
- }
- }
- }
+ advancedUnlockFragment?.loadDatabase(databaseFileUri,
+ mAllowAutoOpenBiometricPrompt
+ && mProgressDatabaseTaskProvider?.isBinded() != true)
}
enableOrNotTheConfirmationButton()
@@ -489,10 +472,6 @@ open class PasswordActivity : SpecialModeActivity() {
override fun onPause() {
mProgressDatabaseTaskProvider?.unregisterProgressTask()
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- advancedUnlockManager?.disconnect()
- }
-
// Reinit locking activity UI variable
LockingActivity.LOCKING_ACTIVITY_UI_VISIBLE_DURING_LOCK = null
mAllowAutoOpenBiometricPrompt = true
@@ -500,12 +479,6 @@ open class PasswordActivity : SpecialModeActivity() {
super.onPause()
}
- override fun onDestroy() {
- advancedUnlockManager = null
-
- super.onDestroy()
- }
-
override fun onSaveInstanceState(outState: Bundle) {
outState.putBoolean(KEY_PERMISSION_ASKED, mPermissionAsked)
mDatabaseKeyFileUri?.let {
@@ -607,11 +580,6 @@ open class PasswordActivity : SpecialModeActivity() {
MenuUtil.defaultMenuInflater(inflater, menu)
}
- if ( Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- // biometric menu
- advancedUnlockManager?.inflateOptionsMenu(inflater, menu)
- }
-
super.onCreateOptionsMenu(menu)
launchEducation(menu)
@@ -687,14 +655,16 @@ open class PasswordActivity : SpecialModeActivity() {
performedNextEducation(passwordActivityEducation, menu)
})
+ // TODO Education
+ /*
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
&& !readOnlyEducationPerformed) {
val biometricCanAuthenticate = AdvancedUnlockHelper.canAuthenticate(this)
PreferencesUtil.isAdvancedUnlockEnable(applicationContext)
&& (biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED || biometricCanAuthenticate == BiometricManager.BIOMETRIC_SUCCESS)
- && advancedUnlockInfoView != null && advancedUnlockInfoView?.visibility == View.VISIBLE
- && advancedUnlockInfoView?.unlockIconImageView != null
- && passwordActivityEducation.checkAndPerformedBiometricEducation(advancedUnlockInfoView?.unlockIconImageView!!,
+ && advancedUnlockFragment != null && advancedUnlockFragment?.visibility == View.VISIBLE
+ && advancedUnlockFragment?.unlockIconImageView != null
+ && passwordActivityEducation.checkAndPerformedBiometricEducation(advancedUnlockFragment?.unlockIconImageView!!,
{
performedNextEducation(passwordActivityEducation, menu)
},
@@ -702,6 +672,8 @@ open class PasswordActivity : SpecialModeActivity() {
performedNextEducation(passwordActivityEducation, menu)
})
}
+
+ */
}
}
@@ -723,10 +695,7 @@ open class PasswordActivity : SpecialModeActivity() {
readOnly = !readOnly
changeOpenFileReadIcon(item)
}
- R.id.menu_keystore_remove_key -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- advancedUnlockManager?.deleteEncryptedDatabaseKey()
- }
- else -> return MenuUtil.onDefaultMenuOptionsItemSelected(this, item)
+ else -> MenuUtil.onDefaultMenuOptionsItemSelected(this, item)
}
return super.onOptionsItemSelected(item)
@@ -741,9 +710,7 @@ open class PasswordActivity : SpecialModeActivity() {
mAllowAutoOpenBiometricPrompt = false
// To get device credential unlock result
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- advancedUnlockManager?.onActivityResult(requestCode, resultCode, data)
- }
+ advancedUnlockFragment?.onActivityResult(requestCode, resultCode, data)
// To get entry in result
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
@@ -778,6 +745,8 @@ open class PasswordActivity : SpecialModeActivity() {
private val TAG = PasswordActivity::class.java.name
+ private const val UNLOCK_FRAGMENT_TAG = "UNLOCK_FRAGMENT_TAG"
+
private const val KEY_FILENAME = "fileName"
private const val KEY_KEYFILE = "keyFile"
private const val VIEW_INTENT = "android.intent.action.VIEW"
diff --git a/app/src/main/java/com/kunzisoft/keepass/biometric/AdvancedUnlockFragment.kt b/app/src/main/java/com/kunzisoft/keepass/biometric/AdvancedUnlockFragment.kt
new file mode 100644
index 000000000..5f945c7ae
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/biometric/AdvancedUnlockFragment.kt
@@ -0,0 +1,149 @@
+package com.kunzisoft.keepass.biometric
+
+import android.content.Context
+import android.content.Intent
+import android.net.Uri
+import android.os.Build
+import android.os.Bundle
+import android.view.*
+import androidx.fragment.app.FragmentActivity
+import com.kunzisoft.keepass.R
+import com.kunzisoft.keepass.activities.stylish.StylishFragment
+import com.kunzisoft.keepass.settings.PreferencesUtil
+
+class AdvancedUnlockFragment: StylishFragment() {
+
+ private var advancedUnlockManager: AdvancedUnlockManager? = null
+
+ private var advancedUnlockEnabled = false
+
+ override fun onAttach(context: Context) {
+ super.onAttach(context)
+
+ advancedUnlockEnabled = PreferencesUtil.isAdvancedUnlockEnable(context)
+ try {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ if (advancedUnlockEnabled) {
+ advancedUnlockManager?.builderListener = context as AdvancedUnlockManager.BuilderListener
+ }
+ }
+ } catch (e: ClassCastException) {
+ throw ClassCastException(context.toString()
+ + " must implement " + AdvancedUnlockManager.BuilderListener::class.java.name)
+ }
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ if (advancedUnlockEnabled) {
+ if (advancedUnlockManager == null) {
+ advancedUnlockManager = AdvancedUnlockManager { context as FragmentActivity }
+ } else {
+ advancedUnlockManager?.retrieveContext = { context as FragmentActivity }
+ }
+ }
+ }
+
+ retainInstance = true
+ setHasOptionsMenu(true)
+ }
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ super.onCreateView(inflater, container, savedInstanceState)
+
+ val rootView = inflater.cloneInContext(contextThemed)
+ .inflate(R.layout.fragment_advanced_unlock, container, false)
+
+ advancedUnlockManager?.advancedUnlockInfoView = rootView.findViewById(R.id.advanced_unlock_view)
+
+ return rootView
+ }
+
+ private data class ActivityResult(var requestCode: Int, var resultCode: Int, var data: Intent?)
+ private var activityResult: ActivityResult? = null
+
+ override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+ // To wait resume
+ activityResult = ActivityResult(requestCode, resultCode, data)
+
+ super.onActivityResult(requestCode, resultCode, data)
+ }
+
+ override fun onResume() {
+ super.onResume()
+ advancedUnlockEnabled = PreferencesUtil.isAdvancedUnlockEnable(requireContext())
+ }
+
+ override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
+ super.onCreateOptionsMenu(menu, inflater)
+
+ if ( Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ // biometric menu
+ advancedUnlockManager?.inflateOptionsMenu(inflater, menu)
+ }
+ }
+
+ override fun onOptionsItemSelected(item: MenuItem): Boolean {
+ when (item.itemId) {
+ R.id.menu_keystore_remove_key -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ advancedUnlockManager?.deleteEncryptedDatabaseKey()
+ }
+ }
+
+ return super.onOptionsItemSelected(item)
+ }
+
+ fun loadDatabase(databaseUri: Uri?, autoOpenPrompt: Boolean) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ // To get device credential unlock result, only if same database uri
+ activityResult?.let {
+ if (databaseUri != null && advancedUnlockManager?.databaseFileUri == databaseUri) {
+ advancedUnlockManager?.onActivityResult(it.requestCode, it.resultCode, it.data)
+ }
+ } ?: run {
+ if (databaseUri != null && advancedUnlockEnabled) {
+ advancedUnlockManager?.connect(databaseUri)
+ advancedUnlockManager?.autoOpenPrompt = autoOpenPrompt
+ } else {
+ advancedUnlockManager?.disconnect()
+ }
+ }
+ activityResult = null
+ }
+ }
+
+ /**
+ * Check unlock availability and change the current mode depending of device's state
+ */
+ fun checkUnlockAvailability() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ advancedUnlockManager?.checkUnlockAvailability()
+ }
+ }
+
+ fun initAdvancedUnlockMode() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ if (advancedUnlockEnabled) {
+ advancedUnlockManager?.initAdvancedUnlockMode()
+ }
+ }
+ }
+
+ override fun onDestroy() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ advancedUnlockManager?.disconnect()
+ advancedUnlockManager?.builderListener = null
+ advancedUnlockManager = null
+ }
+
+ super.onDestroy()
+ }
+
+ override fun onDetach() {
+ advancedUnlockManager?.builderListener = null
+
+ super.onDetach()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/kunzisoft/keepass/biometric/AdvancedUnlockHelper.kt b/app/src/main/java/com/kunzisoft/keepass/biometric/AdvancedUnlockHelper.kt
index 45165b62c..f8638bfe1 100644
--- a/app/src/main/java/com/kunzisoft/keepass/biometric/AdvancedUnlockHelper.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/biometric/AdvancedUnlockHelper.kt
@@ -47,7 +47,7 @@ import javax.crypto.SecretKey
import javax.crypto.spec.IvParameterSpec
@RequiresApi(api = Build.VERSION_CODES.M)
-class AdvancedUnlockHelper(private val context: FragmentActivity) {
+class AdvancedUnlockHelper(private var retrieveContext: () -> FragmentActivity) {
private var keyStore: KeyStore? = null
private var keyGenerator: KeyGenerator? = null
@@ -72,8 +72,8 @@ class AdvancedUnlockHelper(private val context: FragmentActivity) {
private var isKeyManagerInit = false
- private val deviceCredentialUnlockEnable = PreferencesUtil.isDeviceCredentialUnlockEnable(context)
- private val biometricUnlockEnable = PreferencesUtil.isBiometricUnlockEnable(context)
+ private val deviceCredentialUnlockEnable = PreferencesUtil.isDeviceCredentialUnlockEnable(retrieveContext())
+ private val biometricUnlockEnable = PreferencesUtil.isBiometricUnlockEnable(retrieveContext())
val isKeyManagerInitialized: Boolean
get() {
@@ -99,7 +99,7 @@ class AdvancedUnlockHelper(private val context: FragmentActivity) {
}
init {
- if (isDeviceSecure(context)
+ if (isDeviceSecure(retrieveContext())
&& (deviceCredentialUnlockEnable || biometricUnlockEnable)) {
try {
this.keyStore = KeyStore.getInstance(ADVANCED_UNLOCK_KEYSTORE)
@@ -144,11 +144,6 @@ class AdvancedUnlockHelper(private val context: FragmentActivity) {
// Require the user to authenticate with a fingerprint to authorize every use
// of the key
.setUserAuthenticationRequired(true)
- .apply {
- if (isDeviceCredentialBiometricOperation()) {
- setUserAuthenticationParameters(0, KeyProperties.AUTH_DEVICE_CREDENTIAL)
- }
- }
.build())
keyGenerator?.generateKey()
}
@@ -287,20 +282,20 @@ class AdvancedUnlockHelper(private val context: FragmentActivity) {
fun openAdvancedUnlockPrompt(cryptoPrompt: AdvancedUnlockCryptoPrompt) {
// Init advanced unlock prompt
if (biometricPrompt == null) {
- biometricPrompt = BiometricPrompt(context,
+ biometricPrompt = BiometricPrompt(retrieveContext(),
Executors.newSingleThreadExecutor(),
authenticationCallback)
}
- val promptTitle = context.getString(cryptoPrompt.promptTitleId)
+ val promptTitle = retrieveContext().getString(cryptoPrompt.promptTitleId)
val promptDescription = cryptoPrompt.promptDescriptionId?.let { descriptionId ->
- context.getString(descriptionId)
+ retrieveContext().getString(descriptionId)
} ?: ""
if (cryptoPrompt.isDeviceCredentialOperation) {
// TODO open intent keyguard for response
- val keyGuardManager = ContextCompat.getSystemService(context, KeyguardManager::class.java)
- context.startActivityForResult(
+ val keyGuardManager = ContextCompat.getSystemService(retrieveContext(), KeyguardManager::class.java)
+ retrieveContext().startActivityForResult(
keyGuardManager?.createConfirmDeviceCredentialIntent(promptTitle, promptDescription),
REQUEST_DEVICE_CREDENTIAL)
}
@@ -313,7 +308,7 @@ class AdvancedUnlockHelper(private val context: FragmentActivity) {
if (isDeviceCredentialBiometricOperation()) {
setAllowedAuthenticators(DEVICE_CREDENTIAL)
} else {
- setNegativeButtonText(context.getString(android.R.string.cancel))
+ setNegativeButtonText(retrieveContext().getString(android.R.string.cancel))
}
}.build()
@@ -449,9 +444,9 @@ class AdvancedUnlockHelper(private val context: FragmentActivity) {
* Remove entry key in keystore
*/
@RequiresApi(api = Build.VERSION_CODES.M)
- fun deleteEntryKeyInKeystoreForBiometric(context: FragmentActivity,
+ fun deleteEntryKeyInKeystoreForBiometric(fragmentActivity: FragmentActivity,
advancedCallback: AdvancedUnlockErrorCallback) {
- AdvancedUnlockHelper(context).apply {
+ AdvancedUnlockHelper{ fragmentActivity }.apply {
advancedUnlockCallback = object : AdvancedUnlockCallback {
override fun onAuthenticationSucceeded() {}
diff --git a/app/src/main/java/com/kunzisoft/keepass/biometric/AdvancedUnlockManager.kt b/app/src/main/java/com/kunzisoft/keepass/biometric/AdvancedUnlockManager.kt
index 714d863b3..55d818016 100644
--- a/app/src/main/java/com/kunzisoft/keepass/biometric/AdvancedUnlockManager.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/biometric/AdvancedUnlockManager.kt
@@ -39,15 +39,16 @@ import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.view.AdvancedUnlockInfoView
@RequiresApi(api = Build.VERSION_CODES.M)
-class AdvancedUnlockManager(var context: FragmentActivity,
- private var advancedUnlockInfoView: AdvancedUnlockInfoView,
- private var builderListener: BuilderListener)
+class AdvancedUnlockManager(var retrieveContext: () -> FragmentActivity)
: AdvancedUnlockHelper.AdvancedUnlockCallback {
private var advancedUnlockHelper: AdvancedUnlockHelper? = null
private var biometricMode: Mode = Mode.BIOMETRIC_UNAVAILABLE
+ lateinit var advancedUnlockInfoView: AdvancedUnlockInfoView
+ var builderListener: BuilderListener ? = null
- private var databaseFileUri: Uri? = null
+ var databaseFileUri: Uri? = null
+ private set
// Only to fix multiple fingerprint menu #332
private var mAllowAdvancedUnlockMenu = false
@@ -56,8 +57,8 @@ class AdvancedUnlockManager(var context: FragmentActivity,
/**
* Manage setting to auto open biometric prompt
*/
- private var biometricPromptAutoOpenPreference = PreferencesUtil.isAdvancedUnlockPromptAutoOpenEnable(context)
- var isBiometricPromptAutoOpenEnable: Boolean = false
+ private var biometricPromptAutoOpenPreference = PreferencesUtil.isAdvancedUnlockPromptAutoOpenEnable(retrieveContext())
+ var autoOpenPrompt: Boolean = false
get() {
return field && biometricPromptAutoOpenPreference
}
@@ -66,7 +67,7 @@ class AdvancedUnlockManager(var context: FragmentActivity,
// checkBiometricAvailability() allows open biometric prompt and onDestroy() removes the authorization
private var allowOpenBiometricPrompt = false
- private var cipherDatabaseAction = CipherDatabaseAction.getInstance(context.applicationContext)
+ private var cipherDatabaseAction = CipherDatabaseAction.getInstance(retrieveContext().applicationContext)
private var cipherDatabaseListener: CipherDatabaseAction.DatabaseListener? = null
@@ -76,18 +77,18 @@ class AdvancedUnlockManager(var context: FragmentActivity,
*/
fun checkUnlockAvailability() {
- if (PreferencesUtil.isDeviceCredentialUnlockEnable(context)) {
+ if (PreferencesUtil.isDeviceCredentialUnlockEnable(retrieveContext())) {
advancedUnlockInfoView.setIconResource(R.drawable.bolt)
- } else if (PreferencesUtil.isBiometricUnlockEnable(context)) {
+ } else if (PreferencesUtil.isBiometricUnlockEnable(retrieveContext())) {
advancedUnlockInfoView.setIconResource(R.drawable.fingerprint)
}
// biometric not supported (by API level or hardware) so keep option hidden
// or manually disable
- val biometricCanAuthenticate = AdvancedUnlockHelper.canAuthenticate(context)
+ val biometricCanAuthenticate = AdvancedUnlockHelper.canAuthenticate(retrieveContext())
allowOpenBiometricPrompt = true
- if (!PreferencesUtil.isAdvancedUnlockEnable(context)
+ if (!PreferencesUtil.isAdvancedUnlockEnable(retrieveContext())
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE) {
toggleMode(Mode.BIOMETRIC_UNAVAILABLE)
@@ -101,7 +102,7 @@ class AdvancedUnlockManager(var context: FragmentActivity,
// Check if fingerprint well init (be called the first time the fingerprint is configured
// and the activity still active)
if (advancedUnlockHelper?.isKeyManagerInitialized != true) {
- advancedUnlockHelper = AdvancedUnlockHelper(context)
+ advancedUnlockHelper = AdvancedUnlockHelper(retrieveContext)
// callback for fingerprint findings
advancedUnlockHelper?.advancedUnlockCallback = this
}
@@ -109,7 +110,7 @@ class AdvancedUnlockManager(var context: FragmentActivity,
if (advancedUnlockHelper?.isKeyManagerInitialized != true) {
toggleMode(Mode.KEY_MANAGER_UNAVAILABLE)
} else {
- if (builderListener.conditionToStoreCredential()) {
+ if (builderListener?.conditionToStoreCredential() == true) {
// listen for encryption
toggleMode(Mode.STORE_CREDENTIAL)
} else {
@@ -148,7 +149,7 @@ class AdvancedUnlockManager(var context: FragmentActivity,
private fun openBiometricSetting() {
advancedUnlockInfoView.setIconViewClickListener(false) {
// ACTION_SECURITY_SETTINGS does not contain fingerprint enrollment on some devices...
- context.startActivity(Intent(Settings.ACTION_SETTINGS))
+ retrieveContext().startActivity(Intent(Settings.ACTION_SETTINGS))
}
}
@@ -181,12 +182,12 @@ class AdvancedUnlockManager(var context: FragmentActivity,
advancedUnlockInfoView.setIconViewClickListener(false) {
onAuthenticationError(BiometricPrompt.ERROR_UNABLE_TO_PROCESS,
- context.getString(R.string.credential_before_click_advanced_unlock_button))
+ retrieveContext().getString(R.string.credential_before_click_advanced_unlock_button))
}
}
private fun openAdvancedUnlockPrompt(cryptoPrompt: AdvancedUnlockCryptoPrompt) {
- context.runOnUiThread {
+ retrieveContext().runOnUiThread {
if (allowOpenBiometricPrompt) {
try {
advancedUnlockHelper
@@ -229,8 +230,8 @@ class AdvancedUnlockManager(var context: FragmentActivity,
}
// Auto open the biometric prompt
- if (isBiometricPromptAutoOpenEnable) {
- isBiometricPromptAutoOpenEnable = false
+ if (autoOpenPrompt) {
+ autoOpenPrompt = false
openAdvancedUnlockPrompt(cryptoPrompt)
}
}
@@ -266,7 +267,7 @@ class AdvancedUnlockManager(var context: FragmentActivity,
&& (biometricMode != Mode.BIOMETRIC_UNAVAILABLE
&& biometricMode != Mode.KEY_MANAGER_UNAVAILABLE)
mAddBiometricMenuInProgress = false
- context.invalidateOptionsMenu()
+ retrieveContext().invalidateOptionsMenu()
}
}
}
@@ -322,21 +323,21 @@ class AdvancedUnlockManager(var context: FragmentActivity,
}
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
- context.runOnUiThread {
+ retrieveContext().runOnUiThread {
Log.e(TAG, "Biometric authentication error. Code : $errorCode Error : $errString")
setAdvancedUnlockedMessageView(errString.toString())
}
}
override fun onAuthenticationFailed() {
- context.runOnUiThread {
+ retrieveContext().runOnUiThread {
Log.e(TAG, "Biometric authentication failed, biometric not recognized")
setAdvancedUnlockedMessageView(R.string.advanced_unlock_not_recognized)
}
}
override fun onAuthenticationSucceeded() {
- context.runOnUiThread {
+ retrieveContext().runOnUiThread {
when (biometricMode) {
Mode.BIOMETRIC_UNAVAILABLE -> {
}
@@ -350,8 +351,10 @@ class AdvancedUnlockManager(var context: FragmentActivity,
}
Mode.STORE_CREDENTIAL -> {
// newly store the entered password in encrypted way
- advancedUnlockHelper?.encryptData(builderListener.retrieveCredentialForEncryption())
- AdvancedUnlockNotificationService.startServiceForTimeout(context)
+ builderListener?.retrieveCredentialForEncryption()?.let { credential ->
+ advancedUnlockHelper?.encryptData(credential)
+ }
+ AdvancedUnlockNotificationService.startServiceForTimeout(retrieveContext())
}
Mode.EXTRACT_CREDENTIAL -> {
// retrieve the encrypted value from preferences
@@ -369,14 +372,14 @@ class AdvancedUnlockManager(var context: FragmentActivity,
override fun handleEncryptedResult(encryptedValue: String, ivSpec: String) {
databaseFileUri?.let { databaseUri ->
- builderListener.onCredentialEncrypted(databaseUri, encryptedValue, ivSpec)
+ builderListener?.onCredentialEncrypted(databaseUri, encryptedValue, ivSpec)
}
}
override fun handleDecryptedResult(decryptedValue: String) {
// Load database directly with password retrieve
databaseFileUri?.let {
- builderListener.onCredentialDecrypted(it, decryptedValue)
+ builderListener?.onCredentialDecrypted(it, decryptedValue)
}
}
@@ -390,25 +393,25 @@ class AdvancedUnlockManager(var context: FragmentActivity,
}
private fun showFingerPrintViews(show: Boolean) {
- context.runOnUiThread {
+ retrieveContext().runOnUiThread {
advancedUnlockInfoView.visibility = if (show) View.VISIBLE else View.GONE
}
}
private fun setAdvancedUnlockedTitleView(textId: Int) {
- context.runOnUiThread {
+ retrieveContext().runOnUiThread {
advancedUnlockInfoView.setTitle(textId)
}
}
private fun setAdvancedUnlockedMessageView(textId: Int) {
- context.runOnUiThread {
+ retrieveContext().runOnUiThread {
advancedUnlockInfoView.setMessage(textId)
}
}
private fun setAdvancedUnlockedMessageView(text: CharSequence) {
- context.runOnUiThread {
+ retrieveContext().runOnUiThread {
advancedUnlockInfoView.message = text
}
}
diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/MenuUtil.kt b/app/src/main/java/com/kunzisoft/keepass/utils/MenuUtil.kt
index 63d5cb7dc..38a311fc2 100644
--- a/app/src/main/java/com/kunzisoft/keepass/utils/MenuUtil.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/utils/MenuUtil.kt
@@ -53,23 +53,19 @@ object MenuUtil {
fun onDefaultMenuOptionsItemSelected(activity: Activity,
item: MenuItem,
readOnly: Boolean = READ_ONLY_DEFAULT,
- timeoutEnable: Boolean = false): Boolean {
+ timeoutEnable: Boolean = false) {
when (item.itemId) {
R.id.menu_contribute -> {
onContributionItemSelected(activity)
- return true
}
R.id.menu_app_settings -> {
// To avoid flickering when launch settings in a LockingActivity
SettingsActivity.launch(activity, readOnly, timeoutEnable)
- return true
}
R.id.menu_about -> {
val intent = Intent(activity, AboutActivity::class.java)
activity.startActivity(intent)
- return true
}
- else -> return true
}
}
}
diff --git a/app/src/main/res/layout/activity_password.xml b/app/src/main/res/layout/activity_password.xml
index 52e1f3ed2..1955fea2d 100644
--- a/app/src/main/res/layout/activity_password.xml
+++ b/app/src/main/res/layout/activity_password.xml
@@ -68,17 +68,10 @@
android:padding="0dp"
android:contentDescription="@string/about"
android:src="@drawable/ic_launcher_foreground"/>
-
-
-
+ android:layout_height="match_parent" />
+
+
+
\ No newline at end of file