From 715718f3a2eeaa18b66a4f7f43a25cc95eef04ce Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Mon, 28 Sep 2026 11:07:56 +0200 Subject: [PATCH 01/15] add missing items for mFilesToSyncContents Signed-off-by: alperozturk96 --- .../com/nextcloud/utils/extensions/OCFileExtensions.kt | 8 ++++++++ .../android/operations/RefreshFolderOperation.java | 6 ++++++ 2 files changed, 14 insertions(+) diff --git a/app/src/main/java/com/nextcloud/utils/extensions/OCFileExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/OCFileExtensions.kt index 5ffe8829d85e..030209365c6e 100644 --- a/app/src/main/java/com/nextcloud/utils/extensions/OCFileExtensions.kt +++ b/app/src/main/java/com/nextcloud/utils/extensions/OCFileExtensions.kt @@ -87,6 +87,14 @@ private fun transformToRows(list: List, layout: GalleryRowLayout): List< .map { chunk -> GalleryRow(chunk, layout.measure(chunk)) } } +fun OCFile?.isDownloadedFileChanged(serverFile: OCFile): Boolean { + if (this == null || !isDown || serverFile.isFolder) { + return false + } + + return etag.eTagChanged(serverFile.etag) +} + fun OCFile.toEncryptionEvent(encrypt: Boolean): EncryptionEvent = EncryptionEvent( localId, remoteId, diff --git a/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java b/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java index dc27a5f236a3..688e558a2ae1 100644 --- a/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java @@ -19,6 +19,7 @@ import com.nextcloud.utils.ResultParser; import com.nextcloud.utils.e2ee.E2EVersionHelper; import com.nextcloud.utils.share.UnifiedShareSharees; +import com.nextcloud.utils.extensions.OCFileExtensionsKt; import com.nextcloud.utils.extensions.StringExtensionsKt; import com.owncloud.android.datamodel.ArbitraryDataProvider; import com.owncloud.android.datamodel.ArbitraryDataProviderImpl; @@ -619,6 +620,11 @@ private void synchronizeData(List folderAndFiles) { updatedFile.setEncrypted(encrypted); updatedFile.setReadOnly(localFile != null && localFile.isReadOnly()); + if (!encrypted && OCFileExtensionsKt.isDownloadedFileChanged(localFile, remoteFile)) { + mFilesToSyncContents.add(new SynchronizeFileOperation(updatedFile, remoteFile, user, true, mContext, + fileDataStorageManager, true)); + } + updatedFiles.add(updatedFile); } From fe1288e5a5b45e647b34d365110f7b9a04f60361 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Mon, 28 Sep 2026 13:23:52 +0200 Subject: [PATCH 02/15] remove sync in progress dialog Signed-off-by: alperozturk96 --- .../com/owncloud/android/ui/helpers/FileOperationsHelper.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java b/app/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java index f0598060e40a..1770a367fa8c 100755 --- a/app/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java +++ b/app/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java @@ -310,8 +310,6 @@ public void openFile(OCFile file) { return; } - fileActivity.showLoadingDialog(fileActivity.getResources().getString(R.string.sync_in_progress)); - new Thread(() -> { User user = currentAccount.getUser(); final var storageManager = new FileDataStorageManager(user, fileActivity.getContentResolver()); @@ -321,7 +319,6 @@ public void openFile(OCFile file) { final var sfo = new SynchronizeFileOperation(file,null, user, true, fileActivity, storageManager, false); final var result = sfo.execute(fileActivity); - fileActivity.dismissLoadingDialog(); if (result.getCode() == RemoteOperationResult.ResultCode.SYNC_CONFLICT) { // ISSUE 5: if the user is not running the app (this is a service!), // this can be very intrusive; a notification should be preferred From f96ae01e11ee8af3c8e9e431cb71ba5fec6281cc Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Mon, 28 Sep 2026 13:40:06 +0200 Subject: [PATCH 03/15] use kotlin durations Signed-off-by: alperozturk96 --- .../owncloud/android/operations/FolderRefreshScheduler.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/operations/FolderRefreshScheduler.kt b/app/src/main/java/com/owncloud/android/operations/FolderRefreshScheduler.kt index 5d3a52027c3c..9f17a571533e 100644 --- a/app/src/main/java/com/owncloud/android/operations/FolderRefreshScheduler.kt +++ b/app/src/main/java/com/owncloud/android/operations/FolderRefreshScheduler.kt @@ -20,11 +20,11 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.isActive import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import kotlin.time.Duration.Companion.milliseconds +import kotlin.time.Duration.Companion.seconds class FolderRefreshScheduler(private val activity: FileDisplayActivity) { companion object { - private const val ETAG_POLL_INTERVAL_MS = 30_000L + private val ETAG_POLL_INTERVAL = 30.seconds private const val TAG = "FolderRefreshScheduler" } @@ -35,12 +35,12 @@ class FolderRefreshScheduler(private val activity: FileDisplayActivity) { job = activity.lifecycleScope.launch { while (isActive) { - delay(ETAG_POLL_INTERVAL_MS.milliseconds) + delay(ETAG_POLL_INTERVAL) checkAndRefreshIfETagChanged() } } - Log_OC.d(TAG, "eTag polling started interval 30 seconds") + Log_OC.d(TAG, "eTag polling started interval $ETAG_POLL_INTERVAL") } fun stop() { From 9eee0615e1882d39d3a0735cd08606a38da7bb77 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Mon, 28 Sep 2026 13:42:19 +0200 Subject: [PATCH 04/15] reduce auto folder refresh to 10 seconds Signed-off-by: alperozturk96 --- .../com/owncloud/android/operations/FolderRefreshScheduler.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/owncloud/android/operations/FolderRefreshScheduler.kt b/app/src/main/java/com/owncloud/android/operations/FolderRefreshScheduler.kt index 9f17a571533e..d30e593a5749 100644 --- a/app/src/main/java/com/owncloud/android/operations/FolderRefreshScheduler.kt +++ b/app/src/main/java/com/owncloud/android/operations/FolderRefreshScheduler.kt @@ -24,7 +24,7 @@ import kotlin.time.Duration.Companion.seconds class FolderRefreshScheduler(private val activity: FileDisplayActivity) { companion object { - private val ETAG_POLL_INTERVAL = 30.seconds + private val ETAG_POLL_INTERVAL = 10.seconds private const val TAG = "FolderRefreshScheduler" } From fb5fa220812a0fdc900d664eeae5a932ece7da28 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Mon, 5 Oct 2026 12:09:49 +0200 Subject: [PATCH 05/15] Only add when user open the folder first time not every 10 seconds. Signed-off-by: alperozturk96 --- .../android/operations/FolderRefreshScheduler.kt | 4 ++-- .../operations/RefreshFolderOperation.java | 9 ++++++++- .../android/ui/activity/FileDisplayActivity.kt | 15 +++++++++++---- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/operations/FolderRefreshScheduler.kt b/app/src/main/java/com/owncloud/android/operations/FolderRefreshScheduler.kt index d30e593a5749..019aa478846a 100644 --- a/app/src/main/java/com/owncloud/android/operations/FolderRefreshScheduler.kt +++ b/app/src/main/java/com/owncloud/android/operations/FolderRefreshScheduler.kt @@ -90,7 +90,7 @@ class FolderRefreshScheduler(private val activity: FileDisplayActivity) { when (result.code) { RemoteOperationResult.ResultCode.ETAG_CHANGED -> { Log_OC.i(TAG, "eTag poll → eTag changed for '${currentDir.remotePath}', triggering sync") - activity.startSyncFolderOperation(currentDir, ignoreETag = true) + activity.startSyncFolderOperation(currentDir, ignoreETag = true, syncChangedDownloadedFiles = false) } RemoteOperationResult.ResultCode.ETAG_UNCHANGED -> { @@ -99,7 +99,7 @@ class FolderRefreshScheduler(private val activity: FileDisplayActivity) { RemoteOperationResult.ResultCode.FILE_NOT_FOUND -> { Log_OC.w(TAG, "eTag poll → directory not found on server") - activity.startSyncFolderOperation(currentDir, ignoreETag = true) + activity.startSyncFolderOperation(currentDir, ignoreETag = true, syncChangedDownloadedFiles = false) } else -> { diff --git a/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java b/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java index 688e558a2ae1..b08f6c109ec1 100644 --- a/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java @@ -155,6 +155,8 @@ public class RefreshFolderOperation extends RemoteOperation { private final List mFilesToSyncContents; // this will be used for every file when 'folder synchronization' replaces 'folder download' + private boolean syncChangedDownloadedFiles = true; + /** * Creates a new instance of {@link RefreshFolderOperation}. @@ -244,6 +246,10 @@ public Map getForgottenLocalFiles() { return mForgottenLocalFiles; } + public void setSyncChangedDownloadedFiles(boolean syncChangedDownloadedFiles) { + this.syncChangedDownloadedFiles = syncChangedDownloadedFiles; + } + /** * Returns the list of files and folders contained in the synchronized folder, if called after synchronization is * complete. @@ -620,7 +626,8 @@ private void synchronizeData(List folderAndFiles) { updatedFile.setEncrypted(encrypted); updatedFile.setReadOnly(localFile != null && localFile.isReadOnly()); - if (!encrypted && OCFileExtensionsKt.isDownloadedFileChanged(localFile, remoteFile)) { + if (syncChangedDownloadedFiles && !encrypted && + OCFileExtensionsKt.isDownloadedFileChanged(localFile, remoteFile)) { mFilesToSyncContents.add(new SynchronizeFileOperation(updatedFile, remoteFile, user, true, mContext, fileDataStorageManager, true)); } diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt index b2ba6f9dfafc..28b6da2ece69 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt @@ -2600,14 +2600,19 @@ class FileDisplayActivity : * @param ignoreFocus reloads file list even without focus, e.g. on tablet mode, focus can still be in detail view */ @JvmOverloads - fun startSyncFolderOperation(folder: OCFile?, ignoreETag: Boolean, ignoreFocus: Boolean = false) { + fun startSyncFolderOperation( + folder: OCFile?, + ignoreETag: Boolean, + ignoreFocus: Boolean = false, + syncChangedDownloadedFiles: Boolean = true + ) { Log_OC.d(TAG, "startSyncFolderOperation called, ignoreEtag: $ignoreETag, ignoreFocus: $ignoreFocus") if (!searchQuery.isNullOrEmpty() || !user.isPresent) { return } - val syncFolder = Runnable { executeSyncFolderOperation(folder, ignoreETag) } + val syncFolder = Runnable { executeSyncFolderOperation(folder, ignoreETag, syncChangedDownloadedFiles) } // The refresh must not run while another window floats over the activity, e.g. a dialog that is being // dismissed or a rotation. Rather than waiting a fixed delay run right away when it already has focus @@ -2633,7 +2638,7 @@ class FileDisplayActivity : } } - private fun executeSyncFolderOperation(folder: OCFile?, ignoreETag: Boolean) { + private fun executeSyncFolderOperation(folder: OCFile?, ignoreETag: Boolean, syncChangedDownloadedFiles: Boolean) { val folder = folder ?: return user.ifPresent { user -> @@ -2647,7 +2652,9 @@ class FileDisplayActivity : storageManager, user, applicationContext - ).execute( + ).apply { + setSyncChangedDownloadedFiles(syncChangedDownloadedFiles) + }.execute( account, this, { _, _ -> onSyncFinished() }, From 0161c67efde6dd74b0b96eaa24462f8fd2f2bf41 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Mon, 5 Oct 2026 12:38:14 +0200 Subject: [PATCH 06/15] Check local eTag if it's outdated then run SynchronizeFileOperation else skip. Signed-off-by: alperozturk96 # Conflicts: # app/src/main/java/com/nextcloud/utils/extensions/OCFileExtensions.kt --- .../ui/helpers/FileOperationsHelper.java | 28 +++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java b/app/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java index 1770a367fa8c..78473c9c83a6 100755 --- a/app/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java +++ b/app/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java @@ -313,6 +313,14 @@ public void openFile(OCFile file) { new Thread(() -> { User user = currentAccount.getUser(); final var storageManager = new FileDataStorageManager(user, fileActivity.getContentResolver()); + final var storedFile = storageManager.getFileById(file.getFileId()); + final boolean isDownloadedAndUpToDate = storedFile != null && storedFile.isDown() && + !OCFileExtensionsKt.isLocalETagOutdated(storedFile); + if (isDownloadedAndUpToDate) { + startOpenFileIntent(openFileWithIntent); + return; + } + // a fresh object is needed; many things could have occurred to the file // since it was registered to observe again, assuming that local files // are linked to a remote file AT MOST, SOMETHING TO BE DONE; @@ -348,17 +356,21 @@ public void openFile(OCFile file) { } } - fileActivity.runOnUiThread(() -> { - try { - openFileWithIntent.setFlags(openFileWithIntent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK); - fileActivity.startActivity(openFileWithIntent); - } catch (ActivityNotFoundException exception) { - SnackbarUtil.show(fileActivity, R.string.file_list_no_app_for_file_type); - } - }); + startOpenFileIntent(openFileWithIntent); }).start(); } + private void startOpenFileIntent(Intent openFileWithIntent) { + fileActivity.runOnUiThread(() -> { + try { + openFileWithIntent.setFlags(openFileWithIntent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK); + fileActivity.startActivity(openFileWithIntent); + } catch (ActivityNotFoundException exception) { + SnackbarUtil.show(fileActivity, R.string.file_list_no_app_for_file_type); + } + }); + } + public void openFileAsRichDocument(OCFile file, Context context) { Intent collaboraWebViewIntent = new Intent(context, RichDocumentsEditorWebView.class); collaboraWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_TITLE, "Collabora"); From 452e68f602466a0d528e3a9ff18e6b85b10f9bf9 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Mon, 5 Oct 2026 12:56:01 +0200 Subject: [PATCH 07/15] simplify openFile function Signed-off-by: alperozturk96 --- .../ui/helpers/FileOperationsHelper.java | 109 +++++++++--------- 1 file changed, 57 insertions(+), 52 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java b/app/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java index 78473c9c83a6..9adcf301898b 100755 --- a/app/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java +++ b/app/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java @@ -36,7 +36,6 @@ import com.nextcloud.client.jobs.upload.FileUploadHelper; import com.nextcloud.client.jobs.upload.FileUploadWorker; import com.nextcloud.client.network.ConnectivityService; -import com.nextcloud.utils.AppConstants; import com.nextcloud.utils.EditorUtils; import com.nextcloud.utils.SnackbarUtil; import com.nextcloud.utils.extensions.OCFileExtensionsKt; @@ -94,7 +93,6 @@ import java.util.Locale; import java.util.Optional; import java.util.Set; -import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -195,8 +193,7 @@ public void startSyncForFileAndIntent(OCFile file, Intent intent) { // check if latest sync is >30s ago OCFile parent = storageManager.getFileById(file.getParentId()); - if (parent != null && - parent.getLastSyncDateForData() + TimeUnit.SECONDS.toMillis(30) > System.currentTimeMillis()) { + if (parent != null && parent.getLastSyncDateForData() + 30 * 1000 > System.currentTimeMillis()) { EventBus.getDefault().post(new SyncEventFinished(intent)); return; @@ -298,66 +295,74 @@ public void openFile(OCFile file) { // first always try to use available apps if (availableApps.isEmpty()) { - Optional optionalUser = fileActivity.getUser(); + openFileWithoutAvailableApps(file); + return; + } - if (optionalUser.isPresent() && editorUtils.isEditorAvailable(optionalUser.get(), file.getMimeType())) { - TextEditorWebView.Companion.startTextEditor(file, fileActivity); - return; - } + new Thread(() -> syncAndOpenFile(file, openFileWithIntent)).start(); + } - openRichDocumentFileWithoutAvailableApps(file); + private void openFileWithoutAvailableApps(OCFile file) { + Optional optionalUser = fileActivity.getUser(); + if (optionalUser.isPresent() && editorUtils.isEditorAvailable(optionalUser.get(), file.getMimeType())) { + TextEditorWebView.Companion.startTextEditor(file, fileActivity); return; } - new Thread(() -> { - User user = currentAccount.getUser(); - final var storageManager = new FileDataStorageManager(user, fileActivity.getContentResolver()); - final var storedFile = storageManager.getFileById(file.getFileId()); - final boolean isDownloadedAndUpToDate = storedFile != null && storedFile.isDown() && - !OCFileExtensionsKt.isLocalETagOutdated(storedFile); - if (isDownloadedAndUpToDate) { - startOpenFileIntent(openFileWithIntent); - return; - } + openRichDocumentFileWithoutAvailableApps(file); + } - // a fresh object is needed; many things could have occurred to the file - // since it was registered to observe again, assuming that local files - // are linked to a remote file AT MOST, SOMETHING TO BE DONE; - final var sfo = new SynchronizeFileOperation(file,null, user, true, fileActivity, storageManager, false); - final var result = sfo.execute(fileActivity); - - if (result.getCode() == RemoteOperationResult.ResultCode.SYNC_CONFLICT) { - // ISSUE 5: if the user is not running the app (this is a service!), - // this can be very intrusive; a notification should be preferred - Intent intent = ConflictsResolveActivity.createIntent(file, - user, - -1, - Intent.FLAG_ACTIVITY_NEW_TASK, - fileActivity); - fileActivity.startActivity(intent); - return; - } + private void syncAndOpenFile(OCFile file, Intent openFileWithIntent) { + User user = currentAccount.getUser(); + final var storageManager = new FileDataStorageManager(user, fileActivity.getContentResolver()); + if (isDownloadedAndUpToDate(storageManager.getFileById(file.getFileId()))) { + startOpenFileIntent(openFileWithIntent); + return; + } - if (availableApps.isEmpty()) { - fileActivity.runOnUiThread(() -> SnackbarUtil.show(fileActivity, R.string.file_list_no_app_for_file_type)); + // a fresh object is needed; many things could have occurred to the file + // since it was registered to observe again, assuming that local files + // are linked to a remote file AT MOST, SOMETHING TO BE DONE; + final var sfo = new SynchronizeFileOperation(file,null, user, true, fileActivity, storageManager, false); + final var result = sfo.execute(fileActivity); - return; - } + if (result.getCode() == RemoteOperationResult.ResultCode.SYNC_CONFLICT) { + startConflictsResolveActivity(file, user); + return; + } - if (!result.isSuccess()) { - fileActivity.runOnUiThread(() -> SnackbarUtil.show(fileActivity, R.string.file_not_synced)); + if (!result.isSuccess()) { + showFileNotSyncedMessage(); + } - // Sleep to show snackbar message - try { - Thread.sleep(AppConstants.MILLIS_PER_SECOND); - } catch (InterruptedException e) { - Log_OC.e(TAG, "Failed to sleep"); - } - } + startOpenFileIntent(openFileWithIntent); + } - startOpenFileIntent(openFileWithIntent); - }).start(); + private boolean isDownloadedAndUpToDate(@Nullable OCFile storedFile) { + return storedFile != null && storedFile.isDown() && !OCFileExtensionsKt.isLocalETagOutdated(storedFile); + } + + private void startConflictsResolveActivity(OCFile file, User user) { + // ISSUE 5: if the user is not running the app (this is a service!), + // this can be very intrusive; a notification should be preferred + Intent intent = ConflictsResolveActivity.createIntent(file, + user, + -1, + Intent.FLAG_ACTIVITY_NEW_TASK, + fileActivity); + fileActivity.startActivity(intent); + } + + private void showFileNotSyncedMessage() { + fileActivity.runOnUiThread(() -> SnackbarUtil.show(fileActivity, R.string.file_not_synced)); + + // Sleep to show snackbar message + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + Log_OC.e(TAG, "Failed to sleep"); + } } private void startOpenFileIntent(Intent openFileWithIntent) { From 53877f3c8fe7fed7528910d8d4a2ef38ca0ad8a2 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Mon, 5 Oct 2026 13:04:50 +0200 Subject: [PATCH 08/15] fix spotbugs: (This write of this shared primitive variable may not be visible to other threads) Signed-off-by: alperozturk96 --- .../operations/RefreshFolderOperation.java | 41 ++++++++++++------- .../ui/activity/FileDisplayActivity.kt | 7 ++-- 2 files changed, 29 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java b/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java index b08f6c109ec1..f0320b819047 100644 --- a/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java @@ -56,6 +56,7 @@ import java.util.List; import java.util.Map; import java.util.Vector; +import java.util.concurrent.atomic.AtomicInteger; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -114,12 +115,12 @@ public class RefreshFolderOperation extends RemoteOperation { /** * Counter of conflicts found between local and remote files */ - private int mConflictsFound; + private final AtomicInteger mConflictsFound = new AtomicInteger(); /** * Counter of failed operations in synchronization of kept-in-sync files */ - private int mFailsInKeptInSyncFound; + private final AtomicInteger mFailsInKeptInSyncFound = new AtomicInteger(); /** * Map of remote and local paths to files that where locally stored in a location out of the ownCloud folder and @@ -135,7 +136,7 @@ public class RefreshFolderOperation extends RemoteOperation { /** * 'True' means that the remote folder changed and should be fetched */ - private boolean mRemoteFolderChanged; + private volatile boolean mRemoteFolderChanged; /** * 'True' means that the sharees of at least one child of the folder changed @@ -155,7 +156,7 @@ public class RefreshFolderOperation extends RemoteOperation { private final List mFilesToSyncContents; // this will be used for every file when 'folder synchronization' replaces 'folder download' - private boolean syncChangedDownloadedFiles = true; + private final boolean syncChangedDownloadedFiles; /** @@ -177,6 +178,17 @@ public RefreshFolderOperation(OCFile folder, FileDataStorageManager dataStorageManager, User user, Context context) { + this(folder, currentSyncTime, syncFullAccount, ignoreETag, dataStorageManager, user, context, true); + } + + public RefreshFolderOperation(OCFile folder, + long currentSyncTime, + boolean syncFullAccount, + boolean ignoreETag, + FileDataStorageManager dataStorageManager, + User user, + Context context, + boolean syncChangedDownloadedFiles) { mLocalFolder = folder; mCurrentSyncTime = currentSyncTime; mSyncFullAccount = syncFullAccount; @@ -188,6 +200,7 @@ public RefreshFolderOperation(OCFile folder, mIgnoreETag = ignoreETag; mOnlyFileMetadata = false; mFilesToSyncContents = new Vector<>(); + this.syncChangedDownloadedFiles = syncChangedDownloadedFiles; } /** @@ -208,6 +221,7 @@ public RefreshFolderOperation(OCFile folder, mIgnoreETag = false; mOnlyFileMetadata = true; mFilesToSyncContents = new Vector<>(); + syncChangedDownloadedFiles = true; // since metadata worker working in background for sub-folders no need send folder refresh event isMetadataSyncWorkerRunning = true; @@ -232,24 +246,21 @@ public RefreshFolderOperation(OCFile folder, mIgnoreETag = ignoreETag; mOnlyFileMetadata = onlyFileMetadata; mFilesToSyncContents = new Vector<>(); + syncChangedDownloadedFiles = true; } public int getConflictsFound() { - return mConflictsFound; + return mConflictsFound.get(); } public int getFailsInKeptInSyncFound() { - return mFailsInKeptInSyncFound; + return mFailsInKeptInSyncFound.get(); } public Map getForgottenLocalFiles() { return mForgottenLocalFiles; } - public void setSyncChangedDownloadedFiles(boolean syncChangedDownloadedFiles) { - this.syncChangedDownloadedFiles = syncChangedDownloadedFiles; - } - /** * Returns the list of files and folders contained in the synchronized folder, if called after synchronization is * complete. @@ -268,8 +279,8 @@ public List getChildren() { @Override protected RemoteOperationResult run(OwnCloudClient client) { RemoteOperationResult result; - mFailsInKeptInSyncFound = 0; - mConflictsFound = 0; + mFailsInKeptInSyncFound.set(0); + mConflictsFound.set(0); mForgottenLocalFiles.clear(); if (mLocalFolder == null) { @@ -477,7 +488,7 @@ private RemoteOperationResult fetchAndSyncRemoteFolder(OwnCloudClient client) { if (result.isSuccess()) { synchronizeData(result.getData()); - if (mConflictsFound > 0 || mFailsInKeptInSyncFound > 0) { + if (mConflictsFound.get() > 0 || mFailsInKeptInSyncFound.get() > 0) { result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT); // should be a different result code, but will do the job } @@ -835,9 +846,9 @@ private void startContentSynchronizations(List filesTo contentsResult = op.execute(mContext); // async if (!contentsResult.isSuccess()) { if (contentsResult.getCode() == ResultCode.SYNC_CONFLICT) { - mConflictsFound++; + mConflictsFound.incrementAndGet(); } else { - mFailsInKeptInSyncFound++; + mFailsInKeptInSyncFound.incrementAndGet(); if (contentsResult.getException() != null) { Log_OC.e(TAG, "Error while synchronizing favourites : " + contentsResult.getLogMessage(), contentsResult.getException()); diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt index 28b6da2ece69..7c229b737a90 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt @@ -2651,10 +2651,9 @@ class FileDisplayActivity : ignoreETag, storageManager, user, - applicationContext - ).apply { - setSyncChangedDownloadedFiles(syncChangedDownloadedFiles) - }.execute( + applicationContext, + syncChangedDownloadedFiles + ).execute( account, this, { _, _ -> onSyncFinished() }, From e0014fe14e2744628892b552b7bf7d8e1633add7 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Mon, 5 Oct 2026 13:43:07 +0200 Subject: [PATCH 09/15] simplify startContentSynchronizations Signed-off-by: alperozturk96 --- .../operations/RefreshFolderOperation.java | 42 +++++++++++-------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java b/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java index f0320b819047..b6b594d7b626 100644 --- a/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java @@ -841,24 +841,32 @@ public static Map prefillLocalFilesMap(Object metadata, List filesToSyncContents) { - RemoteOperationResult contentsResult; - for (SynchronizeFileOperation op : filesToSyncContents) { - contentsResult = op.execute(mContext); // async - if (!contentsResult.isSuccess()) { - if (contentsResult.getCode() == ResultCode.SYNC_CONFLICT) { - mConflictsFound.incrementAndGet(); - } else { - mFailsInKeptInSyncFound.incrementAndGet(); - if (contentsResult.getException() != null) { - Log_OC.e(TAG, "Error while synchronizing favourites : " - + contentsResult.getLogMessage(), contentsResult.getException()); - } else { - Log_OC.e(TAG, "Error while synchronizing favourites : " - + contentsResult.getLogMessage()); - } - } - } // won't let these fails break the synchronization process + // won't let these fails break the synchronization process + for (SynchronizeFileOperation operation : filesToSyncContents) { + final RemoteOperationResult result = operation.execute(mContext); + if (result.isSuccess()) { + continue; + } + + if (result.getCode() == ResultCode.SYNC_CONFLICT) { + mConflictsFound.incrementAndGet(); + continue; + } + + mFailsInKeptInSyncFound.incrementAndGet(); + logContentSynchronizationFailure(result); + } + } + + private void logContentSynchronizationFailure(RemoteOperationResult result) { + final String message = "Error while synchronizing favourites : " + result.getLogMessage(mContext); + final Exception exception = result.getException(); + if (exception == null) { + Log_OC.e(TAG, message); + return; } + + Log_OC.e(TAG, message, exception); } /** From 18eb033513e82a68b1595371d9d1a71e54e84421 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Mon, 5 Oct 2026 13:54:46 +0200 Subject: [PATCH 10/15] print results Signed-off-by: alperozturk96 --- .../android/operations/RefreshFolderOperation.java | 3 +++ .../android/operations/SynchronizeFileOperation.kt | 12 ++++++++++++ 2 files changed, 15 insertions(+) diff --git a/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java b/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java index b6b594d7b626..227f1a4f0d96 100644 --- a/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java @@ -845,14 +845,17 @@ private void startContentSynchronizations(List filesTo for (SynchronizeFileOperation operation : filesToSyncContents) { final RemoteOperationResult result = operation.execute(mContext); if (result.isSuccess()) { + operation.printSuccess(); continue; } if (result.getCode() == ResultCode.SYNC_CONFLICT) { + operation.printConflict(); mConflictsFound.incrementAndGet(); continue; } + operation.printFailure(); mFailsInKeptInSyncFound.incrementAndGet(); logContentSynchronizationFailure(result); } diff --git a/app/src/main/java/com/owncloud/android/operations/SynchronizeFileOperation.kt b/app/src/main/java/com/owncloud/android/operations/SynchronizeFileOperation.kt index f784b884f010..1c20f88ae0e9 100644 --- a/app/src/main/java/com/owncloud/android/operations/SynchronizeFileOperation.kt +++ b/app/src/main/java/com/owncloud/android/operations/SynchronizeFileOperation.kt @@ -256,6 +256,18 @@ class SynchronizeFileOperation : SyncOperation { } } + fun printSuccess() { + Log_OC.i(TAG, "content synchronized, remote path: $remotePath") + } + + fun printConflict() { + Log_OC.w(TAG, "content has conflict, remote path: $remotePath") + } + + fun printFailure() { + Log_OC.e(TAG, "content not synchronized, remote path: $remotePath") + } + companion object { private val TAG: String = SynchronizeFileOperation::class.java.simpleName } From 6e56eefc9b5fd095ec40c9005ddd4ddfc5b11b7e Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Mon, 5 Oct 2026 13:56:55 +0200 Subject: [PATCH 11/15] only sync when user get in directory Signed-off-by: alperozturk96 --- .../owncloud/android/operations/FolderRefreshScheduler.kt | 4 ++-- .../owncloud/android/operations/RefreshFolderOperation.java | 6 +++--- .../com/owncloud/android/ui/activity/FileDisplayActivity.kt | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/operations/FolderRefreshScheduler.kt b/app/src/main/java/com/owncloud/android/operations/FolderRefreshScheduler.kt index 019aa478846a..d30e593a5749 100644 --- a/app/src/main/java/com/owncloud/android/operations/FolderRefreshScheduler.kt +++ b/app/src/main/java/com/owncloud/android/operations/FolderRefreshScheduler.kt @@ -90,7 +90,7 @@ class FolderRefreshScheduler(private val activity: FileDisplayActivity) { when (result.code) { RemoteOperationResult.ResultCode.ETAG_CHANGED -> { Log_OC.i(TAG, "eTag poll → eTag changed for '${currentDir.remotePath}', triggering sync") - activity.startSyncFolderOperation(currentDir, ignoreETag = true, syncChangedDownloadedFiles = false) + activity.startSyncFolderOperation(currentDir, ignoreETag = true) } RemoteOperationResult.ResultCode.ETAG_UNCHANGED -> { @@ -99,7 +99,7 @@ class FolderRefreshScheduler(private val activity: FileDisplayActivity) { RemoteOperationResult.ResultCode.FILE_NOT_FOUND -> { Log_OC.w(TAG, "eTag poll → directory not found on server") - activity.startSyncFolderOperation(currentDir, ignoreETag = true, syncChangedDownloadedFiles = false) + activity.startSyncFolderOperation(currentDir, ignoreETag = true) } else -> { diff --git a/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java b/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java index 227f1a4f0d96..eecd9468489c 100644 --- a/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java @@ -178,7 +178,7 @@ public RefreshFolderOperation(OCFile folder, FileDataStorageManager dataStorageManager, User user, Context context) { - this(folder, currentSyncTime, syncFullAccount, ignoreETag, dataStorageManager, user, context, true); + this(folder, currentSyncTime, syncFullAccount, ignoreETag, dataStorageManager, user, context, false); } public RefreshFolderOperation(OCFile folder, @@ -221,7 +221,7 @@ public RefreshFolderOperation(OCFile folder, mIgnoreETag = false; mOnlyFileMetadata = true; mFilesToSyncContents = new Vector<>(); - syncChangedDownloadedFiles = true; + syncChangedDownloadedFiles = false; // since metadata worker working in background for sub-folders no need send folder refresh event isMetadataSyncWorkerRunning = true; @@ -246,7 +246,7 @@ public RefreshFolderOperation(OCFile folder, mIgnoreETag = ignoreETag; mOnlyFileMetadata = onlyFileMetadata; mFilesToSyncContents = new Vector<>(); - syncChangedDownloadedFiles = true; + syncChangedDownloadedFiles = false; } public int getConflictsFound() { diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt index 7c229b737a90..8daafd110ed2 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt @@ -2001,7 +2001,7 @@ class FileDisplayActivity : override fun onBrowsedDownTo(directory: OCFile?) { file = directory resetScrollingAndUpdateActionBar() - startSyncFolderOperation(directory, false) + startSyncFolderOperation(directory, ignoreETag = false, syncChangedDownloadedFiles = true) startMetadataSyncForCurrentDir() } @@ -2604,7 +2604,7 @@ class FileDisplayActivity : folder: OCFile?, ignoreETag: Boolean, ignoreFocus: Boolean = false, - syncChangedDownloadedFiles: Boolean = true + syncChangedDownloadedFiles: Boolean = false ) { Log_OC.d(TAG, "startSyncFolderOperation called, ignoreEtag: $ignoreETag, ignoreFocus: $ignoreFocus") From fc441bae6981ec2abe2122770e11cb2a6f6f3bd0 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Tue, 6 Oct 2026 09:48:14 +0200 Subject: [PATCH 12/15] handle Image that isn't downloaded via -> fileOnItemClick -> previewImageWithSearchContext Downloaded text file via -> fileOnItemClick -> previewFile -> startTextPreview Downloaded audio file via > fileOnItemClick -> previewFile -> startAudioPreview Signed-off-by: alperozturk96 --- .../ui/activity/FileDisplayActivity.kt | 37 +++++++++++++------ .../ui/helpers/FileOperationsHelper.java | 25 ++++++++++++- 2 files changed, 48 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt index 8daafd110ed2..34df9166e24e 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt @@ -2111,8 +2111,13 @@ class FileDisplayActivity : null } - val showPreview = file.isDown || MimeTypeUtil.isVideo(file) - startImagePreview(file, showPreview, type, mediaState, sourceView) + if (file.isDown || MimeTypeUtil.isVideo(file)) { + startImagePreview(file, true, type, mediaState, sourceView) + return + } + + val intent = imagePreviewIntent(file, type, mediaState) ?: return + fileOperationsHelper.startSyncForOutdatedFileAndIntent(file, intent) } fun previewFile(file: OCFile, setFabVisible: CompletionCallback?) { @@ -2127,10 +2132,14 @@ class FileDisplayActivity : startPdfPreview(file) } else if (PreviewTextFileFragment.canBePreviewed(file)) { setFabVisible?.onComplete(false) - startTextPreview(file, false) + fileOperationsHelper.startSyncForOutdatedFileAndIntent(file, textPreviewIntent(file)) } else if (canPreviewInAudioPlayer(file)) { setFabVisible?.onComplete(false) - startAudioPreview(file, true, false) + if (file.isDownloading) { + fileOperationsHelper.startSyncForOutdatedFileAndIntent(file, audioPreviewIntent(file)) + } else { + startAudioPlayer(file) + } } else { fileOperationsHelper.openFile(file) } @@ -2781,15 +2790,17 @@ class FileDisplayActivity : if ((showPreview && file.isDown && !file.isDownloading) || streamMedia) { startAudioPlayer(file) } else { - val previewIntent = Intent() - previewIntent.putExtra(EXTRA_FILE, file) - previewIntent.putExtra(AUDIO_PREVIEW, true) val fileOperationsHelper = FileOperationsHelper(this, userAccountManager, connectivityService, editorUtils) - fileOperationsHelper.startSyncForFileAndIntent(file, previewIntent) + fileOperationsHelper.startSyncForFileAndIntent(file, audioPreviewIntent(file)) } } + private fun audioPreviewIntent(file: OCFile): Intent = Intent().apply { + putExtra(EXTRA_FILE, file) + putExtra(AUDIO_PREVIEW, true) + } + private fun startAudioPlayer(file: OCFile) { val collection = listOfFilesFragment?.currentSearchType.toPlaybackCollection() audioPlayerLauncher.launch(this, file, collection) @@ -2818,15 +2829,17 @@ class FileDisplayActivity : configureToolbarForPreview(file) showBottomNavigationBar(false) } else { - val previewIntent = Intent() - previewIntent.putExtra(EXTRA_FILE, file) - previewIntent.putExtra(TEXT_PREVIEW, true) val fileOperationsHelper = FileOperationsHelper(this, userAccountManager, connectivityService, editorUtils) - fileOperationsHelper.startSyncForFileAndIntent(file, previewIntent) + fileOperationsHelper.startSyncForFileAndIntent(file, textPreviewIntent(file)) } } + private fun textPreviewIntent(file: OCFile?): Intent = Intent().apply { + putExtra(EXTRA_FILE, file) + putExtra(TEXT_PREVIEW, true) + } + /** * Starts rich workspace preview for a folder. * diff --git a/app/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java b/app/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java index 9adcf301898b..9a6b20c9b50f 100755 --- a/app/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java +++ b/app/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java @@ -67,7 +67,6 @@ import com.owncloud.android.ui.activity.TextEditorWebView; import com.owncloud.android.ui.dialog.SendFilesDialog; import com.owncloud.android.ui.dialog.SendShareDialog; -import com.owncloud.android.ui.events.EncryptionEvent; import com.owncloud.android.ui.events.FavoriteEvent; import com.owncloud.android.ui.events.FileLockEvent; import com.owncloud.android.ui.events.SyncEventFinished; @@ -321,6 +320,24 @@ private void syncAndOpenFile(OCFile file, Intent openFileWithIntent) { return; } + synchronizeFile(file, user, storageManager, () -> startOpenFileIntent(openFileWithIntent)); + } + + public void startSyncForOutdatedFileAndIntent(OCFile file, Intent intent) { + new Thread(() -> { + User user = currentAccount.getUser(); + final var storageManager = new FileDataStorageManager(user, fileActivity.getContentResolver()); + final Runnable postSyncEventFinished = () -> EventBus.getDefault().post(new SyncEventFinished(intent)); + if (!isDownloadedAndOutdated(storageManager.getFileById(file.getFileId()))) { + postSyncEventFinished.run(); + return; + } + + synchronizeFile(file, user, storageManager, postSyncEventFinished); + }).start(); + } + + private void synchronizeFile(OCFile file, User user, FileDataStorageManager storageManager, Runnable onSynced) { // a fresh object is needed; many things could have occurred to the file // since it was registered to observe again, assuming that local files // are linked to a remote file AT MOST, SOMETHING TO BE DONE; @@ -336,13 +353,17 @@ private void syncAndOpenFile(OCFile file, Intent openFileWithIntent) { showFileNotSyncedMessage(); } - startOpenFileIntent(openFileWithIntent); + onSynced.run(); } private boolean isDownloadedAndUpToDate(@Nullable OCFile storedFile) { return storedFile != null && storedFile.isDown() && !OCFileExtensionsKt.isLocalETagOutdated(storedFile); } + private boolean isDownloadedAndOutdated(@Nullable OCFile storedFile) { + return storedFile != null && storedFile.isDown() && OCFileExtensionsKt.isLocalETagOutdated(storedFile); + } + private void startConflictsResolveActivity(OCFile file, User user) { // ISSUE 5: if the user is not running the app (this is a service!), // this can be very intrusive; a notification should be preferred From 8b751480cbca04ef78e822b400c9380bbe91d204 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Tue, 6 Oct 2026 10:28:38 +0200 Subject: [PATCH 13/15] no need sync for image preview Signed-off-by: alperozturk96 --- .../android/ui/activity/FileDisplayActivity.kt | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt index 34df9166e24e..95a728a24b19 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt @@ -2089,6 +2089,10 @@ class FileDisplayActivity : fun canPreviewInMediaPager(file: OCFile?): Boolean = PreviewMediaFragment.canBePreviewed(file) || (file != null && MimeTypeUtil.isVideo(file)) + /** + * Downloaded and non-downloaded images are handled later in + * [com.owncloud.android.ui.preview.PreviewMediaPagerAdapter.fragmentFor] + */ fun previewImageWithSearchContext( file: OCFile, searchFragment: Boolean, @@ -2111,13 +2115,7 @@ class FileDisplayActivity : null } - if (file.isDown || MimeTypeUtil.isVideo(file)) { - startImagePreview(file, true, type, mediaState, sourceView) - return - } - - val intent = imagePreviewIntent(file, type, mediaState) ?: return - fileOperationsHelper.startSyncForOutdatedFileAndIntent(file, intent) + startImagePreview(file, true, type, mediaState, sourceView) } fun previewFile(file: OCFile, setFabVisible: CompletionCallback?) { From 4e0b65314f36646aa98c29ad2d2b1ad1f63585cc Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Wed, 21 Oct 2026 11:43:21 +0200 Subject: [PATCH 14/15] wip Signed-off-by: alperozturk96 --- .../com/owncloud/android/ui/helpers/FileOperationsHelper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java b/app/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java index 9a6b20c9b50f..e2c70150d992 100755 --- a/app/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java +++ b/app/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java @@ -380,7 +380,7 @@ private void showFileNotSyncedMessage() { // Sleep to show snackbar message try { - Thread.sleep(1000); + Thread.sleep(AppConstants.MILLIS_PER_SECOND); } catch (InterruptedException e) { Log_OC.e(TAG, "Failed to sleep"); } From 15d4dbb335d2937be79c3ff7446a596b880941df Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Wed, 28 Oct 2026 14:40:15 +0300 Subject: [PATCH 15/15] wip Signed-off-by: alperozturk96 --- .../java/com/nextcloud/utils/extensions/OCFileExtensions.kt | 5 +++++ .../owncloud/android/ui/helpers/FileOperationsHelper.java | 1 + 2 files changed, 6 insertions(+) diff --git a/app/src/main/java/com/nextcloud/utils/extensions/OCFileExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/OCFileExtensions.kt index 030209365c6e..acad982bb83b 100644 --- a/app/src/main/java/com/nextcloud/utils/extensions/OCFileExtensions.kt +++ b/app/src/main/java/com/nextcloud/utils/extensions/OCFileExtensions.kt @@ -112,3 +112,8 @@ fun OCFile.createStoragePath(accountName: String) { storagePath = savePath } + +fun OCFile.isLocalETagOutdated(): Boolean { + val serverETag = etagInConflict + return !serverETag.isNullOrEmpty() && etag.eTagChanged(serverETag) +} diff --git a/app/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java b/app/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java index e2c70150d992..4f553fc4f792 100755 --- a/app/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java +++ b/app/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java @@ -36,6 +36,7 @@ import com.nextcloud.client.jobs.upload.FileUploadHelper; import com.nextcloud.client.jobs.upload.FileUploadWorker; import com.nextcloud.client.network.ConnectivityService; +import com.nextcloud.utils.AppConstants; import com.nextcloud.utils.EditorUtils; import com.nextcloud.utils.SnackbarUtil; import com.nextcloud.utils.extensions.OCFileExtensionsKt;