|
@@ -22,6 +22,8 @@ package com.owncloud.android.operations;
|
|
|
import android.accounts.Account;
|
|
|
import android.content.Context;
|
|
|
import android.content.Intent;
|
|
|
+import android.support.annotation.NonNull;
|
|
|
+import android.support.annotation.Nullable;
|
|
|
import android.util.Log;
|
|
|
|
|
|
import com.owncloud.android.datamodel.DecryptedFolderMetadata;
|
|
@@ -147,13 +149,12 @@ public class RefreshFolderOperation extends RemoteOperation {
|
|
|
mStorageManager = dataStorageManager;
|
|
|
mAccount = account;
|
|
|
mContext = context;
|
|
|
- mForgottenLocalFiles = new HashMap<String, String>();
|
|
|
+ mForgottenLocalFiles = new HashMap<>();
|
|
|
mRemoteFolderChanged = false;
|
|
|
mIgnoreETag = ignoreETag;
|
|
|
- mFilesToSyncContents = new Vector<SynchronizeFileOperation>();
|
|
|
+ mFilesToSyncContents = new Vector<>();
|
|
|
}
|
|
|
|
|
|
-
|
|
|
public int getConflictsFound() {
|
|
|
return mConflictsFound;
|
|
|
}
|
|
@@ -183,7 +184,7 @@ public class RefreshFolderOperation extends RemoteOperation {
|
|
|
*/
|
|
|
@Override
|
|
|
protected RemoteOperationResult run(OwnCloudClient client) {
|
|
|
- RemoteOperationResult result = null;
|
|
|
+ RemoteOperationResult result;
|
|
|
mFailsInKeptInSyncFound = 0;
|
|
|
mConflictsFound = 0;
|
|
|
mForgottenLocalFiles.clear();
|
|
@@ -267,7 +268,7 @@ public class RefreshFolderOperation extends RemoteOperation {
|
|
|
|
|
|
private RemoteOperationResult checkForChanges(OwnCloudClient client) {
|
|
|
mRemoteFolderChanged = true;
|
|
|
- RemoteOperationResult result = null;
|
|
|
+ RemoteOperationResult result;
|
|
|
String remotePath = mLocalFolder.getRemotePath();
|
|
|
|
|
|
Log_OC.d(TAG, "Checking changes in " + mAccount.name + remotePath);
|
|
@@ -368,32 +369,18 @@ public class RefreshFolderOperation extends RemoteOperation {
|
|
|
|
|
|
Log_OC.d(TAG, "Remote folder " + mLocalFolder.getRemotePath() + " changed - starting update of local data ");
|
|
|
|
|
|
- List<OCFile> updatedFiles = new Vector<OCFile>(folderAndFiles.size() - 1);
|
|
|
+ List<OCFile> updatedFiles = new Vector<>(folderAndFiles.size() - 1);
|
|
|
mFilesToSyncContents.clear();
|
|
|
|
|
|
// if local folder is encrypted, download fresh metadata
|
|
|
- DecryptedFolderMetadata metadata;
|
|
|
boolean encryptedAncestor = FileStorageUtils.checkEncryptionStatus(mLocalFolder, mStorageManager);
|
|
|
mLocalFolder.setEncrypted(encryptedAncestor);
|
|
|
-
|
|
|
- if (encryptedAncestor && android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
|
|
|
- metadata = EncryptionUtils.downloadFolderMetadata(mLocalFolder, getClient(), mContext, mAccount);
|
|
|
- } else {
|
|
|
- metadata = null;
|
|
|
- }
|
|
|
|
|
|
- // get current data about local contents of the folder to synchronize
|
|
|
- List<OCFile> localFiles = mStorageManager.getFolderContent(mLocalFolder, false);
|
|
|
- Map<String, OCFile> localFilesMap = new HashMap<String, OCFile>(localFiles.size());
|
|
|
-
|
|
|
- for (OCFile file : localFiles) {
|
|
|
- String remotePath = file.getRemotePath();
|
|
|
+ DecryptedFolderMetadata metadata = getDecryptedFolderMetadata(encryptedAncestor);
|
|
|
|
|
|
- if (metadata != null && !file.isFolder()) {
|
|
|
- remotePath = file.getParentRemotePath() + file.getEncryptedFileName();
|
|
|
- }
|
|
|
- localFilesMap.put(remotePath, file);
|
|
|
- }
|
|
|
+ // get current data about local contents of the folder to synchronize
|
|
|
+ Map<String, OCFile> localFilesMap = prefillLocalFilesMap(metadata,
|
|
|
+ mStorageManager.getFolderContent(mLocalFolder, false));
|
|
|
|
|
|
// loop to update every child
|
|
|
OCFile remoteFile;
|
|
@@ -416,36 +403,8 @@ public class RefreshFolderOperation extends RemoteOperation {
|
|
|
// add to updatedFile data about LOCAL STATE (not existing in server)
|
|
|
updatedFile.setLastSyncDateForProperties(mCurrentSyncTime);
|
|
|
|
|
|
- if (localFile != null) {
|
|
|
- updatedFile.setFileId(localFile.getFileId());
|
|
|
- updatedFile.setAvailableOffline(localFile.isAvailableOffline());
|
|
|
- updatedFile.setLastSyncDateForData(localFile.getLastSyncDateForData());
|
|
|
- updatedFile.setModificationTimestampAtLastSyncForData(
|
|
|
- localFile.getModificationTimestampAtLastSyncForData()
|
|
|
- );
|
|
|
- updatedFile.setStoragePath(localFile.getStoragePath());
|
|
|
- // eTag will not be updated unless file CONTENTS are synchronized
|
|
|
- if (!updatedFile.isFolder() && localFile.isDown() &&
|
|
|
- !updatedFile.getEtag().equals(localFile.getEtag())) {
|
|
|
- updatedFile.setEtagInConflict(updatedFile.getEtag());
|
|
|
- }
|
|
|
- updatedFile.setEtag(localFile.getEtag());
|
|
|
- if (updatedFile.isFolder()) {
|
|
|
- updatedFile.setFileLength(remoteFile.getFileLength());
|
|
|
- updatedFile.setMountType(remoteFile.getMountType());
|
|
|
- } else if (mRemoteFolderChanged && MimeTypeUtil.isImage(remoteFile) &&
|
|
|
- remoteFile.getModificationTimestamp() !=
|
|
|
- localFile.getModificationTimestamp()) {
|
|
|
- updatedFile.setNeedsUpdateThumbnail(true);
|
|
|
- Log.d(TAG, "Image " + remoteFile.getFileName() + " updated on the server");
|
|
|
- }
|
|
|
- updatedFile.setPublicLink(localFile.getPublicLink());
|
|
|
- updatedFile.setShareViaLink(localFile.isSharedViaLink());
|
|
|
- updatedFile.setShareWithSharee(localFile.isSharedWithSharee());
|
|
|
- } else {
|
|
|
- // remote eTag will not be updated unless file CONTENTS are synchronized
|
|
|
- updatedFile.setEtag("");
|
|
|
- }
|
|
|
+ // add to updatedFile data from local and remote file
|
|
|
+ setLocalFileDataOnUpdatedFile(remoteFile, localFile, updatedFile, mRemoteFolderChanged);
|
|
|
|
|
|
// check and fix, if needed, local storage path
|
|
|
FileStorageUtils.searchForLocalFileInDefaultPath(updatedFile, mAccount);
|
|
@@ -460,21 +419,7 @@ public class RefreshFolderOperation extends RemoteOperation {
|
|
|
|
|
|
// update file name for encrypted files
|
|
|
if (metadata != null) {
|
|
|
- updatedFile.setEncryptedFileName(updatedFile.getFileName());
|
|
|
- try {
|
|
|
- String decryptedFileName = metadata.getFiles().get(updatedFile.getFileName()).getEncrypted()
|
|
|
- .getFilename();
|
|
|
- String mimetype = metadata.getFiles().get(updatedFile.getFileName()).getEncrypted().getMimetype();
|
|
|
- updatedFile.setFileName(decryptedFileName);
|
|
|
-
|
|
|
- if (mimetype == null || mimetype.isEmpty()) {
|
|
|
- updatedFile.setMimetype("application/octet-stream");
|
|
|
- } else {
|
|
|
- updatedFile.setMimetype(mimetype);
|
|
|
- }
|
|
|
- } catch (NullPointerException e) {
|
|
|
- Log_OC.e(TAG, "Metadata for file " + updatedFile.getFileId() + " not found!");
|
|
|
- }
|
|
|
+ updateFileNameForEncryptedFile(metadata, updatedFile);
|
|
|
}
|
|
|
|
|
|
// we parse content, so either the folder itself or its direct parent (which we check) must be encrypted
|
|
@@ -490,6 +435,87 @@ public class RefreshFolderOperation extends RemoteOperation {
|
|
|
mChildren = updatedFiles;
|
|
|
}
|
|
|
|
|
|
+ @Nullable
|
|
|
+ private DecryptedFolderMetadata getDecryptedFolderMetadata(boolean encryptedAncestor) {
|
|
|
+ DecryptedFolderMetadata metadata;
|
|
|
+ if (encryptedAncestor && android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
|
|
|
+ metadata = EncryptionUtils.downloadFolderMetadata(mLocalFolder, getClient(), mContext, mAccount);
|
|
|
+ } else {
|
|
|
+ metadata = null;
|
|
|
+ }
|
|
|
+ return metadata;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void updateFileNameForEncryptedFile(@NonNull DecryptedFolderMetadata metadata, OCFile updatedFile) {
|
|
|
+ updatedFile.setEncryptedFileName(updatedFile.getFileName());
|
|
|
+ try {
|
|
|
+ String decryptedFileName = metadata.getFiles().get(updatedFile.getFileName()).getEncrypted()
|
|
|
+ .getFilename();
|
|
|
+ String mimetype = metadata.getFiles().get(updatedFile.getFileName()).getEncrypted().getMimetype();
|
|
|
+ updatedFile.setFileName(decryptedFileName);
|
|
|
+
|
|
|
+ if (mimetype == null || mimetype.isEmpty()) {
|
|
|
+ updatedFile.setMimetype("application/octet-stream");
|
|
|
+ } else {
|
|
|
+ updatedFile.setMimetype(mimetype);
|
|
|
+ }
|
|
|
+ } catch (NullPointerException e) {
|
|
|
+ Log_OC.e(TAG, "Metadata for file " + updatedFile.getFileId() + " not found!");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void setLocalFileDataOnUpdatedFile(OCFile remoteFile, OCFile localFile, OCFile updatedFile, boolean remoteFolderChanged) {
|
|
|
+ if (localFile != null) {
|
|
|
+ updatedFile.setFileId(localFile.getFileId());
|
|
|
+ updatedFile.setAvailableOffline(localFile.isAvailableOffline());
|
|
|
+ updatedFile.setLastSyncDateForData(localFile.getLastSyncDateForData());
|
|
|
+ updatedFile.setModificationTimestampAtLastSyncForData(
|
|
|
+ localFile.getModificationTimestampAtLastSyncForData()
|
|
|
+ );
|
|
|
+ updatedFile.setStoragePath(localFile.getStoragePath());
|
|
|
+
|
|
|
+ // eTag will not be updated unless file CONTENTS are synchronized
|
|
|
+ if (!updatedFile.isFolder() && localFile.isDown() &&
|
|
|
+ !updatedFile.getEtag().equals(localFile.getEtag())) {
|
|
|
+ updatedFile.setEtagInConflict(updatedFile.getEtag());
|
|
|
+ }
|
|
|
+
|
|
|
+ updatedFile.setEtag(localFile.getEtag());
|
|
|
+
|
|
|
+ if (updatedFile.isFolder()) {
|
|
|
+ updatedFile.setFileLength(remoteFile.getFileLength());
|
|
|
+ updatedFile.setMountType(remoteFile.getMountType());
|
|
|
+ } else if (remoteFolderChanged && MimeTypeUtil.isImage(remoteFile) &&
|
|
|
+ remoteFile.getModificationTimestamp() !=
|
|
|
+ localFile.getModificationTimestamp()) {
|
|
|
+ updatedFile.setNeedsUpdateThumbnail(true);
|
|
|
+ Log.d(TAG, "Image " + remoteFile.getFileName() + " updated on the server");
|
|
|
+ }
|
|
|
+
|
|
|
+ updatedFile.setPublicLink(localFile.getPublicLink());
|
|
|
+ updatedFile.setShareViaLink(localFile.isSharedViaLink());
|
|
|
+ updatedFile.setShareWithSharee(localFile.isSharedWithSharee());
|
|
|
+ } else {
|
|
|
+ // remote eTag will not be updated unless file CONTENTS are synchronized
|
|
|
+ updatedFile.setEtag("");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @NonNull
|
|
|
+ private Map<String, OCFile> prefillLocalFilesMap(DecryptedFolderMetadata metadata, List<OCFile> localFiles) {
|
|
|
+ Map<String, OCFile> localFilesMap = new HashMap<>(localFiles.size());
|
|
|
+
|
|
|
+ for (OCFile file : localFiles) {
|
|
|
+ String remotePath = file.getRemotePath();
|
|
|
+
|
|
|
+ if (metadata != null && !file.isFolder()) {
|
|
|
+ remotePath = file.getParentRemotePath() + file.getEncryptedFileName();
|
|
|
+ }
|
|
|
+ localFilesMap.put(remotePath, file);
|
|
|
+ }
|
|
|
+ return localFilesMap;
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* Performs a list of synchronization operations, determining if a download or upload is needed
|
|
|
* or if exists conflict due to changes both in local and remote contents of the each file.
|
|
@@ -520,7 +546,6 @@ public class RefreshFolderOperation extends RemoteOperation {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-
|
|
|
/**
|
|
|
* Syncs the Share resources for the files contained in the folder refreshed (children, not deeper descendants).
|
|
|
*
|
|
@@ -538,7 +563,7 @@ public class RefreshFolderOperation extends RemoteOperation {
|
|
|
|
|
|
if (result.isSuccess()) {
|
|
|
// update local database
|
|
|
- ArrayList<OCShare> shares = new ArrayList<OCShare>();
|
|
|
+ ArrayList<OCShare> shares = new ArrayList<>();
|
|
|
for (Object obj : result.getData()) {
|
|
|
shares.add((OCShare) obj);
|
|
|
}
|
|
@@ -548,19 +573,16 @@ public class RefreshFolderOperation extends RemoteOperation {
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
-
|
|
|
/**
|
|
|
- * Sends a message to any application component interested in the progress
|
|
|
+ * Sends a message to any application component interested in the progress
|
|
|
* of the synchronization.
|
|
|
*
|
|
|
- * @param event
|
|
|
- * @param dirRemotePath Remote path of a folder that was just synchronized
|
|
|
- * (with or without success)
|
|
|
- * @param result
|
|
|
+ * @param event broadcast event (Intent Action)
|
|
|
+ * @param dirRemotePath Remote path of a folder that was just synchronized
|
|
|
+ * (with or without success)
|
|
|
+ * @param result remote operation result
|
|
|
*/
|
|
|
- private void sendLocalBroadcast(
|
|
|
- String event, String dirRemotePath, RemoteOperationResult result
|
|
|
- ) {
|
|
|
+ private void sendLocalBroadcast(String event, String dirRemotePath, RemoteOperationResult result) {
|
|
|
Log_OC.d(TAG, "Send broadcast " + event);
|
|
|
Intent intent = new Intent(event);
|
|
|
intent.putExtra(FileSyncAdapter.EXTRA_ACCOUNT_NAME, mAccount.name);
|