X-Git-Url: http://git.linex4red.de/pub/Android/ownCloud.git/blobdiff_plain/ebbc1793e23f67dc7aa2214a542fa2c037fcf4c2..11f6daf40390e5be8d602b7019a9e90f46d0713e:/src/com/owncloud/android/operations/SynchronizeFolderOperation.java diff --git a/src/com/owncloud/android/operations/SynchronizeFolderOperation.java b/src/com/owncloud/android/operations/SynchronizeFolderOperation.java index 11ce41e5..201f2e55 100644 --- a/src/com/owncloud/android/operations/SynchronizeFolderOperation.java +++ b/src/com/owncloud/android/operations/SynchronizeFolderOperation.java @@ -1,5 +1,5 @@ /* ownCloud Android client application - * Copyright (C) 2012-2013 ownCloud Inc. + * Copyright (C) 2012-2014 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, @@ -17,6 +17,26 @@ package com.owncloud.android.operations; +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.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.utils.FileStorageUtils; + +import org.apache.http.HttpStatus; + import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; @@ -29,28 +49,8 @@ import java.util.List; import java.util.Map; import java.util.Vector; -import org.apache.http.HttpStatus; -import android.accounts.Account; -import android.content.Context; -import android.content.Intent; //import android.support.v4.content.LocalBroadcastManager; -import com.owncloud.android.datamodel.FileDataStorageManager; -import com.owncloud.android.datamodel.OCFile; -import com.owncloud.android.lib.network.OwnCloudClient; -import com.owncloud.android.lib.operations.common.OCShare; -import com.owncloud.android.lib.operations.common.RemoteOperation; -import com.owncloud.android.lib.operations.common.RemoteOperationResult; -import com.owncloud.android.lib.operations.common.RemoteOperationResult.ResultCode; -import com.owncloud.android.lib.operations.remote.GetSharesForFileRemoteOperation; -import com.owncloud.android.lib.operations.remote.ReadRemoteFileOperation; -import com.owncloud.android.lib.operations.remote.ReadRemoteFolderOperation; -import com.owncloud.android.lib.operations.common.RemoteFile; -import com.owncloud.android.syncadapter.FileSyncAdapter; -import com.owncloud.android.utils.FileStorageUtils; -import com.owncloud.android.utils.Log_OC; - - /** * Remote operation performing the synchronization of the list of files contained @@ -63,28 +63,22 @@ import com.owncloud.android.utils.Log_OC; * * @author David A. Velasco */ -public class SynchronizeFolderOperation extends RemoteOperation { +public class SynchronizeFolderOperation extends SyncOperation { private static final String TAG = SynchronizeFolderOperation.class.getSimpleName(); - public static final String EVENT_SINGLE_FOLDER_CONTENTS_SYNCED = SynchronizeFolderOperation.class.getName() + ".EVENT_SINGLE_FOLDER_CONTENTS_SYNCED"; - public static final String EVENT_SINGLE_FOLDER_SHARES_SYNCED = SynchronizeFolderOperation.class.getName() + ".EVENT_SINGLE_FOLDER_SHARES_SYNCED"; - /** Time stamp for the synchronization process in progress */ private long mCurrentSyncTime; - + /** Remote folder to synchronize */ private OCFile mLocalFolder; - - /** Access to the local database */ - private FileDataStorageManager mStorageManager; - + /** Account where the file to synchronize belongs */ private Account mAccount; - + /** Android context; necessary to send requests to the download service */ private Context mContext; - + /** Files and folders contained in the synchronized folder after a successful operation */ private List mChildren; @@ -94,74 +88,59 @@ public class SynchronizeFolderOperation extends RemoteOperation { /** 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 */ + /** + * 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 mForgottenLocalFiles; - /** 'True' means that this operation is part of a full account synchronization */ - private boolean mSyncFullAccount; - - /** 'True' means that Share resources bound to the files into the folder should be refreshed also */ - private boolean mRefreshShares; - - /** 'True' means that the remote folder changed from last synchronization and should be fetched */ + /** 'True' means that the remote folder changed and should be fetched */ private boolean mRemoteFolderChanged; - + /** * 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. + * @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( OCFile folder, - long currentSyncTime, - boolean syncFullAccount, - boolean refreshShares, - FileDataStorageManager dataStorageManager, - Account account, - Context context ) { - mLocalFolder = folder; + public SynchronizeFolderOperation(Context context, String remotePath, Account account, long currentSyncTime){ + mLocalFolder = new OCFile(remotePath); mCurrentSyncTime = currentSyncTime; - mSyncFullAccount = syncFullAccount; - mRefreshShares = refreshShares; - mStorageManager = dataStorageManager; mAccount = account; mContext = context; mForgottenLocalFiles = new HashMap(); mRemoteFolderChanged = false; } - - + + public int getConflictsFound() { return mConflictsFound; } - + public int getFailsInFavouritesFound() { return mFailsInFavouritesFound; } - + public Map getForgottenLocalFiles() { return mForgottenLocalFiles; } - + /** - * Returns the list of files and folders contained in the synchronized folder, if called after synchronization is complete. - * + * 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. */ public List getChildren() { return mChildren; } - + /** * Performs the synchronization. - * + * * {@inheritDoc} */ @Override @@ -170,69 +149,58 @@ public class SynchronizeFolderOperation extends RemoteOperation { mFailsInFavouritesFound = 0; mConflictsFound = 0; mForgottenLocalFiles.clear(); - + result = checkForChanges(client); - + if (result.isSuccess()) { if (mRemoteFolderChanged) { result = fetchAndSyncRemoteFolder(client); } else { - mChildren = mStorageManager.getFolderContent(mLocalFolder); + mChildren = getStorageManager().getFolderContent(mLocalFolder); } } - - if (!mSyncFullAccount) { - sendLocalBroadcast(EVENT_SINGLE_FOLDER_CONTENTS_SYNCED, mLocalFolder.getRemotePath(), result); - } - - if (result.isSuccess() && mRefreshShares) { - RemoteOperationResult shareResult = refreshSharesForFolder(client); - if (shareResult.getCode() != ResultCode.FILE_NOT_FOUND) { - result = shareResult; - } // else , keep the previous result ; being conservative for servers where Sharing API is supported, but disabled - } - - if (!mSyncFullAccount) { - sendLocalBroadcast(EVENT_SINGLE_FOLDER_SHARES_SYNCED, mLocalFolder.getRemotePath(), result); - } - + return result; - + } private RemoteOperationResult checkForChanges(OwnCloudClient client) { - mRemoteFolderChanged = false; + mRemoteFolderChanged = true; RemoteOperationResult result = null; String remotePath = null; remotePath = mLocalFolder.getRemotePath(); Log_OC.d(TAG, "Checking changes in " + mAccount.name + remotePath); - - // remote request + + // remote request ReadRemoteFileOperation operation = new ReadRemoteFileOperation(remotePath); 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())); - + mRemoteFolderChanged = + !(remoteFolder.getEtag().equalsIgnoreCase(mLocalFolder.getEtag())); + result = new RemoteOperationResult(ResultCode.OK); - - Log_OC.i(TAG, "Checked " + mAccount.name + remotePath + " : " + (mRemoteFolderChanged ? "changed" : "not changed")); - + + Log_OC.i(TAG, "Checked " + mAccount.name + remotePath + " : " + + (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 + remotePath + " : " + result.getLogMessage(), result.getException()); + Log_OC.e(TAG, "Checked " + mAccount.name + remotePath + " : " + + result.getLogMessage(), result.getException()); } else { - Log_OC.e(TAG, "Checked " + mAccount.name + remotePath + " : " + result.getLogMessage()); + Log_OC.e(TAG, "Checked " + mAccount.name + remotePath + " : " + + result.getLogMessage()); } } - + return result; } @@ -242,62 +210,73 @@ public class SynchronizeFolderOperation extends RemoteOperation { ReadRemoteFolderOperation operation = new ReadRemoteFolderOperation(remotePath); RemoteOperationResult result = operation.execute(client); Log_OC.d(TAG, "Synchronizing " + mAccount.name + remotePath); - + if (result.isSuccess()) { synchronizeData(result.getData(), client); - if (mConflictsFound > 0 || mFailsInFavouritesFound > 0) { - result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT); // should be different result, but will do the job + if (mConflictsFound > 0 || mFailsInFavouritesFound > 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() { - if (mStorageManager.fileExists(mLocalFolder.getFileId())) { + FileDataStorageManager storageManager = getStorageManager(); + if (storageManager.fileExists(mLocalFolder.getFileId())) { String currentSavePath = FileStorageUtils.getSavePath(mAccount.name); - mStorageManager.removeFolder(mLocalFolder, true, (mLocalFolder.isDown() && mLocalFolder.getStoragePath().startsWith(currentSavePath))); + storageManager.removeFolder( + mLocalFolder, + true, + ( mLocalFolder.isDown() && + mLocalFolder.getStoragePath().startsWith(currentSavePath) + ) + ); } } /** - * Synchronizes the data retrieved from the server about the contents of the target folder + * 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. + * + * @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) { - // get 'fresh data' from the database - mLocalFolder = mStorageManager.getFileByPath(mLocalFolder.getRemotePath()); + FileDataStorageManager storageManager = getStorageManager(); - // parse data from remote folder + // get 'fresh data' from the database + mLocalFolder = storageManager.getFileByPath(mLocalFolder.getRemotePath()); + + // 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 "); - + + Log_OC.d(TAG, "Remote folder " + mLocalFolder.getRemotePath() + + " changed - starting update of local data "); + List updatedFiles = new Vector(folderAndFiles.size() - 1); List filesToSyncContents = new Vector(); // get current data about local contents of the folder to synchronize - List localFiles = mStorageManager.getFolderContent(mLocalFolder); + List localFiles = storageManager.getFolderContent(mLocalFolder); Map localFilesMap = new HashMap(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 filesToSyncContents, OwnCloudClient client) { + private void startContentSynchronizations( + List filesToSyncContents, OwnCloudClient client + ) { RemoteOperationResult contentsResult = null; for (SynchronizeFileOperation op: filesToSyncContents) { - contentsResult = op.execute(client); // returns without waiting for upload or download finishes + contentsResult = op.execute(getStorageManager(), mContext); // async 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()); + Log_OC.e(TAG, "Error while synchronizing favourites : " + + contentsResult.getLogMessage(), contentsResult.getException()); } else { - Log_OC.e(TAG, "Error while synchronizing favourites : " + contentsResult.getLogMessage()); + Log_OC.e(TAG, "Error while synchronizing favourites : " + + contentsResult.getLogMessage()); } } } // won't let these fails break the synchronization process @@ -384,12 +395,12 @@ public class SynchronizeFolderOperation extends RemoteOperation { public boolean isMultiStatus(int status) { - return (status == HttpStatus.SC_MULTI_STATUS); + return (status == HttpStatus.SC_MULTI_STATUS); } /** - * Creates and populates a new {@link OCFile} object with the data read from the server. - * + * 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. */ @@ -400,17 +411,19 @@ public class SynchronizeFolderOperation extends RemoteOperation { 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} - *) + * 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. */ private void checkAndFixForeignStoragePath(OCFile file) { @@ -422,7 +435,7 @@ public class SynchronizeFolderOperation extends RemoteOperation { if (FileStorageUtils.getUsableSpace(mAccount.name) < originalFile.length()) { mForgottenLocalFiles.put(file.getRemotePath(), storagePath); file.setStoragePath(null); - + } else { InputStream in = null; OutputStream out = null; @@ -431,12 +444,14 @@ public class SynchronizeFolderOperation extends RemoteOperation { File expectedParent = expectedFile.getParentFile(); expectedParent.mkdirs(); if (!expectedParent.isDirectory()) { - throw new IOException("Unexpected error: parent directory could not be created"); + 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]; @@ -445,52 +460,34 @@ public class SynchronizeFolderOperation extends RemoteOperation { 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); + 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); + Log_OC.d(TAG, "Weird exception while closing output stream for " + + expectedPath + " (ignoring)", e); } } } } } - - - private RemoteOperationResult refreshSharesForFolder(OwnCloudClient client) { - RemoteOperationResult result = null; - - // remote request - GetSharesForFileRemoteOperation operation = new GetSharesForFileRemoteOperation(mLocalFolder.getRemotePath(), false, true); - result = operation.execute(client); - - if (result.isSuccess()) { - // update local database - ArrayList shares = new ArrayList(); - for(Object obj: result.getData()) { - shares.add((OCShare) obj); - } - mStorageManager.saveSharesInFolder(shares, mLocalFolder); - } - return result; - } - /** * 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 + * 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. @@ -505,27 +502,18 @@ public class SynchronizeFolderOperation extends RemoteOperation { } } - /** - * Sends a message to any application component interested in the progress of the synchronization. - * - * @param event - * @param dirRemotePath Remote path of a folder that was just synchronized (with or without success) - * @param result + * Requests for a download to the FileDownloader service + * + * @param file OCFile object representing the file to download */ - private void sendLocalBroadcast(String event, String dirRemotePath, RemoteOperationResult result) { - Log_OC.d(TAG, "Send broadcast " + event); - Intent intent = new Intent(event); - intent.putExtra(FileSyncAdapter.EXTRA_ACCOUNT_NAME, mAccount.name); - if (dirRemotePath != null) { - intent.putExtra(FileSyncAdapter.EXTRA_FOLDER_PATH, dirRemotePath); - } - intent.putExtra(FileSyncAdapter.EXTRA_RESULT, result); - mContext.sendStickyBroadcast(intent); - //LocalBroadcastManager.getInstance(mContext).sendBroadcast(intent); + private void requestForDownloadFile(OCFile file) { + Intent i = new Intent(mContext, FileDownloader.class); + i.putExtra(FileDownloader.EXTRA_ACCOUNT, mAccount); + i.putExtra(FileDownloader.EXTRA_FILE, file); + mContext.startService(i); } - public boolean getRemoteFolderChanged() { return mRemoteFolderChanged; }