Manage Tags #633

This commit is contained in:
J-Jamet
2021-09-28 19:12:34 +02:00
parent f0fdd4a537
commit bedc327e65
11 changed files with 192 additions and 7 deletions

View File

@@ -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)

View File

@@ -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<NodeId<UUID>?>(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

View File

@@ -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 <http://www.gnu.org/licenses/>.
*
*/
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<TagsAdapter.TagViewHolder>() {
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) }
}
}
}

View File

@@ -419,6 +419,7 @@ class Entry : Node, EntryVersionedInterface<Group> {
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<Group> {
expiryTime = newEntryInfo.expiryTime
url = newEntryInfo.url
notes = newEntryInfo.notes
tags = newEntryInfo.tags
addExtraFields(newEntryInfo.customFields)
database?.attachmentPool?.let { binaryPool ->
newEntryInfo.attachments.forEach { attachment ->

View File

@@ -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(";")
}

View File

@@ -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<Field> = mutableListOf()
var attachments: MutableList<Attachment> = 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)

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:viewportHeight="24"
android:viewportWidth="24"
android:width="24dp" >
<path
android:fillColor="#FFFFFF"
android:pathData="M17,3L7,3c-1.1,0 -1.99,0.9 -1.99,2L5,21l7,-3 7,3L19,5c0,-1.1 -0.9,-2 -2,-2zM17,18l-5,-2.18L7,18L7,5h10v13z"/>
</vector>

View File

@@ -112,13 +112,26 @@
android:text="@string/entry_history"/>
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/entry_tags_list_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="6dp"
android:paddingLeft="6dp"
android:paddingEnd="6dp"
android:paddingRight="6dp"
android:layout_gravity="center"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
android:orientation="horizontal"
app:layout_constraintTop_toBottomOf="@+id/history_container"/>
<androidx.fragment.app.FragmentContainerView
android:id="@+id/entry_content"
android:name="com.kunzisoft.keepass.activities.fragments.EntryFragment"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintWidth_percent="@dimen/content_percent"
app:layout_constraintTop_toBottomOf="@+id/history_container"
app:layout_constraintTop_toBottomOf="@+id/entry_tags_list_view"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?><!--
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
-->
<androidx.cardview.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minHeight="32dp"
android:layout_marginStart="6dp"
android:layout_marginLeft="6dp"
android:layout_marginEnd="6dp"
android:layout_marginRight="6dp"
android:layout_marginBottom="2dp"
android:orientation="horizontal">
<TextView
android:id="@+id/tag_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:layout_gravity="center"
app:drawableLeftCompat="@drawable/ic_bookmark_white_24dp"
app:drawableStartCompat="@drawable/ic_bookmark_white_24dp"
app:drawableTint="?attr/colorAccent"
android:drawablePadding="2dp"
tools:text="text"
android:paddingLeft="2dp"
android:paddingStart="2dp"
android:paddingEnd="8dp"
android:paddingRight="8dp" />
</androidx.cardview.widget.CardView>

View File

@@ -1,2 +1,3 @@
* Change default Argon2 parameters #1098
* Add & edit custom icon name #976
* Manage Tags #633

View File

@@ -1,2 +1,3 @@
* Changement des paramètres Argon2 par défaut #1098
* Ajout & édition du nom d'icone customisé #976
* Gestion des Tags #633