Merge branch 'develop' into refresh_folder_contents_when_browsed_into
authorDavid A. Velasco <dvelasco@solidgear.es>
Fri, 18 Oct 2013 10:28:09 +0000 (12:28 +0200)
committerDavid A. Velasco <dvelasco@solidgear.es>
Fri, 18 Oct 2013 10:28:09 +0000 (12:28 +0200)
1  2 
src/com/owncloud/android/files/services/FileUploader.java
src/com/owncloud/android/operations/SynchronizeFileOperation.java
src/com/owncloud/android/operations/SynchronizeFolderOperation.java

@@@ -29,6 -29,7 +29,7 @@@ import java.util.concurrent.ConcurrentH
  import java.util.concurrent.ConcurrentMap;
  
  import org.apache.http.HttpStatus;
+ import org.apache.jackrabbit.webdav.DavConstants;
  import org.apache.jackrabbit.webdav.MultiStatus;
  import org.apache.jackrabbit.webdav.client.methods.PropFindMethod;
  
@@@ -376,7 -377,7 +377,7 @@@ public class FileUploader extends Servi
                  return false;
              String targetKey = buildRemoteName(account, file);
              synchronized (mPendingUploads) {
 -                if (file.isDirectory()) {
 +                if (file.isFolder()) {
                      // this can be slow if there are many uploads :(
                      Iterator<String> it = mPendingUploads.keySet().iterator();
                      boolean found = false;
          PropFindMethod propfind = null;
          RemoteOperationResult result = null;
          try {
-             propfind = new PropFindMethod(mUploadClient.getBaseUri()
-                     + WebdavUtils.encodePath(mCurrentUpload.getRemotePath()));
+             propfind = new PropFindMethod(mUploadClient.getBaseUri() + WebdavUtils.encodePath(mCurrentUpload.getRemotePath()),
+                     DavConstants.PROPFIND_ALL_PROP,
+                     DavConstants.DEPTH_0);
              int status = mUploadClient.executeMethod(propfind);
              boolean isMultiStatus = (status == HttpStatus.SC_MULTI_STATUS);
              if (isMultiStatus) {
@@@ -19,6 -19,7 +19,7 @@@
  package com.owncloud.android.operations;
  
  import org.apache.http.HttpStatus;
+ import org.apache.jackrabbit.webdav.DavConstants;
  import org.apache.jackrabbit.webdav.MultiStatus;
  import org.apache.jackrabbit.webdav.client.methods.PropFindMethod;
  
@@@ -27,7 -28,7 +28,7 @@@ import android.content.Context
  import android.content.Intent;
  
  import com.owncloud.android.Log_OC;
 -import com.owncloud.android.datamodel.DataStorageManager;
 +import com.owncloud.android.datamodel.FileDataStorageManager;
  import com.owncloud.android.datamodel.OCFile;
  import com.owncloud.android.files.services.FileDownloader;
  import com.owncloud.android.files.services.FileUploader;
@@@ -45,7 -46,7 +46,7 @@@ public class SynchronizeFileOperation e
      
      private OCFile mLocalFile;
      private OCFile mServerFile;
 -    private DataStorageManager mStorageManager;
 +    private FileDataStorageManager mStorageManager;
      private Account mAccount;
      private boolean mSyncFileContents;
      private boolean mLocalChangeAlreadyKnown;
@@@ -56,7 -57,7 +57,7 @@@
      public SynchronizeFileOperation(
              OCFile localFile,
              OCFile serverFile,          // make this null to let the operation checks the server; added to reuse info from SynchronizeFolderOperation 
 -            DataStorageManager storageManager, 
 +            FileDataStorageManager storageManager, 
              Account account, 
              boolean syncFileContents,
              boolean localChangeAlreadyKnown, 
@@@ -89,7 -90,9 +90,9 @@@
                  
                  if (mServerFile == null) {
                      /// take the duty of check the server for the current state of the file there
-                     propfind = new PropFindMethod(client.getBaseUri() + WebdavUtils.encodePath(mLocalFile.getRemotePath()));
+                     propfind = new PropFindMethod(client.getBaseUri() + WebdavUtils.encodePath(mLocalFile.getRemotePath()),
+                             DavConstants.PROPFIND_ALL_PROP,
+                             DavConstants.DEPTH_0);
                      int status = client.executeMethod(propfind, SYNC_READ_TIMEOUT, SYNC_CONNECTION_TIMEOUT);
                      boolean isMultiStatus = status == HttpStatus.SC_MULTI_STATUS;
                      if (isMultiStatus) {
                          // TODO this will be always true after the app is upgraded to database version 2; will result in unnecessary uploads
                
                      /// decide action to perform depending upon changes
 -                    if (localChanged && serverChanged) {
 +                    if (!mLocalFile.getEtag().isEmpty() && localChanged && serverChanged) {
                          result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT);
                    
                      } else if (localChanged) {
          file.setFileLength(we.contentLength());
          file.setMimetype(we.contentType());
          file.setModificationTimestamp(we.modifiedTimestamp());
 +        file.setEtag(we.etag());
 +        
          return file;
      }
  
@@@ -28,19 -28,19 +28,20 @@@ import java.util.List
  import java.util.Map;
  import java.util.Vector;
  
 -import org.apache.commons.httpclient.Header;
  import org.apache.http.HttpStatus;
+ import org.apache.jackrabbit.webdav.DavConstants;
  import org.apache.jackrabbit.webdav.MultiStatus;
  import org.apache.jackrabbit.webdav.client.methods.PropFindMethod;
  
  import android.accounts.Account;
  import android.content.Context;
 +import android.content.Intent;
  
  import com.owncloud.android.Log_OC;
 -import com.owncloud.android.datamodel.DataStorageManager;
 +import com.owncloud.android.datamodel.FileDataStorageManager;
  import com.owncloud.android.datamodel.OCFile;
  import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
 +import com.owncloud.android.syncadapter.FileSyncService;
  import com.owncloud.android.utils.FileStorageUtils;
  
  import eu.alefzero.webdav.WebdavClient;
@@@ -49,78 -49,51 +50,78 @@@ import eu.alefzero.webdav.WebdavUtils
  
  
  /**
 - * Remote operation performing the synchronization a the contents of a remote folder with the local database
 + *  Remote operation performing the synchronization of the list of files contained 
 + *  in a folder identified with its remote path.
 + *  
 + *  Fetches the list and properties of the files contained in the given folder, including their 
 + *  properties, and updates the local database with them.
 + *  
 + *  Does NOT enter in the child folders to synchronize their contents also.
   * 
 - * @author David A. Velasco
 + *  @author David A. Velasco
   */
  public class SynchronizeFolderOperation extends RemoteOperation {
  
      private static final String TAG = SynchronizeFolderOperation.class.getSimpleName();
  
 -    /** Remote folder to synchronize */
 -    private String mRemotePath;
      
 -    /** Timestamp for the synchronization in progress */
 +    /** Time stamp for the synchronization process in progress */
      private long mCurrentSyncTime;
      
 -    /** Id of the folder to synchronize in the local database */
 -    private long mParentId;
 +    /** 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 DataStorageManager mStorageManager;
 +    private FileDataStorageManager mStorageManager;
      
      /** Account where the file to synchronize belongs */
      private Account mAccount;
      
 -    /** Android context; necessary to send requests to the download service; maybe something to refactor */
 +    /** Android context; necessary to send requests to the download service */
      private Context mContext;
      
 -    /** Files and folders contained in the synchronized folder */
 +    /** Files and folders contained in the synchronized folder after a successful operation */
      private List<OCFile> mChildren;
  
 +    /** Counter of conflicts found between local and remote files */
      private int mConflictsFound;
  
 +    /** Counter of failed operations in synchronization of kept-in-sync files */
      private int mFailsInFavouritesFound;
  
 +    /** Map of remote and local paths to files that where locally stored in a location out of the ownCloud folder and couldn't be copied automatically into it */
      private Map<String, String> mForgottenLocalFiles;
 +
 +    /** 'True' means that this operation is part of a full account synchronization */ 
 +    private boolean mSyncFullAccount;
      
      
 -    public SynchronizeFolderOperation(  String remotePath, 
 +    /**
 +     * Creates a new instance of {@link SynchronizeFolderOperation}.
 +     * 
 +     * @param   remoteFolderPath        Remote folder to synchronize.
 +     * @param   currentSyncTime         Time stamp for the synchronization process in progress.
 +     * @param   localFolderId           Identifier in the local database of the folder to synchronize.
 +     * @param   updateFolderProperties  'True' means that the properties of the folder should be updated also, not just its content.
 +     * @param   syncFullAccount         'True' means that this operation is part of a full account synchronization.
 +     * @param   dataStorageManager      Interface with the local database.
 +     * @param   account                 ownCloud account where the folder is located. 
 +     * @param   context                 Application context.
 +     */
 +    public SynchronizeFolderOperation(  OCFile folder, 
                                          long currentSyncTime, 
 -                                        long parentId, 
 -                                        DataStorageManager dataStorageManager, 
 +                                        boolean updateFolderProperties,
 +                                        boolean syncFullAccount,
 +                                        FileDataStorageManager dataStorageManager, 
                                          Account account, 
                                          Context context ) {
 -        mRemotePath = remotePath;
 +        mLocalFolder = folder;
          mCurrentSyncTime = currentSyncTime;
 -        mParentId = parentId;
 +        mUpdateFolderProperties = updateFolderProperties;
 +        mSyncFullAccount = syncFullAccount;
          mStorageManager = dataStorageManager;
          mAccount = account;
          mContext = context;
      /**
       * Returns the list of files and folders contained in the synchronized folder, if called after synchronization is complete.
       * 
 -     * @return      List of files and folders contained in the synchronized folder.
 +     * @return  List of files and folders contained in the synchronized folder.
       */
      public List<OCFile> getChildren() {
          return mChildren;
      }
      
 -    
 +    /**
 +     * Performs the synchronization.
 +     * 
 +     * {@inheritDoc}
 +     */
      @Override
      protected RemoteOperationResult run(WebdavClient client) {
          RemoteOperationResult result = null;
          mFailsInFavouritesFound = 0;
          mConflictsFound = 0;
          mForgottenLocalFiles.clear();
 -        
 -        // code before in FileSyncAdapter.fetchData
 +        String remotePath = null;
          PropFindMethod query = null;
          try {
 -            Log_OC.d(TAG, "Synchronizing " + mAccount.name + ", fetching files in " + mRemotePath);
 -            
 +            remotePath = mLocalFolder.getRemotePath();
 +            Log_OC.d(TAG, "Synchronizing " + mAccount.name + remotePath);
 +
              // remote request 
-             query = new PropFindMethod(client.getBaseUri() + WebdavUtils.encodePath(remotePath));
 -            query = new PropFindMethod(client.getBaseUri() + WebdavUtils.encodePath(mRemotePath),
++            query = new PropFindMethod(client.getBaseUri() + WebdavUtils.encodePath(remotePath),
+                     DavConstants.PROPFIND_ALL_PROP,
+                     DavConstants.DEPTH_1);
              int status = client.executeMethod(query);
 -            
 -            // check and process response   - /// TODO take into account all the possible status per child-resource
 -            if (isMultiStatus(status)) { 
 -                MultiStatus resp = query.getResponseBodyAsMultiStatus();
 -            
 -                // synchronize properties of the parent folder, if necessary
 -                if (mParentId == DataStorageManager.ROOT_PARENT_ID) {
 -                    WebdavEntry we = new WebdavEntry(resp.getResponses()[0], client.getBaseUri().getPath());
 -                    OCFile parent = fillOCFile(we);
 -                    mStorageManager.saveFile(parent);
 -                    mParentId = parent.getFileId();
 -                }
 -                
 -                // read contents in folder
 -                List<OCFile> updatedFiles = new Vector<OCFile>(resp.getResponses().length - 1);
 -                List<SynchronizeFileOperation> filesToSyncContents = new Vector<SynchronizeFileOperation>();
 -                for (int i = 1; i < resp.getResponses().length; ++i) {
 -                    /// new OCFile instance with the data from the server
 -                    WebdavEntry we = new WebdavEntry(resp.getResponses()[i], client.getBaseUri().getPath());
 -                    OCFile file = fillOCFile(we);
 -                    
 -                    /// set data about local state, keeping unchanged former data if existing
 -                    file.setLastSyncDateForProperties(mCurrentSyncTime);
 -                    OCFile oldFile = mStorageManager.getFileByPath(file.getRemotePath());
 -                    if (oldFile != null) {
 -                        file.setKeepInSync(oldFile.keepInSync());
 -                        file.setLastSyncDateForData(oldFile.getLastSyncDateForData());
 -                        file.setModificationTimestampAtLastSyncForData(oldFile.getModificationTimestampAtLastSyncForData());    // must be kept unchanged when the file contents are not updated
 -                        checkAndFixForeignStoragePath(oldFile);
 -                        file.setStoragePath(oldFile.getStoragePath());
 -                    }
  
 -                    /// scan default location if local copy of file is not linked in OCFile instance
 -                    if (file.getStoragePath() == null && !file.isDirectory()) {
 -                        File f = new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, file));
 -                        if (f.exists()) {
 -                            file.setStoragePath(f.getAbsolutePath());
 -                            file.setLastSyncDateForData(f.lastModified());
 -                        }
 -                    }
 -                    
 -                    /// prepare content synchronization for kept-in-sync files
 -                    if (file.keepInSync()) {
 -                        SynchronizeFileOperation operation = new SynchronizeFileOperation(  oldFile,        
 -                                                                                            file, 
 -                                                                                            mStorageManager,
 -                                                                                            mAccount,       
 -                                                                                            true, 
 -                                                                                            false,          
 -                                                                                            mContext
 -                                                                                            );
 -                        filesToSyncContents.add(operation);
 -                    }
 -                
 -                    updatedFiles.add(file);
 -                }
 -                                
 -                // 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 files AFTER saving last properties
 -                SynchronizeFileOperation op = null;
 -                RemoteOperationResult contentsResult = null;
 -                for (int i=0; i < filesToSyncContents.size(); i++) {
 -                    op = filesToSyncContents.get(i);
 -                    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
 -                }
 -
 -                    
 -                // removal of obsolete files
 -                mChildren = mStorageManager.getDirectoryContent(mStorageManager.getFileById(mParentId));
 -                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);
 +            // 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 {
 -                        i++;
 +                        result = new RemoteOperationResult(true, status, query.getResponseHeaders());
                      }
 +                } else {
 +                    result = new RemoteOperationResult(ResultCode.OK_NO_CHANGES_ON_DIR);
                  }
                  
              } else {
 +                // synchronization failed
                  client.exhaustResponse(query.getResponseBodyAsStream());
 -            }
 -            
 -            // prepare result object
 -            if (isMultiStatus(status)) {
 -                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());
 +                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)));
 +                    }
                  }
 -            } else {
                  result = new RemoteOperationResult(false, status, query.getResponseHeaders());
              }
 -            
 -            
 -            
 +
          } catch (Exception e) {
              result = new RemoteOperationResult(e);
              
              if (query != null)
                  query.releaseConnection();  // let the connection available for other methods
              if (result.isSuccess()) {
 -                Log_OC.i(TAG, "Synchronizing " + mAccount.name + ", folder " + mRemotePath + ": " + result.getLogMessage());
 +                Log_OC.i(TAG, "Synchronized " + mAccount.name + remotePath + ": " + result.getLogMessage());
              } else {
                  if (result.isException()) {
 -                    Log_OC.e(TAG, "Synchronizing " + mAccount.name + ", folder " + mRemotePath + ": " + result.getLogMessage(), result.getException());
 +                    Log_OC.e(TAG, "Synchronized " + mAccount.name + remotePath  + ": " + result.getLogMessage(), result.getException());
                  } else {
 -                    Log_OC.e(TAG, "Synchronizing " + mAccount.name + ", folder " + mRemotePath + ": " + result.getLogMessage());
 +                    Log_OC.e(TAG, "Synchronized " + mAccount.name + remotePath + ": " + result.getLogMessage());
                  }
              }
 +            
 +            if (!mSyncFullAccount) {            
 +                sendStickyBroadcast(false, remotePath, result);
 +            }
          }
 -        
 +
          return result;
      }
 -    
 +
 +
 +    /**
 +     *  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);
 +            }
 +            
 +            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<OCFile> updatedFiles = new Vector<OCFile>(dataInServer.getResponses().length - 1);
 +            List<SynchronizeFileOperation> filesToSyncContents = new Vector<SynchronizeFileOperation>();
 +
 +            // get current data about local contents of the folder to synchronize
 +            List<OCFile> localFiles = mStorageManager.getFolderContent(mLocalFolder);
 +            Map<String, OCFile> localFilesMap = new HashMap<String, OCFile>(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());
 +                
 +                /// 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, 
 +                                                                                        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.saveFolder(remoteFolder, updatedFiles, localFilesMap.values());
 +
 +            // 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;
 +        
 +    }
 +
 +    /**
 +     * 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
 +        }
 +    }
 +
  
      public boolean isMultiStatus(int status) {
          return (status == HttpStatus.SC_MULTI_STATUS); 
          file.setFileLength(we.contentLength());
          file.setMimetype(we.contentType());
          file.setModificationTimestamp(we.modifiedTimestamp());
 -        file.setParentId(mParentId);
 +        file.setEtag(we.etag());
          return file;
      }
      
       * 
       * If the copy fails, the link to the local file is nullified. The account of forgotten files is kept in 
       * {@link #mForgottenLocalFiles}
 -     * 
 +     *) 
       * @param file      File to check and fix.
       */
      private void checkAndFixForeignStoragePath(OCFile file) {
      }
  
  
 +    /**
 +     * Scans the default location for saving local copies of files searching for
 +     * a 'lost' file with the same full name as the {@link OCFile} received as 
 +     * parameter.
 +     *  
 +     * @param file      File to associate a possible 'lost' local file.
 +     */
 +    private void searchForLocalFileInDefaultPath(OCFile file) {
 +        if (file.getStoragePath() == null && !file.isFolder()) {
 +            File f = new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, file));
 +            if (f.exists()) {
 +                file.setStoragePath(f.getAbsolutePath());
 +                file.setLastSyncDateForData(f.lastModified());
 +            }
 +        }
 +    }
 +
 +    
 +    /**
 +     * Sends a message to any application component interested in the progress of the synchronization.
 +     * 
 +     * @param inProgress        'True' when the synchronization progress is not finished.
 +     * @param dirRemotePath     Remote path of a folder that was just synchronized (with or without success)
 +     */
 +    private void sendStickyBroadcast(boolean inProgress, String dirRemotePath, RemoteOperationResult result) {
 +        Intent i = new Intent(FileSyncService.SYNC_MESSAGE);
 +        i.putExtra(FileSyncService.IN_PROGRESS, inProgress);
 +        i.putExtra(FileSyncService.ACCOUNT_NAME, mAccount.name);
 +        if (dirRemotePath != null) {
 +            i.putExtra(FileSyncService.SYNC_FOLDER_REMOTE_PATH, dirRemotePath);
 +        }
 +        if (result != null) {
 +            i.putExtra(FileSyncService.SYNC_RESULT, result);
 +        }
 +        mContext.sendStickyBroadcast(i);
 +    }
 +
  }