diff --git a/src/com/owncloud/android/files/OwnCloudFileObserver.java b/src/com/owncloud/android/files/OwnCloudFileObserver.java index 551bc25f78..5d8e0b07b0 100644 --- a/src/com/owncloud/android/files/OwnCloudFileObserver.java +++ b/src/com/owncloud/android/files/OwnCloudFileObserver.java @@ -20,92 +20,129 @@ package com.owncloud.android.files; import java.io.File; -import com.owncloud.android.datamodel.FileDataStorageManager; -import com.owncloud.android.datamodel.OCFile; -import com.owncloud.android.lib.common.operations.RemoteOperationResult; -import com.owncloud.android.operations.SynchronizeFileOperation; -import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode; -import com.owncloud.android.ui.activity.ConflictsResolveActivity; -import com.owncloud.android.utils.Log_OC; - import android.accounts.Account; import android.content.Context; import android.content.Intent; import android.os.FileObserver; +import android.os.Handler; + +import com.owncloud.android.datamodel.FileDataStorageManager; +import com.owncloud.android.datamodel.OCFile; +import com.owncloud.android.lib.common.operations.RemoteOperationResult; +import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode; +import com.owncloud.android.operations.SynchronizeFileOperation; +import com.owncloud.android.ui.activity.ConflictsResolveActivity; +import com.owncloud.android.utils.Log_OC; public class OwnCloudFileObserver extends FileObserver { private static int MASK = (FileObserver.MODIFY | FileObserver.CLOSE_WRITE); - + private static int IN_IGNORE = 32768; + private static String TAG = OwnCloudFileObserver.class.getSimpleName(); - + private String mPath; private int mMask; private Account mOCAccount; private Context mContext; private boolean mModified; + private long mFileLastModified; + private boolean mRestartWatching; + private Handler mHandler; - - public OwnCloudFileObserver(String path, Account account, Context context) { - super(path, MASK); + public OwnCloudFileObserver(String path, Account account, Context context, Handler handler) { + super(path, FileObserver.ALL_EVENTS); if (path == null) - throw new IllegalArgumentException("NULL path argument received"); + throw new IllegalArgumentException("NULL path argument received"); if (account == null) - throw new IllegalArgumentException("NULL account argument received"); + throw new IllegalArgumentException("NULL account argument received"); if (context == null) throw new IllegalArgumentException("NULL context argument received"); mPath = path; mOCAccount = account; - mContext = context; + mContext = context; mModified = false; + mFileLastModified = new File(path).lastModified(); + mHandler = handler; + Log_OC.d(TAG, "Create Observer - FileLastModified: " + mFileLastModified); } - - + @Override public void onEvent(int event, String path) { - Log_OC.d(TAG, "Got file modified with event " + event + " and path " + mPath + ((path != null) ? File.separator + path : "")); + Log_OC.d(TAG, "Got file modified with event " + event + " and path " + mPath + + ((path != null) ? File.separator + path : "")); if ((event & MASK) == 0) { - Log_OC.wtf(TAG, "Incorrect event " + event + " sent for file " + mPath + ((path != null) ? File.separator + path : "") + - " with registered for " + mMask + " and original path " + - mPath); + Log_OC.wtf(TAG, "Incorrect event " + event + " sent for file " + mPath + + ((path != null) ? File.separator + path : "") + " with registered for " + mMask + + " and original path " + mPath); + + // in case need start watching again + if ((event & IN_IGNORE) != 0 && mRestartWatching) { + mRestartWatching = false; + + mHandler.postDelayed(new Runnable() { + public void run() { + startWatching(); + } + }, 5000); + + } } else { if ((event & FileObserver.MODIFY) != 0) { // file changed mModified = true; } - // not sure if it's possible, but let's assume that both kind of events can be received at the same time + // not sure if it's possible, but let's assume that both kind of + // events can be received at the same time if ((event & FileObserver.CLOSE_WRITE) != 0) { // file closed if (mModified) { mModified = false; + mRestartWatching = false; + startSyncOperation(); + } else if (isFileUpdated()) { + // if file has been modified but Modify event type has not + // been launched + mRestartWatching = true; + mFileLastModified = new File(mPath).lastModified(); + Log_OC.d(TAG, "CLOSE_WRITE - New FileLastModified: " + mFileLastModified); startSyncOperation(); } } - } + } } - private void startSyncOperation() { FileDataStorageManager storageManager = new FileDataStorageManager(mOCAccount, mContext.getContentResolver()); - OCFile file = storageManager.getFileByLocalPath(mPath); // a fresh object is needed; many things could have occurred to the file since it was registered to observe - // again, assuming that local files are linked to a remote file AT MOST, SOMETHING TO BE DONE; - SynchronizeFileOperation sfo = new SynchronizeFileOperation(file, - null, - mOCAccount, - true, - mContext); + // a fresh object is needed; many things could have occurred to the file + // since it was registered to observe again, assuming that local files + // are linked to a remote file AT MOST, SOMETHING TO BE DONE; + OCFile file = storageManager.getFileByLocalPath(mPath); + SynchronizeFileOperation sfo = new SynchronizeFileOperation(file, null, mOCAccount, true, mContext); RemoteOperationResult result = sfo.execute(storageManager, mContext); 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 + // 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(ConflictsResolveActivity.EXTRA_FILE, file); i.putExtra(ConflictsResolveActivity.EXTRA_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 + // TODO save other errors in some point where the user can inspect them + // later; + // or maybe just toast them; + // or nothing, very strange fails + } + + /** + * Check if the timestamp of last file modification in local is more current + * that the timestamp when setting observer to the file + * + * @return boolean: True if file is updated, False if not + */ + private boolean isFileUpdated() { + Log_OC.d(TAG, "FileLastModified: " + mFileLastModified); + return (new File(mPath).lastModified() > mFileLastModified); } - } diff --git a/src/com/owncloud/android/files/services/FileObserverService.java b/src/com/owncloud/android/files/services/FileObserverService.java index 1bcd8d630e..a90e551e2d 100644 --- a/src/com/owncloud/android/files/services/FileObserverService.java +++ b/src/com/owncloud/android/files/services/FileObserverService.java @@ -22,16 +22,6 @@ import java.io.File; import java.util.HashMap; import java.util.Map; -import com.owncloud.android.MainApp; -import com.owncloud.android.datamodel.FileDataStorageManager; -import com.owncloud.android.datamodel.OCFile; -import com.owncloud.android.db.ProviderMeta.ProviderTableMeta; -import com.owncloud.android.files.OwnCloudFileObserver; -import com.owncloud.android.operations.SynchronizeFileOperation; -import com.owncloud.android.utils.FileStorageUtils; -import com.owncloud.android.utils.Log_OC; - - import android.accounts.Account; import android.accounts.AccountManager; import android.app.Service; @@ -41,8 +31,18 @@ import android.content.Intent; import android.content.IntentFilter; import android.database.Cursor; import android.os.Binder; +import android.os.Handler; import android.os.IBinder; +import com.owncloud.android.MainApp; +import com.owncloud.android.datamodel.FileDataStorageManager; +import com.owncloud.android.datamodel.OCFile; +import com.owncloud.android.db.ProviderMeta.ProviderTableMeta; +import com.owncloud.android.files.OwnCloudFileObserver; +import com.owncloud.android.operations.SynchronizeFileOperation; +import com.owncloud.android.utils.FileStorageUtils; +import com.owncloud.android.utils.Log_OC; + public class FileObserverService extends Service { public final static int CMD_INIT_OBSERVED_LIST = 1; @@ -58,6 +58,7 @@ public class FileObserverService extends Service { private static Map mObserversMap; private static DownloadCompletedReceiverBis mDownloadReceiver; private IBinder mBinder = new LocalBinder(); + private Handler mHandler = new Handler(); public class LocalBinder extends Binder { FileObserverService getService() { @@ -160,10 +161,7 @@ public class FileObserverService extends Service { String path = c.getString(c.getColumnIndex(ProviderTableMeta.FILE_STORAGE_PATH)); if (path == null || path.length() <= 0) continue; - OwnCloudFileObserver observer = - new OwnCloudFileObserver( path, - account, - getApplicationContext()); + OwnCloudFileObserver observer = new OwnCloudFileObserver(path, account, getApplicationContext(), mHandler); mObserversMap.put(path, observer); if (new File(path).exists()) { observer.startWatching(); @@ -202,7 +200,8 @@ public class FileObserverService extends Service { /// the local file was never registered to observe before observer = new OwnCloudFileObserver( localPath, account, - getApplicationContext()); + getApplicationContext(), + mHandler); mObserversMap.put(localPath, observer); Log_OC.d(TAG, "Observer added for path " + localPath);