+ /**
+ * Synchronizes the data retrieved from the server about the contents of the target folder
+ * with the current data in the local database.
+ *
+ * Grants that mChildren is updated with fresh data after execution.
+ *
+ * @param dataInServer Full response got from the server with the data of the target
+ * folder and its direct children.
+ * @param client Client instance to the remote server where the data were
+ * retrieved.
+ * @return 'True' when any change was made in the local data, 'false' otherwise.
+ */
+ private boolean synchronizeData(MultiStatus dataInServer, WebdavClient client) {
+ // get 'fresh data' from the database
+ mLocalFolder = mStorageManager.getFileById(mLocalFolder.getFileId());
+
+ // parse data from remote folder
+ WebdavEntry we = new WebdavEntry(dataInServer.getResponses()[0], client.getBaseUri().getPath());
+ OCFile remoteFolder = fillOCFile(we);
+ remoteFolder.setParentId(mLocalFolder.getParentId());
+ remoteFolder.setFileId(mLocalFolder.getFileId());
+
+ // check if remote and local folder are different
+ boolean folderChanged = !(remoteFolder.getEtag().equalsIgnoreCase(mLocalFolder.getEtag()));
+
+ if (!folderChanged) {
+ if (mUpdateFolderProperties) { // TODO check if this is really necessary
+ mStorageManager.saveFile(remoteFolder);
+ }
+
+ mChildren = mStorageManager.getDirectoryContent(mLocalFolder);
+
+ } else {
+ // read info of folder contents
+ List<OCFile> updatedFiles = new Vector<OCFile>(dataInServer.getResponses().length - 1);
+ List<SynchronizeFileOperation> filesToSyncContents = new Vector<SynchronizeFileOperation>();
+
+ // 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());
+
+ /// add to the remoteFile (the new one) data about LOCAL STATE (not existing in the server side)
+ remoteFile.setLastSyncDateForProperties(mCurrentSyncTime);
+ if (localFile != null) {
+ // properties of local state are kept unmodified
+ 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 need, 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,
+ false,
+ 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.saveFiles(updatedFiles);
+
+ // 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);
+
+ }
+
+ return folderChanged;
+
+ }
+
+
+ /**
+ * Removes obsolete children in the folder after saving all the new data.
+ */
+ private void removeObsoleteFiles() {
+ mChildren = mStorageManager.getDirectoryContent(mLocalFolder);
+ OCFile file;
+ String currentSavePath = FileStorageUtils.getSavePath(mAccount.name);
+ for (int i=0; i < mChildren.size(); ) {
+ file = mChildren.get(i);
+ if (file.getLastSyncDateForProperties() != mCurrentSyncTime) {
+ Log_OC.d(TAG, "removing file: " + file);
+ mStorageManager.removeFile(file, (file.isDown() && file.getStoragePath().startsWith(currentSavePath)));
+ mChildren.remove(i);
+ } else {
+ i++;
+ }
+ }
+ }
+
+
+ /**
+ * 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.
+ *
+ * If download or upload is needed, request the operation to the corresponding service and goes on.
+ *
+ * @param filesToSyncContents Synchronization operations to execute.
+ * @param client Interface to the remote ownCloud server.
+ */
+ private void startContentSynchronizations(List<SynchronizeFileOperation> filesToSyncContents, WebdavClient client) {
+ RemoteOperationResult contentsResult = null;
+ for (SynchronizeFileOperation op: filesToSyncContents) {
+ contentsResult = op.execute(client); // returns without waiting for upload or download finishes
+ if (!contentsResult.isSuccess()) {
+ if (contentsResult.getCode() == ResultCode.SYNC_CONFLICT) {
+ mConflictsFound++;
+ } else {
+ mFailsInFavouritesFound++;
+ 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
+ }
+ }
+
+