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 5c0127548..eef8fe978 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.kt @@ -47,6 +47,7 @@ import com.kunzisoft.keepass.icons.assignDatabaseIcon import com.kunzisoft.keepass.magikeyboard.MagikIME import com.kunzisoft.keepass.database.element.EntryAttachment import com.kunzisoft.keepass.model.EntryAttachmentState +import com.kunzisoft.keepass.model.StreamDirection import com.kunzisoft.keepass.notifications.AttachmentFileNotificationService import com.kunzisoft.keepass.notifications.ClipboardEntryNotificationService import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_DELETE_ENTRY_HISTORY @@ -332,7 +333,7 @@ class EntryActivity : LockingActivity() { entryContentsView?.setHiddenProtectedValue(!mShowPassword) // Manage attachments - entryContentsView?.assignAttachments(entry.getAttachments()) { attachmentItem -> + entryContentsView?.assignAttachments(entry.getAttachments(), StreamDirection.DOWNLOAD) { attachmentItem -> createDocument(this, attachmentItem.name)?.let { requestCode -> mAttachmentsToDownload[requestCode] = attachmentItem } 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 9830e9ee2..eafcae538 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt @@ -295,6 +295,10 @@ class EntryEditActivity : LockingActivity(), onActionTaskListener = object : AttachmentFileNotificationService.ActionTaskListener { override fun onAttachmentAction(fileUri: Uri, entryAttachmentState: EntryAttachmentState) { when (entryAttachmentState.downloadState) { + AttachmentState.START, + AttachmentState.IN_PROGRESS -> { + entryEditContentsView?.putAttachment(entryAttachmentState) + } AttachmentState.COMPLETE -> { entryEditContentsView?.putAttachment(entryAttachmentState) mAttachmentFileBinderManager?.removeAttachmentAction(entryAttachmentState) @@ -303,6 +307,7 @@ class EntryEditActivity : LockingActivity(), // TODO error mDatabase?.removeAttachmentIfNotUsed(entryAttachmentState.entryAttachment) mAttachmentFileBinderManager?.removeAttachmentAction(entryAttachmentState) + entryEditContentsView?.removeAttachment(entryAttachmentState) } else -> { // TODO progress @@ -342,7 +347,7 @@ class EntryEditActivity : LockingActivity(), assignExtraFields(newEntry.customFields.mapTo(ArrayList()) { Field(it.key, it.value) }, mFocusedEditExtraField) - assignAttachments(newEntry.getAttachments()) { attachment -> + assignAttachments(newEntry.getAttachments(), StreamDirection.UPLOAD) { attachment -> newEntry.removeAttachment(attachment) } } diff --git a/app/src/main/java/com/kunzisoft/keepass/adapters/AnimatedItemsAdapter.kt b/app/src/main/java/com/kunzisoft/keepass/adapters/AnimatedItemsAdapter.kt index 62f5578ff..a4deb31eb 100644 --- a/app/src/main/java/com/kunzisoft/keepass/adapters/AnimatedItemsAdapter.kt +++ b/app/src/main/java/com/kunzisoft/keepass/adapters/AnimatedItemsAdapter.kt @@ -46,6 +46,13 @@ abstract class AnimatedItemsAdapter(val contex onListSizeChangedListener?.invoke(previousSize, itemsList.size) } + open fun removeItem(item: Item) { + if (itemsList.contains(item)) { + mItemToRemove = item + notifyItemChanged(itemsList.indexOf(item)) + } + } + fun onBindDeleteButton(holder: T, deleteButton: View, item: Item, position: Int) { deleteButton.apply { visibility = View.VISIBLE diff --git a/app/src/main/java/com/kunzisoft/keepass/adapters/EntryAttachmentsItemsAdapter.kt b/app/src/main/java/com/kunzisoft/keepass/adapters/EntryAttachmentsItemsAdapter.kt index 4a61f74ab..e432bc43c 100644 --- a/app/src/main/java/com/kunzisoft/keepass/adapters/EntryAttachmentsItemsAdapter.kt +++ b/app/src/main/java/com/kunzisoft/keepass/adapters/EntryAttachmentsItemsAdapter.kt @@ -23,6 +23,7 @@ import android.content.Context import android.text.format.Formatter import android.view.View import android.view.ViewGroup +import android.widget.ImageView import android.widget.ProgressBar import android.widget.TextView import androidx.recyclerview.widget.RecyclerView @@ -31,8 +32,9 @@ import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.database.element.database.CompressionAlgorithm import com.kunzisoft.keepass.model.AttachmentState import com.kunzisoft.keepass.model.EntryAttachmentState +import com.kunzisoft.keepass.model.StreamDirection -class EntryAttachmentsItemsAdapter(context: Context, private val editable: Boolean) +class EntryAttachmentsItemsAdapter(context: Context) : AnimatedItemsAdapter(context) { var onItemClickListener: ((item: EntryAttachmentState)->Unit)? = null @@ -61,24 +63,49 @@ class EntryAttachmentsItemsAdapter(context: Context, private val editable: Boole visibility = View.GONE } } - if (editable) { - holder.binaryFileProgressContainer.visibility = View.GONE - holder.binaryFileDeleteButton.apply { - visibility = View.VISIBLE - onBindDeleteButton(holder, this, entryAttachmentState, position) - } - } else { - holder.binaryFileProgressContainer.visibility = View.VISIBLE - holder.binaryFileDeleteButton.visibility = View.GONE - holder.binaryFileProgress.apply { - visibility = when (entryAttachmentState.downloadState) { - AttachmentState.NULL, AttachmentState.COMPLETE, AttachmentState.ERROR -> View.GONE - AttachmentState.START, AttachmentState.IN_PROGRESS -> View.VISIBLE + when (entryAttachmentState.streamDirection) { + StreamDirection.UPLOAD -> { + holder.binaryFileProgressIcon.isActivated = true + when (entryAttachmentState.downloadState) { + AttachmentState.START, + AttachmentState.IN_PROGRESS -> { + holder.binaryFileProgressContainer.visibility = View.VISIBLE + holder.binaryFileProgress.apply { + visibility = View.VISIBLE + progress = entryAttachmentState.downloadProgression + } + holder.binaryFileDeleteButton.apply { + visibility = View.GONE + setOnClickListener(null) + } + } + AttachmentState.NULL, + AttachmentState.ERROR, + AttachmentState.COMPLETE -> { + holder.binaryFileProgressContainer.visibility = View.GONE + holder.binaryFileProgress.visibility = View.GONE + holder.binaryFileDeleteButton.apply { + visibility = View.VISIBLE + onBindDeleteButton(holder, this, entryAttachmentState, position) + } + } } - progress = entryAttachmentState.downloadProgression + holder.itemView.setOnClickListener(null) } - holder.itemView.setOnClickListener { - onItemClickListener?.invoke(entryAttachmentState) + StreamDirection.DOWNLOAD -> { + holder.binaryFileProgressIcon.isActivated = false + holder.binaryFileProgressContainer.visibility = View.VISIBLE + holder.binaryFileDeleteButton.visibility = View.GONE + holder.binaryFileProgress.apply { + visibility = when (entryAttachmentState.downloadState) { + AttachmentState.NULL, AttachmentState.COMPLETE, AttachmentState.ERROR -> View.GONE + AttachmentState.START, AttachmentState.IN_PROGRESS -> View.VISIBLE + } + progress = entryAttachmentState.downloadProgression + } + holder.itemView.setOnClickListener { + onItemClickListener?.invoke(entryAttachmentState) + } } } } @@ -100,6 +127,7 @@ class EntryAttachmentsItemsAdapter(context: Context, private val editable: Boole 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 binaryFileProgressIcon: ImageView = itemView.findViewById(R.id.item_attachment_icon) var binaryFileProgress: ProgressBar = itemView.findViewById(R.id.item_attachment_progress) var binaryFileDeleteButton: View = itemView.findViewById(R.id.item_attachment_delete_button) } diff --git a/app/src/main/java/com/kunzisoft/keepass/model/EntryAttachmentState.kt b/app/src/main/java/com/kunzisoft/keepass/model/EntryAttachmentState.kt index a2296427b..356dc068f 100644 --- a/app/src/main/java/com/kunzisoft/keepass/model/EntryAttachmentState.kt +++ b/app/src/main/java/com/kunzisoft/keepass/model/EntryAttachmentState.kt @@ -27,16 +27,19 @@ import com.kunzisoft.keepass.utils.readEnum import com.kunzisoft.keepass.utils.writeEnum data class EntryAttachmentState(var entryAttachment: EntryAttachment, + var streamDirection: StreamDirection, var downloadState: AttachmentState = AttachmentState.NULL, var downloadProgression: Int = 0) : Parcelable { constructor(parcel: Parcel) : this( parcel.readParcelable(EntryAttachment::class.java.classLoader) ?: EntryAttachment("", BinaryAttachment()), + parcel.readEnum() ?: StreamDirection.DOWNLOAD, parcel.readEnum() ?: AttachmentState.NULL, parcel.readInt()) override fun writeToParcel(parcel: Parcel, flags: Int) { parcel.writeParcelable(entryAttachment, flags) + parcel.writeEnum(streamDirection) parcel.writeEnum(downloadState) parcel.writeInt(downloadProgression) } diff --git a/app/src/main/java/com/kunzisoft/keepass/model/StreamDirection.kt b/app/src/main/java/com/kunzisoft/keepass/model/StreamDirection.kt new file mode 100644 index 000000000..134cb6f29 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/model/StreamDirection.kt @@ -0,0 +1,5 @@ +package com.kunzisoft.keepass.model + +enum class StreamDirection { + UPLOAD, DOWNLOAD +} \ No newline at end of file diff --git a/app/src/main/java/com/kunzisoft/keepass/notifications/AttachmentFileNotificationService.kt b/app/src/main/java/com/kunzisoft/keepass/notifications/AttachmentFileNotificationService.kt index 038d5fb93..609d93068 100644 --- a/app/src/main/java/com/kunzisoft/keepass/notifications/AttachmentFileNotificationService.kt +++ b/app/src/main/java/com/kunzisoft/keepass/notifications/AttachmentFileNotificationService.kt @@ -32,6 +32,7 @@ import com.kunzisoft.keepass.database.element.security.BinaryAttachment import com.kunzisoft.keepass.model.AttachmentState import com.kunzisoft.keepass.database.element.EntryAttachment import com.kunzisoft.keepass.model.EntryAttachmentState +import com.kunzisoft.keepass.model.StreamDirection import com.kunzisoft.keepass.stream.readBytes import com.kunzisoft.keepass.timeout.TimeoutHelper import com.kunzisoft.keepass.utils.UriUtil @@ -101,12 +102,12 @@ class AttachmentFileNotificationService: LockNotificationService() { ACTION_ATTACHMENT_FILE_START_UPLOAD -> { actionUploadOrDownload(downloadFileUri, intent, - AttachmentFileAction.StreamDirection.UPLOAD) + StreamDirection.UPLOAD) } ACTION_ATTACHMENT_FILE_START_DOWNLOAD -> { actionUploadOrDownload(downloadFileUri, intent, - AttachmentFileAction.StreamDirection.DOWNLOAD) + StreamDirection.DOWNLOAD) } else -> { if (downloadFileUri != null) { @@ -166,12 +167,12 @@ class AttachmentFileNotificationService: LockNotificationService() { val fileName = DocumentFile.fromSingleUri(this, attachmentNotification.uri)?.name ?: "" val builder = buildNewNotification().apply { - when (attachmentNotification.streamDirection) { - AttachmentFileAction.StreamDirection.UPLOAD -> { + when (attachmentNotification.entryAttachmentState.streamDirection) { + StreamDirection.UPLOAD -> { setSmallIcon(R.drawable.ic_file_upload_white_24dp) setContentTitle(getString(R.string.upload_attachment, fileName)) } - AttachmentFileAction.StreamDirection.DOWNLOAD -> { + StreamDirection.DOWNLOAD -> { setSmallIcon(R.drawable.ic_file_download_white_24dp) setContentTitle(getString(R.string.download_attachment, fileName)) } @@ -196,11 +197,11 @@ class AttachmentFileNotificationService: LockNotificationService() { } AttachmentState.COMPLETE -> { setContentText(getString(R.string.download_complete)) - when (attachmentNotification.streamDirection) { - AttachmentFileAction.StreamDirection.UPLOAD -> { + when (attachmentNotification.entryAttachmentState.streamDirection) { + StreamDirection.UPLOAD -> { } - AttachmentFileAction.StreamDirection.DOWNLOAD -> { + StreamDirection.DOWNLOAD -> { setContentIntent(pendingContentIntent) } } @@ -228,7 +229,6 @@ class AttachmentFileNotificationService: LockNotificationService() { private data class AttachmentNotification(var uri: Uri, var notificationId: Int, - var streamDirection: AttachmentFileAction.StreamDirection, var entryAttachmentState: EntryAttachmentState, var attachmentFileAction: AttachmentFileAction? = null) { override fun equals(other: Any?): Boolean { @@ -249,7 +249,7 @@ class AttachmentFileNotificationService: LockNotificationService() { private fun actionUploadOrDownload(downloadFileUri: Uri?, intent: Intent, - streamDirection: AttachmentFileAction.StreamDirection) { + streamDirection: StreamDirection) { if (downloadFileUri != null && intent.hasExtra(ATTACHMENT_KEY)) { try { @@ -257,14 +257,13 @@ class AttachmentFileNotificationService: LockNotificationService() { val nextNotificationId = (attachmentNotificationList.maxByOrNull { it.notificationId } ?.notificationId ?: notificationId) + 1 - val entryAttachmentState = EntryAttachmentState(entryAttachment) - val attachmentNotification = AttachmentNotification(downloadFileUri, nextNotificationId, streamDirection, entryAttachmentState) + val entryAttachmentState = EntryAttachmentState(entryAttachment, streamDirection) + val attachmentNotification = AttachmentNotification(downloadFileUri, nextNotificationId, entryAttachmentState) attachmentNotificationList.add(attachmentNotification) mainScope.launch { AttachmentFileAction(attachmentNotification, - contentResolver, - streamDirection).apply { + contentResolver).apply { listener = attachmentFileActionListener }.executeAction() } @@ -277,17 +276,12 @@ class AttachmentFileNotificationService: LockNotificationService() { private class AttachmentFileAction( private val attachmentNotification: AttachmentNotification, - private val contentResolver: ContentResolver, - private val streamDirection: StreamDirection) { + private val contentResolver: ContentResolver) { private val updateMinFrequency = 1000 private var previousSaveTime = System.currentTimeMillis() var listener: AttachmentFileActionListener? = null - enum class StreamDirection { - UPLOAD, DOWNLOAD - } - interface AttachmentFileActionListener { fun onUpdate(attachmentNotification: AttachmentNotification) } 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 1519b3a68..5c068ef06 100644 --- a/app/src/main/java/com/kunzisoft/keepass/view/EntryContentsView.kt +++ b/app/src/main/java/com/kunzisoft/keepass/view/EntryContentsView.kt @@ -35,10 +35,11 @@ import com.kunzisoft.keepass.adapters.EntryAttachmentsItemsAdapter import com.kunzisoft.keepass.adapters.EntryHistoryAdapter import com.kunzisoft.keepass.database.element.DateInstant import com.kunzisoft.keepass.database.element.Entry +import com.kunzisoft.keepass.database.element.EntryAttachment import com.kunzisoft.keepass.database.element.security.ProtectedString import com.kunzisoft.keepass.database.search.UuidUtil -import com.kunzisoft.keepass.database.element.EntryAttachment import com.kunzisoft.keepass.model.EntryAttachmentState +import com.kunzisoft.keepass.model.StreamDirection import com.kunzisoft.keepass.otp.OtpElement import com.kunzisoft.keepass.otp.OtpType import java.util.* @@ -70,7 +71,7 @@ class EntryContentsView @JvmOverloads constructor(context: Context, private val attachmentsContainerView: View private val attachmentsListView: RecyclerView - private val attachmentsAdapter = EntryAttachmentsItemsAdapter(context, false) + private val attachmentsAdapter = EntryAttachmentsItemsAdapter(context) private val historyContainerView: View private val historyListView: RecyclerView @@ -317,9 +318,10 @@ class EntryContentsView @JvmOverloads constructor(context: Context, } fun assignAttachments(attachments: ArrayList, + streamDirection: StreamDirection, onAttachmentClicked: (attachment: EntryAttachment)->Unit) { showAttachments(attachments.isNotEmpty()) - attachmentsAdapter.assignItems(attachments.map { EntryAttachmentState(it) }) + attachmentsAdapter.assignItems(attachments.map { EntryAttachmentState(it, streamDirection) }) attachmentsAdapter.onItemClickListener = { item -> onAttachmentClicked.invoke(item.entryAttachment) } 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 aadd64b42..95ff477ed 100644 --- a/app/src/main/java/com/kunzisoft/keepass/view/EntryEditContentsView.kt +++ b/app/src/main/java/com/kunzisoft/keepass/view/EntryEditContentsView.kt @@ -42,6 +42,7 @@ import com.kunzisoft.keepass.database.element.EntryAttachment import com.kunzisoft.keepass.model.EntryAttachmentState import com.kunzisoft.keepass.model.Field import com.kunzisoft.keepass.model.FocusedEditField +import com.kunzisoft.keepass.model.StreamDirection import org.joda.time.Duration import org.joda.time.Instant @@ -69,7 +70,7 @@ class EntryEditContentsView @JvmOverloads constructor(context: Context, private val attachmentsListView: RecyclerView private val extraFieldsAdapter = EntryExtraFieldsItemsAdapter(context) - private val attachmentsAdapter = EntryAttachmentsItemsAdapter(context, true) + private val attachmentsAdapter = EntryAttachmentsItemsAdapter(context) private var iconColor: Int = 0 private var expiresInstant: DateInstant = DateInstant(Instant.now().plus(Duration.standardDays(30)).toDate()) @@ -284,9 +285,10 @@ class EntryEditContentsView @JvmOverloads constructor(context: Context, } fun assignAttachments(attachments: ArrayList, + streamDirection: StreamDirection, onDeleteItem: (attachment: EntryAttachment)->Unit) { attachmentsContainerView.visibility = if (attachments.isEmpty()) View.GONE else View.VISIBLE - attachmentsAdapter.assignItems(attachments.map { EntryAttachmentState(it) }) + attachmentsAdapter.assignItems(attachments.map { EntryAttachmentState(it, streamDirection) }) attachmentsAdapter.onDeleteButtonClickListener = { item -> onDeleteItem.invoke(item.entryAttachment) } @@ -297,6 +299,10 @@ class EntryEditContentsView @JvmOverloads constructor(context: Context, attachmentsAdapter.putItem(attachment) } + fun removeAttachment(attachment: EntryAttachmentState) { + attachmentsAdapter.removeItem(attachment) + } + /** * Validate or not the entry form * diff --git a/app/src/main/res/drawable/ic_file_stream_white_24dp.xml b/app/src/main/res/drawable/ic_file_stream_white_24dp.xml new file mode 100644 index 000000000..be48e2edc --- /dev/null +++ b/app/src/main/res/drawable/ic_file_stream_white_24dp.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_attachment.xml b/app/src/main/res/layout/item_attachment.xml index 432848e72..74fcd73f8 100644 --- a/app/src/main/res/layout/item_attachment.xml +++ b/app/src/main/res/layout/item_attachment.xml @@ -104,7 +104,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" - android:src="@drawable/ic_file_download_white_24dp" + android:src="@drawable/ic_file_stream_white_24dp" android:contentDescription="@string/download" style="@style/KeepassDXStyle.ImageButton.Simple" />