X-Git-Url: http://git.linex4red.de/pub/Android/ownCloud.git/blobdiff_plain/de94751cde3af2e78212f043d3d0c2aed90f69a8..cfe589231a558f3f7e5ce483ae46f1422182e8e1:/src/com/owncloud/android/files/services/FileObserverService.java diff --git a/src/com/owncloud/android/files/services/FileObserverService.java b/src/com/owncloud/android/files/services/FileObserverService.java index 25b5f1c6..1bcd8d63 100644 --- a/src/com/owncloud/android/files/services/FileObserverService.java +++ b/src/com/owncloud/android/files/services/FileObserverService.java @@ -1,12 +1,36 @@ +/* ownCloud Android client application + * Copyright (C) 2012 Bartek Przybylski + * Copyright (C) 2012-2013 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, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + package com.owncloud.android.files.services; -import java.util.ArrayList; -import java.util.List; +import java.io.File; +import java.util.HashMap; +import java.util.Map; -import com.owncloud.android.AccountUtils; +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; @@ -18,24 +42,23 @@ import android.content.IntentFilter; import android.database.Cursor; import android.os.Binder; import android.os.IBinder; -import android.util.Log; public class FileObserverService extends Service { - public final static String KEY_FILE_CMD = "KEY_FILE_CMD"; - public final static String KEY_CMD_ARG = "KEY_CMD_ARG"; - public final static int CMD_INIT_OBSERVED_LIST = 1; public final static int CMD_ADD_OBSERVED_FILE = 2; public final static int CMD_DEL_OBSERVED_FILE = 3; - public final static int CMD_ADD_DOWNLOADING_FILE = 4; - private static String TAG = "FileObserverService"; - private static List mObservers; - private static List mDownloadReceivers; - private static Object mReceiverListLock = new Object(); - private IBinder mBinder = new LocalBinder(); + public final static String KEY_FILE_CMD = "KEY_FILE_CMD"; + public final static String KEY_CMD_ARG_FILE = "KEY_CMD_ARG_FILE"; + public final static String KEY_CMD_ARG_ACCOUNT = "KEY_CMD_ARG_ACCOUNT"; + + private static String TAG = FileObserverService.class.getSimpleName(); + private static Map mObserversMap; + private static DownloadCompletedReceiverBis mDownloadReceiver; + private IBinder mBinder = new LocalBinder(); + public class LocalBinder extends Binder { FileObserverService getService() { return FileObserverService.this; @@ -43,6 +66,30 @@ public class FileObserverService extends Service { } @Override + public void onCreate() { + super.onCreate(); + mDownloadReceiver = new DownloadCompletedReceiverBis(); + + IntentFilter filter = new IntentFilter(); + filter.addAction(FileDownloader.getDownloadAddedMessage()); + filter.addAction(FileDownloader.getDownloadFinishMessage()); + registerReceiver(mDownloadReceiver, filter); + + mObserversMap = new HashMap(); + //initializeObservedList(); + } + + + @Override + public void onDestroy() { + unregisterReceiver(mDownloadReceiver); + mObserversMap = null; // TODO study carefully the life cycle of Services to grant the best possible observance + Log_OC.d(TAG, "Bye, bye"); + super.onDestroy(); + } + + + @Override public IBinder onBind(Intent intent) { return mBinder; } @@ -57,7 +104,7 @@ public class FileObserverService extends Service { } if (!intent.hasExtra(KEY_FILE_CMD)) { - Log.e(TAG, "No KEY_FILE_CMD argument given"); + Log_OC.e(TAG, "No KEY_FILE_CMD argument given"); return Service.START_STICKY; } @@ -66,34 +113,36 @@ public class FileObserverService extends Service { initializeObservedList(); break; case CMD_ADD_OBSERVED_FILE: - addObservedFile(intent.getStringExtra(KEY_CMD_ARG)); + addObservedFile( (OCFile)intent.getParcelableExtra(KEY_CMD_ARG_FILE), + (Account)intent.getParcelableExtra(KEY_CMD_ARG_ACCOUNT)); break; case CMD_DEL_OBSERVED_FILE: - removeObservedFile(intent.getStringExtra(KEY_CMD_ARG)); - break; - case CMD_ADD_DOWNLOADING_FILE: - addDownloadingFile(intent.getStringExtra(KEY_CMD_ARG)); + removeObservedFile( (OCFile)intent.getParcelableExtra(KEY_CMD_ARG_FILE), + (Account)intent.getParcelableExtra(KEY_CMD_ARG_ACCOUNT)); break; default: - Log.wtf(TAG, "Incorrect key given"); + Log_OC.wtf(TAG, "Incorrect key given"); } return Service.START_STICKY; } + + /** + * Read from the local database the list of files that must to be kept synchronized and + * starts file observers to monitor local changes on them + */ private void initializeObservedList() { - if (mObservers != null) return; // nothing to do here - mObservers = new ArrayList(); - mDownloadReceivers = new ArrayList(); + mObserversMap.clear(); Cursor c = getContentResolver().query( ProviderTableMeta.CONTENT_URI, null, ProviderTableMeta.FILE_KEEP_IN_SYNC + " = ?", new String[] {String.valueOf(1)}, null); - if (!c.moveToFirst()) return; + if (c == null || !c.moveToFirst()) return; AccountManager acm = AccountManager.get(this); - Account[] accounts = acm.getAccounts(); + Account[] accounts = acm.getAccountsByType(MainApp.getAccountType()); do { Account account = null; for (Account a : accounts) @@ -109,125 +158,120 @@ public class FileObserverService extends Service { continue; String path = c.getString(c.getColumnIndex(ProviderTableMeta.FILE_STORAGE_PATH)); + if (path == null || path.length() <= 0) + continue; OwnCloudFileObserver observer = - new OwnCloudFileObserver(path, OwnCloudFileObserver.CHANGES_ONLY); - observer.setContext(getApplicationContext()); - observer.setAccount(account); - observer.setStorageManager(storage); - observer.setOCFile(storage.getFileByPath(c.getString(c.getColumnIndex(ProviderTableMeta.FILE_PATH)))); - observer.startWatching(); - mObservers.add(observer); - Log.d(TAG, "Started watching file " + path); + new OwnCloudFileObserver( path, + account, + getApplicationContext()); + mObserversMap.put(path, observer); + if (new File(path).exists()) { + observer.startWatching(); + Log_OC.d(TAG, "Started watching file " + path); + } } while (c.moveToNext()); c.close(); } - private void addObservedFile(String path) { - if (path == null) return; - if (mObservers == null) { - // this is very rare case when service was killed by system - // and observers list was deleted in that procedure - initializeObservedList(); - } - boolean duplicate = false; - OwnCloudFileObserver observer = null; - for (int i = 0; i < mObservers.size(); ++i) { - observer = mObservers.get(i); - if (observer.getPath().equals(path)) - duplicate = true; - observer.setContext(getBaseContext()); - } - if (duplicate) return; - observer = new OwnCloudFileObserver(path, OwnCloudFileObserver.CHANGES_ONLY); - observer.setContext(getBaseContext()); - Account account = AccountUtils.getCurrentOwnCloudAccount(getBaseContext()); - observer.setAccount(account); - FileDataStorageManager storage = - new FileDataStorageManager(account, getContentResolver()); - observer.setStorageManager(storage); - observer.setOCFile(storage.getFileByLocalPath(path)); - - DownloadCompletedReceiver receiver = new DownloadCompletedReceiver(path, observer); - registerReceiver(receiver, new IntentFilter(FileDownloader.DOWNLOAD_FINISH_MESSAGE)); - - mObservers.add(observer); - Log.d(TAG, "Observer added for path " + path); - } - private void removeObservedFile(String path) { - if (path == null) return; - if (mObservers == null) { - initializeObservedList(); + /** + * Registers the local copy of a remote file to be observed for local changes, + * an automatically updated in the ownCloud server. + * + * This method does NOT perform a {@link SynchronizeFileOperation} over the file. + * + * TODO We are ignoring that, currently, a local file can be linked to different files + * in ownCloud if it's uploaded several times. That's something pending to update: we + * will avoid that the same local file is linked to different remote files. + * + * @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) { + if (file == null) { + Log_OC.e(TAG, "Trying to add a NULL file to observer"); return; } - for (int i = 0; i < mObservers.size(); ++i) { - OwnCloudFileObserver observer = mObservers.get(i); - if (observer.getPath().equals(path)) { - observer.stopWatching(); - mObservers.remove(i); - break; - } - } - Log.d(TAG, "Stopped watching " + path); - } - - private void addDownloadingFile(String remotePath) { - OwnCloudFileObserver observer = null; - for (OwnCloudFileObserver o : mObservers) { - if (o.getRemotePath().equals(remotePath)) { - observer = o; - break; - } + String localPath = file.getStoragePath(); + if (localPath == null || localPath.length() <= 0) { // file downloading / to be download for the first time + localPath = FileStorageUtils.getDefaultSavePathFor(account.name, file); } + OwnCloudFileObserver observer = mObserversMap.get(localPath); if (observer == null) { - Log.e(TAG, "Couldn't find observer for remote file " + remotePath); - return; + /// the local file was never registered to observe before + observer = new OwnCloudFileObserver( localPath, + account, + getApplicationContext()); + mObserversMap.put(localPath, observer); + Log_OC.d(TAG, "Observer added for path " + localPath); + + if (file.isDown()) { + observer.startWatching(); + Log_OC.d(TAG, "Started watching " + localPath); + } // else - the observance can't be started on a file not already down; mDownloadReceiver will get noticed when the download of the file finishes } - observer.stopWatching(); - DownloadCompletedReceiver dcr = new DownloadCompletedReceiver(observer.getPath(), observer); - registerReceiver(dcr, new IntentFilter(FileDownloader.DOWNLOAD_FINISH_MESSAGE)); + } - private static void addReceiverToList(DownloadCompletedReceiver r) { - synchronized(mReceiverListLock) { - mDownloadReceivers.add(r); + /** + * 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. + * + * TODO We are ignoring that, currently, a local file can be linked to different files + * in ownCloud if it's uploaded several times. That's something pending to update: we + * will avoid that the same local file is linked to different remote files. + * + * @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) { + if (file == null) { + Log_OC.e(TAG, "Trying to remove a NULL file"); + return; } - } - - private static void removeReceiverFromList(DownloadCompletedReceiver r) { - synchronized(mReceiverListLock) { - mDownloadReceivers.remove(r); + String localPath = file.getStoragePath(); + if (localPath == null || localPath.length() <= 0) { + localPath = FileStorageUtils.getDefaultSavePathFor(account.name, file); } - } - - private class DownloadCompletedReceiver extends BroadcastReceiver { - String mPath; - OwnCloudFileObserver mObserver; - public DownloadCompletedReceiver(String path, OwnCloudFileObserver observer) { - mPath = path; - mObserver = observer; - addReceiverToList(this); + OwnCloudFileObserver observer = mObserversMap.get(localPath); + if (observer != null) { + observer.stopWatching(); + mObserversMap.remove(observer); + Log_OC.d(TAG, "Stopped watching " + localPath); } + } + + + /** + * Private receiver listening to events broadcast by the FileDownloader service. + * + * Starts and stops the observance on registered files when they are being download, + * in order to avoid to start unnecessary synchronizations. + */ + private class DownloadCompletedReceiverBis extends BroadcastReceiver { + @Override public void onReceive(Context context, Intent intent) { - if (mPath.equals(intent.getStringExtra(FileDownloader.EXTRA_FILE_PATH))) { - context.unregisterReceiver(this); - removeReceiverFromList(this); - mObserver.startWatching(); - Log.d(TAG, "Started watching " + mPath); - return; + String downloadPath = intent.getStringExtra(FileDownloader.EXTRA_FILE_PATH); + OwnCloudFileObserver observer = mObserversMap.get(downloadPath); + if (observer != null) { + if (intent.getAction().equals(FileDownloader.getDownloadFinishMessage()) && + new File(downloadPath).exists()) { // the download could be successful. not; in both cases, the file could be down, due to a former download or upload + observer.startWatching(); + Log_OC.d(TAG, "Watching again " + downloadPath); + + } else if (intent.getAction().equals(FileDownloader.getDownloadAddedMessage())) { + observer.stopWatching(); + Log_OC.d(TAG, "Disabling observance of " + downloadPath); + } } } - @Override - public boolean equals(Object o) { - if (o instanceof DownloadCompletedReceiver) - return mPath.equals(((DownloadCompletedReceiver)o).mPath); - return super.equals(o); - } } + }