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 777d03235757..1c7e20031e2f 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 @@ -44,6 +44,7 @@ import com.owncloud.android.lib.common.utils.Log_OC import com.owncloud.android.lib.resources.status.OCCapability import com.owncloud.android.operations.UploadFileOperation import com.owncloud.android.ui.activity.SettingsActivity +import com.owncloud.android.utils.PermissionUtil import com.owncloud.android.utils.theme.CapabilityUtils import com.owncloud.android.utils.theme.ViewThemeUtils import kotlinx.coroutines.Dispatchers @@ -53,6 +54,7 @@ import kotlinx.coroutines.isActive import kotlinx.coroutines.withContext import java.io.File import kotlin.coroutines.cancellation.CancellationException +import kotlin.time.Duration.Companion.milliseconds @Suppress("LongParameterList", "TooManyFunctions", "TooGenericExceptionCaught") class AutoUploadWorker( @@ -94,6 +96,13 @@ class AutoUploadWorker( Log_OC.d(TAG, syncedFolder.getLog()) + if (!PermissionUtil.checkStoragePermission(context)) { + Log_OC.w( + TAG, + "⚠️ storage/media permission missing or partial — MediaStore scan may return no files" + ) + } + if (canExitEarly(syncFolderId)) { return Result.success() } @@ -274,7 +283,7 @@ class AutoUploadWorker( filePathsWithIds.forEachIndexed { batchIndex, (path, id) -> ensureActive() - delay(retryPolicy.getDelay()) + delay(retryPolicy.getDelay().milliseconds) val file = File(path) val localPath = file.absolutePath @@ -308,8 +317,8 @@ class AutoUploadWorker( val operation = createUploadFileOperation(upload, user) Log_OC.d(TAG, "🕒 uploading: $localPath, id: $generatedId") - val result = operation.execute(client) fileUploadEventBroadcaster.sendUploadStarted(operation, context) + val result = operation.execute(client) UploadErrorNotificationManager.handleResult( context, @@ -330,7 +339,6 @@ class AutoUploadWorker( "❌ upload failed $localPath (${upload.accountName}): ${result.logMessage}" ) - // Mark CONFLICT files as handled to prevent retries if (result.code.isConflict()) { repository.markFileAsHandled(localPath, syncedFolder) Log_OC.w(TAG, "Marked CONFLICT file as handled: $localPath") diff --git a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/FileSystemRepository.kt b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/FileSystemRepository.kt index 6ca9e37629fc..0746c4e66d99 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/FileSystemRepository.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/FileSystemRepository.kt @@ -8,6 +8,7 @@ package com.nextcloud.client.jobs.autoUpload import android.content.Context +import android.database.Cursor import android.net.Uri import android.os.Build import android.provider.MediaStore @@ -142,38 +143,32 @@ class FileSystemRepository( null ) - cursor?.use { - val idxData = cursor.getColumnIndex(MediaStore.MediaColumns.DATA) - val idxModified = cursor.getColumnIndex(MediaStore.MediaColumns.DATE_MODIFIED) - val idxAdded = cursor.getColumnIndex(MediaStore.MediaColumns.DATE_ADDED) + if (cursor == null) { + Log_OC.w(TAG, "MediaStore query returned null (missing permission or unavailable provider): $uri") + return + } - if (idxData == -1) { - Log_OC.e(TAG, "MediaStore column DATA missing — cannot process URI: $uri") - return - } + cursor.use { insertFromCursor(it, uri, syncedFolder, checkFileType) } + } - while (cursor.moveToNext()) { - val filePath = cursor.getString(idxData) + private fun insertFromCursor(cursor: Cursor, uri: Uri, syncedFolder: SyncedFolder, checkFileType: Boolean) { + val idxData = cursor.getColumnIndex(MediaStore.MediaColumns.DATA) + val idxModified = cursor.getColumnIndex(MediaStore.MediaColumns.DATE_MODIFIED) + val idxAdded = cursor.getColumnIndex(MediaStore.MediaColumns.DATE_ADDED) - val lastModifiedMs = if (idxModified != -1) { - cursor.getLong(idxModified) * 1000 - } else { - null - } + if (idxData == -1) { + Log_OC.e(TAG, "MediaStore column DATA missing — cannot process URI: $uri") + return + } - val creationTimeMs = if (idxAdded != -1) { - cursor.getLong(idxAdded) * 1000 - } else { - null - } + while (cursor.moveToNext()) { + val filePath = cursor.getString(idxData) + val lastModifiedMs = if (idxModified != -1) cursor.getLong(idxModified) * 1000 else null + val creationTimeMs = if (idxAdded != -1) cursor.getLong(idxAdded) * 1000 else null - Log_OC.d( - TAG, - "Found file: $filePath (created=$creationTimeMs, modified=$lastModifiedMs)" - ) + Log_OC.d(TAG, "Found file: $filePath (created=$creationTimeMs, modified=$lastModifiedMs)") - insertOrReplace(filePath, lastModifiedMs, creationTimeMs, syncedFolder, checkFileType) - } + insertOrReplace(filePath, lastModifiedMs, creationTimeMs, syncedFolder, checkFileType) } } diff --git a/app/src/main/java/com/nextcloud/utils/extensions/UploadResultExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/UploadResultExtensions.kt index 4ca297751522..96fc79da7023 100644 --- a/app/src/main/java/com/nextcloud/utils/extensions/UploadResultExtensions.kt +++ b/app/src/main/java/com/nextcloud/utils/extensions/UploadResultExtensions.kt @@ -25,10 +25,6 @@ fun UploadResult.isNonRetryable(): Boolean = when (this) { 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