@@ -35,6 +35,7 @@ import eu.opencloud.android.MainApp
3535import eu.opencloud.android.R
3636import eu.opencloud.android.data.download.DownloadProgress
3737import eu.opencloud.android.data.download.DownloadProgressDataStore
38+ import eu.opencloud.android.domain.UseCaseResult
3839import eu.opencloud.android.domain.capabilities.usecases.GetStoredCapabilitiesUseCase
3940import eu.opencloud.android.domain.files.FileRepository
4041import eu.opencloud.android.domain.files.model.OCFile
@@ -43,6 +44,7 @@ import eu.opencloud.android.domain.files.usecases.GetFileByRemotePathUseCase
4344import eu.opencloud.android.domain.spaces.usecases.GetPersonalAndProjectSpacesForAccountUseCase
4445import eu.opencloud.android.domain.spaces.usecases.RefreshSpacesFromServerAsyncUseCase
4546import eu.opencloud.android.presentation.authentication.AccountUtils
47+ import eu.opencloud.android.usecases.synchronization.SynchronizeFileUseCase
4648import eu.opencloud.android.usecases.transfers.downloads.DownloadFileUseCase
4749import eu.opencloud.android.utils.FileStorageUtils
4850import org.koin.core.component.KoinComponent
@@ -82,6 +84,7 @@ class DownloadEverythingWorker(
8284 private val getFileByRemotePathUseCase: GetFileByRemotePathUseCase by inject()
8385 private val fileRepository: FileRepository by inject()
8486 private val downloadFileUseCase: DownloadFileUseCase by inject()
87+ private val synchronizeFileUseCase: SynchronizeFileUseCase by inject()
8588 private val progressDataStore: DownloadProgressDataStore by inject()
8689
8790 private var totalFilesFound = 0
@@ -425,34 +428,14 @@ class DownloadEverythingWorker(
425428
426429 try {
427430 if (file.isAvailableLocally) {
428- // File is already downloaded
429- filesAlreadyLocal++
430- Timber .d(" File already local: ${file.fileName} " )
431- } else {
432- // Skip files larger than currently available space to avoid guaranteed failures
433- val usableSpace = FileStorageUtils .getUsableSpace() - bytesEnqueuedInThisRun
434- if (file.length > 0 && file.length > usableSpace) {
435- filesSkipped++
436- Timber .w(" Skipping ${file.fileName} (${file.length} bytes) — not enough free space" )
431+ if (file.isDownloadedRemoteVersionCurrent()) {
432+ filesAlreadyLocal++
433+ Timber .d(" File already local and current: ${file.fileName} " )
437434 } else {
438- // Enqueue download
439- val downloadId = downloadFileUseCase(
440- DownloadFileUseCase .Params (accountName, file)
441- )
442- if (downloadId != null ) {
443- filesDownloaded++
444- filesEnqueuedInThisRun++
445- bytesEnqueuedInThisRun + = maxOf(0L , file.length)
446- Timber .i(" Enqueued download for: ${file.fileName} " )
447- if (filesEnqueuedInThisRun >= 500 ) {
448- Timber .i(" Reached max enqueued files for this run (500). Requesting retry." )
449- wasInterrupted = true
450- }
451- } else {
452- filesSkipped++
453- Timber .d(" Download already enqueued or skipped: ${file.fileName} " )
454- }
435+ synchronizeStaleLocalFile(file)
455436 }
437+ } else {
438+ enqueueDownloadIfPossible(accountName, file)
456439 }
457440
458441 // Update notification periodically (every 20 files)
@@ -465,6 +448,92 @@ class DownloadEverythingWorker(
465448 }
466449 }
467450
451+ private fun OCFile.isDownloadedRemoteVersionCurrent (): Boolean {
452+ val currentRemoteEtag = remoteEtag
453+ return currentRemoteEtag.isNullOrBlank() || etag == currentRemoteEtag
454+ }
455+
456+ private fun synchronizeStaleLocalFile (file : OCFile ) {
457+ Timber .i(
458+ " File ${file.fileName} is local but stale. Synced etag=${file.etag} , remote etag=${file.remoteEtag} "
459+ )
460+
461+ when (val useCaseResult = synchronizeFileUseCase(SynchronizeFileUseCase .Params (file))) {
462+ is UseCaseResult .Success -> handleStaleLocalSyncResult(file, useCaseResult.data)
463+ is UseCaseResult .Error -> {
464+ filesSkipped++
465+ Timber .e(useCaseResult.throwable, " Error synchronizing stale local file ${file.fileName} " )
466+ }
467+ }
468+ }
469+
470+ private fun handleStaleLocalSyncResult (file : OCFile , syncType : SynchronizeFileUseCase .SyncType ) {
471+ when (syncType) {
472+ is SynchronizeFileUseCase .SyncType .DownloadEnqueued -> {
473+ if (syncType.workerId != null ) {
474+ filesDownloaded++
475+ markDownloadEnqueued(file)
476+ Timber .i(" Enqueued download for stale local file: ${file.fileName} " )
477+ } else {
478+ filesSkipped++
479+ Timber .d(" Download already enqueued or skipped for stale local file: ${file.fileName} " )
480+ }
481+ }
482+ is SynchronizeFileUseCase .SyncType .ConflictResolvedWithCopy -> {
483+ if (syncType.workerId != null ) {
484+ filesDownloaded++
485+ markDownloadEnqueued(file)
486+ Timber .i(" Resolved conflict and enqueued download for stale local file: ${file.fileName} " )
487+ } else {
488+ filesSkipped++
489+ Timber .d(" Conflict was handled but download was not enqueued for stale local file: ${file.fileName} " )
490+ }
491+ }
492+ is SynchronizeFileUseCase .SyncType .UploadEnqueued -> {
493+ filesAlreadyLocal++
494+ Timber .i(" Local changes for ${file.fileName} were queued for upload" )
495+ }
496+ SynchronizeFileUseCase .SyncType .AlreadySynchronized -> {
497+ filesAlreadyLocal++
498+ Timber .d(" File already synchronized after stale check: ${file.fileName} " )
499+ }
500+ SynchronizeFileUseCase .SyncType .FileNotFound -> {
501+ filesSkipped++
502+ Timber .w(" File no longer exists on server: ${file.fileName} " )
503+ }
504+ }
505+ }
506+
507+ private fun enqueueDownloadIfPossible (accountName : String , file : OCFile ) {
508+ val usableSpace = FileStorageUtils .getUsableSpace() - bytesEnqueuedInThisRun
509+ if (file.length > 0 && file.length > usableSpace) {
510+ filesSkipped++
511+ Timber .w(" Skipping ${file.fileName} (${file.length} bytes) — not enough free space" )
512+ return
513+ }
514+
515+ val downloadId = downloadFileUseCase(
516+ DownloadFileUseCase .Params (accountName, file)
517+ )
518+ if (downloadId != null ) {
519+ filesDownloaded++
520+ markDownloadEnqueued(file)
521+ Timber .i(" Enqueued download for: ${file.fileName} " )
522+ } else {
523+ filesSkipped++
524+ Timber .d(" Download already enqueued or skipped: ${file.fileName} " )
525+ }
526+ }
527+
528+ private fun markDownloadEnqueued (file : OCFile ) {
529+ filesEnqueuedInThisRun++
530+ bytesEnqueuedInThisRun + = maxOf(0L , file.length)
531+ if (filesEnqueuedInThisRun >= MAX_ENQUEUED_FILES_PER_RUN ) {
532+ Timber .i(" Reached max enqueued files for this run ($MAX_ENQUEUED_FILES_PER_RUN ). Requesting retry." )
533+ wasInterrupted = true
534+ }
535+ }
536+
468537 private fun createNotificationChannel () {
469538 if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .O ) {
470539 val channel = NotificationChannel (
@@ -513,5 +582,6 @@ class DownloadEverythingWorker(
513582 private const val NOTIFICATION_ID = 9001
514583 private const val MB = 1024L * 1024L
515584 private const val MIN_FREE_SPACE_BYTES = 100L * MB
585+ private const val MAX_ENQUEUED_FILES_PER_RUN = 500
516586 }
517587}
0 commit comments