X-Git-Url: http://git.linex4red.de/pub/Android/ownCloud.git/blobdiff_plain/ab946b9af69f3f299d72026db69f5f650096d8e8..6ace8ab1c18b23a4c016986256075cfda8e50104:/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 new file mode 100644 index 00000000..a0003bec --- /dev/null +++ b/src/com/owncloud/android/files/services/FileObserverService.java @@ -0,0 +1,285 @@ +/* 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.io.File; +import java.util.HashMap; +import java.util.Map; + +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.operations.SynchronizeFileOperation; +import com.owncloud.android.utils.FileStorageUtils; + + +import android.accounts.Account; +import android.accounts.AccountManager; +import android.app.Service; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.database.Cursor; +import android.os.Binder; +import android.os.IBinder; + +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 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(); + + private String mDownloadAddedMessage; + private String mDownloadFinishMessage; + + public class LocalBinder extends Binder { + FileObserverService getService() { + return FileObserverService.this; + } + } + + @Override + public void onCreate() { + super.onCreate(); + mDownloadReceiver = new DownloadCompletedReceiverBis(); + + FileDownloader downloader = new FileDownloader(); + mDownloadAddedMessage = downloader.getDownloadAddedMessage(); + mDownloadFinishMessage= downloader.getDownloadFinishMessage(); + + IntentFilter filter = new IntentFilter(); + filter.addAction(mDownloadAddedMessage); + filter.addAction(mDownloadFinishMessage); + 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; + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + // this occurs when system tries to restart + // service, so we need to reinitialize observers + if (intent == null) { + initializeObservedList(); + return Service.START_STICKY; + } + + if (!intent.hasExtra(KEY_FILE_CMD)) { + Log_OC.e(TAG, "No KEY_FILE_CMD argument given"); + return Service.START_STICKY; + } + + switch (intent.getIntExtra(KEY_FILE_CMD, -1)) { + case CMD_INIT_OBSERVED_LIST: + initializeObservedList(); + break; + case CMD_ADD_OBSERVED_FILE: + addObservedFile( (OCFile)intent.getParcelableExtra(KEY_CMD_ARG_FILE), + (Account)intent.getParcelableExtra(KEY_CMD_ARG_ACCOUNT)); + break; + case CMD_DEL_OBSERVED_FILE: + removeObservedFile( (OCFile)intent.getParcelableExtra(KEY_CMD_ARG_FILE), + (Account)intent.getParcelableExtra(KEY_CMD_ARG_ACCOUNT)); + break; + default: + 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() { + mObserversMap.clear(); + Cursor c = getContentResolver().query( + ProviderTableMeta.CONTENT_URI, + null, + ProviderTableMeta.FILE_KEEP_IN_SYNC + " = ?", + new String[] {String.valueOf(1)}, + null); + if (c == null || !c.moveToFirst()) return; + AccountManager acm = AccountManager.get(this); + Account[] accounts = acm.getAccounts(); + do { + Account account = null; + for (Account a : accounts) + if (a.name.equals(c.getString(c.getColumnIndex(ProviderTableMeta.FILE_ACCOUNT_OWNER)))) { + account = a; + break; + } + + if (account == null) continue; + FileDataStorageManager storage = + new FileDataStorageManager(account, getContentResolver()); + if (!storage.fileExists(c.getString(c.getColumnIndex(ProviderTableMeta.FILE_PATH)))) + continue; + + 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.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(); + } + + + /** + * 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; + } + 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) { + /// 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 + } + + } + + + /** + * 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; + } + String localPath = file.getStoragePath(); + if (localPath == null || localPath.length() <= 0) { + localPath = FileStorageUtils.getDefaultSavePathFor(account.name, file); + } + + 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) { + String downloadPath = intent.getStringExtra(FileDownloader.EXTRA_FILE_PATH); + OwnCloudFileObserver observer = mObserversMap.get(downloadPath); + if (observer != null) { + if (intent.getAction().equals(mDownloadFinishMessage) && + 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(mDownloadAddedMessage)) { + observer.stopWatching(); + Log_OC.d(TAG, "Disabling observance of " + downloadPath); + } + } + } + + } + +}