Merge branch 'develop' into refresh_folder_contents_when_browsed_into
authorDavid A. Velasco <dvelasco@solidgear.es>
Thu, 24 Oct 2013 14:31:56 +0000 (16:31 +0200)
committerDavid A. Velasco <dvelasco@solidgear.es>
Thu, 24 Oct 2013 14:31:56 +0000 (16:31 +0200)
1  2 
src/com/owncloud/android/operations/SynchronizeFolderOperation.java

@@@ -35,13 -35,11 +35,13 @@@ import org.apache.jackrabbit.webdav.cli
  
  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;
@@@ -50,81 -48,55 +50,81 @@@ 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;
      
      /** 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;
 +
 +    /** 'True' means that the remote folder changed from last synchronization and should be fetched */
 +    private boolean mRemoteFolderChanged;
      
      
 -    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 syncFullAccount,
 +                                        FileDataStorageManager dataStorageManager, 
                                          Account account, 
                                          Context context ) {
 -        mRemotePath = remotePath;
 +        mLocalFolder = folder;
          mCurrentSyncTime = currentSyncTime;
 -        mParentId = parentId;
 +        mSyncFullAccount = syncFullAccount;
          mStorageManager = dataStorageManager;
          mAccount = account;
          mContext = context;
          mForgottenLocalFiles = new HashMap<String, String>();
 +        mRemoteFolderChanged = false;
      }
      
      
      /**
       * 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;
          mConflictsFound = 0;
          mForgottenLocalFiles.clear();
          
 -        // code before in FileSyncAdapter.fetchData
 +        result = checkForChanges(client);
 +        
 +        if (result.isSuccess()) {
 +            if (mRemoteFolderChanged) {
 +                result = fetchAndSyncRemoteFolder(client);
 +            } else {
 +                mChildren = mStorageManager.getFolderContent(mLocalFolder);
 +            }
 +        }
 +        
 +        if (!mSyncFullAccount) {            
 +            sendStickyBroadcast(false, mLocalFolder.getRemotePath(), result);
 +        }
 +
 +        return result;
 +        
 +    }
 +
 +
 +    private RemoteOperationResult checkForChanges(WebdavClient client) {
 +        mRemoteFolderChanged = false;
 +        RemoteOperationResult result = null;
 +        String remotePath = null;
          PropFindMethod query = null;
 +        
          try {
 -            Log_OC.d(TAG, "Synchronizing " + mAccount.name + ", fetching files in " + mRemotePath);
 -            
 +            remotePath = mLocalFolder.getRemotePath();
 +            Log_OC.d(TAG, "Checking changes in " + mAccount.name + remotePath);
 +
              // remote request 
 -            query = new PropFindMethod(client.getBaseUri() + WebdavUtils.encodePath(mRemotePath),
 +            query = new PropFindMethod(client.getBaseUri() + WebdavUtils.encodePath(remotePath),
                      DavConstants.PROPFIND_ALL_PROP,
 -                    DavConstants.DEPTH_1);
 +                    DavConstants.DEPTH_0);
              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);
 -                    }
 +            // check and process response
 +            if (isMultiStatus(status)) {
 +                // parse data from remote folder 
 +                WebdavEntry we = new WebdavEntry(query.getResponseBodyAsMultiStatus().getResponses()[0], client.getBaseUri().getPath());
 +                OCFile remoteFolder = fillOCFile(we);
                  
 -                    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);
 +                // check if remote and local folder are different
 +                mRemoteFolderChanged = !(remoteFolder.getEtag().equalsIgnoreCase(mLocalFolder.getEtag()));
                  
 -                // 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);
 -                    } else {
 -                        i++;
 -                    }
 -                }
 +                result = new RemoteOperationResult(ResultCode.OK);
                  
              } else {
 +                // check failed
                  client.exhaustResponse(query.getResponseBodyAsStream());
 +                if (status == HttpStatus.SC_NOT_FOUND) {
 +                    removeLocalFolder();
 +                }
 +                result = new RemoteOperationResult(false, status, query.getResponseHeaders());
 +            }
 +
 +        } catch (Exception e) {
 +            result = new RemoteOperationResult(e);
 +            
 +
 +        } finally {
 +            if (query != null)
 +                query.releaseConnection();  // let the connection available for other methods
 +            if (result.isSuccess()) {
 +                Log_OC.i(TAG, "Checked " + mAccount.name + remotePath + " : " + (mRemoteFolderChanged ? "changed" : "not changed"));
 +            } else {
 +                if (result.isException()) {
 +                    Log_OC.e(TAG, "Checked " + mAccount.name + remotePath  + " : " + result.getLogMessage(), result.getException());
 +                } else {
 +                    Log_OC.e(TAG, "Checked " + mAccount.name + remotePath + " : " + result.getLogMessage());
 +                }
              }
              
 -            // prepare result object
 +        }
 +        return result;
 +    }
 +
 +
 +    private RemoteOperationResult fetchAndSyncRemoteFolder(WebdavClient client) {
 +        RemoteOperationResult result = null;
 +        String remotePath = null;
 +        PropFindMethod query = null;
 +        try {
 +            remotePath = mLocalFolder.getRemotePath();
 +            Log_OC.d(TAG, "Synchronizing " + mAccount.name + remotePath);
 +
 +            // remote request 
 +            query = new PropFindMethod(client.getBaseUri() + WebdavUtils.encodePath(remotePath),
 +                    DavConstants.PROPFIND_ALL_PROP,
 +                    DavConstants.DEPTH_1);
 +            int status = client.executeMethod(query);
 +
 +            // check and process response
              if (isMultiStatus(status)) {
 +                synchronizeData(query.getResponseBodyAsMultiStatus(), client);
                  if (mConflictsFound > 0  || mFailsInFavouritesFound > 0) { 
                      result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT);   // should be different result, but will do the job
 -                            
                  } else {
                      result = new RemoteOperationResult(true, status, query.getResponseHeaders());
                  }
 +                
              } else {
 +                // synchronization failed
 +                client.exhaustResponse(query.getResponseBodyAsStream());
 +                if (status == HttpStatus.SC_NOT_FOUND) {
 +                    removeLocalFolder();
 +                }
                  result = new RemoteOperationResult(false, status, query.getResponseHeaders());
              }
 -            
 -            
 -            
 +
          } catch (Exception e) {
              result = new RemoteOperationResult(e);
              
              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());
                  }
              }
 +            
          }
 -        
          return result;
      }
 -    
 +
 +
 +    private void removeLocalFolder() {
 +        if (mStorageManager.fileExists(mLocalFolder.getFileId())) {
 +            String currentSavePath = FileStorageUtils.getSavePath(mAccount.name);
 +            mStorageManager.removeFolder(mLocalFolder, true, (mLocalFolder.isDown() && mLocalFolder.getStoragePath().startsWith(currentSavePath)));
 +        }
 +    }
 +
 +
 +    /**
 +     *  Synchronizes the data retrieved from the server about the contents of the target folder 
 +     *  with the current data in the local database.
 +     *  
 +     *  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 void synchronizeData(MultiStatus dataInServer, WebdavClient client) {
 +        // get 'fresh data' from the database
 +        mLocalFolder = mStorageManager.getFileByPath(mLocalFolder.getRemotePath());
 +        
 +        // 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());
 +        
 +        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)
 +                if (remoteFile.isFolder()) {
 +                    remoteFile.setFileLength(localFile.getFileLength()); // TODO move operations about size of folders to FileContentProvider
 +                }
 +            } else {
 +                remoteFile.setEtag(""); // remote eTag will not be updated unless contents are synchronized (Synchronize[File|Folder]Operation with remoteFile as parameter)
 +            }
 +
 +            /// check and fix, if needed, local storage path
 +            checkAndFixForeignStoragePath(remoteFile);      // fixing old policy - now local files must be copied into the ownCloud local folder 
 +            searchForLocalFileInDefaultPath(remoteFile);    // legacy   
 +
 +            /// prepare content synchronization for kept-in-sync files
 +            if (remoteFile.keepInSync()) {
 +                SynchronizeFileOperation operation = new SynchronizeFileOperation(  localFile,        
 +                                                                                    remoteFile, 
 +                                                                                    mStorageManager,
 +                                                                                    mAccount,       
 +                                                                                    true, 
 +                                                                                    mContext
 +                                                                                    );
 +                filesToSyncContents.add(operation);
 +            }
 +            
 +            updatedFiles.add(remoteFile);
 +        }
 +
 +        // 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;
 +        
 +    }
 +
 +    /**
 +     * 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.getSyncMessage());
 +        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);
 +    }
 +
 +
 +    public boolean getRemoteFolderChanged() {
 +        return mRemoteFolderChanged;
 +    }
 +
  }