From c880929ffd512c13fc7c7e7a085504b99ab5c09d Mon Sep 17 00:00:00 2001 From: "David A. Velasco" Date: Tue, 8 Jul 2014 12:51:01 +0200 Subject: [PATCH] Fixed last minute bugs, and clean up --- AndroidManifest.xml | 2 +- .../android/files/BootupBroadcastReceiver.java | 2 +- .../android/files/OwnCloudFileObserver.java | 153 --------------------- .../observer}/FileObserverService.java | 104 +++++++------- .../observer/FolderObserver.java} | 74 +++++++--- .../android/ui/activity/FileDisplayActivity.java | 4 +- .../android/ui/fragment/FileDetailFragment.java | 2 +- 7 files changed, 117 insertions(+), 224 deletions(-) delete mode 100644 src/com/owncloud/android/files/OwnCloudFileObserver.java rename src/com/owncloud/android/{files/services => services/observer}/FileObserverService.java (79%) rename src/com/owncloud/android/{files/OwnCloudFolderObserver.java => services/observer/FolderObserver.java} (72%) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index acf695b4..8311d528 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -187,7 +187,7 @@ - + . - * - */ - -package com.owncloud.android.files; - -import java.io.File; - -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); - public static int IN_IGNORE = 32768; - public static int ALL_EVENTS_AND_MORE = 0xffffffff; - - 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, Handler handler) { - super(path, ALL_EVENTS); - if (path == null) - throw new IllegalArgumentException("NULL path argument received"); - if (account == null) - throw new IllegalArgumentException("NULL account argument received"); - if (context == null) - throw new IllegalArgumentException("NULL context argument received"); - mPath = path; - mOCAccount = account; - 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.v(TAG, "Got event " + event + " on FILE " + mPath + " about " - + ((path != null) ? 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); - - // 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 - 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()); - // 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 - 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 - } - - /** - * 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/services/observer/FileObserverService.java similarity index 79% rename from src/com/owncloud/android/files/services/FileObserverService.java rename to src/com/owncloud/android/services/observer/FileObserverService.java index a602f8cf..95e19211 100644 --- a/src/com/owncloud/android/files/services/FileObserverService.java +++ b/src/com/owncloud/android/services/observer/FileObserverService.java @@ -1,6 +1,6 @@ /* ownCloud Android client application * Copyright (C) 2012 Bartek Przybylski - * Copyright (C) 2012-2013 ownCloud Inc. + * Copyright (C) 2012-2014 ownCloud Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -16,7 +16,7 @@ * */ -package com.owncloud.android.files.services; +package com.owncloud.android.services.observer; import java.io.File; import java.util.HashMap; @@ -30,21 +30,20 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.database.Cursor; -import android.os.FileObserver; import android.os.IBinder; import com.owncloud.android.MainApp; import com.owncloud.android.authentication.AccountUtils; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.db.ProviderMeta.ProviderTableMeta; -import com.owncloud.android.files.OwnCloudFolderObserver; +import com.owncloud.android.files.services.FileDownloader; import com.owncloud.android.operations.SynchronizeFileOperation; import com.owncloud.android.utils.FileStorageUtils; import com.owncloud.android.utils.Log_OC; /** - * Service keeping a list of {@link FileObserver} instances that watch for local + * Service keeping a list of {@link FolderObserver} instances that watch for local * changes in favorite files (formerly known as kept-in-sync files) and try to * synchronize them with the OC server as soon as possible. * @@ -70,16 +69,15 @@ public class FileObserverService extends Service { private static String TAG = FileObserverService.class.getSimpleName(); - private Map mFolderObserversMap; + private Map mFolderObserversMap; private DownloadCompletedReceiver mDownloadReceiver; /** - * Factory method to create intents that allow to start an - * ACTION_START_OBSERVE command. + * Factory method to create intents that allow to start an ACTION_START_OBSERVE command. * - * @param context Android context of the caller component. - * @return Intent that starts a command ACTION_START_OBSERVE when - * {@link Context#startService(Intent)} is called. + * @param context Android context of the caller component. + * @return Intent that starts a command ACTION_START_OBSERVE when + * {@link Context#startService(Intent)} is called. */ public static Intent makeInitIntent(Context context) { Intent i = new Intent(context, FileObserverService.class); @@ -91,13 +89,12 @@ public class FileObserverService extends Service { * Factory method to create intents that allow to start or stop the * observance of a file. * - * @param context Android context of the caller component. - * @param file OCFile to start or stop to watch. - * @param account OC account containing file. - * @param watchIt 'True' creates an intent to watch, 'false' an intent to - * stop watching. - * @return Intent to start or stop the observance of a file through a call - * to {@link Context#startService(Intent)}. + * @param context Android context of the caller component. + * @param file OCFile to start or stop to watch. + * @param account OC account containing file. + * @param watchIt 'True' creates an intent to watch, 'false' an intent to stop watching. + * @return Intent to start or stop the observance of a file through a call + * to {@link Context#startService(Intent)}. */ public static Intent makeObservedFileIntent( Context context, OCFile file, Account account, boolean watchIt) { @@ -109,6 +106,9 @@ public class FileObserverService extends Service { return intent; } + /** + * Initialize the service. + */ @Override public void onCreate() { Log_OC.d(TAG, "onCreate"); @@ -120,16 +120,19 @@ public class FileObserverService extends Service { filter.addAction(FileDownloader.getDownloadFinishMessage()); registerReceiver(mDownloadReceiver, filter); - mFolderObserversMap = new HashMap(); + mFolderObserversMap = new HashMap(); } + /** + * Release resources. + */ @Override public void onDestroy() { - Log_OC.d(TAG, "onDestroy - finishing observation of favourite files"); + Log_OC.d(TAG, "onDestroy - finishing observation of favorite files"); unregisterReceiver(mDownloadReceiver); - Iterator itOCFolder = mFolderObserversMap.values().iterator(); + Iterator itOCFolder = mFolderObserversMap.values().iterator(); while (itOCFolder.hasNext()) { itOCFolder.next().stopWatching(); } @@ -139,12 +142,20 @@ public class FileObserverService extends Service { super.onDestroy(); } + /** + * This service cannot be bound. + */ @Override public IBinder onBind(Intent intent) { - // this service cannot be bound return null; } + /** + * Handles requests to: + * - (re)start watching (ACTION_START_OBSERVE) + * - add an {@link OCFile} to be watched (ATION_ADD_OBSERVED_FILE) + * - stop observing an {@link OCFile} (ACTION_DEL_OBSERVED_FILE) + */ @Override public int onStartCommand(Intent intent, int flags, int startId) { Log_OC.d(TAG, "Starting command " + intent); @@ -174,7 +185,9 @@ public class FileObserverService extends Service { /** * Read from the local database the list of files that must to be kept - * synchronized and starts observers to monitor local changes on them + * synchronized and starts observers to monitor local changes on them. + * + * Updates the list of currently observed files if called multiple times. */ private void startObservation() { Log_OC.d(TAG, "Loading all kept-in-sync files from database to start watching them"); @@ -193,7 +206,6 @@ public class FileObserverService extends Service { if (cursorOnKeptInSync.moveToFirst()) { String localPath = ""; - // String remotePath = ""; String accountName = ""; Account account = null; do { @@ -201,11 +213,6 @@ public class FileObserverService extends Service { .getColumnIndex(ProviderTableMeta.FILE_STORAGE_PATH)); accountName = cursorOnKeptInSync.getString(cursorOnKeptInSync .getColumnIndex(ProviderTableMeta.FILE_ACCOUNT_OWNER)); - /* - * remotePath = cursorOnKeptInSync.getString( - * cursorOnKeptInSync - * .getColumnIndex(ProviderTableMeta.FILE_PATH) ); - */ account = new Account(accountName, MainApp.getAccountType()); if (!AccountUtils.exists(account, this) || localPath == null || localPath.length() <= 0) { @@ -232,9 +239,8 @@ public class FileObserverService extends Service { * This method does NOT perform a {@link SynchronizeFileOperation} over the * file. * - * @param file Object representing a remote file which local copy must be - * observed. - * @param account OwnCloud account containing file. + * @param file Object representing a remote file which local copy must be observed. + * @param account OwnCloud account containing file. */ private void addObservedFile(OCFile file, Account account) { Log_OC.v(TAG, "Adding a file to be watched"); @@ -262,17 +268,17 @@ public class FileObserverService extends Service { /** - * Registers the folder to be observed in which there are changes inside any - * file + * Registers a local file to be observed for changes. * - * @param account OwnCloud account containing file. + * @param localPath Absolute path in the local file system to the file to be observed. + * @param account OwnCloud account associated to the local file. */ private void addObservedFile(String localPath, Account account) { File file = new File(localPath); String parentPath = file.getParent(); - OwnCloudFolderObserver observer = mFolderObserversMap.get(parentPath); + FolderObserver observer = mFolderObserversMap.get(parentPath); if (observer == null) { - observer = new OwnCloudFolderObserver(parentPath, account, getApplicationContext()); + observer = new FolderObserver(parentPath, account, getApplicationContext()); mFolderObserversMap.put(parentPath, observer); Log_OC.d(TAG, "Observer added for parent folder " + parentPath + "/"); } @@ -283,14 +289,11 @@ public class FileObserverService extends Service { /** - * Unregisters the local copy of a remote file to be observed for local - * changes. - * - * Starts to watch it, if the file has a local copy to watch. + * Unregisters the local copy of a remote file to be observed for local changes. * - * @param file Object representing a remote file which local copy must be - * not observed longer. - * @param account OwnCloud account containing file. + * @param file Object representing a remote file which local copy must be not + * observed longer. + * @param account OwnCloud account containing file. */ private void removeObservedFile(OCFile file, Account account) { Log_OC.v(TAG, "Removing a file from being watched"); @@ -313,10 +316,15 @@ public class FileObserverService extends Service { } + /** + * Unregisters a local file from being observed for changes. + * + * @param localPath Absolute path in the local file system to the target file. + */ private void removeObservedFile(String localPath) { File file = new File(localPath); String parentPath = file.getParent(); - OwnCloudFolderObserver observer = mFolderObserversMap.get(parentPath); + FolderObserver observer = mFolderObserversMap.get(parentPath); if (observer != null) { observer.stopWatching(file.getName()); if (observer.isEmpty()) { @@ -331,7 +339,7 @@ public class FileObserverService extends Service { /** - * Private receiver listening to events broadcasted by the FileDownloader service. + * Private receiver listening to events broadcasted by the {@link FileDownloader} service. * * Pauses and resumes the observance on registered files while being download, * in order to avoid to unnecessary synchronizations. @@ -344,11 +352,11 @@ public class FileObserverService extends Service { File downloadedFile = new File(intent.getStringExtra(FileDownloader.EXTRA_FILE_PATH)); String parentPath = downloadedFile.getParent(); - OwnCloudFolderObserver observer = mFolderObserversMap.get(parentPath); + FolderObserver observer = mFolderObserversMap.get(parentPath); if (observer != null) { if (intent.getAction().equals(FileDownloader.getDownloadFinishMessage()) && downloadedFile.exists()) { - // no matter if the download was be successful or not; the + // no matter if the download was successful or not; the // file could be down anyway due to a former download or upload observer.startWatching(downloadedFile.getName()); Log_OC.d(TAG, "Resuming observance of " + downloadedFile.getAbsolutePath()); diff --git a/src/com/owncloud/android/files/OwnCloudFolderObserver.java b/src/com/owncloud/android/services/observer/FolderObserver.java similarity index 72% rename from src/com/owncloud/android/files/OwnCloudFolderObserver.java rename to src/com/owncloud/android/services/observer/FolderObserver.java index d958e2f5..ea1a9ede 100644 --- a/src/com/owncloud/android/files/OwnCloudFolderObserver.java +++ b/src/com/owncloud/android/services/observer/FolderObserver.java @@ -15,7 +15,7 @@ * */ -package com.owncloud.android.files; +package com.owncloud.android.services.observer; import java.io.File; import java.util.HashMap; @@ -40,33 +40,45 @@ import com.owncloud.android.utils.Log_OC; * * Takes into account two possible update cases: * - an editor directly updates the file; - * - an editor works on a temporal file, and later replaces the kept-in-sync file with the - * temporal. + * - an editor works on a temporary file, and later replaces the kept-in-sync file with the + * former. * * The second case requires to monitor the folder parent of the files, since a direct * {@link FileObserver} on it will not receive more events after the file is deleted to - * be replaced later. + * be replaced. * * @author David A. Velasco */ -public class OwnCloudFolderObserver extends FileObserver { +public class FolderObserver extends FileObserver { + + private static String TAG = FolderObserver.class.getSimpleName(); private static int UPDATE_MASK = ( FileObserver.ATTRIB | FileObserver.MODIFY | FileObserver.MOVED_TO | FileObserver.CLOSE_WRITE ); + + private static int IN_IGNORE = 32768; /* private static int ALL_EVENTS_EVEN_THOSE_NOT_DOCUMENTED = 0x7fffffff; // NEVER use 0xffffffff */ - private static String TAG = OwnCloudFolderObserver.class.getSimpleName(); - private String mPath; private Account mAccount; private Context mContext; private Map mObservedChildren; - public OwnCloudFolderObserver(String path, Account account, Context context) { + /** + * Constructor. + * + * Initializes the observer to receive events about the update of the passed folder, and + * its children files. + * + * @param path Absolute path to the local folder to watch. + * @param account OwnCloud account associated to the folder. + * @param context Used to start an operation to synchronize the file, when needed. + */ + public FolderObserver(String path, Account account, Context context) { super(path, UPDATE_MASK); if (path == null) @@ -82,7 +94,13 @@ public class OwnCloudFolderObserver extends FileObserver { mObservedChildren = new HashMap(); } - + + /** + * Receives and processes events about updates of the monitor folder and its children files. + * + * @param event Kind of event occurred. + * @param path Relative path of the file referred by the event. + */ @Override public void onEvent(int event, String path) { Log_OC.d(TAG, "Got event " + event + " on FOLDER " + mPath + " about " @@ -101,7 +119,7 @@ public class OwnCloudFolderObserver extends FileObserver { } } - if ((event & FileObserver.CLOSE_WRITE) != 0) { + if ((event & FileObserver.CLOSE_WRITE) != 0 && mObservedChildren.get(path)) { mObservedChildren.put(path, Boolean.valueOf(false)); shouldSynchronize = true; } @@ -111,7 +129,7 @@ public class OwnCloudFolderObserver extends FileObserver { startSyncOperation(path); } - if ((event & OwnCloudFileObserver.IN_IGNORE) != 0 && + if ((event & IN_IGNORE) != 0 && (path == null || path.length() == 0)) { Log_OC.d(TAG, "Stopping the observance on " + mPath); } @@ -119,10 +137,15 @@ public class OwnCloudFolderObserver extends FileObserver { } - public void startWatching(String localPath) { + /** + * Adds a child file to the list of files observed by the folder observer. + * + * @param fileName Name of a file inside the observed folder. + */ + public void startWatching(String fileName) { synchronized (mObservedChildren) { - if (!mObservedChildren.containsKey(localPath)) { - mObservedChildren.put(localPath, Boolean.valueOf(false)); + if (!mObservedChildren.containsKey(fileName)) { + mObservedChildren.put(fileName, Boolean.valueOf(false)); } } @@ -133,9 +156,15 @@ public class OwnCloudFolderObserver extends FileObserver { // else - the observance can't be started on a file not existing; } - public void stopWatching(String localPath) { + + /** + * Removes a child file from the list of files observed by the folder observer. + * + * @param fileName Name of a file inside the observed folder. + */ + public void stopWatching(String fileName) { synchronized (mObservedChildren) { - mObservedChildren.remove(localPath); + mObservedChildren.remove(fileName); if (mObservedChildren.isEmpty()) { stopWatching(); Log_OC.d(TAG, "Stopped watching parent folder " + mPath + "/"); @@ -143,6 +172,9 @@ public class OwnCloudFolderObserver extends FileObserver { } } + /** + * @return 'True' when the folder is not watching any file inside. + */ public boolean isEmpty() { synchronized (mObservedChildren) { return mObservedChildren.isEmpty(); @@ -150,13 +182,19 @@ public class OwnCloudFolderObserver extends FileObserver { } - private void startSyncOperation(String childName) { + /** + * Triggers an operation to synchronize the contents of a file inside the observed folder with + * its remote counterpart in the associated ownCloud account. + * + * @param fileName Name of a file inside the watched folder. + */ + private void startSyncOperation(String fileName) { FileDataStorageManager storageManager = new FileDataStorageManager(mAccount, mContext.getContentResolver()); // 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 + File.separator + childName); + OCFile file = storageManager.getFileByLocalPath(mPath + File.separator + fileName); SynchronizeFileOperation sfo = new SynchronizeFileOperation(file, null, mAccount, true, mContext); RemoteOperationResult result = sfo.execute(storageManager, mContext); diff --git a/src/com/owncloud/android/ui/activity/FileDisplayActivity.java b/src/com/owncloud/android/ui/activity/FileDisplayActivity.java index c1065c4a..640f2b81 100644 --- a/src/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/src/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -66,7 +66,6 @@ import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.files.services.FileDownloader; -import com.owncloud.android.files.services.FileObserverService; import com.owncloud.android.files.services.FileUploader; import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder; import com.owncloud.android.files.services.FileUploader.FileUploaderBinder; @@ -87,6 +86,7 @@ import com.owncloud.android.operations.RenameFileOperation; import com.owncloud.android.operations.SynchronizeFileOperation; import com.owncloud.android.operations.SynchronizeFolderOperation; import com.owncloud.android.operations.UnshareLinkOperation; +import com.owncloud.android.services.observer.FileObserverService; import com.owncloud.android.syncadapter.FileSyncAdapter; import com.owncloud.android.ui.dialog.CreateFolderDialogFragment; import com.owncloud.android.ui.dialog.SslUntrustedCertDialog; @@ -165,7 +165,7 @@ FileFragment.ContainerActivity, OnNavigationListener, OnSslUntrustedCertListener } /// grant that FileObserverService is watching favourite files - if (savedInstanceState != null) { + if (savedInstanceState == null) { Intent initObserversIntent = FileObserverService.makeInitIntent(this); startService(initObserversIntent); } diff --git a/src/com/owncloud/android/ui/fragment/FileDetailFragment.java b/src/com/owncloud/android/ui/fragment/FileDetailFragment.java index 15a8b365..b3d8567d 100644 --- a/src/com/owncloud/android/ui/fragment/FileDetailFragment.java +++ b/src/com/owncloud/android/ui/fragment/FileDetailFragment.java @@ -38,10 +38,10 @@ import com.owncloud.android.R; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.files.FileMenuFilter; -import com.owncloud.android.files.services.FileObserverService; import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder; import com.owncloud.android.files.services.FileUploader.FileUploaderBinder; import com.owncloud.android.lib.common.network.OnDatatransferProgressListener; +import com.owncloud.android.services.observer.FileObserverService; import com.owncloud.android.ui.activity.FileActivity; import com.owncloud.android.ui.activity.FileDisplayActivity; import com.owncloud.android.ui.dialog.RemoveFileDialogFragment; -- 2.11.0