Skip to content

Commit e9b9e93

Browse files
Merge pull request #16499 from nextcloud/backport/16371/stable-3.36
[stable-3.36] fix(auto-upload): handle upload results
2 parents a07567c + 0436d22 commit e9b9e93

4 files changed

Lines changed: 75 additions & 35 deletions

File tree

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/*
2+
* Nextcloud - Android Client
3+
*
4+
* SPDX-FileCopyrightText: 2026 Alper Ozturk <alper.ozturk@nextcloud.com>
5+
* SPDX-License-Identifier: AGPL-3.0-or-later
6+
*/
7+
8+
package com.nextcloud.client.jobs.autoUpload
9+
10+
import com.nextcloud.client.database.entity.UploadEntity
11+
import com.owncloud.android.db.OCUpload
12+
13+
sealed class AutoUploadEntityResult {
14+
data object NonRetryable : AutoUploadEntityResult()
15+
data object CreationError : AutoUploadEntityResult()
16+
data object Uploaded : AutoUploadEntityResult()
17+
data class Success(val data: Pair<UploadEntity, OCUpload>) : AutoUploadEntityResult()
18+
}

app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import androidx.work.ForegroundInfo
1717
import androidx.work.WorkerParameters
1818
import com.nextcloud.client.account.User
1919
import com.nextcloud.client.account.UserAccountManager
20-
import com.nextcloud.client.database.entity.UploadEntity
2120
import com.nextcloud.client.database.entity.toOCUpload
2221
import com.nextcloud.client.database.entity.toUploadEntity
2322
import com.nextcloud.client.device.PowerManagementService
@@ -27,6 +26,7 @@ import com.nextcloud.client.jobs.upload.FileUploadWorker
2726
import com.nextcloud.client.jobs.utils.UploadErrorNotificationManager
2827
import com.nextcloud.client.network.ConnectivityService
2928
import com.nextcloud.client.preferences.SubFolderRule
29+
import com.nextcloud.utils.extensions.isNonRetryable
3030
import com.nextcloud.utils.extensions.updateStatus
3131
import com.owncloud.android.R
3232
import com.owncloud.android.datamodel.ArbitraryDataProviderImpl
@@ -37,6 +37,7 @@ import com.owncloud.android.datamodel.SyncedFolderProvider
3737
import com.owncloud.android.datamodel.UploadsStorageManager
3838
import com.owncloud.android.db.OCUpload
3939
import com.owncloud.android.db.UploadResult
40+
import com.owncloud.android.files.services.NameCollisionPolicy
4041
import com.owncloud.android.lib.common.OwnCloudAccount
4142
import com.owncloud.android.lib.common.OwnCloudClientManagerFactory
4243
import com.owncloud.android.lib.common.operations.RemoteOperationResult
@@ -309,14 +310,14 @@ class AutoUploadWorker(
309310
)
310311

311312
try {
312-
val result = createEntityAndUpload(user, localPath, remotePath)
313-
if (result == null) {
313+
val entityResult = getEntityResult(user, localPath, remotePath)
314+
if (entityResult !is AutoUploadEntityResult.Success) {
314315
repository.markFileAsHandled(localPath, syncedFolder)
315-
Log_OC.d(TAG, "Marked file as handled due to existing conflict: $localPath")
316+
Log_OC.d(TAG, "marked file as handled: $localPath")
316317
continue
317318
}
318319

319-
var (uploadEntity, upload) = result
320+
var (uploadEntity, upload) = entityResult.data
320321

321322
// if local file deleted, upload process cannot be started or retriable thus needs to be removed
322323
if (path.isEmpty() || !file.exists()) {
@@ -403,11 +404,7 @@ class AutoUploadWorker(
403404
}
404405

405406
@Suppress("ReturnCount")
406-
private fun createEntityAndUpload(
407-
user: User,
408-
localPath: String,
409-
remotePath: String
410-
): Pair<UploadEntity, OCUpload>? {
407+
private fun getEntityResult(user: User, localPath: String, remotePath: String): AutoUploadEntityResult {
411408
val (needsCharging, needsWifi, uploadAction) = getUploadSettings(syncedFolder)
412409
Log_OC.d(TAG, "creating oc upload for ${user.accountName}")
413410

@@ -419,16 +416,27 @@ class AutoUploadWorker(
419416
)
420417

421418
val lastUploadResult = uploadEntity?.lastResult?.let { UploadResult.fromValue(it) }
422-
if (lastUploadResult == UploadResult.SYNC_CONFLICT) {
423-
Log_OC.w(TAG, "Conflict already exists, skipping auto-upload: $localPath")
424-
return null
419+
if (lastUploadResult?.isNonRetryable() == true) {
420+
Log_OC.w(
421+
TAG,
422+
"last upload failed with ${lastUploadResult.value}, skipping auto-upload: $localPath"
423+
)
424+
return AutoUploadEntityResult.NonRetryable
425425
}
426426

427427
val upload = try {
428428
uploadEntity?.toOCUpload(null) ?: OCUpload(localPath, remotePath, user.accountName)
429429
} catch (_: IllegalArgumentException) {
430430
Log_OC.e(TAG, "cannot construct oc upload")
431-
return null
431+
return AutoUploadEntityResult.CreationError
432+
}
433+
434+
// only valid for skip collision policy other scenarios will be handled in UploadFileOperation.java
435+
if (upload.lastResult == UploadResult.UPLOADED &&
436+
syncedFolder.nameCollisionPolicy == NameCollisionPolicy.SKIP
437+
) {
438+
Log_OC.d(TAG, "no need to create and process this entity file is already uploaded")
439+
return AutoUploadEntityResult.Uploaded
432440
}
433441

434442
upload.apply {
@@ -445,7 +453,7 @@ class AutoUploadWorker(
445453
}
446454
}
447455

448-
return upload.toUploadEntity() to upload
456+
return AutoUploadEntityResult.Success(upload.toUploadEntity() to upload)
449457
}
450458

451459
private fun createUploadFileOperation(upload: OCUpload, user: User): UploadFileOperation = UploadFileOperation(

app/src/main/java/com/nextcloud/utils/extensions/RemoteOperationResultExtensions.kt

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,10 @@ package com.nextcloud.utils.extensions
99

1010
import com.owncloud.android.MainApp
1111
import com.owncloud.android.R
12-
import com.owncloud.android.datamodel.OCFile
1312
import com.owncloud.android.lib.common.operations.RemoteOperation
1413
import com.owncloud.android.lib.common.operations.RemoteOperationResult
1514
import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode
16-
import com.owncloud.android.lib.resources.files.model.RemoteFile
1715
import com.owncloud.android.utils.ErrorMessageAdapter
18-
import com.owncloud.android.utils.FileStorageUtils
1916

2017
@Suppress("ReturnCount")
2118
fun Pair<RemoteOperationResult<*>?, RemoteOperation<*>?>?.getErrorMessage(): String {
@@ -45,20 +42,3 @@ fun ResultCode.isFileSpecificError(): Boolean {
4542

4643
return !errorCodes.contains(this)
4744
}
48-
49-
@Suppress("Deprecation")
50-
fun RemoteOperationResult<*>?.toOCFile(): List<OCFile>? = if (this?.isSuccess == true) {
51-
data?.toOCFileList()
52-
} else {
53-
null
54-
}
55-
56-
private fun ArrayList<Any>.toOCFileList(): List<OCFile> = this.mapNotNull {
57-
val remoteFile = (it as? RemoteFile)
58-
59-
remoteFile?.let {
60-
remoteFile.toOCFile()
61-
}
62-
}
63-
64-
private fun RemoteFile?.toOCFile(): OCFile = FileStorageUtils.fillOCFile(this)
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Nextcloud - Android Client
3+
*
4+
* SPDX-FileCopyrightText: 2026 Alper Ozturk <alper.ozturk@nextcloud.com>
5+
* SPDX-License-Identifier: AGPL-3.0-or-later
6+
*/
7+
8+
package com.nextcloud.utils.extensions
9+
10+
import com.owncloud.android.db.UploadResult
11+
12+
fun UploadResult.isNonRetryable(): Boolean = when (this) {
13+
UploadResult.FILE_NOT_FOUND,
14+
UploadResult.FILE_ERROR,
15+
UploadResult.FOLDER_ERROR,
16+
UploadResult.CANNOT_CREATE_FILE,
17+
UploadResult.SYNC_CONFLICT,
18+
UploadResult.LOCAL_STORAGE_NOT_COPIED,
19+
UploadResult.VIRUS_DETECTED,
20+
UploadResult.QUOTA_EXCEEDED,
21+
UploadResult.SAME_FILE_CONFLICT,
22+
UploadResult.PRIVILEGES_ERROR,
23+
UploadResult.CREDENTIAL_ERROR,
24+
25+
// most cases covered and mapped from RemoteOperationResult. Most likely UploadResult.UNKNOWN this error will
26+
// occur again
27+
UploadResult.UNKNOWN,
28+
29+
// user's choice
30+
UploadResult.CANCELLED -> true
31+
32+
// everything else may succeed after retry
33+
else -> false
34+
}

0 commit comments

Comments
 (0)