|
@@ -23,10 +23,15 @@ import org.apache.jackrabbit.webdav.MultiStatus;
|
|
|
import org.apache.jackrabbit.webdav.client.methods.PropFindMethod;
|
|
|
|
|
|
import android.accounts.Account;
|
|
|
+import android.content.Context;
|
|
|
+import android.content.Intent;
|
|
|
import android.util.Log;
|
|
|
|
|
|
import com.owncloud.android.datamodel.DataStorageManager;
|
|
|
import com.owncloud.android.datamodel.OCFile;
|
|
|
+import com.owncloud.android.files.services.FileDownloader;
|
|
|
+import com.owncloud.android.files.services.FileUploader;
|
|
|
+import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
|
|
|
|
|
|
import eu.alefzero.webdav.WebdavClient;
|
|
|
import eu.alefzero.webdav.WebdavEntry;
|
|
@@ -35,49 +40,111 @@ import eu.alefzero.webdav.WebdavUtils;
|
|
|
public class SynchronizeFileOperation extends RemoteOperation {
|
|
|
|
|
|
private String TAG = SynchronizeFileOperation.class.getSimpleName();
|
|
|
-
|
|
|
private String mRemotePath;
|
|
|
-
|
|
|
private DataStorageManager mStorageManager;
|
|
|
-
|
|
|
private Account mAccount;
|
|
|
+ private boolean mSyncFileContents;
|
|
|
+ private boolean mLocalChangeAlreadyKnown;
|
|
|
+ private Context mContext;
|
|
|
+
|
|
|
+ private boolean mTransferWasRequested = false;
|
|
|
|
|
|
public SynchronizeFileOperation(
|
|
|
String remotePath,
|
|
|
DataStorageManager dataStorageManager,
|
|
|
- Account account) {
|
|
|
+ Account account,
|
|
|
+ boolean syncFileContents,
|
|
|
+ boolean localChangeAlreadyKnown,
|
|
|
+ Context context) {
|
|
|
+
|
|
|
mRemotePath = remotePath;
|
|
|
mStorageManager = dataStorageManager;
|
|
|
mAccount = account;
|
|
|
+ mSyncFileContents = syncFileContents;
|
|
|
+ mLocalChangeAlreadyKnown = localChangeAlreadyKnown;
|
|
|
+ mContext = context;
|
|
|
}
|
|
|
|
|
|
+
|
|
|
@Override
|
|
|
protected RemoteOperationResult run(WebdavClient client) {
|
|
|
+
|
|
|
PropFindMethod propfind = null;
|
|
|
RemoteOperationResult result = null;
|
|
|
+ mTransferWasRequested = false;
|
|
|
try {
|
|
|
- propfind = new PropFindMethod(client.getBaseUri() + WebdavUtils.encodePath(mRemotePath));
|
|
|
- int status = client.executeMethod(propfind);
|
|
|
- boolean isMultiStatus = status == HttpStatus.SC_MULTI_STATUS;
|
|
|
- Boolean isConflict = Boolean.FALSE;
|
|
|
- if (isMultiStatus) {
|
|
|
- MultiStatus resp = propfind.getResponseBodyAsMultiStatus();
|
|
|
- WebdavEntry we = new WebdavEntry(resp.getResponses()[0],
|
|
|
+ OCFile localFile = mStorageManager.getFileByPath(mRemotePath);
|
|
|
+
|
|
|
+ if (!localFile.isDown()) {
|
|
|
+ /// easy decision
|
|
|
+ requestForDownload(localFile);
|
|
|
+ result = new RemoteOperationResult(ResultCode.OK);
|
|
|
+
|
|
|
+ } else {
|
|
|
+ /// local copy in the device -> need to think a bit more before do nothing
|
|
|
+
|
|
|
+ propfind = new PropFindMethod(client.getBaseUri() + WebdavUtils.encodePath(mRemotePath));
|
|
|
+ int status = client.executeMethod(propfind);
|
|
|
+ boolean isMultiStatus = status == HttpStatus.SC_MULTI_STATUS;
|
|
|
+ if (isMultiStatus) {
|
|
|
+ MultiStatus resp = propfind.getResponseBodyAsMultiStatus();
|
|
|
+ WebdavEntry we = new WebdavEntry(resp.getResponses()[0],
|
|
|
client.getBaseUri().getPath());
|
|
|
- OCFile file = fillOCFile(we);
|
|
|
- OCFile oldFile = mStorageManager.getFileByPath(file.getRemotePath());
|
|
|
- if (oldFile.getFileLength() != file.getFileLength() ||
|
|
|
- oldFile.getModificationTimestamp() != file.getModificationTimestamp()) {
|
|
|
- isConflict = Boolean.TRUE;
|
|
|
- }
|
|
|
+ OCFile serverFile = fillOCFile(we);
|
|
|
+
|
|
|
+ /// check changes in server and local file
|
|
|
+ boolean serverChanged = false;
|
|
|
+ if (serverFile.getEtag() != null) {
|
|
|
+ serverChanged = (!serverFile.getEtag().equals(localFile.getEtag()));
|
|
|
+ } else {
|
|
|
+ // server without etags
|
|
|
+ serverChanged = (serverFile.getModificationTimestamp() > localFile.getModificationTimestamp());
|
|
|
+ }
|
|
|
+ boolean localChanged = (mLocalChangeAlreadyKnown || localFile.getLocalModificationTimestamp() > localFile.getLastSyncDateForData());
|
|
|
+
|
|
|
+ /// decide action to perform depending upon changes
|
|
|
+ if (localChanged && serverChanged) {
|
|
|
+ // conflict
|
|
|
+ result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT);
|
|
|
+
|
|
|
+ } else if (localChanged) {
|
|
|
+ if (mSyncFileContents) {
|
|
|
+ requestForUpload(localFile);
|
|
|
+ // the local update of file properties will be done by the FileUploader service when the upload finishes
|
|
|
+ } else {
|
|
|
+ // NOTHING TO DO HERE: updating the properties of the file in the server without uploading the contents would be stupid;
|
|
|
+ // So, an instance of SynchronizeFileOperation created with syncFileContents == false is completely useless when we suspect
|
|
|
+ // that an upload is necessary (for instance, in FileObserverService).
|
|
|
+ }
|
|
|
+ result = new RemoteOperationResult(ResultCode.OK);
|
|
|
+
|
|
|
+ } else if (serverChanged) {
|
|
|
+ if (mSyncFileContents) {
|
|
|
+ requestForDownload(serverFile);
|
|
|
+ // the update of local data will be done later by the FileUploader service when the upload finishes
|
|
|
+ } else {
|
|
|
+ // TODO CHECK: is this really useful in some point in the code?
|
|
|
+ serverFile.setKeepInSync(localFile.keepInSync());
|
|
|
+ serverFile.setParentId(localFile.getParentId());
|
|
|
+ mStorageManager.saveFile(serverFile);
|
|
|
+
|
|
|
+ }
|
|
|
+ result = new RemoteOperationResult(ResultCode.OK);
|
|
|
|
|
|
- } else {
|
|
|
- client.exhaustResponse(propfind.getResponseBodyAsStream());
|
|
|
- }
|
|
|
+ } else {
|
|
|
+ // nothing changed, nothing to do
|
|
|
+ result = new RemoteOperationResult(ResultCode.OK);
|
|
|
+ }
|
|
|
+
|
|
|
+ } else {
|
|
|
+ client.exhaustResponse(propfind.getResponseBodyAsStream());
|
|
|
+ result = new RemoteOperationResult(false, status);
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ Log.i(TAG, "Synchronizing " + mAccount.name + ", file " + mRemotePath + ": " + result.getLogMessage());
|
|
|
|
|
|
- result = new RemoteOperationResult(isMultiStatus, status);
|
|
|
- result.setExtraData(isConflict);
|
|
|
- Log.i(TAG, "Synchronizing " + mAccount.name + ", file " + mRemotePath + ": " + result.getLogMessage());
|
|
|
} catch (Exception e) {
|
|
|
result = new RemoteOperationResult(e);
|
|
|
Log.e(TAG, "Synchronizing " + mAccount.name + ", file " + mRemotePath + ": " + result.getLogMessage(), result.getException());
|
|
@@ -88,7 +155,39 @@ public class SynchronizeFileOperation extends RemoteOperation {
|
|
|
}
|
|
|
return result;
|
|
|
}
|
|
|
+
|
|
|
|
|
|
+ /**
|
|
|
+ * Requests for an upload to the FileUploader service
|
|
|
+ *
|
|
|
+ * @param localFile OCFile object representing the file to upload
|
|
|
+ */
|
|
|
+ private void requestForUpload(OCFile localFile) {
|
|
|
+ Intent i = new Intent(mContext, FileUploader.class);
|
|
|
+ i.putExtra(FileUploader.KEY_ACCOUNT, mAccount);
|
|
|
+ i.putExtra(FileUploader.KEY_REMOTE_FILE, mRemotePath);
|
|
|
+ i.putExtra(FileUploader.KEY_LOCAL_FILE, localFile.getStoragePath());
|
|
|
+ i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_SINGLE_FILE);
|
|
|
+ i.putExtra(FileUploader.KEY_FORCE_OVERWRITE, true);
|
|
|
+ mContext.startService(i);
|
|
|
+ mTransferWasRequested = true;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Requests for a download to the FileDownloader service
|
|
|
+ *
|
|
|
+ * @param file OCFile object representing the file to download
|
|
|
+ */
|
|
|
+ private void requestForDownload(OCFile file) {
|
|
|
+ Intent i = new Intent(mContext, FileDownloader.class);
|
|
|
+ i.putExtra(FileDownloader.EXTRA_ACCOUNT, mAccount);
|
|
|
+ i.putExtra(FileDownloader.EXTRA_FILE, file);
|
|
|
+ mContext.startService(i);
|
|
|
+ mTransferWasRequested = true;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
/**
|
|
|
* Creates and populates a new {@link OCFile} object with the data read from the server.
|
|
|
*
|
|
@@ -101,8 +200,14 @@ public class SynchronizeFileOperation extends RemoteOperation {
|
|
|
file.setFileLength(we.contentLength());
|
|
|
file.setMimetype(we.contentType());
|
|
|
file.setModificationTimestamp(we.modifiedTimesamp());
|
|
|
- file.setLastSyncDate(System.currentTimeMillis());
|
|
|
+ file.setLastSyncDateForProperties(System.currentTimeMillis());
|
|
|
+ file.setLastSyncDateForData(0);
|
|
|
return file;
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+ public boolean transferWasRequested() {
|
|
|
+ return mTransferWasRequested;
|
|
|
+ }
|
|
|
+
|
|
|
}
|