X-Git-Url: http://git.linex4red.de/pub/Android/ownCloud.git/blobdiff_plain/aef73a5263c94e34a38bc45ce1506651d80c7f78..c2abbaaedb69dba35866a48b745b68f3519cbca1:/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 41e4e52b..7aa3fa9a 100644 --- a/src/com/owncloud/android/files/services/FileObserverService.java +++ b/src/com/owncloud/android/files/services/FileObserverService.java @@ -1,10 +1,10 @@ /* 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 as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * 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 @@ -19,15 +19,17 @@ package com.owncloud.android.files.services; import java.io.File; -import java.util.ArrayList; -import java.util.List; +import java.util.HashMap; +import java.util.Map; -import com.owncloud.android.AccountUtils; +import com.owncloud.android.Log_OC; 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.files.OwnCloudFileObserver.FileObserverStatusListener; -import com.owncloud.android.ui.activity.ConflictsResolveActivity; +import com.owncloud.android.operations.SynchronizeFileOperation; +import com.owncloud.android.utils.FileStorageUtils; + import android.accounts.Account; import android.accounts.AccountManager; @@ -39,24 +41,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 implements FileObserverStatusListener { - public final static String KEY_FILE_CMD = "KEY_FILE_CMD"; - public final static String KEY_CMD_ARG = "KEY_CMD_ARG"; +public class FileObserverService extends Service { 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; + + 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 List mObservers; - private static List mDownloadReceivers; - private static Object mReceiverListLock = new Object(); - private IBinder mBinder = new LocalBinder(); + private static Map mObserversMap; + private static DownloadCompletedReceiverBis mDownloadReceiver; + private IBinder mBinder = new LocalBinder(); + public class LocalBinder extends Binder { FileObserverService getService() { return FileObserverService.this; @@ -64,6 +65,30 @@ public class FileObserverService extends Service implements FileObserverStatusLi } @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() { + super.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"); + } + + + @Override public IBinder onBind(Intent intent) { return mBinder; } @@ -78,7 +103,7 @@ public class FileObserverService extends Service implements FileObserverStatusLi } 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; } @@ -87,32 +112,34 @@ public class FileObserverService extends Service implements FileObserverStatusLi 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(); do { @@ -130,152 +157,122 @@ public class FileObserverService extends Service implements FileObserverStatusLi 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.addObserverStatusListener(this); - observer.startWatching(); - mObservers.add(observer); - Log.d(TAG, "Started watching file " + path); + new OwnCloudFileObserver( path, + account, + getApplicationContext(), + OwnCloudFileObserver.CHANGES_ONLY); + 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(getApplicationContext()); - } - if (duplicate) return; - observer = new OwnCloudFileObserver(path, OwnCloudFileObserver.CHANGES_ONLY); - observer.setContext(getApplicationContext()); - Account account = AccountUtils.getCurrentOwnCloudAccount(getApplicationContext()); - observer.setAccount(account); - FileDataStorageManager storage = - new FileDataStorageManager(account, getContentResolver()); - observer.setStorageManager(storage); - observer.setOCFile(storage.getFileByLocalPath(path)); - observer.addObserverStatusListener(this); - - 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(); - } - for (int i = 0; i < mObservers.size(); ++i) { - OwnCloudFileObserver observer = mObservers.get(i); - if (observer.getPath().equals(path)) { - observer.stopWatching(); - mObservers.remove(i); - Log.d(TAG, "Stopped watching " + path); - break; - } + /** + * 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; } - } - - 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(), + OwnCloudFileObserver.CHANGES_ONLY); + 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); } - } - - @Override - public void OnObservedFileStatusUpdate(String localPath, String remotePath, Account account, Status status) { - switch (status) { - case 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); - break; - } - case SENDING_TO_UPLOADER: - case INCORRECT_MASK: - break; - default: - Log.wtf(TAG, "Unhandled status " + status); + + OwnCloudFileObserver observer = mObserversMap.get(localPath); + if (observer != null) { + observer.stopWatching(); + mObserversMap.remove(observer); + Log_OC.d(TAG, "Stopped watching " + localPath); } + } - private class DownloadCompletedReceiver extends BroadcastReceiver { - String mPath; - OwnCloudFileObserver mObserver; - - public DownloadCompletedReceiver(String path, OwnCloudFileObserver observer) { - mPath = path; - mObserver = observer; - addReceiverToList(this); - } + + /** + * 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))) { - if ((new File(mPath)).exists()) { - // the download could be successful, or not; in both cases, the file could be down, due to a former download or upload - context.unregisterReceiver(this); - removeReceiverFromList(this); - mObserver.startWatching(); - Log.d(TAG, "Started watching " + mPath); - return; - } // else - keep waiting for a future retry of the download ; - // mObserver.startWatching() won't ever work if the file is not in the device when it's called + 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); - } } + }