Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/src/main/java/com/nextcloud/utils/AppConstants.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ object AppConstants {
const val MINUTES_PER_HOUR = 60
val ONE_SECOND = 1.seconds
val TWO_SECOND = 2.seconds
const val THIRTY_SECONDS_IN_MILLIS = 30L * MILLIS_PER_SECOND

const val BYTES_PER_KB = 1024
const val BYTES_PER_MB = BYTES_PER_KB * BYTES_PER_KB
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,14 @@ private fun transformToRows(list: List<OCFile>, 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,
Expand All @@ -104,3 +112,8 @@ fun OCFile.createStoragePath(accountName: String) {

storagePath = savePath
}

fun OCFile.isLocalETagOutdated(): Boolean {
val serverETag = etagInConflict
return !serverETag.isNullOrEmpty() && etag.eTagChanged(serverETag)
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 = 10.seconds
private const val TAG = "FolderRefreshScheduler"
}

Expand All @@ -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() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -55,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;
Expand Down Expand Up @@ -113,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
Expand All @@ -134,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
Expand All @@ -154,6 +156,8 @@ public class RefreshFolderOperation extends RemoteOperation {
private final List<SynchronizeFileOperation> mFilesToSyncContents;
// this will be used for every file when 'folder synchronization' replaces 'folder download'

private final boolean syncChangedDownloadedFiles;


/**
* Creates a new instance of {@link RefreshFolderOperation}.
Expand All @@ -174,6 +178,17 @@ public RefreshFolderOperation(OCFile folder,
FileDataStorageManager dataStorageManager,
User user,
Context context) {
this(folder, currentSyncTime, syncFullAccount, ignoreETag, dataStorageManager, user, context, false);
}

public RefreshFolderOperation(OCFile folder,
long currentSyncTime,
boolean syncFullAccount,
boolean ignoreETag,
FileDataStorageManager dataStorageManager,
User user,
Context context,
boolean syncChangedDownloadedFiles) {
mLocalFolder = folder;
mCurrentSyncTime = currentSyncTime;
mSyncFullAccount = syncFullAccount;
Expand All @@ -185,6 +200,7 @@ public RefreshFolderOperation(OCFile folder,
mIgnoreETag = ignoreETag;
mOnlyFileMetadata = false;
mFilesToSyncContents = new Vector<>();
this.syncChangedDownloadedFiles = syncChangedDownloadedFiles;
}

/**
Expand All @@ -205,6 +221,7 @@ public RefreshFolderOperation(OCFile folder,
mIgnoreETag = false;
mOnlyFileMetadata = true;
mFilesToSyncContents = new Vector<>();
syncChangedDownloadedFiles = false;

// since metadata worker working in background for sub-folders no need send folder refresh event
isMetadataSyncWorkerRunning = true;
Expand All @@ -229,14 +246,15 @@ public RefreshFolderOperation(OCFile folder,
mIgnoreETag = ignoreETag;
mOnlyFileMetadata = onlyFileMetadata;
mFilesToSyncContents = new Vector<>();
syncChangedDownloadedFiles = false;
}

public int getConflictsFound() {
return mConflictsFound;
return mConflictsFound.get();
}

public int getFailsInKeptInSyncFound() {
return mFailsInKeptInSyncFound;
return mFailsInKeptInSyncFound.get();
}

public Map<String, String> getForgottenLocalFiles() {
Expand All @@ -261,8 +279,8 @@ public List<OCFile> getChildren() {
@Override
protected RemoteOperationResult run(OwnCloudClient client) {
RemoteOperationResult result;
mFailsInKeptInSyncFound = 0;
mConflictsFound = 0;
mFailsInKeptInSyncFound.set(0);
mConflictsFound.set(0);
mForgottenLocalFiles.clear();

if (mLocalFolder == null) {
Expand Down Expand Up @@ -470,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
}
Expand Down Expand Up @@ -619,6 +637,12 @@ private void synchronizeData(List<Object> folderAndFiles) {
updatedFile.setEncrypted(encrypted);
updatedFile.setReadOnly(localFile != null && localFile.isReadOnly());

if (syncChangedDownloadedFiles && !encrypted &&
OCFileExtensionsKt.isDownloadedFileChanged(localFile, remoteFile)) {
mFilesToSyncContents.add(new SynchronizeFileOperation(updatedFile, remoteFile, user, true, mContext,
fileDataStorageManager, true));
}

updatedFiles.add(updatedFile);
}

Expand Down Expand Up @@ -817,26 +841,37 @@ public static Map<String, OCFile> prefillLocalFilesMap(Object metadata, List<OCF
* @param filesToSyncContents Synchronization operations to execute.
*/
private void startContentSynchronizations(List<SynchronizeFileOperation> filesToSyncContents) {
RemoteOperationResult contentsResult;
for (SynchronizeFileOperation op : filesToSyncContents) {
contentsResult = op.execute(mContext); // async
if (!contentsResult.isSuccess()) {
if (contentsResult.getCode() == ResultCode.SYNC_CONFLICT) {
mConflictsFound++;
} else {
mFailsInKeptInSyncFound++;
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()) {
operation.printSuccess();
continue;
}

if (result.getCode() == ResultCode.SYNC_CONFLICT) {
operation.printConflict();
mConflictsFound.incrementAndGet();
continue;
}

operation.printFailure();
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);
}

/**
* Sends a message to any application component interested in the progress of the synchronization.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
Loading
Loading