From b0d1f93bfc5952352ad3845b26da5c4c8831fd35 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Sat, 8 Jan 2022 16:06:10 +0100 Subject: [PATCH 01/28] Change header sig --- .../kunzisoft/keepass/database/file/DatabaseHeader.kt | 6 ------ .../keepass/database/file/DatabaseHeaderKDB.kt | 10 +++++----- .../keepass/database/file/DatabaseHeaderKDBX.kt | 7 ++++--- .../keepass/database/file/input/DatabaseInputKDB.kt | 2 +- .../database/file/output/DatabaseHeaderOutputKDBX.kt | 2 +- .../keepass/database/file/output/DatabaseOutputKDB.kt | 2 +- 6 files changed, 12 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/DatabaseHeader.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/DatabaseHeader.kt index 7f1f30838..9f0dc2bd6 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/file/DatabaseHeader.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/DatabaseHeader.kt @@ -19,8 +19,6 @@ */ package com.kunzisoft.keepass.database.file -import com.kunzisoft.keepass.utils.UnsignedInt - abstract class DatabaseHeader { /** @@ -33,8 +31,4 @@ abstract class DatabaseHeader { */ var encryptionIV = ByteArray(16) - companion object { - val PWM_DBSIG_1 = UnsignedInt(-0x655d26fd) - } - } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/DatabaseHeaderKDB.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/DatabaseHeaderKDB.kt index f6493f331..1b30d7644 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/file/DatabaseHeaderKDB.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/DatabaseHeaderKDB.kt @@ -34,7 +34,7 @@ class DatabaseHeaderKDB : DatabaseHeader() { */ var transformSeed = ByteArray(32) - var signature1 = UnsignedInt(0) // = PWM_DBSIG_1 + var signature1 = UnsignedInt(0) // = DBSIG_1 var signature2 = UnsignedInt(0) // = DBSIG_2 var flags= UnsignedInt(0) var version= UnsignedInt(0) @@ -84,9 +84,9 @@ class DatabaseHeaderKDB : DatabaseHeader() { companion object { // DB sig from KeePass 1.03 - val DBSIG_2 = UnsignedInt(-0x4ab4049b) - // DB sig from KeePass 1.03 - val DBVER_DW = UnsignedInt(0x00030003) + val DBSIG_1 = UnsignedInt(-0x655d26fd) // 0x9AA2D903 + val DBSIG_2 = UnsignedInt(-0x4ab4049b) // 0xB54BFB65 + val DBVER_DW = UnsignedInt(0x00030004) val FLAG_SHA2 = UnsignedInt(1) val FLAG_RIJNDAEL = UnsignedInt(2) @@ -97,7 +97,7 @@ class DatabaseHeaderKDB : DatabaseHeader() { const val BUF_SIZE = 124 fun matchesHeader(sig1: UnsignedInt, sig2: UnsignedInt): Boolean { - return sig1.toKotlinInt() == PWM_DBSIG_1.toKotlinInt() && sig2.toKotlinInt() == DBSIG_2.toKotlinInt() + return sig1.toKotlinInt() == DBSIG_1.toKotlinInt() && sig2.toKotlinInt() == DBSIG_2.toKotlinInt() } fun compatibleHeaders(one: UnsignedInt, two: UnsignedInt): Boolean { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/DatabaseHeaderKDBX.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/DatabaseHeaderKDBX.kt index 271ab221f..1bbeaff93 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/file/DatabaseHeaderKDBX.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/DatabaseHeaderKDBX.kt @@ -311,8 +311,9 @@ class DatabaseHeaderKDBX(private val databaseV4: DatabaseKDBX) : DatabaseHeader( companion object { - val DBSIG_PRE2 = UnsignedInt(-0x4ab4049a) - val DBSIG_2 = UnsignedInt(-0x4ab40499) + val DBSIG_1 = UnsignedInt(-0x655d26fd) // 0x9AA2D903 + val DBSIG_PRE2 = UnsignedInt(-0x4ab4049a) // 0xB54BFB66 + val DBSIG_2 = UnsignedInt(-0x4ab40499) // 0xB54BFB67 private val FILE_VERSION_CRITICAL_MASK = UnsignedInt(-0x10000) val FILE_VERSION_31 = UnsignedInt(0x00030001) @@ -335,7 +336,7 @@ class DatabaseHeaderKDBX(private val databaseV4: DatabaseKDBX) : DatabaseHeader( } fun matchesHeader(sig1: UnsignedInt, sig2: UnsignedInt): Boolean { - return sig1 == PWM_DBSIG_1 && (sig2 == DBSIG_PRE2 || sig2 == DBSIG_2) + return sig1 == DBSIG_1 && (sig2 == DBSIG_PRE2 || sig2 == DBSIG_2) } } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInputKDB.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInputKDB.kt index bd947658f..df303bf2a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInputKDB.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInputKDB.kt @@ -98,7 +98,7 @@ class DatabaseInputKDB(cacheDirectory: File, if (fileSize != (contentSize + DatabaseHeaderKDB.BUF_SIZE)) throw IOException("Header corrupted") - if (header.signature1 != DatabaseHeader.PWM_DBSIG_1 + if (header.signature1 != DatabaseHeaderKDB.DBSIG_1 || header.signature2 != DatabaseHeaderKDB.DBSIG_2) { throw SignatureDatabaseException() } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseHeaderOutputKDBX.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseHeaderOutputKDBX.kt index 07ac44677..3c98b84cc 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseHeaderOutputKDBX.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseHeaderOutputKDBX.kt @@ -68,7 +68,7 @@ constructor(private val databaseKDBX: DatabaseKDBX, @Throws(IOException::class) fun output() { - mos.write4BytesUInt(DatabaseHeader.PWM_DBSIG_1) + mos.write4BytesUInt(DatabaseHeaderKDBX.DBSIG_1) mos.write4BytesUInt(DatabaseHeaderKDBX.DBSIG_2) mos.write4BytesUInt(header.version) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDB.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDB.kt index a3f866d97..106e4bf84 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDB.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDB.kt @@ -110,7 +110,7 @@ class DatabaseOutputKDB(private val mDatabaseKDB: DatabaseKDB, override fun outputHeader(outputStream: OutputStream): DatabaseHeaderKDB { // Build header val header = DatabaseHeaderKDB() - header.signature1 = DatabaseHeader.PWM_DBSIG_1 + header.signature1 = DatabaseHeaderKDB.DBSIG_1 header.signature2 = DatabaseHeaderKDB.DBSIG_2 header.flags = DatabaseHeaderKDB.FLAG_SHA2 From b9652291bdef0edc1ea1966b3f54869141eebfae Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Sat, 8 Jan 2022 19:35:42 +0100 Subject: [PATCH 02/28] Manage default user name and color in KDB --- .../keepass/database/element/Database.kt | 27 ++++---- .../keepass/database/element/Group.kt | 3 +- .../database/element/database/DatabaseKDB.kt | 3 + .../database/element/entry/EntryKDB.kt | 57 +++++++++++++---- .../database/file/input/DatabaseInputKDB.kt | 17 ++++- .../database/file/output/DatabaseOutputKDB.kt | 63 +++++++++++++++++-- .../NestedDatabaseSettingsFragment.kt | 28 +++------ 7 files changed, 148 insertions(+), 50 deletions(-) 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 ee597bb0e..550bda556 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 @@ -22,10 +22,10 @@ package com.kunzisoft.keepass.database.element import android.content.ContentResolver import android.content.Context import android.content.res.Resources +import android.graphics.Color import android.net.Uri -import android.os.Build import android.util.Log -import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction +import com.kunzisoft.androidclearchroma.ChromaUtil import com.kunzisoft.keepass.database.action.node.NodeHandler import com.kunzisoft.keepass.database.crypto.EncryptionAlgorithm import com.kunzisoft.keepass.database.crypto.kdf.KdfEngine @@ -226,30 +226,31 @@ class Database { mDatabaseKDBX?.descriptionChanged = DateInstant() } - val allowDefaultUsername: Boolean - get() = mDatabaseKDBX != null - // TODO get() = mDatabaseKDB != null || mDatabaseKDBX != null - var defaultUsername: String get() { - return mDatabaseKDBX?.defaultUserName ?: "" // TODO mDatabaseKDB default username + return mDatabaseKDB?.defaultUserName ?: mDatabaseKDBX?.defaultUserName ?: "" } set(username) { + mDatabaseKDB?.defaultUserName = username mDatabaseKDBX?.defaultUserName = username mDatabaseKDBX?.defaultUserNameChanged = DateInstant() } - val allowCustomColor: Boolean - get() = mDatabaseKDBX != null - // TODO get() = mDatabaseKDB != null || mDatabaseKDBX != null - // with format "#000000" var customColor: String get() { - return mDatabaseKDBX?.color ?: "" // TODO mDatabaseKDB color + var colorString = "" + mDatabaseKDB?.color?.let { + colorString = ChromaUtil.getFormattedColorString(it, false) + } + return mDatabaseKDBX?.color ?: colorString } set(value) { - // TODO Check color string + mDatabaseKDB?.color = if (value == "") { + null + } else { + Color.parseColor(value) + } mDatabaseKDBX?.color = value } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/Group.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/Group.kt index b0311bf6e..3772026e2 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/Group.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/Group.kt @@ -309,8 +309,9 @@ class Group : Node, GroupVersionedInterface { val withoutMetaStream = filters.contains(ChildFilter.META_STREAM) val showExpiredEntries = !filters.contains(ChildFilter.EXPIRED) + // TODO Change KDB parser to remove meta entries return groupKDB?.getChildEntries()?.filter { - (!withoutMetaStream || (withoutMetaStream && !it.isMetaStream)) + (!withoutMetaStream || (withoutMetaStream && !it.isMetaStream())) && (!it.isCurrentlyExpires or showExpiredEntries) }?.map { Entry(it) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDB.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDB.kt index dcd25505a..168c259b9 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDB.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDB.kt @@ -61,6 +61,9 @@ class DatabaseKDB : DatabaseVersioned() { return listOf(BACKUP_FOLDER_TITLE) } + var defaultUserName: String = "" + var color: Int? = null + override val kdfEngine: KdfEngine get() = kdfListV3[0] diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/entry/EntryKDB.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/entry/EntryKDB.kt index 942a54e3c..d2ff9312e 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/entry/EntryKDB.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/entry/EntryKDB.kt @@ -25,6 +25,7 @@ import com.kunzisoft.keepass.database.element.Attachment import com.kunzisoft.keepass.database.element.binary.AttachmentPool import com.kunzisoft.keepass.database.element.binary.BinaryData import com.kunzisoft.keepass.database.element.group.GroupKDB +import com.kunzisoft.keepass.database.element.icon.IconImageStandard import com.kunzisoft.keepass.database.element.icon.IconImageStandard.Companion.KEY_ID import com.kunzisoft.keepass.database.element.node.NodeId import com.kunzisoft.keepass.database.element.node.NodeIdUUID @@ -60,18 +61,43 @@ class EntryKDB : EntryVersioned, NodeKDBInterface private var binaryDataId: Int? = null // Determine if this is a MetaStream entry - val isMetaStream: Boolean - get() { - if (notes.isEmpty()) return false - if (binaryDescription != PMS_ID_BINDESC) return false - if (title.isEmpty()) return false - if (title != PMS_ID_TITLE) return false - if (username.isEmpty()) return false - if (username != PMS_ID_USER) return false - if (url.isEmpty()) return false - if (url != PMS_ID_URL) return false - return icon.standard.id == KEY_ID - } + fun isMetaStream(): Boolean { + if (notes.isEmpty()) return false + if (binaryDescription != PMS_ID_BINDESC) return false + if (title.isEmpty()) return false + if (title != PMS_ID_TITLE) return false + if (username.isEmpty()) return false + if (username != PMS_ID_USER) return false + if (url.isEmpty()) return false + if (url != PMS_ID_URL) return false + return icon.standard.id == KEY_ID + } + + fun isMetaStreamDefaultUsername(): Boolean { + return isMetaStream() && notes == PMS_STREAM_DEFAULTUSER + } + + private fun setMetaStream() { + binaryDescription = PMS_ID_BINDESC + title = PMS_ID_TITLE + username = PMS_ID_USER + url = PMS_ID_URL + icon.standard = IconImageStandard(KEY_ID) + } + + fun setMetaStreamDefaultUsername() { + notes = PMS_STREAM_DEFAULTUSER + setMetaStream() + } + + fun isMetaStreamDatabaseColor(): Boolean { + return isMetaStream() && notes == PMS_STREAM_DBCOLOR + } + + fun setMetaStreamDatabaseColor() { + notes = PMS_STREAM_DBCOLOR + setMetaStream() + } override fun initNodeId(): NodeId { return NodeIdUUID() @@ -184,6 +210,13 @@ class EntryKDB : EntryVersioned, NodeKDBInterface private const val PMS_ID_USER = "SYSTEM" private const val PMS_ID_URL = "$" + const val PMS_STREAM_SIMPLESTATE = "Simple UI State" + const val PMS_STREAM_DEFAULTUSER = "Default User Name" + const val PMS_STREAM_SEARCHHISTORYITEM = "Search History Item" + const val PMS_STREAM_CUSTOMKVP = "Custom KVP" + const val PMS_STREAM_DBCOLOR = "Database Color" + const val PMS_STREAM_KPXICON2 = "KPX_CUSTOM_ICONS_2" + @JvmField val CREATOR: Parcelable.Creator = object : Parcelable.Creator { override fun createFromParcel(parcel: Parcel): EntryKDB { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInputKDB.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInputKDB.kt index df303bf2a..49f886f68 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInputKDB.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInputKDB.kt @@ -30,7 +30,6 @@ import com.kunzisoft.keepass.database.element.group.GroupKDB import com.kunzisoft.keepass.database.element.node.NodeIdInt import com.kunzisoft.keepass.database.element.node.NodeIdUUID import com.kunzisoft.keepass.database.exception.* -import com.kunzisoft.keepass.database.file.DatabaseHeader import com.kunzisoft.keepass.database.file.DatabaseHeaderKDB import com.kunzisoft.keepass.tasks.ProgressTaskUpdater import com.kunzisoft.keepass.utils.* @@ -300,6 +299,22 @@ class DatabaseInputKDB(cacheDirectory: File, } newEntry?.let { entry -> mDatabase.addEntryIndex(entry) + // Parse meta info + if (entry.isMetaStreamDefaultUsername()) { + var defaultUser = "" + entry.getBinary(mDatabase.attachmentPool) + ?.getInputDataStream(mDatabase.binaryCache)?.use { + defaultUser = String(it.readBytes()) + } + mDatabase.defaultUserName = defaultUser + } else if (entry.isMetaStreamDatabaseColor()) { + var color: Int? = null + entry.getBinary(mDatabase.attachmentPool) + ?.getInputDataStream(mDatabase.binaryCache)?.use { + color = it.read() + } + mDatabase.color = color + } currentEntryNumber++ newEntry = null } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDB.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDB.kt index 106e4bf84..5a6fde286 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDB.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDB.kt @@ -47,6 +47,9 @@ class DatabaseOutputKDB(private val mDatabaseKDB: DatabaseKDB, private var mGroupList = mutableListOf() private var mEntryList = mutableListOf() + private var mDefaultUsernameAdded = false + private var mDatabaseColorAdded = false + @Throws(DatabaseOutputException::class) fun getFinalKey(header: DatabaseHeader): ByteArray? { try { @@ -94,8 +97,7 @@ class DatabaseOutputKDB(private val mDatabaseKDB: DatabaseKDB, } finally { // Add again the virtual root group for better management mDatabaseKDB.rootGroup = rootGroup - mGroupList.clear() - mEntryList.clear() + clearParser() } } @@ -211,9 +213,14 @@ class DatabaseOutputKDB(private val mDatabaseKDB: DatabaseKDB, } } - private fun sortNodesForOutput() { + private fun clearParser() { mGroupList.clear() mEntryList.clear() + mDefaultUsernameAdded = false + } + + private fun sortNodesForOutput() { + clearParser() // Rebuild list according to sorting order removing any orphaned groups // Do not keep root mDatabaseKDB.rootGroup?.getChildGroups()?.let { rootSubGroups -> @@ -228,7 +235,39 @@ class DatabaseOutputKDB(private val mDatabaseKDB: DatabaseKDB, mGroupList.add(group) for (childEntry in group.getChildEntries()) { - mEntryList.add(childEntry) + if (childEntry.isMetaStreamDefaultUsername() + && mDatabaseKDB.defaultUserName.isNotEmpty()) { + setDefaultUsername(childEntry) + mEntryList.add(childEntry) + mDefaultUsernameAdded = true + } else if (childEntry.isMetaStreamDatabaseColor() + && mDatabaseKDB.color != null) { + setDatabaseColor(childEntry) + mEntryList.add(childEntry) + mDatabaseColorAdded = true + } else { + mEntryList.add(childEntry) + } + } + + // Add MetaStream + if (!mDefaultUsernameAdded + && mDatabaseKDB.defaultUserName.isNotEmpty()) { + val metaEntry = EntryKDB().apply { + setMetaStreamDefaultUsername() + setDefaultUsername(this) + } + mDatabaseKDB.addEntryTo(metaEntry, group) + mEntryList.add(metaEntry) + } + if (!mDatabaseColorAdded + && mDatabaseKDB.color != null) { + val metaEntry = EntryKDB().apply { + setMetaStreamDatabaseColor() + setDatabaseColor(this) + } + mDatabaseKDB.addEntryTo(metaEntry, group) + mEntryList.add(metaEntry) } // Recurse over children @@ -237,6 +276,22 @@ class DatabaseOutputKDB(private val mDatabaseKDB: DatabaseKDB, } } + private fun setDefaultUsername(entryKDB: EntryKDB) { + val binaryData = mDatabaseKDB.buildNewAttachment() + entryKDB.putBinary(binaryData, mDatabaseKDB.attachmentPool) + BufferedOutputStream(binaryData.getOutputDataStream(mDatabaseKDB.binaryCache)).use { outputStream -> + outputStream.write(mDatabaseKDB.defaultUserName.toByteArray()) + } + } + + private fun setDatabaseColor(entryKDB: EntryKDB) { + val binaryData = mDatabaseKDB.buildNewAttachment() + entryKDB.putBinary(binaryData, mDatabaseKDB.attachmentPool) + BufferedOutputStream(binaryData.getOutputDataStream(mDatabaseKDB.binaryCache)).use { outputStream -> + outputStream.write(mDatabaseKDB.color!!) + } + } + private fun getHeaderHashBuffer(headerDigest: ByteArray): ByteArray? { return try { val byteArrayOutputStream = ByteArrayOutputStream() diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/NestedDatabaseSettingsFragment.kt b/app/src/main/java/com/kunzisoft/keepass/settings/NestedDatabaseSettingsFragment.kt index bbe8b8b81..7072fb4d9 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/NestedDatabaseSettingsFragment.kt +++ b/app/src/main/java/com/kunzisoft/keepass/settings/NestedDatabaseSettingsFragment.kt @@ -30,8 +30,8 @@ import androidx.preference.PreferenceCategory import androidx.preference.SwitchPreference import com.kunzisoft.androidclearchroma.ChromaUtil import com.kunzisoft.keepass.R -import com.kunzisoft.keepass.activities.legacy.DatabaseRetrieval import com.kunzisoft.keepass.activities.dialogs.AssignMasterKeyDialogFragment +import com.kunzisoft.keepass.activities.legacy.DatabaseRetrieval import com.kunzisoft.keepass.activities.legacy.resetAppTimeoutWhenViewTouchedOrFocused import com.kunzisoft.keepass.database.crypto.EncryptionAlgorithm import com.kunzisoft.keepass.database.crypto.kdf.KdfEngine @@ -165,28 +165,18 @@ class NestedDatabaseSettingsFragment : NestedSettingsFragment(), DatabaseRetriev // Database default username dbDefaultUsernamePref = findPreference(getString(R.string.database_default_username_key)) - if (database.allowDefaultUsername) { - dbDefaultUsernamePref?.summary = database.defaultUsername - } else { - dbDefaultUsernamePref?.isEnabled = false - // TODO dbGeneralPrefCategory?.removePreference(dbDefaultUsername) - } + dbDefaultUsernamePref?.summary = database.defaultUsername // Database custom color dbCustomColorPref = findPreference(getString(R.string.database_custom_color_key)) - if (database.allowCustomColor) { - dbCustomColorPref?.apply { - try { - color = Color.parseColor(database.customColor) - summary = database.customColor - } catch (e: Exception) { - color = DialogColorPreference.DISABLE_COLOR - summary = "" - } + dbCustomColorPref?.apply { + try { + color = Color.parseColor(database.customColor) + summary = database.customColor + } catch (e: Exception) { + color = DialogColorPreference.DISABLE_COLOR + summary = "" } - } else { - dbCustomColorPref?.isEnabled = false - // TODO dbGeneralPrefCategory?.removePreference(dbCustomColorPref) } // Version From d2b8c850157b39892a7789cc52daa0083f256f23 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Sat, 8 Jan 2022 20:03:56 +0100 Subject: [PATCH 03/28] Add database color #913 --- CHANGELOG | 2 ++ .../keepass/activities/GroupActivity.kt | 11 ++++++++-- ...info.xml => background_rounded_square.xml} | 0 app/src/main/res/layout/activity_group.xml | 22 +++++++++++++++---- app/src/main/res/values/styles.xml | 2 +- .../metadata/android/en-US/changelogs/92.txt | 4 +++- .../metadata/android/fr-FR/changelogs/92.txt | 4 +++- 7 files changed, 36 insertions(+), 9 deletions(-) rename app/src/main/res/drawable/{background_text_info.xml => background_rounded_square.xml} (100%) diff --git a/CHANGELOG b/CHANGELOG index 447b48e1d..bcb9194bd 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,9 +2,11 @@ KeePassDX(3.1.0) * Add breadcrumb * Add path in search results #1148 * Add group info dialog #1177 + * Add database color #913 * Fix UI in Android 8 #509 * Upgrade libs and SDK to 31 #833 * Stop asking WRITE_EXTERNAL_STORAGE permission + * Fix parser of database v1 KeePassDX(3.0.4) * Fix autofill inline bugs #1173 #1165 diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt index 95c4c3a30..c7da55580 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt @@ -25,6 +25,8 @@ import android.app.TimePickerDialog import android.content.ComponentName import android.content.Context import android.content.Intent +import android.graphics.Color +import android.graphics.PorterDuff import android.os.* import android.util.Log import android.view.Menu @@ -88,6 +90,8 @@ class GroupActivity : DatabaseLockActivity(), private var coordinatorLayout: CoordinatorLayout? = null private var lockView: View? = null private var toolbar: Toolbar? = null + private var databaseNameContainer: ViewGroup? = null + private var databaseColorView: ImageView? = null private var databaseNameView: TextView? = null private var searchContainer: ViewGroup? = null private var searchNumbers: TextView? = null @@ -144,6 +148,8 @@ class GroupActivity : DatabaseLockActivity(), numberChildrenView = findViewById(R.id.group_numbers) addNodeButtonView = findViewById(R.id.add_node_button) toolbar = findViewById(R.id.toolbar) + databaseNameContainer = findViewById(R.id.database_name_container) + databaseColorView = findViewById(R.id.database_color) databaseNameView = findViewById(R.id.database_name) searchContainer = findViewById(R.id.search_container) searchNumbers = findViewById(R.id.search_numbers) @@ -412,6 +418,7 @@ class GroupActivity : DatabaseLockActivity(), // Search suggestion database?.let { databaseNameView?.text = if (it.name.isNotEmpty()) it.name else getString(R.string.database) + databaseColorView?.setColorFilter(Color.parseColor(it.customColor), PorterDuff.Mode.SRC_IN) mSearchSuggestionAdapter = SearchEntryCursorAdapter(this, it) mBreadcrumbAdapter?.iconDrawableFactory = it.iconDrawableFactory mOnSuggestionListener = object : SearchView.OnSuggestionListener { @@ -566,12 +573,12 @@ class GroupActivity : DatabaseLockActivity(), val title = group.title searchString?.text = if (title.isNotEmpty()) title else "" searchNumbers?.text = group.numberOfChildEntries.toString() - databaseNameView?.visibility = View.GONE + databaseNameContainer?.visibility = View.GONE toolbarBreadcrumb?.navigationIcon = null toolbarBreadcrumb?.collapse() } else { searchContainer?.visibility = View.GONE - databaseNameView?.visibility = View.VISIBLE + databaseNameContainer?.visibility = View.VISIBLE // Refresh breadcrumb if (toolbarBreadcrumb?.isVisible != true) { toolbarBreadcrumb?.expand { diff --git a/app/src/main/res/drawable/background_text_info.xml b/app/src/main/res/drawable/background_rounded_square.xml similarity index 100% rename from app/src/main/res/drawable/background_text_info.xml rename to app/src/main/res/drawable/background_rounded_square.xml diff --git a/app/src/main/res/layout/activity_group.xml b/app/src/main/res/layout/activity_group.xml index b477a88f4..7d0372d8d 100644 --- a/app/src/main/res/layout/activity_group.xml +++ b/app/src/main/res/layout/activity_group.xml @@ -39,12 +39,26 @@ android:layout_below="@+id/special_mode_view" android:background="?attr/colorPrimary" android:theme="?attr/toolbarAppearance" > - + android:orientation="horizontal"> + + + 12sp @color/white - @drawable/background_text_info + @drawable/background_rounded_square ?attr/colorAccent diff --git a/fastlane/metadata/android/en-US/changelogs/92.txt b/fastlane/metadata/android/en-US/changelogs/92.txt index ff0dfcf68..c54828038 100644 --- a/fastlane/metadata/android/en-US/changelogs/92.txt +++ b/fastlane/metadata/android/en-US/changelogs/92.txt @@ -1,6 +1,8 @@ * Add breadcrumb * Add path in search results #1148 * Add group info dialog #1177 + * Add database color #913 * Fix UI in Android 8 #509 * Upgrade libs and SDK to 31 #833 - * Stop asking WRITE_EXTERNAL_STORAGE permission \ No newline at end of file + * Stop asking WRITE_EXTERNAL_STORAGE permission + * Fix parser of database v1 \ No newline at end of file diff --git a/fastlane/metadata/android/fr-FR/changelogs/92.txt b/fastlane/metadata/android/fr-FR/changelogs/92.txt index ecf180cc5..a215414e4 100644 --- a/fastlane/metadata/android/fr-FR/changelogs/92.txt +++ b/fastlane/metadata/android/fr-FR/changelogs/92.txt @@ -1,6 +1,8 @@ * Ajout d'un fil d'ariane * Ajout du chemin pour les résultats de recherche #1148 * Ajout d'un dialogue pour les informations de groupe #1177 + * Ajout de couleur de base de données #913 * Correction de l'interface utilisateur dans Android 8 #509 * Mise à jour des librairies et du SDK vers 31 #833 - * Ne demande plus la permission WRITE_EXTERNAL_STORAGE permission \ No newline at end of file + * Ne demande plus la permission WRITE_EXTERNAL_STORAGE permission + * Correction du parseur de la base de données v1 \ No newline at end of file From 12c07cf79305320b3041df92479734266ffcd9eb Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Sat, 8 Jan 2022 20:22:39 +0100 Subject: [PATCH 04/28] Fix refresh database metadata --- .../keepass/activities/GroupActivity.kt | 10 ++++++++- .../activities/stylish/StylishActivity.kt | 6 +++--- .../settings/NestedAppSettingsFragment.kt | 4 ++-- .../NestedDatabaseSettingsFragment.kt | 21 +++++++++++++++++++ 4 files changed, 35 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt index c7da55580..b8559e0a0 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt @@ -418,7 +418,15 @@ class GroupActivity : DatabaseLockActivity(), // Search suggestion database?.let { databaseNameView?.text = if (it.name.isNotEmpty()) it.name else getString(R.string.database) - databaseColorView?.setColorFilter(Color.parseColor(it.customColor), PorterDuff.Mode.SRC_IN) + try { + databaseColorView?.visibility = View.VISIBLE + databaseColorView?.setColorFilter( + Color.parseColor(it.customColor), + PorterDuff.Mode.SRC_IN + ) + } catch (e: Exception) { + databaseColorView?.visibility = View.GONE + } mSearchSuggestionAdapter = SearchEntryCursorAdapter(this, it) mBreadcrumbAdapter?.iconDrawableFactory = it.iconDrawableFactory mOnSuggestionListener = object : SearchView.OnSuggestionListener { diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/stylish/StylishActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/stylish/StylishActivity.kt index 15f76e4f6..32904c342 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/stylish/StylishActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/stylish/StylishActivity.kt @@ -28,7 +28,7 @@ import android.util.Log import android.view.WindowManager import androidx.annotation.StyleRes import androidx.appcompat.app.AppCompatActivity -import com.kunzisoft.keepass.settings.NestedAppSettingsFragment.Companion.DATABASE_APPEARANCE_PREFERENCE_CHANGED +import com.kunzisoft.keepass.settings.NestedAppSettingsFragment.Companion.DATABASE_PREFERENCE_CHANGED /** * Stylish Hide Activity that apply a dynamic style and sets FLAG_SECURE to prevent screenshots / from @@ -89,8 +89,8 @@ abstract class StylishActivity : AppCompatActivity() { super.onResume() if ((customStyle && Stylish.getThemeId(this) != this.themeId) - || DATABASE_APPEARANCE_PREFERENCE_CHANGED) { - DATABASE_APPEARANCE_PREFERENCE_CHANGED = false + || DATABASE_PREFERENCE_CHANGED) { + DATABASE_PREFERENCE_CHANGED = false Log.d(this.javaClass.name, "Theme change detected, restarting activity") recreateActivity() } diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/NestedAppSettingsFragment.kt b/app/src/main/java/com/kunzisoft/keepass/settings/NestedAppSettingsFragment.kt index 291d12379..10bbb3c8a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/NestedAppSettingsFragment.kt +++ b/app/src/main/java/com/kunzisoft/keepass/settings/NestedAppSettingsFragment.kt @@ -452,7 +452,7 @@ class NestedAppSettingsFragment : NestedSettingsFragment() { getString(R.string.show_uuid_key), getString(R.string.enable_education_screens_key), getString(R.string.reset_education_screens_key) -> { - DATABASE_APPEARANCE_PREFERENCE_CHANGED = true + DATABASE_PREFERENCE_CHANGED = true } } return super.onPreferenceTreeClick(preference) @@ -516,6 +516,6 @@ class NestedAppSettingsFragment : NestedSettingsFragment() { companion object { private const val TAG_PREF_FRAGMENT = "TAG_PREF_FRAGMENT" - var DATABASE_APPEARANCE_PREFERENCE_CHANGED = false + var DATABASE_PREFERENCE_CHANGED = false } } \ No newline at end of file diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/NestedDatabaseSettingsFragment.kt b/app/src/main/java/com/kunzisoft/keepass/settings/NestedDatabaseSettingsFragment.kt index 7072fb4d9..dcda198f1 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/NestedDatabaseSettingsFragment.kt +++ b/app/src/main/java/com/kunzisoft/keepass/settings/NestedDatabaseSettingsFragment.kt @@ -671,6 +671,27 @@ class NestedDatabaseSettingsFragment : NestedSettingsFragment(), DatabaseRetriev } } + override fun onPreferenceTreeClick(preference: Preference?): Boolean { + // To reload group when database settings are modified + when (preference?.key) { + getString(R.string.database_name_key), + getString(R.string.database_description_key), + getString(R.string.database_default_username_key), + getString(R.string.database_custom_color_key), + getString(R.string.database_data_compression_key), + getString(R.string.database_data_remove_unlinked_attachments_key), + getString(R.string.recycle_bin_enable_key), + getString(R.string.recycle_bin_group_key), + getString(R.string.templates_group_enable_key), + getString(R.string.templates_group_uuid_key), + getString(R.string.max_history_items_key), + getString(R.string.max_history_size_key) -> { + NestedAppSettingsFragment.DATABASE_PREFERENCE_CHANGED = true + } + } + return super.onPreferenceTreeClick(preference) + } + companion object { private const val TAG_PREF_FRAGMENT = "TAG_PREF_FRAGMENT" } From f0f5258bc96ff70910a0cdad547b41f0c0b30f9a Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Sat, 8 Jan 2022 20:36:59 +0100 Subject: [PATCH 05/28] Fix refresh UI --- .../java/com/kunzisoft/keepass/activities/GroupActivity.kt | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt index b8559e0a0..8c8354aaa 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt @@ -328,9 +328,6 @@ class GroupActivity : DatabaseLockActivity(), } } - assignGroupViewElements(currentGroup) - invalidateOptionsMenu() - loadingView?.hideByFading() } @@ -597,9 +594,8 @@ class GroupActivity : DatabaseLockActivity(), setBreadcrumbNode(group) } } - - // Hide button initAddButton(group) + invalidateOptionsMenu() } private fun setBreadcrumbNode(group: Group?) { From c7b3e0926cd13584ac05e55aec7f73d11d11e278 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Sat, 8 Jan 2022 21:45:11 +0100 Subject: [PATCH 06/28] Fix save database color --- .../database/file/input/DatabaseInputKDB.kt | 9 ++++- .../database/file/output/DatabaseOutputKDB.kt | 35 ++++++++----------- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInputKDB.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInputKDB.kt index 49f886f68..857d1c0cc 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInputKDB.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInputKDB.kt @@ -20,6 +20,7 @@ package com.kunzisoft.keepass.database.file.input +import android.graphics.Color import com.kunzisoft.encrypt.HashManager import com.kunzisoft.keepass.database.crypto.EncryptionAlgorithm import com.kunzisoft.keepass.database.element.DateInstant @@ -311,7 +312,13 @@ class DatabaseInputKDB(cacheDirectory: File, var color: Int? = null entry.getBinary(mDatabase.attachmentPool) ?.getInputDataStream(mDatabase.binaryCache)?.use { - color = it.read() + val reverseColor = UnsignedInt(it.readBytes4ToUInt()).toKotlinInt() + color = Color.argb( + Color.alpha(255), + Color.blue(reverseColor), + Color.green(reverseColor), + Color.red(reverseColor) + ) } mDatabase.color = color } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDB.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDB.kt index 5a6fde286..07cd964b7 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDB.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDB.kt @@ -19,6 +19,7 @@ */ package com.kunzisoft.keepass.database.file.output +import android.graphics.Color import com.kunzisoft.encrypt.HashManager import com.kunzisoft.keepass.database.crypto.EncryptionAlgorithm import com.kunzisoft.keepass.database.element.database.DatabaseKDB @@ -47,9 +48,6 @@ class DatabaseOutputKDB(private val mDatabaseKDB: DatabaseKDB, private var mGroupList = mutableListOf() private var mEntryList = mutableListOf() - private var mDefaultUsernameAdded = false - private var mDatabaseColorAdded = false - @Throws(DatabaseOutputException::class) fun getFinalKey(header: DatabaseHeader): ByteArray? { try { @@ -216,7 +214,6 @@ class DatabaseOutputKDB(private val mDatabaseKDB: DatabaseKDB, private fun clearParser() { mGroupList.clear() mEntryList.clear() - mDefaultUsernameAdded = false } private fun sortNodesForOutput() { @@ -235,24 +232,14 @@ class DatabaseOutputKDB(private val mDatabaseKDB: DatabaseKDB, mGroupList.add(group) for (childEntry in group.getChildEntries()) { - if (childEntry.isMetaStreamDefaultUsername() - && mDatabaseKDB.defaultUserName.isNotEmpty()) { - setDefaultUsername(childEntry) - mEntryList.add(childEntry) - mDefaultUsernameAdded = true - } else if (childEntry.isMetaStreamDatabaseColor() - && mDatabaseKDB.color != null) { - setDatabaseColor(childEntry) - mEntryList.add(childEntry) - mDatabaseColorAdded = true - } else { + if (!childEntry.isMetaStreamDefaultUsername() + && !childEntry.isMetaStreamDatabaseColor()) { mEntryList.add(childEntry) } } // Add MetaStream - if (!mDefaultUsernameAdded - && mDatabaseKDB.defaultUserName.isNotEmpty()) { + if (mDatabaseKDB.defaultUserName.isNotEmpty()) { val metaEntry = EntryKDB().apply { setMetaStreamDefaultUsername() setDefaultUsername(this) @@ -260,8 +247,7 @@ class DatabaseOutputKDB(private val mDatabaseKDB: DatabaseKDB, mDatabaseKDB.addEntryTo(metaEntry, group) mEntryList.add(metaEntry) } - if (!mDatabaseColorAdded - && mDatabaseKDB.color != null) { + if (mDatabaseKDB.color != null) { val metaEntry = EntryKDB().apply { setMetaStreamDatabaseColor() setDatabaseColor(this) @@ -288,7 +274,16 @@ class DatabaseOutputKDB(private val mDatabaseKDB: DatabaseKDB, val binaryData = mDatabaseKDB.buildNewAttachment() entryKDB.putBinary(binaryData, mDatabaseKDB.attachmentPool) BufferedOutputStream(binaryData.getOutputDataStream(mDatabaseKDB.binaryCache)).use { outputStream -> - outputStream.write(mDatabaseKDB.color!!) + var reversColor = Color.BLACK + mDatabaseKDB.color?.let { + reversColor = Color.argb( + Color.alpha(255), + Color.blue(it), + Color.green(it), + Color.red(it) + ) + } + outputStream.write4BytesUInt(UnsignedInt(reversColor)) } } From a59f4d45ca5ce456636c34adc44ebcb701ac0a45 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Sat, 8 Jan 2022 22:29:43 +0100 Subject: [PATCH 07/28] Better color implementation --- .../keepass/activities/GroupActivity.kt | 8 +++---- .../keepass/database/element/Database.kt | 21 ++++++++++--------- .../database/element/database/DatabaseKDB.kt | 1 + .../database/file/input/DatabaseInputKDB.kt | 3 +-- .../database/file/output/DatabaseOutputKDB.kt | 3 +-- .../file/output/DatabaseOutputKDBX.kt | 1 - .../NestedDatabaseSettingsFragment.kt | 11 +++++----- ...baseColorPreferenceDialogFragmentCompat.kt | 13 ++++++------ ...abaseSavePreferenceDialogFragmentCompat.kt | 15 ++++++++++--- 9 files changed, 42 insertions(+), 34 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt index 8c8354aaa..5b8455680 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt @@ -25,7 +25,6 @@ import android.app.TimePickerDialog import android.content.ComponentName import android.content.Context import android.content.Intent -import android.graphics.Color import android.graphics.PorterDuff import android.os.* import android.util.Log @@ -415,13 +414,14 @@ class GroupActivity : DatabaseLockActivity(), // Search suggestion database?.let { databaseNameView?.text = if (it.name.isNotEmpty()) it.name else getString(R.string.database) - try { + val customColor = it.customColor + if (customColor != null) { databaseColorView?.visibility = View.VISIBLE databaseColorView?.setColorFilter( - Color.parseColor(it.customColor), + customColor, PorterDuff.Mode.SRC_IN ) - } catch (e: Exception) { + } else { databaseColorView?.visibility = View.GONE } mSearchSuggestionAdapter = SearchEntryCursorAdapter(this, it) 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 550bda556..5e5c6def2 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 @@ -236,22 +236,23 @@ class Database { mDatabaseKDBX?.defaultUserNameChanged = DateInstant() } - // with format "#000000" - var customColor: String + var customColor: Int? get() { - var colorString = "" - mDatabaseKDB?.color?.let { - colorString = ChromaUtil.getFormattedColorString(it, false) + var colorInt: Int? = null + mDatabaseKDBX?.color?.let { + try { + colorInt = Color.parseColor(it) + } catch (e: Exception) {} } - return mDatabaseKDBX?.color ?: colorString + return mDatabaseKDB?.color ?: colorInt } set(value) { - mDatabaseKDB?.color = if (value == "") { - null + mDatabaseKDB?.color = value + mDatabaseKDBX?.color = if (value == null) { + "" } else { - Color.parseColor(value) + ChromaUtil.getFormattedColorString(value, false) } - mDatabaseKDBX?.color = value } val allowOTP: Boolean diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDB.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDB.kt index 168c259b9..2bd047f8b 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDB.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDB.kt @@ -62,6 +62,7 @@ class DatabaseKDB : DatabaseVersioned() { } var defaultUserName: String = "" + var color: Int? = null override val kdfEngine: KdfEngine diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInputKDB.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInputKDB.kt index 857d1c0cc..1411d8a3b 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInputKDB.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInputKDB.kt @@ -313,8 +313,7 @@ class DatabaseInputKDB(cacheDirectory: File, entry.getBinary(mDatabase.attachmentPool) ?.getInputDataStream(mDatabase.binaryCache)?.use { val reverseColor = UnsignedInt(it.readBytes4ToUInt()).toKotlinInt() - color = Color.argb( - Color.alpha(255), + color = Color.rgb( Color.blue(reverseColor), Color.green(reverseColor), Color.red(reverseColor) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDB.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDB.kt index 07cd964b7..9b92122ca 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDB.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDB.kt @@ -276,8 +276,7 @@ class DatabaseOutputKDB(private val mDatabaseKDB: DatabaseKDB, BufferedOutputStream(binaryData.getOutputDataStream(mDatabaseKDB.binaryCache)).use { outputStream -> var reversColor = Color.BLACK mDatabaseKDB.color?.let { - reversColor = Color.argb( - Color.alpha(255), + reversColor = Color.rgb( Color.blue(it), Color.green(it), Color.red(it) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDBX.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDBX.kt index 474e441b5..885b882a6 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDBX.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDBX.kt @@ -48,7 +48,6 @@ import com.kunzisoft.keepass.database.file.DateKDBXUtil import com.kunzisoft.keepass.stream.HashedBlockOutputStream import com.kunzisoft.keepass.stream.HmacBlockOutputStream import com.kunzisoft.keepass.utils.* -import org.joda.time.DateTime import org.xmlpull.v1.XmlSerializer import java.io.IOException import java.io.OutputStream diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/NestedDatabaseSettingsFragment.kt b/app/src/main/java/com/kunzisoft/keepass/settings/NestedDatabaseSettingsFragment.kt index dcda198f1..a922ecd6b 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/NestedDatabaseSettingsFragment.kt +++ b/app/src/main/java/com/kunzisoft/keepass/settings/NestedDatabaseSettingsFragment.kt @@ -170,10 +170,11 @@ class NestedDatabaseSettingsFragment : NestedSettingsFragment(), DatabaseRetriev // Database custom color dbCustomColorPref = findPreference(getString(R.string.database_custom_color_key)) dbCustomColorPref?.apply { - try { - color = Color.parseColor(database.customColor) - summary = database.customColor - } catch (e: Exception) { + val customColor = database.customColor + if (customColor != null) { + color = customColor + summary = ChromaUtil.getFormattedColorString(customColor, false) + } else{ color = DialogColorPreference.DISABLE_COLOR summary = "" } @@ -416,7 +417,7 @@ class NestedDatabaseSettingsFragment : NestedSettingsFragment(), DatabaseRetriev if (result.isSuccess) { newColor } else { - mDatabase?.customColor = oldColor + mDatabase?.customColor = Color.parseColor(oldColor) oldColor } dbCustomColorPref?.summary = defaultColorToShow diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseColorPreferenceDialogFragmentCompat.kt b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseColorPreferenceDialogFragmentCompat.kt index 8eaeb9c0a..ebd20dd9f 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseColorPreferenceDialogFragmentCompat.kt +++ b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseColorPreferenceDialogFragmentCompat.kt @@ -30,7 +30,6 @@ import android.view.Window import android.widget.CompoundButton import androidx.annotation.ColorInt import androidx.appcompat.app.AlertDialog -import com.kunzisoft.androidclearchroma.ChromaUtil import com.kunzisoft.androidclearchroma.IndicatorMode import com.kunzisoft.androidclearchroma.colormode.ColorMode import com.kunzisoft.androidclearchroma.fragment.ChromaColorFragment @@ -77,12 +76,12 @@ class DatabaseColorPreferenceDialogFragmentCompat : DatabaseSavePreferenceDialog super.onDatabaseRetrieved(database) database?.let { - val initColor = try { + var initColor = it.customColor + if (initColor != null) { enableSwitchView.isChecked = true - Color.parseColor(it.customColor) - } catch (e: Exception) { + } else { enableSwitchView.isChecked = false - DEFAULT_COLOR + initColor = DEFAULT_COLOR } arguments?.putInt(ARG_INITIAL_COLOR, initColor) } @@ -107,9 +106,9 @@ class DatabaseColorPreferenceDialogFragmentCompat : DatabaseSavePreferenceDialog onColorSelectedListener?.invoke(customColorEnable, currentColor) database?.let { val newColor = if (customColorEnable) { - ChromaUtil.getFormattedColorString(currentColor, false) + currentColor } else { - "" + null } val oldColor = database.customColor database.customColor = newColor diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseSavePreferenceDialogFragmentCompat.kt b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseSavePreferenceDialogFragmentCompat.kt index b10afb610..06a7bae25 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseSavePreferenceDialogFragmentCompat.kt +++ b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseSavePreferenceDialogFragmentCompat.kt @@ -22,6 +22,7 @@ package com.kunzisoft.keepass.settings.preferencedialogfragment import android.content.Context import android.os.Bundle import androidx.fragment.app.activityViewModels +import com.kunzisoft.androidclearchroma.ChromaUtil import com.kunzisoft.keepass.activities.legacy.DatabaseRetrieval import com.kunzisoft.keepass.database.crypto.EncryptionAlgorithm import com.kunzisoft.keepass.database.crypto.kdf.KdfEngine @@ -76,9 +77,17 @@ abstract class DatabaseSavePreferenceDialogFragmentCompat // To inherit to save element in database } - protected fun saveColor(oldColor: String, - newColor: String) { - mDatabaseViewModel.saveColor(oldColor, newColor, mDatabaseAutoSaveEnable) + protected fun saveColor(oldColor: Int?, + newColor: Int?) { + val oldColorString = if (oldColor != null) + ChromaUtil.getFormattedColorString(oldColor, false) + else + "" + val newColorString = if (newColor != null) + ChromaUtil.getFormattedColorString(newColor, false) + else + "" + mDatabaseViewModel.saveColor(oldColorString, newColorString, mDatabaseAutoSaveEnable) } protected fun saveCompression(oldCompression: CompressionAlgorithm, From 6547f0ffadeb05683099219e9d57480ff1840c8a Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Tue, 11 Jan 2022 17:46:50 +0100 Subject: [PATCH 08/28] First entry color test --- .../main/res/layout/item_list_nodes_entry.xml | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/app/src/main/res/layout/item_list_nodes_entry.xml b/app/src/main/res/layout/item_list_nodes_entry.xml index 4d250ab6b..377d7e2aa 100644 --- a/app/src/main/res/layout/item_list_nodes_entry.xml +++ b/app/src/main/res/layout/item_list_nodes_entry.xml @@ -56,6 +56,43 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintLeft_toLeftOf="parent" /> + + + + Date: Tue, 11 Jan 2022 18:02:43 +0100 Subject: [PATCH 09/28] Invert info container visibility --- .../keepass/activities/PasswordActivity.kt | 20 +++++++++---------- app/src/main/res/layout/activity_password.xml | 1 + 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/PasswordActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/PasswordActivity.kt index 24067dd5e..79277962c 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/PasswordActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/PasswordActivity.kt @@ -101,15 +101,6 @@ class PasswordActivity : DatabaseModeActivity(), AdvancedUnlockFragment.BuilderL private var mReadOnly: Boolean = false private var mForceReadOnly: Boolean = false - set(value) { - infoContainerView?.visibility = if (value) { - mReadOnly = true - View.VISIBLE - } else { - View.GONE - } - field = value - } private var mAutofillActivityResultLauncher: ActivityResultLauncher? = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) @@ -204,10 +195,19 @@ class PasswordActivity : DatabaseModeActivity(), AdvancedUnlockFragment.BuilderL // Observe database file change mDatabaseFileViewModel.databaseFileLoaded.observe(this) { databaseFile -> + // Force read only if the file does not exists - mForceReadOnly = databaseFile?.let { + val databaseFileNotExists = databaseFile?.let { !it.databaseFileExists } ?: true + infoContainerView?.visibility = if (databaseFileNotExists) { + mReadOnly = true + View.VISIBLE + } else { + View.GONE + } + mForceReadOnly = databaseFileNotExists + invalidateOptionsMenu() // Post init uri with KeyFile only if needed diff --git a/app/src/main/res/layout/activity_password.xml b/app/src/main/res/layout/activity_password.xml index ee9e08253..d253d5624 100644 --- a/app/src/main/res/layout/activity_password.xml +++ b/app/src/main/res/layout/activity_password.xml @@ -220,6 +220,7 @@ android:id="@+id/activity_password_info_container" android:layout_width="match_parent" android:layout_height="wrap_content" + android:visibility="gone" android:orientation="vertical"> Date: Wed, 12 Jan 2022 14:19:04 +0100 Subject: [PATCH 10/28] Select entry colors --- .../keepass/activities/EntryEditActivity.kt | 11 +++ .../dialogs/ColorPickerDialogFragment.kt | 73 ++++++++++++++ .../activities/fragments/EntryEditFragment.kt | 14 +++ .../keepass/adapters/NodesAdapter.kt | 27 +++++- .../keepass/database/element/Entry.kt | 42 ++++++++ .../com/kunzisoft/keepass/model/EntryInfo.kt | 12 +++ .../keepass/view/TemplateAbstractView.kt | 4 + .../keepass/view/TemplateEditView.kt | 24 +++++ .../viewmodels/ColorPickerViewModel.kt | 15 +++ .../keepass/viewmodels/NodeEditViewModel.kt | 29 ++++++ .../ic_color_background_white_24dp.xml | 13 +++ .../ic_color_foreground_white_24dp.xml | 13 +++ app/src/main/res/layout/activity_group.xml | 2 +- .../main/res/layout/fragment_color_picker.xml | 50 ++++++++++ .../main/res/layout/item_list_nodes_entry.xml | 53 +++++----- app/src/main/res/layout/view_template.xml | 68 +++++++++---- app/src/main/res/values/strings.xml | 3 + art/ic_color_background.svg | 97 +++++++++++++++++++ art/ic_color_foreground.svg | 97 +++++++++++++++++++ 19 files changed, 593 insertions(+), 54 deletions(-) create mode 100644 app/src/main/java/com/kunzisoft/keepass/activities/dialogs/ColorPickerDialogFragment.kt create mode 100644 app/src/main/java/com/kunzisoft/keepass/viewmodels/ColorPickerViewModel.kt create mode 100644 app/src/main/res/drawable/ic_color_background_white_24dp.xml create mode 100644 app/src/main/res/drawable/ic_color_foreground_white_24dp.xml create mode 100644 app/src/main/res/layout/fragment_color_picker.xml create mode 100644 art/ic_color_background.svg create mode 100644 art/ic_color_foreground.svg 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 db73bf28d..8fb0cad02 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt @@ -74,6 +74,7 @@ import com.kunzisoft.keepass.tasks.AttachmentFileBinderManager import com.kunzisoft.keepass.timeout.TimeoutHelper import com.kunzisoft.keepass.utils.UriUtil import com.kunzisoft.keepass.view.* +import com.kunzisoft.keepass.viewmodels.ColorPickerViewModel import com.kunzisoft.keepass.viewmodels.EntryEditViewModel import org.joda.time.DateTime import java.util.* @@ -103,6 +104,8 @@ class EntryEditActivity : DatabaseLockActivity(), private var mEntryLoaded: Boolean = false private var mTemplatesSelectorAdapter: TemplatesSelectorAdapter? = null + private val mColorPickerViewModel: ColorPickerViewModel by viewModels() + private var mAllowCustomFields = false private var mAllowOTP = false @@ -243,6 +246,14 @@ class EntryEditActivity : DatabaseLockActivity(), IconPickerActivity.launch(this@EntryEditActivity, iconImage, mIconSelectionActivityResultLauncher) } + mEntryEditViewModel.requestColorSelection.observe(this) { color -> + ColorPickerDialogFragment().show(supportFragmentManager, "ColorPickerFragment") + } + + mColorPickerViewModel.colorPicked.observe(this) { color -> + mEntryEditViewModel.selectColor(color) + } + mEntryEditViewModel.requestDateTimeSelection.observe(this) { dateInstant -> if (dateInstant.type == DateInstant.Type.TIME) { // Launch the time picker diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/ColorPickerDialogFragment.kt b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/ColorPickerDialogFragment.kt new file mode 100644 index 000000000..e11010cbc --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/ColorPickerDialogFragment.kt @@ -0,0 +1,73 @@ +package com.kunzisoft.keepass.activities.dialogs + +import android.app.Dialog +import android.graphics.Color +import android.os.Bundle +import android.widget.CompoundButton +import androidx.annotation.ColorInt +import androidx.appcompat.app.AlertDialog +import androidx.fragment.app.activityViewModels +import com.kunzisoft.androidclearchroma.view.ChromaColorView +import com.kunzisoft.keepass.R +import com.kunzisoft.keepass.viewmodels.ColorPickerViewModel + +class ColorPickerDialogFragment : DatabaseDialogFragment() { + + private val mColorPickerViewModel: ColorPickerViewModel by activityViewModels() + + private lateinit var enableSwitchView: CompoundButton + private lateinit var chromaColorView: ChromaColorView + + private var mDefaultColor = Color.WHITE + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + + activity?.let { activity -> + val root = activity.layoutInflater.inflate(R.layout.fragment_color_picker, null) + enableSwitchView = root.findViewById(R.id.switch_element) + chromaColorView = root.findViewById(R.id.chroma_color_view) + + var activated = false + arguments?.apply { + if (containsKey(ARG_INITIAL_COLOR)) { + mDefaultColor = getInt(ARG_INITIAL_COLOR) + } + if (containsKey(ARG_ACTIVATED)) { + activated = getBoolean(ARG_ACTIVATED) + } + } + enableSwitchView.isChecked = activated + + val builder = AlertDialog.Builder(activity) + builder.setView(root) + .setPositiveButton(android.R.string.ok) { _, _ -> + val color: Int? = if (enableSwitchView.isChecked) + chromaColorView.currentColor + else + null + mColorPickerViewModel.pickColor(color) + } + .setNegativeButton(android.R.string.cancel) { _, _ -> + // Do nothing + } + + return builder.create() + } + return super.onCreateDialog(savedInstanceState) + } + + companion object { + private const val ARG_INITIAL_COLOR = "ARG_INITIAL_COLOR" + private const val ARG_ACTIVATED = "ARG_ACTIVATED" + + fun newInstance( + @ColorInt initialColor: Int?, + ): ColorPickerDialogFragment { + val colorPickerDialogFragment = ColorPickerDialogFragment() + val args = Bundle() + args.putInt(ARG_INITIAL_COLOR, initialColor ?: Color.WHITE) + args.putBoolean(ARG_ACTIVATED, initialColor != null) + return colorPickerDialogFragment + } + } +} \ No newline at end of file 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 1d8bea4bf..4f31ad15a 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 @@ -99,6 +99,12 @@ class EntryEditFragment: DatabaseFragment() { setOnIconClickListener { mEntryEditViewModel.requestIconSelection(templateView.getIcon()) } + setOnBackgroundColorClickListener { + mEntryEditViewModel.requestBackgroundColorSelection(templateView.getBackgroundColor()) + } + setOnForegroundColorClickListener { + mEntryEditViewModel.requestForegroundColorSelection(templateView.getForegroundColor()) + } setOnCustomEditionActionClickListener { field -> mEntryEditViewModel.requestCustomFieldEdition(field) } @@ -147,6 +153,14 @@ class EntryEditFragment: DatabaseFragment() { templateView.setIcon(iconImage) } + mEntryEditViewModel.onBackgroundColorSelected.observe(this) { color -> + templateView.setBackgroundColor(color) + } + + mEntryEditViewModel.onForegroundColorSelected.observe(this) { color -> + templateView.setForegroundColor(color) + } + mEntryEditViewModel.onPasswordSelected.observe(viewLifecycleOwner) { passwordField -> templateView.setPasswordField(passwordField) } diff --git a/app/src/main/java/com/kunzisoft/keepass/adapters/NodesAdapter.kt b/app/src/main/java/com/kunzisoft/keepass/adapters/NodesAdapter.kt index 6018b49bd..892301c39 100644 --- a/app/src/main/java/com/kunzisoft/keepass/adapters/NodesAdapter.kt +++ b/app/src/main/java/com/kunzisoft/keepass/adapters/NodesAdapter.kt @@ -21,6 +21,7 @@ package com.kunzisoft.keepass.adapters import android.content.Context import android.graphics.Color +import android.graphics.PorterDuff import android.util.TypedValue import android.view.LayoutInflater import android.view.View @@ -31,6 +32,7 @@ import android.widget.TextView import android.widget.Toast import androidx.annotation.ColorInt import androidx.core.content.ContextCompat +import androidx.core.view.isVisible import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.SortedList import androidx.recyclerview.widget.SortedListAdapterCallback @@ -39,7 +41,9 @@ import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.database.element.Entry import com.kunzisoft.keepass.database.element.Group import com.kunzisoft.keepass.database.element.SortNodeEnum -import com.kunzisoft.keepass.database.element.node.* +import com.kunzisoft.keepass.database.element.node.Node +import com.kunzisoft.keepass.database.element.node.NodeVersionedInterface +import com.kunzisoft.keepass.database.element.node.Type import com.kunzisoft.keepass.database.element.template.TemplateField import com.kunzisoft.keepass.otp.OtpElement import com.kunzisoft.keepass.otp.OtpType @@ -377,6 +381,25 @@ class NodesAdapter (private val context: Context, val entry = subNode as Entry database.startManageEntry(entry) + // Assign colors + val backgroundColor = entry.backgroundColor + if (backgroundColor != null) { + holder.backgroundView?.setColorFilter(backgroundColor, PorterDuff.Mode.SRC_ATOP) + holder.backgroundView?.isVisible = true + } else { + holder.backgroundView?.isVisible = false + } + val foregroundColor = entry.foregroundColor + if (foregroundColor != null) { + holder.foregroundView?.setColorFilter(foregroundColor, PorterDuff.Mode.SRC_ATOP) + holder.icon.apply { + database.iconDrawableFactory.assignDatabaseIcon(this, subNode.icon, foregroundColor) + } + holder.foregroundView?.isVisible = true + } else { + holder.foregroundView?.isVisible = false + } + holder.text.text = entry.getVisualTitle() // Add subText with username holder.subText?.apply { @@ -510,6 +533,8 @@ class NodesAdapter (private val context: Context, var container: View = itemView.findViewById(R.id.node_container) var imageIdentifier: ImageView? = itemView.findViewById(R.id.node_image_identifier) var icon: ImageView = itemView.findViewById(R.id.node_icon) + var backgroundView: ImageView? = itemView.findViewById(R.id.background_view) + var foregroundView: ImageView? = itemView.findViewById(R.id.foreground_view) var text: TextView = itemView.findViewById(R.id.node_text) var subText: TextView? = itemView.findViewById(R.id.node_subtext) var meta: TextView = itemView.findViewById(R.id.node_meta) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/Entry.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/Entry.kt index 83e2707a0..65bcef378 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/Entry.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/Entry.kt @@ -19,8 +19,10 @@ */ package com.kunzisoft.keepass.database.element +import android.graphics.Color import android.os.Parcel import android.os.Parcelable +import com.kunzisoft.androidclearchroma.ChromaUtil import com.kunzisoft.keepass.database.element.binary.AttachmentPool import com.kunzisoft.keepass.database.element.database.DatabaseKDBX import com.kunzisoft.keepass.database.element.database.DatabaseVersioned @@ -238,6 +240,42 @@ class Entry : Node, EntryVersionedInterface { entryKDBX?.notes = value } + var backgroundColor: Int? + get() { + var colorInt: Int? = null + entryKDBX?.backgroundColor?.let { + try { + colorInt = Color.parseColor(it) + } catch (e: Exception) {} + } + return colorInt + } + set(value) { + entryKDBX?.backgroundColor = if (value == null) { + "" + } else { + ChromaUtil.getFormattedColorString(value, false) + } + } + + var foregroundColor: Int? + get() { + var colorInt: Int? = null + entryKDBX?.foregroundColor?.let { + try { + colorInt = Color.parseColor(it) + } catch (e: Exception) {} + } + return colorInt + } + set(value) { + entryKDBX?.foregroundColor = if (value == null) { + "" + } else { + ChromaUtil.getFormattedColorString(value, false) + } + } + private fun isTan(): Boolean { return title == PMS_TAN_ENTRY && username.isNotEmpty() } @@ -419,6 +457,8 @@ class Entry : Node, EntryVersionedInterface { entryInfo.expiryTime = expiryTime entryInfo.url = url entryInfo.notes = notes + entryInfo.backgroundColor = backgroundColor + entryInfo.foregroundColor = foregroundColor entryInfo.customFields = getExtraFields().toMutableList() // Add otpElement to generate token entryInfo.otpModel = getOtpElement()?.otpModel @@ -453,6 +493,8 @@ class Entry : Node, EntryVersionedInterface { expiryTime = newEntryInfo.expiryTime url = newEntryInfo.url notes = newEntryInfo.notes + backgroundColor = newEntryInfo.backgroundColor + foregroundColor = newEntryInfo.foregroundColor addExtraFields(newEntryInfo.customFields) database?.attachmentPool?.let { binaryPool -> newEntryInfo.attachments.forEach { attachment -> diff --git a/app/src/main/java/com/kunzisoft/keepass/model/EntryInfo.kt b/app/src/main/java/com/kunzisoft/keepass/model/EntryInfo.kt index 9324ea9a8..5ae5fc0fc 100644 --- a/app/src/main/java/com/kunzisoft/keepass/model/EntryInfo.kt +++ b/app/src/main/java/com/kunzisoft/keepass/model/EntryInfo.kt @@ -40,6 +40,8 @@ class EntryInfo : NodeInfo { var password: String = "" var url: String = "" var notes: String = "" + var backgroundColor: Int? = null + var foregroundColor: Int? = null var customFields: MutableList = mutableListOf() var attachments: MutableList = mutableListOf() var otpModel: OtpModel? = null @@ -53,6 +55,10 @@ class EntryInfo : NodeInfo { password = parcel.readString() ?: password url = parcel.readString() ?: url notes = parcel.readString() ?: notes + val readBgColor = parcel.readInt() + backgroundColor = if (readBgColor == -1) null else readBgColor + val readFgColor = parcel.readInt() + foregroundColor = if (readFgColor == -1) null else readFgColor parcel.readList(customFields, Field::class.java.classLoader) parcel.readList(attachments, Attachment::class.java.classLoader) otpModel = parcel.readParcelable(OtpModel::class.java.classLoader) ?: otpModel @@ -70,6 +76,8 @@ class EntryInfo : NodeInfo { parcel.writeString(password) parcel.writeString(url) parcel.writeString(notes) + parcel.writeInt(backgroundColor ?: -1) + parcel.writeInt(foregroundColor ?: -1) parcel.writeList(customFields) parcel.writeList(attachments) parcel.writeParcelable(otpModel, flags) @@ -196,6 +204,8 @@ class EntryInfo : NodeInfo { if (password != other.password) return false if (url != other.url) return false if (notes != other.notes) return false + if (backgroundColor != other.backgroundColor) return false + if (foregroundColor != other.foregroundColor) return false if (customFields != other.customFields) return false if (attachments != other.attachments) return false if (otpModel != other.otpModel) return false @@ -211,6 +221,8 @@ class EntryInfo : NodeInfo { result = 31 * result + password.hashCode() result = 31 * result + url.hashCode() result = 31 * result + notes.hashCode() + result = 31 * result + backgroundColor.hashCode() + result = 31 * result + foregroundColor.hashCode() result = 31 * result + customFields.hashCode() result = 31 * result + attachments.hashCode() result = 31 * result + (otpModel?.hashCode() ?: 0) diff --git a/app/src/main/java/com/kunzisoft/keepass/view/TemplateAbstractView.kt b/app/src/main/java/com/kunzisoft/keepass/view/TemplateAbstractView.kt index 5a8697daa..c8271d7d3 100644 --- a/app/src/main/java/com/kunzisoft/keepass/view/TemplateAbstractView.kt +++ b/app/src/main/java/com/kunzisoft/keepass/view/TemplateAbstractView.kt @@ -46,6 +46,8 @@ abstract class TemplateAbstractView< protected var headerContainerView: ViewGroup protected var entryIconView: ImageView + protected var backgroundColorButton: ImageView + protected var foregroundColorButton: ImageView private var titleContainerView: ViewGroup protected var templateContainerView: ViewGroup private var customFieldsContainerView: SectionView @@ -57,6 +59,8 @@ abstract class TemplateAbstractView< headerContainerView = findViewById(R.id.template_header_container) entryIconView = findViewById(R.id.template_icon_button) + backgroundColorButton = findViewById(R.id.template_background_color_button) + foregroundColorButton = findViewById(R.id.template_foreground_color_button) titleContainerView = findViewById(R.id.template_title_container) templateContainerView = findViewById(R.id.template_fields_container) // To fix card view margin below Marshmallow 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 d3918be16..e2a32b0fd 100644 --- a/app/src/main/java/com/kunzisoft/keepass/view/TemplateEditView.kt +++ b/app/src/main/java/com/kunzisoft/keepass/view/TemplateEditView.kt @@ -54,6 +54,30 @@ class TemplateEditView @JvmOverloads constructor(context: Context, populateIconMethod?.invoke(entryIconView, iconImage) } + fun setOnBackgroundColorClickListener(onClickListener: OnClickListener) { + backgroundColorButton.setOnClickListener(onClickListener) + } + + fun getBackgroundColor(): Int? { + return mEntryInfo?.backgroundColor + } + + fun setBackgroundColor(color: Int?) { + mEntryInfo?.backgroundColor = color + } + + fun setOnForegroundColorClickListener(onClickListener: OnClickListener) { + foregroundColorButton.setOnClickListener(onClickListener) + } + + fun getForegroundColor(): Int? { + return mEntryInfo?.foregroundColor + } + + fun setForegroundColor(color: Int?) { + mEntryInfo?.foregroundColor = color + } + override fun preProcessTemplate() { headerContainerView.isVisible = true } diff --git a/app/src/main/java/com/kunzisoft/keepass/viewmodels/ColorPickerViewModel.kt b/app/src/main/java/com/kunzisoft/keepass/viewmodels/ColorPickerViewModel.kt new file mode 100644 index 000000000..282e3f425 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/viewmodels/ColorPickerViewModel.kt @@ -0,0 +1,15 @@ +package com.kunzisoft.keepass.viewmodels + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel + +class ColorPickerViewModel: ViewModel() { + + val colorPicked : LiveData get() = _colorPicked + private val _colorPicked = MutableLiveData() + + fun pickColor(color: Int?) { + _colorPicked.value = color + } +} \ No newline at end of file diff --git a/app/src/main/java/com/kunzisoft/keepass/viewmodels/NodeEditViewModel.kt b/app/src/main/java/com/kunzisoft/keepass/viewmodels/NodeEditViewModel.kt index 80d9ea79a..4c55af246 100644 --- a/app/src/main/java/com/kunzisoft/keepass/viewmodels/NodeEditViewModel.kt +++ b/app/src/main/java/com/kunzisoft/keepass/viewmodels/NodeEditViewModel.kt @@ -14,6 +14,14 @@ abstract class NodeEditViewModel : ViewModel() { val onIconSelected : LiveData get() = _onIconSelected private val _onIconSelected = SingleLiveEvent() + private var mColorRequest: ColorRequest = ColorRequest.BACKGROUND + val requestColorSelection : LiveData get() = _requestColorSelection + private val _requestColorSelection = SingleLiveEvent() + val onBackgroundColorSelected : LiveData get() = _onBackgroundColorSelected + private val _onBackgroundColorSelected = SingleLiveEvent() + val onForegroundColorSelected : LiveData get() = _onForegroundColorSelected + private val _onForegroundColorSelected = SingleLiveEvent() + val requestDateTimeSelection : LiveData get() = _requestDateTimeSelection private val _requestDateTimeSelection = SingleLiveEvent() val onDateSelected : LiveData get() = _onDateSelected @@ -29,6 +37,23 @@ abstract class NodeEditViewModel : ViewModel() { _onIconSelected.value = iconImage } + fun requestBackgroundColorSelection(initialColor: Int?) { + mColorRequest = ColorRequest.BACKGROUND + _requestColorSelection.value = initialColor + } + + fun requestForegroundColorSelection(initialColor: Int?) { + mColorRequest = ColorRequest.FOREGROUND + _requestColorSelection.value = initialColor + } + + fun selectColor(color: Int?) { + when (mColorRequest) { + ColorRequest.BACKGROUND -> _onBackgroundColorSelected.value = color + ColorRequest.FOREGROUND -> _onForegroundColorSelected.value = color + } + } + fun requestDateTimeSelection(dateInstant: DateInstant) { _requestDateTimeSelection.value = dateInstant } @@ -40,4 +65,8 @@ abstract class NodeEditViewModel : ViewModel() { fun selectTime(hours: Int, minutes: Int) { _onTimeSelected.value = DataTime(hours, minutes) } + + private enum class ColorRequest { + BACKGROUND, FOREGROUND + } } \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_color_background_white_24dp.xml b/app/src/main/res/drawable/ic_color_background_white_24dp.xml new file mode 100644 index 000000000..ea4549a72 --- /dev/null +++ b/app/src/main/res/drawable/ic_color_background_white_24dp.xml @@ -0,0 +1,13 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_color_foreground_white_24dp.xml b/app/src/main/res/drawable/ic_color_foreground_white_24dp.xml new file mode 100644 index 000000000..558ef76ec --- /dev/null +++ b/app/src/main/res/drawable/ic_color_foreground_white_24dp.xml @@ -0,0 +1,13 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_group.xml b/app/src/main/res/layout/activity_group.xml index 7d0372d8d..3edfdcbd6 100644 --- a/app/src/main/res/layout/activity_group.xml +++ b/app/src/main/res/layout/activity_group.xml @@ -51,7 +51,7 @@ android:src="@drawable/background_rounded_square" android:layout_marginRight="6dp" android:layout_marginEnd="6dp" - android:contentDescription="@string/database"/> + android:contentDescription="@string/content_description_database_color"/> + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_list_nodes_entry.xml b/app/src/main/res/layout/item_list_nodes_entry.xml index 377d7e2aa..e32c28440 100644 --- a/app/src/main/res/layout/item_list_nodes_entry.xml +++ b/app/src/main/res/layout/item_list_nodes_entry.xml @@ -38,6 +38,20 @@ app:layout_constraintEnd_toEndOf="parent" android:background="?android:attr/selectableItemBackground" > + + - - + android:contentDescription="@string/content_description_entry_foreground_color" + android:src="@drawable/background_rounded_square" /> . --> - - + + android:layout_height="wrap_content"> + - - + + + + + + - - - - + android:layout_gravity="end"> + + + + Keyfile checkbox Repeat toggle password visibility Entry icon + Database color + Entry foreground color + Entry background color Validate Discard changes? Discard diff --git a/art/ic_color_background.svg b/art/ic_color_background.svg new file mode 100644 index 000000000..76548ca12 --- /dev/null +++ b/art/ic_color_background.svg @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/art/ic_color_foreground.svg b/art/ic_color_foreground.svg new file mode 100644 index 000000000..db19395c5 --- /dev/null +++ b/art/ic_color_foreground.svg @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + From 05fad24edaa36c61e0df43721d7265a06da8bfdb Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Wed, 12 Jan 2022 17:01:35 +0100 Subject: [PATCH 11/28] Fix color picker fragment --- .../keepass/activities/EntryEditActivity.kt | 3 +- .../dialogs/ColorPickerDialogFragment.kt | 41 +++++++++++++------ 2 files changed, 31 insertions(+), 13 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 8fb0cad02..3afc45150 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt @@ -247,7 +247,8 @@ class EntryEditActivity : DatabaseLockActivity(), } mEntryEditViewModel.requestColorSelection.observe(this) { color -> - ColorPickerDialogFragment().show(supportFragmentManager, "ColorPickerFragment") + ColorPickerDialogFragment.newInstance(color) + .show(supportFragmentManager, "ColorPickerFragment") } mColorPickerViewModel.colorPicked.observe(this) { color -> diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/ColorPickerDialogFragment.kt b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/ColorPickerDialogFragment.kt index e11010cbc..180a81a53 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/ColorPickerDialogFragment.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/ColorPickerDialogFragment.kt @@ -19,6 +19,7 @@ class ColorPickerDialogFragment : DatabaseDialogFragment() { private lateinit var chromaColorView: ChromaColorView private var mDefaultColor = Color.WHITE + private var mActivated = false override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { @@ -27,16 +28,25 @@ class ColorPickerDialogFragment : DatabaseDialogFragment() { enableSwitchView = root.findViewById(R.id.switch_element) chromaColorView = root.findViewById(R.id.chroma_color_view) - var activated = false - arguments?.apply { - if (containsKey(ARG_INITIAL_COLOR)) { - mDefaultColor = getInt(ARG_INITIAL_COLOR) + if (savedInstanceState != null) { + if (savedInstanceState.containsKey(ARG_INITIAL_COLOR)) { + mDefaultColor = savedInstanceState.getInt(ARG_INITIAL_COLOR) } - if (containsKey(ARG_ACTIVATED)) { - activated = getBoolean(ARG_ACTIVATED) + if (savedInstanceState.containsKey(ARG_ACTIVATED)) { + mActivated = savedInstanceState.getBoolean(ARG_ACTIVATED) + } + } else { + arguments?.apply { + if (containsKey(ARG_INITIAL_COLOR)) { + mDefaultColor = getInt(ARG_INITIAL_COLOR) + } + if (containsKey(ARG_ACTIVATED)) { + mActivated = getBoolean(ARG_ACTIVATED) + } } } - enableSwitchView.isChecked = activated + enableSwitchView.isChecked = mActivated + chromaColorView.currentColor = mDefaultColor val builder = AlertDialog.Builder(activity) builder.setView(root) @@ -56,6 +66,12 @@ class ColorPickerDialogFragment : DatabaseDialogFragment() { return super.onCreateDialog(savedInstanceState) } + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putInt(ARG_INITIAL_COLOR, chromaColorView.currentColor) + outState.putBoolean(ARG_ACTIVATED, mActivated) + } + companion object { private const val ARG_INITIAL_COLOR = "ARG_INITIAL_COLOR" private const val ARG_ACTIVATED = "ARG_ACTIVATED" @@ -63,11 +79,12 @@ class ColorPickerDialogFragment : DatabaseDialogFragment() { fun newInstance( @ColorInt initialColor: Int?, ): ColorPickerDialogFragment { - val colorPickerDialogFragment = ColorPickerDialogFragment() - val args = Bundle() - args.putInt(ARG_INITIAL_COLOR, initialColor ?: Color.WHITE) - args.putBoolean(ARG_ACTIVATED, initialColor != null) - return colorPickerDialogFragment + return ColorPickerDialogFragment().apply { + arguments = Bundle().apply { + putInt(ARG_INITIAL_COLOR, initialColor ?: Color.WHITE) + putBoolean(ARG_ACTIVATED, initialColor != null) + } + } } } } \ No newline at end of file From cfdc0237d70171b566c088abd72bb9b680188a8a Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Wed, 12 Jan 2022 19:50:38 +0100 Subject: [PATCH 12/28] Add foreground and background colors in list --- .../keepass/adapters/NodesAdapter.kt | 122 +++++++++++++----- .../keepass/view/TemplateEditView.kt | 14 ++ app/src/main/res/layout/item_icon.xml | 2 +- .../main/res/layout/item_list_nodes_entry.xml | 107 ++++++--------- app/src/main/res/layout/view_template.xml | 47 ++++--- app/src/main/res/values/styles.xml | 11 +- 6 files changed, 174 insertions(+), 129 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/adapters/NodesAdapter.kt b/app/src/main/java/com/kunzisoft/keepass/adapters/NodesAdapter.kt index 892301c39..c81171923 100644 --- a/app/src/main/java/com/kunzisoft/keepass/adapters/NodesAdapter.kt +++ b/app/src/main/java/com/kunzisoft/keepass/adapters/NodesAdapter.kt @@ -21,7 +21,6 @@ package com.kunzisoft.keepass.adapters import android.content.Context import android.graphics.Color -import android.graphics.PorterDuff import android.util.TypedValue import android.view.LayoutInflater import android.view.View @@ -32,10 +31,10 @@ import android.widget.TextView import android.widget.Toast import androidx.annotation.ColorInt import androidx.core.content.ContextCompat -import androidx.core.view.isVisible import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.SortedList import androidx.recyclerview.widget.SortedListAdapterCallback +import com.google.android.material.progressindicator.CircularProgressIndicator import com.kunzisoft.keepass.R import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.database.element.Entry @@ -91,9 +90,15 @@ class NodesAdapter (private val context: Context, @ColorInt private val mContentSelectionColor: Int @ColorInt - private val mIconGroupColor: Int + private val mTextColorPrimary: Int @ColorInt - private val mIconEntryColor: Int + private val mTextColor: Int + @ColorInt + private val mTextColorSecondary: Int + @ColorInt + private val mColorAccentLight: Int + @ColorInt + private val mTextColorInverse: Int /** * Determine if the adapter contains or not any element @@ -114,12 +119,24 @@ class NodesAdapter (private val context: Context, this.mContentSelectionColor = ContextCompat.getColor(context, R.color.white) // Retrieve the color to tint the icon val taTextColorPrimary = context.theme.obtainStyledAttributes(intArrayOf(android.R.attr.textColorPrimary)) - this.mIconGroupColor = taTextColorPrimary.getColor(0, Color.BLACK) + this.mTextColorPrimary = taTextColorPrimary.getColor(0, Color.BLACK) taTextColorPrimary.recycle() - // In two times to fix bug compilation + // To get text color val taTextColor = context.theme.obtainStyledAttributes(intArrayOf(android.R.attr.textColor)) - this.mIconEntryColor = taTextColor.getColor(0, Color.BLACK) + this.mTextColor = taTextColor.getColor(0, Color.BLACK) taTextColor.recycle() + // To get text color secondary + val taTextColorSecondary = context.theme.obtainStyledAttributes(intArrayOf(android.R.attr.textColorSecondary)) + this.mTextColorSecondary = taTextColorSecondary.getColor(0, Color.BLACK) + taTextColorSecondary.recycle() + // To get background color for selection + val taSelectionColor = context.theme.obtainStyledAttributes(intArrayOf(R.attr.colorAccentLight)) + this.mColorAccentLight = taSelectionColor.getColor(0, Color.GRAY) + taSelectionColor.recycle() + // To get text color for selection + val taSelectionTextColor = context.theme.obtainStyledAttributes(intArrayOf(R.attr.textColorInverse)) + this.mTextColorInverse = taSelectionTextColor.getColor(0, Color.WHITE) + taSelectionTextColor.recycle() } private fun assignPreferences() { @@ -167,6 +184,8 @@ class NodesAdapter (private val context: Context, if (oldItem is Entry && newItem is Entry) { typeContentTheSame = oldItem.getVisualTitle() == newItem.getVisualTitle() && oldItem.username == newItem.username + && oldItem.backgroundColor == newItem.backgroundColor + && oldItem.foregroundColor == newItem.foregroundColor && oldItem.getOtpElement() == newItem.getOtpElement() && oldItem.containsAttachment() == newItem.containsAttachment() } else if (oldItem is Group && newItem is Group) { @@ -336,8 +355,8 @@ class NodesAdapter (private val context: Context, val iconColor = if (holder.container.isSelected) mContentSelectionColor else when (subNode.type) { - Type.GROUP -> mIconGroupColor - Type.ENTRY -> mIconEntryColor + Type.GROUP -> mTextColorPrimary + Type.ENTRY -> mTextColor } holder.imageIdentifier?.setColorFilter(iconColor) holder.icon.apply { @@ -381,25 +400,6 @@ class NodesAdapter (private val context: Context, val entry = subNode as Entry database.startManageEntry(entry) - // Assign colors - val backgroundColor = entry.backgroundColor - if (backgroundColor != null) { - holder.backgroundView?.setColorFilter(backgroundColor, PorterDuff.Mode.SRC_ATOP) - holder.backgroundView?.isVisible = true - } else { - holder.backgroundView?.isVisible = false - } - val foregroundColor = entry.foregroundColor - if (foregroundColor != null) { - holder.foregroundView?.setColorFilter(foregroundColor, PorterDuff.Mode.SRC_ATOP) - holder.icon.apply { - database.iconDrawableFactory.assignDatabaseIcon(this, subNode.icon, foregroundColor) - } - holder.foregroundView?.isVisible = true - } else { - holder.foregroundView?.isVisible = false - } - holder.text.text = entry.getVisualTitle() // Add subText with username holder.subText?.apply { @@ -436,6 +436,64 @@ class NodesAdapter (private val context: Context, holder.attachmentIcon?.visibility = if (entry.containsAttachment()) View.VISIBLE else View.GONE + // Assign colors + val backgroundColor = entry.backgroundColor + if (!holder.container.isSelected) { + if (backgroundColor != null) { + holder.container.setBackgroundColor(backgroundColor) + } else { + holder.container.setBackgroundColor(Color.TRANSPARENT) + } + } else { + holder.container.setBackgroundColor(mColorAccentLight) + } + val foregroundColor = entry.foregroundColor + if (!holder.container.isSelected) { + if (foregroundColor != null) { + holder.text.setTextColor(foregroundColor) + holder.subText?.setTextColor(foregroundColor) + holder.otpToken?.setTextColor(foregroundColor) + holder.otpProgress?.setIndicatorColor(foregroundColor) + holder.attachmentIcon?.setColorFilter(foregroundColor) + holder.meta.setTextColor(foregroundColor) + holder.icon.apply { + database.iconDrawableFactory.assignDatabaseIcon( + this, + subNode.icon, + foregroundColor + ) + } + } else { + holder.text.setTextColor(mTextColor) + holder.subText?.setTextColor(mTextColorSecondary) + holder.otpToken?.setTextColor(mTextColorSecondary) + holder.otpProgress?.setIndicatorColor(mTextColorSecondary) + holder.attachmentIcon?.setColorFilter(mTextColorSecondary) + holder.meta.setTextColor(mTextColor) + holder.icon.apply { + database.iconDrawableFactory.assignDatabaseIcon( + this, + subNode.icon, + mTextColor + ) + } + } + } else { + holder.text.setTextColor(mTextColorInverse) + holder.subText?.setTextColor(mTextColorInverse) + holder.otpToken?.setTextColor(mTextColorInverse) + holder.otpProgress?.setIndicatorColor(mTextColorInverse) + holder.attachmentIcon?.setColorFilter(mTextColorInverse) + holder.meta.setTextColor(mTextColorInverse) + holder.icon.apply { + database.iconDrawableFactory.assignDatabaseIcon( + this, + subNode.icon, + mTextColorInverse + ) + } + } + database.stopManageEntry(entry) } @@ -468,13 +526,13 @@ class NodesAdapter (private val context: Context, OtpType.HOTP -> { holder?.otpProgress?.apply { max = 100 - progress = 100 + setProgressCompat(100, true) } } OtpType.TOTP -> { holder?.otpProgress?.apply { max = otpElement.period - progress = otpElement.secondsRemaining + setProgressCompat(otpElement.secondsRemaining, true) } } null -> {} @@ -533,14 +591,12 @@ class NodesAdapter (private val context: Context, var container: View = itemView.findViewById(R.id.node_container) var imageIdentifier: ImageView? = itemView.findViewById(R.id.node_image_identifier) var icon: ImageView = itemView.findViewById(R.id.node_icon) - var backgroundView: ImageView? = itemView.findViewById(R.id.background_view) - var foregroundView: ImageView? = itemView.findViewById(R.id.foreground_view) var text: TextView = itemView.findViewById(R.id.node_text) var subText: TextView? = itemView.findViewById(R.id.node_subtext) var meta: TextView = itemView.findViewById(R.id.node_meta) var path: TextView? = itemView.findViewById(R.id.node_path) var otpContainer: ViewGroup? = itemView.findViewById(R.id.node_otp_container) - var otpProgress: ProgressBar? = itemView.findViewById(R.id.node_otp_progress) + var otpProgress: CircularProgressIndicator? = itemView.findViewById(R.id.node_otp_progress) var otpToken: TextView? = itemView.findViewById(R.id.node_otp_token) var otpRunnable: OtpRunnable = OtpRunnable(otpContainer) var numberChildren: TextView? = itemView.findViewById(R.id.node_child_numbers) 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 e2a32b0fd..7d5c76a06 100644 --- a/app/src/main/java/com/kunzisoft/keepass/view/TemplateEditView.kt +++ b/app/src/main/java/com/kunzisoft/keepass/view/TemplateEditView.kt @@ -1,6 +1,8 @@ package com.kunzisoft.keepass.view import android.content.Context +import android.graphics.PorterDuff +import android.graphics.PorterDuffColorFilter import android.os.Build import android.util.AttributeSet import android.view.View @@ -63,6 +65,9 @@ class TemplateEditView @JvmOverloads constructor(context: Context, } fun setBackgroundColor(color: Int?) { + color?.let { + backgroundColorButton.colorFilter = PorterDuffColorFilter(it, PorterDuff.Mode.SRC_ATOP) + } mEntryInfo?.backgroundColor = color } @@ -75,6 +80,9 @@ class TemplateEditView @JvmOverloads constructor(context: Context, } fun setForegroundColor(color: Int?) { + color?.let { + foregroundColorButton.colorFilter = PorterDuffColorFilter(it, PorterDuff.Mode.SRC_ATOP) + } mEntryInfo?.foregroundColor = color } @@ -220,6 +228,12 @@ class TemplateEditView @JvmOverloads constructor(context: Context, override fun populateViewsWithEntryInfo(showEmptyFields: Boolean): List { refreshIcon() + mEntryInfo?.backgroundColor?.let { + backgroundColorButton.colorFilter = PorterDuffColorFilter(it, PorterDuff.Mode.SRC_ATOP) + } + mEntryInfo?.foregroundColor?.let { + foregroundColorButton.colorFilter = PorterDuffColorFilter(it, PorterDuff.Mode.SRC_ATOP) + } return super.populateViewsWithEntryInfo(showEmptyFields) } diff --git a/app/src/main/res/layout/item_icon.xml b/app/src/main/res/layout/item_icon.xml index b854e916e..9e6469044 100644 --- a/app/src/main/res/layout/item_icon.xml +++ b/app/src/main/res/layout/item_icon.xml @@ -22,7 +22,7 @@ android:id="@+id/icon_container" android:layout_width="match_parent" android:layout_height="80dp" - android:background="@drawable/background_item_selection"> + style="@style/KeepassDXStyle.Selectable.Item"> - - + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintWidth_percent="@dimen/content_percent"> - - + app:layout_constraintTop_toTopOf="parent" /> + app:layout_constraintLeft_toRightOf="@+id/node_icon" + app:layout_constraintRight_toLeftOf="@+id/node_options" + app:layout_constraintStart_toEndOf="@+id/node_icon" + app:layout_constraintTop_toTopOf="parent"> + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="parent"> + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="parent"> + - + android:layout_width="wrap_content" + android:layout_height="wrap_content" + app:indicatorSize="16dp" + app:trackThickness="2dp" + app:indicatorDirectionCircular="counterclockwise" + android:layout_gravity="center" /> @@ -207,12 +182,12 @@ + android:layout_marginRight="48dp" + android:background="?android:attr/listDivider" + app:layout_constraintBottom_toBottomOf="parent" /> \ No newline at end of file diff --git a/app/src/main/res/layout/view_template.xml b/app/src/main/res/layout/view_template.xml index 8b15faf89..d07d12258 100644 --- a/app/src/main/res/layout/view_template.xml +++ b/app/src/main/res/layout/view_template.xml @@ -33,6 +33,26 @@ + + + + + android:layout_gravity="center" + android:contentDescription="@string/content_description_entry_icon"/> - - - - diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 621de6eca..0155638e0 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -436,6 +436,13 @@ @color/background_button_color_secondary @color/background_button_color_secondary + -