diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.kt index ba9954f80..fd6bf10c8 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.kt @@ -323,6 +323,27 @@ class EntryActivity : DatabaseLockActivity() { } } + lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.RESUMED) { + mEntryViewModel.entryState.collect { entryState -> + when (entryState) { + is EntryViewModel.EntryState.Loading -> {} + is EntryViewModel.EntryState.RequestUnprotectField -> { + val fieldView = entryState.protectedFieldView + if (fieldView.isCurrentlyProtected()) { + checkUserVerification( + userVerificationViewModel = mUserVerificationViewModel, + dataToVerify = UserVerificationData(protectedFieldView = fieldView) + ) + } else { + fieldView.protect() + } + mEntryViewModel.actionPerformed() + } + } + } + } + } lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.RESUMED) { mUserVerificationViewModel.userVerificationState.collect { uVState -> @@ -333,7 +354,10 @@ class EntryActivity : DatabaseLockActivity() { mUserVerificationViewModel.onUserVerificationReceived() } is UserVerificationViewModel.UIState.OnUserVerificationSucceeded -> { + // Edit Entry if corresponding data editEntry(uVState.dataToVerify.database, uVState.dataToVerify.entryId) + // Unprotect field if corresponding data + uVState.dataToVerify.protectedFieldView?.unprotect() mUserVerificationViewModel.onUserVerificationReceived() } } diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt index c51619f47..ec41a8bf4 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt @@ -477,7 +477,7 @@ class EntryEditActivity : DatabaseLockActivity(), searchAction = { // Nothing when search retrieved }, - selectionAction = { intentSender, typeMode, searchInfo -> + selectionAction = { _, typeMode, _ -> when(typeMode) { TypeMode.DEFAULT -> {} TypeMode.MAGIKEYBOARD -> diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/fragments/EntryFragment.kt b/app/src/main/java/com/kunzisoft/keepass/activities/fragments/EntryFragment.kt index 5b276a168..47dd60682 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/fragments/EntryFragment.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/fragments/EntryFragment.kt @@ -152,10 +152,12 @@ class EntryFragment: DatabaseFragment() { private fun assignEntryInfo(entryInfo: EntryInfo?) { // Set copy buttons templateView.apply { + setOnUnprotectClickListener { _, textEditFieldView -> + mEntryViewModel.requestUnprotectField(textEditFieldView) + } setOnAskCopySafeClickListener { showClipboardDialog() } - setOnCopyActionClickListener { field -> mClipboardHelper?.timeoutCopyToClipboard( TemplateField.getLocalizedName(context, field.name), @@ -242,7 +244,7 @@ class EntryFragment: DatabaseFragment() { fun firstEntryFieldCopyView(): View? { return try { templateView.getActionImageView() - } catch (e: Exception) { + } catch (_: Exception) { null } } diff --git a/app/src/main/java/com/kunzisoft/keepass/credentialprovider/UserVerificationHelper.kt b/app/src/main/java/com/kunzisoft/keepass/credentialprovider/UserVerificationHelper.kt index 06593ce15..fc3d26563 100644 --- a/app/src/main/java/com/kunzisoft/keepass/credentialprovider/UserVerificationHelper.kt +++ b/app/src/main/java/com/kunzisoft/keepass/credentialprovider/UserVerificationHelper.kt @@ -90,7 +90,9 @@ class UserVerificationHelper { * Check if the User needs to be verified for this entry */ fun EntryInfo.isUserVerificationNeeded(): Boolean { - return this.passkey != null + // Apply to any entry with protected content + // Not only this.passkey != null + return true } fun Fragment.checkUserVerification( diff --git a/app/src/main/java/com/kunzisoft/keepass/view/ProtectedFieldView.kt b/app/src/main/java/com/kunzisoft/keepass/view/ProtectedFieldView.kt index be78ffed3..29a0051b3 100644 --- a/app/src/main/java/com/kunzisoft/keepass/view/ProtectedFieldView.kt +++ b/app/src/main/java/com/kunzisoft/keepass/view/ProtectedFieldView.kt @@ -3,7 +3,7 @@ package com.kunzisoft.keepass.view import android.view.View.OnClickListener interface ProtectedFieldView { - fun setOnUnprotectClickListener(onUnprotectClickListener: OnClickListener?) + fun setProtection(protection: Boolean, onUnprotectClickListener: OnClickListener?) fun protect() fun unprotect() fun isCurrentlyProtected(): Boolean diff --git a/app/src/main/java/com/kunzisoft/keepass/view/TemplateEditView.kt b/app/src/main/java/com/kunzisoft/keepass/view/TemplateEditView.kt index 35595e7e2..041b277e2 100644 --- a/app/src/main/java/com/kunzisoft/keepass/view/TemplateEditView.kt +++ b/app/src/main/java/com/kunzisoft/keepass/view/TemplateEditView.kt @@ -127,10 +127,8 @@ class TemplateEditView @JvmOverloads constructor(context: Context, PasswordTextEditFieldView(it) else TextEditFieldView(it)).apply { // hiddenProtectedValue (mHideProtectedValue) don't work with TextInputLayout - if (field.protectedValue.isProtected) { - setOnUnprotectClickListener { - mOnUnprotectClickListener?.invoke(field, this) - } + setProtection(field.protectedValue.isProtected) { + mOnUnprotectClickListener?.invoke(field, this) } default = templateAttribute.default setMaxChars(templateAttribute.options.getNumberChars()) @@ -198,7 +196,7 @@ class TemplateEditView @JvmOverloads constructor(context: Context, val value = field.protectedValue.toString().trim() type = dateInstantType activation = value.isNotEmpty() - } catch (e: Exception) { + } catch (_: Exception) { type = dateInstantType activation = false } diff --git a/app/src/main/java/com/kunzisoft/keepass/view/TemplateView.kt b/app/src/main/java/com/kunzisoft/keepass/view/TemplateView.kt index ff3466991..9062d6921 100644 --- a/app/src/main/java/com/kunzisoft/keepass/view/TemplateView.kt +++ b/app/src/main/java/com/kunzisoft/keepass/view/TemplateView.kt @@ -25,6 +25,11 @@ class TemplateView @JvmOverloads constructor(context: Context, : TemplateAbstractView (context, attrs, defStyle) { + private var mOnUnprotectClickListener: ((Field, ProtectedFieldView) -> Unit)? = null + fun setOnUnprotectClickListener(listener: ((Field, ProtectedFieldView) -> Unit)?) { + this.mOnUnprotectClickListener = listener + } + private var mOnAskCopySafeClickListener: (() -> Unit)? = null fun setOnAskCopySafeClickListener(listener: (() -> Unit)? = null) { this.mOnAskCopySafeClickListener = listener @@ -58,7 +63,9 @@ class TemplateView @JvmOverloads constructor(context: Context, PasskeyTextFieldView(it) else TextFieldView(it)).apply { applyFontVisibility(mFontInVisibility) - setProtection(field.protectedValue.isProtected) + setProtection(field.protectedValue.isProtected) { + mOnUnprotectClickListener?.invoke(field, this) + } label = templateAttribute.alias ?: TemplateField.getLocalizedName(context, field.name) setMaxChars(templateAttribute.options.getNumberChars()) @@ -114,7 +121,7 @@ class TemplateView @JvmOverloads constructor(context: Context, try { val value = field.protectedValue.toString().trim() activation = value.isNotEmpty() - } catch (e: Exception) { + } catch (_: Exception) { activation = false } } diff --git a/app/src/main/java/com/kunzisoft/keepass/view/TextEditFieldView.kt b/app/src/main/java/com/kunzisoft/keepass/view/TextEditFieldView.kt index 8fbb20bd3..8c742d24b 100644 --- a/app/src/main/java/com/kunzisoft/keepass/view/TextEditFieldView.kt +++ b/app/src/main/java/com/kunzisoft/keepass/view/TextEditFieldView.kt @@ -171,14 +171,16 @@ open class TextEditFieldView @JvmOverloads constructor(context: Context, } } - override fun setOnUnprotectClickListener(onUnprotectClickListener: OnClickListener?) { - labelView.endIconMode = TextInputLayout.END_ICON_PASSWORD_TOGGLE - valueView.inputType = valueView.inputType or InputType.TYPE_TEXT_VARIATION_PASSWORD - /* - // FIXME Called by itself during orientation change - labelView.setEndIconOnClickListener { - onUnprotectClickListener?.onClick(this@TextEditFieldView) - }*/ + override fun setProtection(protection: Boolean, onUnprotectClickListener: OnClickListener?) { + if (protection) { + labelView.endIconMode = TextInputLayout.END_ICON_PASSWORD_TOGGLE + valueView.inputType = valueView.inputType or InputType.TYPE_TEXT_VARIATION_PASSWORD + /* + // FIXME Called by itself during orientation change + labelView.setEndIconOnClickListener { + onUnprotectClickListener?.onClick(this@TextEditFieldView) + }*/ + } } override fun isCurrentlyProtected(): Boolean { diff --git a/app/src/main/java/com/kunzisoft/keepass/view/TextFieldView.kt b/app/src/main/java/com/kunzisoft/keepass/view/TextFieldView.kt index 64c4bf814..2906b498a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/view/TextFieldView.kt +++ b/app/src/main/java/com/kunzisoft/keepass/view/TextFieldView.kt @@ -42,7 +42,8 @@ import com.kunzisoft.keepass.utils.AppUtil.openExternalApp open class TextFieldView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) - : RelativeLayout(context, attrs, defStyle), GenericTextFieldView { + : RelativeLayout(context, attrs, defStyle), + GenericTextFieldView, ProtectedFieldView { protected var labelViewId = ViewCompat.generateViewId() private var valueViewId = ViewCompat.generateViewId() @@ -204,17 +205,30 @@ open class TextFieldView @JvmOverloads constructor(context: Context, } } - fun setProtection(protection: Boolean) { + override fun setProtection(protection: Boolean, onUnprotectClickListener: OnClickListener?) { showButton.isVisible = protection showButton.isSelected = true showButton.setOnClickListener { - showButton.isSelected = !showButton.isSelected - changeProtectedValueParameters() + onUnprotectClickListener?.onClick(this@TextFieldView) } changeProtectedValueParameters() invalidate() } + override fun isCurrentlyProtected(): Boolean { + return showButton.isSelected + } + + override fun protect() { + showButton.isSelected = !showButton.isSelected + changeProtectedValueParameters() + } + + override fun unprotect() { + showButton.isSelected = !showButton.isSelected + changeProtectedValueParameters() + } + protected fun changeProtectedValueParameters() { valueView.apply { if (showButton.isVisible) { diff --git a/app/src/main/java/com/kunzisoft/keepass/viewmodels/EntryViewModel.kt b/app/src/main/java/com/kunzisoft/keepass/viewmodels/EntryViewModel.kt index 272680c81..1f6768d2c 100644 --- a/app/src/main/java/com/kunzisoft/keepass/viewmodels/EntryViewModel.kt +++ b/app/src/main/java/com/kunzisoft/keepass/viewmodels/EntryViewModel.kt @@ -31,6 +31,9 @@ import com.kunzisoft.keepass.model.EntryAttachmentState import com.kunzisoft.keepass.model.EntryInfo import com.kunzisoft.keepass.otp.OtpElement import com.kunzisoft.keepass.utils.IOActionTask +import com.kunzisoft.keepass.view.ProtectedFieldView +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow import java.util.UUID @@ -67,6 +70,9 @@ class EntryViewModel: ViewModel() { val historySelected : LiveData get() = _historySelected private val _historySelected = SingleLiveEvent() + private val mEntryState = MutableStateFlow(EntryState.Loading) + val entryState: StateFlow = mEntryState + fun loadDatabase(database: ContextualDatabase?) { loadEntry(database, mainEntryId, historyPosition) } @@ -126,6 +132,10 @@ class EntryViewModel: ViewModel() { } } + fun requestUnprotectField(fieldView: ProtectedFieldView) { + mEntryState.value = EntryState.RequestUnprotectField(fieldView) + } + fun onOtpElementUpdated(optElement: OtpElement?) { _onOtpElementUpdated.value = optElement } @@ -146,6 +156,10 @@ class EntryViewModel: ViewModel() { _sectionSelected.value = section } + fun actionPerformed() { + mEntryState.value = EntryState.Loading + } + data class EntryInfoHistory(var mainEntryId: NodeId, var historyPosition: Int, val template: Template, @@ -167,6 +181,13 @@ class EntryViewModel: ViewModel() { } } + sealed class EntryState { + object Loading: EntryState() + data class RequestUnprotectField( + val protectedFieldView: ProtectedFieldView + ): EntryState() + } + companion object { private val TAG = EntryViewModel::class.java.name }