Merge branch 'develop' into cancel_transfer_for_deleted_users
authormasensio <masensio@solidgear.es>
Thu, 19 Feb 2015 11:54:59 +0000 (12:54 +0100)
committermasensio <masensio@solidgear.es>
Thu, 19 Feb 2015 11:54:59 +0000 (12:54 +0100)
Conflicts:
AndroidManifest.xml

1  2 
AndroidManifest.xml
src/com/owncloud/android/files/services/FileDownloader.java
src/com/owncloud/android/files/services/FileUploader.java
src/com/owncloud/android/ui/activity/FileDisplayActivity.java

diff --combined AndroidManifest.xml
@@@ -3,7 -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,
@@@ -18,8 -18,8 +18,8 @@@
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
   -->
  <manifest package="com.owncloud.android"
-     android:versionCode="10600200"
-     android:versionName="1.6.2" xmlns:android="http://schemas.android.com/apk/res/android">
+     android:versionCode="10700000"
+     android:versionName="1.7.0" xmlns:android="http://schemas.android.com/apk/res/android">
  
      <uses-permission android:name="android.permission.GET_ACCOUNTS" />
      <uses-permission android:name="android.permission.USE_CREDENTIALS" />
@@@ -86,7 -86,7 +86,6 @@@
              android:name=".ui.activity.Preferences"
              android:theme="@style/Theme.ownCloud" >
          </activity>
-         
 -
          <activity     
              android:name=".ui.preview.PreviewImageActivity" 
              />
@@@ -25,10 -25,8 +25,10 @@@ import java.util.HashMap
  import java.util.Iterator;
  import java.util.Map;
  import java.util.Vector;
 +import java.util.concurrent.ConcurrentMap;
  
  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;
@@@ -111,15 -109,31 +111,31 @@@ public class FileDownloader extends Ser
      @Override
      public void onCreate() {
          super.onCreate();
+         Log_OC.d(TAG, "Creating service");
          mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
-         HandlerThread thread = new HandlerThread("FileDownloaderThread",
-                 Process.THREAD_PRIORITY_BACKGROUND);
+         HandlerThread thread = new HandlerThread("FileDownloaderThread", Process.THREAD_PRIORITY_BACKGROUND);
          thread.start();
          mServiceLooper = thread.getLooper();
          mServiceHandler = new ServiceHandler(mServiceLooper, this);
          mBinder = new FileDownloaderBinder();
      }
  
+     /**
+      * Service clean up
+      */
+     @Override
+     public void onDestroy() {
+         Log_OC.v(TAG, "Destroying service" );
+         mBinder = null;
+         mServiceHandler = null;
+         mServiceLooper.quit();
+         mServiceLooper = null;
+         mNotificationManager = null;
+         super.onDestroy();
+     }
      /**
       * Entry point to add one or several files to the queue of downloads.
       *
       */
      @Override
      public int onStartCommand(Intent intent, int flags, int startId) {
+         Log_OC.d(TAG, "Starting command with id " + startId);
          if (    !intent.hasExtra(EXTRA_ACCOUNT) ||
                  !intent.hasExtra(EXTRA_FILE)
             ) {
              );
              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(),
                  }
              }
          }
 -        
 +
 +        /**
 +         * 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
 +            ConcurrentMap downloadsAccount = mPendingDownloads.get(account);
 +            Iterator<String> it = downloadsAccount.keySet().iterator();
 +            Log_OC.d(TAG, "Number of pending downloads= " + downloadsAccount.size());
 +            while (it.hasNext()) {
 +                String key = it.next();
 +                Log_OC.d(TAG, "download CANCELLED " + key);
 +                if (key.startsWith(account.name)) {
 +                    DownloadFileOperation download;
 +                    synchronized (mPendingDownloads) {
 +                        download = mPendingDownloads.get(key);
 +                        if (download != null) {
 +                            String remotePath = download.getRemotePath();
 +                            if (mPendingDownloads.contains(account, remotePath)) {
 +                                mPendingDownloads.remove(account, remotePath);
 +                            }
 +                        }
 +                    }
 +                }
 +            }
 +        }
          
          public void clearListeners() {
              mBoundListeners.clear();
                  boundListener.onTransferProgress(progressRate, totalTransferredSoFar, totalToTransfer, fileName);
              }
          }
 +
 +        /**
 +         * Review downloads and cancel it if its account doesn't exist
 +         */
 +        public void reviewDownloads() {
 +            if (mCurrentDownload != null &&
 +                    !AccountUtils.exists(mCurrentDownload.getAccount(), getApplicationContext())) {
 +                mCurrentDownload.cancel();
 +            }
 +            // The rest of downloads are cancelled when they try to start
 +        }
          
      }
      
                  Iterator<String> it = requestedDownloads.iterator();
                  while (it.hasNext()) {
                      String next = it.next();
-                     /*Log_OC.v(   "NOW " + TAG + ", thread " + Thread.currentThread().getName(),
-                             "Handling download file " + next);*/
                      mService.downloadFile(next);
                  }
              }
+             Log_OC.d(TAG, "Stopping after command with id " + msg.arg1);
              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 
       */
      private void downloadFile(String downloadKey) {
          mCurrentDownload = mPendingDownloads.get(downloadKey);
  
          if (mCurrentDownload != null) {
 -            
 -            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);
 -
 +            // Detect if the account exists
 +            if (AccountUtils.exists(mCurrentDownload.getAccount(), getApplicationContext())) {
 +                Log_OC.d(TAG, "Account " + mCurrentDownload.getAccount().toString() + " exists");
 +                notifyDownloadStart(mCurrentDownload);
  
 -                /// 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());
  
 +            }
          }
      }
  
          sendStickyBroadcast(added);
      }
  
 +    /**
 +     * Remove downloads of an account
 +     * @param account
 +     */
 +    private void cancelDownloadsForAccount(Account account){
 +        // Cancel pending downloads
 +        ConcurrentMap downloadsAccount = mPendingDownloads.get(account);
 +        Iterator<String> it = downloadsAccount.keySet().iterator();
 +        Log_OC.d(TAG, "Number of pending downloads= " + downloadsAccount.size());
 +        while (it.hasNext()) {
 +            String key = it.next();
 +            Log_OC.d(TAG, "download CANCELLED " + key);
 +            if (key.startsWith(account.name)) {
 +                DownloadFileOperation download;
 +                synchronized (mPendingDownloads) {
 +                    download = mPendingDownloads.get(key);
 +                    if (download != null) {
 +                        String remotePath = download.getRemotePath();
 +                        if (mPendingDownloads.contains(account, remotePath)) {
 +                            mPendingDownloads.remove(account, remotePath);
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +
  }
@@@ -1,6 -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,
@@@ -56,7 -56,6 +56,7 @@@ import com.owncloud.android.lib.common.
  import com.owncloud.android.lib.common.OwnCloudClientManagerFactory;
  import com.owncloud.android.lib.common.accounts.AccountUtils.Constants;
  import com.owncloud.android.lib.common.network.OnDatatransferProgressListener;
 +import com.owncloud.android.lib.common.operations.OperationCancelledException;
  import com.owncloud.android.lib.common.operations.RemoteOperation;
  import com.owncloud.android.lib.common.operations.RemoteOperationResult;
  import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode;
@@@ -125,14 -124,14 +125,14 @@@ public class FileUploader extends Servi
      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
       */
  
      /**
       * 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
      @Override
      public void onCreate() {
          super.onCreate();
-         Log_OC.i(TAG, "mPendingUploads size:" + mPendingUploads.size());
+         Log_OC.d(TAG, "Creating service");
          mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
          HandlerThread thread = new HandlerThread("FileUploaderThread", Process.THREAD_PRIORITY_BACKGROUND);
          thread.start();
      }
  
      /**
+      * Service clean up
+      */
+     @Override
+     public void onDestroy() {
+         Log_OC.v(TAG, "Destroying service" );
+         mBinder = null;
+         mServiceHandler = null;
+         mServiceLooper.quit();
+         mServiceLooper = null;
+         mNotificationManager = null;
+         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.
       */
      @Override
      public int onStartCommand(Intent intent, int flags, int startId) {
+         Log_OC.d(TAG, "Starting command with id " + startId);
          if (!intent.hasExtra(KEY_ACCOUNT) || !intent.hasExtra(KEY_UPLOAD_TYPE)
                  || !(intent.hasExtra(KEY_LOCAL_FILE) || intent.hasExtra(KEY_FILE))) {
              Log_OC.e(TAG, "Not enough information provided in intent");
          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;
          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;
          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();
      /**
       * 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.
       */
      public IBinder onBind(Intent arg0) {
          return mBinder;
      }
 -    
 +
      /**
       * Called when ALL the bound clients were onbound.
       */
          ((FileUploaderBinder)mBinder).clearListeners();
          return false;   // not accepting rebinding (default behaviour)
      }
 -    
  
      /**
       * 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
           */
                  upload = mPendingUploads.remove(buildRemoteName(account, file));
              }
              if (upload != null) {
 -                upload.cancel();
 +                mCurrentUpload.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
 +            Iterator<String> it = mPendingUploads.keySet().iterator();
 +            Log_OC.d(TAG, "Number of pending uploads= "  + mPendingUploads.size());
 +            while (it.hasNext()) {
 +                String key = it.next();
 +                Log_OC.d(TAG, "mPendingUploads CANCELLED " + key);
 +                if (key.startsWith(account.name)) {
 +                    synchronized (mPendingUploads) {
 +                        mPendingUploads.remove(key);
 +                    }
 +                }
              }
          }
 -        
 -        
 -        
 +
          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
           */
  
          /**
           * 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.
              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.
  
          @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 reviewUploads() {
 +            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()}.
       */
                      mService.uploadFile(it.next());
                  }
              }
+             Log_OC.d(TAG, "Stopping command after id " + msg.arg1);
              mService.stopSelf(msg.arg1);
          }
      }
  
      /**
       * Core upload method: sends the file(s) to upload
 -     * 
 +     *
       * @param uploadKey Key to access the upload to perform, contained in
       *            mPendingUploads
       */
  
          if (mCurrentUpload != null) {
  
 -            notifyUploadStart(mCurrentUpload);
 +            // Detect if the account exists
 +            if (AccountUtils.exists(mCurrentUpload.getAccount(), getApplicationContext())) {
 +                Log_OC.d(TAG, "Account " + mCurrentUpload.getAccount().toString() + " 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);
 +
 +            }
          }
  
      }
      /**
       * 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);
          }
          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);
          }
          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() {
              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()) {
                  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);
      }
  
      private OCFile obtainNewOCFileToUpload(String remotePath, String localPath, String mimeType,
 -            FileDataStorageManager storageManager) {
 +                                           FileDataStorageManager storageManager) {
  
          // MIME type
          if (mimeType == null || mimeType.length() <= 0) {
              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);
  
      /**
       * 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)
          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());
  
      /**
       * Updates the status notification with the result of an upload operation.
 -     * 
 +     *
       * @param uploadResult Result of the upload operation.
       * @param upload Finished upload operation
       */
          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);
                          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 {
                          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
                                  );
                              }
                      }
                  }
              }
 -            
 +
              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);
 -                
 +
              }
          }
      }
      /**
       * 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());
          }
       * @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);
 +                }
 +            }
 +        }
 +    }
  }
@@@ -1,6 -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,6 -96,7 +96,6 @@@ import com.owncloud.android.operations.
  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;
@@@ -255,7 -256,7 +255,7 @@@ OnSslUntrustedCertListener, OnEnforceab
              setNavigationListWithFolder(file);
              
              if (!stateWasRecovered) {
-                 Log_OC.e(TAG, "Initializing Fragments in onAccountChanged..");
+                 Log_OC.d(TAG, "Initializing Fragments in onAccountChanged..");
                  initFragmentsWithFile();
                  if (file.isFolder()) {
                      startSyncFolderOperation(file, false);
      }
  
      private void startSynchronization() {
-         Log_OC.e(TAG, "Got to start sync");
+         Log_OC.d(TAG, "Got to start sync");
          if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.KITKAT) {
-             Log_OC.e(TAG, "Canceling all syncs for " + MainApp.getAuthority());
+             Log_OC.d(TAG, "Canceling all syncs for " + MainApp.getAuthority());
              ContentResolver.cancelSync(null, MainApp.getAuthority());   // cancel the current synchronizations of any ownCloud account
              Bundle bundle = new Bundle();
              bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
              bundle.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
-             Log_OC.e(TAG, "Requesting sync for " + getAccount().name + " at " + MainApp.getAuthority());
+             Log_OC.d(TAG, "Requesting sync for " + getAccount().name + " at " + MainApp.getAuthority());
              ContentResolver.requestSync(
                      getAccount(),
                      MainApp.getAuthority(), bundle);
          } else {
-             Log_OC.e(TAG, "Requesting sync for " + getAccount().name + " at " + MainApp.getAuthority() + " with new API");
+             Log_OC.d(TAG, "Requesting sync for " + getAccount().name + " at " + MainApp.getAuthority() + " with new API");
              SyncRequest.Builder builder = new SyncRequest.Builder();
              builder.setSyncAdapter(getAccount(), MainApp.getAuthority());
              builder.setExpedited(true);
          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);
              }
  
          } finally {
              if (filepath == null) {
-                 Log_OC.e(TAG, "Couldnt resolve path to file");
+                 Log_OC.e(TAG, "Couldn't resolve path to file");
                  Toast t = Toast.makeText(this, getString(R.string.filedisplay_unexpected_bad_get_content), Toast.LENGTH_LONG);
                  t.show();
                  return;
      @Override
      protected void onSaveInstanceState(Bundle outState) {
          // responsibility of restore is preferred in onCreate() before than in onRestoreInstanceState when there are Fragments involved
-         Log_OC.e(TAG, "onSaveInstanceState() start");
+         Log_OC.d(TAG, "onSaveInstanceState() start");
          super.onSaveInstanceState(outState);
          outState.putParcelable(FileDisplayActivity.KEY_WAITING_TO_PREVIEW, mWaitingToPreview);
          outState.putBoolean(FileDisplayActivity.KEY_SYNC_IN_PROGRESS, mSyncInProgress);
      @Override
      protected void onResume() {
          super.onResume();
-         Log_OC.e(TAG, "onResume() start");
+         Log_OC.d(TAG, "onResume() start");
          
          // refresh list of files
          refreshListOfFilesFragment();
  
      @Override
      protected void onPause() {
-         Log_OC.e(TAG, "onPause() start");
+         Log_OC.d(TAG, "onPause() start");
          if (mSyncBroadcastReceiver != null) {
              unregisterReceiver(mSyncBroadcastReceiver);
              //LocalBroadcastManager.getInstance(this).unregisterReceiver(mSyncBroadcastReceiver);