mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
fix: Biometric error prompts #2081
This commit is contained in:
@@ -32,7 +32,6 @@ import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Button
|
||||
import android.widget.CompoundButton
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
@@ -43,6 +42,9 @@ import androidx.appcompat.widget.Toolbar
|
||||
import androidx.biometric.BiometricManager
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
import androidx.fragment.app.commit
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.activities.dialogs.DuplicateUuidDialog
|
||||
@@ -81,10 +83,11 @@ import com.kunzisoft.keepass.view.asError
|
||||
import com.kunzisoft.keepass.view.showActionErrorIfNeeded
|
||||
import com.kunzisoft.keepass.viewmodels.AdvancedUnlockViewModel
|
||||
import com.kunzisoft.keepass.viewmodels.DatabaseFileViewModel
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.FileNotFoundException
|
||||
|
||||
|
||||
class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.BuilderListener {
|
||||
class MainCredentialActivity : DatabaseModeActivity() {
|
||||
|
||||
// Views
|
||||
private var toolbar: Toolbar? = null
|
||||
@@ -166,21 +169,13 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
|
||||
}
|
||||
|
||||
// Listen password checkbox to init advanced unlock and confirmation button
|
||||
mainCredentialView?.onPasswordChecked =
|
||||
CompoundButton.OnCheckedChangeListener { _, _ ->
|
||||
mAdvancedUnlockViewModel.checkUnlockAvailability()
|
||||
enableConfirmationButton()
|
||||
}
|
||||
mainCredentialView?.onKeyFileChecked =
|
||||
CompoundButton.OnCheckedChangeListener { _, _ ->
|
||||
// TODO mAdvancedUnlockViewModel.checkUnlockAvailability()
|
||||
enableConfirmationButton()
|
||||
}
|
||||
mainCredentialView?.onHardwareKeyChecked =
|
||||
CompoundButton.OnCheckedChangeListener { _, _ ->
|
||||
// TODO mAdvancedUnlockViewModel.checkUnlockAvailability()
|
||||
enableConfirmationButton()
|
||||
}
|
||||
mainCredentialView?.onConditionToStoreCredentialChanged = { credentialStorage, verified ->
|
||||
mAdvancedUnlockViewModel.checkUnlockAvailability(
|
||||
conditionToStoreCredentialVerified = verified
|
||||
)
|
||||
// TODO Async by ViewModel
|
||||
enableConfirmationButton()
|
||||
}
|
||||
|
||||
// Observe if default database
|
||||
mDatabaseFileViewModel.isDefaultDatabase.observe(this) { isDefaultDatabase ->
|
||||
@@ -228,6 +223,27 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
|
||||
|
||||
onDatabaseFileLoaded(databaseFile?.databaseUri, keyFileUri, hardwareKey)
|
||||
}
|
||||
|
||||
lifecycleScope.launch {
|
||||
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||
mAdvancedUnlockViewModel.uiState.collect { uiState ->
|
||||
// New value received
|
||||
if (uiState.isCredentialRequired) {
|
||||
mAdvancedUnlockViewModel.provideCredentialForEncryption(
|
||||
getCredentialForEncryption()
|
||||
)
|
||||
}
|
||||
uiState.cipherEncryptDatabase?.let { cipherEncryptDatabase ->
|
||||
onCredentialEncrypted(cipherEncryptDatabase)
|
||||
mAdvancedUnlockViewModel.consumeCredentialEncrypted()
|
||||
}
|
||||
uiState.cipherDecryptDatabase?.let { cipherDecryptDatabase ->
|
||||
onCredentialDecrypted(cipherDecryptDatabase)
|
||||
mAdvancedUnlockViewModel.consumeCredentialDecrypted()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
@@ -400,23 +416,6 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
|
||||
}
|
||||
}
|
||||
|
||||
override fun retrieveCredentialForEncryption(): ByteArray {
|
||||
return mainCredentialView?.retrieveCredentialForStorage(credentialStorageListener)
|
||||
?: byteArrayOf()
|
||||
}
|
||||
|
||||
override fun conditionToStoreCredential(): Boolean {
|
||||
return mainCredentialView?.conditionToStoreCredential() == true
|
||||
}
|
||||
|
||||
override fun onCredentialEncrypted(cipherEncryptDatabase: CipherEncryptDatabase) {
|
||||
// Load the database if password is registered with biometric
|
||||
loadDatabase(mDatabaseFileUri,
|
||||
mainCredentialView?.getMainCredential(),
|
||||
cipherEncryptDatabase
|
||||
)
|
||||
}
|
||||
|
||||
private val credentialStorageListener = object: MainCredentialView.CredentialStorageListener {
|
||||
override fun passwordToStore(password: String?): ByteArray? {
|
||||
return password?.toByteArray()
|
||||
@@ -433,7 +432,20 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCredentialDecrypted(cipherDecryptDatabase: CipherDecryptDatabase) {
|
||||
private fun getCredentialForEncryption(): ByteArray {
|
||||
return mainCredentialView?.retrieveCredentialForStorage(credentialStorageListener)
|
||||
?: byteArrayOf()
|
||||
}
|
||||
|
||||
private fun onCredentialEncrypted(cipherEncryptDatabase: CipherEncryptDatabase) {
|
||||
// Load the database if password is registered with biometric
|
||||
loadDatabase(mDatabaseFileUri,
|
||||
mainCredentialView?.getMainCredential(),
|
||||
cipherEncryptDatabase
|
||||
)
|
||||
}
|
||||
|
||||
private fun onCredentialDecrypted(cipherDecryptDatabase: CipherDecryptDatabase) {
|
||||
// Load the database if password is retrieve from biometric
|
||||
// Retrieve from biometric
|
||||
val mainCredential = mainCredentialView?.getMainCredential() ?: MainCredential()
|
||||
|
||||
@@ -35,7 +35,9 @@ import androidx.biometric.BiometricPrompt
|
||||
import androidx.core.view.MenuProvider
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.app.database.CipherDatabaseAction
|
||||
import com.kunzisoft.keepass.database.exception.UnknownDatabaseLocationException
|
||||
@@ -52,8 +54,6 @@ import kotlinx.coroutines.launch
|
||||
|
||||
class AdvancedUnlockFragment: Fragment(), AdvancedUnlockManager.AdvancedUnlockCallback {
|
||||
|
||||
private var mBuilderListener: BuilderListener? = null
|
||||
|
||||
private var mAdvancedUnlockEnabled = false
|
||||
private var mAutoOpenPromptEnabled = false
|
||||
|
||||
@@ -84,6 +84,8 @@ class AdvancedUnlockFragment: Fragment(), AdvancedUnlockManager.AdvancedUnlockCa
|
||||
// Only keep connection when we request a device credential activity
|
||||
private var keepConnection = false
|
||||
|
||||
private var isConditionToStoreCredentialVerified = false
|
||||
|
||||
private var mDeviceCredentialResultLauncher = registerForActivityResult(
|
||||
ActivityResultContracts.StartActivityForResult()
|
||||
) { result ->
|
||||
@@ -120,14 +122,6 @@ class AdvancedUnlockFragment: Fragment(), AdvancedUnlockManager.AdvancedUnlockCa
|
||||
|
||||
mAdvancedUnlockEnabled = PreferencesUtil.isAdvancedUnlockEnable(context)
|
||||
mAutoOpenPromptEnabled = PreferencesUtil.isAdvancedUnlockPromptAutoOpenEnable(context)
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
mBuilderListener = context as BuilderListener
|
||||
}
|
||||
} catch (e: ClassCastException) {
|
||||
throw ClassCastException(context.toString()
|
||||
+ " must implement " + BuilderListener::class.java.name)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
@@ -138,11 +132,6 @@ class AdvancedUnlockFragment: Fragment(), AdvancedUnlockManager.AdvancedUnlockCa
|
||||
mAdvancedUnlockViewModel.onInitAdvancedUnlockModeRequested.observe(this) {
|
||||
initAdvancedUnlockMode()
|
||||
}
|
||||
|
||||
mAdvancedUnlockViewModel.onUnlockAvailabilityCheckRequested.observe(this) {
|
||||
checkUnlockAvailability()
|
||||
}
|
||||
|
||||
mAdvancedUnlockViewModel.onDatabaseFileLoaded.observe(this) {
|
||||
onDatabaseLoaded(it)
|
||||
}
|
||||
@@ -162,6 +151,27 @@ class AdvancedUnlockFragment: Fragment(), AdvancedUnlockManager.AdvancedUnlockCa
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
activity?.addMenuProvider(menuProvider, viewLifecycleOwner)
|
||||
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||
mAdvancedUnlockViewModel.uiState.collect { uiState ->
|
||||
// New credential value received
|
||||
uiState.credential?.let {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
advancedUnlockManager?.encryptData(uiState.credential)
|
||||
}
|
||||
mAdvancedUnlockViewModel.consumeCredentialForEncryption()
|
||||
}
|
||||
// Condition to store credential verified
|
||||
isConditionToStoreCredentialVerified = uiState.isConditionToStoreCredentialVerified
|
||||
// Check unlock availability
|
||||
if (uiState.onUnlockAvailabilityCheckRequested) {
|
||||
checkUnlockAvailability()
|
||||
mAdvancedUnlockViewModel.consumeCheckUnlockAvailability()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
@@ -250,7 +260,7 @@ class AdvancedUnlockFragment: Fragment(), AdvancedUnlockManager.AdvancedUnlockCa
|
||||
if (advancedUnlockManager?.isKeyManagerInitialized != true) {
|
||||
toggleMode(Mode.KEY_MANAGER_UNAVAILABLE)
|
||||
} else {
|
||||
if (mBuilderListener?.conditionToStoreCredential() == true) {
|
||||
if (isConditionToStoreCredentialVerified) {
|
||||
// listen for encryption
|
||||
toggleMode(Mode.STORE_CREDENTIAL)
|
||||
} else {
|
||||
@@ -261,8 +271,13 @@ class AdvancedUnlockFragment: Fragment(), AdvancedUnlockManager.AdvancedUnlockCa
|
||||
// listen for decryption
|
||||
Mode.EXTRACT_CREDENTIAL
|
||||
} else {
|
||||
// wait for typing
|
||||
Mode.WAIT_CREDENTIAL
|
||||
if (isConditionToStoreCredentialVerified) {
|
||||
// if condition OK, key manager in error
|
||||
Mode.KEY_MANAGER_UNAVAILABLE
|
||||
} else {
|
||||
// wait for typing
|
||||
Mode.WAIT_CREDENTIAL
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -523,9 +538,7 @@ class AdvancedUnlockFragment: Fragment(), AdvancedUnlockManager.AdvancedUnlockCa
|
||||
}
|
||||
Mode.STORE_CREDENTIAL -> {
|
||||
// newly store the entered password in encrypted way
|
||||
mBuilderListener?.retrieveCredentialForEncryption()?.let { credential ->
|
||||
advancedUnlockManager?.encryptData(credential)
|
||||
}
|
||||
mAdvancedUnlockViewModel.retrieveCredentialForEncryption()
|
||||
}
|
||||
Mode.EXTRACT_CREDENTIAL -> {
|
||||
// retrieve the encrypted value from preferences
|
||||
@@ -545,7 +558,7 @@ class AdvancedUnlockFragment: Fragment(), AdvancedUnlockManager.AdvancedUnlockCa
|
||||
|
||||
override fun handleEncryptedResult(encryptedValue: ByteArray, ivSpec: ByteArray) {
|
||||
databaseFileUri?.let { databaseUri ->
|
||||
mBuilderListener?.onCredentialEncrypted(
|
||||
mAdvancedUnlockViewModel.onCredentialEncrypted(
|
||||
CipherEncryptDatabase().apply {
|
||||
this.databaseUri = databaseUri
|
||||
this.credentialStorage = credentialDatabaseStorage
|
||||
@@ -559,7 +572,7 @@ class AdvancedUnlockFragment: Fragment(), AdvancedUnlockManager.AdvancedUnlockCa
|
||||
override fun handleDecryptedResult(decryptedValue: ByteArray) {
|
||||
// Load database directly with password retrieve
|
||||
databaseFileUri?.let { databaseUri ->
|
||||
mBuilderListener?.onCredentialDecrypted(
|
||||
mAdvancedUnlockViewModel.onCredentialDecrypted(
|
||||
CipherDecryptDatabase().apply {
|
||||
this.databaseUri = databaseUri
|
||||
this.credentialStorage = credentialDatabaseStorage
|
||||
@@ -630,13 +643,6 @@ class AdvancedUnlockFragment: Fragment(), AdvancedUnlockManager.AdvancedUnlockCa
|
||||
EXTRACT_CREDENTIAL
|
||||
}
|
||||
|
||||
interface BuilderListener {
|
||||
fun retrieveCredentialForEncryption(): ByteArray
|
||||
fun conditionToStoreCredential(): Boolean
|
||||
fun onCredentialEncrypted(cipherEncryptDatabase: CipherEncryptDatabase)
|
||||
fun onCredentialDecrypted(cipherDecryptDatabase: CipherDecryptDatabase)
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
if (!keepConnection) {
|
||||
@@ -645,13 +651,11 @@ class AdvancedUnlockFragment: Fragment(), AdvancedUnlockManager.AdvancedUnlockCa
|
||||
advancedUnlockManager = null
|
||||
}
|
||||
}
|
||||
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
mAdvancedUnlockInfoView = null
|
||||
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
@@ -659,20 +663,12 @@ class AdvancedUnlockFragment: Fragment(), AdvancedUnlockManager.AdvancedUnlockCa
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
disconnect()
|
||||
advancedUnlockManager = null
|
||||
mBuilderListener = null
|
||||
}
|
||||
|
||||
mAdvancedUnlockViewModel.deleteData()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
override fun onDetach() {
|
||||
mBuilderListener = null
|
||||
|
||||
super.onDetach()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private val TAG = AdvancedUnlockFragment::class.java.name
|
||||
}
|
||||
}
|
||||
@@ -53,9 +53,7 @@ class MainCredentialView @JvmOverloads constructor(context: Context,
|
||||
private var checkboxHardwareView: CompoundButton
|
||||
private var hardwareKeySelectionView: HardwareKeySelectionView
|
||||
|
||||
var onPasswordChecked: (CompoundButton.OnCheckedChangeListener)? = null
|
||||
var onKeyFileChecked: (CompoundButton.OnCheckedChangeListener)? = null
|
||||
var onHardwareKeyChecked: (CompoundButton.OnCheckedChangeListener)? = null
|
||||
var onConditionToStoreCredentialChanged: ((CredentialStorage, verified: Boolean) -> Unit)? = null
|
||||
var onValidateListener: (() -> Unit)? = null
|
||||
|
||||
private var mCredentialStorage: CredentialStorage = CredentialStorage.PASSWORD
|
||||
@@ -104,7 +102,10 @@ class MainCredentialView @JvmOverloads constructor(context: Context,
|
||||
}
|
||||
|
||||
checkboxPasswordView.setOnCheckedChangeListener { view, checked ->
|
||||
onPasswordChecked?.onCheckedChanged(view, checked)
|
||||
onConditionToStoreCredentialChanged?.invoke(
|
||||
mCredentialStorage,
|
||||
conditionToStoreCredential()
|
||||
)
|
||||
}
|
||||
checkboxKeyFileView.setOnCheckedChangeListener { view, checked ->
|
||||
if (checked) {
|
||||
@@ -112,7 +113,10 @@ class MainCredentialView @JvmOverloads constructor(context: Context,
|
||||
checkboxKeyFileView.isChecked = false
|
||||
}
|
||||
}
|
||||
onKeyFileChecked?.onCheckedChanged(view, checked)
|
||||
onConditionToStoreCredentialChanged?.invoke(
|
||||
mCredentialStorage,
|
||||
conditionToStoreCredential()
|
||||
)
|
||||
}
|
||||
checkboxHardwareView.setOnCheckedChangeListener { view, checked ->
|
||||
if (checked) {
|
||||
@@ -120,7 +124,10 @@ class MainCredentialView @JvmOverloads constructor(context: Context,
|
||||
checkboxHardwareView.isChecked = false
|
||||
}
|
||||
}
|
||||
onHardwareKeyChecked?.onCheckedChanged(view, checked)
|
||||
onConditionToStoreCredentialChanged?.invoke(
|
||||
mCredentialStorage,
|
||||
conditionToStoreCredential()
|
||||
)
|
||||
}
|
||||
|
||||
hardwareKeySelectionView.selectionListener = { _ ->
|
||||
|
||||
@@ -3,18 +3,23 @@ package com.kunzisoft.keepass.viewmodels
|
||||
import android.net.Uri
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.kunzisoft.keepass.model.CipherDecryptDatabase
|
||||
import com.kunzisoft.keepass.model.CipherEncryptDatabase
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
|
||||
class AdvancedUnlockViewModel : ViewModel() {
|
||||
|
||||
var allowAutoOpenBiometricPrompt : Boolean = true
|
||||
var deviceCredentialAuthSucceeded: Boolean? = null
|
||||
|
||||
private val _uiState = MutableStateFlow(DeviceUnlockUiStates())
|
||||
val uiState: StateFlow<DeviceUnlockUiStates> = _uiState
|
||||
|
||||
val onInitAdvancedUnlockModeRequested : LiveData<Void?> get() = _onInitAdvancedUnlockModeRequested
|
||||
private val _onInitAdvancedUnlockModeRequested = SingleLiveEvent<Void?>()
|
||||
|
||||
val onUnlockAvailabilityCheckRequested : LiveData<Void?> get() = _onUnlockAvailabilityCheckRequested
|
||||
private val _onUnlockAvailabilityCheckRequested = SingleLiveEvent<Void?>()
|
||||
|
||||
val onDatabaseFileLoaded : LiveData<Uri?> get() = _onDatabaseFileLoaded
|
||||
private val _onDatabaseFileLoaded = SingleLiveEvent<Uri?>()
|
||||
|
||||
@@ -22,11 +27,135 @@ class AdvancedUnlockViewModel : ViewModel() {
|
||||
_onInitAdvancedUnlockModeRequested.call()
|
||||
}
|
||||
|
||||
fun checkUnlockAvailability() {
|
||||
_onUnlockAvailabilityCheckRequested.call()
|
||||
fun checkUnlockAvailability(conditionToStoreCredentialVerified: Boolean) {
|
||||
_uiState.update { currentState ->
|
||||
currentState.copy(
|
||||
onUnlockAvailabilityCheckRequested = true,
|
||||
isConditionToStoreCredentialVerified = conditionToStoreCredentialVerified
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun consumeCheckUnlockAvailability() {
|
||||
_uiState.update { currentState ->
|
||||
currentState.copy(
|
||||
onUnlockAvailabilityCheckRequested = false
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun databaseFileLoaded(databaseUri: Uri?) {
|
||||
_onDatabaseFileLoaded.value = databaseUri
|
||||
}
|
||||
|
||||
fun retrieveCredentialForEncryption() {
|
||||
_uiState.update { currentState ->
|
||||
currentState.copy(
|
||||
isCredentialRequired = true,
|
||||
credential = null
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun provideCredentialForEncryption(credential: ByteArray) {
|
||||
_uiState.update { currentState ->
|
||||
currentState.copy(
|
||||
isCredentialRequired = false,
|
||||
credential = credential
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun consumeCredentialForEncryption() {
|
||||
_uiState.update { currentState ->
|
||||
currentState.copy(
|
||||
isCredentialRequired = false,
|
||||
credential = null
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun onCredentialEncrypted(cipherEncryptDatabase: CipherEncryptDatabase) {
|
||||
_uiState.update { currentState ->
|
||||
currentState.copy(
|
||||
cipherEncryptDatabase = cipherEncryptDatabase
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun consumeCredentialEncrypted() {
|
||||
_uiState.update { currentState ->
|
||||
currentState.copy(
|
||||
cipherEncryptDatabase = null
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun onCredentialDecrypted(cipherDecryptDatabase: CipherDecryptDatabase) {
|
||||
_uiState.update { currentState ->
|
||||
currentState.copy(
|
||||
cipherDecryptDatabase = cipherDecryptDatabase
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun consumeCredentialDecrypted() {
|
||||
_uiState.update { currentState ->
|
||||
currentState.copy(
|
||||
cipherDecryptDatabase = null
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun deleteData() {
|
||||
_uiState.update { currentState ->
|
||||
currentState.copy(
|
||||
initAdvancedUnlockMode = false,
|
||||
databaseFileUri = null,
|
||||
isCredentialRequired = false,
|
||||
credential = null,
|
||||
isConditionToStoreCredentialVerified = false,
|
||||
onUnlockAvailabilityCheckRequested = false,
|
||||
cipherEncryptDatabase = null,
|
||||
cipherDecryptDatabase = null
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class DeviceUnlockUiStates(
|
||||
val initAdvancedUnlockMode: Boolean = false,
|
||||
val databaseFileUri: Uri? = null,
|
||||
val isCredentialRequired: Boolean = false,
|
||||
val credential: ByteArray? = null,
|
||||
val isConditionToStoreCredentialVerified: Boolean = false,
|
||||
val onUnlockAvailabilityCheckRequested: Boolean = false,
|
||||
val cipherEncryptDatabase: CipherEncryptDatabase? = null,
|
||||
val cipherDecryptDatabase: CipherDecryptDatabase? = null
|
||||
) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as DeviceUnlockUiStates
|
||||
|
||||
if (isCredentialRequired != other.isCredentialRequired) return false
|
||||
if (isConditionToStoreCredentialVerified != other.isConditionToStoreCredentialVerified) return false
|
||||
if (onUnlockAvailabilityCheckRequested != other.onUnlockAvailabilityCheckRequested) return false
|
||||
if (!credential.contentEquals(other.credential)) return false
|
||||
if (cipherEncryptDatabase != other.cipherEncryptDatabase) return false
|
||||
if (cipherDecryptDatabase != other.cipherDecryptDatabase) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = isCredentialRequired.hashCode()
|
||||
result = 31 * result + isConditionToStoreCredentialVerified.hashCode()
|
||||
result = 31 * result + onUnlockAvailabilityCheckRequested.hashCode()
|
||||
result = 31 * result + (credential?.contentHashCode() ?: 0)
|
||||
result = 31 * result + (cipherEncryptDatabase?.hashCode() ?: 0)
|
||||
result = 31 * result + (cipherDecryptDatabase?.hashCode() ?: 0)
|
||||
return result
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user