Browse Source

Merge pull request #886 from owncloud/cancel_transfer_for_deleted_users

Cancel pending or in-progress downloads and uploads when an account is deleted from the device.
David A. Velasco 10 years ago
parent
commit
b46ac9c649

+ 1 - 2
AndroidManifest.xml

@@ -3,7 +3,7 @@
   ownCloud Android client application
 
   Copyright (C) 2012  Bartek Przybylski
-  Copyright (C) 2012-2014 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,
@@ -86,7 +86,6 @@
             android:name=".ui.activity.Preferences"
             android:theme="@style/Theme.ownCloud" >
         </activity>
-
         <activity	
             android:name=".ui.preview.PreviewImageActivity" 
             />

+ 1 - 1
owncloud-android-library

@@ -1 +1 @@
-Subproject commit c2b5ddd2364f53201e7df6210ce04bc6bf4a2bbd
+Subproject commit 942971a202f7404b135b26795c1b545c390a023f

+ 233 - 165
src/com/owncloud/android/files/services/FileDownloader.java

@@ -27,6 +27,7 @@ import java.util.Map;
 import java.util.Vector;
 
 import com.owncloud.android.R;
+import com.owncloud.android.authentication.AccountUtils;
 import com.owncloud.android.authentication.AuthenticatorActivity;
 import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.OCFile;
@@ -49,7 +50,9 @@ import com.owncloud.android.ui.preview.PreviewImageFragment;
 import com.owncloud.android.utils.ErrorMessageAdapter;
 
 import android.accounts.Account;
+import android.accounts.AccountManager;
 import android.accounts.AccountsException;
+import android.accounts.OnAccountsUpdateListener;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.Service;
@@ -64,19 +67,20 @@ import android.os.Process;
 import android.support.v4.app.NotificationCompat;
 import android.util.Pair;
 
-public class FileDownloader extends Service implements OnDatatransferProgressListener {
-    
+public class FileDownloader extends Service
+        implements OnDatatransferProgressListener, OnAccountsUpdateListener {
+
     public static final String EXTRA_ACCOUNT = "ACCOUNT";
     public static final String EXTRA_FILE = "FILE";
 
     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";    
+    public static final String EXTRA_DOWNLOAD_RESULT = "RESULT";
     public static final String EXTRA_FILE_PATH = "FILE_PATH";
     public static final String EXTRA_REMOTE_PATH = "REMOTE_PATH";
     public static final String EXTRA_LINKED_TO_PATH = "LINKED_TO";
     public static final String ACCOUNT_NAME = "ACCOUNT_NAME";
-    
+
     private static final String TAG = "FileDownloader";
 
     private Looper mServiceLooper;
@@ -85,24 +89,24 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
     private OwnCloudClient mDownloadClient = null;
     private Account mCurrentAccount = null;
     private FileDataStorageManager mStorageManager;
-    
+
     private IndexedForest<DownloadFileOperation> mPendingDownloads = new IndexedForest<DownloadFileOperation>();
 
     private DownloadFileOperation mCurrentDownload = null;
-    
+
     private NotificationManager mNotificationManager;
     private NotificationCompat.Builder mNotificationBuilder;
     private int mLastPercent;
 
-    
+
     public static String getDownloadAddedMessage() {
         return FileDownloader.class.getName() + DOWNLOAD_ADDED_MESSAGE;
     }
-    
+
     public static String getDownloadFinishMessage() {
         return FileDownloader.class.getName() + DOWNLOAD_FINISH_MESSAGE;
     }
-    
+
     /**
      * Service initialization
      */
@@ -116,6 +120,10 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
         mServiceLooper = thread.getLooper();
         mServiceHandler = new ServiceHandler(mServiceLooper, this);
         mBinder = new FileDownloaderBinder();
+
+        // add AccountsUpdatedListener
+        AccountManager am = AccountManager.get(getApplicationContext());
+        am.addOnAccountsUpdatedListener(this, null, false);
     }
 
 
@@ -124,19 +132,24 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
      */
     @Override
     public void onDestroy() {
-        Log_OC.v(TAG, "Destroying service" );
+        Log_OC.v(TAG, "Destroying service");
         mBinder = null;
         mServiceHandler = null;
         mServiceLooper.quit();
         mServiceLooper = null;
         mNotificationManager = null;
+
+        // remove AccountsUpdatedListener
+        AccountManager am = AccountManager.get(getApplicationContext());
+        am.removeOnAccountsUpdatedListener(this);
+
         super.onDestroy();
     }
 
 
     /**
      * Entry point to add one or several files to the queue of downloads.
-     *
+     * <p/>
      * 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.
      */
@@ -144,9 +157,9 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
     public int onStartCommand(Intent intent, int flags, int startId) {
         Log_OC.d(TAG, "Starting command with id " + startId);
 
-        if (    !intent.hasExtra(EXTRA_ACCOUNT) ||
+        if (!intent.hasExtra(EXTRA_ACCOUNT) ||
                 !intent.hasExtra(EXTRA_FILE)
-           ) {
+                ) {
             Log_OC.e(TAG, "Not enough information provided in intent");
             return START_NOT_STICKY;
         } else {
@@ -158,22 +171,22 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
                     "Received request to download file"
             );*/
 
-                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(
+            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;
-                    requestedDownloads.add(downloadKey);
+                );
+                String downloadKey = putResult.first;
+                requestedDownloads.add(downloadKey);
                     /*Log_OC.v(
                         "NOW " + TAG + ", thread " + Thread.currentThread().getName(),
                         "Download on " + file.getRemotePath() + " added to queue"
                     );*/
 
-                    // Store file on db with state 'downloading'
+                // 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());
@@ -181,19 +194,19 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
                     storageManager.saveFile(file);
                     */
 
-                    sendBroadcastNewDownload(newDownload, putResult.second);
+                sendBroadcastNewDownload(newDownload, putResult.second);
 
-                } catch (IllegalArgumentException e) {
-                    Log_OC.e(TAG, "Not enough information provided in intent: " + e.getMessage());
-                    return START_NOT_STICKY;
-                }
+            } catch (IllegalArgumentException e) {
+                Log_OC.e(TAG, "Not enough information provided in intent: " + e.getMessage());
+                return START_NOT_STICKY;
+            }
 
-                if (requestedDownloads.size() > 0) {
-                    Message msg = mServiceHandler.obtainMessage();
-                    msg.arg1 = startId;
-                    msg.obj = requestedDownloads;
-                    mServiceHandler.sendMessage(msg);
-                }
+            if (requestedDownloads.size() > 0) {
+                Message msg = mServiceHandler.obtainMessage();
+                msg.arg1 = startId;
+                msg.obj = requestedDownloads;
+                mServiceHandler.sendMessage(msg);
+            }
             //}
         }
 
@@ -204,7 +217,7 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
     /**
      * Provides a binder object that clients can use to perform operations on the queue of downloads,
      * excepting the addition of new files.
-     *
+     * <p/>
      * Implemented to perform cancellation, pause and resume of existing downloads.
      */
     @Override
@@ -218,19 +231,29 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
      */
     @Override
     public boolean onUnbind(Intent intent) {
-        ((FileDownloaderBinder)mBinder).clearListeners();
+        ((FileDownloaderBinder) mBinder).clearListeners();
         return false;   // not accepting rebinding (default behaviour)
     }
 
+    @Override
+    public void onAccountsUpdated(Account[] accounts) {
+         //review the current download and cancel it if its account doesn't exist
+        if (mCurrentDownload != null &&
+                !AccountUtils.exists(mCurrentDownload.getAccount(), getApplicationContext())) {
+            mCurrentDownload.cancel();
+        }
+        // The rest of downloads are cancelled when they try to start
+    }
+
 
     /**
-     *  Binder to let client components to perform operations on the queue of downloads.
-     *
-     *  It provides by itself the available operations.
+     * Binder to let client components to perform operations on the queue of downloads.
+     * <p/>
+     * 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.
          */
@@ -241,8 +264,8 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
         /**
          * Cancels a pending or current download of a remote file.
          *
-         * @param account       ownCloud account where the remote file is stored.
-         * @param file          A file in the queue of pending downloads
+         * @param account ownCloud account where the remote file is stored.
+         * @param file    A file in the queue of pending downloads
          */
         public void cancel(Account account, OCFile file) {
             /*Log_OC.v(
@@ -251,7 +274,8 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
             );
             Log_OC.v(   "NOW " + TAG + ", thread " + Thread.currentThread().getName(),
                     "Removing download of " + file.getRemotePath());*/
-            Pair<DownloadFileOperation, String> removeResult = mPendingDownloads.remove(account, file.getRemotePath());
+            Pair<DownloadFileOperation, String> removeResult =
+                    mPendingDownloads.remove(account, file.getRemotePath());
             DownloadFileOperation download = removeResult.first;
             if (download != null) {
                 /*Log_OC.v(   "NOW " + TAG + ", thread " + Thread.currentThread().getName(),
@@ -267,8 +291,25 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
                 }
             }
         }
-        
-        
+
+        /**
+         * Cancels a pending or current upload for an account
+         *
+         * @param account Owncloud accountName where the remote file will be stored.
+         */
+        public void cancel(Account account) {
+            Log_OC.d(TAG, "Account= " + account.name);
+
+            if (mCurrentDownload != null) {
+                Log_OC.d(TAG, "Current Download Account= " + mCurrentDownload.getAccount().name);
+                if (mCurrentDownload.getAccount().name.equals(account.name)) {
+                    mCurrentDownload.cancel();
+                }
+            }
+            // Cancel pending downloads
+            cancelDownloadsForAccount(account);
+        }
+
         public void clearListeners() {
             mBoundListeners.clear();
         }
@@ -277,43 +318,43 @@ 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.
-         * 
+         * <p/>
          * 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 file          A file that could be in the queue of downloads.
+         *
+         * @param account ownCloud account where the remote file is stored.
+         * @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;
             return (mPendingDownloads.contains(account, file.getRemotePath()));
         }
 
-        
+
         /**
          * Adds a listener interested in the progress of the download for a concrete file.
-         * 
-         * @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 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.
          */
-        public void addDatatransferProgressListener (
+        public void addDatatransferProgressListener(
                 OnDatatransferProgressListener listener, Account account, OCFile file
         ) {
             if (account == null || file == null || listener == null) return;
             //String targetKey = buildKey(account, file.getRemotePath());
             mBoundListeners.put(file.getFileId(), listener);
         }
-        
-        
+
+
         /**
          * Removes a listener interested in the progress of the download for a concrete file.
-         * 
-         * @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 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.
          */
-        public void removeDatatransferProgressListener (
+        public void removeDatatransferProgressListener(
                 OnDatatransferProgressListener listener, Account account, OCFile file
         ) {
             if (account == null || file == null || listener == null) return;
@@ -326,25 +367,37 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
 
         @Override
         public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer,
-                String fileName) {
+                                       String fileName) {
             //String key = buildKey(mCurrentDownload.getAccount(), mCurrentDownload.getFile().getRemotePath());
             OnDatatransferProgressListener boundListener = mBoundListeners.get(mCurrentDownload.getFile().getFileId());
             if (boundListener != null) {
                 boundListener.onTransferProgress(progressRate, totalTransferredSoFar, totalToTransfer, fileName);
             }
         }
-        
+
+        /**
+         * Review downloads and cancel it if its account doesn't exist
+         */
+        public void checkAccountOfCurrentDownload() {
+            if (mCurrentDownload != null &&
+                    !AccountUtils.exists(mCurrentDownload.getAccount(), getApplicationContext())) {
+                mCurrentDownload.cancel();
+            }
+            // The rest of downloads are cancelled when they try to start
+        }
+
     }
-    
-    
-    /** 
-     * Download worker. Performs the pending downloads in the order they were requested. 
-     * 
-     * Created with the Looper of a new thread, started in {@link FileUploader#onCreate()}. 
+
+
+    /**
+     * Download worker. Performs the pending downloads in the order they were requested.
+     * <p/>
+     * Created with the Looper of a new thread, started in {@link FileUploader#onCreate()}.
      */
     private static class ServiceHandler extends Handler {
         // don't make it a final class, and don't remove the static ; lint will warn about a possible memory leak
         FileDownloader mService;
+
         public ServiceHandler(Looper looper, FileDownloader service) {
             super(looper);
             if (service == null)
@@ -367,12 +420,12 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
             mService.stopSelf(msg.arg1);
         }
     }
-    
+
 
     /**
      * Core download method: requests a file to download and stores it.
-     * 
-     * @param downloadKey   Key to access the download to perform, contained in mPendingDownloads 
+     *
+     * @param downloadKey Key to access the download to perform, contained in mPendingDownloads
      */
     private void downloadFile(String downloadKey) {
 
@@ -381,54 +434,61 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
         mCurrentDownload = mPendingDownloads.get(downloadKey);
 
         if (mCurrentDownload != null) {
-            
-            notifyDownloadStart(mCurrentDownload);
+            // Detect if the account exists
+            if (AccountUtils.exists(mCurrentDownload.getAccount(), getApplicationContext())) {
+                Log_OC.d(TAG, "Account " + mCurrentDownload.getAccount().name + " exists");
+                notifyDownloadStart(mCurrentDownload);
 
-            RemoteOperationResult downloadResult = null;
-            try {
-                /// prepare client object to send the request to the ownCloud server
-                if (mCurrentAccount == null || !mCurrentAccount.equals(mCurrentDownload.getAccount())) {
-                    mCurrentAccount = mCurrentDownload.getAccount();
-                    mStorageManager = new FileDataStorageManager(
-                            mCurrentAccount,
-                            getContentResolver()
-                    );
-                }   // else, reuse storage manager from previous operation
-
-                // always get client from client manager, to get fresh credentials in case of update
-                OwnCloudAccount ocAccount = new OwnCloudAccount(mCurrentAccount, this);
-                mDownloadClient = OwnCloudClientManagerFactory.getDefaultSingleton().
-                        getClientFor(ocAccount, this);
-
-
-                /// perform the download
-                /*Log_OC.v(   "NOW " + TAG + ", thread " + Thread.currentThread().getName(),
+                RemoteOperationResult downloadResult = null;
+                try {
+                    /// prepare client object to send the request to the ownCloud server
+                    if (mCurrentAccount == null || !mCurrentAccount.equals(mCurrentDownload.getAccount())) {
+                        mCurrentAccount = mCurrentDownload.getAccount();
+                        mStorageManager = new FileDataStorageManager(
+                                mCurrentAccount,
+                                getContentResolver()
+                        );
+                    }   // else, reuse storage manager from previous operation
+
+                    // always get client from client manager, to get fresh credentials in case of update
+                    OwnCloudAccount ocAccount = new OwnCloudAccount(mCurrentAccount, this);
+                    mDownloadClient = OwnCloudClientManagerFactory.getDefaultSingleton().
+                            getClientFor(ocAccount, this);
+
+
+                    /// perform the download
+                    /*Log_OC.v(   "NOW " + TAG + ", thread " + Thread.currentThread().getName(),
                         "Executing download of " + mCurrentDownload.getRemotePath());*/
-                downloadResult = mCurrentDownload.execute(mDownloadClient);
-                if (downloadResult.isSuccess()) {
-                    saveDownloadedFile();
-                }
-            
-            } catch (AccountsException 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 " + mCurrentAccount.name, e);
-                downloadResult = new RemoteOperationResult(e);
-                
-            } finally {
+                    downloadResult = mCurrentDownload.execute(mDownloadClient);
+                    if (downloadResult.isSuccess()) {
+                        saveDownloadedFile();
+                    }
+
+                } catch (AccountsException 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 " + mCurrentAccount.name, e);
+                    downloadResult = new RemoteOperationResult(e);
+
+                } finally {
                 /*Log_OC.v(   "NOW " + TAG + ", thread " + Thread.currentThread().getName(),
                         "Removing payload " + mCurrentDownload.getRemotePath());*/
 
-                Pair<DownloadFileOperation, String> removeResult =
-                        mPendingDownloads.removePayload(mCurrentAccount, mCurrentDownload.getRemotePath());
+                    Pair<DownloadFileOperation, String> removeResult =
+                            mPendingDownloads.removePayload(mCurrentAccount, mCurrentDownload.getRemotePath());
 
-                /// notify result
-                notifyDownloadResult(mCurrentDownload, downloadResult);
+                    /// notify result
+                    notifyDownloadResult(mCurrentDownload, downloadResult);
 
-                sendBroadcastDownloadFinished(mCurrentDownload, downloadResult, removeResult.second);
-            }
+                    sendBroadcastDownloadFinished(mCurrentDownload, downloadResult, removeResult.second);
+                }
+            } else {
+                // Cancel the transfer
+                Log_OC.d(TAG, "Account " + mCurrentDownload.getAccount().toString() + " doesn't exist");
+                cancelDownloadsForAccount(mCurrentDownload.getAccount());
 
+            }
         }
     }
 
@@ -465,13 +525,13 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
 
     /**
      * Creates a status notification to show the download progress
-     * 
-     * @param download  Download operation starting.
+     *
+     * @param download Download operation starting.
      */
     private void notifyDownloadStart(DownloadFileOperation download) {
         /// create status notification with a progress bar
         mLastPercent = 0;
-        mNotificationBuilder = 
+        mNotificationBuilder =
                 NotificationBuilderWithProgressBar.newNotificationBuilderWithProgressBar(this);
         mNotificationBuilder
                 .setSmallIcon(R.drawable.notification_icon)
@@ -483,7 +543,7 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
                         String.format(getString(R.string.downloader_download_in_progress_content), 0,
                                 new File(download.getSavePath()).getName())
                 );
-                
+
         /// includes a pending intent in the notification showing the details view of the file
         Intent showDetailsIntent = null;
         if (PreviewImageFragment.canBePreviewed(download.getFile())) {
@@ -494,22 +554,21 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
         showDetailsIntent.putExtra(FileActivity.EXTRA_FILE, download.getFile());
         showDetailsIntent.putExtra(FileActivity.EXTRA_ACCOUNT, download.getAccount());
         showDetailsIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
-        
+
         mNotificationBuilder.setContentIntent(PendingIntent.getActivity(
-            this, (int) System.currentTimeMillis(), showDetailsIntent, 0
+                this, (int) System.currentTimeMillis(), showDetailsIntent, 0
         ));
 
         mNotificationManager.notify(R.string.downloader_download_in_progress_ticker, mNotificationBuilder.build());
     }
 
-    
+
     /**
      * Callback method to update the progress bar in the status notification.
      */
     @Override
-    public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer, String filePath)
-    {
-        int percent = (int)(100.0*((double)totalTransferredSoFar)/((double)totalToTransfer));
+    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);
             String fileName = filePath.substring(filePath.lastIndexOf(FileUtils.PATH_SEPARATOR) + 1);
@@ -519,36 +578,36 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
         }
         mLastPercent = percent;
     }
-    
-    
+
+
     /**
      * Updates the status notification with the result of a download operation.
-     * 
-     * @param downloadResult    Result of the download operation.
-     * @param download          Finished download operation
+     *
+     * @param downloadResult Result of the download operation.
+     * @param download       Finished download operation
      */
     private void notifyDownloadResult(DownloadFileOperation download, RemoteOperationResult downloadResult) {
         mNotificationManager.cancel(R.string.downloader_download_in_progress_ticker);
         if (!downloadResult.isCancelled()) {
-            int tickerId = (downloadResult.isSuccess()) ? R.string.downloader_download_succeeded_ticker : 
-                R.string.downloader_download_failed_ticker;
-            
+            int tickerId = (downloadResult.isSuccess()) ? R.string.downloader_download_succeeded_ticker :
+                    R.string.downloader_download_failed_ticker;
+
             boolean needsToUpdateCredentials = (
                     downloadResult.getCode() == ResultCode.UNAUTHORIZED ||
-                    downloadResult.isIdPRedirection()
+                            downloadResult.isIdPRedirection()
             );
-            tickerId = (needsToUpdateCredentials) ? 
+            tickerId = (needsToUpdateCredentials) ?
                     R.string.downloader_download_failed_credentials_error : tickerId;
-            
+
             mNotificationBuilder
-            .setTicker(getString(tickerId))
-            .setContentTitle(getString(tickerId))
-            .setAutoCancel(true)
-            .setOngoing(false)
-            .setProgress(0, 0, false);
-            
+                    .setTicker(getString(tickerId))
+                    .setContentTitle(getString(tickerId))
+                    .setAutoCancel(true)
+                    .setOngoing(false)
+                    .setProgress(0, 0, false);
+
             if (needsToUpdateCredentials) {
-                
+
                 // let the user update credentials with one click
                 Intent updateAccountCredentials = new Intent(this, AuthenticatorActivity.class);
                 updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACCOUNT, download.getAccount());
@@ -559,41 +618,41 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
                 updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
                 updateAccountCredentials.addFlags(Intent.FLAG_FROM_BACKGROUND);
                 mNotificationBuilder
-                    .setContentIntent(PendingIntent.getActivity(
-                        this, (int) System.currentTimeMillis(), updateAccountCredentials, PendingIntent.FLAG_ONE_SHOT));
-                
+                        .setContentIntent(PendingIntent.getActivity(
+                                this, (int) System.currentTimeMillis(), updateAccountCredentials, PendingIntent.FLAG_ONE_SHOT));
+
             } else {
                 // TODO put something smart in showDetailsIntent
-                Intent   showDetailsIntent = new Intent();
+                Intent showDetailsIntent = new Intent();
                 mNotificationBuilder
-                    .setContentIntent(PendingIntent.getActivity(
-                        this, (int) System.currentTimeMillis(), showDetailsIntent, 0));
+                        .setContentIntent(PendingIntent.getActivity(
+                                this, (int) System.currentTimeMillis(), showDetailsIntent, 0));
             }
-            
+
             mNotificationBuilder.setContentText(
                     ErrorMessageAdapter.getErrorCauseMessage(downloadResult, download, getResources())
             );
             mNotificationManager.notify(tickerId, mNotificationBuilder.build());
-            
+
             // Remove success notification
-            if (downloadResult.isSuccess()) {   
+            if (downloadResult.isSuccess()) {
                 // Sleep 2 seconds, so show the notification before remove it
                 NotificationDelayer.cancelWithDelay(
-                        mNotificationManager, 
-                        R.string.downloader_download_succeeded_ticker, 
+                        mNotificationManager,
+                        R.string.downloader_download_succeeded_ticker,
                         2000);
             }
-                
+
         }
     }
-    
-    
+
+
     /**
      * Sends a broadcast when a download finishes in order to the interested activities can update their view
-     * 
-     * @param download                  Finished download operation
-     * @param downloadResult            Result of the download operation
-     * @param unlinkedFromRemotePath    Path in the downloads tree where the download was unlinked from
+     *
+     * @param download               Finished download operation
+     * @param downloadResult         Result of the download operation
+     * @param unlinkedFromRemotePath Path in the downloads tree where the download was unlinked from
      */
     private void sendBroadcastDownloadFinished(
             DownloadFileOperation download,
@@ -609,13 +668,13 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
         }
         sendStickyBroadcast(end);
     }
-    
-    
+
+
     /**
      * Sends a broadcast when a new download is added to the queue.
-     * 
-     * @param download              Added download operation
-     * @param linkedToRemotePath    Path in the downloads tree where the download was linked to
+     *
+     * @param download           Added download operation
+     * @param linkedToRemotePath Path in the downloads tree where the download was linked to
      */
     private void sendBroadcastNewDownload(DownloadFileOperation download, String linkedToRemotePath) {
         Intent added = new Intent(getDownloadAddedMessage());
@@ -626,4 +685,13 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
         sendStickyBroadcast(added);
     }
 
+    /**
+     * Remove downloads of an account
+     *
+     * @param account       Downloads account to remove
+     */
+    private void cancelDownloadsForAccount(Account account) {
+        // Cancel pending downloads
+        mPendingDownloads.remove(account);
+    }
 }

+ 217 - 145
src/com/owncloud/android/files/services/FileUploader.java

@@ -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,
@@ -31,6 +31,7 @@ import java.util.concurrent.ConcurrentMap;
 import android.accounts.Account;
 import android.accounts.AccountManager;
 import android.accounts.AccountsException;
+import android.accounts.OnAccountsUpdateListener;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.Service;
@@ -76,8 +77,8 @@ import com.owncloud.android.utils.ErrorMessageAdapter;
 import com.owncloud.android.utils.UriUtils;
 
 
-
-public class FileUploader extends Service implements OnDatatransferProgressListener {
+public class FileUploader extends Service
+        implements OnDatatransferProgressListener, OnAccountsUpdateListener {
 
     private static final String UPLOAD_FINISH_MESSAGE = "UPLOAD_FINISH";
     public static final String EXTRA_UPLOAD_RESULT = "RESULT";
@@ -124,14 +125,14 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
     private static final String MIME_TYPE_PDF = "application/pdf";
     private static final String FILE_EXTENSION_PDF = ".pdf";
 
-    
+
     public static String getUploadFinishMessage() {
         return FileUploader.class.getName().toString() + UPLOAD_FINISH_MESSAGE;
     }
-    
+
     /**
      * Builds a key for mPendingUploads from the account and file to upload
-     * 
+     *
      * @param account   Account where the file to upload is stored
      * @param file      File to upload
      */
@@ -145,7 +146,7 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
 
     /**
      * Checks if an ownCloud server version should support chunked uploads.
-     * 
+     *
      * @param version OwnCloud version instance corresponding to an ownCloud
      *            server.
      * @return 'True' if the ownCloud server with version supports chunked
@@ -168,6 +169,10 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
         mServiceLooper = thread.getLooper();
         mServiceHandler = new ServiceHandler(mServiceLooper, this);
         mBinder = new FileUploaderBinder();
+
+        // add AccountsUpdatedListener
+        AccountManager am = AccountManager.get(getApplicationContext());
+        am.addOnAccountsUpdatedListener(this, null, false);
     }
 
     /**
@@ -181,13 +186,18 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
         mServiceLooper.quit();
         mServiceLooper = null;
         mNotificationManager = null;
+
+        // remove AccountsUpdatedListener
+        AccountManager am = AccountManager.get(getApplicationContext());
+        am.removeOnAccountsUpdatedListener(this);
+
         super.onDestroy();
     }
 
 
     /**
      * Entry point to add one or several files to the queue of uploads.
-     * 
+     *
      * New uploads 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.
@@ -246,7 +256,7 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
         boolean forceOverwrite = intent.getBooleanExtra(KEY_FORCE_OVERWRITE, false);
         boolean isInstant = intent.getBooleanExtra(KEY_INSTANT_UPLOAD, false);
         int localAction = intent.getIntExtra(KEY_LOCAL_BEHAVIOUR, LOCAL_BEHAVIOUR_COPY);
-        
+
         if (intent.hasExtra(KEY_FILE) && files == null) {
             Log_OC.e(TAG, "Incorrect array for OCFiles provided in upload intent");
             return Service.START_NOT_STICKY;
@@ -279,7 +289,7 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
         AccountManager aMgr = AccountManager.get(this);
         String version = aMgr.getUserData(account, Constants.KEY_OC_VERSION);
         OwnCloudVersion ocv = new OwnCloudVersion(version);
-        
+
         boolean chunked = FileUploader.chunkedUploadIsSupported(ocv);
         AbstractList<String> requestedUploads = new Vector<String>();
         String uploadKey = null;
@@ -287,7 +297,7 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
         try {
             for (int i = 0; i < files.length; i++) {
                 uploadKey = buildRemoteName(account, files[i].getRemotePath());
-                newUpload = new UploadFileOperation(account, files[i], chunked, isInstant, forceOverwrite, localAction, 
+                newUpload = new UploadFileOperation(account, files[i], chunked, isInstant, forceOverwrite, localAction,
                         getApplicationContext());
                 if (isInstant) {
                     newUpload.setRemoteFolderToBeCreated();
@@ -326,7 +336,7 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
     /**
      * Provides a binder object that clients can use to perform operations on
      * the queue of uploads, excepting the addition of new files.
-     * 
+     *
      * Implemented to perform cancellation, pause and resume of existing
      * uploads.
      */
@@ -334,7 +344,7 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
     public IBinder onBind(Intent arg0) {
         return mBinder;
     }
-    
+
     /**
      * Called when ALL the bound clients were onbound.
      */
@@ -343,24 +353,33 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
         ((FileUploaderBinder)mBinder).clearListeners();
         return false;   // not accepting rebinding (default behaviour)
     }
-    
+
+    @Override
+    public void onAccountsUpdated(Account[] accounts) {
+        // Review current upload, and cancel it if its account doen't exist
+        if (mCurrentUpload != null &&
+                !AccountUtils.exists(mCurrentUpload.getAccount(), getApplicationContext())) {
+            mCurrentUpload.cancel();
+        }
+        // The rest of uploads are cancelled when they try to start
+    }
 
     /**
      * Binder to let client components to perform operations on the queue of
      * uploads.
-     * 
+     *
      * It provides by itself the available operations.
      */
     public class FileUploaderBinder extends Binder implements OnDatatransferProgressListener {
-        
-        /** 
+
+        /**
          * Map of listeners that will be reported about progress of uploads from a {@link FileUploaderBinder} instance 
          */
         private Map<String, OnDatatransferProgressListener> mBoundListeners = new HashMap<String, OnDatatransferProgressListener>();
-        
+
         /**
          * Cancels a pending or current upload of a remote file.
-         * 
+         *
          * @param account Owncloud account where the remote file will be stored.
          * @param file A file in the queue of pending uploads
          */
@@ -373,22 +392,35 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
                 upload.cancel();
             }
         }
-        
-        
-        
+
+        /**
+         * Cancels a pending or current upload for an account
+         *
+         * @param account Owncloud accountName where the remote file will be stored.
+         */
+        public void cancel(Account account) {
+            Log_OC.d(TAG, "Account= " + account.name);
+
+            if (mCurrentUpload != null) {
+                Log_OC.d(TAG, "Current Upload Account= " + mCurrentUpload.getAccount().name);
+                if (mCurrentUpload.getAccount().name.equals(account.name)) {
+                    mCurrentUpload.cancel();
+                }
+            }
+            // Cancel pending uploads
+            cancelUploadForAccount(account.name);
+        }
+
         public void clearListeners() {
             mBoundListeners.clear();
         }
 
-
-        
-        
         /**
          * Returns True when the file described by 'file' is being uploaded to
          * the ownCloud account 'account' or waiting for it
-         * 
+         *
          * If 'file' is a directory, returns 'true' if some of its descendant files is uploading or waiting to upload. 
-         * 
+         *
          * @param account   ownCloud account where the remote file will be stored.
          * @param file      A file that could be in the queue of pending uploads
          */
@@ -414,7 +446,7 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
 
         /**
          * Adds a listener interested in the progress of the upload for a concrete file.
-         * 
+         *
          * @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.
@@ -424,12 +456,12 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
             String targetKey = buildRemoteName(account, file);
             mBoundListeners.put(targetKey, listener);
         }
-        
-        
-        
+
+
+
         /**
          * Removes a listener interested in the progress of the upload for a concrete file.
-         * 
+         *
          * @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.
@@ -445,20 +477,30 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
 
         @Override
         public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer,
-                String fileName) {
+                                       String fileName) {
             String key = buildRemoteName(mCurrentUpload.getAccount(), mCurrentUpload.getFile());
             OnDatatransferProgressListener boundListener = mBoundListeners.get(key);
             if (boundListener != null) {
                 boundListener.onTransferProgress(progressRate, totalTransferredSoFar, totalToTransfer, fileName);
             }
         }
-        
+
+        /**
+         * Review uploads and cancel it if its account doesn't exist
+         */
+        public void checkAccountOfCurrentUpload() {
+            if (mCurrentUpload != null &&
+                    !AccountUtils.exists(mCurrentUpload.getAccount(), getApplicationContext())) {
+                mCurrentUpload.cancel();
+            }
+            // The rest of uploads are cancelled when they try to start
+        }
     }
 
     /**
      * Upload worker. Performs the pending uploads in the order they were
      * requested.
-     * 
+     *
      * Created with the Looper of a new thread, started in
      * {@link FileUploader#onCreate()}.
      */
@@ -491,7 +533,7 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
 
     /**
      * Core upload method: sends the file(s) to upload
-     * 
+     *
      * @param uploadKey Key to access the upload to perform, contained in
      *            mPendingUploads
      */
@@ -503,63 +545,73 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
 
         if (mCurrentUpload != null) {
 
-            notifyUploadStart(mCurrentUpload);
+            // Detect if the account exists
+            if (AccountUtils.exists(mCurrentUpload.getAccount(), getApplicationContext())) {
+                Log_OC.d(TAG, "Account " + mCurrentUpload.getAccount().name + " exists");
 
-            RemoteOperationResult uploadResult = null, grantResult = null;
-            
-            try {
-                /// prepare client object to send requests to the ownCloud server
-                if (mUploadClient == null || !mLastAccount.equals(mCurrentUpload.getAccount())) {
-                    mLastAccount = mCurrentUpload.getAccount();
-                    mStorageManager = 
-                            new FileDataStorageManager(mLastAccount, getContentResolver());
-                    OwnCloudAccount ocAccount = new OwnCloudAccount(mLastAccount, this);
-                    mUploadClient = OwnCloudClientManagerFactory.getDefaultSingleton().
-                            getClientFor(ocAccount, this);
-                }
-                
-                /// check the existence of the parent folder for the file to upload
-                String remoteParentPath = new File(mCurrentUpload.getRemotePath()).getParent();
-                remoteParentPath = remoteParentPath.endsWith(OCFile.PATH_SEPARATOR) ? remoteParentPath : remoteParentPath + OCFile.PATH_SEPARATOR;
-                grantResult = grantFolderExistence(remoteParentPath);
-            
-                /// perform the upload
-                if (grantResult.isSuccess()) {
-                    OCFile parent = mStorageManager.getFileByPath(remoteParentPath);
-                    mCurrentUpload.getFile().setParentId(parent.getFileId());
-                    uploadResult = mCurrentUpload.execute(mUploadClient);
-                    if (uploadResult.isSuccess()) {
-                        saveUploadedFile();
+                notifyUploadStart(mCurrentUpload);
+
+                RemoteOperationResult uploadResult = null, grantResult = null;
+
+                try {
+                    /// prepare client object to send requests to the ownCloud server
+                    if (mUploadClient == null || !mLastAccount.equals(mCurrentUpload.getAccount())) {
+                        mLastAccount = mCurrentUpload.getAccount();
+                        mStorageManager =
+                                new FileDataStorageManager(mLastAccount, getContentResolver());
+                        OwnCloudAccount ocAccount = new OwnCloudAccount(mLastAccount, this);
+                        mUploadClient = OwnCloudClientManagerFactory.getDefaultSingleton().
+                                getClientFor(ocAccount, this);
+                    }
+
+                    /// check the existence of the parent folder for the file to upload
+                    String remoteParentPath = new File(mCurrentUpload.getRemotePath()).getParent();
+                    remoteParentPath = remoteParentPath.endsWith(OCFile.PATH_SEPARATOR) ?
+                            remoteParentPath : remoteParentPath + OCFile.PATH_SEPARATOR;
+                    grantResult = grantFolderExistence(remoteParentPath);
+
+                    /// perform the upload
+                    if (grantResult.isSuccess()) {
+                        OCFile parent = mStorageManager.getFileByPath(remoteParentPath);
+                        mCurrentUpload.getFile().setParentId(parent.getFileId());
+                        uploadResult = mCurrentUpload.execute(mUploadClient);
+                        if (uploadResult.isSuccess()) {
+                            saveUploadedFile();
+                        }
+                    } else {
+                        uploadResult = grantResult;
+                    }
+
+                } catch (AccountsException e) {
+                    Log_OC.e(TAG, "Error while trying to get autorization for " + mLastAccount.name, e);
+                    uploadResult = new RemoteOperationResult(e);
+
+                } catch (IOException e) {
+                    Log_OC.e(TAG, "Error while trying to get autorization for " + mLastAccount.name, e);
+                    uploadResult = new RemoteOperationResult(e);
+
+                } finally {
+                    synchronized (mPendingUploads) {
+                        mPendingUploads.remove(uploadKey);
+                        Log_OC.i(TAG, "Remove CurrentUploadItem from pending upload Item Map.");
+                    }
+                    if (uploadResult.isException()) {
+                        // enforce the creation of a new client object for next uploads; this grant that a new socket will
+                        // be created in the future if the current exception is due to an abrupt lose of network connection
+                        mUploadClient = null;
                     }
-                } else {
-                    uploadResult = grantResult;
-                }
-                
-            } catch (AccountsException e) {
-                Log_OC.e(TAG, "Error while trying to get autorization for " + mLastAccount.name, e);
-                uploadResult = new RemoteOperationResult(e);
-                
-            } catch (IOException e) {
-                Log_OC.e(TAG, "Error while trying to get autorization for " + mLastAccount.name, e);
-                uploadResult = new RemoteOperationResult(e);
-                
-            } finally {
-                synchronized (mPendingUploads) {
-                    mPendingUploads.remove(uploadKey);
-                    Log_OC.i(TAG, "Remove CurrentUploadItem from pending upload Item Map.");
-                }
-                if (uploadResult.isException()) {
-                    // enforce the creation of a new client object for next uploads; this grant that a new socket will 
-                    // be created in the future if the current exception is due to an abrupt lose of network connection
-                    mUploadClient = null;
                 }
-            }
-            
-            /// notify result
-            
-            notifyUploadResult(uploadResult, mCurrentUpload);
-            sendFinalBroadcast(mCurrentUpload, uploadResult);
 
+                /// notify result
+                notifyUploadResult(uploadResult, mCurrentUpload);
+                sendFinalBroadcast(mCurrentUpload, uploadResult);
+
+            } else {
+                // Cancel the transfer
+                Log_OC.d(TAG, "Account " + mCurrentUpload.getAccount().toString() + " doesn't exist");
+                cancelUploadForAccount(mCurrentUpload.getAccount().name);
+
+            }
         }
 
     }
@@ -567,17 +619,18 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
     /**
      * Checks the existence of the folder where the current file will be uploaded both in the remote server 
      * and in the local database.
-     * 
+     *
      * If the upload is set to enforce the creation of the folder, the method tries to create it both remote
      * and locally.
-     *  
+     *
      *  @param  pathToGrant     Full remote path whose existence will be granted.
      *  @return  An {@link OCFile} instance corresponding to the folder where the file will be uploaded.
      */
     private RemoteOperationResult grantFolderExistence(String pathToGrant) {
         RemoteOperation operation = new ExistenceCheckRemoteOperation(pathToGrant, this, false);
         RemoteOperationResult result = operation.execute(mUploadClient);
-        if (!result.isSuccess() && result.getCode() == ResultCode.FILE_NOT_FOUND && mCurrentUpload.isRemoteFolderToBeCreated()) {
+        if (!result.isSuccess() && result.getCode() == ResultCode.FILE_NOT_FOUND &&
+                mCurrentUpload.isRemoteFolderToBeCreated()) {
             SyncOperation syncOp = new CreateFolderOperation( pathToGrant, true);
             result = syncOp.execute(mUploadClient, mStorageManager);
         }
@@ -595,10 +648,11 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
         return result;
     }
 
-    
+
     private OCFile createLocalFolder(String remotePath) {
         String parentPath = new File(remotePath).getParent();
-        parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath : parentPath + OCFile.PATH_SEPARATOR;
+        parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ?
+                parentPath : parentPath + OCFile.PATH_SEPARATOR;
         OCFile parent = mStorageManager.getFileByPath(parentPath);
         if (parent == null) {
             parent = createLocalFolder(parentPath);
@@ -612,15 +666,15 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
         }
         return null;
     }
-    
+
 
     /**
      * Saves a OC File after a successful upload.
-     * 
+     *
      * A PROPFIND is necessary to keep the props in the local database
      * synchronized with the server, specially the modification time and Etag
      * (where available)
-     * 
+     *
      * TODO refactor this ugly thing
      */
     private void saveUploadedFile() {
@@ -639,7 +693,7 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
             updateOCFile(file, (RemoteFile) result.getData().get(0));
             file.setLastSyncDateForProperties(syncDate);
         }
-        
+
         // / maybe this would be better as part of UploadFileOperation... or
         // maybe all this method
         if (mCurrentUpload.wasRenamed()) {
@@ -649,8 +703,8 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
                 mStorageManager.saveFile(oldFile);
 
             } // else: it was just an automatic renaming due to a name
-              // coincidence; nothing else is needed, the storagePath is right
-              // in the instance returned by mCurrentUpload.getFile()
+            // coincidence; nothing else is needed, the storagePath is right
+            // in the instance returned by mCurrentUpload.getFile()
         }
         file.setNeedsUpdateThumbnail(true);
         mStorageManager.saveFile(file);
@@ -667,7 +721,7 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
     }
 
     private OCFile obtainNewOCFileToUpload(String remotePath, String localPath, String mimeType,
-            FileDataStorageManager storageManager) {
+                                           FileDataStorageManager storageManager) {
 
         // MIME type
         if (mimeType == null || mimeType.length() <= 0) {
@@ -697,7 +751,7 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
             newFile.setFileLength(localFile.length());
             newFile.setLastSyncDateForData(localFile.lastModified());
         } // don't worry about not assigning size, the problems with localPath
-          // are checked when the UploadFileOperation instance is created
+        // are checked when the UploadFileOperation instance is created
 
 
         newFile.setMimetype(mimeType);
@@ -707,13 +761,13 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
 
     /**
      * Creates a status notification to show the upload progress
-     * 
+     *
      * @param upload Upload operation starting.
      */
     private void notifyUploadStart(UploadFileOperation upload) {
         // / create status notification with a progress bar
         mLastPercent = 0;
-        mNotificationBuilder = 
+        mNotificationBuilder =
                 NotificationBuilderWithProgressBar.newNotificationBuilderWithProgressBar(this);
         mNotificationBuilder
                 .setOngoing(true)
@@ -730,7 +784,7 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
         showDetailsIntent.putExtra(FileActivity.EXTRA_ACCOUNT, upload.getAccount());
         showDetailsIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
         mNotificationBuilder.setContentIntent(PendingIntent.getActivity(
-            this, (int) System.currentTimeMillis(), showDetailsIntent, 0
+                this, (int) System.currentTimeMillis(), showDetailsIntent, 0
         ));
 
         mNotificationManager.notify(R.string.uploader_upload_in_progress_ticker, mNotificationBuilder.build());
@@ -754,7 +808,7 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
 
     /**
      * Updates the status notification with the result of an upload operation.
-     * 
+     *
      * @param uploadResult Result of the upload operation.
      * @param upload Finished upload operation
      */
@@ -763,33 +817,33 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
         Log_OC.d(TAG, "NotifyUploadResult with resultCode: " + uploadResult.getCode());
         // / cancelled operation or success -> silent removal of progress notification
         mNotificationManager.cancel(R.string.uploader_upload_in_progress_ticker);
-        
+
         // Show the result: success or fail notification
         if (!uploadResult.isCancelled()) {
-            int tickerId = (uploadResult.isSuccess()) ? R.string.uploader_upload_succeeded_ticker : 
-                R.string.uploader_upload_failed_ticker;
-            
+            int tickerId = (uploadResult.isSuccess()) ? R.string.uploader_upload_succeeded_ticker :
+                    R.string.uploader_upload_failed_ticker;
+
             String content = null;
 
             // check credentials error
             boolean needsToUpdateCredentials = (
-                    uploadResult.getCode() == ResultCode.UNAUTHORIZED || 
-                    uploadResult.isIdPRedirection()
+                    uploadResult.getCode() == ResultCode.UNAUTHORIZED ||
+                            uploadResult.isIdPRedirection()
             );
-            tickerId = (needsToUpdateCredentials) ? 
+            tickerId = (needsToUpdateCredentials) ?
                     R.string.uploader_upload_failed_credentials_error : tickerId;
 
             mNotificationBuilder
-            .setTicker(getString(tickerId))
-            .setContentTitle(getString(tickerId))
-            .setAutoCancel(true)
-            .setOngoing(false)
-            .setProgress(0, 0, false);
-            
+                    .setTicker(getString(tickerId))
+                    .setContentTitle(getString(tickerId))
+                    .setAutoCancel(true)
+                    .setOngoing(false)
+                    .setProgress(0, 0, false);
+
             content =  ErrorMessageAdapter.getErrorCauseMessage(
                     uploadResult, upload, getResources()
             );
-            
+
             if (needsToUpdateCredentials) {
                 // let the user update credentials with one click
                 Intent updateAccountCredentials = new Intent(this, AuthenticatorActivity.class);
@@ -797,24 +851,24 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
                         AuthenticatorActivity.EXTRA_ACCOUNT, upload.getAccount()
                 );
                 updateAccountCredentials.putExtra(
-                        AuthenticatorActivity.EXTRA_ACTION, 
+                        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);
                 mNotificationBuilder.setContentIntent(PendingIntent.getActivity(
-                    this, 
-                    (int) System.currentTimeMillis(), 
-                    updateAccountCredentials, 
-                    PendingIntent.FLAG_ONE_SHOT
+                        this,
+                        (int) System.currentTimeMillis(),
+                        updateAccountCredentials,
+                        PendingIntent.FLAG_ONE_SHOT
                 ));
-                
-                mUploadClient = null;   
-                    // grant that future retries on the same account will get the fresh credentials
+
+                mUploadClient = null;
+                // grant that future retries on the same account will get the fresh credentials
             } else {
                 mNotificationBuilder.setContentText(content);
-    
+
                 if (upload.isInstant()) {
                     DbHandler db = null;
                     try {
@@ -825,12 +879,12 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
                         if (uploadResult.getCode() == ResultCode.QUOTA_EXCEEDED) {
                             //message = getString(R.string.failed_upload_quota_exceeded_text);
                             if (db.updateFileState(
-                                    upload.getOriginalStoragePath(), 
+                                    upload.getOriginalStoragePath(),
                                     DbHandler.UPLOAD_STATUS_UPLOAD_FAILED,
                                     message) == 0) {
                                 db.putFileForLater(
-                                        upload.getOriginalStoragePath(), 
-                                        upload.getAccount().name, 
+                                        upload.getOriginalStoragePath(),
+                                        upload.getAccount().name,
                                         message
                                 );
                             }
@@ -842,22 +896,22 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
                     }
                 }
             }
-            
+
             mNotificationBuilder.setContentText(content);
             mNotificationManager.notify(tickerId, mNotificationBuilder.build());
-            
+
             if (uploadResult.isSuccess()) {
-                
+
                 DbHandler db = new DbHandler(this.getBaseContext());
                 db.removeIUPendingFile(mCurrentUpload.getOriginalStoragePath());
                 db.close();
 
                 // remove success notification, with a delay of 2 seconds
                 NotificationDelayer.cancelWithDelay(
-                        mNotificationManager, 
-                        R.string.uploader_upload_succeeded_ticker, 
+                        mNotificationManager,
+                        R.string.uploader_upload_succeeded_ticker,
                         2000);
-                
+
             }
         }
     }
@@ -865,17 +919,17 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
     /**
      * Sends a broadcast in order to the interested activities can update their
      * view
-     * 
+     *
      * @param upload Finished upload operation
      * @param uploadResult Result of the upload operation
      */
     private void sendFinalBroadcast(UploadFileOperation upload, RemoteOperationResult uploadResult) {
         Intent end = new Intent(getUploadFinishMessage());
         end.putExtra(EXTRA_REMOTE_PATH, upload.getRemotePath()); // real remote
-                                                                 // path, after
-                                                                 // possible
-                                                                 // automatic
-                                                                 // renaming
+        // path, after
+        // possible
+        // automatic
+        // renaming
         if (upload.wasRenamed()) {
             end.putExtra(EXTRA_OLD_REMOTE_PATH, upload.getOldFile().getRemotePath());
         }
@@ -893,9 +947,27 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
      * @return true if is needed to add the pdf file extension to the file
      */
     private boolean isPdfFileFromContentProviderWithoutExtension(String localPath, String mimeType) {
-        return localPath.startsWith(UriUtils.URI_CONTENT_SCHEME) && 
-                mimeType.equals(MIME_TYPE_PDF) && 
+        return localPath.startsWith(UriUtils.URI_CONTENT_SCHEME) &&
+                mimeType.equals(MIME_TYPE_PDF) &&
                 !localPath.endsWith(FILE_EXTENSION_PDF);
     }
 
+    /**
+     * Remove uploads of an account
+     * @param accountName
+     */
+    private void cancelUploadForAccount(String accountName){
+        // this can be slow if there are many uploads :(
+        Iterator<String> it = mPendingUploads.keySet().iterator();
+        Log_OC.d(TAG, "Number of pending updloads= "  + mPendingUploads.size());
+        while (it.hasNext()) {
+            String key = it.next();
+            Log_OC.d(TAG, "mPendingUploads CANCELLED " + key);
+            if (key.startsWith(accountName)) {
+                synchronized (mPendingUploads) {
+                    mPendingUploads.remove(key);
+                }
+            }
+        }
+    }
 }

+ 16 - 2
src/com/owncloud/android/files/services/IndexedForest.java

@@ -21,6 +21,7 @@ import android.accounts.Account;
 import android.util.Pair;
 
 import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.lib.common.utils.Log_OC;
 
 import java.io.File;
 import java.util.HashSet;
@@ -210,6 +211,21 @@ public class IndexedForest<V> {
     }
 
 
+    /**
+     * Remove the elements that contains account as a part of its key
+     * @param account
+     */
+    public void remove(Account account){
+        Iterator<String> it = mMap.keySet().iterator();
+        while (it.hasNext()) {
+            String key = it.next();
+            Log_OC.d("IndexedForest", "Number of pending downloads= "  + mMap.size());
+            if (key.startsWith(account.name)) {
+                mMap.remove(key);
+            }
+        }
+    }
+
     /**
      * Builds a key to index files
      *
@@ -220,6 +236,4 @@ public class IndexedForest<V> {
         return account.name + remotePath;
     }
 
-
-
 }

+ 0 - 1
src/com/owncloud/android/operations/DownloadFileOperation.java

@@ -177,5 +177,4 @@ public class DownloadFileOperation extends RemoteOperation {
             mDataTransferListeners.remove(listener);
         }
     }
-    
 }

+ 79 - 55
src/com/owncloud/android/operations/UploadFileOperation.java

@@ -1,5 +1,5 @@
 /* ownCloud Android client application
- *   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,
@@ -26,6 +26,7 @@ import java.io.OutputStream;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Set;
+import java.util.concurrent.CancellationException;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 import org.apache.commons.httpclient.methods.PutMethod;
@@ -76,7 +77,7 @@ public class UploadFileOperation extends RemoteOperation {
     private String mOriginalStoragePath = null;
     PutMethod mPutMethod = null;
     private Set<OnDatatransferProgressListener> mDataTransferListeners = new HashSet<OnDatatransferProgressListener>();
-    private final AtomicBoolean mCancellationRequested = new AtomicBoolean(false);
+    private AtomicBoolean mCancellationRequested = new AtomicBoolean(false);
     private Context mContext;
     
     private UploadRemoteFileOperation mUploadOperation;
@@ -212,7 +213,8 @@ public class UploadFileOperation extends RemoteOperation {
 
             // check location of local file; if not the expected, copy to a
             // temporal file before upload (if COPY is the expected behaviour)
-            if (!mOriginalStoragePath.equals(expectedPath) && mLocalBehaviour == FileUploader.LOCAL_BEHAVIOUR_COPY) {
+            if (!mOriginalStoragePath.equals(expectedPath) &&
+                    mLocalBehaviour == FileUploader.LOCAL_BEHAVIOUR_COPY) {
 
                 if (FileStorageUtils.getUsableSpace(mAccount.name) < originalFile.length()) {
                     result = new RemoteOperationResult(ResultCode.LOCAL_STORAGE_FULL);
@@ -221,7 +223,8 @@ public class UploadFileOperation extends RemoteOperation {
 
                 } else {
 
-                    String temporalPath = FileStorageUtils.getTemporalPath(mAccount.name) + mFile.getRemotePath();
+                    String temporalPath = FileStorageUtils.getTemporalPath(mAccount.name) +
+                            mFile.getRemotePath();
                     mFile.setStoragePath(temporalPath);
                     temporalFile = new File(temporalPath);
 
@@ -251,10 +254,10 @@ public class UploadFileOperation extends RemoteOperation {
                             int nRead;
                             byte[] data = new byte[16384];
 
-                            while ((nRead = in.read(data, 0, data.length)) != -1) {
+                            while (!mCancellationRequested.get() &&
+                                    (nRead = in.read(data, 0, data.length)) != -1) {
                                 out.write(data, 0, nRead);
                             }
-
                             out.flush();
 
                         } else {
@@ -268,12 +271,17 @@ public class UploadFileOperation extends RemoteOperation {
                                 out = new FileOutputStream(temporalFile);
                                 byte[] buf = new byte[1024];
                                 int len;
-                                while ((len = in.read(buf)) > 0) {
+                                while (!mCancellationRequested.get() && (len = in.read(buf)) > 0) {
                                     out.write(buf, 0, len);
                                 }
                             }
                         }
 
+                        if (mCancellationRequested.get()) {
+                            result = new RemoteOperationResult(new OperationCancelledException());
+                        }
+
+
                     } catch (Exception e) {
                         result = new RemoteOperationResult(ResultCode.LOCAL_STORAGE_NOT_COPIED);
                         return result;
@@ -283,63 +291,69 @@ public class UploadFileOperation extends RemoteOperation {
                             if (in != null)
                                 in.close();
                         } catch (Exception e) {
-                            Log_OC.d(TAG, "Weird exception while closing input stream for " + mOriginalStoragePath + " (ignoring)", e);
+                            Log_OC.d(TAG, "Weird exception while closing input stream for " +
+                                    mOriginalStoragePath + " (ignoring)", e);
                         }
                         try {
                             if (out != null)
                                 out.close();
                         } catch (Exception e) {
-                            Log_OC.d(TAG, "Weird exception while closing output stream for " + expectedPath + " (ignoring)", e);
+                            Log_OC.d(TAG, "Weird exception while closing output stream for " +
+                                    expectedPath + " (ignoring)", e);
                         }
                     }
                 }
             }
-            localCopyPassed = true;
+            localCopyPassed = (result == null);
 
             /// perform the upload
-            if ( mChunked && (new File(mFile.getStoragePath())).length() > ChunkedUploadRemoteFileOperation.CHUNK_SIZE ) {
-                mUploadOperation = new ChunkedUploadRemoteFileOperation(mFile.getStoragePath(), mFile.getRemotePath(), 
-                        mFile.getMimetype());
+            if ( mChunked &&
+                    (new File(mFile.getStoragePath())).length() >
+                            ChunkedUploadRemoteFileOperation.CHUNK_SIZE ) {
+                mUploadOperation = new ChunkedUploadRemoteFileOperation(mFile.getStoragePath(),
+                        mFile.getRemotePath(), mFile.getMimetype());
             } else {
-                mUploadOperation = new UploadRemoteFileOperation(mFile.getStoragePath(), mFile.getRemotePath(), 
-                        mFile.getMimetype());
+                mUploadOperation = new UploadRemoteFileOperation(mFile.getStoragePath(),
+                        mFile.getRemotePath(), mFile.getMimetype());
             }
             Iterator <OnDatatransferProgressListener> listener = mDataTransferListeners.iterator();
             while (listener.hasNext()) {
                 mUploadOperation.addDatatransferProgressListener(listener.next());
             }
-            result = mUploadOperation.execute(client);
-
-            /// move local temporal file or original file to its corresponding
-            // location in the ownCloud local folder
-            if (result.isSuccess()) {
-                if (mLocalBehaviour == FileUploader.LOCAL_BEHAVIOUR_FORGET) {
-                    mFile.setStoragePath(null);
-
-                } else {
-                    mFile.setStoragePath(expectedPath);
-                    File fileToMove = null;
-                    if (temporalFile != null) { // FileUploader.LOCAL_BEHAVIOUR_COPY
-                                                // ; see where temporalFile was
-                                                // set
-                        fileToMove = temporalFile;
-                    } else { // FileUploader.LOCAL_BEHAVIOUR_MOVE
-                        fileToMove = originalFile;
-                    }
-                    if (!expectedFile.equals(fileToMove)) {
-                        File expectedFolder = expectedFile.getParentFile();
-                        expectedFolder.mkdirs();
-                        if (!expectedFolder.isDirectory() || !fileToMove.renameTo(expectedFile)) {
-                            mFile.setStoragePath(null); // forget the local file
-                            // by now, treat this as a success; the file was
-                            // uploaded; the user won't like that the local file
-                            // is not linked, but this should be a very rare
-                            // fail;
-                            // the best option could be show a warning message
-                            // (but not a fail)
-                            // result = new
-                            // RemoteOperationResult(ResultCode.LOCAL_STORAGE_NOT_MOVED);
-                            // return result;
+            if (!mCancellationRequested.get()) {
+                result = mUploadOperation.execute(client);
+
+                /// move local temporal file or original file to its corresponding
+                // location in the ownCloud local folder
+                if (result.isSuccess()) {
+                    if (mLocalBehaviour == FileUploader.LOCAL_BEHAVIOUR_FORGET) {
+                        mFile.setStoragePath(null);
+
+                    } else {
+                        mFile.setStoragePath(expectedPath);
+                        File fileToMove = null;
+                        if (temporalFile != null) { // FileUploader.LOCAL_BEHAVIOUR_COPY
+                            // ; see where temporalFile was
+                            // set
+                            fileToMove = temporalFile;
+                        } else { // FileUploader.LOCAL_BEHAVIOUR_MOVE
+                            fileToMove = originalFile;
+                        }
+                        if (!expectedFile.equals(fileToMove)) {
+                            File expectedFolder = expectedFile.getParentFile();
+                            expectedFolder.mkdirs();
+                            if (!expectedFolder.isDirectory() || !fileToMove.renameTo(expectedFile)) {
+                                mFile.setStoragePath(null); // forget the local file
+                                // by now, treat this as a success; the file was
+                                // uploaded; the user won't like that the local file
+                                // is not linked, but this should be a very rare
+                                // fail;
+                                // the best option could be show a warning message
+                                // (but not a fail)
+                                // result = new
+                                // RemoteOperationResult(ResultCode.LOCAL_STORAGE_NOT_MOVED);
+                                // return result;
+                            }
                         }
                     }
                 }
@@ -358,19 +372,23 @@ public class UploadFileOperation extends RemoteOperation {
                 temporalFile.delete();
             }
             if (result.isSuccess()) {
-                Log_OC.i(TAG, "Upload of " + mOriginalStoragePath + " to " + mRemotePath + ": " + result.getLogMessage());
+                Log_OC.i(TAG, "Upload of " + mOriginalStoragePath + " to " + mRemotePath + ": " +
+                        result.getLogMessage());
             } else {
                 if (result.getException() != null) {
                     String complement = "";
                     if (!nameCheckPassed) {
                         complement = " (while checking file existence in server)";
                     } else if (!localCopyPassed) {
-                        complement = " (while copying local file to " + FileStorageUtils.getSavePath(mAccount.name)
+                        complement = " (while copying local file to " +
+                                FileStorageUtils.getSavePath(mAccount.name)
                                 + ")";
                     }
-                    Log_OC.e(TAG, "Upload of " + mOriginalStoragePath + " to " + mRemotePath + ": " + result.getLogMessage() + complement, result.getException());
+                    Log_OC.e(TAG, "Upload of " + mOriginalStoragePath + " to " + mRemotePath +
+                            ": " + result.getLogMessage() + complement, result.getException());
                 } else {
-                    Log_OC.e(TAG, "Upload of " + mOriginalStoragePath + " to " + mRemotePath + ": " + result.getLogMessage());
+                    Log_OC.e(TAG, "Upload of " + mOriginalStoragePath + " to " + mRemotePath +
+                            ": " + result.getLogMessage());
                 }
             }
         }
@@ -385,7 +403,8 @@ public class UploadFileOperation extends RemoteOperation {
         newFile.setFileLength(mFile.getFileLength());
         newFile.setMimetype(mFile.getMimetype());
         newFile.setModificationTimestamp(mFile.getModificationTimestamp());
-        newFile.setModificationTimestampAtLastSyncForData(mFile.getModificationTimestampAtLastSyncForData());
+        newFile.setModificationTimestampAtLastSyncForData(
+                mFile.getModificationTimestampAtLastSyncForData());
         // newFile.setEtag(mFile.getEtag())
         newFile.setKeepInSync(mFile.keepInSync());
         newFile.setLastSyncDateForProperties(mFile.getLastSyncDateForProperties());
@@ -400,7 +419,8 @@ public class UploadFileOperation extends RemoteOperation {
      * Checks if remotePath does not exist in the server and returns it, or adds
      * a suffix to it in order to avoid the server file is overwritten.
      * 
-     * @param string
+     * @param wc
+     * @param remotePath
      * @return
      */
     private String getAvailableRemotePath(OwnCloudClient wc, String remotePath) throws Exception {
@@ -436,12 +456,16 @@ public class UploadFileOperation extends RemoteOperation {
     }
 
     private boolean existsFile(OwnCloudClient client, String remotePath){
-        ExistenceCheckRemoteOperation existsOperation = new ExistenceCheckRemoteOperation(remotePath, mContext, false);
+        ExistenceCheckRemoteOperation existsOperation =
+                new ExistenceCheckRemoteOperation(remotePath, mContext, false);
         RemoteOperationResult result = existsOperation.execute(client);
         return result.isSuccess();
     }
     
     public void cancel() {
-        mUploadOperation.cancel();
+        mCancellationRequested = new AtomicBoolean(true);
+        if (mUploadOperation != null) {
+            mUploadOperation.cancel();
+        }
     }
 }

+ 7 - 10
src/com/owncloud/android/ui/activity/FileActivity.java

@@ -1,6 +1,6 @@
 /* ownCloud Android client application
  *   Copyright (C) 2011  Bartek Przybylski
- *   Copyright (C) 2012-2014 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,
@@ -56,7 +56,6 @@ import com.owncloud.android.lib.common.utils.Log_OC;
 import com.owncloud.android.operations.CreateShareOperation;
 import com.owncloud.android.operations.SynchronizeFolderOperation;
 import com.owncloud.android.operations.UnshareLinkOperation;
-
 import com.owncloud.android.services.OperationsService;
 import com.owncloud.android.services.OperationsService.OperationsServiceBinder;
 import com.owncloud.android.ui.dialog.LoadingDialog;
@@ -68,8 +67,8 @@ import com.owncloud.android.utils.ErrorMessageAdapter;
  * 
  * @author David A. Velasco
  */
-public class FileActivity extends SherlockFragmentActivity 
-implements OnRemoteOperationListener, ComponentsGetter {
+public class FileActivity extends SherlockFragmentActivity
+        implements OnRemoteOperationListener, ComponentsGetter {
 
     public static final String EXTRA_FILE = "com.owncloud.android.ui.activity.FILE";
     public static final String EXTRA_ACCOUNT = "com.owncloud.android.ui.activity.ACCOUNT";
@@ -79,7 +78,7 @@ implements OnRemoteOperationListener, ComponentsGetter {
     public static final String TAG = FileActivity.class.getSimpleName();
     
     private static final String DIALOG_WAIT_TAG = "DIALOG_WAIT";
-    private static final String KEY_WAITING_FOR_OP_ID = "WAITING_FOR_OP_ID";;
+    private static final String KEY_WAITING_FOR_OP_ID = "WAITING_FOR_OP_ID";
     
     protected static final long DELAY_TO_REQUEST_OPERATION_ON_ACTIVITY_RESULTS = 200;
     
@@ -158,7 +157,7 @@ implements OnRemoteOperationListener, ComponentsGetter {
         if (mUploadServiceConnection != null) {
             bindService(new Intent(this, FileUploader.class), mUploadServiceConnection, Context.BIND_AUTO_CREATE);
         }
-        
+
     }
 
     
@@ -221,6 +220,7 @@ implements OnRemoteOperationListener, ComponentsGetter {
             unbindService(mUploadServiceConnection);
             mUploadServiceConnection = null;
         }
+
         super.onDestroy();
     }
     
@@ -256,8 +256,6 @@ implements OnRemoteOperationListener, ComponentsGetter {
      *  to create a new ownCloud {@link Account}.
      *  
      *  POSTCONDITION: updates {@link #mAccountWasSet} and {@link #mAccountWasRestored}.
-     *   
-     *  @return     'True' if the checked {@link Account} was valid.
      */
     private void swapToDefaultAccount() {
         // default to the most recently used account
@@ -356,7 +354,6 @@ implements OnRemoteOperationListener, ComponentsGetter {
     protected ServiceConnection newTransferenceServiceConnection() {
         return null;
     }
-    
 
     /**
      * Helper class handling a callback from the {@link AccountManager} after the creation of
@@ -605,7 +602,7 @@ implements OnRemoteOperationListener, ComponentsGetter {
     @Override
     public FileUploaderBinder getFileUploaderBinder() {
         return mUploaderBinder;
-    };    
+    }
     
     
 }

+ 2 - 2
src/com/owncloud/android/ui/activity/FileDisplayActivity.java

@@ -1,6 +1,6 @@
 /* ownCloud Android client application
  *   Copyright (C) 2011  Bartek Przybylski
- *   Copyright (C) 2012-2014 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,
@@ -96,7 +96,6 @@ import com.owncloud.android.operations.RefreshFolderOperation;
 import com.owncloud.android.operations.UnshareLinkOperation;
 import com.owncloud.android.services.observer.FileObserverService;
 import com.owncloud.android.syncadapter.FileSyncAdapter;
-import com.owncloud.android.ui.adapter.FileListListAdapter;
 import com.owncloud.android.ui.dialog.CreateFolderDialogFragment;
 import com.owncloud.android.ui.dialog.SslUntrustedCertDialog;
 import com.owncloud.android.ui.dialog.SslUntrustedCertDialog.OnSslUntrustedCertListener;
@@ -649,6 +648,7 @@ OnSslUntrustedCertListener, OnEnforceableRefreshListener {
         if (filePaths != null) {
             String[] remotePaths = new String[filePaths.length];
             String remotePathBase = "";
+
             for (int j = mDirectories.getCount() - 2; j >= 0; --j) {
                 remotePathBase += OCFile.PATH_SEPARATOR + mDirectories.getItem(j);
             }

+ 115 - 4
src/com/owncloud/android/ui/activity/Preferences.java

@@ -1,6 +1,6 @@
 /* ownCloud Android client application
  *   Copyright (C) 2011  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,13 +21,17 @@ import android.accounts.Account;
 import android.accounts.AccountManager;
 import android.accounts.AccountManagerCallback;
 import android.accounts.AccountManagerFuture;
+import android.content.ComponentName;
+import android.content.Context;
 import android.content.Intent;
+import android.content.ServiceConnection;
 import android.content.SharedPreferences;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.IBinder;
 import android.preference.CheckBoxPreference;
 import android.preference.Preference;
 import android.preference.Preference.OnPreferenceChangeListener;
@@ -50,12 +54,19 @@ import com.owncloud.android.MainApp;
 import com.owncloud.android.R;
 import com.owncloud.android.authentication.AccountUtils;
 import com.owncloud.android.authentication.AuthenticatorActivity;
+import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.db.DbHandler;
+import com.owncloud.android.files.FileOperationsHelper;
+import com.owncloud.android.files.services.FileDownloader;
+import com.owncloud.android.files.services.FileUploader;
 import com.owncloud.android.lib.common.utils.Log_OC;
+import com.owncloud.android.services.OperationsService;
 import com.owncloud.android.ui.RadioButtonPreference;
 import com.owncloud.android.utils.DisplayUtils;
 
+import java.io.File;
+
 
 /**
  * An Activity that allows the user to change the application's settings.
@@ -63,7 +74,8 @@ import com.owncloud.android.utils.DisplayUtils;
  * @author Bartek Przybylski
  * @author David A. Velasco
  */
-public class Preferences extends SherlockPreferenceActivity implements AccountManagerCallback<Boolean> {
+public class Preferences extends SherlockPreferenceActivity
+        implements AccountManagerCallback<Boolean>, ComponentsGetter {
     
     private static final String TAG = "OwnCloudPreferences";
 
@@ -88,6 +100,9 @@ public class Preferences extends SherlockPreferenceActivity implements AccountMa
     private Preference mPrefInstantVideoUploadPathWiFi;
     private String mUploadVideoPath;
 
+    protected FileDownloader.FileDownloaderBinder mDownloaderBinder = null;
+    protected FileUploader.FileUploaderBinder mUploaderBinder = null;
+    private ServiceConnection mDownloadServiceConnection, mUploadServiceConnection = null;
 
     @SuppressWarnings("deprecation")
     @Override
@@ -199,13 +214,13 @@ public class Preferences extends SherlockPreferenceActivity implements AccountMa
                         String username = currentAccount.name.substring(0, currentAccount.name.lastIndexOf('@'));
                         
                         String recommendSubject = String.format(getString(R.string.recommend_subject), appName);
-                        String recommendText = String.format(getString(R.string.recommend_text), appName, downloadUrl, username);
+                        String recommendText = String.format(getString(R.string.recommend_text),
+                                appName, downloadUrl, username);
                         
                         intent.putExtra(Intent.EXTRA_SUBJECT, recommendSubject);
                         intent.putExtra(Intent.EXTRA_TEXT, recommendText);
                         startActivity(intent);
 
-
                         return(true);
 
                     }
@@ -337,6 +352,18 @@ public class Preferences extends SherlockPreferenceActivity implements AccountMa
        loadInstantUploadPath();
        loadInstantUploadVideoPath();
 
+        /* ComponentsGetter */
+        mDownloadServiceConnection = newTransferenceServiceConnection();
+        if (mDownloadServiceConnection != null) {
+            bindService(new Intent(this, FileDownloader.class), mDownloadServiceConnection,
+                    Context.BIND_AUTO_CREATE);
+        }
+        mUploadServiceConnection = newTransferenceServiceConnection();
+        if (mUploadServiceConnection != null) {
+            bindService(new Intent(this, FileUploader.class), mUploadServiceConnection,
+                    Context.BIND_AUTO_CREATE);
+        }
+
     }
     
     private void toggleInstantPictureOptions(Boolean value){
@@ -402,6 +429,7 @@ public class Preferences extends SherlockPreferenceActivity implements AccountMa
 
                     // Remove account
                     am.removeAccount(a, this, mHandler);
+                    Log_OC.d(TAG, "Remove an account " + a.name);
                 }
             }
         }
@@ -412,6 +440,18 @@ public class Preferences extends SherlockPreferenceActivity implements AccountMa
     @Override
     public void run(AccountManagerFuture<Boolean> future) {
         if (future.isDone()) {
+            // after remove account
+            Account account = new Account(mAccountName, MainApp.getAccountType());
+            if (!AccountUtils.exists(account, MainApp.getAppContext())) {
+                // Cancel tranfers
+                if (mUploaderBinder != null) {
+                    mUploaderBinder.cancel(account);
+                }
+                if (mDownloaderBinder != null) {
+                    mDownloaderBinder.cancel(account);
+                }
+            }
+
             Account a = AccountUtils.getCurrentOwnCloudAccount(this);
             String accountName = "";
             if (a == null) {
@@ -494,6 +534,16 @@ public class Preferences extends SherlockPreferenceActivity implements AccountMa
     @Override
     protected void onDestroy() {
         mDbHandler.close();
+
+        if (mDownloadServiceConnection != null) {
+            unbindService(mDownloadServiceConnection);
+            mDownloadServiceConnection = null;
+        }
+        if (mUploadServiceConnection != null) {
+            unbindService(mUploadServiceConnection);
+            mUploadServiceConnection = null;
+        }
+
         super.onDestroy();
     }
 
@@ -637,4 +687,65 @@ public class Preferences extends SherlockPreferenceActivity implements AccountMa
         editor.putString("instant_video_upload_path", mUploadVideoPath);
         editor.commit();
     }
+
+    // Methods for ComponetsGetter
+    @Override
+    public FileDownloader.FileDownloaderBinder getFileDownloaderBinder() {
+        return mDownloaderBinder;
+    }
+
+
+    @Override
+    public FileUploader.FileUploaderBinder getFileUploaderBinder() {
+        return mUploaderBinder;
+    }
+
+    @Override
+    public OperationsService.OperationsServiceBinder getOperationsServiceBinder() {
+        return null;
+    }
+
+    @Override
+    public FileDataStorageManager getStorageManager() {
+        return null;
+    }
+
+    @Override
+    public FileOperationsHelper getFileOperationsHelper() {
+        return null;
+    }
+
+    protected ServiceConnection newTransferenceServiceConnection() {
+        return new PreferencesServiceConnection();
+    }
+
+    /** Defines callbacks for service binding, passed to bindService() */
+    private class PreferencesServiceConnection implements ServiceConnection {
+
+        @Override
+        public void onServiceConnected(ComponentName component, IBinder service) {
+
+            if (component.equals(new ComponentName(Preferences.this, FileDownloader.class))) {
+                mDownloaderBinder = (FileDownloader.FileDownloaderBinder) service;
+
+            } else if (component.equals(new ComponentName(Preferences.this, FileUploader.class))) {
+                Log_OC.d(TAG, "Upload service connected");
+                mUploaderBinder = (FileUploader.FileUploaderBinder) service;
+            } else {
+                return;
+            }
+
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName component) {
+            if (component.equals(new ComponentName(Preferences.this, FileDownloader.class))) {
+                Log_OC.d(TAG, "Download service suddenly disconnected");
+                mDownloaderBinder = null;
+            } else if (component.equals(new ComponentName(Preferences.this, FileUploader.class))) {
+                Log_OC.d(TAG, "Upload service suddenly disconnected");
+                mUploaderBinder = null;
+            }
+        }
+    };
 }