From 6f88c8bcbdb503c6ef21935930cfbbab9223d572 Mon Sep 17 00:00:00 2001 From: parneet-guraya Date: Mon, 8 Jan 2024 21:53:15 +0530 Subject: [PATCH] add cancel upload functionality Signed-off-by: parneet-guraya --- .../talk/jobs/UploadAndShareFilesWorker.kt | 55 +++++++++++++++---- .../upload/chunked/ChunkedFileUploader.kt | 27 ++++++++- .../res/drawable/ic_cancel_white_24dp.xml | 5 ++ 3 files changed, 75 insertions(+), 12 deletions(-) create mode 100644 app/src/main/res/drawable/ic_cancel_white_24dp.xml diff --git a/app/src/main/java/com/nextcloud/talk/jobs/UploadAndShareFilesWorker.kt b/app/src/main/java/com/nextcloud/talk/jobs/UploadAndShareFilesWorker.kt index 6ace53128..ee24bd099 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/UploadAndShareFilesWorker.kt +++ b/app/src/main/java/com/nextcloud/talk/jobs/UploadAndShareFilesWorker.kt @@ -60,6 +60,7 @@ import com.nextcloud.talk.utils.permissions.PlatformPermissionUtil import com.nextcloud.talk.utils.preferences.AppPreferences import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.OkHttpClient +import java.io.File import javax.inject.Inject @AutoInjector(NextcloudTalkApplication::class) @@ -91,6 +92,9 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa lateinit var roomToken: String lateinit var conversationName: String lateinit var currentUser: User + private var isChunkedUploading = false + private var file: File? = null + private var chunkedFileUploader: ChunkedFileUploader? = null @Suppress("Detekt.TooGenericExceptionCaught") override fun doWork(): Result { @@ -120,28 +124,30 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa val sourceFileUri = Uri.parse(sourceFile) fileName = FileUtils.getFileName(sourceFileUri, context) - val file = FileUtils.getFileFromUri(context, sourceFileUri) + file = FileUtils.getFileFromUri(context, sourceFileUri) val remotePath = getRemotePath(currentUser) val uploadSuccess: Boolean initNotificationSetup() - + file?.let { isChunkedUploading = it.length() > CHUNK_UPLOAD_THRESHOLD_SIZE } if (file == null) { uploadSuccess = false - } else if (file.length() > CHUNK_UPLOAD_THRESHOLD_SIZE) { - Log.d(TAG, "starting chunked upload because size is " + file.length()) + } else if (isChunkedUploading) { + Log.d(TAG, "starting chunked upload because size is " + file!!.length()) initNotificationWithPercentage() val mimeType = context.contentResolver.getType(sourceFileUri)?.toMediaTypeOrNull() - uploadSuccess = ChunkedFileUploader( + chunkedFileUploader = ChunkedFileUploader( okHttpClient, currentUser, roomToken, metaData, this - ).upload( - file, + ) + + uploadSuccess = chunkedFileUploader!!.upload( + file!!, mimeType, remotePath ) @@ -164,6 +170,9 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa if (uploadSuccess) { mNotifyManager?.cancel(notificationId) return Result.success() + } else if (isStopped) { + // since work is cancelled the result would be ignored anyways + return Result.failure() } Log.e(TAG, "Something went wrong when trying to upload file") @@ -195,6 +204,15 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa mNotifyManager!!.notify(notificationId, notification) } + override fun onStopped() { + if (file != null && isChunkedUploading) { + chunkedFileUploader?.abortUpload { + mNotifyManager?.cancel(notificationId) + } + } + super.onStopped() + } + private fun initNotificationSetup() { mNotifyManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager mBuilder = NotificationCompat.Builder( @@ -206,13 +224,17 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa private fun initNotificationWithPercentage() { notification = mBuilder!! - .setContentTitle(context.resources.getString(R.string.nc_upload_in_progess)) + .setContentTitle(getResourceString(context, R.string.nc_upload_in_progess)) .setContentText(getNotificationContentText(ZERO_PERCENT)) .setSmallIcon(R.drawable.upload_white) .setOngoing(true) .setProgress(HUNDRED_PERCENT, ZERO_PERCENT, false) .setPriority(NotificationCompat.PRIORITY_LOW) .setContentIntent(getIntentToOpenConversation()) + .addAction( + R.drawable.ic_cancel_white_24dp, getResourceString(context, R.string.nc_cancel), + getCancelUploadIntent() + ) .build() notificationId = SystemClock.uptimeMillis().toInt() @@ -221,7 +243,7 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa private fun getNotificationContentText(percentage: Int): String { return String.format( - context.resources.getString(R.string.nc_upload_notification_text), + getResourceString(context, R.string.nc_upload_notification_text), getShortenedFileName(), conversationName, percentage @@ -236,6 +258,11 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa } } + private fun getCancelUploadIntent(): PendingIntent { + return WorkManager.getInstance(applicationContext) + .createCancelPendingIntent(id) + } + private fun getIntentToOpenConversation(): PendingIntent? { val bundle = Bundle() val intent = Intent(context, MainActivity::class.java) @@ -257,9 +284,9 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa } private fun showFailedToUploadNotification() { - val failureTitle = context.resources.getString(R.string.nc_upload_failed_notification_title) + val failureTitle = getResourceString(context, R.string.nc_upload_failed_notification_title) val failureText = String.format( - context.resources.getString(R.string.nc_upload_failed_notification_text), + getResourceString(context, R.string.nc_upload_failed_notification_text), fileName ) notification = mBuilder!! @@ -275,6 +302,10 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa mNotifyManager!!.notify(SystemClock.uptimeMillis().toInt(), notification) } + private fun getResourceString(context: Context, resourceId: Int): String { + return context.resources.getString(resourceId) + } + companion object { private val TAG = UploadAndShareFilesWorker::class.simpleName private const val DEVICE_SOURCE_FILE = "DEVICE_SOURCE_FILE" @@ -301,6 +332,7 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa REQUEST_PERMISSION ) } + Build.VERSION.SDK_INT > Build.VERSION_CODES.Q -> { activity.requestPermissions( arrayOf( @@ -309,6 +341,7 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa REQUEST_PERMISSION ) } + else -> { activity.requestPermissions( arrayOf( diff --git a/app/src/main/java/com/nextcloud/talk/upload/chunked/ChunkedFileUploader.kt b/app/src/main/java/com/nextcloud/talk/upload/chunked/ChunkedFileUploader.kt index 6c7021371..23204c263 100644 --- a/app/src/main/java/com/nextcloud/talk/upload/chunked/ChunkedFileUploader.kt +++ b/app/src/main/java/com/nextcloud/talk/upload/chunked/ChunkedFileUploader.kt @@ -76,6 +76,8 @@ class ChunkedFileUploader( private var okHttpClientNoRedirects: OkHttpClient? = null private var remoteChunkUrl: String + private var uploadFolderUri: String = "" + private var isUploadAborted = false init { initHttpClient(okHttpClient, currentUser) @@ -85,7 +87,7 @@ class ChunkedFileUploader( @Suppress("Detekt.TooGenericExceptionCaught") fun upload(localFile: File, mimeType: MediaType?, targetPath: String): Boolean { try { - val uploadFolderUri: String = remoteChunkUrl + "/" + FileUtils.md5Sum(localFile) + uploadFolderUri = remoteChunkUrl + "/" + FileUtils.md5Sum(localFile) val davResource = DavResource( okHttpClientNoRedirects!!, uploadFolderUri.toHttpUrlOrNull()!! @@ -100,6 +102,7 @@ class ChunkedFileUploader( Log.d(TAG, "missingChunks: " + missingChunks.size) for (missingChunk in missingChunks) { + if (isUploadAborted) return false uploadChunk(localFile, uploadFolderUri, mimeType, missingChunk, missingChunk.length()) } @@ -327,6 +330,19 @@ class ChunkedFileUploader( } } + fun abortUpload(onSuccess: () -> Unit) { + isUploadAborted = true + DavResource( + okHttpClientNoRedirects!!, + uploadFolderUri.toHttpUrlOrNull()!! + ).delete { response: Response -> + when { + response.isSuccessful -> onSuccess() + else -> isUploadAborted = false + } + } + } + private fun getModelFromResponse(response: at.bitfire.dav4jvm.Response, remotePath: String): RemoteFileBrowserItem { val remoteFileBrowserItem = RemoteFileBrowserItem() remoteFileBrowserItem.path = Uri.decode(remotePath) @@ -353,30 +369,39 @@ class ChunkedFileUploader( is OCId -> { remoteFileBrowserItem.remoteId = property.ocId } + is ResourceType -> { remoteFileBrowserItem.isFile = !property.types.contains(ResourceType.COLLECTION) } + is GetLastModified -> { remoteFileBrowserItem.modifiedTimestamp = property.lastModified } + is GetContentType -> { remoteFileBrowserItem.mimeType = property.type } + is OCSize -> { remoteFileBrowserItem.size = property.ocSize } + is NCPreview -> { remoteFileBrowserItem.hasPreview = property.isNcPreview } + is OCFavorite -> { remoteFileBrowserItem.isFavorite = property.isOcFavorite } + is DisplayName -> { remoteFileBrowserItem.displayName = property.displayName } + is NCEncrypted -> { remoteFileBrowserItem.isEncrypted = property.isNcEncrypted } + is NCPermission -> { remoteFileBrowserItem.permissions = property.ncPermission } diff --git a/app/src/main/res/drawable/ic_cancel_white_24dp.xml b/app/src/main/res/drawable/ic_cancel_white_24dp.xml new file mode 100644 index 000000000..675c776d5 --- /dev/null +++ b/app/src/main/res/drawable/ic_cancel_white_24dp.xml @@ -0,0 +1,5 @@ + + +