mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
Add cancellation to binary uploading
This commit is contained in:
@@ -35,10 +35,8 @@ import com.kunzisoft.keepass.model.AttachmentState
|
||||
import com.kunzisoft.keepass.model.EntryAttachmentState
|
||||
import com.kunzisoft.keepass.model.StreamDirection
|
||||
import com.kunzisoft.keepass.stream.readAllBytes
|
||||
import com.kunzisoft.keepass.timeout.TimeoutHelper
|
||||
import com.kunzisoft.keepass.utils.UriUtil
|
||||
import kotlinx.coroutines.*
|
||||
import java.io.BufferedInputStream
|
||||
import java.util.*
|
||||
import java.util.concurrent.CopyOnWriteArrayList
|
||||
|
||||
@@ -101,12 +99,15 @@ class AttachmentFileNotificationService: LockNotificationService() {
|
||||
|
||||
when(intent?.action) {
|
||||
ACTION_ATTACHMENT_FILE_START_UPLOAD -> {
|
||||
actionUploadOrDownload(downloadFileUri,
|
||||
actionStartUploadOrDownload(downloadFileUri,
|
||||
intent,
|
||||
StreamDirection.UPLOAD)
|
||||
}
|
||||
ACTION_ATTACHMENT_FILE_STOP_UPLOAD -> {
|
||||
actionStopUpload()
|
||||
}
|
||||
ACTION_ATTACHMENT_FILE_START_DOWNLOAD -> {
|
||||
actionUploadOrDownload(downloadFileUri,
|
||||
actionStartUploadOrDownload(downloadFileUri,
|
||||
intent,
|
||||
StreamDirection.DOWNLOAD)
|
||||
}
|
||||
@@ -216,15 +217,22 @@ class AttachmentFileNotificationService: LockNotificationService() {
|
||||
setDeleteIntent(pendingDeleteIntent)
|
||||
setOngoing(false)
|
||||
}
|
||||
AttachmentState.CANCELED -> {
|
||||
setContentText(getString(R.string.download_canceled))
|
||||
setDeleteIntent(pendingDeleteIntent)
|
||||
setOngoing(false)
|
||||
}
|
||||
AttachmentState.ERROR -> {
|
||||
setContentText(getString(R.string.error_file_not_create))
|
||||
setDeleteIntent(pendingDeleteIntent)
|
||||
setOngoing(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
when (attachmentNotification.entryAttachmentState.downloadState) {
|
||||
AttachmentState.ERROR,
|
||||
AttachmentState.COMPLETE -> {
|
||||
AttachmentState.COMPLETE,
|
||||
AttachmentState.CANCELED,
|
||||
AttachmentState.ERROR -> {
|
||||
stopForeground(false)
|
||||
notificationManager?.notify(attachmentNotification.notificationId, builder.build())
|
||||
} else -> {
|
||||
@@ -235,6 +243,7 @@ class AttachmentFileNotificationService: LockNotificationService() {
|
||||
|
||||
override fun onDestroy() {
|
||||
attachmentNotificationList.forEach { attachmentNotification ->
|
||||
attachmentNotification.attachmentFileAction?.cancel()
|
||||
attachmentNotification.attachmentFileAction?.listener = null
|
||||
notificationManager?.cancel(attachmentNotification.notificationId)
|
||||
}
|
||||
@@ -263,10 +272,10 @@ class AttachmentFileNotificationService: LockNotificationService() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun actionUploadOrDownload(downloadFileUri: Uri?,
|
||||
intent: Intent,
|
||||
streamDirection: StreamDirection) {
|
||||
if (downloadFileUri != null
|
||||
private fun actionStartUploadOrDownload(fileUri: Uri?,
|
||||
intent: Intent,
|
||||
streamDirection: StreamDirection) {
|
||||
if (fileUri != null
|
||||
&& intent.hasExtra(ATTACHMENT_KEY)) {
|
||||
try {
|
||||
intent.getParcelableExtra<Attachment>(ATTACHMENT_KEY)?.let { entryAttachment ->
|
||||
@@ -274,7 +283,7 @@ class AttachmentFileNotificationService: LockNotificationService() {
|
||||
val nextNotificationId = (attachmentNotificationList.maxByOrNull { it.notificationId }
|
||||
?.notificationId ?: notificationId) + 1
|
||||
val entryAttachmentState = EntryAttachmentState(entryAttachment, streamDirection)
|
||||
val attachmentNotification = AttachmentNotification(downloadFileUri, nextNotificationId, entryAttachmentState)
|
||||
val attachmentNotification = AttachmentNotification(fileUri, nextNotificationId, entryAttachmentState)
|
||||
|
||||
// Add action to the list on start
|
||||
attachmentNotificationList.add(attachmentNotification)
|
||||
@@ -287,11 +296,24 @@ class AttachmentFileNotificationService: LockNotificationService() {
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Unable to upload/download $downloadFileUri", e)
|
||||
Log.e(TAG, "Unable to upload/download $fileUri", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun actionStopUpload() {
|
||||
try {
|
||||
// Stop each upload
|
||||
attachmentNotificationList.filter {
|
||||
it.entryAttachmentState.streamDirection == StreamDirection.UPLOAD
|
||||
}.forEach {
|
||||
it.attachmentFileAction?.cancel()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Unable to stop upload", e)
|
||||
}
|
||||
}
|
||||
|
||||
private class AttachmentFileAction(
|
||||
private val attachmentNotification: AttachmentNotification,
|
||||
private val contentResolver: ContentResolver) {
|
||||
@@ -308,8 +330,6 @@ class AttachmentFileNotificationService: LockNotificationService() {
|
||||
|
||||
// on pre execute
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
TimeoutHelper.temporarilyDisableTimeout()
|
||||
|
||||
attachmentNotification.attachmentFileAction = this@AttachmentFileAction
|
||||
attachmentNotification.entryAttachmentState.apply {
|
||||
downloadState = AttachmentState.START
|
||||
@@ -320,53 +340,62 @@ class AttachmentFileNotificationService: LockNotificationService() {
|
||||
|
||||
withContext(Dispatchers.IO) {
|
||||
// on Progress with thread
|
||||
val asyncResult: Deferred<Boolean> = async {
|
||||
var progressResult = true
|
||||
try {
|
||||
attachmentNotification.entryAttachmentState.apply {
|
||||
downloadState = AttachmentState.IN_PROGRESS
|
||||
val asyncAction = launch {
|
||||
attachmentNotification.entryAttachmentState.apply {
|
||||
try {
|
||||
downloadState = AttachmentState.IN_PROGRESS
|
||||
|
||||
when (streamDirection) {
|
||||
StreamDirection.UPLOAD -> {
|
||||
uploadToDatabase(
|
||||
attachmentNotification.uri,
|
||||
attachment.binaryAttachment,
|
||||
contentResolver, 1024) { percent ->
|
||||
publishProgress(percent)
|
||||
when (streamDirection) {
|
||||
StreamDirection.UPLOAD -> {
|
||||
uploadToDatabase(
|
||||
attachmentNotification.uri,
|
||||
attachment.binaryAttachment,
|
||||
contentResolver, 1024,
|
||||
{ // Cancellation
|
||||
downloadState == AttachmentState.CANCELED
|
||||
}
|
||||
) { percent ->
|
||||
publishProgress(percent)
|
||||
}
|
||||
}
|
||||
StreamDirection.DOWNLOAD -> {
|
||||
downloadFromDatabase(
|
||||
attachmentNotification.uri,
|
||||
attachment.binaryAttachment,
|
||||
contentResolver, 1024) { percent ->
|
||||
publishProgress(percent)
|
||||
}
|
||||
}
|
||||
}
|
||||
StreamDirection.DOWNLOAD -> {
|
||||
downloadFromDatabase(
|
||||
attachmentNotification.uri,
|
||||
attachment.binaryAttachment,
|
||||
contentResolver, 1024) { percent ->
|
||||
publishProgress(percent)
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
downloadState = AttachmentState.ERROR
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
progressResult = false
|
||||
}
|
||||
progressResult
|
||||
attachmentNotification.entryAttachmentState.downloadState
|
||||
}
|
||||
|
||||
// on post execute
|
||||
withContext(Dispatchers.Main) {
|
||||
val result = asyncResult.await()
|
||||
attachmentNotification.attachmentFileAction = null
|
||||
asyncAction.join()
|
||||
attachmentNotification.entryAttachmentState.apply {
|
||||
downloadState = if (result) AttachmentState.COMPLETE else AttachmentState.ERROR
|
||||
downloadProgression = 100
|
||||
if (downloadState != AttachmentState.CANCELED
|
||||
&& downloadState != AttachmentState.ERROR) {
|
||||
downloadState = AttachmentState.COMPLETE
|
||||
downloadProgression = 100
|
||||
}
|
||||
}
|
||||
attachmentNotification.attachmentFileAction = null
|
||||
listener?.onUpdate(attachmentNotification)
|
||||
TimeoutHelper.releaseTemporarilyDisableTimeout()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
fun cancel() {
|
||||
attachmentNotification.entryAttachmentState.downloadState = AttachmentState.CANCELED
|
||||
}
|
||||
|
||||
fun downloadFromDatabase(attachmentToUploadUri: Uri,
|
||||
binaryAttachment: BinaryAttachment,
|
||||
contentResolver: ContentResolver,
|
||||
@@ -396,13 +425,14 @@ class AttachmentFileNotificationService: LockNotificationService() {
|
||||
binaryAttachment: BinaryAttachment,
|
||||
contentResolver: ContentResolver,
|
||||
bufferSize: Int = DEFAULT_BUFFER_SIZE,
|
||||
canceled: ()-> Boolean = { false },
|
||||
update: ((percent: Int)->Unit)? = null) {
|
||||
var dataUploaded = 0L
|
||||
val fileSize = contentResolver.openFileDescriptor(attachmentFromDownloadUri, "r")?.statSize ?: 0
|
||||
UriUtil.getUriInputStream(contentResolver, attachmentFromDownloadUri)?.use { inputStream ->
|
||||
Database.getInstance().loadedCipherKey?.let { binaryCipherKey ->
|
||||
binaryAttachment.getGzipOutputDataStream(binaryCipherKey).use { outputStream ->
|
||||
inputStream.readAllBytes(bufferSize) { buffer ->
|
||||
inputStream.readAllBytes(bufferSize, canceled) { buffer ->
|
||||
outputStream.write(buffer)
|
||||
dataUploaded += buffer.size
|
||||
try {
|
||||
@@ -422,7 +452,9 @@ class AttachmentFileNotificationService: LockNotificationService() {
|
||||
val currentTime = System.currentTimeMillis()
|
||||
if (previousSaveTime + updateMinFrequency < currentTime) {
|
||||
attachmentNotification.entryAttachmentState.apply {
|
||||
downloadState = AttachmentState.IN_PROGRESS
|
||||
if (downloadState != AttachmentState.CANCELED) {
|
||||
downloadState = AttachmentState.IN_PROGRESS
|
||||
}
|
||||
downloadProgression = percent
|
||||
}
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
@@ -444,6 +476,7 @@ class AttachmentFileNotificationService: LockNotificationService() {
|
||||
private const val CHANNEL_ATTACHMENT_ID = "com.kunzisoft.keepass.notification.channel.attachment"
|
||||
|
||||
const val ACTION_ATTACHMENT_FILE_START_UPLOAD = "ACTION_ATTACHMENT_FILE_START_UPLOAD"
|
||||
const val ACTION_ATTACHMENT_FILE_STOP_UPLOAD = "ACTION_ATTACHMENT_FILE_STOP_UPLOAD"
|
||||
const val ACTION_ATTACHMENT_FILE_START_DOWNLOAD = "ACTION_ATTACHMENT_FILE_START_DOWNLOAD"
|
||||
const val ACTION_ATTACHMENT_REMOVE = "ACTION_ATTACHMENT_REMOVE"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user