Upload progression

This commit is contained in:
J-Jamet
2020-08-25 11:37:11 +02:00
parent 1719887e55
commit 4d9e2e1471
11 changed files with 103 additions and 45 deletions

View File

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

View File

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

View File

@@ -46,6 +46,13 @@ abstract class AnimatedItemsAdapter<Item, T: RecyclerView.ViewHolder>(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

View File

@@ -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<EntryAttachmentState, EntryAttachmentsItemsAdapter.EntryBinariesViewHolder>(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)
}

View File

@@ -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>() ?: StreamDirection.DOWNLOAD,
parcel.readEnum<AttachmentState>() ?: AttachmentState.NULL,
parcel.readInt())
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeParcelable(entryAttachment, flags)
parcel.writeEnum(streamDirection)
parcel.writeEnum(downloadState)
parcel.writeInt(downloadProgression)
}

View File

@@ -0,0 +1,5 @@
package com.kunzisoft.keepass.model
enum class StreamDirection {
UPLOAD, DOWNLOAD
}

View File

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

View File

@@ -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<EntryAttachment>,
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)
}

View File

@@ -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<EntryAttachment>,
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
*

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_activated="true"
android:drawable="@drawable/ic_file_upload_white_24dp" />
<item android:state_activated="false"
android:drawable="@drawable/ic_file_download_white_24dp" />
</selector>

View File

@@ -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" />
<ProgressBar