Fixed some bugs in the update of OCFile#mLastSyncDateForProperties at account synchronization time ; and some refactoring

This commit is contained in:
David A. Velasco 2012-11-19 15:08:48 +01:00
parent c301865c35
commit 22a789e8d2
6 changed files with 110 additions and 157 deletions

View file

@ -124,10 +124,6 @@ public class FileDataStorageManager implements DataStorageManager {
if (fileExists(file.getRemotePath())) { if (fileExists(file.getRemotePath())) {
OCFile oldFile = getFileByPath(file.getRemotePath()); OCFile oldFile = getFileByPath(file.getRemotePath());
if (file.getStoragePath() == null && oldFile.isDown())
file.setStoragePath(oldFile.getStoragePath());
if (!file.isDirectory());
cv.put(ProviderTableMeta.FILE_STORAGE_PATH, file.getStoragePath());
file.setFileId(oldFile.getFileId()); file.setFileId(oldFile.getFileId());
overriden = true; overriden = true;
@ -203,12 +199,8 @@ public class FileDataStorageManager implements DataStorageManager {
cv.put(ProviderTableMeta.FILE_KEEP_IN_SYNC, file.keepInSync() ? 1 : 0); cv.put(ProviderTableMeta.FILE_KEEP_IN_SYNC, file.keepInSync() ? 1 : 0);
if (fileExists(file.getRemotePath())) { if (fileExists(file.getRemotePath())) {
OCFile tmpfile = getFileByPath(file.getRemotePath()); OCFile oldFile = getFileByPath(file.getRemotePath());
file.setStoragePath(tmpfile.getStoragePath()); file.setFileId(oldFile.getFileId());
if (!file.isDirectory());
cv.put(ProviderTableMeta.FILE_STORAGE_PATH, file.getStoragePath());
file.setFileId(tmpfile.getFileId());
operations.add(ContentProviderOperation.newUpdate(ProviderTableMeta.CONTENT_URI). operations.add(ContentProviderOperation.newUpdate(ProviderTableMeta.CONTENT_URI).
withValues(cv). withValues(cv).
withSelection( ProviderTableMeta._ID + "=?", withSelection( ProviderTableMeta._ID + "=?",
@ -391,10 +383,12 @@ public class FileDataStorageManager implements DataStorageManager {
file.setStoragePath(c.getString(c file.setStoragePath(c.getString(c
.getColumnIndex(ProviderTableMeta.FILE_STORAGE_PATH))); .getColumnIndex(ProviderTableMeta.FILE_STORAGE_PATH)));
if (file.getStoragePath() == null) { if (file.getStoragePath() == null) {
// try to find existing file and bind it with current account // try to find existing file and bind it with current account; - with the current update of SynchronizeFolderOperation, this won't be necessary anymore after a full synchronization of the account
File f = new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, file)); File f = new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, file));
if (f.exists()) if (f.exists()) {
file.setStoragePath(f.getAbsolutePath()); file.setStoragePath(f.getAbsolutePath());
file.setLastSyncDateForData(f.lastModified());
}
} }
} }
file.setFileLength(c.getLong(c file.setFileLength(c.getLong(c

View file

@ -19,19 +19,21 @@
package com.owncloud.android.files; package com.owncloud.android.files;
import java.io.File; import java.io.File;
import java.util.LinkedList;
import java.util.List;
import com.owncloud.android.datamodel.DataStorageManager; import com.owncloud.android.datamodel.FileDataStorageManager;
import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.network.OwnCloudClientUtils; import com.owncloud.android.network.OwnCloudClientUtils;
import com.owncloud.android.operations.RemoteOperationResult; import com.owncloud.android.operations.RemoteOperationResult;
import com.owncloud.android.operations.SynchronizeFileOperation; import com.owncloud.android.operations.SynchronizeFileOperation;
import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
import com.owncloud.android.ui.activity.ConflictsResolveActivity;
import com.owncloud.android.utils.FileStorageUtils;
import eu.alefzero.webdav.WebdavClient; import eu.alefzero.webdav.WebdavClient;
import android.accounts.Account; import android.accounts.Account;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.os.FileObserver; import android.os.FileObserver;
import android.util.Log; import android.util.Log;
@ -40,51 +42,31 @@ public class OwnCloudFileObserver extends FileObserver {
public static int CHANGES_ONLY = CLOSE_WRITE; public static int CHANGES_ONLY = CLOSE_WRITE;
private static String TAG = OwnCloudFileObserver.class.getSimpleName(); private static String TAG = OwnCloudFileObserver.class.getSimpleName();
private String mPath; private String mPath;
private int mMask; private int mMask;
private DataStorageManager mStorage;
private Account mOCAccount; private Account mOCAccount;
private OCFile mFile; private OCFile mFile;
private Context mContext; private Context mContext;
private List<FileObserverStatusListener> mListeners;
public OwnCloudFileObserver(String path) {
this(path, ALL_EVENTS);
}
public OwnCloudFileObserver(String path, int mask) {
super(path, mask);
mPath = path;
mMask = mask;
mListeners = new LinkedList<FileObserverStatusListener>();
}
public void setAccount(Account account) {
mOCAccount = account;
}
public void setStorageManager(DataStorageManager manager) {
mStorage = manager;
}
public void setOCFile(OCFile file) {
mFile = file;
}
public void setContext(Context context) {
mContext = context;
}
public String getPath() {
return mPath;
}
public String getRemotePath() { public OwnCloudFileObserver(String path, OCFile file, Account account, Context context, int mask) {
return mFile.getRemotePath(); super(path, mask);
} if (path == null)
throw new IllegalArgumentException("NULL path argument received");
public void addObserverStatusListener(FileObserverStatusListener listener) { if (file == null)
mListeners.add(listener); throw new IllegalArgumentException("NULL file argument received");
if (account == null)
throw new IllegalArgumentException("NULL account argument received");
if (context == null)
throw new IllegalArgumentException("NULL context argument received");
if (!path.equals(file.getStoragePath()) && !path.equals(FileStorageUtils.getDefaultSavePathFor(account.name, file)))
throw new IllegalArgumentException("File argument is not linked to the local file set in path argument");
mPath = path;
mFile = file;
mOCAccount = account;
mContext = context;
mMask = mask;
} }
@Override @Override
@ -94,34 +76,29 @@ public class OwnCloudFileObserver extends FileObserver {
Log.wtf(TAG, "Incorrect event " + event + " sent for file " + mPath + ((path != null) ? File.separator + path : "") + Log.wtf(TAG, "Incorrect event " + event + " sent for file " + mPath + ((path != null) ? File.separator + path : "") +
" with registered for " + mMask + " and original path " + " with registered for " + mMask + " and original path " +
mPath); 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; return;
} }
WebdavClient wc = OwnCloudClientUtils.createOwnCloudClient(mOCAccount, mContext); WebdavClient wc = OwnCloudClientUtils.createOwnCloudClient(mOCAccount, mContext);
SynchronizeFileOperation sfo = new SynchronizeFileOperation(mFile, null, mStorage, mOCAccount, true, false, mContext); SynchronizeFileOperation sfo = new SynchronizeFileOperation(mFile,
null,
new FileDataStorageManager(mOCAccount, mContext.getContentResolver()),
mOCAccount,
true,
true,
mContext);
RemoteOperationResult result = sfo.execute(wc); RemoteOperationResult result = sfo.execute(wc);
for (FileObserverStatusListener l : mListeners) { if (result.getCode() == ResultCode.SYNC_CONFLICT) {
l.onObservedFileStatusUpdate(mPath, getRemotePath(), mOCAccount, result); // 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(mContext, ConflictsResolveActivity.class);
i.setFlags(i.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
i.putExtra("remotepath", mFile.getRemotePath());
i.putExtra("localpath", mPath);
i.putExtra("account", mOCAccount);
mContext.startActivity(i);
} }
// TODO save other errors in some point where the user can inspect them later;
} // or maybe just toast them;
// or nothing, very strange fails
public interface FileObserverStatusListener {
public void onObservedFileStatusUpdate(String localPath,
String remotePath,
Account account,
RemoteOperationResult result);
}
public OCFile getOCFile() {
return mFile;
}
public Account getAccount() {
return mOCAccount;
} }
} }

View file

@ -26,11 +26,7 @@ import com.owncloud.android.datamodel.FileDataStorageManager;
import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.db.ProviderMeta.ProviderTableMeta; import com.owncloud.android.db.ProviderMeta.ProviderTableMeta;
import com.owncloud.android.files.OwnCloudFileObserver; 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.operations.SynchronizeFileOperation; import com.owncloud.android.operations.SynchronizeFileOperation;
import com.owncloud.android.ui.activity.ConflictsResolveActivity;
import com.owncloud.android.utils.FileStorageUtils; import com.owncloud.android.utils.FileStorageUtils;
import android.accounts.Account; import android.accounts.Account;
@ -45,7 +41,7 @@ import android.os.Binder;
import android.os.IBinder; import android.os.IBinder;
import android.util.Log; import android.util.Log;
public class FileObserverService extends Service implements FileObserverStatusListener { public class FileObserverService extends Service {
public final static int CMD_INIT_OBSERVED_LIST = 1; public final static int CMD_INIT_OBSERVED_LIST = 1;
public final static int CMD_ADD_OBSERVED_FILE = 2; public final static int CMD_ADD_OBSERVED_FILE = 2;
@ -77,7 +73,7 @@ public class FileObserverService extends Service implements FileObserverStatusLi
registerReceiver(mDownloadReceiver, filter); registerReceiver(mDownloadReceiver, filter);
mObserversMap = new HashMap<String, OwnCloudFileObserver>(); mObserversMap = new HashMap<String, OwnCloudFileObserver>();
initializeObservedList(); //initializeObservedList();
} }
@ -86,6 +82,7 @@ public class FileObserverService extends Service implements FileObserverStatusLi
super.onDestroy(); super.onDestroy();
unregisterReceiver(mDownloadReceiver); unregisterReceiver(mDownloadReceiver);
mObserversMap = null; // TODO study carefully the life cycle of Services to grant the best possible observance mObserversMap = null; // TODO study carefully the life cycle of Services to grant the best possible observance
Log.d(TAG, "Bye, bye");
} }
@ -159,12 +156,11 @@ public class FileObserverService extends Service implements FileObserverStatusLi
String path = c.getString(c.getColumnIndex(ProviderTableMeta.FILE_STORAGE_PATH)); String path = c.getString(c.getColumnIndex(ProviderTableMeta.FILE_STORAGE_PATH));
OwnCloudFileObserver observer = OwnCloudFileObserver observer =
new OwnCloudFileObserver(path, OwnCloudFileObserver.CHANGES_ONLY); new OwnCloudFileObserver( path,
observer.setContext(getApplicationContext()); storage.getFileByPath(c.getString(c.getColumnIndex(ProviderTableMeta.FILE_PATH))),
observer.setAccount(account); account,
observer.setStorageManager(storage); getApplicationContext(),
observer.setOCFile(storage.getFileByPath(c.getString(c.getColumnIndex(ProviderTableMeta.FILE_PATH)))); OwnCloudFileObserver.CHANGES_ONLY);
observer.addObserverStatusListener(this);
mObserversMap.put(path, observer); mObserversMap.put(path, observer);
if (new File(path).exists()) { if (new File(path).exists()) {
observer.startWatching(); observer.startWatching();
@ -175,6 +171,7 @@ public class FileObserverService extends Service implements FileObserverStatusLi
c.close(); c.close();
} }
/** /**
* Registers the local copy of a remote file to be observed for local changes, * Registers the local copy of a remote file to be observed for local changes,
* an automatically updated in the ownCloud server. * an automatically updated in the ownCloud server.
@ -200,17 +197,11 @@ public class FileObserverService extends Service implements FileObserverStatusLi
OwnCloudFileObserver observer = mObserversMap.get(localPath); OwnCloudFileObserver observer = mObserversMap.get(localPath);
if (observer == null) { if (observer == null) {
/// the local file was never registered to observe before /// the local file was never registered to observe before
observer = new OwnCloudFileObserver(localPath, OwnCloudFileObserver.CHANGES_ONLY); observer = new OwnCloudFileObserver( localPath,
//Account account = AccountUtils.getCurrentOwnCloudAccount(getApplicationContext()); file,
observer.setAccount(account); account,
FileDataStorageManager storage = getApplicationContext(),
new FileDataStorageManager(account, getContentResolver()); // I don't trust in this resolver's life span... OwnCloudFileObserver.CHANGES_ONLY);
observer.setStorageManager(storage);
//observer.setOCFile(storage.getFileByLocalPath(path)); // ISSUE 10 - the fix in FileDetailsFragment to avoid path == null was not enough; it the file was never down before, this sets a NULL OCFile in the observer
observer.setOCFile(file);
observer.addObserverStatusListener(this);
observer.setContext(getApplicationContext());
mObserversMap.put(localPath, observer); mObserversMap.put(localPath, observer);
Log.d(TAG, "Observer added for path " + localPath); Log.d(TAG, "Observer added for path " + localPath);
@ -254,26 +245,6 @@ public class FileObserverService extends Service implements FileObserverStatusLi
} }
@Override
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);
i.putExtra("remotepath", remotePath);
i.putExtra("localpath", localPath);
i.putExtra("account", account);
startActivity(i);
} else {
// TODO send notification to the notification bar?
}
} // else, nothing else to do; now it's duty of FileUploader service
}
/** /**
* Private receiver listening to events broadcast by the FileDownloader service. * Private receiver listening to events broadcast by the FileDownloader service.

View file

@ -419,12 +419,14 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
private void saveUploadedFile() { private void saveUploadedFile() {
OCFile file = mCurrentUpload.getFile(); OCFile file = mCurrentUpload.getFile();
/// new PROPFIND to keep data consistent with server in theory, should return the same we already have
PropFindMethod propfind = null; PropFindMethod propfind = null;
RemoteOperationResult result = null; RemoteOperationResult result = null;
long syncDate = System.currentTimeMillis();
try { try {
propfind = new PropFindMethod(mUploadClient.getBaseUri() + WebdavUtils.encodePath(mCurrentUpload.getRemotePath())); propfind = new PropFindMethod(mUploadClient.getBaseUri() + WebdavUtils.encodePath(mCurrentUpload.getRemotePath()));
int status = mUploadClient.executeMethod(propfind); int status = mUploadClient.executeMethod(propfind);
boolean isMultiStatus = status == HttpStatus.SC_MULTI_STATUS; boolean isMultiStatus = (status == HttpStatus.SC_MULTI_STATUS);
if (isMultiStatus) { if (isMultiStatus) {
MultiStatus resp = propfind.getResponseBodyAsMultiStatus(); MultiStatus resp = propfind.getResponseBodyAsMultiStatus();
WebdavEntry we = new WebdavEntry(resp.getResponses()[0], WebdavEntry we = new WebdavEntry(resp.getResponses()[0],
@ -432,10 +434,10 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
OCFile newFile = fillOCFile(we); OCFile newFile = fillOCFile(we);
newFile.setStoragePath(file.getStoragePath()); newFile.setStoragePath(file.getStoragePath());
newFile.setKeepInSync(file.keepInSync()); newFile.setKeepInSync(file.keepInSync());
newFile.setLastSyncDateForProperties(syncDate);
file = newFile; file = newFile;
} else { } else {
// this would be a problem
mUploadClient.exhaustResponse(propfind.getResponseBodyAsStream()); mUploadClient.exhaustResponse(propfind.getResponseBodyAsStream());
} }
@ -451,27 +453,22 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
propfind.releaseConnection(); propfind.releaseConnection();
} }
long syncDate = System.currentTimeMillis(); file.setLastSyncDateForData(syncDate); // this is right, no matter if the PROPFIND was successful or not
if (result.isSuccess()) {
file.setLastSyncDateForProperties(syncDate); if (!result.isSuccess() && !mCurrentUpload.getRemotePath().equals(file.getRemotePath())) {
// true when the file was automatically renamed to avoid an overwrite ; yes, this is a bit obscure...
} else { OCFile newFile = new OCFile(mCurrentUpload.getRemotePath());
// file was successfully uploaded, but the new time stamp and Etag in the server could not be read; newFile.setCreationTimestamp(file.getCreationTimestamp());
// just keeping old values :( newFile.setFileLength(file.getFileLength());
if (!mCurrentUpload.getRemotePath().equals(file.getRemotePath())) { newFile.setMimetype(file.getMimetype());
// true when the file was automatically renamed to avoid an overwrite newFile.setModificationTimestamp(file.getModificationTimestamp());
OCFile newFile = new OCFile(mCurrentUpload.getRemotePath()); newFile.setLastSyncDateForProperties(file.getLastSyncDateForProperties());
newFile.setCreationTimestamp(file.getCreationTimestamp()); newFile.setStoragePath(file.getStoragePath());
newFile.setFileLength(file.getFileLength()); newFile.setKeepInSync(file.keepInSync());
newFile.setMimetype(file.getMimetype()); // newFile.setEtag(file.getEtag())
newFile.setModificationTimestamp(file.getModificationTimestamp()); file = newFile;
newFile.setLastSyncDateForProperties(file.getLastSyncDateForProperties());
newFile.setKeepInSync(file.keepInSync());
// newFile.setEtag(file.getEtag()) // TODO and this is still worse
file = newFile;
}
} }
file.setLastSyncDateForData(syncDate);
mStorageManager.saveFile(file); mStorageManager.saveFile(file);
} }

View file

@ -95,6 +95,7 @@ public class SynchronizeFileOperation extends RemoteOperation {
WebdavEntry we = new WebdavEntry(resp.getResponses()[0], WebdavEntry we = new WebdavEntry(resp.getResponses()[0],
client.getBaseUri().getPath()); client.getBaseUri().getPath());
mServerFile = fillOCFile(we); mServerFile = fillOCFile(we);
mServerFile.setLastSyncDateForProperties(System.currentTimeMillis());
} else { } else {
client.exhaustResponse(propfind.getResponseBodyAsStream()); client.exhaustResponse(propfind.getResponseBodyAsStream());
@ -137,6 +138,8 @@ public class SynchronizeFileOperation extends RemoteOperation {
} else { } else {
// TODO CHECK: is this really useful in some point in the code? // TODO CHECK: is this really useful in some point in the code?
mServerFile.setKeepInSync(mLocalFile.keepInSync()); mServerFile.setKeepInSync(mLocalFile.keepInSync());
mServerFile.setLastSyncDateForData(mLocalFile.getLastSyncDateForData());
mServerFile.setStoragePath(mLocalFile.getStoragePath());
mServerFile.setParentId(mLocalFile.getParentId()); mServerFile.setParentId(mLocalFile.getParentId());
mStorageManager.saveFile(mServerFile); mStorageManager.saveFile(mServerFile);
@ -210,8 +213,6 @@ public class SynchronizeFileOperation extends RemoteOperation {
file.setFileLength(we.contentLength()); file.setFileLength(we.contentLength());
file.setMimetype(we.contentType()); file.setMimetype(we.contentType());
file.setModificationTimestamp(we.modifiedTimesamp()); file.setModificationTimestamp(we.modifiedTimesamp());
file.setLastSyncDateForProperties(System.currentTimeMillis());
file.setLastSyncDateForData(0);
return file; return file;
} }

View file

@ -18,6 +18,7 @@
package com.owncloud.android.operations; package com.owncloud.android.operations;
import java.io.File;
import java.util.List; import java.util.List;
import java.util.Vector; import java.util.Vector;
@ -138,27 +139,40 @@ public class SynchronizeFolderOperation extends RemoteOperation {
List<OCFile> updatedFiles = new Vector<OCFile>(resp.getResponses().length - 1); List<OCFile> updatedFiles = new Vector<OCFile>(resp.getResponses().length - 1);
List<SynchronizeFileOperation> filesToSyncContents = new Vector<SynchronizeFileOperation>(); List<SynchronizeFileOperation> filesToSyncContents = new Vector<SynchronizeFileOperation>();
for (int i = 1; i < resp.getResponses().length; ++i) { for (int i = 1; i < resp.getResponses().length; ++i) {
/// new OCFile instance with the data from the server
WebdavEntry we = new WebdavEntry(resp.getResponses()[i], client.getBaseUri().getPath()); WebdavEntry we = new WebdavEntry(resp.getResponses()[i], client.getBaseUri().getPath());
OCFile file = fillOCFile(we); OCFile file = fillOCFile(we);
/// set data about local state, keeping unchanged former data if existing
file.setLastSyncDateForProperties(mCurrentSyncTime);
OCFile oldFile = mStorageManager.getFileByPath(file.getRemotePath()); OCFile oldFile = mStorageManager.getFileByPath(file.getRemotePath());
if (oldFile != null) { if (oldFile != null) {
file.setKeepInSync(oldFile.keepInSync()); file.setKeepInSync(oldFile.keepInSync());
file.setLastSyncDateForData(oldFile.getLastSyncDateForData()); file.setLastSyncDateForData(oldFile.getLastSyncDateForData());
if (file.keepInSync()) { file.setStoragePath(oldFile.getStoragePath());
//disableObservance(file); // first disable observer so we won't get file upload right after download }
// // now, the FileDownloader service sends a broadcast before start a download; the FileObserverService is listening for it
//requestFileSynchronization(file, oldFile, client); /// scan default location if local copy of file is not linked in OCFile instance
SynchronizeFileOperation operation = new SynchronizeFileOperation( oldFile, if (file.getStoragePath() == null && !file.isDirectory()) {
file, File f = new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, file));
mStorageManager, if (f.exists()) {
mAccount, file.setStoragePath(f.getAbsolutePath());
true, file.setLastSyncDateForData(f.lastModified());
false,
mContext
);
filesToSyncContents.add(operation);
} }
} }
/// prepare content synchronization for kept-in-sync files
if (file.keepInSync()) {
SynchronizeFileOperation operation = new SynchronizeFileOperation( oldFile,
file,
mStorageManager,
mAccount,
true,
false,
mContext
);
filesToSyncContents.add(operation);
}
updatedFiles.add(file); updatedFiles.add(file);
} }
@ -250,7 +264,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.modifiedTimesamp()); file.setModificationTimestamp(we.modifiedTimesamp());
file.setLastSyncDateForProperties(mCurrentSyncTime);
file.setParentId(mParentId); file.setParentId(mParentId);
return file; return file;
} }