X-Git-Url: http://git.linex4red.de/pub/Android/ownCloud.git/blobdiff_plain/a6a56bae2acae4f69c421fa5b8166a3cd5ebaf9f..ff82b51e49f40155e7c340090f5ee759af2bf3ad:/src/com/owncloud/android/operations/SynchronizeFolderOperation.java?ds=sidebyside diff --git a/src/com/owncloud/android/operations/SynchronizeFolderOperation.java b/src/com/owncloud/android/operations/SynchronizeFolderOperation.java index 7b157c38..6bf1ef82 100644 --- a/src/com/owncloud/android/operations/SynchronizeFolderOperation.java +++ b/src/com/owncloud/android/operations/SynchronizeFolderOperation.java @@ -1,5 +1,8 @@ -/* ownCloud Android client application - * Copyright (C) 2012-2013 ownCloud Inc. +/** + * 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, @@ -18,351 +21,525 @@ package com.owncloud.android.operations; import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; +import java.util.ArrayList; import java.util.HashMap; 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.MultiStatus; -import org.apache.jackrabbit.webdav.client.methods.PropFindMethod; - import android.accounts.Account; import android.content.Context; +import android.content.Intent; +import android.util.Log; -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.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 eu.alefzero.webdav.WebdavClient; -import eu.alefzero.webdav.WebdavEntry; -import eu.alefzero.webdav.WebdavUtils; +import java.util.concurrent.atomic.AtomicBoolean; + +//import android.support.v4.content.LocalBroadcastManager; /** - * Remote operation performing the synchronization a the contents of a remote folder with the local database - * - * @author David A. Velasco + * 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. */ -public class SynchronizeFolderOperation extends RemoteOperation { +public class SynchronizeFolderOperation extends SyncOperation { 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; - - /** Access to the local database */ - private DataStorageManager mStorageManager; + + /** 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; 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 */ - private List mChildren; + /** Locally cached information about folder to synchronize */ + private OCFile mLocalFolder; + + /** Files and folders contained in the synchronized folder after a successful operation */ + //private List mChildren; + + /** Counter of conflicts found between local and remote files */ private int mConflictsFound; - private int mFailsInFavouritesFound; + /** Counter of failed operations in synchronization of kept-in-sync files */ + private int mFailsInFileSyncsFound; - private Map mForgottenLocalFiles; - + /** 'True' means that the remote folder changed and should be fetched */ + private boolean mRemoteFolderChanged; + + private List mFilesForDirectDownload; + // to avoid extra PROPFINDs when there was no change in the folder - public SynchronizeFolderOperation( String remotePath, - long currentSyncTime, - long parentId, - DataStorageManager dataStorageManager, - Account account, - Context context ) { + private List mFilesToSyncContentsWithoutUpload; + // this will go out when 'folder synchronization' replaces 'folder download'; step by step + + private List mFavouriteFilesToSyncContents; + // this will be used for every file when 'folder synchronization' replaces 'folder download' + + private final AtomicBoolean mCancellationRequested; + + /** + * Creates a new instance of {@link SynchronizeFolderOperation}. + * + * @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 SynchronizeFolderOperation(Context context, String remotePath, Account account, + long currentSyncTime){ mRemotePath = remotePath; mCurrentSyncTime = currentSyncTime; - mParentId = parentId; - mStorageManager = dataStorageManager; mAccount = account; mContext = context; - mForgottenLocalFiles = new HashMap(); + mRemoteFolderChanged = false; + mFilesForDirectDownload = new Vector(); + mFilesToSyncContentsWithoutUpload = new Vector(); + mFavouriteFilesToSyncContents = new Vector(); + mCancellationRequested = new AtomicBoolean(false); } - - + + public int getConflictsFound() { return mConflictsFound; } - - public int getFailsInFavouritesFound() { - return mFailsInFavouritesFound; - } - - public Map getForgottenLocalFiles() { - return mForgottenLocalFiles; + + public int getFailsInFileSyncsFound() { + return mFailsInFileSyncsFound; } - + /** - * 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. + * Performs the synchronization. + * + * {@inheritDoc} */ - public List getChildren() { - return mChildren; - } - - @Override - protected RemoteOperationResult run(WebdavClient client) { + protected RemoteOperationResult run(OwnCloudClient client) { RemoteOperationResult result = null; - mFailsInFavouritesFound = 0; + mFailsInFileSyncsFound = 0; mConflictsFound = 0; - mForgottenLocalFiles.clear(); - // code before in FileSyncAdapter.fetchData - PropFindMethod query = null; try { - Log_OC.d(TAG, "Synchronizing " + mAccount.name + ", fetching files in " + mRemotePath); + // get locally cached information about folder + mLocalFolder = getStorageManager().getFileByPath(mRemotePath); - // remote request - query = new PropFindMethod(client.getBaseUri() + WebdavUtils.encodePath(mRemotePath)); - 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 updatedFiles = new Vector(resp.getResponses().length - 1); - List filesToSyncContents = new Vector(); - 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()); - } - } + result = checkForChanges(client); + + if (result.isSuccess()) { + if (mRemoteFolderChanged) { + result = fetchAndSyncRemoteFolder(client); - /// 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); + } else { + prepareOpsFromLocalKnowledge(); } - - // 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 + if (result.isSuccess()) { + syncContents(client); } - - // 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); - } else { - i++; - } + } + + 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 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 updatedFiles = new Vector(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 localFiles = storageManager.getFolderContent(mLocalFolder/*, false*/); + Map localFilesMap = new HashMap(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 0 || mFailsInFavouritesFound > 0) { - result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT); // should be different result, but will do the job - - } else { - result = new RemoteOperationResult(true, status); - Header hCookie = query.getResponseHeader("Cookie"); - if (hCookie != null) { - Log_OC.e(TAG, "PROPFIND cookie: " + hCookie.getValue()); - } else { - Log_OC.e(TAG, "PROPFIND NO COOKIE"); + /// 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.keepInSync()) { + /// prepare content synchronization for kept-in-sync files + SynchronizeFileOperation operation = new SynchronizeFileOperation( + localFile, + remoteFile, + mAccount, + true, + mContext + ); + mFavouriteFilesToSyncContents.add(operation); + } else { - result = new RemoteOperationResult(false, status); + /// prepare limited synchronization for regular files + SynchronizeFileOperation operation = new SynchronizeFileOperation( + localFile, + remoteFile, + mAccount, + true, + false, + mContext + ); + mFilesToSyncContentsWithoutUpload.add(operation); } - - - - } 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, "Synchronizing " + mAccount.name + ", folder " + mRemotePath + ": " + result.getLogMessage()); + 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 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 { - if (result.isException()) { - Log_OC.e(TAG, "Synchronizing " + mAccount.name + ", folder " + mRemotePath + ": " + result.getLogMessage(), result.getException()); - } else { - Log_OC.e(TAG, "Synchronizing " + mAccount.name + ", folder " + mRemotePath + ": " + result.getLogMessage()); + /// prepare limited synchronization for regular files + if (!child.isDown()) { + mFilesForDirectDownload.add(child); } } } - - return result; } - - public boolean isMultiStatus(int status) { - return (status == HttpStatus.SC_MULTI_STATUS); + + 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 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 OCFile} object with the data read from the server. - * - * @param we WebDAV entry read from the server for a WebDAV resource (remote file or folder). + * 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(WebdavEntry we) { - OCFile file = new OCFile(we.decodedPath()); - file.setCreationTimestamp(we.createTimestamp()); - file.setFileLength(we.contentLength()); - file.setMimetype(we.contentType()); - file.setModificationTimestamp(we.modifiedTimestamp()); - file.setParentId(mParentId); + 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; } - + /** - * Checks the storage path of the OCFile received as parameter. If it's out of the local ownCloud folder, - * tries to copy the file inside it. - * - * 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. + * 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 checkAndFixForeignStoragePath(OCFile file) { - String storagePath = file.getStoragePath(); - String expectedPath = FileStorageUtils.getDefaultSavePathFor(mAccount.name, file); - if (storagePath != null && !storagePath.equals(expectedPath)) { - /// fix storagePaths out of the local ownCloud folder - File originalFile = new File(storagePath); - if (FileStorageUtils.getUsableSpace(mAccount.name) < originalFile.length()) { - mForgottenLocalFiles.put(file.getRemotePath(), storagePath); - file.setStoragePath(null); - - } else { - InputStream in = null; - OutputStream out = null; - try { - File expectedFile = new File(expectedPath); - File expectedParent = expectedFile.getParentFile(); - expectedParent.mkdirs(); - if (!expectedParent.isDirectory()) { - throw new IOException("Unexpected error: parent directory could not be created"); - } - expectedFile.createNewFile(); - if (!expectedFile.isFile()) { - throw new IOException("Unexpected error: target file could not be created"); - } - in = new FileInputStream(originalFile); - out = new FileOutputStream(expectedFile); - byte[] buf = new byte[1024]; - int len; - while ((len = in.read(buf)) > 0){ - out.write(buf, 0, len); - } - file.setStoragePath(expectedPath); - - } catch (Exception e) { - Log_OC.e(TAG, "Exception while copying foreign file " + expectedPath, e); - mForgottenLocalFiles.put(file.getRemotePath(), storagePath); - file.setStoragePath(null); - - } finally { - try { - if (in != null) in.close(); - } catch (Exception e) { - Log_OC.d(TAG, "Weird exception while closing input stream for " + storagePath + " (ignoring)", e); - } - try { - if (out != null) out.close(); - } catch (Exception e) { - Log_OC.d(TAG, "Weird exception while closing output stream for " + expectedPath + " (ignoring)", e); - } - } + 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_SYNC_FOLDER); + intent.putExtra(OperationsService.EXTRA_ACCOUNT, mAccount); + intent.putExtra(OperationsService.EXTRA_REMOTE_PATH, path); + mContext.startService(intent); + } + + public String getRemotePath() { + return mRemotePath; + } }