Possibility to remove attachment

This commit is contained in:
J-Jamet
2020-08-15 12:01:08 +02:00
parent c5ef11febc
commit 0e5b7fbfa2
14 changed files with 176 additions and 56 deletions

View File

@@ -350,7 +350,6 @@ class EntryActivity : LockingActivity() {
}
}
}
entryContentsView?.refreshAttachments()
// Assign dates
entryContentsView?.assignCreationDate(entry.creationTime)

View File

@@ -300,6 +300,10 @@ class EntryEditActivity : LockingActivity(),
for ((key, value) in newEntry.customFields) {
putCustomField(Field(key, value))
}
assignAttachments(newEntry.getAttachments())
onAttachmentDeleted { attachment, _ ->
newEntry.removeAttachment(attachment)
}
}
}

View File

@@ -33,11 +33,13 @@ import com.kunzisoft.keepass.database.element.database.CompressionAlgorithm
import com.kunzisoft.keepass.model.AttachmentState
import com.kunzisoft.keepass.model.EntryAttachment
class EntryAttachmentsAdapter(val context: Context) : RecyclerView.Adapter<EntryAttachmentsAdapter.EntryBinariesViewHolder>() {
class EntryAttachmentsAdapter(val context: Context, private val editable: Boolean)
: RecyclerView.Adapter<EntryAttachmentsAdapter.EntryBinariesViewHolder>() {
private val inflater: LayoutInflater = LayoutInflater.from(context)
var entryAttachmentsList: MutableList<EntryAttachment> = ArrayList()
var onItemClickListener: ((item: EntryAttachment, position: Int)->Unit)? = null
var onDeleteListener: ((item: EntryAttachment, position: Int)->Unit)? = null
private val mDatabase = Database.getInstance()
@@ -61,6 +63,18 @@ class EntryAttachmentsAdapter(val context: Context) : RecyclerView.Adapter<Entry
visibility = View.GONE
}
}
if (editable) {
holder.binaryFileProgressContainer.visibility = View.GONE
holder.binaryFileDeleteButton.apply {
visibility = View.VISIBLE
setOnClickListener {
onDeleteListener?.invoke(entryAttachment, position)
deleteAttachment(entryAttachment)
}
}
} else {
holder.binaryFileProgressContainer.visibility = View.VISIBLE
holder.binaryFileDeleteButton.visibility = View.GONE
holder.binaryFileProgress.apply {
visibility = when (entryAttachment.downloadState) {
AttachmentState.NULL, AttachmentState.COMPLETE, AttachmentState.ERROR -> View.GONE
@@ -68,16 +82,33 @@ class EntryAttachmentsAdapter(val context: Context) : RecyclerView.Adapter<Entry
}
progress = entryAttachment.downloadProgression
}
holder.itemView.setOnClickListener {
onItemClickListener?.invoke(entryAttachment, position)
}
}
}
override fun getItemCount(): Int {
return entryAttachmentsList.size
}
fun assignAttachments(attachments: List<EntryAttachment>) {
entryAttachmentsList.apply {
clear()
addAll(attachments)
}
notifyDataSetChanged()
}
fun deleteAttachment(attachment: EntryAttachment) {
val position = entryAttachmentsList.indexOf(attachment)
if (position >= 0) {
entryAttachmentsList.removeAt(position)
notifyItemRemoved(position)
}
// TODO Animation
}
fun updateProgress(entryAttachment: EntryAttachment) {
val indexEntryAttachment = entryAttachmentsList.indexOfLast { current -> current.name == entryAttachment.name }
if (indexEntryAttachment != -1) {
@@ -95,6 +126,8 @@ class EntryAttachmentsAdapter(val context: Context) : RecyclerView.Adapter<Entry
var binaryFileTitle: TextView = itemView.findViewById(R.id.item_attachment_title)
var binaryFileSize: TextView = itemView.findViewById(R.id.item_attachment_size)
var binaryFileCompression: TextView = itemView.findViewById(R.id.item_attachment_compression)
var binaryFileProgressContainer: View = itemView.findViewById(R.id.item_attachment_progress_container)
var binaryFileProgress: ProgressBar = itemView.findViewById(R.id.item_attachment_progress)
var binaryFileDeleteButton: View = itemView.findViewById(R.id.item_attachment_delete_button)
}
}

View File

@@ -329,22 +329,31 @@ class Entry : Node, EntryVersionedInterface<Group> {
fun getAttachments(): ArrayList<EntryAttachment> {
val attachments = ArrayList<EntryAttachment>()
val binaryDescriptionKDB = entryKDB?.binaryDescription ?: ""
val binaryKDB = entryKDB?.binaryData
if (binaryKDB != null) {
attachments.add(EntryAttachment(binaryDescriptionKDB, binaryKDB))
entryKDB?.binaryData?.let { binaryKDB ->
attachments.add(EntryAttachment(entryKDB?.binaryDescription ?: "", binaryKDB))
}
val actionEach = object : (Map.Entry<String, BinaryAttachment>)->Unit {
override fun invoke(mapEntry: Map.Entry<String, BinaryAttachment>) {
attachments.add(EntryAttachment(mapEntry.key, mapEntry.value))
entryKDBX?.binaries?.let { binariesKDBX ->
for ((key, value) in binariesKDBX) {
attachments.add(EntryAttachment(key, value))
}
}
entryKDBX?.binaries?.forEach(actionEach)
return attachments
}
fun removeAttachment(attachment: EntryAttachment) {
entryKDB?.apply {
if (binaryDescription == attachment.name
&& binaryData == attachment.binaryAttachment) {
binaryDescription = ""
binaryData = null
}
}
entryKDBX?.removeProtectedBinary(attachment.name)
}
fun getHistory(): ArrayList<Entry> {
val history = ArrayList<Entry>()
val entryKDBXHistory = entryKDBX?.history ?: ArrayList()

View File

@@ -288,6 +288,10 @@ class EntryKDBX : EntryVersioned<UUID, UUID, GroupKDBX, EntryKDBX>, NodeKDBXInte
binaries[key] = value
}
fun removeProtectedBinary(name: String) {
binaries.remove(name)
}
fun sizeOfHistory(): Int {
return history.size
}

View File

@@ -47,6 +47,22 @@ data class EntryAttachment(var name: String,
return 0
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is EntryAttachment) return false
if (name != other.name) return false
if (binaryAttachment != other.binaryAttachment) return false
return true
}
override fun hashCode(): Int {
var result = name.hashCode()
result = 31 * result + binaryAttachment.hashCode()
return result
}
companion object CREATOR : Parcelable.Creator<EntryAttachment> {
override fun createFromParcel(parcel: Parcel): EntryAttachment {
return EntryAttachment(parcel)

View File

@@ -86,7 +86,7 @@ class EntryContentsView @JvmOverloads constructor(context: Context,
private val attachmentsContainerView: View
private val attachmentsListView: RecyclerView
private val attachmentsAdapter = EntryAttachmentsAdapter(context)
private val attachmentsAdapter = EntryAttachmentsAdapter(context, false)
private val historyContainerView: View
private val historyListView: RecyclerView
@@ -366,13 +366,8 @@ class EntryContentsView @JvmOverloads constructor(context: Context,
attachmentsContainerView.visibility = if (show) View.VISIBLE else View.GONE
}
fun refreshAttachments() {
attachmentsAdapter.notifyDataSetChanged()
}
fun assignAttachments(attachments: ArrayList<EntryAttachment>) {
attachmentsAdapter.clear()
attachmentsAdapter.entryAttachmentsList.addAll(attachments)
attachmentsAdapter.assignAttachments(attachments)
}
fun updateAttachmentDownloadProgress(attachmentToDownload: EntryAttachment) {

View File

@@ -44,7 +44,7 @@ class EntryCustomField @JvmOverloads constructor(context: Context,
init {
val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater?
inflater?.inflate(R.layout.item_entry_new_field, this)
inflater?.inflate(R.layout.item_entry_extra_field, this)
labelView = findViewById(R.id.title)
valueView = findViewById(R.id.value)

View File

@@ -22,18 +22,22 @@ package com.kunzisoft.keepass.view
import android.content.Context
import android.graphics.Color
import android.util.AttributeSet
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.*
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.SimpleItemAnimator
import com.google.android.material.textfield.TextInputLayout
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.adapters.EntryAttachmentsAdapter
import com.kunzisoft.keepass.database.element.DateInstant
import com.kunzisoft.keepass.database.element.icon.IconImage
import com.kunzisoft.keepass.icons.IconDrawableFactory
import com.kunzisoft.keepass.icons.assignDatabaseIcon
import com.kunzisoft.keepass.icons.assignDefaultDatabaseIcon
import com.kunzisoft.keepass.model.EntryAttachment
import com.kunzisoft.keepass.model.Field
import org.joda.time.Duration
import org.joda.time.Instant
@@ -57,6 +61,9 @@ class EntryEditContentsView @JvmOverloads constructor(context: Context,
private val entryExpiresTextView: TextView
private val entryNotesView: EditText
private val entryExtraFieldsContainer: ViewGroup
private val attachmentsListView: RecyclerView
private val attachmentsAdapter = EntryAttachmentsAdapter(context, true)
private var iconColor: Int = 0
private var expiresInstant: DateInstant = DateInstant(Instant.now().plus(Duration.standardDays(30)).toDate())
@@ -86,6 +93,12 @@ class EntryEditContentsView @JvmOverloads constructor(context: Context,
entryExpiresTextView = findViewById(R.id.entry_edit_expires_text)
entryNotesView = findViewById(R.id.entry_edit_notes)
entryExtraFieldsContainer = findViewById(R.id.entry_edit_extra_fields_container)
attachmentsListView = findViewById(R.id.entry_attachments_list)
attachmentsListView?.apply {
layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, true)
adapter = attachmentsAdapter
(itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
}
entryExpiresCheckBox.setOnCheckedChangeListener { _, _ ->
assignExpiresDateText()
@@ -255,6 +268,16 @@ class EntryEditContentsView @JvmOverloads constructor(context: Context,
return extraFieldView
}
fun assignAttachments(attachments: java.util.ArrayList<EntryAttachment>) {
attachmentsAdapter.assignAttachments(attachments)
}
fun onAttachmentDeleted(action: (attachment: EntryAttachment, position: Int)->Unit) {
attachmentsAdapter.onDeleteListener = { item, position ->
action.invoke(item, position)
}
}
/**
* Validate or not the entry form
*

View File

@@ -66,7 +66,7 @@ class EntryEditExtraField @JvmOverloads constructor(context: Context,
init {
val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater?
inflater?.inflate(R.layout.view_entry_edit_extra_field, this)
inflater?.inflate(R.layout.item_entry_edit_extra_field, this)
valueLayoutView = findViewById(R.id.entry_extra_field_value_container)
valueView = findViewById(R.id.entry_extra_field_value)

View File

@@ -44,7 +44,7 @@
android:gravity="end"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/item_attachment_icon" >
app:layout_constraintEnd_toStartOf="@+id/item_attachment_action_container" >
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/item_attachment_size"
android:layout_width="wrap_content"
@@ -65,13 +65,35 @@
android:paddingEnd="8dp"
tools:text="GZip" />
</LinearLayout>
<FrameLayout
android:id="@+id/item_attachment_action_container"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_width="36dp"
android:layout_height="36dp">
<ImageView
android:id="@+id/item_attachment_delete_button"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="6dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:src="@drawable/ic_close_white_24dp"
app:tint="?attr/colorAccent"
android:contentDescription="@string/content_description_remove_field">
</ImageView>
<FrameLayout
android:id="@+id/item_attachment_progress_container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/item_attachment_icon"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_width="36dp"
android:layout_height="36dp"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="6dp"
android:src="@drawable/ic_file_download_white_24dp"
android:tint="?attr/colorAccent" />
@@ -81,8 +103,10 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
style="@style/KeepassDXStyle.ProgressBar.Circle"
android:layout_width="36dp"
android:layout_height="36dp"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:max="100"
android:progress="60" />
</FrameLayout>
</FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -33,11 +33,7 @@
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/entry_extra_field_delete"
android:paddingLeft="@dimen/default_margin"
android:paddingStart="@dimen/default_margin"
android:paddingRight="0dp"
android:paddingEnd="0dp">
app:layout_constraintEnd_toStartOf="@+id/entry_extra_field_delete">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/entry_extra_field_value"
@@ -51,9 +47,10 @@
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/entry_extra_field_delete"
android:layout_width="48dp"
android:layout_height="48dp"
android:padding="12dp"
android:layout_width="36dp"
android:layout_height="36dp"
android:padding="6dp"
android:layout_marginTop="12dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:src="@drawable/ic_close_white_24dp"

View File

@@ -212,10 +212,26 @@
android:id="@+id/entry_edit_extra_fields_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/default_margin"
android:layout_marginBottom="@dimen/default_margin"
android:layout_margin="@dimen/default_margin"
android:orientation="vertical">
</LinearLayout>
</androidx.cardview.widget.CardView>
<androidx.cardview.widget.CardView
android:id="@+id/entry_edit_attachments_container_parent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/card_view_margin_bottom"
android:layout_marginStart="0dp"
android:layout_marginEnd="0dp"
android:layout_marginLeft="0dp"
android:layout_marginRight="0dp"
app:layout_constraintTop_toBottomOf="@+id/entry_edit_extra_fields_container_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/entry_attachments_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/default_margin" />
</androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>