mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
fix: Copy protected field behind UV #2283
This commit is contained in:
@@ -56,10 +56,11 @@ 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.UserVerificationActionType
|
||||||
import com.kunzisoft.keepass.credentialprovider.UserVerificationData
|
import com.kunzisoft.keepass.credentialprovider.UserVerificationData
|
||||||
import com.kunzisoft.keepass.credentialprovider.UserVerificationHelper.Companion.checkUserVerification
|
import com.kunzisoft.keepass.credentialprovider.UserVerificationHelper.Companion.checkUserVerification
|
||||||
import com.kunzisoft.keepass.credentialprovider.UserVerificationHelper.Companion.isUserVerificationNeeded
|
import com.kunzisoft.keepass.credentialprovider.UserVerificationHelper.Companion.isUserVerificationNeeded
|
||||||
import com.kunzisoft.keepass.credentialprovider.UserVerificationHelper.Companion.requestUnprotectField
|
import com.kunzisoft.keepass.credentialprovider.UserVerificationHelper.Companion.requestShowUnprotectField
|
||||||
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
|
||||||
@@ -330,11 +331,26 @@ class EntryActivity : DatabaseLockActivity() {
|
|||||||
when (entryState) {
|
when (entryState) {
|
||||||
is EntryViewModel.EntryState.Loading -> {}
|
is EntryViewModel.EntryState.Loading -> {}
|
||||||
is EntryViewModel.EntryState.RequestUnprotectField -> {
|
is EntryViewModel.EntryState.RequestUnprotectField -> {
|
||||||
requestUnprotectField(
|
mDatabase?.let { database ->
|
||||||
userVerificationViewModel = mUserVerificationViewModel,
|
requestShowUnprotectField(
|
||||||
database = mDatabase,
|
userVerificationViewModel = mUserVerificationViewModel,
|
||||||
protectedFieldView = entryState.protectedFieldView
|
database = database,
|
||||||
)
|
protectedFieldView = entryState.protectedFieldView
|
||||||
|
)
|
||||||
|
}
|
||||||
|
mEntryViewModel.actionPerformed()
|
||||||
|
}
|
||||||
|
is EntryViewModel.EntryState.RequestCopyProtectedField -> {
|
||||||
|
mDatabase?.let { database ->
|
||||||
|
checkUserVerification(
|
||||||
|
userVerificationViewModel = mUserVerificationViewModel,
|
||||||
|
dataToVerify = UserVerificationData(
|
||||||
|
actionType = UserVerificationActionType.COPY_PROTECTED_FIELD,
|
||||||
|
database = database,
|
||||||
|
field = entryState.field,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
mEntryViewModel.actionPerformed()
|
mEntryViewModel.actionPerformed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -351,10 +367,24 @@ class EntryActivity : DatabaseLockActivity() {
|
|||||||
mUserVerificationViewModel.onUserVerificationReceived()
|
mUserVerificationViewModel.onUserVerificationReceived()
|
||||||
}
|
}
|
||||||
is UserVerificationViewModel.UIState.OnUserVerificationSucceeded -> {
|
is UserVerificationViewModel.UIState.OnUserVerificationSucceeded -> {
|
||||||
// Edit Entry if corresponding data
|
val data = uVState.dataToVerify
|
||||||
editEntry(uVState.dataToVerify.database, uVState.dataToVerify.entryId)
|
when (data.actionType) {
|
||||||
// Unprotect field if corresponding data
|
UserVerificationActionType.SHOW_PROTECTED_FIELD -> {
|
||||||
uVState.dataToVerify.protectedFieldView?.unprotect()
|
// Unprotect field by its view
|
||||||
|
data.protectedFieldView?.unprotect()
|
||||||
|
}
|
||||||
|
UserVerificationActionType.COPY_PROTECTED_FIELD -> {
|
||||||
|
// Copy field value
|
||||||
|
data.field?.let {
|
||||||
|
mEntryViewModel.copyToClipboard(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UserVerificationActionType.EDIT_ENTRY -> {
|
||||||
|
// Edit Entry
|
||||||
|
editEntry(data.database, data.entryId)
|
||||||
|
}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
mUserVerificationViewModel.onUserVerificationReceived()
|
mUserVerificationViewModel.onUserVerificationReceived()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -531,10 +561,16 @@ class EntryActivity : DatabaseLockActivity() {
|
|||||||
when (item.itemId) {
|
when (item.itemId) {
|
||||||
R.id.menu_edit -> {
|
R.id.menu_edit -> {
|
||||||
if (mEntryViewModel.entryInfo?.isUserVerificationNeeded() == true) {
|
if (mEntryViewModel.entryInfo?.isUserVerificationNeeded() == true) {
|
||||||
checkUserVerification(
|
mDatabase?.let { database ->
|
||||||
userVerificationViewModel = mUserVerificationViewModel,
|
checkUserVerification(
|
||||||
dataToVerify = UserVerificationData(mDatabase, mEntryViewModel.mainEntryId)
|
userVerificationViewModel = mUserVerificationViewModel,
|
||||||
)
|
dataToVerify = UserVerificationData(
|
||||||
|
actionType = UserVerificationActionType.EDIT_ENTRY,
|
||||||
|
database = database,
|
||||||
|
entryId = mEntryViewModel.mainEntryId
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
editEntry(mDatabase, mEntryViewModel.mainEntryId)
|
editEntry(mDatabase, mEntryViewModel.mainEntryId)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,7 +63,8 @@ import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.buildSpecia
|
|||||||
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.retrieveRegisterInfo
|
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.retrieveRegisterInfo
|
||||||
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.retrieveSearchInfo
|
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.retrieveSearchInfo
|
||||||
import com.kunzisoft.keepass.credentialprovider.TypeMode
|
import com.kunzisoft.keepass.credentialprovider.TypeMode
|
||||||
import com.kunzisoft.keepass.credentialprovider.UserVerificationHelper.Companion.requestUnprotectField
|
import com.kunzisoft.keepass.credentialprovider.UserVerificationActionType
|
||||||
|
import com.kunzisoft.keepass.credentialprovider.UserVerificationHelper.Companion.requestShowUnprotectField
|
||||||
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
|
||||||
import com.kunzisoft.keepass.database.element.Attachment
|
import com.kunzisoft.keepass.database.element.Attachment
|
||||||
@@ -403,11 +404,13 @@ class EntryEditActivity : DatabaseLockActivity(),
|
|||||||
mEntryEditViewModel.actionPerformed()
|
mEntryEditViewModel.actionPerformed()
|
||||||
}
|
}
|
||||||
is EntryEditViewModel.EntryEditState.RequestUnprotectField -> {
|
is EntryEditViewModel.EntryEditState.RequestUnprotectField -> {
|
||||||
requestUnprotectField(
|
mDatabase?.let { database ->
|
||||||
userVerificationViewModel = mUserVerificationViewModel,
|
requestShowUnprotectField(
|
||||||
database = mDatabase,
|
userVerificationViewModel = mUserVerificationViewModel,
|
||||||
protectedFieldView = uiState.protectedFieldView
|
database = database,
|
||||||
)
|
protectedFieldView = uiState.protectedFieldView
|
||||||
|
)
|
||||||
|
}
|
||||||
mEntryEditViewModel.actionPerformed()
|
mEntryEditViewModel.actionPerformed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -424,7 +427,12 @@ class EntryEditActivity : DatabaseLockActivity(),
|
|||||||
mUserVerificationViewModel.onUserVerificationReceived()
|
mUserVerificationViewModel.onUserVerificationReceived()
|
||||||
}
|
}
|
||||||
is UserVerificationViewModel.UIState.OnUserVerificationSucceeded -> {
|
is UserVerificationViewModel.UIState.OnUserVerificationSucceeded -> {
|
||||||
uVState.dataToVerify.protectedFieldView?.unprotect()
|
when (uVState.dataToVerify.actionType) {
|
||||||
|
UserVerificationActionType.SHOW_PROTECTED_FIELD -> {
|
||||||
|
uVState.dataToVerify.protectedFieldView?.unprotect()
|
||||||
|
}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
mUserVerificationViewModel.onUserVerificationReceived()
|
mUserVerificationViewModel.onUserVerificationReceived()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ 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.UserVerificationActionType
|
||||||
import com.kunzisoft.keepass.credentialprovider.UserVerificationData
|
import com.kunzisoft.keepass.credentialprovider.UserVerificationData
|
||||||
import com.kunzisoft.keepass.credentialprovider.UserVerificationHelper.Companion.checkUserVerification
|
import com.kunzisoft.keepass.credentialprovider.UserVerificationHelper.Companion.checkUserVerification
|
||||||
import com.kunzisoft.keepass.credentialprovider.UserVerificationHelper.Companion.isUserVerificationNeeded
|
import com.kunzisoft.keepass.credentialprovider.UserVerificationHelper.Companion.isUserVerificationNeeded
|
||||||
@@ -586,7 +587,15 @@ class GroupActivity : DatabaseLockActivity(),
|
|||||||
mUserVerificationViewModel.onUserVerificationReceived()
|
mUserVerificationViewModel.onUserVerificationReceived()
|
||||||
}
|
}
|
||||||
is UserVerificationViewModel.UIState.OnUserVerificationSucceeded -> {
|
is UserVerificationViewModel.UIState.OnUserVerificationSucceeded -> {
|
||||||
editEntry(uVState.dataToVerify.database, uVState.dataToVerify.entryId)
|
val data = uVState.dataToVerify
|
||||||
|
when (data.actionType) {
|
||||||
|
UserVerificationActionType.EDIT_ENTRY -> {
|
||||||
|
editEntry(uVState.dataToVerify.database, uVState.dataToVerify.entryId)
|
||||||
|
}
|
||||||
|
UserVerificationActionType.MERGE_FROM_DATABASE -> {}
|
||||||
|
UserVerificationActionType.SAVE_TO_DATABASE -> {}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
mUserVerificationViewModel.onUserVerificationReceived()
|
mUserVerificationViewModel.onUserVerificationReceived()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1101,7 +1110,11 @@ class GroupActivity : DatabaseLockActivity(),
|
|||||||
if ((node as Entry).getEntryInfo(database).isUserVerificationNeeded()) {
|
if ((node as Entry).getEntryInfo(database).isUserVerificationNeeded()) {
|
||||||
checkUserVerification(
|
checkUserVerification(
|
||||||
userVerificationViewModel = mUserVerificationViewModel,
|
userVerificationViewModel = mUserVerificationViewModel,
|
||||||
dataToVerify = UserVerificationData(database, node.nodeId)
|
dataToVerify = UserVerificationData(
|
||||||
|
actionType = UserVerificationActionType.EDIT_ENTRY,
|
||||||
|
database = database,
|
||||||
|
entryId = node.nodeId
|
||||||
|
)
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
editEntry(database, node.nodeId)
|
editEntry(database, node.nodeId)
|
||||||
|
|||||||
@@ -16,13 +16,10 @@ import com.kunzisoft.keepass.R
|
|||||||
import com.kunzisoft.keepass.adapters.EntryAttachmentsItemsAdapter
|
import com.kunzisoft.keepass.adapters.EntryAttachmentsItemsAdapter
|
||||||
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
|
||||||
import com.kunzisoft.keepass.database.element.template.TemplateField
|
|
||||||
import com.kunzisoft.keepass.database.helper.getLocalizedName
|
|
||||||
import com.kunzisoft.keepass.model.EntryAttachmentState
|
import com.kunzisoft.keepass.model.EntryAttachmentState
|
||||||
import com.kunzisoft.keepass.model.EntryInfo
|
import com.kunzisoft.keepass.model.EntryInfo
|
||||||
import com.kunzisoft.keepass.model.StreamDirection
|
import com.kunzisoft.keepass.model.StreamDirection
|
||||||
import com.kunzisoft.keepass.settings.PreferencesUtil
|
import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||||
import com.kunzisoft.keepass.timeout.ClipboardHelper
|
|
||||||
import com.kunzisoft.keepass.utils.TimeUtil.getDateTimeString
|
import com.kunzisoft.keepass.utils.TimeUtil.getDateTimeString
|
||||||
import com.kunzisoft.keepass.utils.UUIDUtils.asHexString
|
import com.kunzisoft.keepass.utils.UUIDUtils.asHexString
|
||||||
import com.kunzisoft.keepass.view.TemplateView
|
import com.kunzisoft.keepass.view.TemplateView
|
||||||
@@ -50,8 +47,6 @@ class EntryFragment: DatabaseFragment() {
|
|||||||
private lateinit var uuidContainerView: View
|
private lateinit var uuidContainerView: View
|
||||||
private lateinit var uuidReferenceView: TextView
|
private lateinit var uuidReferenceView: TextView
|
||||||
|
|
||||||
private var mClipboardHelper: ClipboardHelper? = null
|
|
||||||
|
|
||||||
private val mEntryViewModel: EntryViewModel by activityViewModels()
|
private val mEntryViewModel: EntryViewModel by activityViewModels()
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater,
|
override fun onCreateView(inflater: LayoutInflater,
|
||||||
@@ -66,10 +61,6 @@ class EntryFragment: DatabaseFragment() {
|
|||||||
savedInstanceState: Bundle?) {
|
savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
context?.let { context ->
|
|
||||||
mClipboardHelper = ClipboardHelper(context)
|
|
||||||
}
|
|
||||||
|
|
||||||
rootView = view
|
rootView = view
|
||||||
// Hide only the first time
|
// Hide only the first time
|
||||||
if (savedInstanceState == null) {
|
if (savedInstanceState == null) {
|
||||||
@@ -152,18 +143,14 @@ class EntryFragment: DatabaseFragment() {
|
|||||||
private fun assignEntryInfo(entryInfo: EntryInfo?) {
|
private fun assignEntryInfo(entryInfo: EntryInfo?) {
|
||||||
// Set copy buttons
|
// Set copy buttons
|
||||||
templateView.apply {
|
templateView.apply {
|
||||||
setOnUnprotectClickListener { _, textEditFieldView ->
|
setOnUnprotectClickListener { protectedFieldView ->
|
||||||
mEntryViewModel.requestUnprotectField(textEditFieldView)
|
mEntryViewModel.requestUnprotectField(protectedFieldView)
|
||||||
}
|
}
|
||||||
setOnAskCopySafeClickListener {
|
setOnAskCopySafeClickListener {
|
||||||
showClipboardDialog()
|
showClipboardDialog()
|
||||||
}
|
}
|
||||||
setOnCopyActionClickListener { field ->
|
setOnCopyActionClickListener { field, protectedFieldView ->
|
||||||
mClipboardHelper?.timeoutCopyToClipboard(
|
mEntryViewModel.requestCopyField(field, protectedFieldView)
|
||||||
TemplateField.getLocalizedName(context, field.name),
|
|
||||||
field.protectedValue.stringValue,
|
|
||||||
field.protectedValue.isProtected
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,7 +238,7 @@ class EntryFragment: DatabaseFragment() {
|
|||||||
|
|
||||||
fun launchEntryCopyEducationAction() {
|
fun launchEntryCopyEducationAction() {
|
||||||
val appNameString = getString(R.string.app_name)
|
val appNameString = getString(R.string.app_name)
|
||||||
mClipboardHelper?.timeoutCopyToClipboard(appNameString, appNameString)
|
mEntryViewModel.copyToClipboard(appNameString)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
@@ -1,12 +1,25 @@
|
|||||||
package com.kunzisoft.keepass.credentialprovider
|
package com.kunzisoft.keepass.credentialprovider
|
||||||
|
|
||||||
import com.kunzisoft.keepass.database.ContextualDatabase
|
import com.kunzisoft.keepass.database.ContextualDatabase
|
||||||
|
import com.kunzisoft.keepass.database.element.Field
|
||||||
import com.kunzisoft.keepass.database.element.node.NodeId
|
import com.kunzisoft.keepass.database.element.node.NodeId
|
||||||
import com.kunzisoft.keepass.view.ProtectedFieldView
|
import com.kunzisoft.keepass.view.ProtectedFieldView
|
||||||
|
|
||||||
data class UserVerificationData(
|
data class UserVerificationData(
|
||||||
|
val actionType: UserVerificationActionType,
|
||||||
val database: ContextualDatabase? = null,
|
val database: ContextualDatabase? = null,
|
||||||
val entryId: NodeId<*>? = null,
|
val entryId: NodeId<*>? = null,
|
||||||
|
val field: Field? = null,
|
||||||
val protectedFieldView: ProtectedFieldView? = null,
|
val protectedFieldView: ProtectedFieldView? = null,
|
||||||
val preferenceKey: String? = null
|
val preferenceKey: String? = null
|
||||||
)
|
)
|
||||||
|
|
||||||
|
enum class UserVerificationActionType {
|
||||||
|
LAUNCH_PASSKEY_CEREMONY,
|
||||||
|
SHOW_PROTECTED_FIELD,
|
||||||
|
COPY_PROTECTED_FIELD,
|
||||||
|
EDIT_ENTRY,
|
||||||
|
EDIT_DATABASE_SETTING,
|
||||||
|
MERGE_FROM_DATABASE,
|
||||||
|
SAVE_TO_DATABASE
|
||||||
|
}
|
||||||
@@ -104,15 +104,16 @@ class UserVerificationHelper {
|
|||||||
activity?.checkUserVerification(userVerificationViewModel, dataToVerify)
|
activity?.checkUserVerification(userVerificationViewModel, dataToVerify)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun FragmentActivity.requestUnprotectField(
|
fun FragmentActivity.requestShowUnprotectField(
|
||||||
userVerificationViewModel: UserVerificationViewModel,
|
userVerificationViewModel: UserVerificationViewModel,
|
||||||
database: ContextualDatabase?,
|
database: ContextualDatabase,
|
||||||
protectedFieldView: ProtectedFieldView
|
protectedFieldView: ProtectedFieldView
|
||||||
) {
|
) {
|
||||||
if (protectedFieldView.isCurrentlyProtected()) {
|
if (protectedFieldView.isCurrentlyProtected()) {
|
||||||
checkUserVerification(
|
checkUserVerification(
|
||||||
userVerificationViewModel = userVerificationViewModel,
|
userVerificationViewModel = userVerificationViewModel,
|
||||||
dataToVerify = UserVerificationData(
|
dataToVerify = UserVerificationData(
|
||||||
|
actionType = UserVerificationActionType.SHOW_PROTECTED_FIELD,
|
||||||
database = database,
|
database = database,
|
||||||
protectedFieldView = protectedFieldView
|
protectedFieldView = protectedFieldView
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -45,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.UserVerificationActionType
|
||||||
import com.kunzisoft.keepass.credentialprovider.UserVerificationData
|
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.checkUserVerification
|
import com.kunzisoft.keepass.credentialprovider.UserVerificationHelper.Companion.checkUserVerification
|
||||||
@@ -179,12 +180,18 @@ class PasskeyLauncherActivity : DatabaseLockActivity() {
|
|||||||
when (uiState) {
|
when (uiState) {
|
||||||
is UserVerificationViewModel.UIState.Loading -> {}
|
is UserVerificationViewModel.UIState.Loading -> {}
|
||||||
is UserVerificationViewModel.UIState.OnUserVerificationSucceeded -> {
|
is UserVerificationViewModel.UIState.OnUserVerificationSucceeded -> {
|
||||||
passkeyLauncherViewModel.launchActionIfNeeded(
|
val data = uiState.dataToVerify
|
||||||
userVerified = true,
|
when (data.actionType) {
|
||||||
intent = intent,
|
UserVerificationActionType.LAUNCH_PASSKEY_CEREMONY -> {
|
||||||
specialMode = mSpecialMode,
|
passkeyLauncherViewModel.launchActionIfNeeded(
|
||||||
database = uiState.dataToVerify.database
|
userVerified = true,
|
||||||
)
|
intent = intent,
|
||||||
|
specialMode = mSpecialMode,
|
||||||
|
database = uiState.dataToVerify.database
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
userVerificationViewModel.onUserVerificationReceived()
|
userVerificationViewModel.onUserVerificationReceived()
|
||||||
}
|
}
|
||||||
is UserVerificationViewModel.UIState.OnUserVerificationCanceled -> {
|
is UserVerificationViewModel.UIState.OnUserVerificationCanceled -> {
|
||||||
@@ -207,7 +214,10 @@ class PasskeyLauncherActivity : DatabaseLockActivity() {
|
|||||||
if (userVerificationNeeded) {
|
if (userVerificationNeeded) {
|
||||||
checkUserVerification(
|
checkUserVerification(
|
||||||
userVerificationViewModel = userVerificationViewModel,
|
userVerificationViewModel = userVerificationViewModel,
|
||||||
dataToVerify = UserVerificationData(database)
|
dataToVerify = UserVerificationData(
|
||||||
|
actionType = UserVerificationActionType.LAUNCH_PASSKEY_CEREMONY,
|
||||||
|
database = database
|
||||||
|
)
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
passkeyLauncherViewModel.launchActionIfNeeded(
|
passkeyLauncherViewModel.launchActionIfNeeded(
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ import com.kunzisoft.keepass.R
|
|||||||
import com.kunzisoft.keepass.activities.dialogs.SetMainCredentialDialogFragment
|
import com.kunzisoft.keepass.activities.dialogs.SetMainCredentialDialogFragment
|
||||||
import com.kunzisoft.keepass.activities.legacy.DatabaseRetrieval
|
import com.kunzisoft.keepass.activities.legacy.DatabaseRetrieval
|
||||||
import com.kunzisoft.keepass.activities.legacy.resetAppTimeoutWhenViewTouchedOrFocused
|
import com.kunzisoft.keepass.activities.legacy.resetAppTimeoutWhenViewTouchedOrFocused
|
||||||
|
import com.kunzisoft.keepass.credentialprovider.UserVerificationActionType
|
||||||
import com.kunzisoft.keepass.credentialprovider.UserVerificationData
|
import com.kunzisoft.keepass.credentialprovider.UserVerificationData
|
||||||
import com.kunzisoft.keepass.credentialprovider.UserVerificationHelper.Companion.checkUserVerification
|
import com.kunzisoft.keepass.credentialprovider.UserVerificationHelper.Companion.checkUserVerification
|
||||||
import com.kunzisoft.keepass.database.ContextualDatabase
|
import com.kunzisoft.keepass.database.ContextualDatabase
|
||||||
@@ -188,28 +189,34 @@ class NestedDatabaseSettingsFragment : NestedSettingsFragment(), DatabaseRetriev
|
|||||||
mUserVerificationViewModel.onUserVerificationReceived()
|
mUserVerificationViewModel.onUserVerificationReceived()
|
||||||
}
|
}
|
||||||
is UserVerificationViewModel.UIState.OnUserVerificationSucceeded -> {
|
is UserVerificationViewModel.UIState.OnUserVerificationSucceeded -> {
|
||||||
state.dataToVerify.database?.let { database ->
|
val data = state.dataToVerify
|
||||||
state.dataToVerify.preferenceKey?.let { preferenceKey ->
|
when (data.actionType) {
|
||||||
|
UserVerificationActionType.EDIT_DATABASE_SETTING -> {
|
||||||
|
val database = data.database
|
||||||
|
val preferenceKey = data.preferenceKey
|
||||||
|
if (database != null && preferenceKey != null) {
|
||||||
// Main Preferences
|
// Main Preferences
|
||||||
when (preferenceKey) {
|
when (preferenceKey) {
|
||||||
// Master Key
|
// Master Key
|
||||||
getString(R.string.settings_database_change_credentials_key) -> {
|
getString(R.string.settings_database_change_credentials_key) -> {
|
||||||
SetMainCredentialDialogFragment
|
SetMainCredentialDialogFragment
|
||||||
.getInstance(database.allowNoMasterKey)
|
.getInstance(database.allowNoMasterKey)
|
||||||
.show(parentFragmentManager, "passwordDialog")
|
.show(parentFragmentManager, "passwordDialog")
|
||||||
|
}
|
||||||
|
else -> {}
|
||||||
}
|
}
|
||||||
else -> {}
|
// TODO Settings in compose
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
mSettingsViewModel.dialogFragment?.let { dialogFragment ->
|
||||||
|
dialogFragment.setTargetFragment(
|
||||||
|
this@NestedDatabaseSettingsFragment, 0
|
||||||
|
)
|
||||||
|
dialogFragment.show(parentFragmentManager, TAG_PREF_FRAGMENT)
|
||||||
|
}
|
||||||
|
mSettingsViewModel.dialogFragment = null
|
||||||
}
|
}
|
||||||
// TODO Settings in compose
|
|
||||||
@Suppress("DEPRECATION")
|
|
||||||
mSettingsViewModel.dialogFragment?.let { dialogFragment ->
|
|
||||||
dialogFragment.setTargetFragment(
|
|
||||||
this@NestedDatabaseSettingsFragment, 0
|
|
||||||
)
|
|
||||||
dialogFragment.show(parentFragmentManager, TAG_PREF_FRAGMENT)
|
|
||||||
}
|
|
||||||
mSettingsViewModel.dialogFragment = null
|
|
||||||
}
|
}
|
||||||
|
else -> {}
|
||||||
}
|
}
|
||||||
mUserVerificationViewModel.onUserVerificationReceived()
|
mUserVerificationViewModel.onUserVerificationReceived()
|
||||||
}
|
}
|
||||||
@@ -483,6 +490,7 @@ class NestedDatabaseSettingsFragment : NestedSettingsFragment(), DatabaseRetriev
|
|||||||
checkUserVerification(
|
checkUserVerification(
|
||||||
mUserVerificationViewModel,
|
mUserVerificationViewModel,
|
||||||
UserVerificationData(
|
UserVerificationData(
|
||||||
|
actionType = UserVerificationActionType.EDIT_DATABASE_SETTING,
|
||||||
database = database,
|
database = database,
|
||||||
preferenceKey = changeCredentialKey
|
preferenceKey = changeCredentialKey
|
||||||
)
|
)
|
||||||
@@ -513,7 +521,7 @@ class NestedDatabaseSettingsFragment : NestedSettingsFragment(), DatabaseRetriev
|
|||||||
// To reassign color listener after orientation change
|
// To reassign color listener after orientation change
|
||||||
val chromaDialog = parentFragmentManager.findFragmentByTag(TAG_PREF_FRAGMENT) as DatabaseColorPreferenceDialogFragmentCompat?
|
val chromaDialog = parentFragmentManager.findFragmentByTag(TAG_PREF_FRAGMENT) as DatabaseColorPreferenceDialogFragmentCompat?
|
||||||
chromaDialog?.onColorSelectedListener = colorSelectedListener
|
chromaDialog?.onColorSelectedListener = colorSelectedListener
|
||||||
} catch (e: Exception) {}
|
} catch (_: Exception) {}
|
||||||
|
|
||||||
return view
|
return view
|
||||||
}
|
}
|
||||||
@@ -785,6 +793,7 @@ class NestedDatabaseSettingsFragment : NestedSettingsFragment(), DatabaseRetriev
|
|||||||
checkUserVerification(
|
checkUserVerification(
|
||||||
mUserVerificationViewModel,
|
mUserVerificationViewModel,
|
||||||
UserVerificationData(
|
UserVerificationData(
|
||||||
|
actionType = UserVerificationActionType.EDIT_DATABASE_SETTING,
|
||||||
database = mDatabase,
|
database = mDatabase,
|
||||||
preferenceKey = preference.key
|
preferenceKey = preference.key
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ class TemplateView @JvmOverloads constructor(context: Context,
|
|||||||
: TemplateAbstractView<TextFieldView, TextFieldView, DateTimeFieldView>
|
: TemplateAbstractView<TextFieldView, TextFieldView, DateTimeFieldView>
|
||||||
(context, attrs, defStyle) {
|
(context, attrs, defStyle) {
|
||||||
|
|
||||||
private var mOnUnprotectClickListener: ((Field, ProtectedFieldView) -> Unit)? = null
|
private var mOnUnprotectClickListener: ((ProtectedFieldView) -> Unit)? = null
|
||||||
fun setOnUnprotectClickListener(listener: ((Field, ProtectedFieldView) -> Unit)?) {
|
fun setOnUnprotectClickListener(listener: ((ProtectedFieldView) -> Unit)?) {
|
||||||
this.mOnUnprotectClickListener = listener
|
this.mOnUnprotectClickListener = listener
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,8 +34,8 @@ class TemplateView @JvmOverloads constructor(context: Context,
|
|||||||
fun setOnAskCopySafeClickListener(listener: (() -> Unit)? = null) {
|
fun setOnAskCopySafeClickListener(listener: (() -> Unit)? = null) {
|
||||||
this.mOnAskCopySafeClickListener = listener
|
this.mOnAskCopySafeClickListener = listener
|
||||||
}
|
}
|
||||||
private var mOnCopyActionClickListener: ((Field) -> Unit)? = null
|
private var mOnCopyActionClickListener: ((Field, ProtectedFieldView) -> Unit)? = null
|
||||||
fun setOnCopyActionClickListener(listener: ((Field) -> Unit)? = null) {
|
fun setOnCopyActionClickListener(listener: ((Field, ProtectedFieldView) -> Unit)? = null) {
|
||||||
this.mOnCopyActionClickListener = listener
|
this.mOnCopyActionClickListener = listener
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,7 +64,7 @@ class TemplateView @JvmOverloads constructor(context: Context,
|
|||||||
else TextFieldView(it)).apply {
|
else TextFieldView(it)).apply {
|
||||||
applyFontVisibility(mFontInVisibility)
|
applyFontVisibility(mFontInVisibility)
|
||||||
setProtection(field.protectedValue.isProtected) {
|
setProtection(field.protectedValue.isProtected) {
|
||||||
mOnUnprotectClickListener?.invoke(field, this)
|
mOnUnprotectClickListener?.invoke(this)
|
||||||
}
|
}
|
||||||
label = templateAttribute.alias
|
label = templateAttribute.alias
|
||||||
?: TemplateField.getLocalizedName(context, field.name)
|
?: TemplateField.getLocalizedName(context, field.name)
|
||||||
@@ -85,7 +85,15 @@ class TemplateView @JvmOverloads constructor(context: Context,
|
|||||||
setCopyButtonState(TextFieldView.ButtonState.ACTIVATE)
|
setCopyButtonState(TextFieldView.ButtonState.ACTIVATE)
|
||||||
setCopyButtonClickListener { label, value ->
|
setCopyButtonClickListener { label, value ->
|
||||||
mOnCopyActionClickListener
|
mOnCopyActionClickListener
|
||||||
?.invoke(Field(label, ProtectedString(true, value)))
|
?.invoke(
|
||||||
|
Field(
|
||||||
|
name = label,
|
||||||
|
value = ProtectedString(
|
||||||
|
enableProtection = true,
|
||||||
|
string = value
|
||||||
|
)
|
||||||
|
), this
|
||||||
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setCopyButtonState(TextFieldView.ButtonState.GONE)
|
setCopyButtonState(TextFieldView.ButtonState.GONE)
|
||||||
@@ -96,7 +104,15 @@ class TemplateView @JvmOverloads constructor(context: Context,
|
|||||||
setCopyButtonState(TextFieldView.ButtonState.ACTIVATE)
|
setCopyButtonState(TextFieldView.ButtonState.ACTIVATE)
|
||||||
setCopyButtonClickListener { label, value ->
|
setCopyButtonClickListener { label, value ->
|
||||||
mOnCopyActionClickListener
|
mOnCopyActionClickListener
|
||||||
?.invoke(Field(label, ProtectedString(false, value)))
|
?.invoke(
|
||||||
|
Field(
|
||||||
|
name = label,
|
||||||
|
value = ProtectedString(
|
||||||
|
enableProtection = false,
|
||||||
|
string = value
|
||||||
|
)
|
||||||
|
), this
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -184,9 +200,15 @@ class TemplateView @JvmOverloads constructor(context: Context,
|
|||||||
value = otpElement.tokenString
|
value = otpElement.tokenString
|
||||||
setCopyButtonState(TextFieldView.ButtonState.ACTIVATE)
|
setCopyButtonState(TextFieldView.ButtonState.ACTIVATE)
|
||||||
setCopyButtonClickListener { _, _ ->
|
setCopyButtonClickListener { _, _ ->
|
||||||
mOnCopyActionClickListener?.invoke(Field(
|
mOnCopyActionClickListener?.invoke(
|
||||||
otpElement.type.name,
|
Field(
|
||||||
ProtectedString(false, otpElement.token)))
|
name = otpElement.type.name,
|
||||||
|
value = ProtectedString(
|
||||||
|
enableProtection = false,
|
||||||
|
string = otpElement.token
|
||||||
|
)
|
||||||
|
), this
|
||||||
|
)
|
||||||
}
|
}
|
||||||
textDirection = TEXT_DIRECTION_LTR
|
textDirection = TEXT_DIRECTION_LTR
|
||||||
mLastOtpTokenView = this
|
mLastOtpTokenView = this
|
||||||
|
|||||||
@@ -19,17 +19,22 @@
|
|||||||
*/
|
*/
|
||||||
package com.kunzisoft.keepass.viewmodels
|
package com.kunzisoft.keepass.viewmodels
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import androidx.lifecycle.AndroidViewModel
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel
|
|
||||||
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
|
||||||
|
import com.kunzisoft.keepass.database.element.Field
|
||||||
import com.kunzisoft.keepass.database.element.node.NodeId
|
import com.kunzisoft.keepass.database.element.node.NodeId
|
||||||
import com.kunzisoft.keepass.database.element.node.NodeIdUUID
|
import com.kunzisoft.keepass.database.element.node.NodeIdUUID
|
||||||
import com.kunzisoft.keepass.database.element.template.Template
|
import com.kunzisoft.keepass.database.element.template.Template
|
||||||
|
import com.kunzisoft.keepass.database.element.template.TemplateField
|
||||||
|
import com.kunzisoft.keepass.database.helper.getLocalizedName
|
||||||
import com.kunzisoft.keepass.model.EntryAttachmentState
|
import com.kunzisoft.keepass.model.EntryAttachmentState
|
||||||
import com.kunzisoft.keepass.model.EntryInfo
|
import com.kunzisoft.keepass.model.EntryInfo
|
||||||
import com.kunzisoft.keepass.otp.OtpElement
|
import com.kunzisoft.keepass.otp.OtpElement
|
||||||
|
import com.kunzisoft.keepass.timeout.ClipboardHelper
|
||||||
import com.kunzisoft.keepass.utils.IOActionTask
|
import com.kunzisoft.keepass.utils.IOActionTask
|
||||||
import com.kunzisoft.keepass.view.ProtectedFieldView
|
import com.kunzisoft.keepass.view.ProtectedFieldView
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
@@ -37,7 +42,7 @@ import kotlinx.coroutines.flow.StateFlow
|
|||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
|
|
||||||
class EntryViewModel: ViewModel() {
|
class EntryViewModel(application: Application): AndroidViewModel(application) {
|
||||||
|
|
||||||
var mainEntryId: NodeId<UUID>? = null
|
var mainEntryId: NodeId<UUID>? = null
|
||||||
private set
|
private set
|
||||||
@@ -50,6 +55,8 @@ class EntryViewModel: ViewModel() {
|
|||||||
var entryLoaded = false
|
var entryLoaded = false
|
||||||
private set
|
private set
|
||||||
|
|
||||||
|
private var mClipboardHelper: ClipboardHelper = ClipboardHelper(application)
|
||||||
|
|
||||||
val entryInfoHistory : LiveData<EntryInfoHistory?> get() = _entryInfoHistory
|
val entryInfoHistory : LiveData<EntryInfoHistory?> get() = _entryInfoHistory
|
||||||
private val _entryInfoHistory = MutableLiveData<EntryInfoHistory?>()
|
private val _entryInfoHistory = MutableLiveData<EntryInfoHistory?>()
|
||||||
|
|
||||||
@@ -136,6 +143,14 @@ class EntryViewModel: ViewModel() {
|
|||||||
mEntryState.value = EntryState.RequestUnprotectField(fieldView)
|
mEntryState.value = EntryState.RequestUnprotectField(fieldView)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun requestCopyField(field: Field, fieldView: ProtectedFieldView) {
|
||||||
|
// Only request the User Verification if the field is protected and not shown
|
||||||
|
if (field.protectedValue.isProtected && fieldView.isCurrentlyProtected())
|
||||||
|
mEntryState.value = EntryState.RequestCopyProtectedField(field)
|
||||||
|
else
|
||||||
|
copyToClipboard(field)
|
||||||
|
}
|
||||||
|
|
||||||
fun onOtpElementUpdated(optElement: OtpElement?) {
|
fun onOtpElementUpdated(optElement: OtpElement?) {
|
||||||
_onOtpElementUpdated.value = optElement
|
_onOtpElementUpdated.value = optElement
|
||||||
}
|
}
|
||||||
@@ -156,6 +171,18 @@ class EntryViewModel: ViewModel() {
|
|||||||
_sectionSelected.value = section
|
_sectionSelected.value = section
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun copyToClipboard(field: Field) {
|
||||||
|
mClipboardHelper.timeoutCopyToClipboard(
|
||||||
|
TemplateField.getLocalizedName(getApplication(), field.name),
|
||||||
|
field.protectedValue.stringValue,
|
||||||
|
field.protectedValue.isProtected
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun copyToClipboard(text: String) {
|
||||||
|
mClipboardHelper.timeoutCopyToClipboard(text, text)
|
||||||
|
}
|
||||||
|
|
||||||
fun actionPerformed() {
|
fun actionPerformed() {
|
||||||
mEntryState.value = EntryState.Loading
|
mEntryState.value = EntryState.Loading
|
||||||
}
|
}
|
||||||
@@ -186,6 +213,9 @@ class EntryViewModel: ViewModel() {
|
|||||||
data class RequestUnprotectField(
|
data class RequestUnprotectField(
|
||||||
val protectedFieldView: ProtectedFieldView
|
val protectedFieldView: ProtectedFieldView
|
||||||
): EntryState()
|
): EntryState()
|
||||||
|
data class RequestCopyProtectedField(
|
||||||
|
val field: Field
|
||||||
|
): EntryState()
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
@@ -15,16 +15,17 @@ class UserVerificationViewModel: ViewModel() {
|
|||||||
private val mUiState = MutableStateFlow<UIState>(UIState.Loading)
|
private val mUiState = MutableStateFlow<UIState>(UIState.Loading)
|
||||||
val userVerificationState: StateFlow<UIState> = mUiState
|
val userVerificationState: StateFlow<UIState> = mUiState
|
||||||
|
|
||||||
var dataToVerify: UserVerificationData = UserVerificationData()
|
var dataToVerify: UserVerificationData? = null
|
||||||
|
|
||||||
fun checkMainCredential(checkString: String) {
|
fun checkMainCredential(checkString: String) {
|
||||||
// Check the password part
|
// Check the password part
|
||||||
if (dataToVerify.database?.checkKey(getCheckKey(checkString)) == true)
|
val data = dataToVerify
|
||||||
onUserVerificationSucceeded(dataToVerify)
|
if (data?.database?.checkKey(getCheckKey(checkString)) == true)
|
||||||
|
onUserVerificationSucceeded(data)
|
||||||
else {
|
else {
|
||||||
onUserVerificationFailed(dataToVerify, InvalidCredentialsDatabaseException())
|
onUserVerificationFailed(dataToVerify, InvalidCredentialsDatabaseException())
|
||||||
}
|
}
|
||||||
dataToVerify = UserVerificationData()
|
dataToVerify = null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onUserVerificationSucceeded(dataToVerify: UserVerificationData) {
|
fun onUserVerificationSucceeded(dataToVerify: UserVerificationData) {
|
||||||
@@ -32,7 +33,7 @@ class UserVerificationViewModel: ViewModel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun onUserVerificationFailed(
|
fun onUserVerificationFailed(
|
||||||
dataToVerify: UserVerificationData = UserVerificationData(),
|
dataToVerify: UserVerificationData? = null,
|
||||||
error: Throwable? = null
|
error: Throwable? = null
|
||||||
) {
|
) {
|
||||||
this.dataToVerify = dataToVerify
|
this.dataToVerify = dataToVerify
|
||||||
@@ -45,9 +46,11 @@ class UserVerificationViewModel: ViewModel() {
|
|||||||
|
|
||||||
sealed class UIState {
|
sealed class UIState {
|
||||||
object Loading: UIState()
|
object Loading: UIState()
|
||||||
data class OnUserVerificationSucceeded(val dataToVerify: UserVerificationData): UIState()
|
data class OnUserVerificationSucceeded(
|
||||||
|
val dataToVerify: UserVerificationData
|
||||||
|
): UIState()
|
||||||
data class OnUserVerificationCanceled(
|
data class OnUserVerificationCanceled(
|
||||||
val dataToVerify: UserVerificationData,
|
val dataToVerify: UserVerificationData?,
|
||||||
val error: Throwable?
|
val error: Throwable?
|
||||||
): UIState()
|
): UIState()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user