From: David A. Velasco Date: Thu, 15 Nov 2012 16:18:04 +0000 (+0100) Subject: Download button in file details view upgraded to sync file content in both directions X-Git-Tag: oc-android-1.4.3~105 X-Git-Url: http://git.linex4red.de/pub/Android/ownCloud.git/commitdiff_plain/ff2271a8c531efe4c2602c63934eca568c397510?ds=inline;hp=--cc Download button in file details view upgraded to sync file content in both directions --- ff2271a8c531efe4c2602c63934eca568c397510 diff --git a/res/values/strings.xml b/res/values/strings.xml index fd2915c7..bfbf4853 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -169,10 +169,10 @@ Rename Remove - "Do you really want to remove %1$s ?" - Local only - Remove from server - Remote and local + "Do you really want to remove %1$s ?" + Local only + Remove from server + Remote and local "Successful removal" "Removal could not be completed" @@ -180,6 +180,9 @@ "Local copy could not be renamed; try a differente new name" "Rename could not be completed" + Remote file could not be checked + File contents already synchronized + Directory could not be created Wait a moment diff --git a/src/com/owncloud/android/datamodel/FileDataStorageManager.java b/src/com/owncloud/android/datamodel/FileDataStorageManager.java index c992af2c..4c92d09b 100644 --- a/src/com/owncloud/android/datamodel/FileDataStorageManager.java +++ b/src/com/owncloud/android/datamodel/FileDataStorageManager.java @@ -118,7 +118,8 @@ public class FileDataStorageManager implements DataStorageManager { if (!file.isDirectory()) cv.put(ProviderTableMeta.FILE_STORAGE_PATH, file.getStoragePath()); cv.put(ProviderTableMeta.FILE_ACCOUNT_OWNER, mAccount.name); - cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE, file.getLastSyncDate()); + cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE, file.getLastSyncDateForProperties()); + cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA, file.getLastSyncDateForData()); cv.put(ProviderTableMeta.FILE_KEEP_IN_SYNC, file.keepInSync() ? 1 : 0); if (fileExists(file.getRemotePath())) { @@ -197,7 +198,8 @@ public class FileDataStorageManager implements DataStorageManager { if (!file.isDirectory()) cv.put(ProviderTableMeta.FILE_STORAGE_PATH, file.getStoragePath()); cv.put(ProviderTableMeta.FILE_ACCOUNT_OWNER, mAccount.name); - cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE, file.getLastSyncDate()); + cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE, file.getLastSyncDateForProperties()); + cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA, file.getLastSyncDateForData()); cv.put(ProviderTableMeta.FILE_KEEP_IN_SYNC, file.keepInSync() ? 1 : 0); if (fileExists(file.getRemotePath())) { @@ -401,8 +403,10 @@ public class FileDataStorageManager implements DataStorageManager { .getColumnIndex(ProviderTableMeta.FILE_CREATION))); file.setModificationTimestamp(c.getLong(c .getColumnIndex(ProviderTableMeta.FILE_MODIFIED))); - file.setLastSyncDate(c.getLong(c + file.setLastSyncDateForProperties(c.getLong(c .getColumnIndex(ProviderTableMeta.FILE_LAST_SYNC_DATE))); + file.setLastSyncDateForData(c.getLong(c. + getColumnIndex(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA))); file.setKeepInSync(c.getInt( c.getColumnIndex(ProviderTableMeta.FILE_KEEP_IN_SYNC)) == 1 ? true : false); } diff --git a/src/com/owncloud/android/datamodel/OCFile.java b/src/com/owncloud/android/datamodel/OCFile.java index ec5a67a9..766a8325 100644 --- a/src/com/owncloud/android/datamodel/OCFile.java +++ b/src/com/owncloud/android/datamodel/OCFile.java @@ -48,9 +48,12 @@ public class OCFile implements Parcelable, Comparable { private String mLocalPath; private String mMimeType; private boolean mNeedsUpdating; - private long mLastSyncDate; + private long mLastSyncDateForProperties; + private long mLastSyncDateForData; private boolean mKeepInSync; + private String mEtag; + /** * Create new {@link OCFile} with given path. * @@ -83,7 +86,8 @@ public class OCFile implements Parcelable, Comparable { mMimeType = source.readString(); mNeedsUpdating = source.readInt() == 0; mKeepInSync = source.readInt() == 1; - mLastSyncDate = source.readLong(); + mLastSyncDateForProperties = source.readLong(); + mLastSyncDateForData = source.readLong(); } @Override @@ -98,7 +102,8 @@ public class OCFile implements Parcelable, Comparable { dest.writeString(mMimeType); dest.writeInt(mNeedsUpdating ? 1 : 0); dest.writeInt(mKeepInSync ? 1 : 0); - dest.writeLong(mLastSyncDate); + dest.writeLong(mLastSyncDateForProperties); + dest.writeLong(mLastSyncDateForData); } /** @@ -254,7 +259,8 @@ public class OCFile implements Parcelable, Comparable { mLength = 0; mCreationTimestamp = 0; mModifiedTimestamp = 0; - mLastSyncDate = 0; + mLastSyncDateForProperties = 0; + mLastSyncDateForData = 0; mKeepInSync = false; mNeedsUpdating = false; } @@ -322,12 +328,20 @@ public class OCFile implements Parcelable, Comparable { return mNeedsUpdating; } - public long getLastSyncDate() { - return mLastSyncDate; + public long getLastSyncDateForProperties() { + return mLastSyncDateForProperties; + } + + public void setLastSyncDateForProperties(long lastSyncDate) { + mLastSyncDateForProperties = lastSyncDate; } - public void setLastSyncDate(long lastSyncDate) { - mLastSyncDate = lastSyncDate; + public long getLastSyncDateForData() { + return mLastSyncDateForData; + } + + public void setLastSyncDateForData(long lastSyncDate) { + mLastSyncDateForData = lastSyncDate; } public void setKeepInSync(boolean keepInSync) { @@ -374,4 +388,16 @@ public class OCFile implements Parcelable, Comparable { return asString; } + public String getEtag() { + return mEtag; + } + + public long getLocalModificationTimestamp() { + if (mLocalPath != null && mLocalPath.length() > 0) { + File f = new File(mLocalPath); + return f.lastModified(); + } + return 0; + } + } diff --git a/src/com/owncloud/android/db/ProviderMeta.java b/src/com/owncloud/android/db/ProviderMeta.java index c7f86b63..faa16d7c 100644 --- a/src/com/owncloud/android/db/ProviderMeta.java +++ b/src/com/owncloud/android/db/ProviderMeta.java @@ -31,7 +31,8 @@ public class ProviderMeta { public static final String AUTHORITY_FILES = "org.owncloud"; public static final String DB_FILE = "owncloud.db"; public static final String DB_NAME = "filelist"; - public static final int DB_VERSION = 2; + //public static final int DB_VERSION = 2; + public static final int DB_VERSION = 3; private ProviderMeta() { } @@ -57,7 +58,8 @@ public class ProviderMeta { public static final String FILE_STORAGE_PATH = "media_path"; public static final String FILE_PATH = "path"; public static final String FILE_ACCOUNT_OWNER = "file_owner"; - public static final String FILE_LAST_SYNC_DATE = "last_sync_date"; + public static final String FILE_LAST_SYNC_DATE = "last_sync_date"; // _for_properties, but let's keep it as it is + public static final String FILE_LAST_SYNC_DATE_FOR_DATA = "last_sync_date_for_data"; public static final String FILE_KEEP_IN_SYNC = "keep_in_sync"; public static final String DEFAULT_SORT_ORDER = FILE_NAME diff --git a/src/com/owncloud/android/files/OwnCloudFileObserver.java b/src/com/owncloud/android/files/OwnCloudFileObserver.java index 1e0c176c..c2a066ee 100644 --- a/src/com/owncloud/android/files/OwnCloudFileObserver.java +++ b/src/com/owncloud/android/files/OwnCloudFileObserver.java @@ -24,8 +24,6 @@ import java.util.List; import com.owncloud.android.datamodel.DataStorageManager; import com.owncloud.android.datamodel.OCFile; -import com.owncloud.android.files.OwnCloudFileObserver.FileObserverStatusListener.Status; -import com.owncloud.android.files.services.FileUploader; import com.owncloud.android.network.OwnCloudClientUtils; import com.owncloud.android.operations.RemoteOperationResult; import com.owncloud.android.operations.SynchronizeFileOperation; @@ -34,7 +32,6 @@ import eu.alefzero.webdav.WebdavClient; import android.accounts.Account; import android.content.Context; -import android.content.Intent; import android.os.FileObserver; import android.util.Log; @@ -97,44 +94,26 @@ public class OwnCloudFileObserver extends FileObserver { Log.wtf(TAG, "Incorrect event " + event + " sent for file " + mPath + ((path != null) ? File.separator + path : "") + " with registered for " + mMask + " and original path " + mPath); + /* Unexpected event that will be ignored; no reason to propagate it for (FileObserverStatusListener l : mListeners) l.OnObservedFileStatusUpdate(mPath, getRemotePath(), mOCAccount, Status.INCORRECT_MASK); + */ return; } WebdavClient wc = OwnCloudClientUtils.createOwnCloudClient(mOCAccount, mContext); - SynchronizeFileOperation sfo = new SynchronizeFileOperation(mFile.getRemotePath(), mStorage, mOCAccount); + SynchronizeFileOperation sfo = new SynchronizeFileOperation(getRemotePath(), mStorage, mOCAccount, true, false, mContext); RemoteOperationResult result = sfo.execute(wc); - - if (result.getExtraData() == Boolean.TRUE) { - // inform user about conflict and let him decide what to do - for (FileObserverStatusListener l : mListeners) - l.OnObservedFileStatusUpdate(mPath, getRemotePath(), mOCAccount, Status.CONFLICT); - return; + for (FileObserverStatusListener l : mListeners) { + l.onObservedFileStatusUpdate(mPath, getRemotePath(), mOCAccount, result); } - - for (FileObserverStatusListener l : mListeners) - l.OnObservedFileStatusUpdate(mPath, getRemotePath(), mOCAccount, Status.SENDING_TO_UPLOADER); - Intent i = new Intent(mContext, FileUploader.class); - i.putExtra(FileUploader.KEY_ACCOUNT, mOCAccount); - i.putExtra(FileUploader.KEY_REMOTE_FILE, mFile.getRemotePath()); - i.putExtra(FileUploader.KEY_LOCAL_FILE, mPath); - i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_SINGLE_FILE); - i.putExtra(FileUploader.KEY_FORCE_OVERWRITE, true); - mContext.startService(i); } public interface FileObserverStatusListener { - public enum Status { - SENDING_TO_UPLOADER, - CONFLICT, - INCORRECT_MASK - } - - public void OnObservedFileStatusUpdate(String localPath, + public void onObservedFileStatusUpdate(String localPath, String remotePath, Account account, - FileObserverStatusListener.Status status); + RemoteOperationResult result); } public OCFile getOCFile() { diff --git a/src/com/owncloud/android/files/services/FileDownloader.java b/src/com/owncloud/android/files/services/FileDownloader.java index 0ca42484..b89a1c37 100644 --- a/src/com/owncloud/android/files/services/FileDownloader.java +++ b/src/com/owncloud/android/files/services/FileDownloader.java @@ -59,6 +59,7 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis public static final String EXTRA_ACCOUNT = "ACCOUNT"; public static final String EXTRA_FILE = "FILE"; + public static final String DOWNLOAD_ADDED_MESSAGE = "DOWNLOAD_ADDED"; public static final String DOWNLOAD_FINISH_MESSAGE = "DOWNLOAD_FINISH"; public static final String EXTRA_DOWNLOAD_RESULT = "RESULT"; public static final String EXTRA_FILE_PATH = "FILE_PATH"; @@ -282,7 +283,9 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis */ private void saveDownloadedFile() { OCFile file = mCurrentDownload.getFile(); - file.setLastSyncDate(System.currentTimeMillis()); + long syncDate = System.currentTimeMillis(); + file.setLastSyncDateForProperties(syncDate); + file.setLastSyncDateForData(syncDate); file.setModificationTimestamp(mCurrentDownload.getModificationTimestamp()); // file.setEtag(mCurrentDownload.getEtag()); // TODO Etag, where available file.setMimetype(mCurrentDownload.getMimeType()); diff --git a/src/com/owncloud/android/files/services/FileObserverService.java b/src/com/owncloud/android/files/services/FileObserverService.java index effa520d..678c46d2 100644 --- a/src/com/owncloud/android/files/services/FileObserverService.java +++ b/src/com/owncloud/android/files/services/FileObserverService.java @@ -27,6 +27,8 @@ import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.db.ProviderMeta.ProviderTableMeta; import com.owncloud.android.files.OwnCloudFileObserver; import com.owncloud.android.files.OwnCloudFileObserver.FileObserverStatusListener; +import com.owncloud.android.operations.RemoteOperationResult; +import com.owncloud.android.operations.RemoteOperationResult.ResultCode; import com.owncloud.android.ui.activity.ConflictsResolveActivity; import com.owncloud.android.utils.FileStorageUtils; @@ -302,10 +304,9 @@ public class FileObserverService extends Service implements FileObserverStatusLi } @Override - public void OnObservedFileStatusUpdate(String localPath, String remotePath, Account account, Status status) { - switch (status) { - case CONFLICT: - { + public void onObservedFileStatusUpdate(String localPath, String remotePath, Account account, RemoteOperationResult result) { + if (!result.isSuccess()) { + if (result.getCode() == ResultCode.SYNC_CONFLICT) { // ISSUE 5: if the user is not running the app (this is a service!), this can be very intrusive; a notification should be preferred Intent i = new Intent(getApplicationContext(), ConflictsResolveActivity.class); i.setFlags(i.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK); @@ -313,14 +314,11 @@ public class FileObserverService extends Service implements FileObserverStatusLi i.putExtra("localpath", localPath); i.putExtra("account", account); startActivity(i); - break; + + } else { + // TODO send notification to the notification bar? } - case SENDING_TO_UPLOADER: - case INCORRECT_MASK: - break; - default: - Log.wtf(TAG, "Unhandled status " + status); - } + } // else, nothing else to do; now it's duty of FileUploader service } private class DownloadCompletedReceiver extends BroadcastReceiver { diff --git a/src/com/owncloud/android/files/services/FileUploader.java b/src/com/owncloud/android/files/services/FileUploader.java index c8916ef2..20c81adf 100644 --- a/src/com/owncloud/android/files/services/FileUploader.java +++ b/src/com/owncloud/android/files/services/FileUploader.java @@ -74,14 +74,16 @@ public class FileUploader extends Service implements OnDatatransferProgressListe public static final String EXTRA_UPLOAD_RESULT = "RESULT"; public static final String EXTRA_REMOTE_PATH = "REMOTE_PATH"; public static final String EXTRA_FILE_PATH = "FILE_PATH"; + public static final String ACCOUNT_NAME = "ACCOUNT_NAME"; public static final String KEY_LOCAL_FILE = "LOCAL_FILE"; public static final String KEY_REMOTE_FILE = "REMOTE_FILE"; + public static final String KEY_MIME_TYPE = "MIME_TYPE"; + public static final String KEY_ACCOUNT = "ACCOUNT"; + public static final String KEY_UPLOAD_TYPE = "UPLOAD_TYPE"; public static final String KEY_FORCE_OVERWRITE = "KEY_FORCE_OVERWRITE"; - public static final String ACCOUNT_NAME = "ACCOUNT_NAME"; - public static final String KEY_MIME_TYPE = "MIME_TYPE"; public static final String KEY_INSTANT_UPLOAD = "INSTANT_UPLOAD"; public static final int UPLOAD_SINGLE_FILE = 0; @@ -211,7 +213,14 @@ public class FileUploader extends Service implements OnDatatransferProgressListe try { for (int i=0; i < localPaths.length; i++) { uploadKey = buildRemoteName(account, remotePaths[i]); - file = obtainNewOCFileToUpload(remotePaths[i], localPaths[i], ((mimeTypes!=null)?mimeTypes[i]:(String)null), isInstant, forceOverwrite, storageManager); + file = storageManager.getFileByLocalPath(remotePaths[i]); + if (file != null) { + Log.d(TAG, "Upload of file already in server: " + remotePaths[i]); + // TODO - review handling of input OCFiles in FileDownloader and FileUploader ; some times retrieving them from database can be necessary, some times not; we should make something consistent + } else { + Log.d(TAG, "Upload of new file: " + remotePaths[i]); + file = obtainNewOCFileToUpload(remotePaths[i], localPaths[i], ((mimeTypes!=null)?mimeTypes[i]:(String)null), isInstant, storageManager); + } if (chunked) { newUpload = new ChunkedUploadFileOperation(account, file, isInstant, forceOverwrite); } else { @@ -427,7 +436,11 @@ public class FileUploader extends Service implements OnDatatransferProgressListe propfind.releaseConnection(); } - if (!result.isSuccess()) { + long syncDate = System.currentTimeMillis(); + if (result.isSuccess()) { + file.setLastSyncDateForProperties(syncDate); + + } else { // file was successfully uploaded, but the new time stamp and Etag in the server could not be read; // just keeping old values :( if (!mCurrentUpload.getRemotePath().equals(file.getRemotePath())) { @@ -436,13 +449,14 @@ public class FileUploader extends Service implements OnDatatransferProgressListe newFile.setCreationTimestamp(file.getCreationTimestamp()); newFile.setFileLength(file.getFileLength()); newFile.setMimetype(file.getMimetype()); - newFile.setModificationTimestamp(file.getModificationTimestamp()); // this is specially BAD + newFile.setModificationTimestamp(file.getModificationTimestamp()); + newFile.setLastSyncDateForProperties(file.getLastSyncDateForProperties()); + newFile.setKeepInSync(file.keepInSync()); // newFile.setEtag(file.getEtag()) // TODO and this is still worse file = newFile; } } - - file.setLastSyncDate(System.currentTimeMillis()); + file.setLastSyncDateForData(syncDate); mStorageManager.saveFile(file); } @@ -472,11 +486,11 @@ public class FileUploader extends Service implements OnDatatransferProgressListe } - private OCFile obtainNewOCFileToUpload(String remotePath, String localPath, String mimeType, boolean isInstant, boolean forceOverwrite, FileDataStorageManager storageManager) { + private OCFile obtainNewOCFileToUpload(String remotePath, String localPath, String mimeType, boolean isInstant, FileDataStorageManager storageManager) { OCFile newFile = new OCFile(remotePath); newFile.setStoragePath(localPath); - newFile.setLastSyncDate(0); - newFile.setKeepInSync(forceOverwrite); + newFile.setLastSyncDateForProperties(0); + newFile.setLastSyncDateForData(0); // size if (localPath != null && localPath.length() > 0) { diff --git a/src/com/owncloud/android/operations/RemoteOperationResult.java b/src/com/owncloud/android/operations/RemoteOperationResult.java index d8fbe460..557c9cd2 100644 --- a/src/com/owncloud/android/operations/RemoteOperationResult.java +++ b/src/com/owncloud/android/operations/RemoteOperationResult.java @@ -44,8 +44,8 @@ import com.owncloud.android.network.CertificateCombinedException; */ public class RemoteOperationResult implements Serializable { - /** Generated - to refresh every time the class changes */ - private static final long serialVersionUID = -7805531062432602444L; + /** Generated - should be refreshed every time the class changes!! */ + private static final long serialVersionUID = 5336333154035462033L; public enum ResultCode { @@ -69,7 +69,8 @@ public class RemoteOperationResult implements Serializable { CANCELLED, INVALID_LOCAL_FILE_NAME, INVALID_OVERWRITE, - CONFLICT + CONFLICT, + SYNC_CONFLICT } private boolean mSuccess = false; diff --git a/src/com/owncloud/android/operations/RenameFileOperation.java b/src/com/owncloud/android/operations/RenameFileOperation.java index ada17432..483a312e 100644 --- a/src/com/owncloud/android/operations/RenameFileOperation.java +++ b/src/com/owncloud/android/operations/RenameFileOperation.java @@ -191,7 +191,8 @@ public class RenameFileOperation extends RemoteOperation { file.setFileId(mFile.getFileId()); file.setFileLength(mFile.getFileLength()); file.setKeepInSync(mFile.keepInSync()); - file.setLastSyncDate(mFile.getLastSyncDate()); + file.setLastSyncDateForProperties(mFile.getLastSyncDateForProperties()); + file.setLastSyncDateForData(mFile.getLastSyncDateForData()); file.setMimetype(mFile.getMimetype()); file.setModificationTimestamp(mFile.getModificationTimestamp()); file.setParentId(mFile.getParentId()); diff --git a/src/com/owncloud/android/operations/SynchronizeFileOperation.java b/src/com/owncloud/android/operations/SynchronizeFileOperation.java index b25fa1b8..71c6d437 100644 --- a/src/com/owncloud/android/operations/SynchronizeFileOperation.java +++ b/src/com/owncloud/android/operations/SynchronizeFileOperation.java @@ -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,8 +155,40 @@ 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. * * @param we WebDAV entry read from the server for a WebDAV resource (remote file or folder). @@ -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; + } + } diff --git a/src/com/owncloud/android/operations/SynchronizeFolderOperation.java b/src/com/owncloud/android/operations/SynchronizeFolderOperation.java index 08ddf7ee..28a2def4 100644 --- a/src/com/owncloud/android/operations/SynchronizeFolderOperation.java +++ b/src/com/owncloud/android/operations/SynchronizeFolderOperation.java @@ -151,7 +151,7 @@ public class SynchronizeFolderOperation extends RemoteOperation { String currentSavePath = FileStorageUtils.getSavePath(mAccount.name); for (int i=0; i < mChildren.size(); ) { file = mChildren.get(i); - if (file.getLastSyncDate() != mCurrentSyncTime) { + if (file.getLastSyncDateForProperties() != mCurrentSyncTime) { Log.d(TAG, "removing file: " + file); mStorageManager.removeFile(file, (file.isDown() && file.getStoragePath().startsWith(currentSavePath))); mChildren.remove(i); @@ -199,7 +199,7 @@ public class SynchronizeFolderOperation extends RemoteOperation { file.setFileLength(we.contentLength()); file.setMimetype(we.contentType()); file.setModificationTimestamp(we.modifiedTimesamp()); - file.setLastSyncDate(mCurrentSyncTime); + file.setLastSyncDateForProperties(mCurrentSyncTime); return file; } diff --git a/src/com/owncloud/android/providers/FileContentProvider.java b/src/com/owncloud/android/providers/FileContentProvider.java index 980e0457..c99616ef 100644 --- a/src/com/owncloud/android/providers/FileContentProvider.java +++ b/src/com/owncloud/android/providers/FileContentProvider.java @@ -70,6 +70,8 @@ public class FileContentProvider extends ContentProvider { ProviderTableMeta.FILE_STORAGE_PATH); mProjectionMap.put(ProviderTableMeta.FILE_LAST_SYNC_DATE, ProviderTableMeta.FILE_LAST_SYNC_DATE); + mProjectionMap.put(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA, + ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA); mProjectionMap.put(ProviderTableMeta.FILE_KEEP_IN_SYNC, ProviderTableMeta.FILE_KEEP_IN_SYNC); mProjectionMap.put(ProviderTableMeta.FILE_ACCOUNT_OWNER, @@ -221,18 +223,31 @@ public class FileContentProvider extends ContentProvider { + ProviderTableMeta.FILE_STORAGE_PATH + " TEXT, " + ProviderTableMeta.FILE_ACCOUNT_OWNER + " TEXT, " + ProviderTableMeta.FILE_LAST_SYNC_DATE + " INTEGER, " - + ProviderTableMeta.FILE_KEEP_IN_SYNC + " INTEGER );"); + + ProviderTableMeta.FILE_KEEP_IN_SYNC + " INTEGER, " + + ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA + " INTEGER );" + ); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { Log.i("SQL", "Entering in onUpgrade"); + boolean upgraded = false; if (oldVersion == 1 && newVersion >= 2) { - Log.i("SQL", "Entering in the ADD in onUpgrade"); + Log.i("SQL", "Entering in the #1 ADD in onUpgrade"); db.execSQL("ALTER TABLE " + ProviderTableMeta.DB_NAME + " ADD COLUMN " + ProviderTableMeta.FILE_KEEP_IN_SYNC + " INTEGER " + " DEFAULT 0"); - } else Log.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion + ", newVersion == " + newVersion); + upgraded = true; + } + if (oldVersion < 3 && newVersion >= 3) { + Log.i("SQL", "Entering in the #2 ADD in onUpgrade"); + db.execSQL("ALTER TABLE " + ProviderTableMeta.DB_NAME + + " ADD COLUMN " + ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA + " INTEGER " + + " DEFAULT 0"); + upgraded = true; + } + if (!upgraded) + Log.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion + ", newVersion == " + newVersion); } } diff --git a/src/com/owncloud/android/ui/fragment/FileDetailFragment.java b/src/com/owncloud/android/ui/fragment/FileDetailFragment.java index 76a387fd..01fc1866 100644 --- a/src/com/owncloud/android/ui/fragment/FileDetailFragment.java +++ b/src/com/owncloud/android/ui/fragment/FileDetailFragment.java @@ -82,6 +82,8 @@ import com.owncloud.android.operations.RemoteOperationResult; import com.owncloud.android.operations.RemoteOperationResult.ResultCode; import com.owncloud.android.operations.RemoveFileOperation; import com.owncloud.android.operations.RenameFileOperation; +import com.owncloud.android.operations.SynchronizeFileOperation; +import com.owncloud.android.ui.activity.ConflictsResolveActivity; import com.owncloud.android.ui.activity.FileDetailActivity; import com.owncloud.android.ui.activity.FileDisplayActivity; import com.owncloud.android.ui.activity.TransferServiceGetter; @@ -119,7 +121,7 @@ public class FileDetailFragment extends SherlockFragment implements private Handler mHandler; private RemoteOperation mLastRemoteOperation; - private static final String TAG = "FileDetailFragment"; + private static final String TAG = FileDetailFragment.class.getSimpleName(); public static final String FTAG = "FileDetails"; public static final String FTAG_CONFIRMATION = "REMOVE_CONFIRMATION_FRAGMENT"; @@ -255,6 +257,7 @@ public class FileDetailFragment extends SherlockFragment implements @Override public void onClick(View v) { + FileDataStorageManager fdsm = new FileDataStorageManager(mAccount, getActivity().getApplicationContext().getContentResolver()); switch (v.getId()) { case R.id.fdDownloadBtn: { //if (FileDownloader.isDownloading(mAccount, mFile.getRemotePath())) { @@ -289,26 +292,21 @@ public class FileDetailFragment extends SherlockFragment implements } } else { - // ISSUE 6: this button should be promoted to 'synchronize' if the file is DOWN, not just redownload - Intent i = new Intent(getActivity(), FileDownloader.class); - i.putExtra(FileDownloader.EXTRA_ACCOUNT, mAccount); - i.putExtra(FileDownloader.EXTRA_FILE, mFile); - /*i.putExtra(FileDownloader.EXTRA_REMOTE_PATH, mFile.getRemotePath()); - i.putExtra(FileDownloader.EXTRA_FILE_PATH, mFile.getRemotePath()); - i.putExtra(FileDownloader.EXTRA_FILE_SIZE, mFile.getFileLength());*/ + mLastRemoteOperation = new SynchronizeFileOperation(mFile.getRemotePath(), fdsm, mAccount, true, false, getActivity()); + WebdavClient wc = OwnCloudClientUtils.createOwnCloudClient(mAccount, getSherlockActivity().getApplicationContext()); + mLastRemoteOperation.execute(wc, this, mHandler); // update ui - setButtonsForTransferring(); - - getActivity().startService(i); - mContainerActivity.onFileStateChanged(); // this is not working; it is performed before the fileDownloadService registers it as 'in progress' + boolean inDisplayActivity = getActivity() instanceof FileDisplayActivity; + getActivity().showDialog((inDisplayActivity)? FileDisplayActivity.DIALOG_SHORT_WAIT : FileDetailActivity.DIALOG_SHORT_WAIT); + setButtonsForTransferring(); // disable button immediately, although the synchronization does not result in a file transference + } break; } case R.id.fdKeepInSync: { CheckBox cb = (CheckBox) getView().findViewById(R.id.fdKeepInSync); mFile.setKeepInSync(cb.isChecked()); - FileDataStorageManager fdsm = new FileDataStorageManager(mAccount, getActivity().getApplicationContext().getContentResolver()); fdsm.saveFile(mFile); /* NOT HERE @@ -923,6 +921,9 @@ public class FileDetailFragment extends SherlockFragment implements } else if (operation instanceof RenameFileOperation) { onRenameFileOperationFinish((RenameFileOperation)operation, result); + + } else if (operation instanceof SynchronizeFileOperation) { + onSynchronizeFileOperationFinish((SynchronizeFileOperation)operation, result); } } } @@ -977,5 +978,39 @@ public class FileDetailFragment extends SherlockFragment implements } } + private void onSynchronizeFileOperationFinish(SynchronizeFileOperation operation, RemoteOperationResult result) { + boolean inDisplayActivity = getActivity() instanceof FileDisplayActivity; + getActivity().dismissDialog((inDisplayActivity)? FileDisplayActivity.DIALOG_SHORT_WAIT : FileDetailActivity.DIALOG_SHORT_WAIT); + + if (!result.isSuccess()) { + if (result.getCode() == ResultCode.SYNC_CONFLICT) { + Intent i = new Intent(getActivity(), ConflictsResolveActivity.class); + //i.setFlags(i.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK); + i.putExtra("remotepath", mFile.getRemotePath()); + i.putExtra("localpath", mFile.getStoragePath()); + i.putExtra("account", mAccount); + startActivity(i); + + } else { + Toast msg = Toast.makeText(getActivity(), R.string.sync_file_fail_msg, Toast.LENGTH_LONG); + msg.show(); + } + + } else { + if (operation.transferWasRequested()) { + mContainerActivity.onFileStateChanged(); // this is not working; FileDownloader won't do NOTHING at all until this method finishes, so + // checking the service to see if the file is downloading results in FALSE + } else { + Toast msg = Toast.makeText(getActivity(), R.string.sync_file_nothing_to_do_msg, Toast.LENGTH_LONG); + msg.show(); + if (mFile.isDown()) { + setButtonsForDown(); + + } else { + setButtonsForRemote(); + } + } + } + } }