Răsfoiți Sursa

add cancel upload functionality

Signed-off-by: parneet-guraya <gurayaparneet@gmail.com>
parneet-guraya 1 an în urmă
părinte
comite
6f88c8bcbd

+ 44 - 11
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(

+ 26 - 1
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
             }

+ 5 - 0
app/src/main/res/drawable/ic_cancel_white_24dp.xml

@@ -0,0 +1,5 @@
+<vector android:height="24dp" android:tint="#FFFFFF"
+    android:viewportHeight="24" android:viewportWidth="24"
+    android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="@android:color/white" android:pathData="M12,2C6.47,2 2,6.47 2,12s4.47,10 10,10 10,-4.47 10,-10S17.53,2 12,2zM17,15.59L15.59,17 12,13.41 8.41,17 7,15.59 10.59,12 7,8.41 8.41,7 12,10.59 15.59,7 17,8.41 13.41,12 17,15.59z"/>
+</vector>