diff --git a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadEntityResult.kt b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadEntityResult.kt new file mode 100644 index 000000000000..bd6b8a208d5c --- /dev/null +++ b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadEntityResult.kt @@ -0,0 +1,18 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.client.jobs.autoUpload + +import com.nextcloud.client.database.entity.UploadEntity +import com.owncloud.android.db.OCUpload + +sealed class AutoUploadEntityResult { + data object NonRetryable : AutoUploadEntityResult() + data object CreationError : AutoUploadEntityResult() + data object Uploaded : AutoUploadEntityResult() + data class Success(val data: Pair) : AutoUploadEntityResult() +} diff --git a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt index db464ccfa11d..603a98e6a0bd 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt @@ -16,7 +16,6 @@ import androidx.work.ForegroundInfo import androidx.work.WorkerParameters import com.nextcloud.client.account.User import com.nextcloud.client.account.UserAccountManager -import com.nextcloud.client.database.entity.UploadEntity import com.nextcloud.client.database.entity.toOCUpload import com.nextcloud.client.database.entity.toUploadEntity import com.nextcloud.client.device.PowerManagementService @@ -25,6 +24,7 @@ import com.nextcloud.client.jobs.upload.FileUploadWorker import com.nextcloud.client.jobs.utils.UploadErrorNotificationManager import com.nextcloud.client.network.ConnectivityService import com.nextcloud.client.preferences.SubFolderRule +import com.nextcloud.utils.extensions.isNonRetryable import com.nextcloud.utils.extensions.updateStatus import com.owncloud.android.R import com.owncloud.android.datamodel.ArbitraryDataProviderImpl @@ -35,6 +35,7 @@ import com.owncloud.android.datamodel.SyncedFolderProvider import com.owncloud.android.datamodel.UploadsStorageManager import com.owncloud.android.db.OCUpload import com.owncloud.android.db.UploadResult +import com.owncloud.android.files.services.NameCollisionPolicy import com.owncloud.android.lib.common.OwnCloudAccount import com.owncloud.android.lib.common.OwnCloudClientManagerFactory import com.owncloud.android.lib.common.operations.RemoteOperationResult @@ -304,14 +305,14 @@ class AutoUploadWorker( ) try { - val result = createEntityAndUpload(user, localPath, remotePath) - if (result == null) { + val entityResult = getEntityResult(user, localPath, remotePath) + if (entityResult !is AutoUploadEntityResult.Success) { repository.markFileAsHandled(localPath, syncedFolder) - Log_OC.d(TAG, "Marked file as handled due to existing conflict: $localPath") + Log_OC.d(TAG, "marked file as handled: $localPath") continue } - var (uploadEntity, upload) = result + var (uploadEntity, upload) = entityResult.data // if local file deleted, upload process cannot be started or retriable thus needs to be removed if (path.isEmpty() || !file.exists()) { @@ -391,11 +392,7 @@ class AutoUploadWorker( } @Suppress("ReturnCount") - private fun createEntityAndUpload( - user: User, - localPath: String, - remotePath: String - ): Pair? { + private fun getEntityResult(user: User, localPath: String, remotePath: String): AutoUploadEntityResult { val (needsCharging, needsWifi, uploadAction) = getUploadSettings(syncedFolder) Log_OC.d(TAG, "creating oc upload for ${user.accountName}") @@ -407,16 +404,27 @@ class AutoUploadWorker( ) val lastUploadResult = uploadEntity?.lastResult?.let { UploadResult.fromValue(it) } - if (lastUploadResult == UploadResult.SYNC_CONFLICT) { - Log_OC.w(TAG, "Conflict already exists, skipping auto-upload: $localPath") - return null + if (lastUploadResult?.isNonRetryable() == true) { + Log_OC.w( + TAG, + "last upload failed with ${lastUploadResult.value}, skipping auto-upload: $localPath" + ) + return AutoUploadEntityResult.NonRetryable } val upload = try { uploadEntity?.toOCUpload(null) ?: OCUpload(localPath, remotePath, user.accountName) } catch (_: IllegalArgumentException) { Log_OC.e(TAG, "cannot construct oc upload") - return null + return AutoUploadEntityResult.CreationError + } + + // only valid for skip collision policy other scenarios will be handled in UploadFileOperation.java + if (upload.lastResult == UploadResult.UPLOADED && + syncedFolder.nameCollisionPolicy == NameCollisionPolicy.SKIP + ) { + Log_OC.d(TAG, "no need to create and process this entity file is already uploaded") + return AutoUploadEntityResult.Uploaded } upload.apply { @@ -433,7 +441,7 @@ class AutoUploadWorker( } } - return upload.toUploadEntity() to upload + return AutoUploadEntityResult.Success(upload.toUploadEntity() to upload) } private fun createUploadFileOperation(upload: OCUpload, user: User): UploadFileOperation = UploadFileOperation( diff --git a/app/src/main/java/com/nextcloud/utils/extensions/RemoteOperationResultExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/RemoteOperationResultExtensions.kt index caf0ad8b99f8..3a9f73c81f5f 100644 --- a/app/src/main/java/com/nextcloud/utils/extensions/RemoteOperationResultExtensions.kt +++ b/app/src/main/java/com/nextcloud/utils/extensions/RemoteOperationResultExtensions.kt @@ -9,13 +9,10 @@ package com.nextcloud.utils.extensions import com.owncloud.android.MainApp import com.owncloud.android.R -import com.owncloud.android.datamodel.OCFile import com.owncloud.android.lib.common.operations.RemoteOperation import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode -import com.owncloud.android.lib.resources.files.model.RemoteFile import com.owncloud.android.utils.ErrorMessageAdapter -import com.owncloud.android.utils.FileStorageUtils @Suppress("ReturnCount") fun Pair?, RemoteOperation<*>?>?.getErrorMessage(): String { @@ -45,20 +42,3 @@ fun ResultCode.isFileSpecificError(): Boolean { return !errorCodes.contains(this) } - -@Suppress("Deprecation") -fun RemoteOperationResult<*>?.toOCFile(): List? = if (this?.isSuccess == true) { - data?.toOCFileList() -} else { - null -} - -private fun ArrayList.toOCFileList(): List = this.mapNotNull { - val remoteFile = (it as? RemoteFile) - - remoteFile?.let { - remoteFile.toOCFile() - } -} - -private fun RemoteFile?.toOCFile(): OCFile = FileStorageUtils.fillOCFile(this) diff --git a/app/src/main/java/com/nextcloud/utils/extensions/UploadResultExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/UploadResultExtensions.kt new file mode 100644 index 000000000000..9c1f1218e57a --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/extensions/UploadResultExtensions.kt @@ -0,0 +1,34 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.utils.extensions + +import com.owncloud.android.db.UploadResult + +fun UploadResult.isNonRetryable(): Boolean = when (this) { + UploadResult.FILE_NOT_FOUND, + UploadResult.FILE_ERROR, + UploadResult.FOLDER_ERROR, + UploadResult.CANNOT_CREATE_FILE, + UploadResult.SYNC_CONFLICT, + UploadResult.LOCAL_STORAGE_NOT_COPIED, + UploadResult.VIRUS_DETECTED, + UploadResult.QUOTA_EXCEEDED, + UploadResult.SAME_FILE_CONFLICT, + UploadResult.PRIVILEGES_ERROR, + UploadResult.CREDENTIAL_ERROR, + + // most cases covered and mapped from RemoteOperationResult. Most likely UploadResult.UNKNOWN this error will + // occur again + UploadResult.UNKNOWN, + + // user's choice + UploadResult.CANCELLED -> true + + // everything else may succeed after retry + else -> false +}