Skip to content

Commit 9e3cfd8

Browse files
surinder-tsysalperozturk96
authored andcommitted
rebased with master
Signed-off-by: Surinder Kumar <surinder.kumar@t-systems.com>
1 parent b54926e commit 9e3cfd8

3 files changed

Lines changed: 199 additions & 47 deletions

File tree

app/src/main/java/com/nextcloud/client/jobs/upload/AlbumFileUploadWorker.kt

Lines changed: 94 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ package com.nextcloud.client.jobs.upload
99

1010
import android.app.Notification
1111
import android.content.Context
12+
import androidx.core.app.NotificationCompat
1213
import androidx.localbroadcastmanager.content.LocalBroadcastManager
1314
import androidx.work.CoroutineWorker
1415
import androidx.work.ForegroundInfo
@@ -18,6 +19,7 @@ import com.nextcloud.client.account.UserAccountManager
1819
import com.nextcloud.client.device.PowerManagementService
1920
import com.nextcloud.client.jobs.BackgroundJobManager
2021
import com.nextcloud.client.jobs.BackgroundJobManagerImpl
22+
import com.nextcloud.client.jobs.upload.FileUploadWorker.Companion.SHOW_SAME_FILE_ALREADY_EXISTS_NOTIFICATION
2123
import com.nextcloud.client.jobs.utils.UploadErrorNotificationManager
2224
import com.nextcloud.client.network.ConnectivityService
2325
import com.nextcloud.client.preferences.AppPreferences
@@ -39,11 +41,13 @@ import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCo
3941
import com.owncloud.android.lib.common.utils.Log_OC
4042
import com.owncloud.android.operations.UploadFileOperation
4143
import com.owncloud.android.operations.albums.CopyFileToAlbumOperation
44+
import com.owncloud.android.ui.notifications.NotificationUtils
4245
import com.owncloud.android.utils.theme.ViewThemeUtils
4346
import kotlinx.coroutines.Dispatchers
4447
import kotlinx.coroutines.ensureActive
4548
import kotlinx.coroutines.withContext
4649
import java.io.File
50+
import java.util.concurrent.ConcurrentHashMap
4751
import kotlin.random.Random
4852

4953
/**
@@ -68,16 +72,43 @@ class AlbumFileUploadWorker(
6872
companion object {
6973
val TAG: String = AlbumFileUploadWorker::class.java.simpleName
7074

71-
var currentUploadFileOperation: UploadFileOperation? = null
72-
73-
private const val BATCH_SIZE = 100
74-
7575
const val ALBUM_NAME = "album_name"
7676

7777
const val ACCOUNT = "data_account"
7878
const val UPLOAD_IDS = "uploads_ids"
7979
const val CURRENT_BATCH_INDEX = "batch_index"
8080
const val TOTAL_UPLOAD_SIZE = "total_upload_size"
81+
private const val BATCH_SIZE = 100
82+
83+
private val activeOperations = ConcurrentHashMap<Long, UploadFileOperation>()
84+
85+
@JvmOverloads
86+
fun cancelUpload(remotePath: String?, accountName: String?, onCompleted: () -> Unit = {}) {
87+
val operation =
88+
activeOperations.values.find { it.remotePath == remotePath && it.user.accountName == accountName }
89+
90+
operation?.let {
91+
Log_OC.d(TAG, "upload operation is cancelled: $remotePath")
92+
operation.cancel(ResultCode.USER_CANCELLED)
93+
activeOperations.remove(operation.ocUploadId)
94+
}
95+
96+
onCompleted()
97+
}
98+
99+
suspend fun cancelUploads(remotePaths: List<String>, accountName: String) {
100+
withContext(Dispatchers.IO) {
101+
remotePaths.forEach {
102+
cancelUpload(it, accountName)
103+
}
104+
}
105+
}
106+
107+
fun getCurrentUpload(id: Long?): UploadFileOperation? = activeOperations[id]
108+
109+
fun isUploading(remotePath: String?, accountName: String?): Boolean = activeOperations.values.any {
110+
it.remotePath == remotePath && it.user.accountName == accountName
111+
}
81112
}
82113

83114
private var lastPercent = 0
@@ -87,19 +118,20 @@ class AlbumFileUploadWorker(
87118
private val fileUploadEventBroadcaster = FileUploadEventBroadcaster(localBroadcastManager)
88119

89120
override suspend fun doWork(): Result = try {
121+
trySetForeground()
122+
90123
Log_OC.d(TAG, "AlbumFileUploadWorker started")
91124
val workerName = BackgroundJobManagerImpl.formatClassTag(this::class)
92125
backgroundJobManager.logStartOfWorker(workerName)
93126

94-
trySetForeground()
95-
96127
val result = uploadFiles()
97128
backgroundJobManager.logEndOfWorker(workerName, result)
98129
notificationManager.dismissNotification()
99130
result
100131
} catch (t: Throwable) {
101132
Log_OC.e(TAG, "exception $t")
102-
currentUploadFileOperation?.cancel(null)
133+
activeOperations.values.forEach { it.cancel(null) }
134+
activeOperations.clear()
103135
Result.failure()
104136
} finally {
105137
// Ensure all database operations are complete before signaling completion
@@ -111,8 +143,7 @@ class AlbumFileUploadWorker(
111143
try {
112144
val notificationTitle = notificationManager.currentOperationTitle
113145
?: context.getString(R.string.foreground_service_upload)
114-
115-
val notification = notificationManager.createSilentNotification(notificationTitle, R.drawable.uploads)
146+
val notification = createNotification(notificationTitle)
116147
updateForegroundInfo(notification)
117148
} catch (e: Exception) {
118149
// Continue without foreground service - uploads will still work
@@ -123,7 +154,7 @@ class AlbumFileUploadWorker(
123154
override suspend fun getForegroundInfo(): ForegroundInfo {
124155
val notificationTitle = notificationManager.currentOperationTitle
125156
?: context.getString(R.string.foreground_service_upload)
126-
val notification = notificationManager.createSilentNotification(notificationTitle, R.drawable.uploads)
157+
val notification = createNotification(notificationTitle)
127158

128159
return ForegroundServiceHelper.createWorkerForegroundInfo(
129160
notificationId,
@@ -141,6 +172,18 @@ class AlbumFileUploadWorker(
141172
setForeground(foregroundInfo)
142173
}
143174

175+
private fun createNotification(title: String): Notification =
176+
NotificationCompat.Builder(context, NotificationUtils.NOTIFICATION_CHANNEL_UPLOAD)
177+
.setContentTitle(title)
178+
.setSmallIcon(R.drawable.uploads)
179+
.setOngoing(true)
180+
.setSound(null)
181+
.setVibrate(null)
182+
.setOnlyAlertOnce(true)
183+
.setPriority(NotificationCompat.PRIORITY_LOW)
184+
.setSilent(true)
185+
.build()
186+
144187
@Suppress("ReturnCount", "LongMethod", "DEPRECATION")
145188
private suspend fun uploadFiles(): Result = withContext(Dispatchers.IO) {
146189
val accountName = inputData.getString(ACCOUNT)
@@ -204,7 +247,7 @@ class AlbumFileUploadWorker(
204247

205248
fileUploadEventBroadcaster.sendUploadEnqueued(context)
206249
val operation = createUploadFileOperation(upload, user)
207-
currentUploadFileOperation = operation
250+
activeOperations[upload.uploadId] = operation
208251

209252
val currentIndex = (index + 1)
210253
val currentUploadIndex = (currentIndex + previouslyUploadedFileSize)
@@ -216,11 +259,9 @@ class AlbumFileUploadWorker(
216259
)
217260

218261
val result = withContext(Dispatchers.IO) {
219-
upload(operation, albumName, user, client)
262+
upload(upload, operation, albumName, user, client)
220263
}
221-
val entity = uploadsStorageManager.uploadDao.getUploadById(upload.uploadId, accountName)
222-
uploadsStorageManager.updateStatus(entity, result.isSuccess)
223-
currentUploadFileOperation = null
264+
activeOperations.remove(upload.uploadId)
224265

225266
if (result.code == ResultCode.QUOTA_EXCEEDED) {
226267
Log_OC.w(TAG, "Quota exceeded, stopping uploads")
@@ -289,6 +330,7 @@ class AlbumFileUploadWorker(
289330

290331
@Suppress("TooGenericExceptionCaught", "DEPRECATION")
291332
private suspend fun upload(
333+
upload: OCUpload,
292334
operation: UploadFileOperation,
293335
albumName: String,
294336
user: User,
@@ -314,35 +356,43 @@ class AlbumFileUploadWorker(
314356
fileUploadEventBroadcaster.sendUploadStarted(operation, context)
315357
} catch (e: Exception) {
316358
Log_OC.e(TAG, "Error uploading", e)
317-
result = RemoteOperationResult(e)
318-
} finally {
319-
if (!isStopped) {
320-
uploadsStorageManager.updateDatabaseUploadResult(result, operation)
321-
// resolving file conflict will trigger normal file upload and shows two upload process
322-
// one for normal and one for Album upload
323-
// as customizing conflict can break normal upload
324-
// so we are removing the upload if it's a conflict
325-
// Note: this is fallback logic because default policy while uploading is RENAME
326-
// if in some case code reach here it will remove the upload
327-
// so we are checking it first and removing the upload
328-
if (result.code == ResultCode.SYNC_CONFLICT) {
329-
uploadsStorageManager.removeUpload(
330-
operation.user.accountName,
331-
operation.remotePath
359+
uploadsStorageManager.run {
360+
uploadDao.getUploadById(upload.uploadId, user.accountName)?.let { entity ->
361+
updateStatus(
362+
entity,
363+
UploadsStorageManager.UploadStatus.UPLOAD_FAILED
332364
)
333-
} else {
334-
UploadErrorNotificationManager.handleResult(
335-
context,
336-
notificationManager,
337-
operation,
338-
result,
339-
onSameFileConflict = {
340-
withContext(Dispatchers.Main) {
365+
}
366+
}
367+
result = RemoteOperationResult(e)
368+
}
369+
370+
if (!isStopped) {
371+
// resolving file conflict will trigger normal file upload and shows two upload process
372+
// one for normal and one for Album upload
373+
// as customizing conflict can break normal upload
374+
// so we are removing the upload if it's a conflict
375+
// Note: this is fallback logic because default policy while uploading is RENAME
376+
// if in some case code reach here it will remove the upload
377+
// so we are checking it first and removing the upload
378+
if (result.code == ResultCode.SYNC_CONFLICT) {
379+
activeOperations.remove(upload.uploadId)
380+
} else {
381+
UploadErrorNotificationManager.handleResult(
382+
context,
383+
notificationManager,
384+
operation,
385+
result,
386+
onSameFileConflict = {
387+
withContext(Dispatchers.Main) {
388+
val showSameFileAlreadyExistsNotification =
389+
inputData.getBoolean(SHOW_SAME_FILE_ALREADY_EXISTS_NOTIFICATION, false)
390+
if (showSameFileAlreadyExistsNotification) {
341391
notificationManager.showSameFileAlreadyExistsNotification(operation.fileName)
342392
}
343393
}
344-
)
345-
}
394+
}
395+
)
346396
}
347397
}
348398

@@ -368,6 +418,9 @@ class AlbumFileUploadWorker(
368418

369419
if (percent != lastPercent && (currentTime - lastUpdateTime) >= minProgressUpdateInterval) {
370420
notificationManager.run {
421+
val currentUploadFileOperation =
422+
activeOperations.values.find { it.originalStoragePath == fileAbsoluteName }
423+
371424
val accountName = currentUploadFileOperation?.user?.accountName
372425
val remotePath = currentUploadFileOperation?.remotePath
373426

@@ -376,7 +429,7 @@ class AlbumFileUploadWorker(
376429
if (accountName != null && remotePath != null) {
377430
val key: String = FileUploadHelper.buildRemoteName(accountName, remotePath)
378431
val boundListener = FileUploadHelper.mBoundListeners[key]
379-
val filename = currentUploadFileOperation?.fileName ?: ""
432+
val filename = currentUploadFileOperation.fileName ?: ""
380433

381434
boundListener?.onTransferProgress(
382435
progressRate,

app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadWorker.kt

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ package com.nextcloud.client.jobs.upload
99

1010
import android.app.Notification
1111
import android.content.Context
12+
import androidx.core.app.NotificationCompat
1213
import androidx.localbroadcastmanager.content.LocalBroadcastManager
1314
import androidx.work.CoroutineWorker
1415
import androidx.work.ForegroundInfo
@@ -43,6 +44,7 @@ import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCo
4344
import com.owncloud.android.lib.common.utils.Log_OC
4445
import com.owncloud.android.operations.UploadFileOperation
4546
import com.owncloud.android.operations.factory.UploadFileOperationFactory
47+
import com.owncloud.android.ui.notifications.NotificationUtils
4648
import com.owncloud.android.utils.theme.ViewThemeUtils
4749
import kotlinx.coroutines.Dispatchers
4850
import kotlinx.coroutines.ensureActive
@@ -159,8 +161,7 @@ class FileUploadWorker(
159161
try {
160162
val notificationTitle = notificationManager.currentOperationTitle
161163
?: context.getString(R.string.foreground_service_upload)
162-
163-
val notification = notificationManager.createSilentNotification(notificationTitle, R.drawable.uploads)
164+
val notification = createNotification(notificationTitle)
164165
updateForegroundInfo(notification)
165166
} catch (e: Exception) {
166167
// Continue without foreground service - uploads will still work
@@ -171,7 +172,8 @@ class FileUploadWorker(
171172
override suspend fun getForegroundInfo(): ForegroundInfo {
172173
val notificationTitle = notificationManager.currentOperationTitle
173174
?: context.getString(R.string.foreground_service_upload)
174-
val notification = notificationManager.createSilentNotification(notificationTitle, R.drawable.uploads)
175+
val notification = createNotification(notificationTitle)
176+
175177
return ForegroundServiceHelper.createWorkerForegroundInfo(
176178
notificationId,
177179
notification,
@@ -188,6 +190,18 @@ class FileUploadWorker(
188190
setForeground(foregroundInfo)
189191
}
190192

193+
private fun createNotification(title: String): Notification =
194+
NotificationCompat.Builder(context, NotificationUtils.NOTIFICATION_CHANNEL_UPLOAD)
195+
.setContentTitle(title)
196+
.setSmallIcon(R.drawable.uploads)
197+
.setOngoing(true)
198+
.setSound(null)
199+
.setVibrate(null)
200+
.setOnlyAlertOnce(true)
201+
.setPriority(NotificationCompat.PRIORITY_LOW)
202+
.setSilent(true)
203+
.build()
204+
191205
@Suppress("ReturnCount", "LongMethod", "DEPRECATION")
192206
private suspend fun uploadFiles(): Result = withContext(Dispatchers.IO) {
193207
val accountName = inputData.getString(ACCOUNT)

0 commit comments

Comments
 (0)