Added indexed tree of synchronizing folders to SyncFolderHandler
authorDavid A. Velasco <dvelasco@solidgear.es>
Mon, 26 Jan 2015 08:42:44 +0000 (09:42 +0100)
committerDavid A. Velasco <dvelasco@solidgear.es>
Mon, 26 Jan 2015 08:42:44 +0000 (09:42 +0100)
src/com/owncloud/android/files/FileMenuFilter.java
src/com/owncloud/android/files/FileOperationsHelper.java
src/com/owncloud/android/files/services/FileDownloader.java
src/com/owncloud/android/files/services/IndexedForest.java
src/com/owncloud/android/services/OperationsService.java
src/com/owncloud/android/services/SyncFolderHandler.java
src/com/owncloud/android/ui/adapter/FileListListAdapter.java

index b63446b..89b00dc 100644 (file)
@@ -31,6 +31,7 @@ import com.owncloud.android.files.services.FileDownloader;
 import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
 import com.owncloud.android.files.services.FileUploader;
 import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
+import com.owncloud.android.services.OperationsService;
 import com.owncloud.android.services.OperationsService.OperationsServiceBinder;
 import com.owncloud.android.ui.activity.ComponentsGetter;
 
@@ -140,9 +141,10 @@ public class FileMenuFilter {
         boolean downloading = false;
         boolean uploading = false;
         if (mComponentsGetter != null && mFile != null && mAccount != null) {
-            //downloading = mFile.isDownloading() || mFile.isSynchronizing();
             FileDownloaderBinder downloaderBinder = mComponentsGetter.getFileDownloaderBinder();
             downloading = (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, mFile));
+            OperationsServiceBinder opsBinder = mComponentsGetter.getOperationsServiceBinder();
+            downloading |= (opsBinder != null && opsBinder.isSynchronizing(mAccount, mFile.getRemotePath()));
             FileUploaderBinder uploaderBinder = mComponentsGetter.getFileUploaderBinder();
             uploading = (uploaderBinder != null && uploaderBinder.isUploading(mAccount, mFile));
         }
index 35b6924..22be2a7 100644 (file)
@@ -285,18 +285,15 @@ public class FileOperationsHelper {
     public void cancelTransference(OCFile file) {
         Account account = mFileActivity.getAccount();
         if (file.isFolder()) {
-            // this goes to the queue!! :S
-            Intent intent = new Intent(mFileActivity, OperationsService.class);
-            intent.setAction(OperationsService.ACTION_CANCEL_SYNC_FOLDER);
-            intent.putExtra(OperationsService.EXTRA_ACCOUNT, account);
-            intent.putExtra(OperationsService.EXTRA_FILE, file);
-            mFileActivity.startService(intent);
+            OperationsService.OperationsServiceBinder opsBinder = mFileActivity.getOperationsServiceBinder();
+            if (opsBinder != null) {
+                opsBinder.cancel(account, file);
+            }
         }
 
         // for both files and folders
         FileDownloaderBinder downloaderBinder = mFileActivity.getFileDownloaderBinder();
         FileUploaderBinder uploaderBinder = mFileActivity.getFileUploaderBinder();
-        //if (downloaderBinder != null && file.isDownloading()) {
         if (downloaderBinder != null && downloaderBinder.isDownloading(account, file)) {
             downloaderBinder.cancel(account, file);
 
index 1ecae2c..06ebc0b 100644 (file)
@@ -1,6 +1,6 @@
 /* ownCloud Android client application
  *   Copyright (C) 2012 Bartek Przybylski
- *   Copyright (C) 2012-2013 ownCloud Inc.
+ *   Copyright (C) 2012-2015 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,
@@ -21,7 +21,6 @@ package com.owncloud.android.files.services;
 import java.io.File;
 import java.io.IOException;
 import java.util.AbstractList;
-import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
@@ -70,8 +69,6 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
     public static final String EXTRA_ACCOUNT = "ACCOUNT";
     public static final String EXTRA_FILE = "FILE";
 
-    public static final String ACTION_CANCEL_FILE_DOWNLOAD = "CANCEL_FILE_DOWNLOAD";
-
     private static final String DOWNLOAD_ADDED_MESSAGE = "DOWNLOAD_ADDED";
     private static final String DOWNLOAD_FINISH_MESSAGE = "DOWNLOAD_FINISH";
     public static final String EXTRA_DOWNLOAD_RESULT = "RESULT";    
@@ -86,7 +83,7 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
     private ServiceHandler mServiceHandler;
     private IBinder mBinder;
     private OwnCloudClient mDownloadClient = null;
-    private Account mLastAccount = null;
+    private Account mCurrentAccount = null;
     private FileDataStorageManager mStorageManager;
     
     private IndexedForest<DownloadFileOperation> mPendingDownloads = new IndexedForest<DownloadFileOperation>();
@@ -154,12 +151,12 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
                 AbstractList<String> requestedDownloads = new Vector<String>();
                 try {
                     DownloadFileOperation newDownload = new DownloadFileOperation(account, file);
+                    newDownload.addDatatransferProgressListener(this);
+                    newDownload.addDatatransferProgressListener((FileDownloaderBinder) mBinder);
                     Pair<String, String> putResult = mPendingDownloads.putIfAbsent(
                         account, file.getRemotePath(), newDownload
                     );
                     String downloadKey = putResult.first;
-                    newDownload.addDatatransferProgressListener(this);
-                    newDownload.addDatatransferProgressListener((FileDownloaderBinder) mBinder);
                     requestedDownloads.add(downloadKey);
 
                     // Store file on db with state 'downloading'
@@ -239,9 +236,9 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
             if (download != null) {
                 download.cancel();
             } else {
-                // TODO synchronize
-                if (mCurrentDownload.getRemotePath().startsWith(file.getRemotePath()) &&
-                        account.name.equals(mLastAccount.name)) {
+                if (mCurrentDownload != null && mCurrentAccount != null &&
+                        mCurrentDownload.getRemotePath().startsWith(file.getRemotePath()) &&
+                        account.name.equals(mCurrentAccount.name)) {
                     mCurrentDownload.cancel();
                 }
             }
@@ -362,11 +359,11 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
             RemoteOperationResult downloadResult = null;
             try {
                 /// prepare client object to send the request to the ownCloud server
-                if (mDownloadClient == null || !mLastAccount.equals(mCurrentDownload.getAccount())) {
-                    mLastAccount = mCurrentDownload.getAccount();
+                if (mDownloadClient == null || !mCurrentAccount.equals(mCurrentDownload.getAccount())) {
+                    mCurrentAccount = mCurrentDownload.getAccount();
                     mStorageManager = 
-                            new FileDataStorageManager(mLastAccount, getContentResolver());
-                    OwnCloudAccount ocAccount = new OwnCloudAccount(mLastAccount, this);
+                            new FileDataStorageManager(mCurrentAccount, getContentResolver());
+                    OwnCloudAccount ocAccount = new OwnCloudAccount(mCurrentAccount, this);
                     mDownloadClient = OwnCloudClientManagerFactory.getDefaultSingleton().
                             getClientFor(ocAccount, this);
                 }
@@ -381,15 +378,15 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
                 }
             
             } catch (AccountsException e) {
-                Log_OC.e(TAG, "Error while trying to get authorization for " + mLastAccount.name, e);
+                Log_OC.e(TAG, "Error while trying to get authorization for " + mCurrentAccount.name, e);
                 downloadResult = new RemoteOperationResult(e);
             } catch (IOException e) {
-                Log_OC.e(TAG, "Error while trying to get authorization for " + mLastAccount.name, e);
+                Log_OC.e(TAG, "Error while trying to get authorization for " + mCurrentAccount.name, e);
                 downloadResult = new RemoteOperationResult(e);
                 
             } finally {
                 Pair<DownloadFileOperation, String> removeResult =
-                        mPendingDownloads.remove(mLastAccount, mCurrentDownload.getRemotePath());
+                        mPendingDownloads.removePayload(mCurrentAccount, mCurrentDownload.getRemotePath());
 
                 /// notify result
                 notifyDownloadResult(mCurrentDownload, downloadResult);
index 0f737aa..e2e9cb8 100644 (file)
@@ -89,6 +89,10 @@ public class IndexedForest<V> {
         public void removeChild(Node<V> removed) {
             mChildren.remove(removed);
         }
+
+        public void clearPayload() {
+            mPayload = null;
+        }
     }
 
 
@@ -129,6 +133,20 @@ public class IndexedForest<V> {
         return new Pair<String, String>(targetKey, linkedTo);
     };
 
+
+    public Pair<V, String> removePayload(Account account, String remotePath) {
+        String targetKey = buildKey(account, remotePath);
+        Node<V> target = mMap.get(targetKey);
+        if (target != null) {
+            target.clearPayload();
+            if (!target.hasChildren()) {
+                return remove(account, remotePath);
+            }
+        }
+        return new Pair<V, String>(null, null);
+    }
+
+
     public /* synchronized */ Pair<V, String> remove(Account account, String remotePath) {
         String targetKey = buildKey(account, remotePath);
         Node<V> firstRemoved = mMap.remove(targetKey);
@@ -155,14 +173,11 @@ public class IndexedForest<V> {
             if (parent != null) {
                 unlinkedFrom = parent.getKey().substring(account.name.length());
             }
-        }
 
-        if (firstRemoved != null) {
             return new Pair<V, String>(firstRemoved.getPayload(), unlinkedFrom);
-        } else {
-            return new Pair<V, String>(null, unlinkedFrom);
         }
 
+        return new Pair<V, String>(null, null);
     }
 
     private void removeDescendants(Node<V> removed) {
@@ -189,6 +204,11 @@ public class IndexedForest<V> {
         }
     }
 
+    public V get(Account account, String remotePath) {
+        String key = buildKey(account, remotePath);
+        return get(key);
+    }
+
 
     /**
      * Builds a key to index files
index db30355..a47385b 100644 (file)
@@ -103,7 +103,7 @@ public class OperationsService extends Service {
     public static final String ACTION_CREATE_FOLDER = "CREATE_FOLDER";
     public static final String ACTION_SYNC_FILE = "SYNC_FILE";
     public static final String ACTION_SYNC_FOLDER = "SYNC_FOLDER";  // for the moment, just to download
-    public static final String ACTION_CANCEL_SYNC_FOLDER = "CANCEL_SYNC_FOLDER";  // for the moment, just to download
+    //public static final String ACTION_CANCEL_SYNC_FOLDER = "CANCEL_SYNC_FOLDER";  // for the moment, just to download
     public static final String ACTION_MOVE_FILE = "MOVE_FILE";
     
     public static final String ACTION_OPERATION_ADDED = OperationsService.class.getName() + ".OPERATION_ADDED";
@@ -187,21 +187,6 @@ public class OperationsService extends Service {
                 mSyncFolderHandler.sendMessage(msg);
             }
 
-        } else if (ACTION_CANCEL_SYNC_FOLDER.equals(intent.getAction())) {
-            if (!intent.hasExtra(EXTRA_ACCOUNT) || !intent.hasExtra(EXTRA_FILE)) {
-                Log_OC.e(TAG, "Not enough information provided in intent");
-                return START_NOT_STICKY;
-            }
-            final Account account = intent.getParcelableExtra(EXTRA_ACCOUNT);
-            final OCFile file = intent.getParcelableExtra(EXTRA_FILE);
-            // Cancel operation
-            new Thread(new Runnable() {
-                public void run() {
-                    // Cancel the download
-                    mSyncFolderHandler.cancel(account,file);
-                }
-            }).start();
-
         } else {
             Message msg = mOperationsHandler.obtainMessage();
             msg.arg1 = startId;
@@ -272,23 +257,23 @@ public class OperationsService extends Service {
                 new ConcurrentHashMap<OnRemoteOperationListener, Handler>();
         
         private ServiceHandler mServiceHandler = null;   
-        
-        
+
         public OperationsServiceBinder(ServiceHandler serviceHandler) {
             mServiceHandler = serviceHandler;
         }
 
 
         /**
-         * Cancels an operation
+         * Cancels a pending or current synchronization.
          *
-         * TODO
+         * @param account       ownCloud account where the remote folder is stored.
+         * @param file          A folder in the queue of pending synchronizations
          */
-        public void cancel() {
-            // TODO
+        public void cancel(Account account, OCFile file) {
+            mSyncFolderHandler.cancel(account, file);
         }
-        
-        
+
+
         public void clearListeners() {
             
             mBoundListeners.clear();
index 171a2f2..b0116b3 100644 (file)
@@ -28,6 +28,7 @@ import android.util.Pair;
 import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.files.services.FileDownloader;
+import com.owncloud.android.files.services.IndexedForest;
 import com.owncloud.android.lib.common.OwnCloudAccount;
 import com.owncloud.android.lib.common.OwnCloudClient;
 import com.owncloud.android.lib.common.OwnCloudClientManagerFactory;
@@ -37,10 +38,6 @@ import com.owncloud.android.operations.SynchronizeFolderOperation;
 import com.owncloud.android.utils.FileStorageUtils;
 
 import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
 
 /**
  * SyncFolder worker. Performs the pending operations in the order they were requested.
@@ -55,9 +52,11 @@ class SyncFolderHandler extends Handler {
 
     OperationsService mService;
 
-    private ConcurrentMap<String,SynchronizeFolderOperation> mPendingOperations =
-            new ConcurrentHashMap<String,SynchronizeFolderOperation>();
+    private IndexedForest<SynchronizeFolderOperation> mPendingOperations =
+            new IndexedForest<SynchronizeFolderOperation>();
+
     private OwnCloudClient mOwnCloudClient = null;
+    private Account mCurrentAccount = null;
     private FileDataStorageManager mStorageManager;
     private SynchronizeFolderOperation mCurrentSyncOperation;
 
@@ -71,18 +70,16 @@ class SyncFolderHandler extends Handler {
     }
 
 
+    /**
+     * Returns True when the folder located in 'remotePath' in the ownCloud account 'account', or any of its
+     * descendants, is being synchronized (or waiting for it).
+     *
+     * @param account       ownCloud account where the remote folder is stored.
+     * @param remotePath    The path to a folder that could be in the queue of synchronizations.
+     */
     public boolean isSynchronizing(Account account, String remotePath) {
         if (account == null || remotePath == null) return false;
-        String targetKey = buildRemoteName(account, remotePath);
-        synchronized (mPendingOperations) {
-            // TODO - this can be slow when synchronizing a big tree - need a better data structure
-            Iterator<String> it = mPendingOperations.keySet().iterator();
-            boolean found = false;
-            while (it.hasNext() && !found) {
-                found = it.next().startsWith(targetKey);
-            }
-            return found;
-        }
+        return (mPendingOperations.contains(account, remotePath));
     }
 
 
@@ -99,24 +96,24 @@ class SyncFolderHandler extends Handler {
      */
     private void doOperation(Account account, String remotePath) {
 
-        String syncKey = buildRemoteName(account,remotePath);
-
-        synchronized(mPendingOperations) {
-            mCurrentSyncOperation = mPendingOperations.get(syncKey);
-        }
+        mCurrentSyncOperation = mPendingOperations.get(account, remotePath);
 
         if (mCurrentSyncOperation != null) {
             RemoteOperationResult result = null;
 
             try {
-
-                OwnCloudAccount ocAccount = new OwnCloudAccount(account, mService);
-                mOwnCloudClient = OwnCloudClientManagerFactory.getDefaultSingleton().
-                        getClientFor(ocAccount, mService);
-                mStorageManager = new FileDataStorageManager(
-                        account,
-                        mService.getContentResolver()
-                );
+                if (mOwnCloudClient == null || !account.equals(mCurrentAccount)) {
+                    /// get client object to send the request to the ownCloud server, if cannot
+                    mCurrentAccount = account;
+                    mStorageManager = new FileDataStorageManager(
+                            account,
+                            mService.getContentResolver()
+                    );
+                    OwnCloudAccount ocAccount = new OwnCloudAccount(account, mService);
+                    mOwnCloudClient = OwnCloudClientManagerFactory.getDefaultSingleton().
+                            getClientFor(ocAccount, mService);
+
+                }   // else, reuse client from previous operation
 
                 result = mCurrentSyncOperation.execute(mOwnCloudClient, mStorageManager);
 
@@ -125,23 +122,7 @@ class SyncFolderHandler extends Handler {
             } catch (IOException e) {
                 Log_OC.e(TAG, "Error while trying to get autorization", e);
             } finally {
-                synchronized (mPendingOperations) {
-                    mPendingOperations.remove(syncKey);
-                    /*
-                    SynchronizeFolderOperation checkedOp = mCurrentSyncOperation;
-                    String checkedKey = syncKey;
-                    while (checkedOp.getPendingChildrenCount() <= 0) {
-                    // while (!checkedOp.hasChildren()) {
-                        mPendingOperations.remove(checkedKey);
-                        String parentKey = buildRemoteName(account, (new File(checkedOp.getFolderPath())).getParent());
-                        // String parentKey = buildRemoteName(account, checkedOp.getParentPath());
-                        SynchronizeFolderOperation parentOp = mPendingOperations.get(parentKey);
-                        if (parentOp != null) {
-                            parentOp.decreasePendingChildrenCount();
-                        }
-                    }
-                    */
-                }
+                mPendingOperations.removePayload(account, remotePath);
 
                 mService.dispatchResultToOperationListeners(null, mCurrentSyncOperation, result);
 
@@ -151,43 +132,33 @@ class SyncFolderHandler extends Handler {
     }
 
     public void add(Account account, String remotePath, SynchronizeFolderOperation syncFolderOperation){
-        String syncKey = buildRemoteName(account,remotePath);
-        mPendingOperations.putIfAbsent(syncKey,syncFolderOperation);
-        sendBroadcastNewSyncFolder(account, remotePath);
+        mPendingOperations.putIfAbsent(account, remotePath, syncFolderOperation);
+        sendBroadcastNewSyncFolder(account, remotePath);    // TODO upgrade!
     }
 
+
     /**
-     * Cancels sync operations.
-     * @param account       Owncloud account where the remote file is stored.
-     * @param file          File OCFile
+     * Cancels a pending or current sync' operation.
+     *
+     * @param account       ownCloud account where the remote file is stored.
+     * @param file          A file in the queue of pending synchronizations
      */
     public void cancel(Account account, OCFile file){
-        SynchronizeFolderOperation syncOperation = null;
-        String targetKey = buildRemoteName(account, file.getRemotePath());
-        ArrayList<String> keyItems = new ArrayList<String>();
-        synchronized (mPendingOperations) {
-            if (file.isFolder()) {
-                Log_OC.d(TAG, "Canceling pending sync operations");
-                Iterator<String> it = mPendingOperations.keySet().iterator();
-                boolean found = false;
-                while (it.hasNext()) {
-                    String keySyncOperation = it.next();
-                    found = keySyncOperation.startsWith(targetKey);
-                    if (found) {
-                        keyItems.add(keySyncOperation);
-                    }
-                }
-
-            } else {
-                // this is not really expected...
-                Log_OC.d(TAG, "Canceling sync operation");
-                keyItems.add(buildRemoteName(account, file.getRemotePath()));
-            }
-            for (String item: keyItems) {
-                syncOperation = mPendingOperations.remove(item);
-                if (syncOperation != null) {
-                    syncOperation.cancel();
-                }
+        if (account == null || file == null) {
+            Log_OC.e(TAG, "Cannot cancel with NULL parameters");
+            return;
+        }
+        Pair<SynchronizeFolderOperation, String> removeResult =
+                mPendingOperations.remove(account, file.getRemotePath());
+        SynchronizeFolderOperation synchronization = removeResult.first;
+        if (synchronization != null) {
+            synchronization.cancel();
+        } else {
+            // TODO synchronize
+            if (mCurrentSyncOperation != null && mCurrentAccount != null &&
+                    mCurrentSyncOperation.getFolderPath().startsWith(file.getRemotePath()) &&
+                    account.name.equals(mCurrentAccount.name)) {
+                mCurrentSyncOperation.cancel();
             }
         }
 
@@ -195,17 +166,6 @@ class SyncFolderHandler extends Handler {
     }
 
     /**
-     * Builds a key from the account and file to download
-     *
-     * @param account   Account where the file to download is stored
-     * @param path      File path
-     */
-    private String buildRemoteName(Account account, String path) {
-        return account.name + path;
-    }
-
-
-    /**
      * TODO review this method when "folder synchronization" replaces "folder download"; this is a fast and ugly
      * patch.
      */
index 33f16c8..df05ba4 100644 (file)
@@ -43,6 +43,7 @@ import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.datamodel.ThumbnailsCacheManager;\r
 import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;\r
 import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;\r
+import com.owncloud.android.services.OperationsService.OperationsServiceBinder;\r
 import com.owncloud.android.ui.activity.ComponentsGetter;\r
 import com.owncloud.android.utils.DisplayUtils;\r
 import com.owncloud.android.utils.FileStorageUtils;\r
@@ -154,8 +155,10 @@ public class FileListListAdapter extends BaseAdapter implements ListAdapter {
             localStateView.bringToFront();\r
             FileDownloaderBinder downloaderBinder = mTransferServiceGetter.getFileDownloaderBinder();\r
             FileUploaderBinder uploaderBinder = mTransferServiceGetter.getFileUploaderBinder();\r
-            //if (file.isSynchronizing() || file.isDownloading()) {\r
-            if (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, file)) {\r
+            boolean downloading = (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, file));\r
+            OperationsServiceBinder opsBinder = mTransferServiceGetter.getOperationsServiceBinder();\r
+            downloading |= (opsBinder != null && opsBinder.isSynchronizing(mAccount, file.getRemotePath()));\r
+            if (downloading) {\r
                 localStateView.setImageResource(R.drawable.downloading_file_indicator);\r
                 localStateView.setVisibility(View.VISIBLE);\r
             } else if (uploaderBinder != null && uploaderBinder.isUploading(mAccount, file)) {\r