diff --git a/CHANGELOG b/CHANGELOG index dc7174086..eb0e1374c 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ KeePassDX(3.1.0) * Change default Argon2 parameters #1098 * Add & edit custom icon name #976 + * Manage Tags #633 KeePassDX(3.0.2) * Samsung DeX mode #1114 #245 (Thx @chenxiaolong) diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.kt index eb03b76da..114c3ba16 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.kt @@ -35,12 +35,15 @@ import android.widget.ProgressBar import androidx.activity.viewModels import androidx.appcompat.widget.Toolbar import androidx.coordinatorlayout.widget.CoordinatorLayout +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView import com.google.android.material.appbar.CollapsingToolbarLayout import com.kunzisoft.keepass.R import com.kunzisoft.keepass.activities.fragments.EntryFragment import com.kunzisoft.keepass.activities.helpers.ExternalFileHelper import com.kunzisoft.keepass.activities.helpers.SpecialMode import com.kunzisoft.keepass.activities.legacy.DatabaseLockActivity +import com.kunzisoft.keepass.adapters.TagsAdapter import com.kunzisoft.keepass.database.element.Attachment import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.database.element.icon.IconImage @@ -70,6 +73,8 @@ class EntryActivity : DatabaseLockActivity() { private var collapsingToolbarLayout: CollapsingToolbarLayout? = null private var titleIconView: ImageView? = null private var historyView: View? = null + private var tagsListView: RecyclerView? = null + private var tagsAdapter: TagsAdapter? = null private var entryProgress: ProgressBar? = null private var lockView: View? = null private var toolbar: Toolbar? = null @@ -105,6 +110,7 @@ class EntryActivity : DatabaseLockActivity() { collapsingToolbarLayout = findViewById(R.id.toolbar_layout) titleIconView = findViewById(R.id.entry_icon) historyView = findViewById(R.id.history_container) + tagsListView = findViewById(R.id.entry_tags_list_view) entryProgress = findViewById(R.id.entry_progress) lockView = findViewById(R.id.lock_button) loadingView = findViewById(R.id.loading) @@ -118,6 +124,13 @@ class EntryActivity : DatabaseLockActivity() { mIconColor = taIconColor.getColor(0, Color.BLACK) taIconColor.recycle() + // Init Tags adapter + tagsAdapter = TagsAdapter(this) + tagsListView?.apply { + layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) + adapter = tagsAdapter + } + // Get Entry from UUID try { intent.getParcelableExtra?>(KEY_ENTRY)?.let { mainEntryId -> @@ -179,6 +192,10 @@ class EntryActivity : DatabaseLockActivity() { collapsingToolbarLayout?.title = entryTitle toolbar?.title = entryTitle mUrl = entryInfo.url + // Assign tags + val tags = entryInfo.tags + tagsListView?.visibility = if (tags.isEmpty()) View.GONE else View.VISIBLE + tagsAdapter?.setTags(tags) loadingView?.hideByFading() mEntryLoaded = true diff --git a/app/src/main/java/com/kunzisoft/keepass/adapters/TagsAdapter.kt b/app/src/main/java/com/kunzisoft/keepass/adapters/TagsAdapter.kt new file mode 100644 index 000000000..c5a8e24f1 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/adapters/TagsAdapter.kt @@ -0,0 +1,73 @@ +/* + * Copyright 2021 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePassDX. + * + * KeePassDX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePassDX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePassDX. If not, see . + * + */ +package com.kunzisoft.keepass.adapters + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.kunzisoft.keepass.R +import com.kunzisoft.keepass.database.element.Tags + +class TagsAdapter(context: Context) : RecyclerView.Adapter() { + + private val inflater: LayoutInflater = LayoutInflater.from(context) + private var mTags: Tags = Tags() + var onItemClickListener: OnItemClickListener? = null + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TagViewHolder { + val view = inflater.inflate(R.layout.item_tag, parent, false) + return TagViewHolder(view) + } + + override fun onBindViewHolder(holder: TagViewHolder, position: Int) { + val field = mTags.get(position) + holder.name.text = field + holder.bind(field, onItemClickListener) + } + + override fun getItemCount(): Int { + return mTags.size() + } + + fun setTags(tags: Tags) { + mTags.setTags(tags) + notifyDataSetChanged() + } + + fun clear() { + mTags.clear() + } + + interface OnItemClickListener { + fun onItemClick(item: String) + } + + inner class TagViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + + var name: TextView = itemView.findViewById(R.id.tag_name) + + fun bind(item: String, listener: OnItemClickListener?) { + itemView.setOnClickListener { listener?.onItemClick(item) } + } + } +} \ No newline at end of file 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..f23b276d2 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 @@ -419,6 +419,7 @@ class Entry : Node, EntryVersionedInterface { entryInfo.expiryTime = expiryTime entryInfo.url = url entryInfo.notes = notes + entryInfo.tags = tags entryInfo.customFields = getExtraFields().toMutableList() // Add otpElement to generate token entryInfo.otpModel = getOtpElement()?.otpModel @@ -453,6 +454,7 @@ class Entry : Node, EntryVersionedInterface { expiryTime = newEntryInfo.expiryTime url = newEntryInfo.url notes = newEntryInfo.notes + tags = newEntryInfo.tags addExtraFields(newEntryInfo.customFields) database?.attachmentPool?.let { binaryPool -> newEntryInfo.attachments.forEach { attachment -> diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/Tags.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/Tags.kt index bdda16cde..82b2de421 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/Tags.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/Tags.kt @@ -25,10 +25,32 @@ class Tags: Parcelable { return 0 } + fun setTags(tags: Tags) { + mTags.clear() + mTags.addAll(tags.mTags) + } + + fun get(position: Int): String { + return mTags[position] + } + + fun put(tag: String) { + if (!mTags.contains(tag)) + mTags.add(tag) + } + fun isEmpty(): Boolean { return mTags.isEmpty() } + fun size(): Int { + return mTags.size + } + + fun clear() { + mTags.clear() + } + override fun toString(): String { return mTags.joinToString(";") } 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..4a60e870b 100644 --- a/app/src/main/java/com/kunzisoft/keepass/model/EntryInfo.kt +++ b/app/src/main/java/com/kunzisoft/keepass/model/EntryInfo.kt @@ -22,10 +22,7 @@ package com.kunzisoft.keepass.model import android.os.Parcel import android.os.ParcelUuid import android.os.Parcelable -import com.kunzisoft.keepass.database.element.Attachment -import com.kunzisoft.keepass.database.element.Database -import com.kunzisoft.keepass.database.element.DateInstant -import com.kunzisoft.keepass.database.element.Field +import com.kunzisoft.keepass.database.element.* import com.kunzisoft.keepass.database.element.security.ProtectedString import com.kunzisoft.keepass.database.element.template.TemplateField import com.kunzisoft.keepass.otp.OtpElement @@ -40,6 +37,7 @@ class EntryInfo : NodeInfo { var password: String = "" var url: String = "" var notes: String = "" + var tags: Tags = Tags() var customFields: MutableList = mutableListOf() var attachments: MutableList = mutableListOf() var otpModel: OtpModel? = null @@ -53,6 +51,7 @@ class EntryInfo : NodeInfo { password = parcel.readString() ?: password url = parcel.readString() ?: url notes = parcel.readString() ?: notes + tags = parcel.readParcelable(Tags::class.java.classLoader) ?: tags parcel.readList(customFields, Field::class.java.classLoader) parcel.readList(attachments, Attachment::class.java.classLoader) otpModel = parcel.readParcelable(OtpModel::class.java.classLoader) ?: otpModel @@ -70,6 +69,7 @@ class EntryInfo : NodeInfo { parcel.writeString(password) parcel.writeString(url) parcel.writeString(notes) + parcel.writeParcelable(tags, flags) parcel.writeList(customFields) parcel.writeList(attachments) parcel.writeParcelable(otpModel, flags) @@ -196,6 +196,7 @@ class EntryInfo : NodeInfo { if (password != other.password) return false if (url != other.url) return false if (notes != other.notes) return false + if (tags != other.tags) return false if (customFields != other.customFields) return false if (attachments != other.attachments) return false if (otpModel != other.otpModel) return false @@ -211,6 +212,7 @@ class EntryInfo : NodeInfo { result = 31 * result + password.hashCode() result = 31 * result + url.hashCode() result = 31 * result + notes.hashCode() + result = 31 * result + tags.hashCode() result = 31 * result + customFields.hashCode() result = 31 * result + attachments.hashCode() result = 31 * result + (otpModel?.hashCode() ?: 0) diff --git a/app/src/main/res/drawable/ic_bookmark_white_24dp.xml b/app/src/main/res/drawable/ic_bookmark_white_24dp.xml new file mode 100644 index 000000000..663edd72b --- /dev/null +++ b/app/src/main/res/drawable/ic_bookmark_white_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/activity_entry.xml b/app/src/main/res/layout/activity_entry.xml index fe37fea78..46801b252 100644 --- a/app/src/main/res/layout/activity_entry.xml +++ b/app/src/main/res/layout/activity_entry.xml @@ -112,13 +112,26 @@ android:text="@string/entry_history"/> + + diff --git a/app/src/main/res/layout/item_tag.xml b/app/src/main/res/layout/item_tag.xml new file mode 100644 index 000000000..fd9808b11 --- /dev/null +++ b/app/src/main/res/layout/item_tag.xml @@ -0,0 +1,44 @@ + + + + \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/90.txt b/fastlane/metadata/android/en-US/changelogs/90.txt index 037dc1d3e..3f81346d3 100644 --- a/fastlane/metadata/android/en-US/changelogs/90.txt +++ b/fastlane/metadata/android/en-US/changelogs/90.txt @@ -1,2 +1,3 @@ * Change default Argon2 parameters #1098 - * Add & edit custom icon name #976 \ No newline at end of file + * Add & edit custom icon name #976 + * Manage Tags #633 \ No newline at end of file diff --git a/fastlane/metadata/android/fr-FR/changelogs/90.txt b/fastlane/metadata/android/fr-FR/changelogs/90.txt index f1b03ae58..686107704 100644 --- a/fastlane/metadata/android/fr-FR/changelogs/90.txt +++ b/fastlane/metadata/android/fr-FR/changelogs/90.txt @@ -1,2 +1,3 @@ * Changement des paramètres Argon2 par défaut #1098 - * Ajout & édition du nom d'icone customisé #976 \ No newline at end of file + * Ajout & édition du nom d'icone customisé #976 + * Gestion des Tags #633 \ No newline at end of file