From a70fca493d3882b4712290c5745e4fd9c504353c Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Mon, 7 Jun 2021 14:54:03 +0200 Subject: [PATCH] Better template engine encapsulation --- .../keepass/activities/EntryEditActivity.kt | 36 ++++++------ .../activities/fragments/EntryEditFragment.kt | 15 ----- .../activities/fragments/EntryFragment.kt | 4 +- .../keepass/database/element/Database.kt | 15 +++-- .../database/element/database/DatabaseKDBX.kt | 16 ++++-- .../database/element/entry/EntryKDBX.kt | 4 +- .../database/element/template/Template.kt | 4 +- .../element/template/TemplateEngine.kt | 57 ++++++++++++++++--- .../element/template/TemplateField.kt | 16 +----- .../keepass/viewmodels/EntryViewModel.kt | 2 +- 10 files changed, 97 insertions(+), 72 deletions(-) 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 928f9b8a4..ebbfef800 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt @@ -91,6 +91,7 @@ class EntryEditActivity : LockingActivity(), private var mParent: Group? = null private var mIsNew: Boolean = false private var mIsTemplate: Boolean = false + private var mEntryTemplate: Template? = null // Views private var coordinatorLayout: CoordinatorLayout? = null @@ -187,12 +188,6 @@ class EntryEditActivity : LockingActivity(), tempEntryInfo?.username = mDatabase?.defaultUsername ?: "" } - // Build template selector - val templates = mDatabase?.getTemplates(mIsTemplate) - val entryTemplate: Template? = mEntry?.let { - mDatabase?.getTemplate(it) - } ?: if (templates?.isNotEmpty() == true) Template.STANDARD else null - // Retrieve data from registration val registerInfo = EntrySelectionHelper.retrieveRegisterInfoFromIntent(intent) val searchInfo: SearchInfo? = registerInfo?.searchInfo @@ -209,7 +204,7 @@ class EntryEditActivity : LockingActivity(), // Build fragment to manage entry modification entryEditFragment = supportFragmentManager.findFragmentByTag(ENTRY_EDIT_FRAGMENT_TAG) as? EntryEditFragment? if (entryEditFragment == null) { - entryEditFragment = EntryEditFragment.getInstance(tempEntryInfo, entryTemplate) + entryEditFragment = EntryEditFragment.getInstance(tempEntryInfo, mEntryTemplate) } entryEditFragment?.apply { drawFactory = mDatabase?.iconDrawableFactory @@ -251,9 +246,11 @@ class EntryEditActivity : LockingActivity(), // Change template dynamically templateSelectorSpinner = findViewById(R.id.entry_edit_template_selector) templateSelectorSpinner?.apply { + // Build template selector + val templates = mDatabase?.getTemplates(mIsTemplate) if (templates != null && templates.isNotEmpty()) { adapter = TemplatesSelectorAdapter(this@EntryEditActivity, mDatabase, templates) - setSelection(templates.indexOf(entryTemplate)) + setSelection(templates.indexOf(mEntryTemplate)) onItemSelectedListener = object : AdapterView.OnItemSelectedListener { override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { val newTemplate = templates[position] @@ -341,13 +338,17 @@ class EntryEditActivity : LockingActivity(), } private fun checkIfTemplate() { - mIsTemplate = mDatabase?.templatesGroup?.let { - mDatabase?.templatesGroup == mParent - } ?: false - if (mIsTemplate) { - mEntry?.let { - mEntry = mDatabase?.decodeTemplateEntry(it) - } + // Define is current entry is a template (in direct template group) + mIsTemplate = mDatabase?.entryIsTemplate(mEntry) ?: false + + val templates = mDatabase?.getTemplates(mIsTemplate) + mEntryTemplate = mEntry?.let { + mDatabase?.getTemplate(it) + } ?: if (templates?.isNotEmpty() == true) Template.STANDARD else null + + // Decode the entry + mEntry?.let { + mEntry = mDatabase?.decodeEntryWithTemplateConfiguration(it) } } @@ -601,9 +602,8 @@ class EntryEditActivity : LockingActivity(), newEntry.setEntryInfo(mDatabase, newEntryInfo) // Encode entry properties for template - if (mIsTemplate) { - newEntry = mDatabase?.encodeTemplateEntry(newEntry) ?: newEntry - } + val template = entryEditFragment?.getTemplate() ?: Template.STANDARD // TODO Move + newEntry = mDatabase?.encodeEntryWithTemplateConfiguration(newEntry, template) ?: newEntry // Delete temp attachment if not used mTempAttachments.forEach { tempAttachmentState -> diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/fragments/EntryEditFragment.kt b/app/src/main/java/com/kunzisoft/keepass/activities/fragments/EntryEditFragment.kt index 9e37729f8..2c7df39e5 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/fragments/EntryEditFragment.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/fragments/EntryEditFragment.kt @@ -56,8 +56,6 @@ import com.kunzisoft.keepass.otp.OtpEntryFields import com.kunzisoft.keepass.settings.PreferencesUtil import com.kunzisoft.keepass.view.* import org.joda.time.DateTime -import java.util.* -import kotlin.collections.ArrayList class EntryEditFragment: DatabaseFragment() { @@ -234,11 +232,6 @@ class EntryEditFragment: DatabaseFragment() { } val customFieldsNotConsumed = ArrayList(mEntryInfo.customFields) - // Ignore Template field - customFieldsNotConsumed.indexOfFirst { it.name == TemplateEngine.TEMPLATE_ENTRY_UUID }.let { - if (it != -1) - customFieldsNotConsumed.removeAt(it) - } mTemplate.sections.forEach { templateSection -> @@ -445,14 +438,6 @@ class EntryEditFragment: DatabaseFragment() { mEntryInfo.customFields = mCustomFieldIds.map { getCustomField(it.label) - }.toMutableList().also { customFields -> - // Add template field - if (mTemplate != Template.STANDARD - && mTemplate != Template.CREATION) { - TemplateField.getTemplateUUIDField(mTemplate)?.let { templateField -> - customFields.add(templateField) - } - } } mEntryInfo.otpModel = OtpEntryFields.parseFields { key -> 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 9596296c2..633c262bd 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 @@ -250,9 +250,7 @@ class EntryFragment: DatabaseFragment() { entryInfo?.customFields?.forEach { field -> val label = field.name // OTP field is already managed in dedicated view - // Template UUID must not be shown - if (label != OtpEntryFields.OTP_TOKEN_FIELD - && label != TemplateEngine.TEMPLATE_ENTRY_UUID) { + if (label != OtpEntryFields.OTP_TOKEN_FIELD) { val value = field.protectedValue val allowCopyProtectedField = !value.isProtected || allowCopyPasswordAndProtectedFields if (allowCopyProtectedField) { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt index 0265f21fb..ccb57d0e1 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt @@ -154,18 +154,25 @@ class Database { return null } - fun decodeTemplateEntry(entry: Entry): Entry { + fun entryIsTemplate(entry: Entry?): Boolean { + // Define is current entry is a template (in direct template group) + if (entry == null || templatesGroup == null) + return false + return templatesGroup == entry.parent + } + + fun decodeEntryWithTemplateConfiguration(entry: Entry): Entry { entry.entryKDBX?.let { - mDatabaseKDBX?.decodeTemplateEntry(it)?.let { decode -> + mDatabaseKDBX?.decodeEntryWithTemplateConfiguration(it, entryIsTemplate(entry))?.let { decode -> return Entry(decode) } } return entry } - fun encodeTemplateEntry(entry: Entry): Entry { + fun encodeEntryWithTemplateConfiguration(entry: Entry, template: Template): Entry { entry.entryKDBX?.let { - mDatabaseKDBX?.encodeTemplateEntry(it)?.let { encode -> + mDatabaseKDBX?.encodeEntryWithTemplateConfiguration(it, entryIsTemplate(entry), template)?.let { encode -> return Entry(encode) } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDBX.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDBX.kt index a6adb9260..d009e5af7 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDBX.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDBX.kt @@ -369,12 +369,20 @@ class DatabaseKDBX : DatabaseVersioned { return mTemplateEngine.getTemplate(entry) } - fun decodeTemplateEntry(entryKDBX: EntryKDBX): EntryKDBX { - return mTemplateEngine.decodeTemplateEntry(entryKDBX) + fun decodeEntryWithTemplateConfiguration(entryKDBX: EntryKDBX, entryIsTemplate: Boolean): EntryKDBX { + return if (entryIsTemplate) { + mTemplateEngine.decodeTemplateEntry(entryKDBX) + } else { + mTemplateEngine.removeMetaTemplateRecognitionFromEntry(entryKDBX) + } } - fun encodeTemplateEntry(entryKDBX: EntryKDBX): EntryKDBX { - return mTemplateEngine.encodeTemplateEntry(entryKDBX) + fun encodeEntryWithTemplateConfiguration(entryKDBX: EntryKDBX, entryIsTemplate: Boolean, template: Template): EntryKDBX { + return if (entryIsTemplate) { + mTemplateEngine.encodeTemplateEntry(entryKDBX) + } else { + mTemplateEngine.addMetaTemplateRecognitionToEntry(template, entryKDBX) + } } /* diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/entry/EntryKDBX.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/entry/EntryKDBX.kt index 6b439c6bd..f1c1a152d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/entry/EntryKDBX.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/entry/EntryKDBX.kt @@ -297,8 +297,8 @@ class EntryKDBX : EntryVersioned, NodeKDBXInte fields[label] = value } - fun removeField(field: Field) { - fields.remove(field.name) + fun removeField(name: String) { + fields.remove(name) } fun removeAllFields() { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/template/Template.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/template/Template.kt index 60b6a8ce1..163da4fc1 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/template/Template.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/template/Template.kt @@ -25,7 +25,6 @@ import com.kunzisoft.keepass.database.element.database.DatabaseVersioned import com.kunzisoft.keepass.database.element.icon.IconImage import com.kunzisoft.keepass.database.element.icon.IconImageStandard import com.kunzisoft.keepass.database.element.icon.IconImageStandard.Companion.BUILD_ID -import com.kunzisoft.keepass.database.element.template.TemplateEngine.Companion.TEMPLATE_LABEL_VERSION import com.kunzisoft.keepass.database.element.template.TemplateField.LABEL_EXPIRATION import com.kunzisoft.keepass.database.element.template.TemplateField.LABEL_NOTES import com.kunzisoft.keepass.database.element.template.TemplateField.LABEL_PASSWORD @@ -141,8 +140,7 @@ class Template : Parcelable { get() { val sections = ArrayList() val mainSection = TemplateSection(ArrayList().apply { - add(TemplateAttribute(TEMPLATE_LABEL_VERSION, TemplateAttributeType.INLINE)) - // Dynamic part after this + // Dynamic part }) sections.add(mainSection) return Template(UUID(0, 1), diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/template/TemplateEngine.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/template/TemplateEngine.kt index 54f5ed1ab..278a5905f 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/template/TemplateEngine.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/template/TemplateEngine.kt @@ -192,12 +192,15 @@ class TemplateEngine(private val mDatabase: DatabaseKDBX) { val entryCopy = EntryKDBX().apply { updateWith(templateEntry) } - templateEntry.doForEachDecodedCustomField { field, -> + // Remove template version + entryCopy.removeField(TEMPLATE_LABEL_VERSION) + // Dynamic attributes + templateEntry.doForEachDecodedCustomField { field -> conditionCustomFields(attributes, field, { if (field.name.startsWith(TEMPLATE_ATTRIBUTE_TYPE_PREFIX)) { it.attribute.defaultValue = field.protectedValue.stringValue } - entryCopy.removeField(field) + entryCopy.removeField(field.name) }, { }) } @@ -219,6 +222,9 @@ class TemplateEngine(private val mDatabase: DatabaseKDBX) { val entryCopy = EntryKDBX().apply { updateWith(templateEntry) } + // Add template version + entryCopy.putField(TEMPLATE_LABEL_VERSION, ProtectedString(false, "1")) + // Dynamic attributes var index = 0 templateEntry.doForEachDecodedCustomField { field -> val label = field.name.removePrefix(PREFIX_DECODED_TEMPLATE).removeSuffix(SUFFIX_DECODED_TEMPLATE) @@ -228,7 +234,7 @@ class TemplateEngine(private val mDatabase: DatabaseKDBX) { // Keep template version as is } else -> { - entryCopy.removeField(field) + entryCopy.removeField(field.name) entryCopy.putField(TEMPLATE_ATTRIBUTE_POSITION_PREFIX+'_'+label, ProtectedString(false, index.toString())) entryCopy.putField(TEMPLATE_ATTRIBUTE_TITLE_PREFIX+'_'+label, @@ -248,13 +254,45 @@ class TemplateEngine(private val mDatabase: DatabaseKDBX) { return entryCopy } + private fun getTemplateUUIDField(template: Template): Field? { + UuidUtil.toHexString(template.uuid)?.let { uuidString -> + return Field(TEMPLATE_ENTRY_UUID, + ProtectedString(false, uuidString)) + } + return null + } + + fun removeMetaTemplateRecognitionFromEntry(entry: EntryKDBX): EntryKDBX { + val entryCopy = EntryKDBX().apply { + updateWith(entry) + } + entryCopy.removeField(TEMPLATE_ENTRY_UUID) + return entryCopy + } + + fun addMetaTemplateRecognitionToEntry(template: Template, entry: EntryKDBX): EntryKDBX { + val entryCopy = EntryKDBX().apply { + updateWith(entry) + } + // Add template field + if (template != Template.STANDARD + && template != Template.CREATION) { + getTemplateUUIDField(template)?.let { templateField -> + entryCopy.putField(templateField) + } + } else { + entryCopy.removeField(TEMPLATE_ENTRY_UUID) + } + return entryCopy + } + companion object { private data class TemplateAttributePosition(var position: Int, var attribute: TemplateAttribute) private val TAG = TemplateEngine::class.java.name - const val PREFIX_DECODED_TEMPLATE = "[" - const val SUFFIX_DECODED_TEMPLATE = "]" + private const val PREFIX_DECODED_TEMPLATE = "[" + private const val SUFFIX_DECODED_TEMPLATE = "]" // Custom template ref private const val TEMPLATE_ATTRIBUTE_TITLE = "@title" @@ -265,8 +303,8 @@ class TemplateEngine(private val mDatabase: DatabaseKDBX) { private const val TEMPLATE_ATTRIBUTE_EXPIRES = "@expires" private const val TEMPLATE_ATTRIBUTE_NOTES = "@notes" - const val TEMPLATE_LABEL_VERSION = "_etm_template" - const val TEMPLATE_ENTRY_UUID = "_etm_template_uuid" + private const val TEMPLATE_LABEL_VERSION = "_etm_template" + private const val TEMPLATE_ENTRY_UUID = "_etm_template_uuid" private const val TEMPLATE_ATTRIBUTE_POSITION_PREFIX = "_etm_position" private const val TEMPLATE_ATTRIBUTE_TITLE_PREFIX = "_etm_title" private const val TEMPLATE_ATTRIBUTE_TYPE_PREFIX = "_etm_type" @@ -284,6 +322,11 @@ class TemplateEngine(private val mDatabase: DatabaseKDBX) { return resources.getString(R.string.templates) } + fun isTemplateNameAttribute(name: String): Boolean { + return name.startsWith(PREFIX_DECODED_TEMPLATE) + && name.endsWith(SUFFIX_DECODED_TEMPLATE) + } + fun decodeTemplateAttribute(name: String): String { return when { TEMPLATE_LABEL_VERSION.equals(name, true) -> TemplateField.LABEL_VERSION diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/template/TemplateField.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/template/TemplateField.kt index 14e1ee45d..5566cf3be 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/template/TemplateField.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/template/TemplateField.kt @@ -2,11 +2,6 @@ package com.kunzisoft.keepass.database.element.template import android.content.Context import com.kunzisoft.keepass.R -import com.kunzisoft.keepass.database.element.security.ProtectedString -import com.kunzisoft.keepass.database.element.Field -import com.kunzisoft.keepass.database.element.template.TemplateEngine.Companion.PREFIX_DECODED_TEMPLATE -import com.kunzisoft.keepass.database.element.template.TemplateEngine.Companion.SUFFIX_DECODED_TEMPLATE -import com.kunzisoft.keepass.utils.UuidUtil object TemplateField { @@ -47,8 +42,7 @@ object TemplateField { fun getLocalizedName(context: Context?, name: String): String { if (context == null - || name.startsWith(PREFIX_DECODED_TEMPLATE) - && name.endsWith(SUFFIX_DECODED_TEMPLATE)) + || TemplateEngine.isTemplateNameAttribute(name)) return name return when { @@ -79,12 +73,4 @@ object TemplateField { else -> name } } - - fun getTemplateUUIDField(template: Template): Field? { - UuidUtil.toHexString(template.uuid)?.let { uuidString -> - return Field(TemplateEngine.TEMPLATE_ENTRY_UUID, - ProtectedString(false, uuidString)) - } - return null - } } \ No newline at end of file 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 640cf1647..190a3321a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/viewmodels/EntryViewModel.kt +++ b/app/src/main/java/com/kunzisoft/keepass/viewmodels/EntryViewModel.kt @@ -41,7 +41,7 @@ class EntryViewModel: ViewModel() { entry?.touch(modified = false, touchParents = false) // To simplify template field visibility entry?.let { - // TODO entry = mDatabase.decodeTemplateEntry(it) + entry = mDatabase.decodeEntryWithTemplateConfiguration(it) } _entry.value = EntryHistory(entry, entryLastVersion, historyPosition) } else {