add cancel upload functionality

Signed-off-by: parneet-guraya <gurayaparneet@gmail.com>
This commit is contained in:
parneet-guraya 2024-01-08 21:53:15 +05:30 committed by Marcel Hibbe
parent 64732b8155
commit 6f88c8bcbd
No known key found for this signature in database
GPG Key ID: C793F8B59F43CE7B
3 changed files with 75 additions and 12 deletions

View File

@ -60,6 +60,7 @@ import com.nextcloud.talk.utils.permissions.PlatformPermissionUtil
import com.nextcloud.talk.utils.preferences.AppPreferences import com.nextcloud.talk.utils.preferences.AppPreferences
import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import java.io.File
import javax.inject.Inject import javax.inject.Inject
@AutoInjector(NextcloudTalkApplication::class) @AutoInjector(NextcloudTalkApplication::class)
@ -91,6 +92,9 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa
lateinit var roomToken: String lateinit var roomToken: String
lateinit var conversationName: String lateinit var conversationName: String
lateinit var currentUser: User lateinit var currentUser: User
private var isChunkedUploading = false
private var file: File? = null
private var chunkedFileUploader: ChunkedFileUploader? = null
@Suppress("Detekt.TooGenericExceptionCaught") @Suppress("Detekt.TooGenericExceptionCaught")
override fun doWork(): Result { override fun doWork(): Result {
@ -120,28 +124,30 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa
val sourceFileUri = Uri.parse(sourceFile) val sourceFileUri = Uri.parse(sourceFile)
fileName = FileUtils.getFileName(sourceFileUri, context) fileName = FileUtils.getFileName(sourceFileUri, context)
val file = FileUtils.getFileFromUri(context, sourceFileUri) file = FileUtils.getFileFromUri(context, sourceFileUri)
val remotePath = getRemotePath(currentUser) val remotePath = getRemotePath(currentUser)
val uploadSuccess: Boolean val uploadSuccess: Boolean
initNotificationSetup() initNotificationSetup()
file?.let { isChunkedUploading = it.length() > CHUNK_UPLOAD_THRESHOLD_SIZE }
if (file == null) { if (file == null) {
uploadSuccess = false uploadSuccess = false
} else if (file.length() > CHUNK_UPLOAD_THRESHOLD_SIZE) { } else if (isChunkedUploading) {
Log.d(TAG, "starting chunked upload because size is " + file.length()) Log.d(TAG, "starting chunked upload because size is " + file!!.length())
initNotificationWithPercentage() initNotificationWithPercentage()
val mimeType = context.contentResolver.getType(sourceFileUri)?.toMediaTypeOrNull() val mimeType = context.contentResolver.getType(sourceFileUri)?.toMediaTypeOrNull()
uploadSuccess = ChunkedFileUploader( chunkedFileUploader = ChunkedFileUploader(
okHttpClient, okHttpClient,
currentUser, currentUser,
roomToken, roomToken,
metaData, metaData,
this this
).upload( )
file,
uploadSuccess = chunkedFileUploader!!.upload(
file!!,
mimeType, mimeType,
remotePath remotePath
) )
@ -164,6 +170,9 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa
if (uploadSuccess) { if (uploadSuccess) {
mNotifyManager?.cancel(notificationId) mNotifyManager?.cancel(notificationId)
return Result.success() 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") 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) mNotifyManager!!.notify(notificationId, notification)
} }
override fun onStopped() {
if (file != null && isChunkedUploading) {
chunkedFileUploader?.abortUpload {
mNotifyManager?.cancel(notificationId)
}
}
super.onStopped()
}
private fun initNotificationSetup() { private fun initNotificationSetup() {
mNotifyManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager mNotifyManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
mBuilder = NotificationCompat.Builder( mBuilder = NotificationCompat.Builder(
@ -206,13 +224,17 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa
private fun initNotificationWithPercentage() { private fun initNotificationWithPercentage() {
notification = mBuilder!! 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)) .setContentText(getNotificationContentText(ZERO_PERCENT))
.setSmallIcon(R.drawable.upload_white) .setSmallIcon(R.drawable.upload_white)
.setOngoing(true) .setOngoing(true)
.setProgress(HUNDRED_PERCENT, ZERO_PERCENT, false) .setProgress(HUNDRED_PERCENT, ZERO_PERCENT, false)
.setPriority(NotificationCompat.PRIORITY_LOW) .setPriority(NotificationCompat.PRIORITY_LOW)
.setContentIntent(getIntentToOpenConversation()) .setContentIntent(getIntentToOpenConversation())
.addAction(
R.drawable.ic_cancel_white_24dp, getResourceString(context, R.string.nc_cancel),
getCancelUploadIntent()
)
.build() .build()
notificationId = SystemClock.uptimeMillis().toInt() notificationId = SystemClock.uptimeMillis().toInt()
@ -221,7 +243,7 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa
private fun getNotificationContentText(percentage: Int): String { private fun getNotificationContentText(percentage: Int): String {
return String.format( return String.format(
context.resources.getString(R.string.nc_upload_notification_text), getResourceString(context, R.string.nc_upload_notification_text),
getShortenedFileName(), getShortenedFileName(),
conversationName, conversationName,
percentage 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? { private fun getIntentToOpenConversation(): PendingIntent? {
val bundle = Bundle() val bundle = Bundle()
val intent = Intent(context, MainActivity::class.java) val intent = Intent(context, MainActivity::class.java)
@ -257,9 +284,9 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa
} }
private fun showFailedToUploadNotification() { 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( val failureText = String.format(
context.resources.getString(R.string.nc_upload_failed_notification_text), getResourceString(context, R.string.nc_upload_failed_notification_text),
fileName fileName
) )
notification = mBuilder!! notification = mBuilder!!
@ -275,6 +302,10 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa
mNotifyManager!!.notify(SystemClock.uptimeMillis().toInt(), notification) mNotifyManager!!.notify(SystemClock.uptimeMillis().toInt(), notification)
} }
private fun getResourceString(context: Context, resourceId: Int): String {
return context.resources.getString(resourceId)
}
companion object { companion object {
private val TAG = UploadAndShareFilesWorker::class.simpleName private val TAG = UploadAndShareFilesWorker::class.simpleName
private const val DEVICE_SOURCE_FILE = "DEVICE_SOURCE_FILE" private const val DEVICE_SOURCE_FILE = "DEVICE_SOURCE_FILE"
@ -301,6 +332,7 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa
REQUEST_PERMISSION REQUEST_PERMISSION
) )
} }
Build.VERSION.SDK_INT > Build.VERSION_CODES.Q -> { Build.VERSION.SDK_INT > Build.VERSION_CODES.Q -> {
activity.requestPermissions( activity.requestPermissions(
arrayOf( arrayOf(
@ -309,6 +341,7 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa
REQUEST_PERMISSION REQUEST_PERMISSION
) )
} }
else -> { else -> {
activity.requestPermissions( activity.requestPermissions(
arrayOf( arrayOf(

View File

@ -76,6 +76,8 @@ class ChunkedFileUploader(
private var okHttpClientNoRedirects: OkHttpClient? = null private var okHttpClientNoRedirects: OkHttpClient? = null
private var remoteChunkUrl: String private var remoteChunkUrl: String
private var uploadFolderUri: String = ""
private var isUploadAborted = false
init { init {
initHttpClient(okHttpClient, currentUser) initHttpClient(okHttpClient, currentUser)
@ -85,7 +87,7 @@ class ChunkedFileUploader(
@Suppress("Detekt.TooGenericExceptionCaught") @Suppress("Detekt.TooGenericExceptionCaught")
fun upload(localFile: File, mimeType: MediaType?, targetPath: String): Boolean { fun upload(localFile: File, mimeType: MediaType?, targetPath: String): Boolean {
try { try {
val uploadFolderUri: String = remoteChunkUrl + "/" + FileUtils.md5Sum(localFile) uploadFolderUri = remoteChunkUrl + "/" + FileUtils.md5Sum(localFile)
val davResource = DavResource( val davResource = DavResource(
okHttpClientNoRedirects!!, okHttpClientNoRedirects!!,
uploadFolderUri.toHttpUrlOrNull()!! uploadFolderUri.toHttpUrlOrNull()!!
@ -100,6 +102,7 @@ class ChunkedFileUploader(
Log.d(TAG, "missingChunks: " + missingChunks.size) Log.d(TAG, "missingChunks: " + missingChunks.size)
for (missingChunk in missingChunks) { for (missingChunk in missingChunks) {
if (isUploadAborted) return false
uploadChunk(localFile, uploadFolderUri, mimeType, missingChunk, missingChunk.length()) 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 { private fun getModelFromResponse(response: at.bitfire.dav4jvm.Response, remotePath: String): RemoteFileBrowserItem {
val remoteFileBrowserItem = RemoteFileBrowserItem() val remoteFileBrowserItem = RemoteFileBrowserItem()
remoteFileBrowserItem.path = Uri.decode(remotePath) remoteFileBrowserItem.path = Uri.decode(remotePath)
@ -353,30 +369,39 @@ class ChunkedFileUploader(
is OCId -> { is OCId -> {
remoteFileBrowserItem.remoteId = property.ocId remoteFileBrowserItem.remoteId = property.ocId
} }
is ResourceType -> { is ResourceType -> {
remoteFileBrowserItem.isFile = !property.types.contains(ResourceType.COLLECTION) remoteFileBrowserItem.isFile = !property.types.contains(ResourceType.COLLECTION)
} }
is GetLastModified -> { is GetLastModified -> {
remoteFileBrowserItem.modifiedTimestamp = property.lastModified remoteFileBrowserItem.modifiedTimestamp = property.lastModified
} }
is GetContentType -> { is GetContentType -> {
remoteFileBrowserItem.mimeType = property.type remoteFileBrowserItem.mimeType = property.type
} }
is OCSize -> { is OCSize -> {
remoteFileBrowserItem.size = property.ocSize remoteFileBrowserItem.size = property.ocSize
} }
is NCPreview -> { is NCPreview -> {
remoteFileBrowserItem.hasPreview = property.isNcPreview remoteFileBrowserItem.hasPreview = property.isNcPreview
} }
is OCFavorite -> { is OCFavorite -> {
remoteFileBrowserItem.isFavorite = property.isOcFavorite remoteFileBrowserItem.isFavorite = property.isOcFavorite
} }
is DisplayName -> { is DisplayName -> {
remoteFileBrowserItem.displayName = property.displayName remoteFileBrowserItem.displayName = property.displayName
} }
is NCEncrypted -> { is NCEncrypted -> {
remoteFileBrowserItem.isEncrypted = property.isNcEncrypted remoteFileBrowserItem.isEncrypted = property.isNcEncrypted
} }
is NCPermission -> { is NCPermission -> {
remoteFileBrowserItem.permissions = property.ncPermission remoteFileBrowserItem.permissions = property.ncPermission
} }

View File

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