mirror of
https://github.com/nextcloud/talk-android
synced 2025-08-04 18:45:03 +01:00
add cancel upload functionality
Signed-off-by: parneet-guraya <gurayaparneet@gmail.com>
This commit is contained in:
parent
64732b8155
commit
6f88c8bcbd
@ -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(
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
5
app/src/main/res/drawable/ic_cancel_white_24dp.xml
Normal file
5
app/src/main/res/drawable/ic_cancel_white_24dp.xml
Normal 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>
|
Loading…
Reference in New Issue
Block a user