mirror of
https://github.com/nextcloud/android.git
synced 2024-11-23 21:55:48 +03:00
Clean up of synchronization code
This commit is contained in:
parent
8a27bf186a
commit
23c9be24a2
7 changed files with 387 additions and 308 deletions
|
@ -126,7 +126,7 @@ public class FileDataStorageManager implements DataStorageManager {
|
||||||
cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, file.getFileLength());
|
cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, file.getFileLength());
|
||||||
cv.put(ProviderTableMeta.FILE_CONTENT_TYPE, file.getMimetype());
|
cv.put(ProviderTableMeta.FILE_CONTENT_TYPE, file.getMimetype());
|
||||||
cv.put(ProviderTableMeta.FILE_NAME, file.getFileName());
|
cv.put(ProviderTableMeta.FILE_NAME, file.getFileName());
|
||||||
if (file.getParentId() != DataStorageManager.ROOT_PARENT_ID)
|
//if (file.getParentId() != DataStorageManager.ROOT_PARENT_ID)
|
||||||
cv.put(ProviderTableMeta.FILE_PARENT, file.getParentId());
|
cv.put(ProviderTableMeta.FILE_PARENT, file.getParentId());
|
||||||
cv.put(ProviderTableMeta.FILE_PATH, file.getRemotePath());
|
cv.put(ProviderTableMeta.FILE_PATH, file.getRemotePath());
|
||||||
if (!file.isDirectory())
|
if (!file.isDirectory())
|
||||||
|
|
|
@ -23,7 +23,6 @@ import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -50,7 +49,13 @@ 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
|
||||||
*/
|
*/
|
||||||
|
@ -58,17 +63,15 @@ public class SynchronizeFolderOperation extends RemoteOperation {
|
||||||
|
|
||||||
private static final String TAG = SynchronizeFolderOperation.class.getSimpleName();
|
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;
|
private long mCurrentSyncTime;
|
||||||
|
|
||||||
/** Id of the folder to synchronize in the local database */
|
/** Remote folder to synchronize */
|
||||||
private long mParentId;
|
private OCFile mLocalFolder;
|
||||||
|
|
||||||
/** Boolean to indicate if is mandatory to update the folder */
|
/** 'True' means that the properties of the folder should be updated also, not just its content */
|
||||||
private boolean mEnforceMetadataUpdate;
|
private boolean mUpdateFolderProperties;
|
||||||
|
|
||||||
/** Access to the local database */
|
/** Access to the local database */
|
||||||
private DataStorageManager mStorageManager;
|
private DataStorageManager mStorageManager;
|
||||||
|
@ -76,33 +79,47 @@ public class SynchronizeFolderOperation extends RemoteOperation {
|
||||||
/** Account where the file to synchronize belongs */
|
/** Account where the file to synchronize belongs */
|
||||||
private Account mAccount;
|
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;
|
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;
|
private List<OCFile> mChildren;
|
||||||
|
|
||||||
|
/** Counter of conflicts found between local and remote files */
|
||||||
private int mConflictsFound;
|
private int mConflictsFound;
|
||||||
|
|
||||||
|
/** Counter of failed operations in synchronization of kept-in-sync files */
|
||||||
private int mFailsInFavouritesFound;
|
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;
|
private Map<String, String> mForgottenLocalFiles;
|
||||||
|
|
||||||
|
/** 'True' means that this operation is part of a full account synchronization */
|
||||||
private boolean mSyncFullAccount;
|
private boolean mSyncFullAccount;
|
||||||
|
|
||||||
|
|
||||||
public SynchronizeFolderOperation( String remotePath,
|
/**
|
||||||
|
* Creates a new instance of {@link SynchronizeFolderOperation}.
|
||||||
|
*
|
||||||
|
* @param remoteFolderPath Remote folder to synchronize.
|
||||||
|
* @param currentSyncTime Time stamp for the synchronization process in progress.
|
||||||
|
* @param localFolderId Identifier in the local database of the folder to synchronize.
|
||||||
|
* @param updateFolderProperties 'True' means that the properties of the folder should be updated also, not just its content.
|
||||||
|
* @param syncFullAccount 'True' means that this operation is part of a full account synchronization.
|
||||||
|
* @param dataStorageManager Interface with the local database.
|
||||||
|
* @param account ownCloud account where the folder is located.
|
||||||
|
* @param context Application context.
|
||||||
|
*/
|
||||||
|
public SynchronizeFolderOperation( OCFile folder,
|
||||||
long currentSyncTime,
|
long currentSyncTime,
|
||||||
long parentId,
|
boolean updateFolderProperties,
|
||||||
boolean enforceMetadataUpdate,
|
|
||||||
boolean syncFullAccount,
|
boolean syncFullAccount,
|
||||||
DataStorageManager dataStorageManager,
|
DataStorageManager dataStorageManager,
|
||||||
Account account,
|
Account account,
|
||||||
Context context ) {
|
Context context ) {
|
||||||
mRemotePath = remotePath;
|
mLocalFolder = folder;
|
||||||
mCurrentSyncTime = currentSyncTime;
|
mCurrentSyncTime = currentSyncTime;
|
||||||
mParentId = parentId;
|
mUpdateFolderProperties = updateFolderProperties;
|
||||||
mEnforceMetadataUpdate = enforceMetadataUpdate;
|
|
||||||
mSyncFullAccount = syncFullAccount;
|
mSyncFullAccount = syncFullAccount;
|
||||||
mStorageManager = dataStorageManager;
|
mStorageManager = dataStorageManager;
|
||||||
mAccount = account;
|
mAccount = account;
|
||||||
|
@ -132,99 +149,147 @@ public class SynchronizeFolderOperation extends RemoteOperation {
|
||||||
return mChildren;
|
return mChildren;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getRemotePath() {
|
/**
|
||||||
return mRemotePath;
|
* Performs the synchronization.
|
||||||
}
|
*
|
||||||
|
* {@inheritDoc}
|
||||||
public long getParentId() {
|
*/
|
||||||
return mParentId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected RemoteOperationResult run(WebdavClient client) {
|
protected RemoteOperationResult run(WebdavClient client) {
|
||||||
RemoteOperationResult result = null;
|
RemoteOperationResult result = null;
|
||||||
mFailsInFavouritesFound = 0;
|
mFailsInFavouritesFound = 0;
|
||||||
mConflictsFound = 0;
|
mConflictsFound = 0;
|
||||||
mForgottenLocalFiles.clear();
|
mForgottenLocalFiles.clear();
|
||||||
boolean dirChanged = false;
|
String remotePath = null;
|
||||||
|
|
||||||
// code before in FileSyncAdapter.fetchData
|
|
||||||
PropFindMethod query = null;
|
PropFindMethod query = null;
|
||||||
try {
|
try {
|
||||||
Log_OC.d(TAG, "Synchronizing " + mAccount.name + ", fetching files in " + mRemotePath);
|
remotePath = mLocalFolder.getRemotePath();
|
||||||
|
Log_OC.d(TAG, "Synchronizing " + mAccount.name + remotePath);
|
||||||
|
|
||||||
// remote request
|
// remote request
|
||||||
query = new PropFindMethod(client.getBaseUri() + WebdavUtils.encodePath(mRemotePath));
|
query = new PropFindMethod(client.getBaseUri() + WebdavUtils.encodePath(remotePath));
|
||||||
int status = client.executeMethod(query);
|
int status = client.executeMethod(query);
|
||||||
|
|
||||||
// check and process response - /// TODO take into account all the possible status per child-resource
|
// check and process response
|
||||||
if (isMultiStatus(status)) {
|
if (isMultiStatus(status)) {
|
||||||
MultiStatus resp = query.getResponseBodyAsMultiStatus();
|
boolean folderChanged = synchronizeData(query.getResponseBodyAsMultiStatus(), client);
|
||||||
|
if (folderChanged) {
|
||||||
// synchronize properties of the parent folder, if necessary
|
if (mConflictsFound > 0 || mFailsInFavouritesFound > 0) {
|
||||||
WebdavEntry we = new WebdavEntry(resp.getResponses()[0], client.getBaseUri().getPath());
|
result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT); // should be different result, but will do the job
|
||||||
|
} else {
|
||||||
// Properties of server folder
|
result = new RemoteOperationResult(true, status, query.getResponseHeaders());
|
||||||
OCFile parent = fillOCFile(we);
|
|
||||||
// Properties of local folder
|
|
||||||
OCFile localParent = mStorageManager.getFileByPath(mRemotePath);
|
|
||||||
if (localParent == null || !(parent.getEtag().equalsIgnoreCase(localParent.getEtag())) || mEnforceMetadataUpdate) {
|
|
||||||
if (localParent != null) {
|
|
||||||
parent.setParentId(localParent.getParentId());
|
|
||||||
}
|
}
|
||||||
mStorageManager.saveFile(parent);
|
} else {
|
||||||
if (mParentId == DataStorageManager.ROOT_PARENT_ID)
|
result = new RemoteOperationResult(ResultCode.OK_NO_CHANGES_ON_DIR);
|
||||||
mParentId = parent.getFileId();
|
|
||||||
dirChanged = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dirChanged) {
|
} else {
|
||||||
// read contents in folder
|
// synchronization failed
|
||||||
List<String> filesOnServer = new ArrayList<String> (); // Contains the lists of files on server
|
client.exhaustResponse(query.getResponseBodyAsStream());
|
||||||
List<OCFile> updatedFiles = new Vector<OCFile>(resp.getResponses().length - 1);
|
if (status == HttpStatus.SC_NOT_FOUND) {
|
||||||
|
if (mStorageManager.fileExists(mLocalFolder.getFileId())) {
|
||||||
|
String currentSavePath = FileStorageUtils.getSavePath(mAccount.name);
|
||||||
|
mStorageManager.removeDirectory(mLocalFolder, true, (mLocalFolder.isDown() && mLocalFolder.getStoragePath().startsWith(currentSavePath)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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, "Synchronized " + mAccount.name + remotePath + ": " + result.getLogMessage());
|
||||||
|
} else {
|
||||||
|
if (result.isException()) {
|
||||||
|
Log_OC.e(TAG, "Synchroned " + mAccount.name + remotePath + ": " + result.getLogMessage(), result.getException());
|
||||||
|
} else {
|
||||||
|
Log_OC.e(TAG, "Synchroned " + mAccount.name + remotePath + ": " + result.getLogMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mSyncFullAccount) {
|
||||||
|
sendStickyBroadcast(false, remotePath, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Synchronizes the data retrieved from the server about the contents of the target folder
|
||||||
|
* with the current data in the local database.
|
||||||
|
*
|
||||||
|
* Grants that mChildren is updated with fresh data after execution.
|
||||||
|
*
|
||||||
|
* @param dataInServer Full response got from the server with the data of the target
|
||||||
|
* folder and its direct children.
|
||||||
|
* @param client Client instance to the remote server where the data were
|
||||||
|
* retrieved.
|
||||||
|
* @return 'True' when any change was made in the local data, 'false' otherwise.
|
||||||
|
*/
|
||||||
|
private boolean synchronizeData(MultiStatus dataInServer, WebdavClient client) {
|
||||||
|
// get 'fresh data' from the database
|
||||||
|
mLocalFolder = mStorageManager.getFileById(mLocalFolder.getFileId());
|
||||||
|
|
||||||
|
// parse data from remote folder
|
||||||
|
WebdavEntry we = new WebdavEntry(dataInServer.getResponses()[0], client.getBaseUri().getPath());
|
||||||
|
OCFile remoteFolder = fillOCFile(we);
|
||||||
|
remoteFolder.setParentId(mLocalFolder.getParentId());
|
||||||
|
remoteFolder.setFileId(mLocalFolder.getFileId());
|
||||||
|
|
||||||
|
// check if remote and local folder are different
|
||||||
|
boolean folderChanged = !(remoteFolder.getEtag().equalsIgnoreCase(mLocalFolder.getEtag()));
|
||||||
|
|
||||||
|
if (!folderChanged) {
|
||||||
|
if (mUpdateFolderProperties) { // TODO check if this is really necessary
|
||||||
|
mStorageManager.saveFile(remoteFolder);
|
||||||
|
}
|
||||||
|
|
||||||
|
mChildren = mStorageManager.getDirectoryContent(mLocalFolder);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// read info of folder contents
|
||||||
|
List<OCFile> updatedFiles = new Vector<OCFile>(dataInServer.getResponses().length - 1);
|
||||||
List<SynchronizeFileOperation> filesToSyncContents = new Vector<SynchronizeFileOperation>();
|
List<SynchronizeFileOperation> filesToSyncContents = new Vector<SynchronizeFileOperation>();
|
||||||
for (int i = 1; i < resp.getResponses().length; ++i) {
|
|
||||||
|
// 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
|
/// new OCFile instance with the data from the server
|
||||||
we = new WebdavEntry(resp.getResponses()[i], client.getBaseUri().getPath());
|
we = new WebdavEntry(dataInServer.getResponses()[i], client.getBaseUri().getPath());
|
||||||
OCFile file = fillOCFile(we);
|
remoteFile = fillOCFile(we);
|
||||||
|
remoteFile.setParentId(mLocalFolder.getFileId());
|
||||||
|
|
||||||
filesOnServer.add(file.getRemotePath()); // Registry the file in the list
|
/// retrieve local data for the read file
|
||||||
|
localFile = mStorageManager.getFileByPath(remoteFile.getRemotePath());
|
||||||
|
|
||||||
/// set data about local state, keeping unchanged former data if existing
|
/// add to the remoteFile (the new one) data about LOCAL STATE (not existing in the server side)
|
||||||
file.setLastSyncDateForProperties(mCurrentSyncTime);
|
remoteFile.setLastSyncDateForProperties(mCurrentSyncTime);
|
||||||
OCFile oldFile = mStorageManager.getFileByPath(file.getRemotePath());
|
if (localFile != null) {
|
||||||
|
// properties of local state are kept unmodified
|
||||||
// Check if it is needed to synchronize the folder
|
remoteFile.setKeepInSync(localFile.keepInSync());
|
||||||
if (oldFile != null) {
|
remoteFile.setLastSyncDateForData(localFile.getLastSyncDateForData());
|
||||||
if (!file.getEtag().equalsIgnoreCase(oldFile.getEtag())) {
|
remoteFile.setModificationTimestampAtLastSyncForData(localFile.getModificationTimestampAtLastSyncForData());
|
||||||
}
|
remoteFile.setStoragePath(localFile.getStoragePath());
|
||||||
|
remoteFile.setEtag(localFile.getEtag()); // eTag will not be updated unless contents are synchronized (Synchronize[File|Folder]Operation with remoteFile as parameter)
|
||||||
|
} else {
|
||||||
|
remoteFile.setEtag(""); // remote eTag will not be updated unless contents are synchronized (Synchronize[File|Folder]Operation with remoteFile as parameter)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oldFile != null) {
|
/// check and fix, if need, local storage path
|
||||||
file.setKeepInSync(oldFile.keepInSync());
|
checkAndFixForeignStoragePath(remoteFile); // fixing old policy - now local files must be copied into the ownCloud local folder
|
||||||
file.setLastSyncDateForData(oldFile.getLastSyncDateForData());
|
searchForLocalFileInDefaultPath(remoteFile); // legacy
|
||||||
file.setModificationTimestampAtLastSyncForData(oldFile.getModificationTimestampAtLastSyncForData()); // must be kept unchanged when the file contents are not updated
|
|
||||||
checkAndFixForeignStoragePath(oldFile);
|
|
||||||
file.setStoragePath(oldFile.getStoragePath());
|
|
||||||
if (file.isDirectory())
|
|
||||||
file.setEtag(oldFile.getEtag());
|
|
||||||
} else
|
|
||||||
if (file.isDirectory())
|
|
||||||
file.setEtag("");
|
|
||||||
|
|
||||||
/// 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
|
/// prepare content synchronization for kept-in-sync files
|
||||||
if (file.keepInSync()) {
|
if (remoteFile.keepInSync()) {
|
||||||
SynchronizeFileOperation operation = new SynchronizeFileOperation( oldFile,
|
SynchronizeFileOperation operation = new SynchronizeFileOperation( localFile,
|
||||||
file,
|
remoteFile,
|
||||||
mStorageManager,
|
mStorageManager,
|
||||||
mAccount,
|
mAccount,
|
||||||
true,
|
true,
|
||||||
|
@ -234,13 +299,58 @@ public class SynchronizeFolderOperation extends RemoteOperation {
|
||||||
filesToSyncContents.add(operation);
|
filesToSyncContents.add(operation);
|
||||||
}
|
}
|
||||||
|
|
||||||
updatedFiles.add(file);
|
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)
|
// 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);
|
mStorageManager.saveFiles(updatedFiles);
|
||||||
|
|
||||||
// request for the synchronization of files AFTER saving last properties
|
// 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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return folderChanged;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes obsolete children in the folder after saving all the new data.
|
||||||
|
*/
|
||||||
|
private void removeObsoleteFiles() {
|
||||||
|
mChildren = mStorageManager.getDirectoryContent(mLocalFolder);
|
||||||
|
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++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
RemoteOperationResult contentsResult = null;
|
||||||
for (SynchronizeFileOperation op: filesToSyncContents) {
|
for (SynchronizeFileOperation op: filesToSyncContents) {
|
||||||
contentsResult = op.execute(client); // returns without waiting for upload or download finishes
|
contentsResult = op.execute(client); // returns without waiting for upload or download finishes
|
||||||
|
@ -257,75 +367,6 @@ public class SynchronizeFolderOperation extends RemoteOperation {
|
||||||
}
|
}
|
||||||
} // won't let these fails break the synchronization process
|
} // 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++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
client.exhaustResponse(query.getResponseBodyAsStream());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// prepare result object
|
|
||||||
if (!dirChanged) {
|
|
||||||
result = new RemoteOperationResult(ResultCode.OK_NO_CHANGES_ON_DIR);
|
|
||||||
mChildren = mStorageManager.getDirectoryContent(mStorageManager.getFileById(mParentId));
|
|
||||||
|
|
||||||
} else {
|
|
||||||
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 {
|
|
||||||
if (status == HttpStatus.SC_NOT_FOUND) {
|
|
||||||
OCFile dir = mStorageManager.getFileByPath(mRemotePath);
|
|
||||||
if (dir != null) {
|
|
||||||
String currentSavePath = FileStorageUtils.getSavePath(mAccount.name);
|
|
||||||
mStorageManager.removeDirectory(dir, true, (dir.isDown() && dir.getStoragePath().startsWith(currentSavePath)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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, "Synchroned " + mAccount.name + ", folder " + mRemotePath + ": " + result.getLogMessage());
|
|
||||||
} else {
|
|
||||||
if (result.isException()) {
|
|
||||||
Log_OC.e(TAG, "Synchroned " + mAccount.name + ", folder " + mRemotePath + ": " + result.getLogMessage(), result.getException());
|
|
||||||
} else {
|
|
||||||
Log_OC.e(TAG, "Synchroned " + mAccount.name + ", folder " + mRemotePath + ": " + result.getLogMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mSyncFullAccount) {
|
|
||||||
sendStickyBroadcast(false, mRemotePath, result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -346,7 +387,6 @@ public class SynchronizeFolderOperation extends RemoteOperation {
|
||||||
file.setFileLength(we.contentLength());
|
file.setFileLength(we.contentLength());
|
||||||
file.setMimetype(we.contentType());
|
file.setMimetype(we.contentType());
|
||||||
file.setModificationTimestamp(we.modifiedTimestamp());
|
file.setModificationTimestamp(we.modifiedTimestamp());
|
||||||
file.setParentId(mParentId);
|
|
||||||
file.setEtag(we.etag());
|
file.setEtag(we.etag());
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
@ -415,6 +455,25 @@ public class SynchronizeFolderOperation extends RemoteOperation {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.isDirectory()) {
|
||||||
|
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.
|
* Sends a message to any application component interested in the progress of the synchronization.
|
||||||
*
|
*
|
||||||
|
|
|
@ -19,14 +19,14 @@
|
||||||
package com.owncloud.android.syncadapter;
|
package com.owncloud.android.syncadapter;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.UnknownHostException;
|
//import java.net.UnknownHostException;
|
||||||
import java.util.Date;
|
//import java.util.Date;
|
||||||
|
|
||||||
import org.apache.http.HttpRequest;
|
import org.apache.http.HttpRequest;
|
||||||
import org.apache.http.HttpResponse;
|
import org.apache.http.HttpResponse;
|
||||||
import org.apache.http.client.ClientProtocolException;
|
import org.apache.http.client.ClientProtocolException;
|
||||||
import org.apache.http.conn.ConnectionKeepAliveStrategy;
|
//import org.apache.http.conn.ConnectionKeepAliveStrategy;
|
||||||
import org.apache.http.protocol.HttpContext;
|
//import org.apache.http.protocol.HttpContext;
|
||||||
|
|
||||||
import com.owncloud.android.authentication.AccountUtils;
|
import com.owncloud.android.authentication.AccountUtils;
|
||||||
import com.owncloud.android.authentication.AccountUtils.AccountNotFoundException;
|
import com.owncloud.android.authentication.AccountUtils.AccountNotFoundException;
|
||||||
|
@ -43,11 +43,13 @@ import android.content.Context;
|
||||||
import eu.alefzero.webdav.WebdavClient;
|
import eu.alefzero.webdav.WebdavClient;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base SyncAdapter for OwnCloud Designed to be subclassed for the concrete
|
* Base synchronization adapter for ownCloud designed to be subclassed for different
|
||||||
* SyncAdapter, like ConcatsSync, CalendarSync, FileSync etc..
|
* resource types, like FileSync, ConcatsSync, CalendarSync, etc..
|
||||||
|
*
|
||||||
|
* Implements the standard {@link AbstractThreadedSyncAdapter}.
|
||||||
*
|
*
|
||||||
* @author sassman
|
* @author sassman
|
||||||
*
|
* @author David A. Velasco
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractOwnCloudSyncAdapter extends
|
public abstract class AbstractOwnCloudSyncAdapter extends
|
||||||
AbstractThreadedSyncAdapter {
|
AbstractThreadedSyncAdapter {
|
||||||
|
@ -55,7 +57,7 @@ public abstract class AbstractOwnCloudSyncAdapter extends
|
||||||
private AccountManager accountManager;
|
private AccountManager accountManager;
|
||||||
private Account account;
|
private Account account;
|
||||||
private ContentProviderClient contentProvider;
|
private ContentProviderClient contentProvider;
|
||||||
private Date lastUpdated;
|
//private Date lastUpdated;
|
||||||
private DataStorageManager mStoreManager;
|
private DataStorageManager mStoreManager;
|
||||||
|
|
||||||
private WebdavClient mClient = null;
|
private WebdavClient mClient = null;
|
||||||
|
@ -89,14 +91,6 @@ public abstract class AbstractOwnCloudSyncAdapter extends
|
||||||
this.contentProvider = contentProvider;
|
this.contentProvider = contentProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Date getLastUpdated() {
|
|
||||||
return lastUpdated;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setLastUpdated(Date lastUpdated) {
|
|
||||||
this.lastUpdated = lastUpdated;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setStorageManager(DataStorageManager storage_manager) {
|
public void setStorageManager(DataStorageManager storage_manager) {
|
||||||
mStoreManager = storage_manager;
|
mStoreManager = storage_manager;
|
||||||
}
|
}
|
||||||
|
@ -105,6 +99,41 @@ public abstract class AbstractOwnCloudSyncAdapter extends
|
||||||
return mStoreManager;
|
return mStoreManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void initClientForCurrentAccount() throws OperationCanceledException, AuthenticatorException, IOException, AccountNotFoundException {
|
||||||
|
AccountUtils.constructFullURLForAccount(getContext(), account);
|
||||||
|
mClient = OwnCloudClientUtils.createOwnCloudClient(account, getContext());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected WebdavClient getClient() {
|
||||||
|
return mClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* method called by ContactSyncAdapter, that is never used */
|
||||||
|
protected HttpResponse fireRawRequest(HttpRequest query)
|
||||||
|
throws ClientProtocolException, OperationCanceledException,
|
||||||
|
AuthenticatorException, IOException {
|
||||||
|
/*
|
||||||
|
* BasicHttpContext httpContext = new BasicHttpContext(); BasicScheme
|
||||||
|
* basicAuth = new BasicScheme();
|
||||||
|
* httpContext.setAttribute("preemptive-auth", basicAuth);
|
||||||
|
*
|
||||||
|
* HttpResponse response = getClient().execute(mHost, query,
|
||||||
|
* httpContext);
|
||||||
|
*/
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* methods never used below */
|
||||||
|
/*
|
||||||
|
public Date getLastUpdated() {
|
||||||
|
return lastUpdated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastUpdated(Date lastUpdated) {
|
||||||
|
this.lastUpdated = lastUpdated;
|
||||||
|
}
|
||||||
|
|
||||||
protected ConnectionKeepAliveStrategy getKeepAliveStrategy() {
|
protected ConnectionKeepAliveStrategy getKeepAliveStrategy() {
|
||||||
return new ConnectionKeepAliveStrategy() {
|
return new ConnectionKeepAliveStrategy() {
|
||||||
public long getKeepAliveDuration(HttpResponse response,
|
public long getKeepAliveDuration(HttpResponse response,
|
||||||
|
@ -128,27 +157,5 @@ public abstract class AbstractOwnCloudSyncAdapter extends
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected HttpResponse fireRawRequest(HttpRequest query)
|
|
||||||
throws ClientProtocolException, OperationCanceledException,
|
|
||||||
AuthenticatorException, IOException {
|
|
||||||
/*
|
|
||||||
* BasicHttpContext httpContext = new BasicHttpContext(); BasicScheme
|
|
||||||
* basicAuth = new BasicScheme();
|
|
||||||
* httpContext.setAttribute("preemptive-auth", basicAuth);
|
|
||||||
*
|
|
||||||
* HttpResponse response = getClient().execute(mHost, query,
|
|
||||||
* httpContext);
|
|
||||||
*/
|
*/
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void initClientForCurrentAccount() throws OperationCanceledException, AuthenticatorException, IOException, AccountNotFoundException {
|
|
||||||
AccountUtils.constructFullURLForAccount(getContext(), account);
|
|
||||||
mClient = OwnCloudClientUtils.createOwnCloudClient(account, getContext());
|
|
||||||
}
|
|
||||||
|
|
||||||
protected WebdavClient getClient() {
|
|
||||||
return mClient;
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -30,7 +30,6 @@ import com.owncloud.android.Log_OC;
|
||||||
import com.owncloud.android.R;
|
import com.owncloud.android.R;
|
||||||
import com.owncloud.android.authentication.AccountAuthenticator;
|
import com.owncloud.android.authentication.AccountAuthenticator;
|
||||||
import com.owncloud.android.authentication.AuthenticatorActivity;
|
import com.owncloud.android.authentication.AuthenticatorActivity;
|
||||||
import com.owncloud.android.datamodel.DataStorageManager;
|
|
||||||
import com.owncloud.android.datamodel.FileDataStorageManager;
|
import com.owncloud.android.datamodel.FileDataStorageManager;
|
||||||
import com.owncloud.android.datamodel.OCFile;
|
import com.owncloud.android.datamodel.OCFile;
|
||||||
import com.owncloud.android.operations.RemoteOperationResult;
|
import com.owncloud.android.operations.RemoteOperationResult;
|
||||||
|
@ -44,6 +43,7 @@ import android.accounts.AccountsException;
|
||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
|
import android.content.AbstractThreadedSyncAdapter;
|
||||||
import android.content.ContentProviderClient;
|
import android.content.ContentProviderClient;
|
||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
@ -52,36 +52,60 @@ import android.content.SyncResult;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SyncAdapter implementation for syncing sample SyncAdapter contacts to the
|
* Implementation of {@link AbstractThreadedSyncAdapter} responsible for synchronizing
|
||||||
* platform ContactOperations provider.
|
* ownCloud files.
|
||||||
|
*
|
||||||
|
* Performs a full synchronization of the account recieved in {@link #onPerformSync(Account, Bundle, String, ContentProviderClient, SyncResult)}.
|
||||||
*
|
*
|
||||||
* @author Bartek Przybylski
|
* @author Bartek Przybylski
|
||||||
* @author David A. Velasco
|
* @author David A. Velasco
|
||||||
*/
|
*/
|
||||||
public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter {
|
public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter {
|
||||||
|
|
||||||
private final static String TAG = "FileSyncAdapter";
|
private final static String TAG = FileSyncAdapter.class.getSimpleName();
|
||||||
|
|
||||||
/**
|
/** Maximum number of failed folder synchronizations that are supported before finishing the synchronization operation */
|
||||||
* Maximum number of failed folder synchronizations that are supported before finishing the synchronization operation
|
|
||||||
*/
|
|
||||||
private static final int MAX_FAILED_RESULTS = 3;
|
private static final int MAX_FAILED_RESULTS = 3;
|
||||||
|
|
||||||
|
|
||||||
|
/** Time stamp for the current synchronization process, used to distinguish fresh data */
|
||||||
private long mCurrentSyncTime;
|
private long mCurrentSyncTime;
|
||||||
|
|
||||||
|
/** Flag made 'true' when a request to cancel the synchronization is received */
|
||||||
private boolean mCancellation;
|
private boolean mCancellation;
|
||||||
|
|
||||||
|
/** When 'true' the process was requested by the user through the user interface; when 'false', it was requested automatically by the system */
|
||||||
private boolean mIsManualSync;
|
private boolean mIsManualSync;
|
||||||
|
|
||||||
|
/** Counter for failed operations in the synchronization process */
|
||||||
private int mFailedResultsCounter;
|
private int mFailedResultsCounter;
|
||||||
|
|
||||||
|
/** Result of the last failed operation */
|
||||||
private RemoteOperationResult mLastFailedResult;
|
private RemoteOperationResult mLastFailedResult;
|
||||||
private SyncResult mSyncResult;
|
|
||||||
|
/** Counter of conflicts found between local and remote files */
|
||||||
private int mConflictsFound;
|
private int mConflictsFound;
|
||||||
|
|
||||||
|
/** Counter of failed operations in synchronization of kept-in-sync files */
|
||||||
private int mFailsInFavouritesFound;
|
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;
|
private Map<String, String> mForgottenLocalFiles;
|
||||||
|
|
||||||
|
/** {@link SyncResult} instance to return to the system when the synchronization finish */
|
||||||
|
private SyncResult mSyncResult;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an {@link FileSyncAdapter}
|
||||||
|
*
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
public FileSyncAdapter(Context context, boolean autoInitialize) {
|
public FileSyncAdapter(Context context, boolean autoInitialize) {
|
||||||
super(context, autoInitialize);
|
super(context, autoInitialize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
|
@ -99,20 +123,20 @@ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter {
|
||||||
mForgottenLocalFiles = new HashMap<String, String>();
|
mForgottenLocalFiles = new HashMap<String, String>();
|
||||||
mSyncResult = syncResult;
|
mSyncResult = syncResult;
|
||||||
mSyncResult.fullSyncRequested = false;
|
mSyncResult.fullSyncRequested = false;
|
||||||
mSyncResult.delayUntil = 60*60*24; // sync after 24h
|
mSyncResult.delayUntil = 60*60*24; // avoid too many automatic synchronizations
|
||||||
|
|
||||||
this.setAccount(account);
|
this.setAccount(account);
|
||||||
this.setContentProvider(provider);
|
this.setContentProvider(provider);
|
||||||
this.setStorageManager(new FileDataStorageManager(account, getContentProvider()));
|
this.setStorageManager(new FileDataStorageManager(account, provider));
|
||||||
try {
|
try {
|
||||||
this.initClientForCurrentAccount();
|
this.initClientForCurrentAccount();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
/// the account is unknown for the Synchronization Manager, or unreachable for this context; don't try this again
|
/// the account is unknown for the Synchronization Manager, unreachable this context, or can not be authenticated; don't try this again
|
||||||
mSyncResult.tooManyRetries = true;
|
mSyncResult.tooManyRetries = true;
|
||||||
notifyFailedSynchronization();
|
notifyFailedSynchronization();
|
||||||
return;
|
return;
|
||||||
} catch (AccountsException e) {
|
} catch (AccountsException e) {
|
||||||
/// the account is unknown for the Synchronization Manager, or unreachable for this context; don't try this again
|
/// the account is unknown for the Synchronization Manager, unreachable this context, or can not be authenticated; don't try this again
|
||||||
mSyncResult.tooManyRetries = true;
|
mSyncResult.tooManyRetries = true;
|
||||||
notifyFailedSynchronization();
|
notifyFailedSynchronization();
|
||||||
return;
|
return;
|
||||||
|
@ -125,10 +149,10 @@ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter {
|
||||||
updateOCVersion();
|
updateOCVersion();
|
||||||
mCurrentSyncTime = System.currentTimeMillis();
|
mCurrentSyncTime = System.currentTimeMillis();
|
||||||
if (!mCancellation) {
|
if (!mCancellation) {
|
||||||
fetchData(OCFile.PATH_SEPARATOR, DataStorageManager.ROOT_PARENT_ID);
|
synchronizeFolder(getStorageManager().getFileByPath(OCFile.PATH_SEPARATOR), true);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
Log_OC.d(TAG, "Leaving synchronization before any remote request due to cancellation was requested");
|
Log_OC.d(TAG, "Leaving synchronization before synchronizing the root folder because cancelation request");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -142,14 +166,12 @@ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter {
|
||||||
|
|
||||||
/// notify the user about the failure of MANUAL synchronization
|
/// notify the user about the failure of MANUAL synchronization
|
||||||
notifyFailedSynchronization();
|
notifyFailedSynchronization();
|
||||||
|
|
||||||
}
|
}
|
||||||
if (mConflictsFound > 0 || mFailsInFavouritesFound > 0) {
|
if (mConflictsFound > 0 || mFailsInFavouritesFound > 0) {
|
||||||
notifyFailsInFavourites();
|
notifyFailsInFavourites();
|
||||||
}
|
}
|
||||||
if (mForgottenLocalFiles.size() > 0) {
|
if (mForgottenLocalFiles.size() > 0) {
|
||||||
notifyForgottenLocalFiles();
|
notifyForgottenLocalFiles();
|
||||||
|
|
||||||
}
|
}
|
||||||
sendStickyBroadcast(false, null, mLastFailedResult); // message to signal the end to the UI
|
sendStickyBroadcast(false, null, mLastFailedResult); // message to signal the end to the UI
|
||||||
}
|
}
|
||||||
|
@ -159,8 +181,12 @@ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter {
|
||||||
/**
|
/**
|
||||||
* Called by system SyncManager when a synchronization is required to be cancelled.
|
* Called by system SyncManager when a synchronization is required to be cancelled.
|
||||||
*
|
*
|
||||||
* Sets the mCancellation flag to 'true'. THe synchronization will be stopped when before a new folder is fetched. Data of the last folder
|
* Sets the mCancellation flag to 'true'. THe synchronization will be stopped later,
|
||||||
* fetched will be still saved in the database. See onPerformSync implementation.
|
* before a new folder is fetched. Data of the last folder synchronized will be still
|
||||||
|
* locally saved.
|
||||||
|
*
|
||||||
|
* See {@link #onPerformSync(Account, Bundle, String, ContentProviderClient, SyncResult)}
|
||||||
|
* and {@link #synchronizeFolder(String, long)}.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void onSyncCanceled() {
|
public void onSyncCanceled() {
|
||||||
|
@ -183,23 +209,37 @@ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Synchronize the properties of files and folders contained in a remote folder given by remotePath.
|
* Synchronizes the list of files contained in a folder identified with its remote path.
|
||||||
*
|
*
|
||||||
* @param remotePath Remote path to the folder to synchronize.
|
* Fetches the list and properties of the files contained in the given folder, including their
|
||||||
* @param parentId Database Id of the folder to synchronize.
|
* properties, and updates the local database with them.
|
||||||
|
*
|
||||||
|
* Enters in the child folders to synchronize their contents also, following a recursive
|
||||||
|
* depth first strategy.
|
||||||
|
*
|
||||||
|
* @param folder Folder to synchronize.
|
||||||
|
* @param updateFolderProperties When 'true', updates also the properties of the of the target folder.
|
||||||
*/
|
*/
|
||||||
private void fetchData(String remotePath, long parentId) {
|
private void synchronizeFolder(OCFile folder, boolean updateFolderProperties) {
|
||||||
|
|
||||||
if (mFailedResultsCounter > MAX_FAILED_RESULTS || isFinisher(mLastFailedResult))
|
if (mFailedResultsCounter > MAX_FAILED_RESULTS || isFinisher(mLastFailedResult))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
boolean enforceMetadataUpdate = (parentId == DataStorageManager.ROOT_PARENT_ID);
|
/*
|
||||||
|
OCFile folder,
|
||||||
|
long currentSyncTime,
|
||||||
|
boolean updateFolderProperties,
|
||||||
|
boolean syncFullAccount,
|
||||||
|
DataStorageManager dataStorageManager,
|
||||||
|
Account account,
|
||||||
|
Context context ) {
|
||||||
|
|
||||||
// perform folder synchronization
|
}
|
||||||
SynchronizeFolderOperation synchFolderOp = new SynchronizeFolderOperation( remotePath,
|
*/
|
||||||
|
// folder synchronization
|
||||||
|
SynchronizeFolderOperation synchFolderOp = new SynchronizeFolderOperation( folder,
|
||||||
mCurrentSyncTime,
|
mCurrentSyncTime,
|
||||||
parentId,
|
updateFolderProperties,
|
||||||
enforceMetadataUpdate,
|
|
||||||
true,
|
true,
|
||||||
getStorageManager(),
|
getStorageManager(),
|
||||||
getAccount(),
|
getAccount(),
|
||||||
|
@ -209,8 +249,9 @@ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter {
|
||||||
|
|
||||||
|
|
||||||
// synchronized folder -> notice to UI - ALWAYS, although !result.isSuccess
|
// synchronized folder -> notice to UI - ALWAYS, although !result.isSuccess
|
||||||
sendStickyBroadcast(true, remotePath, null);
|
sendStickyBroadcast(true, folder.getRemotePath(), null);
|
||||||
|
|
||||||
|
// check the result of synchronizing the folder
|
||||||
if (result.isSuccess() || result.getCode() == ResultCode.SYNC_CONFLICT) {
|
if (result.isSuccess() || result.getCode() == ResultCode.SYNC_CONFLICT) {
|
||||||
|
|
||||||
if (result.getCode() == ResultCode.SYNC_CONFLICT) {
|
if (result.getCode() == ResultCode.SYNC_CONFLICT) {
|
||||||
|
@ -224,11 +265,13 @@ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter {
|
||||||
List<OCFile> children = synchFolderOp.getChildren();
|
List<OCFile> children = synchFolderOp.getChildren();
|
||||||
fetchChildren(children); // beware of the 'hidden' recursion here!
|
fetchChildren(children); // beware of the 'hidden' recursion here!
|
||||||
|
|
||||||
sendStickyBroadcast(true, remotePath, null);
|
// update folder size again after recursive synchronization
|
||||||
|
getStorageManager().calculateFolderSize(folder.getFileId());
|
||||||
|
sendStickyBroadcast(true, folder.getRemotePath(), null); // notify again
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
// in failures, the statistics for the global result are updated
|
||||||
if (result.getCode() == RemoteOperationResult.ResultCode.UNAUTHORIZED ||
|
if (result.getCode() == RemoteOperationResult.ResultCode.UNAUTHORIZED ||
|
||||||
// (result.isTemporalRedirection() && result.isIdPRedirection() &&
|
|
||||||
( result.isIdPRedirection() &&
|
( result.isIdPRedirection() &&
|
||||||
AccountAuthenticator.AUTH_TOKEN_TYPE_SAML_WEB_SSO_SESSION_COOKIE.equals(getClient().getAuthTokenType()))) {
|
AccountAuthenticator.AUTH_TOKEN_TYPE_SAML_WEB_SSO_SESSION_COOKIE.equals(getClient().getAuthTokenType()))) {
|
||||||
mSyncResult.stats.numAuthExceptions++;
|
mSyncResult.stats.numAuthExceptions++;
|
||||||
|
@ -264,23 +307,20 @@ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Synchronize data of folders in the list of received files
|
* Triggers the synchronization of any folder contained in the list of received files.
|
||||||
*
|
*
|
||||||
* @param files Files to recursively fetch
|
* @param files Files to recursively synchronize.
|
||||||
*/
|
*/
|
||||||
private void fetchChildren(List<OCFile> files) {
|
private void fetchChildren(List<OCFile> files) {
|
||||||
int i;
|
int i;
|
||||||
for (i=0; i < files.size() && !mCancellation; i++) {
|
for (i=0; i < files.size() && !mCancellation; i++) {
|
||||||
OCFile newFile = files.get(i);
|
OCFile newFile = files.get(i);
|
||||||
if (newFile.isDirectory()) {
|
if (newFile.isDirectory()) {
|
||||||
fetchData(newFile.getRemotePath(), newFile.getFileId());
|
synchronizeFolder(newFile, false);
|
||||||
|
|
||||||
// Update folder size on DB
|
|
||||||
getStorageManager().calculateFolderSize(newFile.getFileId());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mCancellation && i <files.size()) Log_OC.d(TAG, "Leaving synchronization before synchronizing " + files.get(i).getRemotePath() + " because cancelation request");
|
if (mCancellation && i <files.size()) Log_OC.d(TAG, "Leaving synchronization before synchronizing " + files.get(i).getRemotePath() + " due to cancelation request");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -22,12 +22,15 @@ import android.content.Intent;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Background service for syncing files to our local Database
|
* Background service for synchronizing remote files with their local state.
|
||||||
|
*
|
||||||
|
* Serves as a connector to an instance of {@link FileSyncAdapter}, as required by standard Android APIs.
|
||||||
*
|
*
|
||||||
* @author Bartek Przybylski
|
* @author Bartek Przybylski
|
||||||
*
|
* @author David A. Velasco
|
||||||
*/
|
*/
|
||||||
public class FileSyncService extends Service {
|
public class FileSyncService extends Service {
|
||||||
|
|
||||||
public static final String SYNC_MESSAGE = "ACCOUNT_SYNC";
|
public static final String SYNC_MESSAGE = "ACCOUNT_SYNC";
|
||||||
public static final String SYNC_FOLDER_REMOTE_PATH = "SYNC_FOLDER_REMOTE_PATH";
|
public static final String SYNC_FOLDER_REMOTE_PATH = "SYNC_FOLDER_REMOTE_PATH";
|
||||||
public static final String IN_PROGRESS = "SYNC_IN_PROGRESS";
|
public static final String IN_PROGRESS = "SYNC_IN_PROGRESS";
|
||||||
|
@ -48,4 +51,5 @@ public class FileSyncService extends Service {
|
||||||
public IBinder onBind(Intent intent) {
|
public IBinder onBind(Intent intent) {
|
||||||
return new FileSyncAdapter(getApplicationContext(), true).getSyncAdapterBinder();
|
return new FileSyncAdapter(getApplicationContext(), true).getSyncAdapterBinder();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -862,75 +862,45 @@ OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNa
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
boolean inProgress = intent.getBooleanExtra(FileSyncService.IN_PROGRESS, false);
|
boolean inProgress = intent.getBooleanExtra(FileSyncService.IN_PROGRESS, false);
|
||||||
String accountName = intent.getStringExtra(FileSyncService.ACCOUNT_NAME);
|
String accountName = intent.getStringExtra(FileSyncService.ACCOUNT_NAME);
|
||||||
|
RemoteOperationResult synchResult = (RemoteOperationResult)intent.getSerializableExtra(FileSyncService.SYNC_RESULT);
|
||||||
|
|
||||||
Log_OC.d(TAG, "sync of account " + accountName + " is in_progress: " + inProgress);
|
Log_OC.d(TAG, "sync of account " + accountName + " is in_progress: " + inProgress);
|
||||||
|
|
||||||
RemoteOperationResult synchResult = (RemoteOperationResult)intent.getSerializableExtra(FileSyncService.SYNC_RESULT);
|
|
||||||
|
|
||||||
if (getAccount() != null && accountName.equals(getAccount().name)) {
|
if (getAccount() != null && accountName.equals(getAccount().name)) {
|
||||||
|
|
||||||
String synchFolderRemotePath = intent.getStringExtra(FileSyncService.SYNC_FOLDER_REMOTE_PATH);
|
String synchFolderRemotePath = intent.getStringExtra(FileSyncService.SYNC_FOLDER_REMOTE_PATH);
|
||||||
|
|
||||||
/*
|
OCFile currentFile = mStorageManager.getFileById(getFile().getFileId());
|
||||||
boolean fillBlankRoot = false;
|
|
||||||
if (currentDir == null) {
|
|
||||||
currentDir = mStorageManager.getFileByPath(OCFile.PATH_SEPARATOR);
|
|
||||||
fillBlankRoot = (currentDir != null);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
OCFile currentDir = mStorageManager.getFileById(getCurrentDir().getFileId());
|
OCFile currentDir = mStorageManager.getFileById(getCurrentDir().getFileId());
|
||||||
|
|
||||||
if (currentDir == null) {
|
if (currentDir == null) {
|
||||||
// current folder was removed from the server
|
// current folder was removed from the server
|
||||||
Toast.makeText(FileDisplayActivity.this, getString(R.string.sync_current_folder_was_removed), Toast.LENGTH_LONG)
|
Toast.makeText(FileDisplayActivity.this, getString(R.string.sync_current_folder_was_removed), Toast.LENGTH_LONG)
|
||||||
.show();
|
.show();
|
||||||
onBackPressed();
|
onBackPressed();
|
||||||
|
|
||||||
} else if (synchFolderRemotePath != null && currentDir.getRemotePath().equals(synchFolderRemotePath)) {
|
} else {
|
||||||
|
if (currentFile == null && !getFile().isDirectory()) {
|
||||||
/*OCFile synchDir = getStorageManager().getFileByPath(synchFolderRemotePath);
|
// currently selected file was removed in the server, and now we know it
|
||||||
//boolean needToRefresh = false;
|
cleanSecondFragment();
|
||||||
if (synchDir == null) {
|
currentFile = currentDir;
|
||||||
// after synchronizing the current folder does not exist (was deleted in the server) ; need to move to other
|
}
|
||||||
/*
|
|
||||||
String synchPath = synchFolderRemotePath;
|
|
||||||
do {
|
|
||||||
String synchParentPath = new File(synchPath).getParent();
|
|
||||||
synchParentPath = synchParentPath.endsWith(OCFile.PATH_SEPARATOR) ? synchParentPath : synchParentPath + OCFile.PATH_SEPARATOR;
|
|
||||||
synchDir = getStorageManager().getFileByPath(synchParentPath);
|
|
||||||
popDirname();
|
|
||||||
synchPath = synchParentPath;
|
|
||||||
} while (synchDir == null); // sooner of later will get ROOT, that never is null
|
|
||||||
currentDir = synchDir;
|
|
||||||
*-/
|
|
||||||
Toast.makeText(FileDisplayActivity.this,
|
|
||||||
String.format(getString(R.string.sync_current_folder_was_removed), "LOLO"),
|
|
||||||
Toast.LENGTH_LONG).show();
|
|
||||||
//needToRefresh = true;
|
|
||||||
onBackPressed();
|
|
||||||
} else {*/
|
|
||||||
|
|
||||||
|
if (synchFolderRemotePath != null && currentDir.getRemotePath().equals(synchFolderRemotePath)) {
|
||||||
OCFileListFragment fileListFragment = getListOfFilesFragment();
|
OCFileListFragment fileListFragment = getListOfFilesFragment();
|
||||||
if (fileListFragment != null) {
|
if (fileListFragment != null) {
|
||||||
fileListFragment.listDirectory(currentDir);
|
fileListFragment.listDirectory(currentDir);
|
||||||
}
|
}
|
||||||
//boolean existsSecondFragment = (getSecondFragment() != null);
|
|
||||||
//if (!existsSecondFragment) {
|
|
||||||
if (getSecondFragment() == null) {
|
|
||||||
setFile(currentDir);
|
|
||||||
}
|
}
|
||||||
//updateFragmentsVisibility(existsSecondFragment);
|
setFile(currentFile);
|
||||||
//updateNavigationElementsInActionBar(existsSecondFragment ? getFile() : null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setSupportProgressBarIndeterminateVisibility(inProgress);
|
setSupportProgressBarIndeterminateVisibility(inProgress);
|
||||||
removeStickyBroadcast(intent);
|
removeStickyBroadcast(intent);
|
||||||
|
|
||||||
mSyncInProgress = inProgress;
|
mSyncInProgress = inProgress;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (synchResult != null) {
|
if (synchResult != null) {
|
||||||
if (synchResult.getCode().equals(RemoteOperationResult.ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED)) {
|
if (synchResult.getCode().equals(RemoteOperationResult.ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED)) {
|
||||||
mLastSslUntrustedServerResult = synchResult;
|
mLastSslUntrustedServerResult = synchResult;
|
||||||
|
@ -1015,7 +985,7 @@ OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNa
|
||||||
cleanSecondFragment();
|
cleanSecondFragment();
|
||||||
|
|
||||||
// Sync Folder
|
// Sync Folder
|
||||||
startSyncFolderOperation(directory.getRemotePath(), directory.getFileId());
|
startSyncFolderOperation(directory);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1425,15 +1395,14 @@ OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNa
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void startSyncFolderOperation(String remotePath, long parentId) {
|
public void startSyncFolderOperation(OCFile folder) {
|
||||||
long currentSyncTime = System.currentTimeMillis();
|
long currentSyncTime = System.currentTimeMillis();
|
||||||
|
|
||||||
mSyncInProgress = true;
|
mSyncInProgress = true;
|
||||||
|
|
||||||
// perform folder synchronization
|
// perform folder synchronization
|
||||||
RemoteOperation synchFolderOp = new SynchronizeFolderOperation( remotePath,
|
RemoteOperation synchFolderOp = new SynchronizeFolderOperation( folder,
|
||||||
currentSyncTime,
|
currentSyncTime,
|
||||||
parentId,
|
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
getStorageManager(),
|
getStorageManager(),
|
||||||
|
|
|
@ -141,7 +141,7 @@ public class OCFileListFragment extends ExtendedListFragment implements EditName
|
||||||
if (mFile != null) {
|
if (mFile != null) {
|
||||||
listDirectory(mFile);
|
listDirectory(mFile);
|
||||||
|
|
||||||
mContainerActivity.startSyncFolderOperation(mFile.getRemotePath(), mFile.getFileId());
|
mContainerActivity.startSyncFolderOperation(mFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -408,7 +408,7 @@ public class OCFileListFragment extends ExtendedListFragment implements EditName
|
||||||
|
|
||||||
public void startImagePreview(OCFile file);
|
public void startImagePreview(OCFile file);
|
||||||
|
|
||||||
public void startSyncFolderOperation(String remotePath, long parentId);
|
public void startSyncFolderOperation(OCFile folder);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Getter for the current DataStorageManager in the container activity
|
* Getter for the current DataStorageManager in the container activity
|
||||||
|
|
Loading…
Reference in a new issue