Added indexed tree of folders downloading anything
authorDavid A. Velasco <dvelasco@solidgear.es>
Thu, 22 Jan 2015 16:06:09 +0000 (17:06 +0100)
committerDavid A. Velasco <dvelasco@solidgear.es>
Thu, 22 Jan 2015 16:06:09 +0000 (17:06 +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 [new file with mode: 0644]
src/com/owncloud/android/services/OperationsService.java
src/com/owncloud/android/ui/activity/FileDisplayActivity.java
src/com/owncloud/android/ui/adapter/FileListListAdapter.java
src/com/owncloud/android/ui/fragment/FileDetailFragment.java
src/com/owncloud/android/ui/preview/PreviewImageActivity.java

index f2a0baf..b63446b 100644 (file)
@@ -140,7 +140,9 @@ public class FileMenuFilter {
         boolean downloading = false;
         boolean uploading = false;
         if (mComponentsGetter != null && mFile != null && mAccount != null) {
         boolean downloading = false;
         boolean uploading = false;
         if (mComponentsGetter != null && mFile != null && mAccount != null) {
-            downloading = mFile.isDownloading() || mFile.isSynchronizing();
+            //downloading = mFile.isDownloading() || mFile.isSynchronizing();
+            FileDownloaderBinder downloaderBinder = mComponentsGetter.getFileDownloaderBinder();
+            downloading = (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, mFile));
             FileUploaderBinder uploaderBinder = mComponentsGetter.getFileUploaderBinder();
             uploading = (uploaderBinder != null && uploaderBinder.isUploading(mAccount, mFile));
         }
             FileUploaderBinder uploaderBinder = mComponentsGetter.getFileUploaderBinder();
             uploading = (uploaderBinder != null && uploaderBinder.isUploading(mAccount, mFile));
         }
index c8cb275..47e4472 100644 (file)
@@ -287,7 +287,8 @@ public class FileOperationsHelper {
         if (!file.isFolder()) {
             FileDownloaderBinder downloaderBinder = mFileActivity.getFileDownloaderBinder();
             FileUploaderBinder uploaderBinder = mFileActivity.getFileUploaderBinder();
         if (!file.isFolder()) {
             FileDownloaderBinder downloaderBinder = mFileActivity.getFileDownloaderBinder();
             FileUploaderBinder uploaderBinder = mFileActivity.getFileUploaderBinder();
-            if (downloaderBinder != null && file.isDownloading()) {
+            //if (downloaderBinder != null && file.isDownloading()) {
+            if (downloaderBinder != null && downloaderBinder.isDownloading(account, file)) {
                 // Remove etag for parent, if file is a keep_in_sync
                 if (file.keepInSync()) {
                     OCFile parent = mFileActivity.getStorageManager().getFileById(file.getParentId());
                 // Remove etag for parent, if file is a keep_in_sync
                 if (file.keepInSync()) {
                     OCFile parent = mFileActivity.getStorageManager().getFileById(file.getParentId());
index 61483af..2b70606 100644 (file)
@@ -26,8 +26,6 @@ import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Vector;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Vector;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
 
 import com.owncloud.android.R;
 import com.owncloud.android.authentication.AuthenticatorActivity;
 
 import com.owncloud.android.R;
 import com.owncloud.android.authentication.AuthenticatorActivity;
@@ -89,7 +87,8 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
     private Account mLastAccount = null;
     private FileDataStorageManager mStorageManager;
     
     private Account mLastAccount = null;
     private FileDataStorageManager mStorageManager;
     
-    private ConcurrentMap<String, DownloadFileOperation> mPendingDownloads = new ConcurrentHashMap<String, DownloadFileOperation>();
+    private IndexedForest<DownloadFileOperation> mPendingDownloads = new IndexedForest<DownloadFileOperation>();
+
     private DownloadFileOperation mCurrentDownload = null;
     
     private NotificationManager mNotificationManager;
     private DownloadFileOperation mCurrentDownload = null;
     
     private NotificationManager mNotificationManager;
@@ -106,17 +105,6 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
     }
     
     /**
     }
     
     /**
-     * Builds a key for mPendingDownloads from the account and file to download
-     * 
-     * @param account   Account where the file to download is stored
-     * @param file      File to download
-     */
-    private String buildRemoteName(Account account, OCFile file) {
-        return account.name + file.getRemotePath();
-    }
-
-    
-    /**
      * Service initialization
      */
     @Override
      * Service initialization
      */
     @Override
@@ -133,16 +121,14 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
 
     /**
      * Entry point to add one or several files to the queue of downloads.
 
     /**
      * Entry point to add one or several files to the queue of downloads.
-     * 
-     * New downloads are added calling to startService(), resulting in a call to this method. This ensures the service will keep on working 
-     * although the caller activity goes away.
+     *
+     * New downloads are added calling to startService(), resulting in a call to this method.
+     * This ensures the service will keep on working although the caller activity goes away.
      */
     @Override
     public int onStartCommand(Intent intent, int flags, int startId) {
         if (    !intent.hasExtra(EXTRA_ACCOUNT) ||
                 !intent.hasExtra(EXTRA_FILE)
      */
     @Override
     public int onStartCommand(Intent intent, int flags, int startId) {
         if (    !intent.hasExtra(EXTRA_ACCOUNT) ||
                 !intent.hasExtra(EXTRA_FILE)
-                /*!intent.hasExtra(EXTRA_FILE_PATH) ||
-                !intent.hasExtra(EXTRA_REMOTE_PATH)*/
            ) {
             Log_OC.e(TAG, "Not enough information provided in intent");
             return START_NOT_STICKY;
            ) {
             Log_OC.e(TAG, "Not enough information provided in intent");
             return START_NOT_STICKY;
@@ -161,19 +147,21 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
 
             } else {
 
 
             } else {
 
-                AbstractList<String> requestedDownloads = new Vector<String>(); // dvelasco: now this always contains just one element, but that can change in a near future (download of multiple selection)
-                String downloadKey = buildRemoteName(account, file);
+                AbstractList<String> requestedDownloads = new Vector<String>();
                 try {
                     DownloadFileOperation newDownload = new DownloadFileOperation(account, file);
                 try {
                     DownloadFileOperation newDownload = new DownloadFileOperation(account, file);
-                    mPendingDownloads.putIfAbsent(downloadKey, newDownload);
+                    String downloadKey = mPendingDownloads.putIfAbsent(account, file.getRemotePath(), newDownload);
                     newDownload.addDatatransferProgressListener(this);
                     newDownload.addDatatransferProgressListener((FileDownloaderBinder) mBinder);
                     requestedDownloads.add(downloadKey);
 
                     // Store file on db with state 'downloading'
                     newDownload.addDatatransferProgressListener(this);
                     newDownload.addDatatransferProgressListener((FileDownloaderBinder) mBinder);
                     requestedDownloads.add(downloadKey);
 
                     // Store file on db with state 'downloading'
+                    /*
+                    TODO - check if helps with UI responsiveness, letting only folders use FileDownloaderBinder to check
                     FileDataStorageManager storageManager = new FileDataStorageManager(account, getContentResolver());
                     file.setDownloading(true);
                     storageManager.saveFile(file);
                     FileDataStorageManager storageManager = new FileDataStorageManager(account, getContentResolver());
                     file.setDownloading(true);
                     storageManager.saveFile(file);
+                    */
 
                     sendBroadcastNewDownload(newDownload);
 
 
                     sendBroadcastNewDownload(newDownload);
 
@@ -193,11 +181,12 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
 
         return START_NOT_STICKY;
     }
 
         return START_NOT_STICKY;
     }
-    
-    
+
+
     /**
     /**
-     * Provides a binder object that clients can use to perform operations on the queue of downloads, excepting the addition of new files. 
-     * 
+     * Provides a binder object that clients can use to perform operations on the queue of downloads,
+     * excepting the addition of new files.
+     *
      * Implemented to perform cancellation, pause and resume of existing downloads.
      */
     @Override
      * Implemented to perform cancellation, pause and resume of existing downloads.
      */
     @Override
@@ -215,18 +204,20 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
         return false;   // not accepting rebinding (default behaviour)
     }
 
         return false;   // not accepting rebinding (default behaviour)
     }
 
-    
+
     /**
      *  Binder to let client components to perform operations on the queue of downloads.
     /**
      *  Binder to let client components to perform operations on the queue of downloads.
-     * 
+     *
      *  It provides by itself the available operations.
      */
     public class FileDownloaderBinder extends Binder implements OnDatatransferProgressListener {
         
         /** 
      *  It provides by itself the available operations.
      */
     public class FileDownloaderBinder extends Binder implements OnDatatransferProgressListener {
         
         /** 
-         * Map of listeners that will be reported about progress of downloads from a {@link FileDownloaderBinder} instance 
+         * Map of listeners that will be reported about progress of downloads from a {@link FileDownloaderBinder}
+         * instance.
          */
          */
-        private Map<String, OnDatatransferProgressListener> mBoundListeners = new HashMap<String, OnDatatransferProgressListener>();
+        private Map<Long, OnDatatransferProgressListener> mBoundListeners =
+                new HashMap<Long, OnDatatransferProgressListener>();
 
 
         /**
 
 
         /**
@@ -237,9 +228,7 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
          */
         public void cancel(Account account, OCFile file) {
             DownloadFileOperation download = null;
          */
         public void cancel(Account account, OCFile file) {
             DownloadFileOperation download = null;
-            synchronized (mPendingDownloads) {
-                download = mPendingDownloads.remove(buildRemoteName(account, file));
-            }
+            download = mPendingDownloads.remove(account, file.getRemotePath());
             if (download != null) {
                 download.cancel();
             }
             if (download != null) {
                 download.cancel();
             }
@@ -252,32 +241,19 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
 
 
         /**
 
 
         /**
-         * Returns True when the file described by 'file' in the ownCloud account 'account' is downloading or waiting to download.
+         * Returns True when the file described by 'file' in the ownCloud account 'account' is downloading or
+         * waiting to download.
          * 
          * 
-         * If 'file' is a directory, returns 'true' if some of its descendant files is downloading or waiting to download. 
+         * If 'file' is a directory, returns 'true' if any of its descendant files is downloading or
+         * waiting to download.
          * 
          * 
-         * @param account       Owncloud account where the remote file is stored.
+         * @param account       ownCloud account where the remote file is stored.
          * @param file          A file that could be in the queue of downloads.
          */
          * @param file          A file that could be in the queue of downloads.
          */
-        /*
         public boolean isDownloading(Account account, OCFile file) {
             if (account == null || file == null) return false;
         public boolean isDownloading(Account account, OCFile file) {
             if (account == null || file == null) return false;
-            String targetKey = buildRemoteName(account, file);
-            synchronized (mPendingDownloads) {
-                if (file.isFolder()) {
-                    // this can be slow if there are many downloads :(
-                    Iterator<String> it = mPendingDownloads.keySet().iterator();
-                    boolean found = false;
-                    while (it.hasNext() && !found) {
-                        found = it.next().startsWith(targetKey);
-                    }
-                    return found;
-                } else {
-                    return (mPendingDownloads.containsKey(targetKey));
-                }
-            }
+            return (mPendingDownloads.contains(account, file.getRemotePath()));
         }
         }
-        */
 
         
         /**
 
         
         /**
@@ -285,12 +261,14 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
          * 
          * @param listener      Object to notify about progress of transfer.    
          * @param account       ownCloud account holding the file of interest.
          * 
          * @param listener      Object to notify about progress of transfer.    
          * @param account       ownCloud account holding the file of interest.
-         * @param file          {@link OCfile} of interest for listener. 
+         * @param file          {@link OCFile} of interest for listener.
          */
          */
-        public void addDatatransferProgressListener (OnDatatransferProgressListener listener, Account account, OCFile file) {
+        public void addDatatransferProgressListener (
+                OnDatatransferProgressListener listener, Account account, OCFile file
+        ) {
             if (account == null || file == null || listener == null) return;
             if (account == null || file == null || listener == null) return;
-            String targetKey = buildRemoteName(account, file);
-            mBoundListeners.put(targetKey, listener);
+            //String targetKey = buildKey(account, file.getRemotePath());
+            mBoundListeners.put(file.getFileId(), listener);
         }
         
         
         }
         
         
@@ -299,21 +277,24 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
          * 
          * @param listener      Object to notify about progress of transfer.    
          * @param account       ownCloud account holding the file of interest.
          * 
          * @param listener      Object to notify about progress of transfer.    
          * @param account       ownCloud account holding the file of interest.
-         * @param file          {@link OCfile} of interest for listener. 
+         * @param file          {@link OCFile} of interest for listener.
          */
          */
-        public void removeDatatransferProgressListener (OnDatatransferProgressListener listener, Account account, OCFile file) {
+        public void removeDatatransferProgressListener (
+                OnDatatransferProgressListener listener, Account account, OCFile file
+        ) {
             if (account == null || file == null || listener == null) return;
             if (account == null || file == null || listener == null) return;
-            String targetKey = buildRemoteName(account, file);
-            if (mBoundListeners.get(targetKey) == listener) {
-                mBoundListeners.remove(targetKey);
+            //String targetKey = buildKey(account, file.getRemotePath());
+            Long fileId = file.getFileId();
+            if (mBoundListeners.get(fileId) == listener) {
+                mBoundListeners.remove(fileId);
             }
         }
 
         @Override
         public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer,
                 String fileName) {
             }
         }
 
         @Override
         public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer,
                 String fileName) {
-            String key = buildRemoteName(mCurrentDownload.getAccount(), mCurrentDownload.getFile());
-            OnDatatransferProgressListener boundListener = mBoundListeners.get(key);
+            //String key = buildKey(mCurrentDownload.getAccount(), mCurrentDownload.getFile().getRemotePath());
+            OnDatatransferProgressListener boundListener = mBoundListeners.get(mCurrentDownload.getFile().getFileId());
             if (boundListener != null) {
                 boundListener.onTransferProgress(progressRate, totalTransferredSoFar, totalToTransfer, fileName);
             }
             if (boundListener != null) {
                 boundListener.onTransferProgress(progressRate, totalTransferredSoFar, totalToTransfer, fileName);
             }
@@ -358,11 +339,9 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
      * @param downloadKey   Key to access the download to perform, contained in mPendingDownloads 
      */
     private void downloadFile(String downloadKey) {
      * @param downloadKey   Key to access the download to perform, contained in mPendingDownloads 
      */
     private void downloadFile(String downloadKey) {
-        
-        synchronized(mPendingDownloads) {
-            mCurrentDownload = mPendingDownloads.get(downloadKey);
-        }
-        
+
+        mCurrentDownload = mPendingDownloads.get(downloadKey);
+
         if (mCurrentDownload != null) {
             
             notifyDownloadStart(mCurrentDownload);
         if (mCurrentDownload != null) {
             
             notifyDownloadStart(mCurrentDownload);
@@ -383,21 +362,20 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
                 downloadResult = mCurrentDownload.execute(mDownloadClient);
                 if (downloadResult.isSuccess()) {
                     saveDownloadedFile();
                 downloadResult = mCurrentDownload.execute(mDownloadClient);
                 if (downloadResult.isSuccess()) {
                     saveDownloadedFile();
-                } else {
+                /*} else {
                     updateUnsuccessfulDownloadedFile();
                     updateUnsuccessfulDownloadedFile();
+                    */
                 }
             
             } catch (AccountsException e) {
                 }
             
             } catch (AccountsException e) {
-                Log_OC.e(TAG, "Error while trying to get autorization for " + mLastAccount.name, e);
+                Log_OC.e(TAG, "Error while trying to get authorization for " + mLastAccount.name, e);
                 downloadResult = new RemoteOperationResult(e);
             } catch (IOException e) {
                 downloadResult = new RemoteOperationResult(e);
             } catch (IOException e) {
-                Log_OC.e(TAG, "Error while trying to get autorization for " + mLastAccount.name, e);
+                Log_OC.e(TAG, "Error while trying to get authorization for " + mLastAccount.name, e);
                 downloadResult = new RemoteOperationResult(e);
                 
             } finally {
                 downloadResult = new RemoteOperationResult(e);
                 
             } finally {
-                synchronized(mPendingDownloads) {
-                    mPendingDownloads.remove(downloadKey);
-                }
+                mPendingDownloads.remove(mLastAccount, mCurrentDownload.getRemotePath());
             }
 
             
             }
 
             
@@ -425,7 +403,7 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
         file.setStoragePath(mCurrentDownload.getSavePath());
         file.setFileLength((new File(mCurrentDownload.getSavePath()).length()));
         file.setRemoteId(mCurrentDownload.getFile().getRemoteId());
         file.setStoragePath(mCurrentDownload.getSavePath());
         file.setFileLength((new File(mCurrentDownload.getSavePath()).length()));
         file.setRemoteId(mCurrentDownload.getFile().getRemoteId());
-        file.setDownloading(false);
+        //file.setDownloading(false);
         mStorageManager.saveFile(file);
         mStorageManager.triggerMediaScan(file.getStoragePath());
     }
         mStorageManager.saveFile(file);
         mStorageManager.triggerMediaScan(file.getStoragePath());
     }
@@ -484,7 +462,8 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
      * Callback method to update the progress bar in the status notification.
      */
     @Override
      * Callback method to update the progress bar in the status notification.
      */
     @Override
-    public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer, String filePath) {
+    public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer, String filePath)
+    {
         int percent = (int)(100.0*((double)totalTransferredSoFar)/((double)totalToTransfer));
         if (percent != mLastPercent) {
             mNotificationBuilder.setProgress(100, percent, totalToTransfer < 0);
         int percent = (int)(100.0*((double)totalTransferredSoFar)/((double)totalToTransfer));
         if (percent != mLastPercent) {
             mNotificationBuilder.setProgress(100, percent, totalToTransfer < 0);
@@ -528,7 +507,9 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
                 // let the user update credentials with one click
                 Intent updateAccountCredentials = new Intent(this, AuthenticatorActivity.class);
                 updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACCOUNT, download.getAccount());
                 // let the user update credentials with one click
                 Intent updateAccountCredentials = new Intent(this, AuthenticatorActivity.class);
                 updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACCOUNT, download.getAccount());
-                updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACTION, AuthenticatorActivity.ACTION_UPDATE_EXPIRED_TOKEN);
+                updateAccountCredentials.putExtra(
+                        AuthenticatorActivity.EXTRA_ACTION, AuthenticatorActivity.ACTION_UPDATE_EXPIRED_TOKEN
+                );
                 updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                 updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
                 updateAccountCredentials.addFlags(Intent.FLAG_FROM_BACKGROUND);
                 updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                 updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
                 updateAccountCredentials.addFlags(Intent.FLAG_FROM_BACKGROUND);
@@ -536,7 +517,7 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
                     .setContentIntent(PendingIntent.getActivity(
                         this, (int) System.currentTimeMillis(), updateAccountCredentials, PendingIntent.FLAG_ONE_SHOT));
                 
                     .setContentIntent(PendingIntent.getActivity(
                         this, (int) System.currentTimeMillis(), updateAccountCredentials, PendingIntent.FLAG_ONE_SHOT));
                 
-                mDownloadClient = null;   // grant that future retries on the same account will get the fresh credentials
+                mDownloadClient = null;  // grant that future retries on the same account will get the fresh credentials
                 
             } else {
                 // TODO put something smart in showDetailsIntent
                 
             } else {
                 // TODO put something smart in showDetailsIntent
@@ -546,7 +527,9 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
                         this, (int) System.currentTimeMillis(), showDetailsIntent, 0));
             }
             
                         this, (int) System.currentTimeMillis(), showDetailsIntent, 0));
             }
             
-            mNotificationBuilder.setContentText(ErrorMessageAdapter.getErrorCauseMessage(downloadResult, download, getResources()));
+            mNotificationBuilder.setContentText(
+                    ErrorMessageAdapter.getErrorCauseMessage(downloadResult, download, getResources())
+            );
             mNotificationManager.notify(tickerId, mNotificationBuilder.build());
             
             // Remove success notification
             mNotificationManager.notify(tickerId, mNotificationBuilder.build());
             
             // Remove success notification
@@ -593,38 +576,47 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
 
     /**
      * Cancel operation
 
     /**
      * Cancel operation
-     * @param account       Owncloud account where the remote file is stored.
+     * @param account       ownCloud account where the remote file is stored.
      * @param file          File OCFile
      */
     public void cancel(Account account, OCFile file){
         DownloadFileOperation download = null;
      * @param file          File OCFile
      */
     public void cancel(Account account, OCFile file){
         DownloadFileOperation download = null;
-        String targetKey = buildRemoteName(account, file);
+        //String targetKey = buildKey(account, file.getRemotePath());
         ArrayList<String> keyItems = new ArrayList<String>();
         ArrayList<String> keyItems = new ArrayList<String>();
-        synchronized (mPendingDownloads) {
-            if (file.isFolder()) {
-                Log_OC.d(TAG, "Folder download. Canceling pending downloads (from folder)");
-                Iterator<String> it = mPendingDownloads.keySet().iterator();
-                boolean found = false;
-                while (it.hasNext()) {
-                    String keyDownloadOperation = it.next();
-                    found = keyDownloadOperation.startsWith(targetKey);
-                    if (found) {
-                        keyItems.add(keyDownloadOperation);
-                    }
+        if (file.isFolder()) {
+            Log_OC.d(TAG, "Folder download. Canceling pending downloads (from folder)");
+
+            // TODO
+            /*
+            Iterator<String> it = mPendingDownloads.keySet().iterator();
+            boolean found = false;
+            while (it.hasNext()) {
+                String keyDownloadOperation = it.next();
+                found = keyDownloadOperation.startsWith(targetKey);
+                if (found) {
+                    keyItems.add(keyDownloadOperation);
                 }
                 }
-            } else {
-                // this is not really expected...
-                Log_OC.d(TAG, "Canceling file download");
-                keyItems.add(buildRemoteName(account, file));
             }
             }
-        }
-        for (String item: keyItems) {
-            download = mPendingDownloads.remove(item);
-            Log_OC.d(TAG, "Key removed: " + item);
 
 
+            for (String item: keyItems) {
+                download = mPendingDownloads.remove(item);
+                Log_OC.d(TAG, "Key removed: " + item);
+
+                if (download != null) {
+                    download.cancel();
+                }
+            }
+
+            */
+
+        } else {
+            // this is not really expected...
+            Log_OC.d(TAG, "Canceling file download");
+            download = mPendingDownloads.remove(account, file.getRemotePath());
             if (download != null) {
                 download.cancel();
             }
         }
     }
             if (download != null) {
                 download.cancel();
             }
         }
     }
+
 }
 }
diff --git a/src/com/owncloud/android/files/services/IndexedForest.java b/src/com/owncloud/android/files/services/IndexedForest.java
new file mode 100644 (file)
index 0000000..daf46a5
--- /dev/null
@@ -0,0 +1,179 @@
+/* ownCloud Android client application
+ *   Copyright (C) 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,
+ *   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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package com.owncloud.android.files.services;
+
+import android.accounts.Account;
+
+import com.owncloud.android.datamodel.OCFile;
+
+import java.io.File;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ *  Helper structure to keep the trees of folders containing any file downloading or synchronizing.
+ *
+ *  A map provides the indexation based in hashing.
+ *
+ *  A tree is created per account.
+ *
+ * @author David A. Velasco
+ */
+public class IndexedForest<V> {
+
+    private ConcurrentMap<String, Node<V>> mMap = new ConcurrentHashMap<String, Node<V>>();
+
+    private class Node<V> {
+        String mKey = null;
+        Node<V> mParent = null;
+        Set<Node<V>> mChildren = new HashSet<Node<V>>();    // TODO be careful with hash()
+        V mPayload = null;
+
+        // payload is optional
+        public Node(String key, V payload) {
+            if (key == null) {
+                throw new IllegalArgumentException("Argument key MUST NOT be null");
+            }
+            mKey = key;
+            mPayload = payload;
+        }
+
+        public Node<V> getParent() {
+            return mParent;
+        };
+
+        public Set<Node<V>> getChildren() {
+            return mChildren;
+        }
+
+        public String getKey() {
+            return mKey;
+        }
+
+        public V getPayload() {
+            return mPayload;
+        }
+
+        public void addChild(Node<V> child) {
+            mChildren.add(child);
+            child.setParent(this);
+        }
+
+        private void setParent(Node<V> parent) {
+            mParent = parent;
+        }
+
+        public boolean hasChildren() {
+            return mChildren.size() > 0;
+        }
+
+        public void removeChild(Node<V> removed) {
+            mChildren.remove(removed);
+        }
+    }
+
+
+    public /* synchronized */ String putIfAbsent(Account account, String remotePath, V value) {
+        String targetKey = buildKey(account, remotePath);
+        Node<V> valuedNode = new Node(targetKey, value);
+        mMap.putIfAbsent(
+                targetKey,
+                valuedNode
+        );
+
+        String currentPath = remotePath, parentPath = null, parentKey = null;
+        Node<V> currentNode = valuedNode, parentNode = null;
+        boolean linked = false;
+        while (!OCFile.ROOT_PATH.equals(currentPath) && !linked) {
+            parentPath = new File(currentPath).getParent();
+            if (!parentPath.endsWith(OCFile.PATH_SEPARATOR)) {
+                parentPath += OCFile.PATH_SEPARATOR;
+            }
+            parentKey = buildKey(account, parentPath);
+            parentNode = mMap.get(parentKey);
+            if (parentNode == null) {
+                parentNode = new Node(parentKey, null);
+                parentNode.addChild(currentNode);
+                mMap.put(parentKey, parentNode);
+            } else {
+                parentNode.addChild(currentNode);
+                linked = true;
+            }
+            currentPath = parentPath;
+            currentNode = parentNode;
+        }
+
+        return targetKey;
+    };
+
+    public /* synchronized */ V remove(Account account, String remotePath) {
+        String targetKey = buildKey(account, remotePath);
+        Node<V> firstRemoved = mMap.remove(targetKey);
+
+        if (firstRemoved != null) {
+            Node<V> removed = firstRemoved;
+            Node<V> parent = removed.getParent();
+            while (parent != null) {
+                parent.removeChild(removed);
+                if (!parent.hasChildren()) {
+                    removed = mMap.remove(parent.getKey());
+                    parent = removed.getParent();
+                } else {
+                    parent = null;
+                }
+            }
+        }
+
+        if (firstRemoved != null) {
+            return firstRemoved.getPayload();
+        } else {
+            return null;
+        }
+
+    }
+
+    public boolean contains(Account account, String remotePath) {
+        String targetKey = buildKey(account, remotePath);
+        return mMap.containsKey(targetKey);
+    }
+
+    public /* synchronized */ V get(String key) {
+        Node<V> node = mMap.get(key);
+        if (node != null) {
+            return node.getPayload();
+        } else {
+            return null;
+        }
+    }
+
+
+    /**
+     * Builds a key to index files
+     *
+     * @param account       Account where the file to download is stored
+     * @param remotePath    Path of the file in the server
+     */
+    private String buildKey(Account account, String remotePath) {
+        return account.name + remotePath;
+    }
+
+
+
+}
index 779e5b6..5e038f1 100644 (file)
@@ -17,6 +17,7 @@
 
 package com.owncloud.android.services;
 
 
 package com.owncloud.android.services;
 
+import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Iterator;
@@ -469,8 +470,22 @@ public class OperationsService extends Service {
                 } catch (IOException e) {
                     Log_OC.e(TAG, "Error while trying to get autorization", e);
                 } finally {
                 } catch (IOException e) {
                     Log_OC.e(TAG, "Error while trying to get autorization", e);
                 } finally {
-                    synchronized(mPendingOperations) {
+                    synchronized (mPendingOperations) {
                         mPendingOperations.remove(syncKey);
                         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();
+                            }
+                        }
+                        */
                     }
 
                     mService.dispatchResultToOperationListeners(null, mCurrentSyncOperation, result);
                     }
 
                     mService.dispatchResultToOperationListeners(null, mCurrentSyncOperation, result);
index fbd35ed..a0ce0db 100644 (file)
@@ -1255,7 +1255,7 @@ OnSslUntrustedCertListener, OnEnforceableRefreshListener {
 
 
     /**
 
 
     /**
-     * Class waiting for broadcast events from the {@link FielDownloader} service.
+     * Class waiting for broadcast events from the {@link FileDownloader} service.
      * 
      * Updates the UI when a download is started or finished, provided that it is relevant for the
      * current folder.
      * 
      * Updates the UI when a download is started or finished, provided that it is relevant for the
      * current folder.
@@ -1725,7 +1725,8 @@ OnSslUntrustedCertListener, OnEnforceableRefreshListener {
 
     private void requestForDownload() {
         Account account = getAccount();
 
     private void requestForDownload() {
         Account account = getAccount();
-        if (mWaitingToPreview.isDownloading()) {
+        //if (!mWaitingToPreview.isDownloading()) {
+        if (!mDownloaderBinder.isDownloading(account, mWaitingToPreview)) {
             Intent i = new Intent(this, FileDownloader.class);
             i.putExtra(FileDownloader.EXTRA_ACCOUNT, account);
             i.putExtra(FileDownloader.EXTRA_FILE, mWaitingToPreview);
             Intent i = new Intent(this, FileDownloader.class);
             i.putExtra(FileDownloader.EXTRA_ACCOUNT, account);
             i.putExtra(FileDownloader.EXTRA_FILE, mWaitingToPreview);
@@ -1781,9 +1782,10 @@ OnSslUntrustedCertListener, OnEnforceableRefreshListener {
     }
     
     private void requestForDownload(OCFile file) {
     }
     
     private void requestForDownload(OCFile file) {
-        if (file.isDownloading()) {
+        Account account = getAccount();
+        if (!mDownloaderBinder.isDownloading(account, mWaitingToPreview)) {
             Intent i = new Intent(this, FileDownloader.class);
             Intent i = new Intent(this, FileDownloader.class);
-            i.putExtra(FileDownloader.EXTRA_ACCOUNT, getAccount());
+            i.putExtra(FileDownloader.EXTRA_ACCOUNT, account);
             i.putExtra(FileDownloader.EXTRA_FILE, file);
             startService(i);
         }
             i.putExtra(FileDownloader.EXTRA_FILE, file);
             startService(i);
         }
index bfec11c..33f16c8 100644 (file)
@@ -41,6 +41,7 @@ import com.owncloud.android.authentication.AccountUtils;
 import com.owncloud.android.datamodel.FileDataStorageManager;\r
 import com.owncloud.android.datamodel.OCFile;\r
 import com.owncloud.android.datamodel.ThumbnailsCacheManager;\r
 import com.owncloud.android.datamodel.FileDataStorageManager;\r
 import com.owncloud.android.datamodel.OCFile;\r
 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.ui.activity.ComponentsGetter;\r
 import com.owncloud.android.utils.DisplayUtils;\r
 import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;\r
 import com.owncloud.android.ui.activity.ComponentsGetter;\r
 import com.owncloud.android.utils.DisplayUtils;\r
@@ -151,8 +152,10 @@ public class FileListListAdapter extends BaseAdapter implements ListAdapter {
 \r
             ImageView localStateView = (ImageView) view.findViewById(R.id.imageView2);\r
             localStateView.bringToFront();\r
 \r
             ImageView localStateView = (ImageView) view.findViewById(R.id.imageView2);\r
             localStateView.bringToFront();\r
+            FileDownloaderBinder downloaderBinder = mTransferServiceGetter.getFileDownloaderBinder();\r
             FileUploaderBinder uploaderBinder = mTransferServiceGetter.getFileUploaderBinder();\r
             FileUploaderBinder uploaderBinder = mTransferServiceGetter.getFileUploaderBinder();\r
-            if (file.isSynchronizing() || file.isDownloading()) {\r
+            //if (file.isSynchronizing() || file.isDownloading()) {\r
+            if (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, file)) {\r
                 localStateView.setImageResource(R.drawable.downloading_file_indicator);\r
                 localStateView.setVisibility(View.VISIBLE);\r
             } else if (uploaderBinder != null && uploaderBinder.isUploading(mAccount, file)) {\r
                 localStateView.setImageResource(R.drawable.downloading_file_indicator);\r
                 localStateView.setVisibility(View.VISIBLE);\r
             } else if (uploaderBinder != null && uploaderBinder.isUploading(mAccount, file)) {\r
index dc607b5..30ac7e1 100644 (file)
@@ -346,8 +346,12 @@ public class FileDetailFragment extends FileFragment implements OnClickListener
             cb.setChecked(file.keepInSync());
 
             // configure UI for depending upon local state of the file
             cb.setChecked(file.keepInSync());
 
             // configure UI for depending upon local state of the file
+            FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder();
             FileUploaderBinder uploaderBinder = mContainerActivity.getFileUploaderBinder();
             FileUploaderBinder uploaderBinder = mContainerActivity.getFileUploaderBinder();
-            if (transferring || file.isDownloading() || uploaderBinder.isUploading(mAccount, file)) {
+            if (transferring ||
+                    (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, file)) ||
+                    (uploaderBinder != null && uploaderBinder.isUploading(mAccount, file))
+                    ) {
                 setButtonsForTransferring();
                 
             } else if (file.isDown()) {
                 setButtonsForTransferring();
                 
             } else if (file.isDown()) {
@@ -446,8 +450,10 @@ public class FileDetailFragment extends FileFragment implements OnClickListener
             getView().findViewById(R.id.fdProgressBlock).setVisibility(View.VISIBLE);
             TextView progressText = (TextView)getView().findViewById(R.id.fdProgressText);
             progressText.setVisibility(View.VISIBLE);
             getView().findViewById(R.id.fdProgressBlock).setVisibility(View.VISIBLE);
             TextView progressText = (TextView)getView().findViewById(R.id.fdProgressText);
             progressText.setVisibility(View.VISIBLE);
+            FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder();
             FileUploaderBinder uploaderBinder = mContainerActivity.getFileUploaderBinder();
             FileUploaderBinder uploaderBinder = mContainerActivity.getFileUploaderBinder();
-            if (getFile().isDownloading()) {
+            //if (getFile().isDownloading()) {
+            if (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, getFile())) {
                 progressText.setText(R.string.downloader_download_in_progress_ticker);
             } else if (uploaderBinder != null && uploaderBinder.isUploading(mAccount, getFile())) {
                 progressText.setText(R.string.uploader_upload_in_progress_ticker);
                 progressText.setText(R.string.downloader_download_in_progress_ticker);
             } else if (uploaderBinder != null && uploaderBinder.isUploading(mAccount, getFile())) {
                 progressText.setText(R.string.uploader_upload_in_progress_ticker);
index 13d6adc..1cee30e 100644 (file)
@@ -365,7 +365,7 @@ ViewPager.OnPageChangeListener, OnRemoteOperationListener {
         if (mDownloaderBinder == null) {
             Log_OC.d(TAG, "requestForDownload called without binder to download service");
             
         if (mDownloaderBinder == null) {
             Log_OC.d(TAG, "requestForDownload called without binder to download service");
             
-        } else if (!file.isDownloading()) {
+        } else if (!mDownloaderBinder.isDownloading(getAccount(), file)) {
             Intent i = new Intent(this, FileDownloader.class);
             i.putExtra(FileDownloader.EXTRA_ACCOUNT, getAccount());
             i.putExtra(FileDownloader.EXTRA_FILE, file);
             Intent i = new Intent(this, FileDownloader.class);
             i.putExtra(FileDownloader.EXTRA_ACCOUNT, getAccount());
             i.putExtra(FileDownloader.EXTRA_FILE, file);