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 0491be68f..e731f15eb 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.kt @@ -350,7 +350,6 @@ class EntryActivity : LockingActivity() { } } } - entryContentsView?.refreshAttachments() // Assign dates entryContentsView?.assignCreationDate(entry.creationTime) 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 35f51cbdf..d85635b57 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt @@ -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) + } } } diff --git a/app/src/main/java/com/kunzisoft/keepass/adapters/EntryAttachmentsAdapter.kt b/app/src/main/java/com/kunzisoft/keepass/adapters/EntryAttachmentsAdapter.kt index c0471f43a..79d20d699 100644 --- a/app/src/main/java/com/kunzisoft/keepass/adapters/EntryAttachmentsAdapter.kt +++ b/app/src/main/java/com/kunzisoft/keepass/adapters/EntryAttachmentsAdapter.kt @@ -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() { +class EntryAttachmentsAdapter(val context: Context, private val editable: Boolean) + : RecyclerView.Adapter() { private val inflater: LayoutInflater = LayoutInflater.from(context) var entryAttachmentsList: MutableList = ArrayList() var onItemClickListener: ((item: EntryAttachment, position: Int)->Unit)? = null + var onDeleteListener: ((item: EntryAttachment, position: Int)->Unit)? = null private val mDatabase = Database.getInstance() @@ -61,16 +63,28 @@ class EntryAttachmentsAdapter(val context: Context) : RecyclerView.Adapter View.GONE - AttachmentState.START, AttachmentState.IN_PROGRESS -> View.VISIBLE + 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 + AttachmentState.START, AttachmentState.IN_PROGRESS -> View.VISIBLE + } + progress = entryAttachment.downloadProgression + } + holder.itemView.setOnClickListener { + onItemClickListener?.invoke(entryAttachment, position) } - progress = entryAttachment.downloadProgression - } - - holder.itemView.setOnClickListener { - onItemClickListener?.invoke(entryAttachment, position) } } @@ -78,6 +92,23 @@ class EntryAttachmentsAdapter(val context: Context) : RecyclerView.Adapter) { + 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 { fun getAttachments(): ArrayList { val attachments = ArrayList() - 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)->Unit { - override fun invoke(mapEntry: Map.Entry) { - 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 { val history = ArrayList() val entryKDBXHistory = entryKDBX?.history ?: ArrayList() diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/entry/EntryKDBX.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/entry/EntryKDBX.kt index 30c05ef3b..0c0922f8d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/entry/EntryKDBX.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/entry/EntryKDBX.kt @@ -288,6 +288,10 @@ class EntryKDBX : EntryVersioned, NodeKDBXInte binaries[key] = value } + fun removeProtectedBinary(name: String) { + binaries.remove(name) + } + fun sizeOfHistory(): Int { return history.size } diff --git a/app/src/main/java/com/kunzisoft/keepass/model/EntryAttachment.kt b/app/src/main/java/com/kunzisoft/keepass/model/EntryAttachment.kt index 7b3b2833f..d2727fa03 100644 --- a/app/src/main/java/com/kunzisoft/keepass/model/EntryAttachment.kt +++ b/app/src/main/java/com/kunzisoft/keepass/model/EntryAttachment.kt @@ -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 { override fun createFromParcel(parcel: Parcel): EntryAttachment { return EntryAttachment(parcel) diff --git a/app/src/main/java/com/kunzisoft/keepass/view/EntryContentsView.kt b/app/src/main/java/com/kunzisoft/keepass/view/EntryContentsView.kt index 5de9bec19..ff09d8284 100644 --- a/app/src/main/java/com/kunzisoft/keepass/view/EntryContentsView.kt +++ b/app/src/main/java/com/kunzisoft/keepass/view/EntryContentsView.kt @@ -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) { - attachmentsAdapter.clear() - attachmentsAdapter.entryAttachmentsList.addAll(attachments) + attachmentsAdapter.assignAttachments(attachments) } fun updateAttachmentDownloadProgress(attachmentToDownload: EntryAttachment) { diff --git a/app/src/main/java/com/kunzisoft/keepass/view/EntryCustomField.kt b/app/src/main/java/com/kunzisoft/keepass/view/EntryCustomField.kt index bce15c50f..2653e05cd 100644 --- a/app/src/main/java/com/kunzisoft/keepass/view/EntryCustomField.kt +++ b/app/src/main/java/com/kunzisoft/keepass/view/EntryCustomField.kt @@ -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) diff --git a/app/src/main/java/com/kunzisoft/keepass/view/EntryEditContentsView.kt b/app/src/main/java/com/kunzisoft/keepass/view/EntryEditContentsView.kt index 5b7e7d71a..ea8105bd5 100644 --- a/app/src/main/java/com/kunzisoft/keepass/view/EntryEditContentsView.kt +++ b/app/src/main/java/com/kunzisoft/keepass/view/EntryEditContentsView.kt @@ -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) { + 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 * diff --git a/app/src/main/java/com/kunzisoft/keepass/view/EntryEditExtraField.kt b/app/src/main/java/com/kunzisoft/keepass/view/EntryEditExtraField.kt index 4be75991c..13bdfdde0 100644 --- a/app/src/main/java/com/kunzisoft/keepass/view/EntryEditExtraField.kt +++ b/app/src/main/java/com/kunzisoft/keepass/view/EntryEditExtraField.kt @@ -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) diff --git a/app/src/main/res/layout/item_attachment.xml b/app/src/main/res/layout/item_attachment.xml index bae3c40d8..f91f2c85c 100644 --- a/app/src/main/res/layout/item_attachment.xml +++ b/app/src/main/res/layout/item_attachment.xml @@ -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" > - - + android:layout_height="36dp"> + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/view_entry_edit_extra_field.xml b/app/src/main/res/layout/item_entry_edit_extra_field.xml similarity index 89% rename from app/src/main/res/layout/view_entry_edit_extra_field.xml rename to app/src/main/res/layout/item_entry_edit_extra_field.xml index ed48c5280..782142eda 100644 --- a/app/src/main/res/layout/view_entry_edit_extra_field.xml +++ b/app/src/main/res/layout/item_entry_edit_extra_field.xml @@ -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"> + + + + \ No newline at end of file