fix: Add User Verification for Entry Edition #2283

This commit is contained in:
J-Jamet
2025-11-29 13:04:01 +01:00
parent 7ed8a44168
commit d251788b1a
7 changed files with 164 additions and 78 deletions

View File

@@ -41,6 +41,9 @@ import androidx.core.graphics.BlendModeColorFilterCompat
import androidx.core.graphics.BlendModeCompat import androidx.core.graphics.BlendModeCompat
import androidx.core.graphics.ColorUtils import androidx.core.graphics.ColorUtils
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.appbar.AppBarLayout import com.google.android.material.appbar.AppBarLayout
@@ -53,6 +56,8 @@ import com.kunzisoft.keepass.activities.helpers.ExternalFileHelper
import com.kunzisoft.keepass.activities.legacy.DatabaseLockActivity import com.kunzisoft.keepass.activities.legacy.DatabaseLockActivity
import com.kunzisoft.keepass.adapters.TagsAdapter import com.kunzisoft.keepass.adapters.TagsAdapter
import com.kunzisoft.keepass.credentialprovider.SpecialMode import com.kunzisoft.keepass.credentialprovider.SpecialMode
import com.kunzisoft.keepass.credentialprovider.UserVerificationData
import com.kunzisoft.keepass.credentialprovider.UserVerificationHelper.Companion.askUserVerification
import com.kunzisoft.keepass.credentialprovider.magikeyboard.MagikeyboardService import com.kunzisoft.keepass.credentialprovider.magikeyboard.MagikeyboardService
import com.kunzisoft.keepass.database.ContextualDatabase import com.kunzisoft.keepass.database.ContextualDatabase
import com.kunzisoft.keepass.database.element.Attachment import com.kunzisoft.keepass.database.element.Attachment
@@ -79,6 +84,8 @@ import com.kunzisoft.keepass.view.hideByFading
import com.kunzisoft.keepass.view.setTransparentNavigationBar import com.kunzisoft.keepass.view.setTransparentNavigationBar
import com.kunzisoft.keepass.view.showActionErrorIfNeeded import com.kunzisoft.keepass.view.showActionErrorIfNeeded
import com.kunzisoft.keepass.viewmodels.EntryViewModel import com.kunzisoft.keepass.viewmodels.EntryViewModel
import com.kunzisoft.keepass.viewmodels.UserVerificationViewModel
import kotlinx.coroutines.launch
import java.util.EnumSet import java.util.EnumSet
import java.util.UUID import java.util.UUID
@@ -100,14 +107,10 @@ class EntryActivity : DatabaseLockActivity() {
private var loadingView: ProgressBar? = null private var loadingView: ProgressBar? = null
private val mEntryViewModel: EntryViewModel by viewModels() private val mEntryViewModel: EntryViewModel by viewModels()
private val mUserVerificationViewModel: UserVerificationViewModel by viewModels()
private val mEntryActivityEducation = EntryActivityEducation(this) private val mEntryActivityEducation = EntryActivityEducation(this)
private var mMainEntryId: NodeId<UUID>? = null
private var mHistoryPosition: Int = -1
private var mEntryIsHistory: Boolean = false
private var mEntryLoaded = false
private var mAttachmentFileBinderManager: AttachmentFileBinderManager? = null private var mAttachmentFileBinderManager: AttachmentFileBinderManager? = null
private var mExternalFileHelper: ExternalFileHelper? = null private var mExternalFileHelper: ExternalFileHelper? = null
private var mAttachmentSelected: Attachment? = null private var mAttachmentSelected: Attachment? = null
@@ -238,13 +241,9 @@ class EntryActivity : DatabaseLockActivity() {
mEntryViewModel.entryInfoHistory.observe(this) { entryInfoHistory -> mEntryViewModel.entryInfoHistory.observe(this) { entryInfoHistory ->
if (entryInfoHistory != null) { if (entryInfoHistory != null) {
this.mMainEntryId = entryInfoHistory.mainEntryId
// Manage history position // Manage history position
val historyPosition = entryInfoHistory.historyPosition val historyPosition = entryInfoHistory.historyPosition
this.mHistoryPosition = historyPosition
val entryIsHistory = historyPosition > -1 val entryIsHistory = historyPosition > -1
this.mEntryIsHistory = entryIsHistory
// Assign history dedicated view // Assign history dedicated view
historyView?.visibility = if (entryIsHistory) View.VISIBLE else View.GONE historyView?.visibility = if (entryIsHistory) View.VISIBLE else View.GONE
// TODO History badge // TODO History badge
@@ -279,7 +278,6 @@ class EntryActivity : DatabaseLockActivity() {
mForegroundColor = if (showEntryColors) entryInfo.foregroundColor else null mForegroundColor = if (showEntryColors) entryInfo.foregroundColor else null
loadingView?.hideByFading() loadingView?.hideByFading()
mEntryLoaded = true
} else { } else {
finish() finish()
} }
@@ -322,6 +320,33 @@ class EntryActivity : DatabaseLockActivity() {
) )
} }
} }
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.RESUMED) {
mUserVerificationViewModel.uiState.collect { uIState ->
when (uIState) {
is UserVerificationViewModel.UIState.Loading -> {}
is UserVerificationViewModel.UIState.OnUserVerificationCanceled -> {
mUserVerificationViewModel.onUserVerificationReceived()
}
is UserVerificationViewModel.UIState.OnUserVerificationSucceeded -> {
uIState.dataToVerify.database?.let { database ->
uIState.dataToVerify.entryId?.let { entryId ->
EntryEditActivity.launch(
activity = this@EntryActivity,
database = database,
registrationType = EntryEditActivity.RegistrationType.UPDATE,
nodeId = entryId,
activityResultLauncher = mEntryActivityResultLauncher
)
}
}
mUserVerificationViewModel.onUserVerificationReceived()
}
}
}
}
}
} }
override fun finishActivityIfReloadRequested(): Boolean { override fun finishActivityIfReloadRequested(): Boolean {
@@ -410,13 +435,13 @@ class EntryActivity : DatabaseLockActivity() {
override fun onCreateOptionsMenu(menu: Menu): Boolean { override fun onCreateOptionsMenu(menu: Menu): Boolean {
super.onCreateOptionsMenu(menu) super.onCreateOptionsMenu(menu)
if (mEntryLoaded) { if (mEntryViewModel.entryLoaded) {
val inflater = menuInflater val inflater = menuInflater
inflater.inflate(R.menu.entry, menu) inflater.inflate(R.menu.entry, menu)
inflater.inflate(R.menu.database, menu) inflater.inflate(R.menu.database, menu)
if (mEntryIsHistory && !mDatabaseReadOnly) { if (mEntryViewModel.entryIsHistory && !mDatabaseReadOnly) {
inflater.inflate(R.menu.entry_history, menu) inflater.inflate(R.menu.entry_history, menu)
} }
@@ -429,7 +454,7 @@ class EntryActivity : DatabaseLockActivity() {
} }
override fun onPrepareOptionsMenu(menu: Menu?): Boolean { override fun onPrepareOptionsMenu(menu: Menu?): Boolean {
if (mEntryIsHistory || mDatabaseReadOnly) { if (mEntryViewModel.entryIsHistory || mDatabaseReadOnly) {
menu?.findItem(R.id.menu_save_database)?.isVisible = false menu?.findItem(R.id.menu_save_database)?.isVisible = false
menu?.findItem(R.id.menu_merge_database)?.isVisible = false menu?.findItem(R.id.menu_merge_database)?.isVisible = false
menu?.findItem(R.id.menu_edit)?.isVisible = false menu?.findItem(R.id.menu_edit)?.isVisible = false
@@ -477,31 +502,27 @@ class EntryActivity : DatabaseLockActivity() {
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) { when (item.itemId) {
R.id.menu_edit -> { R.id.menu_edit -> {
mDatabase?.let { database -> askUserVerification(
mMainEntryId?.let { entryId -> userVerificationViewModel = mUserVerificationViewModel,
EntryEditActivity.launch( userVerificationCondition = true,
activity = this, dataToVerify = UserVerificationData(mDatabase, mEntryViewModel.mainEntryId)
database = database,
registrationType = EntryEditActivity.RegistrationType.UPDATE,
nodeId = entryId,
activityResultLauncher = mEntryActivityResultLauncher
) )
}
}
return true return true
} }
R.id.menu_restore_entry_history -> { R.id.menu_restore_entry_history -> {
mMainEntryId?.let { mainEntryId -> mEntryViewModel.mainEntryId?.let { mainEntryId ->
restoreEntryHistory( restoreEntryHistory(
mainEntryId, mainEntryId,
mHistoryPosition) mEntryViewModel.historyPosition
)
} }
} }
R.id.menu_delete_entry_history -> { R.id.menu_delete_entry_history -> {
mMainEntryId?.let { mainEntryId -> mEntryViewModel.mainEntryId?.let { mainEntryId ->
deleteEntryHistory( deleteEntryHistory(
mainEntryId, mainEntryId,
mHistoryPosition) mEntryViewModel.historyPosition
)
} }
} }
R.id.menu_save_database -> { R.id.menu_save_database -> {
@@ -521,7 +542,7 @@ class EntryActivity : DatabaseLockActivity() {
override fun finish() { override fun finish() {
// Transit data in previous Activity after an update // Transit data in previous Activity after an update
Intent().apply { Intent().apply {
putExtra(EntryEditActivity.ADD_OR_UPDATE_ENTRY_KEY, mMainEntryId) putExtra(EntryEditActivity.ADD_OR_UPDATE_ENTRY_KEY, mEntryViewModel.mainEntryId)
setResult(RESULT_OK, this) setResult(RESULT_OK, this)
} }
super.finish() super.finish()

View File

@@ -52,7 +52,9 @@ import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat import androidx.core.view.WindowInsetsControllerCompat
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.drawerlayout.widget.DrawerLayout import androidx.drawerlayout.widget.DrawerLayout
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.datepicker.MaterialDatePicker import com.google.android.material.datepicker.MaterialDatePicker
import com.google.android.material.timepicker.MaterialTimePicker import com.google.android.material.timepicker.MaterialTimePicker
@@ -73,6 +75,8 @@ import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.removeModes
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.retrieveSearchInfo import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.retrieveSearchInfo
import com.kunzisoft.keepass.credentialprovider.SpecialMode import com.kunzisoft.keepass.credentialprovider.SpecialMode
import com.kunzisoft.keepass.credentialprovider.TypeMode import com.kunzisoft.keepass.credentialprovider.TypeMode
import com.kunzisoft.keepass.credentialprovider.UserVerificationData
import com.kunzisoft.keepass.credentialprovider.UserVerificationHelper.Companion.askUserVerification
import com.kunzisoft.keepass.credentialprovider.magikeyboard.MagikeyboardService import com.kunzisoft.keepass.credentialprovider.magikeyboard.MagikeyboardService
import com.kunzisoft.keepass.credentialprovider.passkey.util.PasskeyHelper.buildPasskeyResponseAndSetResult import com.kunzisoft.keepass.credentialprovider.passkey.util.PasskeyHelper.buildPasskeyResponseAndSetResult
import com.kunzisoft.keepass.database.ContextualDatabase import com.kunzisoft.keepass.database.ContextualDatabase
@@ -122,6 +126,7 @@ import com.kunzisoft.keepass.view.updateLockPaddingStart
import com.kunzisoft.keepass.viewmodels.GroupEditViewModel import com.kunzisoft.keepass.viewmodels.GroupEditViewModel
import com.kunzisoft.keepass.viewmodels.GroupViewModel import com.kunzisoft.keepass.viewmodels.GroupViewModel
import com.kunzisoft.keepass.viewmodels.MainCredentialViewModel import com.kunzisoft.keepass.viewmodels.MainCredentialViewModel
import com.kunzisoft.keepass.viewmodels.UserVerificationViewModel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.joda.time.LocalDateTime import org.joda.time.LocalDateTime
import java.util.EnumSet import java.util.EnumSet
@@ -158,6 +163,7 @@ class GroupActivity : DatabaseLockActivity(),
private val mGroupViewModel: GroupViewModel by viewModels() private val mGroupViewModel: GroupViewModel by viewModels()
private val mGroupEditViewModel: GroupEditViewModel by viewModels() private val mGroupEditViewModel: GroupEditViewModel by viewModels()
private val mMainCredentialViewModel: MainCredentialViewModel by viewModels() private val mMainCredentialViewModel: MainCredentialViewModel by viewModels()
private val mUserVerificationViewModel: UserVerificationViewModel by viewModels()
private val mGroupActivityEducation = GroupActivityEducation(this) private val mGroupActivityEducation = GroupActivityEducation(this)
@@ -565,6 +571,33 @@ class GroupActivity : DatabaseLockActivity(),
} }
} }
} }
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.RESUMED) {
mUserVerificationViewModel.uiState.collect { uIState ->
when (uIState) {
is UserVerificationViewModel.UIState.Loading -> {}
is UserVerificationViewModel.UIState.OnUserVerificationCanceled -> {
mUserVerificationViewModel.onUserVerificationReceived()
}
is UserVerificationViewModel.UIState.OnUserVerificationSucceeded -> {
uIState.dataToVerify.database?.let { database ->
uIState.dataToVerify.entryId?.let { entryId ->
EntryEditActivity.launch(
activity = this@GroupActivity,
database = database,
registrationType = EntryEditActivity.RegistrationType.UPDATE,
nodeId = entryId,
activityResultLauncher = mEntryActivityResultLauncher
)
}
}
mUserVerificationViewModel.onUserVerificationReceived()
}
}
}
}
}
} }
override fun viewToInvalidateTimeout(): View? { override fun viewToInvalidateTimeout(): View? {
@@ -1060,12 +1093,10 @@ class GroupActivity : DatabaseLockActivity(),
launchDialogForGroupUpdate(node as Group) launchDialogForGroupUpdate(node as Group)
} }
Type.ENTRY -> { Type.ENTRY -> {
EntryEditActivity.launch( askUserVerification(
activity = this@GroupActivity, userVerificationViewModel = mUserVerificationViewModel,
database = database, userVerificationCondition = true,
registrationType = EntryEditActivity.RegistrationType.UPDATE, dataToVerify = UserVerificationData(database,node.nodeId)
nodeId = (node as Entry).nodeId,
activityResultLauncher = mEntryActivityResultLauncher
) )
} }
} }

View File

@@ -0,0 +1,9 @@
package com.kunzisoft.keepass.credentialprovider
import com.kunzisoft.keepass.database.ContextualDatabase
import com.kunzisoft.keepass.database.element.node.NodeId
data class UserVerificationData(
val database: ContextualDatabase? = null,
val entryId: NodeId<*>? = null
)

View File

@@ -14,7 +14,6 @@ import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.dialogs.MainCredentialDialogFragment import com.kunzisoft.keepass.activities.dialogs.MainCredentialDialogFragment
import com.kunzisoft.keepass.activities.dialogs.MainCredentialDialogFragment.Companion.TAG_ASK_MAIN_CREDENTIAL import com.kunzisoft.keepass.activities.dialogs.MainCredentialDialogFragment.Companion.TAG_ASK_MAIN_CREDENTIAL
import com.kunzisoft.keepass.credentialprovider.passkey.data.UserVerificationRequirement import com.kunzisoft.keepass.credentialprovider.passkey.data.UserVerificationRequirement
import com.kunzisoft.keepass.database.ContextualDatabase
import com.kunzisoft.keepass.utils.getEnumExtra import com.kunzisoft.keepass.utils.getEnumExtra
import com.kunzisoft.keepass.utils.putEnumExtra import com.kunzisoft.keepass.utils.putEnumExtra
import com.kunzisoft.keepass.view.toastError import com.kunzisoft.keepass.view.toastError
@@ -86,12 +85,13 @@ class UserVerificationHelper {
* Ask for the database credential otherwise * Ask for the database credential otherwise
*/ */
fun FragmentActivity.askUserVerification( fun FragmentActivity.askUserVerification(
database: ContextualDatabase?, userVerificationViewModel: UserVerificationViewModel,
userVerificationViewModel: UserVerificationViewModel userVerificationCondition: Boolean,
dataToVerify: UserVerificationData
) { ) {
if (this.intent.getUserVerificationCondition()) { if (userVerificationCondition) {
// Important to check the nullable database here // Important to check the nullable database here
database?.let { dataToVerify.database?.let {
if (isAuthenticatorsAllowed()) { if (isAuthenticatorsAllowed()) {
BiometricPrompt( BiometricPrompt(
this, ContextCompat.getMainExecutor(this), this, ContextCompat.getMainExecutor(this),
@@ -113,20 +113,20 @@ class UserVerificationHelper {
toastError(SecurityException("Authentication error: $errString")) toastError(SecurityException("Authentication error: $errString"))
} }
} }
userVerificationViewModel.onUserVerificationFailed(database) userVerificationViewModel.onUserVerificationFailed(dataToVerify)
} }
override fun onAuthenticationSucceeded( override fun onAuthenticationSucceeded(
result: BiometricPrompt.AuthenticationResult result: BiometricPrompt.AuthenticationResult
) { ) {
super.onAuthenticationSucceeded(result) super.onAuthenticationSucceeded(result)
userVerificationViewModel.onUserVerificationSucceeded(database) userVerificationViewModel.onUserVerificationSucceeded(dataToVerify)
} }
override fun onAuthenticationFailed() { override fun onAuthenticationFailed() {
super.onAuthenticationFailed() super.onAuthenticationFailed()
toastError(SecurityException(getString(R.string.device_unlock_not_recognized))) toastError(SecurityException(getString(R.string.device_unlock_not_recognized)))
userVerificationViewModel.onUserVerificationFailed(database) userVerificationViewModel.onUserVerificationFailed(dataToVerify)
} }
}).authenticate( }).authenticate(
BiometricPrompt.PromptInfo.Builder() BiometricPrompt.PromptInfo.Builder()
@@ -141,7 +141,7 @@ class UserVerificationHelper {
.findFragmentByTag(TAG_ASK_MAIN_CREDENTIAL) as? MainCredentialDialogFragment? .findFragmentByTag(TAG_ASK_MAIN_CREDENTIAL) as? MainCredentialDialogFragment?
if (mainCredentialDialogFragment == null) { if (mainCredentialDialogFragment == null) {
mainCredentialDialogFragment = MainCredentialDialogFragment mainCredentialDialogFragment = MainCredentialDialogFragment
.getInstance(database.fileUri) .getInstance(dataToVerify.database.fileUri)
mainCredentialDialogFragment.show( mainCredentialDialogFragment.show(
supportFragmentManager, supportFragmentManager,
TAG_ASK_MAIN_CREDENTIAL TAG_ASK_MAIN_CREDENTIAL
@@ -150,7 +150,7 @@ class UserVerificationHelper {
} }
} }
} else { } else {
userVerificationViewModel.onUserVerificationSucceeded(database) userVerificationViewModel.onUserVerificationSucceeded(dataToVerify)
} }
} }
} }

View File

@@ -31,7 +31,9 @@ import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.kunzisoft.keepass.R import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.FileDatabaseSelectActivity import com.kunzisoft.keepass.activities.FileDatabaseSelectActivity
import com.kunzisoft.keepass.activities.GroupActivity import com.kunzisoft.keepass.activities.GroupActivity
@@ -43,6 +45,7 @@ import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.addTypeMode
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.setActivityResult import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.setActivityResult
import com.kunzisoft.keepass.credentialprovider.SpecialMode import com.kunzisoft.keepass.credentialprovider.SpecialMode
import com.kunzisoft.keepass.credentialprovider.TypeMode import com.kunzisoft.keepass.credentialprovider.TypeMode
import com.kunzisoft.keepass.credentialprovider.UserVerificationData
import com.kunzisoft.keepass.credentialprovider.UserVerificationHelper.Companion.addUserVerification import com.kunzisoft.keepass.credentialprovider.UserVerificationHelper.Companion.addUserVerification
import com.kunzisoft.keepass.credentialprovider.UserVerificationHelper.Companion.askUserVerification import com.kunzisoft.keepass.credentialprovider.UserVerificationHelper.Companion.askUserVerification
import com.kunzisoft.keepass.credentialprovider.UserVerificationHelper.Companion.getUserVerificationCondition import com.kunzisoft.keepass.credentialprovider.UserVerificationHelper.Companion.getUserVerificationCondition
@@ -186,6 +189,7 @@ class PasskeyLauncherActivity : DatabaseLockActivity() {
} }
} }
lifecycleScope.launch { lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.RESUMED) {
userVerificationViewModel.uiState.collect { uiState -> userVerificationViewModel.uiState.collect { uiState ->
when (uiState) { when (uiState) {
is UserVerificationViewModel.UIState.Loading -> {} is UserVerificationViewModel.UIState.Loading -> {}
@@ -194,11 +198,14 @@ class PasskeyLauncherActivity : DatabaseLockActivity() {
userVerified = true, userVerified = true,
intent = intent, intent = intent,
specialMode = mSpecialMode, specialMode = mSpecialMode,
database = uiState.database database = uiState.dataToVerify.database
) )
userVerificationViewModel.onUserVerificationReceived()
} }
is UserVerificationViewModel.UIState.OnUserVerificationCanceled -> { is UserVerificationViewModel.UIState.OnUserVerificationCanceled -> {
passkeyLauncherViewModel.cancelResult() passkeyLauncherViewModel.cancelResult()
userVerificationViewModel.onUserVerificationReceived()
}
} }
} }
} }
@@ -208,9 +215,11 @@ class PasskeyLauncherActivity : DatabaseLockActivity() {
override fun onUnknownDatabaseRetrieved(database: ContextualDatabase?) { override fun onUnknownDatabaseRetrieved(database: ContextualDatabase?) {
super.onUnknownDatabaseRetrieved(database) super.onUnknownDatabaseRetrieved(database)
// To manage https://github.com/Kunzisoft/KeePassDX/issues/2283 // To manage https://github.com/Kunzisoft/KeePassDX/issues/2283
// When a database is opened
askUserVerification( askUserVerification(
database = database, userVerificationViewModel = userVerificationViewModel,
userVerificationViewModel = userVerificationViewModel userVerificationCondition = intent.getUserVerificationCondition(),
dataToVerify = UserVerificationData(database)
) )
} }
@@ -227,9 +236,13 @@ class PasskeyLauncherActivity : DatabaseLockActivity() {
} }
ACTION_DATABASE_CHECK_CREDENTIAL_TASK -> { ACTION_DATABASE_CHECK_CREDENTIAL_TASK -> {
if (result.isSuccess) { if (result.isSuccess) {
userVerificationViewModel.onUserVerificationSucceeded(database) userVerificationViewModel.onUserVerificationSucceeded(
UserVerificationData(database)
)
} else { } else {
userVerificationViewModel.onUserVerificationFailed(database) userVerificationViewModel.onUserVerificationFailed(
UserVerificationData(database)
)
} }
} }
} }

View File

@@ -36,8 +36,14 @@ import java.util.UUID
class EntryViewModel: ViewModel() { class EntryViewModel: ViewModel() {
private var mMainEntryId: NodeId<UUID>? = null var mainEntryId: NodeId<UUID>? = null
private var mHistoryPosition: Int = -1 private set
var historyPosition: Int = -1
private set
var entryIsHistory: Boolean = false
private set
var entryLoaded = false
private set
val entryInfoHistory : LiveData<EntryInfoHistory?> get() = _entryInfoHistory val entryInfoHistory : LiveData<EntryInfoHistory?> get() = _entryInfoHistory
private val _entryInfoHistory = MutableLiveData<EntryInfoHistory?>() private val _entryInfoHistory = MutableLiveData<EntryInfoHistory?>()
@@ -60,12 +66,12 @@ class EntryViewModel: ViewModel() {
private val _historySelected = SingleLiveEvent<EntryHistory>() private val _historySelected = SingleLiveEvent<EntryHistory>()
fun loadDatabase(database: ContextualDatabase?) { fun loadDatabase(database: ContextualDatabase?) {
loadEntry(database, mMainEntryId, mHistoryPosition) loadEntry(database, mainEntryId, historyPosition)
} }
fun loadEntry(database: ContextualDatabase?, mainEntryId: NodeId<UUID>?, historyPosition: Int = -1) { fun loadEntry(database: ContextualDatabase?, mainEntryId: NodeId<UUID>?, historyPosition: Int = -1) {
this.mMainEntryId = mainEntryId this.mainEntryId = mainEntryId
this.mHistoryPosition = historyPosition this.historyPosition = historyPosition
if (database != null && mainEntryId != null) { if (database != null && mainEntryId != null) {
IOActionTask( IOActionTask(
@@ -104,6 +110,12 @@ class EntryViewModel: ViewModel() {
} }
}, },
{ entryInfoHistory -> { entryInfoHistory ->
if (entryInfoHistory != null) {
this.mainEntryId = entryInfoHistory.mainEntryId
this.historyPosition = historyPosition
this.entryIsHistory = historyPosition > -1
this.entryLoaded = true
}
_entryInfoHistory.value = entryInfoHistory _entryInfoHistory.value = entryInfoHistory
_entryHistory.value = entryInfoHistory?.entryHistory _entryHistory.value = entryInfoHistory?.entryHistory
} }

View File

@@ -1,7 +1,7 @@
package com.kunzisoft.keepass.viewmodels package com.kunzisoft.keepass.viewmodels
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import com.kunzisoft.keepass.database.ContextualDatabase import com.kunzisoft.keepass.credentialprovider.UserVerificationData
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
@@ -13,22 +13,22 @@ class UserVerificationViewModel: ViewModel() {
private val mUiState = MutableStateFlow<UIState>(UIState.Loading) private val mUiState = MutableStateFlow<UIState>(UIState.Loading)
val uiState: StateFlow<UIState> = mUiState val uiState: StateFlow<UIState> = mUiState
fun onUserVerificationSucceeded(database: ContextualDatabase?) { fun onUserVerificationSucceeded(dataToVerify: UserVerificationData) {
mUiState.value = UIState.OnUserVerificationSucceeded(database) mUiState.value = UIState.OnUserVerificationSucceeded(dataToVerify)
} }
fun onUserVerificationFailed(database: ContextualDatabase? = null) { fun onUserVerificationFailed(dataToVerify: UserVerificationData = UserVerificationData()) {
mUiState.value = UIState.OnUserVerificationCanceled(database) mUiState.value = UIState.OnUserVerificationCanceled(dataToVerify)
}
fun onUserVerificationReceived() {
mUiState.value = UIState.Loading
} }
sealed class UIState { sealed class UIState {
object Loading: UIState() object Loading: UIState()
data class OnUserVerificationSucceeded( data class OnUserVerificationSucceeded(val dataToVerify: UserVerificationData): UIState()
val database: ContextualDatabase? data class OnUserVerificationCanceled(val dataToVerify: UserVerificationData): UIState()
): UIState()
data class OnUserVerificationCanceled(
val database: ContextualDatabase?
): UIState()
} }
} }