mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
Manage Tags #633
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 ->
|
||||
|
||||
@@ -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(";")
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
9
app/src/main/res/drawable/ic_bookmark_white_24dp.xml
Normal file
9
app/src/main/res/drawable/ic_bookmark_white_24dp.xml
Normal 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>
|
||||
@@ -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>
|
||||
|
||||
44
app/src/main/res/layout/item_tag.xml
Normal file
44
app/src/main/res/layout/item_tag.xml
Normal 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>
|
||||
@@ -1,2 +1,3 @@
|
||||
* Change default Argon2 parameters #1098
|
||||
* Add & edit custom icon name #976
|
||||
* Manage Tags #633
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user