From: David A. Velasco Date: Mon, 21 Oct 2013 08:51:58 +0000 (+0200) Subject: Check changes in eTag of folders BEFORE requesting their list of contents X-Git-Tag: oc-android-1.5.5~155^2~9 X-Git-Url: http://git.linex4red.de/pub/Android/ownCloud.git/commitdiff_plain/e0dc1134f8acd4cd1fa89401d86488fe0e9a86c5?hp=-c Check changes in eTag of folders BEFORE requesting their list of contents --- e0dc1134f8acd4cd1fa89401d86488fe0e9a86c5 diff --git a/src/com/owncloud/android/operations/RemoteOperationResult.java b/src/com/owncloud/android/operations/RemoteOperationResult.java index ca88210d..999974cf 100644 --- a/src/com/owncloud/android/operations/RemoteOperationResult.java +++ b/src/com/owncloud/android/operations/RemoteOperationResult.java @@ -60,7 +60,6 @@ public class RemoteOperationResult implements Serializable { OK, OK_SSL, OK_NO_SSL, - OK_NO_CHANGES_ON_DIR, UNHANDLED_HTTP_CODE, UNAUTHORIZED, FILE_NOT_FOUND, @@ -99,7 +98,7 @@ public class RemoteOperationResult implements Serializable { public RemoteOperationResult(ResultCode code) { mCode = code; - mSuccess = (code == ResultCode.OK || code == ResultCode.OK_SSL || code == ResultCode.OK_NO_SSL || code == ResultCode.OK_NO_CHANGES_ON_DIR); + mSuccess = (code == ResultCode.OK || code == ResultCode.OK_SSL || code == ResultCode.OK_NO_SSL); } private RemoteOperationResult(boolean success, int httpCode) { diff --git a/src/com/owncloud/android/operations/SynchronizeFolderOperation.java b/src/com/owncloud/android/operations/SynchronizeFolderOperation.java index d1dd3c4f..f16a1f3e 100644 --- a/src/com/owncloud/android/operations/SynchronizeFolderOperation.java +++ b/src/com/owncloud/android/operations/SynchronizeFolderOperation.java @@ -71,9 +71,6 @@ public class SynchronizeFolderOperation extends RemoteOperation { /** Remote folder to synchronize */ private OCFile mLocalFolder; - /** 'True' means that the properties of the folder should be updated also, not just its content */ - private boolean mUpdateFolderProperties; - /** Access to the local database */ private FileDataStorageManager mStorageManager; @@ -97,6 +94,9 @@ public class SynchronizeFolderOperation extends RemoteOperation { /** 'True' means that this operation is part of a full account synchronization */ private boolean mSyncFullAccount; + + /** 'True' means that the remote folder changed from last synchronization and should be fetched */ + private boolean mRemoteFolderChanged; /** @@ -113,19 +113,18 @@ public class SynchronizeFolderOperation extends RemoteOperation { */ public SynchronizeFolderOperation( OCFile folder, long currentSyncTime, - boolean updateFolderProperties, boolean syncFullAccount, FileDataStorageManager dataStorageManager, Account account, Context context ) { mLocalFolder = folder; mCurrentSyncTime = currentSyncTime; - mUpdateFolderProperties = updateFolderProperties; mSyncFullAccount = syncFullAccount; mStorageManager = dataStorageManager; mAccount = account; mContext = context; mForgottenLocalFiles = new HashMap(); + mRemoteFolderChanged = false; } @@ -161,6 +160,86 @@ public class SynchronizeFolderOperation extends RemoteOperation { mFailsInFavouritesFound = 0; mConflictsFound = 0; mForgottenLocalFiles.clear(); + + result = checkForChanges(client); + + if (result.isSuccess()) { + if (mRemoteFolderChanged) { + result = fetchAndSyncRemoteFolder(client); + } else { + mChildren = mStorageManager.getFolderContent(mLocalFolder); + } + } + + if (!mSyncFullAccount) { + sendStickyBroadcast(false, mLocalFolder.getRemotePath(), result); + } + + return result; + + } + + + private RemoteOperationResult checkForChanges(WebdavClient client) { + mRemoteFolderChanged = false; + RemoteOperationResult result = null; + String remotePath = null; + PropFindMethod query = null; + + try { + remotePath = mLocalFolder.getRemotePath(); + Log_OC.d(TAG, "Checking changes in " + mAccount.name + remotePath); + + // remote request + query = new PropFindMethod(client.getBaseUri() + WebdavUtils.encodePath(remotePath), + DavConstants.PROPFIND_ALL_PROP, + DavConstants.DEPTH_0); + int status = client.executeMethod(query); + + // check and process response + if (isMultiStatus(status)) { + // parse data from remote folder + WebdavEntry we = new WebdavEntry(query.getResponseBodyAsMultiStatus().getResponses()[0], client.getBaseUri().getPath()); + OCFile remoteFolder = fillOCFile(we); + + // check if remote and local folder are different + mRemoteFolderChanged = !(remoteFolder.getEtag().equalsIgnoreCase(mLocalFolder.getEtag())); + + result = new RemoteOperationResult(ResultCode.OK); + + } else { + // check failed + client.exhaustResponse(query.getResponseBodyAsStream()); + if (status == HttpStatus.SC_NOT_FOUND) { + removeLocalFolder(); + } + result = new RemoteOperationResult(false, status, query.getResponseHeaders()); + } + + } catch (Exception e) { + result = new RemoteOperationResult(e); + + + } finally { + if (query != null) + query.releaseConnection(); // let the connection available for other methods + if (result.isSuccess()) { + Log_OC.i(TAG, "Checked " + mAccount.name + remotePath + " : " + (mRemoteFolderChanged ? "changed" : "not changed")); + } else { + if (result.isException()) { + Log_OC.e(TAG, "Checked " + mAccount.name + remotePath + " : " + result.getLogMessage(), result.getException()); + } else { + Log_OC.e(TAG, "Checked " + mAccount.name + remotePath + " : " + result.getLogMessage()); + } + } + + } + return result; + } + + + private RemoteOperationResult fetchAndSyncRemoteFolder(WebdavClient client) { + RemoteOperationResult result = null; String remotePath = null; PropFindMethod query = null; try { @@ -175,25 +254,18 @@ public class SynchronizeFolderOperation extends RemoteOperation { // check and process response if (isMultiStatus(status)) { - boolean folderChanged = synchronizeData(query.getResponseBodyAsMultiStatus(), client); - if (folderChanged) { - if (mConflictsFound > 0 || mFailsInFavouritesFound > 0) { - result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT); // should be different result, but will do the job - } else { - result = new RemoteOperationResult(true, status, query.getResponseHeaders()); - } + synchronizeData(query.getResponseBodyAsMultiStatus(), client); + if (mConflictsFound > 0 || mFailsInFavouritesFound > 0) { + result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT); // should be different result, but will do the job } else { - result = new RemoteOperationResult(ResultCode.OK_NO_CHANGES_ON_DIR); + result = new RemoteOperationResult(true, status, query.getResponseHeaders()); } } else { // synchronization failed client.exhaustResponse(query.getResponseBodyAsStream()); if (status == HttpStatus.SC_NOT_FOUND) { - if (mStorageManager.fileExists(mLocalFolder.getFileId())) { - String currentSavePath = FileStorageUtils.getSavePath(mAccount.name); - mStorageManager.removeFolder(mLocalFolder, true, (mLocalFolder.isDown() && mLocalFolder.getStoragePath().startsWith(currentSavePath))); - } + removeLocalFolder(); } result = new RemoteOperationResult(false, status, query.getResponseHeaders()); } @@ -215,15 +287,19 @@ public class SynchronizeFolderOperation extends RemoteOperation { } } - if (!mSyncFullAccount) { - sendStickyBroadcast(false, remotePath, result); - } } - return result; } + private void removeLocalFolder() { + if (mStorageManager.fileExists(mLocalFolder.getFileId())) { + String currentSavePath = FileStorageUtils.getSavePath(mAccount.name); + mStorageManager.removeFolder(mLocalFolder, true, (mLocalFolder.isDown() && mLocalFolder.getStoragePath().startsWith(currentSavePath))); + } + } + + /** * Synchronizes the data retrieved from the server about the contents of the target folder * with the current data in the local database. @@ -236,7 +312,7 @@ public class SynchronizeFolderOperation extends RemoteOperation { * retrieved. * @return 'True' when any change was made in the local data, 'false' otherwise. */ - private boolean synchronizeData(MultiStatus dataInServer, WebdavClient client) { + private void synchronizeData(MultiStatus dataInServer, WebdavClient client) { // get 'fresh data' from the database mLocalFolder = mStorageManager.getFileById(mLocalFolder.getFileId()); @@ -246,91 +322,75 @@ public class SynchronizeFolderOperation extends RemoteOperation { remoteFolder.setParentId(mLocalFolder.getParentId()); remoteFolder.setFileId(mLocalFolder.getFileId()); - // check if remote and local folder are different - boolean folderChanged = !(remoteFolder.getEtag().equalsIgnoreCase(mLocalFolder.getEtag())); + Log_OC.d(TAG, "Remote folder " + mLocalFolder.getRemotePath() + " changed - starting update of local data "); - if (!folderChanged) { - if (mUpdateFolderProperties) { // TODO check if this is really necessary - mStorageManager.saveFile(remoteFolder); - } - - Log_OC.d(TAG, "Remote folder " + mLocalFolder.getRemotePath() + " didn't change"); - mChildren = mStorageManager.getFolderContent(mLocalFolder); - - } else { - Log_OC.d(TAG, "Remote folder " + mLocalFolder.getRemotePath() + " changed - starting update of local data "); + List updatedFiles = new Vector(dataInServer.getResponses().length - 1); + List filesToSyncContents = new Vector(); + + // get current data about local contents of the folder to synchronize + List localFiles = mStorageManager.getFolderContent(mLocalFolder); + Map localFilesMap = new HashMap(localFiles.size()); + for (OCFile file : localFiles) { + localFilesMap.put(file.getRemotePath(), file); + } + + // loop to update every child + OCFile remoteFile = null, localFile = null; + for (int i = 1; i < dataInServer.getResponses().length; ++i) { + /// new OCFile instance with the data from the server + we = new WebdavEntry(dataInServer.getResponses()[i], client.getBaseUri().getPath()); + remoteFile = fillOCFile(we); + remoteFile.setParentId(mLocalFolder.getFileId()); + + /// retrieve local data for the read file + //localFile = mStorageManager.getFileByPath(remoteFile.getRemotePath()); + localFile = localFilesMap.remove(remoteFile.getRemotePath()); - List updatedFiles = new Vector(dataInServer.getResponses().length - 1); - List filesToSyncContents = new Vector(); - - // get current data about local contents of the folder to synchronize - List localFiles = mStorageManager.getFolderContent(mLocalFolder); - Map localFilesMap = new HashMap(localFiles.size()); - for (OCFile file : localFiles) { - localFilesMap.put(file.getRemotePath(), file); + /// add to the remoteFile (the new one) data about LOCAL STATE (not existing in the server side) + remoteFile.setLastSyncDateForProperties(mCurrentSyncTime); + if (localFile != null) { + // some properties of local state are kept unmodified + remoteFile.setFileId(localFile.getFileId()); + remoteFile.setKeepInSync(localFile.keepInSync()); + remoteFile.setLastSyncDateForData(localFile.getLastSyncDateForData()); + remoteFile.setModificationTimestampAtLastSyncForData(localFile.getModificationTimestampAtLastSyncForData()); + remoteFile.setStoragePath(localFile.getStoragePath()); + remoteFile.setEtag(localFile.getEtag()); // eTag will not be updated unless contents are synchronized (Synchronize[File|Folder]Operation with remoteFile as parameter) + } else { + remoteFile.setEtag(""); // remote eTag will not be updated unless contents are synchronized (Synchronize[File|Folder]Operation with remoteFile as parameter) } - - // loop to update every child - OCFile remoteFile = null, localFile = null; - for (int i = 1; i < dataInServer.getResponses().length; ++i) { - /// new OCFile instance with the data from the server - we = new WebdavEntry(dataInServer.getResponses()[i], client.getBaseUri().getPath()); - remoteFile = fillOCFile(we); - remoteFile.setParentId(mLocalFolder.getFileId()); - - /// retrieve local data for the read file - //localFile = mStorageManager.getFileByPath(remoteFile.getRemotePath()); - localFile = localFilesMap.remove(remoteFile.getRemotePath()); - - /// add to the remoteFile (the new one) data about LOCAL STATE (not existing in the server side) - remoteFile.setLastSyncDateForProperties(mCurrentSyncTime); - if (localFile != null) { - // some properties of local state are kept unmodified - remoteFile.setFileId(localFile.getFileId()); - remoteFile.setKeepInSync(localFile.keepInSync()); - remoteFile.setLastSyncDateForData(localFile.getLastSyncDateForData()); - remoteFile.setModificationTimestampAtLastSyncForData(localFile.getModificationTimestampAtLastSyncForData()); - remoteFile.setStoragePath(localFile.getStoragePath()); - remoteFile.setEtag(localFile.getEtag()); // eTag will not be updated unless contents are synchronized (Synchronize[File|Folder]Operation with remoteFile as parameter) - } else { - remoteFile.setEtag(""); // remote eTag will not be updated unless contents are synchronized (Synchronize[File|Folder]Operation with remoteFile as parameter) - } - /// check and fix, if needed, local storage path - checkAndFixForeignStoragePath(remoteFile); // fixing old policy - now local files must be copied into the ownCloud local folder - searchForLocalFileInDefaultPath(remoteFile); // legacy - - /// prepare content synchronization for kept-in-sync files - if (remoteFile.keepInSync()) { - SynchronizeFileOperation operation = new SynchronizeFileOperation( localFile, - remoteFile, - mStorageManager, - mAccount, - true, - mContext - ); - filesToSyncContents.add(operation); - } - - updatedFiles.add(remoteFile); + /// check and fix, if needed, local storage path + checkAndFixForeignStoragePath(remoteFile); // fixing old policy - now local files must be copied into the ownCloud local folder + searchForLocalFileInDefaultPath(remoteFile); // legacy + + /// prepare content synchronization for kept-in-sync files + if (remoteFile.keepInSync()) { + SynchronizeFileOperation operation = new SynchronizeFileOperation( localFile, + remoteFile, + mStorageManager, + mAccount, + true, + mContext + ); + filesToSyncContents.add(operation); } + + updatedFiles.add(remoteFile); + } - // save updated contents in local database; all at once, trying to get a best performance in database update (not a big deal, indeed) - mStorageManager.saveFolder(remoteFolder, updatedFiles, localFilesMap.values()); + // save updated contents in local database; all at once, trying to get a best performance in database update (not a big deal, indeed) + mStorageManager.saveFolder(remoteFolder, updatedFiles, localFilesMap.values()); - // request for the synchronization of file contents AFTER saving current remote properties - startContentSynchronizations(filesToSyncContents, client); + // request for the synchronization of file contents AFTER saving current remote properties + startContentSynchronizations(filesToSyncContents, client); - // removal of obsolete files - //removeObsoleteFiles(); - - // must be done AFTER saving all the children information, so that eTag is not updated in the database in case of unexpected exceptions - //mStorageManager.saveFile(remoteFolder); - mChildren = updatedFiles; - - } - - return folderChanged; + // removal of obsolete files + //removeObsoleteFiles(); + + // must be done AFTER saving all the children information, so that eTag is not updated in the database in case of unexpected exceptions + //mStorageManager.saveFile(remoteFolder); + mChildren = updatedFiles; } @@ -486,4 +546,9 @@ public class SynchronizeFolderOperation extends RemoteOperation { mContext.sendStickyBroadcast(i); } + + public boolean getRemoteFolderChanged() { + return mRemoteFolderChanged; + } + } diff --git a/src/com/owncloud/android/syncadapter/FileSyncAdapter.java b/src/com/owncloud/android/syncadapter/FileSyncAdapter.java index 06a83e99..2bb36729 100644 --- a/src/com/owncloud/android/syncadapter/FileSyncAdapter.java +++ b/src/com/owncloud/android/syncadapter/FileSyncAdapter.java @@ -149,7 +149,7 @@ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter { updateOCVersion(); mCurrentSyncTime = System.currentTimeMillis(); if (!mCancellation) { - synchronizeFolder(getStorageManager().getFileByPath(OCFile.ROOT_PATH), true); + synchronizeFolder(getStorageManager().getFileByPath(OCFile.ROOT_PATH)); } else { Log_OC.d(TAG, "Leaving synchronization before synchronizing the root folder because cancelation request"); @@ -218,9 +218,8 @@ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter { * depth first strategy. * * @param folder Folder to synchronize. - * @param updateFolderProperties When 'true', updates also the properties of the of the target folder. */ - private void synchronizeFolder(OCFile folder, boolean updateFolderProperties) { + private void synchronizeFolder(OCFile folder) { if (mFailedResultsCounter > MAX_FAILED_RESULTS || isFinisher(mLastFailedResult)) return; @@ -239,7 +238,6 @@ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter { // folder synchronization SynchronizeFolderOperation synchFolderOp = new SynchronizeFolderOperation( folder, mCurrentSyncTime, - updateFolderProperties, true, getStorageManager(), getAccount(), @@ -261,9 +259,11 @@ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter { if (synchFolderOp.getForgottenLocalFiles().size() > 0) { mForgottenLocalFiles.putAll(synchFolderOp.getForgottenLocalFiles()); } - // synchronize children folders - List children = synchFolderOp.getChildren(); - fetchChildren(folder, children); // beware of the 'hidden' recursion here! + if (result.isSuccess() && synchFolderOp.getRemoteFolderChanged()) { + // synchronize children folders + List children = synchFolderOp.getChildren(); + fetchChildren(folder, children); // beware of the 'hidden' recursion here! + } } else { // in failures, the statistics for the global result are updated @@ -312,7 +312,7 @@ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter { for (i=0; i < files.size() && !mCancellation; i++) { OCFile newFile = files.get(i); if (newFile.isFolder()) { - synchronizeFolder(newFile, false); + synchronizeFolder(newFile); // update the size of the parent folder again after recursive synchronization getStorageManager().updateFolderSize(parent.getFileId()); sendStickyBroadcast(true, parent.getRemotePath(), null); // notify again to refresh size in UI diff --git a/src/com/owncloud/android/ui/activity/FileDisplayActivity.java b/src/com/owncloud/android/ui/activity/FileDisplayActivity.java index d65ec6c8..5eaa76c7 100644 --- a/src/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/src/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -1449,7 +1449,6 @@ OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNa RemoteOperation synchFolderOp = new SynchronizeFolderOperation( folder, currentSyncTime, false, - false, getStorageManager(), getAccount(), getApplicationContext()