Add fragment

This commit is contained in:
J-Jamet
2020-12-13 14:18:21 +01:00
parent bd0b5b0954
commit 6be0457947
8 changed files with 268 additions and 150 deletions

View File

@@ -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 {

View File

@@ -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"

View File

@@ -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()
}
}

View File

@@ -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() {}

View File

@@ -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
}
}

View File

@@ -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
}
}
}

View File

@@ -68,17 +68,10 @@
android:padding="0dp"
android:contentDescription="@string/about"
android:src="@drawable/ic_launcher_foreground"/>
<FrameLayout
<androidx.fragment.app.FragmentContainerView
android:id="@+id/fragment_advanced_unlock_container_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.kunzisoft.keepass.view.AdvancedUnlockInfoView
android:id="@+id/biometric_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="?attr/colorPrimary"
android:visibility="gone"/>
</FrameLayout>
android:layout_height="match_parent" />
</FrameLayout>
<androidx.appcompat.widget.Toolbar

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.kunzisoft.keepass.view.AdvancedUnlockInfoView
android:id="@+id/advanced_unlock_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="?attr/colorPrimary"
android:visibility="gone"/>
</FrameLayout>