Browse Source

Keep the old operation to sync folder as DownloadFolderOperation to allow its use while the new SyncFolderOperation gets stable

David A. Velasco 10 năm trước cách đây
mục cha
commit
0162d2f9c9

+ 22 - 8
src/com/owncloud/android/files/FileOperationsHelper.java

@@ -209,11 +209,8 @@ public class FileOperationsHelper {
      * Request the synchronization of a file or folder with the OC server, including its contents.
      *
      * @param file          The file or folder to synchronize
-     * @param twoWays       TMP parameter: when 'false', only download is tried; valid for folders only, single files
-     *                      are always synchronized in both ways.
      */
-    public void syncFile(OCFile file, boolean twoWays) {
-        
+    public void syncFile(OCFile file) {
         if (!file.isFolder()){
             Intent intent = new Intent(mFileActivity, OperationsService.class);
             intent.setAction(OperationsService.ACTION_SYNC_FILE);
@@ -223,20 +220,37 @@ public class FileOperationsHelper {
             mWaitingForOpId = mFileActivity.getOperationsServiceBinder().queueNewOperation(intent);
             mFileActivity.showLoadingDialog();
             
-        } else if (twoWays){
+        } else {
             Intent intent = new Intent(mFileActivity, OperationsService.class);
             intent.setAction(OperationsService.ACTION_SYNC_FOLDER);
             intent.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount());
             intent.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath());
             mFileActivity.startService(intent);
 
+        }
+    }
+
+
+    /**
+     * Request the synchronization of a file or the DOWNLOAD OF A FOLDER, including its contents.
+     *
+     * For files, it's the same as syncFile(OCFile file); for folders, this method does not trigger uploads for
+     * file locally modified.
+     *
+     * Kept 'til synchronization of full folders is considered good enough.
+     *
+     * @param file          The file or folder to synchronize
+     */
+    public void downloadFile(OCFile file) {
+        if (!file.isFolder()){
+            syncFile(file);
+
         } else {
             Intent intent = new Intent(mFileActivity, OperationsService.class);
-            intent.setAction(OperationsService.ACTION_SYNC_FOLDER);
+            intent.setAction(OperationsService.ACTION_DOWNLOAD_FOLDER);
             intent.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount());
             intent.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath());
             mFileActivity.startService(intent);
-
         }
     }
 
@@ -254,7 +268,7 @@ public class FileOperationsHelper {
 
         /// immediate content synchronization
         if (file.isFavorite()) {
-            syncFile(file, true);
+            syncFile(file);
         }
     }
     

+ 552 - 0
src/com/owncloud/android/operations/DownloadFolderOperation.java

@@ -0,0 +1,552 @@
+/**
+ *   ownCloud Android client application
+ *
+ *   @author David A. Velasco
+ *   Copyright (C) 2015 ownCloud Inc.
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License version 2,
+ *   as published by the Free Software Foundation.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package com.owncloud.android.operations;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Vector;
+
+import android.accounts.Account;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+
+import com.owncloud.android.datamodel.FileDataStorageManager;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.files.services.FileDownloader;
+import com.owncloud.android.lib.common.OwnCloudClient;
+import com.owncloud.android.lib.common.operations.OperationCancelledException;
+import com.owncloud.android.lib.common.operations.RemoteOperationResult;
+import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode;
+import com.owncloud.android.lib.common.utils.Log_OC;
+import com.owncloud.android.lib.resources.files.ReadRemoteFileOperation;
+import com.owncloud.android.lib.resources.files.ReadRemoteFolderOperation;
+import com.owncloud.android.lib.resources.files.RemoteFile;
+import com.owncloud.android.operations.common.SyncOperation;
+import com.owncloud.android.services.OperationsService;
+import com.owncloud.android.utils.FileStorageUtils;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+//import android.support.v4.content.LocalBroadcastManager;
+
+
+/**
+ *  Remote operation performing the DOWNLOAD 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 download their contents also, BUT request for a new operation instance
+ *  doing so.
+ *
+ *  TODO: REMOVE WHEN SYNCHRONIZATION OF FULL FOLDERS IS GOOD ENOUGH
+ *
+ *  This class is here just to keep the 'download folder' option available separately until 'sync folder' is ready to go
+ */
+public class DownloadFolderOperation extends SyncOperation {
+
+    private static final String TAG = SynchronizeFolderOperation.class.getSimpleName();
+
+    /** Time stamp for the synchronization process in progress */
+    private long mCurrentSyncTime;
+
+    /** Remote path of the folder to synchronize */
+    private String mRemotePath;
+
+    /** Account where the file to synchronize belongs */
+    private Account mAccount;
+
+    /** Android context; necessary to send requests to the download service */
+    private Context mContext;
+
+    /** Locally cached information about folder to synchronize */
+    private OCFile mLocalFolder;
+
+    /** 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 mFailsInFileSyncsFound;
+
+    /** 'True' means that the remote folder changed and should be fetched */
+    private boolean mRemoteFolderChanged;
+
+    private List<OCFile> mFilesForDirectDownload;
+    // to avoid extra PROPFINDs when there was no change in the folder
+
+    private List<SyncOperation> mFilesToSyncContentsWithoutUpload;
+    // this will go out when 'folder synchronization' replaces 'folder download'; step by step
+
+    private List<SyncOperation> mFavouriteFilesToSyncContents;
+    // this will be used for every file when 'folder synchronization' replaces 'folder download'
+
+    private final AtomicBoolean mCancellationRequested;
+
+    /**
+     * Creates a new instance of {@link DownloadFolderOperation}.
+     *
+     * @param   context                 Application context.
+     * @param   remotePath              Path to synchronize.
+     * @param   account                 ownCloud account where the folder is located.
+     * @param   currentSyncTime         Time stamp for the synchronization process in progress.
+     */
+    public DownloadFolderOperation(Context context, String remotePath, Account account,
+                                      long currentSyncTime){
+        mRemotePath = remotePath;
+        mCurrentSyncTime = currentSyncTime;
+        mAccount = account;
+        mContext = context;
+        mRemoteFolderChanged = false;
+        mFilesForDirectDownload = new Vector<OCFile>();
+        mFilesToSyncContentsWithoutUpload = new Vector<SyncOperation>();
+        mFavouriteFilesToSyncContents = new Vector<SyncOperation>();
+        mCancellationRequested = new AtomicBoolean(false);
+    }
+
+
+    public int getConflictsFound() {
+        return mConflictsFound;
+    }
+
+    public int getFailsInFileSyncsFound() {
+        return mFailsInFileSyncsFound;
+    }
+
+    /**
+     * Performs the synchronization.
+     *
+     * {@inheritDoc}
+     */
+    @Override
+    protected RemoteOperationResult run(OwnCloudClient client) {
+        RemoteOperationResult result = null;
+        mFailsInFileSyncsFound = 0;
+        mConflictsFound = 0;
+
+        try {
+            // get locally cached information about folder 
+            mLocalFolder = getStorageManager().getFileByPath(mRemotePath);
+
+            result = checkForChanges(client);
+
+            if (result.isSuccess()) {
+                if (mRemoteFolderChanged) {
+                    result = fetchAndSyncRemoteFolder(client);
+
+                } else {
+                    prepareOpsFromLocalKnowledge();
+                }
+
+                if (result.isSuccess()) {
+                    syncContents(client);
+                }
+
+            }
+
+            if (mCancellationRequested.get()) {
+                throw new OperationCancelledException();
+            }
+
+        } catch (OperationCancelledException e) {
+            result = new RemoteOperationResult(e);
+        }
+
+        return result;
+
+    }
+
+    private RemoteOperationResult checkForChanges(OwnCloudClient client)
+            throws OperationCancelledException {
+        Log_OC.d(TAG, "Checking changes in " + mAccount.name + mRemotePath);
+
+        mRemoteFolderChanged = true;
+        RemoteOperationResult result = null;
+
+        if (mCancellationRequested.get()) {
+            throw new OperationCancelledException();
+        }
+
+        // remote request
+        ReadRemoteFileOperation operation = new ReadRemoteFileOperation(mRemotePath);
+        result = operation.execute(client);
+        if (result.isSuccess()){
+            OCFile remoteFolder = FileStorageUtils.fillOCFile((RemoteFile) result.getData().get(0));
+
+            // check if remote and local folder are different
+            mRemoteFolderChanged =
+                    !(remoteFolder.getEtag().equalsIgnoreCase(mLocalFolder.getEtag()));
+
+            result = new RemoteOperationResult(ResultCode.OK);
+
+            Log_OC.i(TAG, "Checked " + mAccount.name + mRemotePath + " : " +
+                    (mRemoteFolderChanged ? "changed" : "not changed"));
+
+        } else {
+            // check failed
+            if (result.getCode() == ResultCode.FILE_NOT_FOUND) {
+                removeLocalFolder();
+            }
+            if (result.isException()) {
+                Log_OC.e(TAG, "Checked " + mAccount.name + mRemotePath  + " : " +
+                        result.getLogMessage(), result.getException());
+            } else {
+                Log_OC.e(TAG, "Checked " + mAccount.name + mRemotePath + " : " +
+                        result.getLogMessage());
+            }
+
+        }
+
+        return result;
+    }
+
+
+    private RemoteOperationResult fetchAndSyncRemoteFolder(OwnCloudClient client)
+            throws OperationCancelledException {
+        if (mCancellationRequested.get()) {
+            throw new OperationCancelledException();
+        }
+
+        ReadRemoteFolderOperation operation = new ReadRemoteFolderOperation(mRemotePath);
+        RemoteOperationResult result = operation.execute(client);
+        Log_OC.d(TAG, "Synchronizing " + mAccount.name + mRemotePath);
+
+        if (result.isSuccess()) {
+            synchronizeData(result.getData(), client);
+            if (mConflictsFound > 0  || mFailsInFileSyncsFound > 0) {
+                result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT);
+                // should be a different result code, but will do the job
+            }
+        } else {
+            if (result.getCode() == ResultCode.FILE_NOT_FOUND)
+                removeLocalFolder();
+        }
+
+
+        return result;
+    }
+
+
+    private void removeLocalFolder() {
+        FileDataStorageManager storageManager = getStorageManager();
+        if (storageManager.fileExists(mLocalFolder.getFileId())) {
+            String currentSavePath = FileStorageUtils.getSavePath(mAccount.name);
+            storageManager.removeFolder(
+                    mLocalFolder,
+                    true,
+                    (   mLocalFolder.isDown() &&        // TODO: debug, I think this is
+                            // always false for folders
+                            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.
+     *
+     *  Grants that mChildren is updated with fresh data after execution.
+     *
+     *  @param folderAndFiles   Remote folder and children files in Folder
+     *
+     *  @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 void synchronizeData(ArrayList<Object> folderAndFiles, OwnCloudClient client)
+            throws OperationCancelledException {
+        FileDataStorageManager storageManager = getStorageManager();
+
+        // parse data from remote folder
+        OCFile remoteFolder = fillOCFile((RemoteFile)folderAndFiles.get(0));
+        remoteFolder.setParentId(mLocalFolder.getParentId());
+        remoteFolder.setFileId(mLocalFolder.getFileId());
+
+        Log_OC.d(TAG, "Remote folder " + mLocalFolder.getRemotePath()
+                + " changed - starting update of local data ");
+
+        List<OCFile> updatedFiles = new Vector<OCFile>(folderAndFiles.size() - 1);
+        mFilesForDirectDownload.clear();
+        mFilesToSyncContentsWithoutUpload.clear();
+        mFavouriteFilesToSyncContents.clear();
+
+        if (mCancellationRequested.get()) {
+            throw new OperationCancelledException();
+        }
+
+        // get current data about local contents of the folder to synchronize
+        // TODO Enable when "On Device" is recovered ?
+        List<OCFile> localFiles = storageManager.getFolderContent(mLocalFolder/*, false*/);
+        Map<String, OCFile> localFilesMap = new HashMap<String, OCFile>(localFiles.size());
+        for (OCFile file : localFiles) {
+            localFilesMap.put(file.getRemotePath(), file);
+        }
+
+        // loop to synchronize every child
+        OCFile remoteFile = null, localFile = null;
+        for (int i=1; i<folderAndFiles.size(); i++) {
+            /// new OCFile instance with the data from the server
+            remoteFile = fillOCFile((RemoteFile)folderAndFiles.get(i));
+            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 server)
+            remoteFile.setLastSyncDateForProperties(mCurrentSyncTime);
+            if (localFile != null) {
+                // some properties of local state are kept unmodified
+                remoteFile.setFileId(localFile.getFileId());
+                remoteFile.setFavorite(localFile.isFavorite());
+                remoteFile.setLastSyncDateForData(localFile.getLastSyncDateForData());
+                remoteFile.setModificationTimestampAtLastSyncForData(
+                        localFile.getModificationTimestampAtLastSyncForData()
+                );
+                remoteFile.setStoragePath(localFile.getStoragePath());
+                // eTag will not be updated unless contents are synchronized
+                //  (Synchronize[File|Folder]Operation with remoteFile as parameter)
+                remoteFile.setEtag(localFile.getEtag());
+                if (remoteFile.isFolder()) {
+                    remoteFile.setFileLength(localFile.getFileLength());
+                    // TODO move operations about size of folders to FileContentProvider
+                } else if (mRemoteFolderChanged && remoteFile.isImage() &&
+                        remoteFile.getModificationTimestamp() !=
+                                localFile.getModificationTimestamp()) {
+                    remoteFile.setNeedsUpdateThumbnail(true);
+                    Log.d(TAG, "Image " + remoteFile.getFileName() + " updated on the server");
+                }
+                remoteFile.setPublicLink(localFile.getPublicLink());
+                remoteFile.setShareByLink(localFile.isShareByLink());
+            } else {
+                // remote eTag will not be updated unless contents are synchronized
+                //  (Synchronize[File|Folder]Operation with remoteFile as parameter)
+                remoteFile.setEtag("");
+            }
+
+            /// check and fix, if needed, local storage path
+            searchForLocalFileInDefaultPath(remoteFile);
+
+            /// classify file to sync/download contents later
+            if (remoteFile.isFolder()) {
+                /// to download children files recursively
+                synchronized (mCancellationRequested) {
+                    if (mCancellationRequested.get()) {
+                        throw new OperationCancelledException();
+                    }
+                    startSyncFolderOperation(remoteFile.getRemotePath());
+                }
+
+            } else if (remoteFile.isFavorite()) {
+                /// prepare content synchronization for kept-in-sync files
+                SynchronizeFileOperation operation = new SynchronizeFileOperation(
+                        localFile,
+                        remoteFile,
+                        mAccount,
+                        true,
+                        mContext
+                );
+                mFavouriteFilesToSyncContents.add(operation);
+                
+            } else {
+                /// prepare limited synchronization for regular files
+                SynchronizeFileOperation operation = new SynchronizeFileOperation(
+                        localFile,
+                        remoteFile,
+                        mAccount,
+                        true,
+                        false,
+                        mContext
+                    );
+                mFilesToSyncContentsWithoutUpload.add(operation);
+            }
+
+            updatedFiles.add(remoteFile);
+        }
+
+        // save updated contents in local database
+        storageManager.saveFolder(remoteFolder, updatedFiles, localFilesMap.values());
+
+    }
+
+
+    private void prepareOpsFromLocalKnowledge() throws OperationCancelledException {
+        // TODO Enable when "On Device" is recovered ?
+        List<OCFile> children = getStorageManager().getFolderContent(mLocalFolder/*, false*/);
+        for (OCFile child : children) {
+            /// classify file to sync/download contents later
+            if (child.isFolder()) {
+                /// to download children files recursively
+                synchronized(mCancellationRequested) {
+                    if (mCancellationRequested.get()) {
+                        throw new OperationCancelledException();
+                    }
+                    startSyncFolderOperation(child.getRemotePath());
+                }
+
+            } else {
+                /// prepare limited synchronization for regular files
+                if (!child.isDown()) {
+                    mFilesForDirectDownload.add(child);
+
+                }
+
+            }
+        }
+    }
+
+
+    private void syncContents(OwnCloudClient client) throws OperationCancelledException {
+        startDirectDownloads();
+        startContentSynchronizations(mFilesToSyncContentsWithoutUpload, client);
+        startContentSynchronizations(mFavouriteFilesToSyncContents, client);
+    }
+
+
+    private void startDirectDownloads() throws OperationCancelledException {
+        for (OCFile file : mFilesForDirectDownload) {
+            synchronized(mCancellationRequested) {
+                if (mCancellationRequested.get()) {
+                    throw new OperationCancelledException();
+                }
+                Intent i = new Intent(mContext, FileDownloader.class);
+                i.putExtra(FileDownloader.EXTRA_ACCOUNT, mAccount);
+                i.putExtra(FileDownloader.EXTRA_FILE, file);
+                mContext.startService(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<SyncOperation> filesToSyncContents,
+                                              OwnCloudClient client)
+            throws OperationCancelledException {
+
+        Log_OC.v(TAG, "Starting content synchronization... ");
+        RemoteOperationResult contentsResult = null;
+        for (SyncOperation op: filesToSyncContents) {
+            if (mCancellationRequested.get()) {
+                throw new OperationCancelledException();
+            }
+            contentsResult = op.execute(getStorageManager(), mContext);
+            if (!contentsResult.isSuccess()) {
+                if (contentsResult.getCode() == ResultCode.SYNC_CONFLICT) {
+                    mConflictsFound++;
+                } else {
+                    mFailsInFileSyncsFound++;
+                    if (contentsResult.getException() != null) {
+                        Log_OC.e(TAG, "Error while synchronizing file : "
+                                +  contentsResult.getLogMessage(), contentsResult.getException());
+                    } else {
+                        Log_OC.e(TAG, "Error while synchronizing file : "
+                                + contentsResult.getLogMessage());
+                    }
+                }
+                // TODO - use the errors count in notifications
+            }   // won't let these fails break the synchronization process
+        }
+    }
+
+
+    /**
+     * Creates and populates a new {@link com.owncloud.android.datamodel.OCFile}
+     * object with the data read from the server.
+     *
+     * @param remote    remote file read from the server (remote file or folder).
+     * @return          New OCFile instance representing the remote resource described by we.
+     */
+    private OCFile fillOCFile(RemoteFile remote) {
+        OCFile file = new OCFile(remote.getRemotePath());
+        file.setCreationTimestamp(remote.getCreationTimestamp());
+        file.setFileLength(remote.getLength());
+        file.setMimetype(remote.getMimeType());
+        file.setModificationTimestamp(remote.getModifiedTimestamp());
+        file.setEtag(remote.getEtag());
+        file.setPermissions(remote.getPermissions());
+        file.setRemoteId(remote.getRemoteId());
+        return file;
+    }
+
+
+    /**
+     * Scans the default location for saving local copies of files searching for
+     * a 'lost' file with the same full name as the {@link com.owncloud.android.datamodel.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());
+            }
+        }
+    }
+
+
+    /**
+     * Cancel operation
+     */
+    public void cancel() {
+        mCancellationRequested.set(true);
+    }
+
+    public String getFolderPath() {
+        String path = mLocalFolder.getStoragePath();
+        if (path != null && path.length() > 0) {
+            return path;
+        }
+        return FileStorageUtils.getDefaultSavePathFor(mAccount.name, mLocalFolder);
+    }
+
+    private void startSyncFolderOperation(String path){
+        Intent intent = new Intent(mContext, OperationsService.class);
+        intent.setAction(OperationsService.ACTION_DOWNLOAD_FOLDER);
+        intent.putExtra(OperationsService.EXTRA_ACCOUNT, mAccount);
+        intent.putExtra(OperationsService.EXTRA_REMOTE_PATH, path);
+        mContext.startService(intent);
+    }
+
+    public String getRemotePath() {
+        return mRemotePath;
+    }
+}

+ 2 - 7
src/com/owncloud/android/operations/SynchronizeFolderOperation.java

@@ -60,7 +60,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
  *  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.
+ *  Does NOT enter in the child folders to synchronize their contents also, BUT requests for a new operation instance
+ *  doing so.
  */
 public class SynchronizeFolderOperation extends SyncOperation {
 
@@ -96,9 +97,6 @@ public class SynchronizeFolderOperation extends SyncOperation {
     private List<OCFile> mFilesForDirectDownload;
         // to avoid extra PROPFINDs when there was no change in the folder
     
-    private List<SyncOperation> mFilesToSyncContentsWithoutUpload;
-        // this will go out when 'folder synchronization' replaces 'folder download'; step by step  
-
     private List<SyncOperation> mFilesToSyncContents;
         // this will be used for every file when 'folder synchronization' replaces 'folder download' 
 
@@ -120,7 +118,6 @@ public class SynchronizeFolderOperation extends SyncOperation {
         mContext = context;
         mRemoteFolderChanged = false;
         mFilesForDirectDownload = new Vector<OCFile>();
-        mFilesToSyncContentsWithoutUpload = new Vector<SyncOperation>();
         mFilesToSyncContents = new Vector<SyncOperation>();
         mCancellationRequested = new AtomicBoolean(false);
     }
@@ -290,7 +287,6 @@ public class SynchronizeFolderOperation extends SyncOperation {
 
         List<OCFile> updatedFiles = new Vector<OCFile>(folderAndFiles.size() - 1);
         mFilesForDirectDownload.clear();
-        mFilesToSyncContentsWithoutUpload.clear();
         mFilesToSyncContents.clear();
 
         if (mCancellationRequested.get()) {
@@ -433,7 +429,6 @@ public class SynchronizeFolderOperation extends SyncOperation {
 
     private void syncContents(OwnCloudClient client) throws OperationCancelledException {
         startDirectDownloads();
-        startContentSynchronizations(mFilesToSyncContentsWithoutUpload, client);
         startContentSynchronizations(mFilesToSyncContents, client);
     }
 

+ 15 - 3
src/com/owncloud/android/services/OperationsService.java

@@ -53,6 +53,7 @@ import com.owncloud.android.lib.resources.shares.ShareType;
 import com.owncloud.android.lib.resources.status.OwnCloudVersion;
 import com.owncloud.android.lib.resources.users.GetRemoteUserNameOperation;
 import com.owncloud.android.operations.CopyFileOperation;
+import com.owncloud.android.operations.DownloadFolderOperation;
 import com.owncloud.android.operations.CreateFolderOperation;
 import com.owncloud.android.operations.CreateShareOperation;
 import com.owncloud.android.operations.GetServerInfoOperation;
@@ -100,7 +101,8 @@ public class OperationsService extends Service {
     public static final String ACTION_REMOVE = "REMOVE";
     public static final String ACTION_CREATE_FOLDER = "CREATE_FOLDER";
     public static final String ACTION_SYNC_FILE = "SYNC_FILE";
-    public static final String ACTION_SYNC_FOLDER = "SYNC_FOLDER";//for the moment, just to download
+    public static final String ACTION_SYNC_FOLDER = "SYNC_FOLDER";
+    public static final String ACTION_DOWNLOAD_FOLDER = "DOWNLOAD_FOLDER" ;
     public static final String ACTION_MOVE_FILE = "MOVE_FILE";
     public static final String ACTION_COPY_FILE = "COPY_FILE";
 
@@ -615,7 +617,7 @@ public class OperationsService extends Service {
                     );
                     
                 } else if (action.equals(ACTION_SYNC_FOLDER)) {
-                    // Sync file
+                    // Sync folder (all its descendant files are sync'ed)
                     String remotePath = operationIntent.getStringExtra(EXTRA_REMOTE_PATH);
                     operation = new SynchronizeFolderOperation(
                             this,                       // TODO remove this dependency from construction time
@@ -623,7 +625,17 @@ public class OperationsService extends Service {
                             account, 
                             System.currentTimeMillis()  // TODO remove this dependency from construction time
                     );
-                    
+
+                } else if (action.equals(ACTION_DOWNLOAD_FOLDER)) { // TODO remove when sync of folders is good enough
+                    // Download folder (all its descendant files are downloaded)
+                    String remotePath = operationIntent.getStringExtra(EXTRA_REMOTE_PATH);
+                    operation = new DownloadFolderOperation(
+                            this,
+                            remotePath,
+                            account,
+                            System.currentTimeMillis()
+                    );
+
                 } else if (action.equals(ACTION_MOVE_FILE)) {
                     // Move file/folder
                     String remotePath = operationIntent.getStringExtra(EXTRA_REMOTE_PATH);

+ 1 - 1
src/com/owncloud/android/ui/fragment/FileDetailFragment.java

@@ -252,7 +252,7 @@ public class FileDetailFragment extends FileFragment implements OnClickListener
             }
             case R.id.action_download_file:
             case R.id.action_sync_file: {
-                mContainerActivity.getFileOperationsHelper().syncFile(getFile(), true);
+                mContainerActivity.getFileOperationsHelper().syncFile(getFile());
                 return true;
             }
             case R.id.action_send_file: {

+ 2 - 2
src/com/owncloud/android/ui/fragment/OCFileListFragment.java

@@ -364,11 +364,11 @@ public class OCFileListFragment extends ExtendedListFragment implements FileActi
                 return true;
             }
             case R.id.action_download_file: {
-                mContainerActivity.getFileOperationsHelper().syncFile(mTargetFile, false);
+                mContainerActivity.getFileOperationsHelper().downloadFile(mTargetFile);
                 return true;
             }
             case R.id.action_sync_file: {
-                mContainerActivity.getFileOperationsHelper().syncFile(mTargetFile, true);
+                mContainerActivity.getFileOperationsHelper().syncFile(mTargetFile);
                 return true;
             }
             case R.id.action_cancel_download:

+ 1 - 1
src/com/owncloud/android/ui/preview/PreviewImageFragment.java

@@ -311,7 +311,7 @@ public class PreviewImageFragment extends FileFragment {
                 return true;
             }
             case R.id.action_sync_file: {
-                mContainerActivity.getFileOperationsHelper().syncFile(getFile(), true);
+                mContainerActivity.getFileOperationsHelper().syncFile(getFile());
                 return true;
             }
             case R.id.action_favorite_file:{

+ 1 - 1
src/com/owncloud/android/ui/preview/PreviewMediaFragment.java

@@ -378,7 +378,7 @@ public class PreviewMediaFragment extends FileFragment implements
                 return true;
             }
             case R.id.action_sync_file: {
-                mContainerActivity.getFileOperationsHelper().syncFile(getFile(), true);
+                mContainerActivity.getFileOperationsHelper().syncFile(getFile());
                 return true;
             }
             case R.id.action_favorite_file:{

+ 1 - 1
src/com/owncloud/android/ui/preview/PreviewTextFragment.java

@@ -324,7 +324,7 @@ public class PreviewTextFragment extends FileFragment {
                 return true;
             }
             case R.id.action_sync_file: {
-                mContainerActivity.getFileOperationsHelper().syncFile(getFile(), true);
+                mContainerActivity.getFileOperationsHelper().syncFile(getFile());
                 return true;
             }