mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
Upload progression
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.kunzisoft.keepass.model
|
||||
|
||||
enum class StreamDirection {
|
||||
UPLOAD, DOWNLOAD
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
7
app/src/main/res/drawable/ic_file_stream_white_24dp.xml
Normal file
7
app/src/main/res/drawable/ic_file_stream_white_24dp.xml
Normal 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>
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user