Fix crash got with: 1. try to rename / delete a file with an outdated access token...
[pub/Android/ownCloud.git] / src / com / owncloud / android / files / services / FileDownloader.java
index 0ca4248..00d5f19 100644 (file)
@@ -1,9 +1,10 @@
 /* ownCloud Android client application\r
  *   Copyright (C) 2012 Bartek Przybylski\r
+ *   Copyright (C) 2012-2013 ownCloud Inc.\r
  *\r
  *   This program is free software: you can redistribute it and/or modify\r
  *   it under the terms of the GNU General Public License as published by\r
- *   the Free Software Foundation, either version 3 of the License, or\r
+ *   the Free Software Foundation, either version 2 of the License, or\r
  *   (at your option) any later version.\r
  *\r
  *   This program is distributed in the hope that it will be useful,\r
 package com.owncloud.android.files.services;\r
 \r
 import java.io.File;\r
+import java.io.IOException;\r
 import java.util.AbstractList;\r
 import java.util.Iterator;\r
 import java.util.Vector;\r
 import java.util.concurrent.ConcurrentHashMap;\r
 import java.util.concurrent.ConcurrentMap;\r
 \r
+import com.owncloud.android.authentication.AuthenticatorActivity;\r
 import com.owncloud.android.datamodel.FileDataStorageManager;\r
 import com.owncloud.android.datamodel.OCFile;\r
 import eu.alefzero.webdav.OnDatatransferProgressListener;\r
@@ -32,10 +35,12 @@ import eu.alefzero.webdav.OnDatatransferProgressListener;
 import com.owncloud.android.network.OwnCloudClientUtils;\r
 import com.owncloud.android.operations.DownloadFileOperation;\r
 import com.owncloud.android.operations.RemoteOperationResult;\r
+import com.owncloud.android.operations.RemoteOperationResult.ResultCode;\r
 import com.owncloud.android.ui.activity.FileDetailActivity;\r
 import com.owncloud.android.ui.fragment.FileDetailFragment;\r
 \r
 import android.accounts.Account;\r
+import android.accounts.AccountsException;\r
 import android.app.Notification;\r
 import android.app.NotificationManager;\r
 import android.app.PendingIntent;\r
@@ -59,6 +64,7 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
     public static final String EXTRA_ACCOUNT = "ACCOUNT";\r
     public static final String EXTRA_FILE = "FILE";\r
     \r
+    public static final String DOWNLOAD_ADDED_MESSAGE = "DOWNLOAD_ADDED";\r
     public static final String DOWNLOAD_FINISH_MESSAGE = "DOWNLOAD_FINISH";\r
     public static final String EXTRA_DOWNLOAD_RESULT = "RESULT";    \r
     public static final String EXTRA_FILE_PATH = "FILE_PATH";\r
@@ -135,6 +141,7 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
             mPendingDownloads.putIfAbsent(downloadKey, newDownload);\r
             newDownload.addDatatransferProgressListener(this);\r
             requestedDownloads.add(downloadKey);\r
+            sendBroadcastNewDownload(newDownload);\r
             \r
         } catch (IllegalArgumentException e) {\r
             Log.e(TAG, "Not enough information provided in intent: " + e.getMessage());\r
@@ -188,14 +195,28 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
         \r
         \r
         /**\r
-         * Returns True when the file described by 'file' in the ownCloud account 'account' is downloading or waiting to download\r
+         * Returns True when the file described by 'file' in the ownCloud account 'account' is downloading or waiting to download.\r
+         * \r
+         * If 'file' is a directory, returns 'true' if some of its descendant files is downloading or waiting to download. \r
          * \r
          * @param account       Owncloud account where the remote file is stored.\r
          * @param file          A file that could be in the queue of downloads.\r
          */\r
         public boolean isDownloading(Account account, OCFile file) {\r
+            if (account == null || file == null) return false;\r
+            String targetKey = buildRemoteName(account, file);\r
             synchronized (mPendingDownloads) {\r
-                return (mPendingDownloads.containsKey(buildRemoteName(account, file)));\r
+                if (file.isDirectory()) {\r
+                    // this can be slow if there are many downloads :(\r
+                    Iterator<String> it = mPendingDownloads.keySet().iterator();\r
+                    boolean found = false;\r
+                    while (it.hasNext() && !found) {\r
+                        found = it.next().startsWith(targetKey);\r
+                    }\r
+                    return found;\r
+                } else {\r
+                    return (mPendingDownloads.containsKey(targetKey));\r
+                }\r
             }\r
         }\r
     }\r
@@ -247,21 +268,30 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
             \r
             notifyDownloadStart(mCurrentDownload);\r
 \r
-            /// prepare client object to send the request to the ownCloud server\r
-            if (mDownloadClient == null || !mLastAccount.equals(mCurrentDownload.getAccount())) {\r
-                mLastAccount = mCurrentDownload.getAccount();\r
-                mStorageManager = new FileDataStorageManager(mLastAccount, getContentResolver());\r
-                mDownloadClient = OwnCloudClientUtils.createOwnCloudClient(mLastAccount, getApplicationContext());\r
-            }\r
-\r
-            /// perform the download\r
             RemoteOperationResult downloadResult = null;\r
             try {\r
-                downloadResult = mCurrentDownload.execute(mDownloadClient);\r
+                /// prepare client object to send the request to the ownCloud server\r
+                if (mDownloadClient == null || !mLastAccount.equals(mCurrentDownload.getAccount())) {\r
+                    mLastAccount = mCurrentDownload.getAccount();\r
+                    mStorageManager = new FileDataStorageManager(mLastAccount, getContentResolver());\r
+                    mDownloadClient = OwnCloudClientUtils.createOwnCloudClient(mLastAccount, getApplicationContext());\r
+                }\r
+\r
+                /// perform the download\r
+                if (downloadResult == null) {\r
+                    downloadResult = mCurrentDownload.execute(mDownloadClient);\r
+                }\r
                 if (downloadResult.isSuccess()) {\r
                     saveDownloadedFile();\r
                 }\r
             \r
+            } catch (AccountsException e) {\r
+                Log.e(TAG, "Error while trying to get autorization for " + mLastAccount.name, e);\r
+                downloadResult = new RemoteOperationResult(e);\r
+            } catch (IOException e) {\r
+                Log.e(TAG, "Error while trying to get autorization for " + mLastAccount.name, e);\r
+                downloadResult = new RemoteOperationResult(e);\r
+                \r
             } finally {\r
                 synchronized(mPendingDownloads) {\r
                     mPendingDownloads.remove(downloadKey);\r
@@ -272,7 +302,7 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
             /// notify result\r
             notifyDownloadResult(mCurrentDownload, downloadResult);\r
             \r
-            sendFinalBroadcast(mCurrentDownload, downloadResult);\r
+            sendBroadcastDownloadFinished(mCurrentDownload, downloadResult);\r
         }\r
     }\r
 \r
@@ -282,8 +312,11 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
      */\r
     private void saveDownloadedFile() {\r
         OCFile file = mCurrentDownload.getFile();\r
-        file.setLastSyncDate(System.currentTimeMillis());\r
+        long syncDate = System.currentTimeMillis();\r
+        file.setLastSyncDateForProperties(syncDate);\r
+        file.setLastSyncDateForData(syncDate);\r
         file.setModificationTimestamp(mCurrentDownload.getModificationTimestamp());\r
+        file.setModificationTimestampAtLastSyncForData(mCurrentDownload.getModificationTimestamp());\r
         // file.setEtag(mCurrentDownload.getEtag());    // TODO Etag, where available\r
         file.setMimetype(mCurrentDownload.getMimeType());\r
         file.setStoragePath(mCurrentDownload.getSavePath());\r
@@ -356,27 +389,59 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
             int contentId = (downloadResult.isSuccess()) ? R.string.downloader_download_succeeded_content : R.string.downloader_download_failed_content;\r
             Notification finalNotification = new Notification(R.drawable.icon, getString(tickerId), System.currentTimeMillis());\r
             finalNotification.flags |= Notification.FLAG_AUTO_CANCEL;\r
-            // TODO put something smart in the contentIntent below\r
-            finalNotification.contentIntent = PendingIntent.getActivity(getApplicationContext(), (int)System.currentTimeMillis(), new Intent(), 0);\r
-            finalNotification.setLatestEventInfo(getApplicationContext(), getString(tickerId), String.format(getString(contentId), new File(download.getSavePath()).getName()), finalNotification.contentIntent);\r
+            boolean needsToUpdateCredentials = (downloadResult.getCode() == ResultCode.UNAUTHORIZED);\r
+            if (needsToUpdateCredentials) {\r
+                // let the user update credentials with one click\r
+                Intent updateAccountCredentials = new Intent(this, AuthenticatorActivity.class);\r
+                updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACCOUNT, download.getAccount());\r
+                updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACTION, AuthenticatorActivity.ACTION_UPDATE_TOKEN);\r
+                updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\r
+                updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);\r
+                updateAccountCredentials.addFlags(Intent.FLAG_FROM_BACKGROUND);\r
+                finalNotification.contentIntent = PendingIntent.getActivity(this, (int)System.currentTimeMillis(), updateAccountCredentials, PendingIntent.FLAG_ONE_SHOT);\r
+                finalNotification.setLatestEventInfo(   getApplicationContext(), \r
+                                                        getString(tickerId), \r
+                                                        String.format(getString(contentId), new File(download.getSavePath()).getName()),\r
+                                                        finalNotification.contentIntent);\r
+                mDownloadClient = null;   // grant that future retries on the same account will get the fresh credentials\r
+                \r
+            } else {\r
+                // TODO put something smart in the contentIntent below\r
+                finalNotification.contentIntent = PendingIntent.getActivity(getApplicationContext(), (int)System.currentTimeMillis(), new Intent(), 0);\r
+                finalNotification.setLatestEventInfo(getApplicationContext(), getString(tickerId), String.format(getString(contentId), new File(download.getSavePath()).getName()), finalNotification.contentIntent);\r
+            }\r
             mNotificationManager.notify(tickerId, finalNotification);\r
         }\r
     }\r
     \r
     \r
     /**\r
-     * Sends a broadcast in order to the interested activities can update their view\r
+     * Sends a broadcast when a download finishes in order to the interested activities can update their view\r
      * \r
      * @param download          Finished download operation\r
      * @param downloadResult    Result of the download operation\r
      */\r
-    private void sendFinalBroadcast(DownloadFileOperation download, RemoteOperationResult downloadResult) {\r
+    private void sendBroadcastDownloadFinished(DownloadFileOperation download, RemoteOperationResult downloadResult) {\r
         Intent end = new Intent(DOWNLOAD_FINISH_MESSAGE);\r
         end.putExtra(EXTRA_DOWNLOAD_RESULT, downloadResult.isSuccess());\r
         end.putExtra(ACCOUNT_NAME, download.getAccount().name);\r
         end.putExtra(EXTRA_REMOTE_PATH, download.getRemotePath());\r
         end.putExtra(EXTRA_FILE_PATH, download.getSavePath());\r
-        sendBroadcast(end);\r
+        sendStickyBroadcast(end);\r
+    }\r
+    \r
+    \r
+    /**\r
+     * Sends a broadcast when a new download is added to the queue.\r
+     * \r
+     * @param download          Added download operation\r
+     */\r
+    private void sendBroadcastNewDownload(DownloadFileOperation download) {\r
+        Intent added = new Intent(DOWNLOAD_ADDED_MESSAGE);\r
+        /*added.putExtra(ACCOUNT_NAME, download.getAccount().name);\r
+        added.putExtra(EXTRA_REMOTE_PATH, download.getRemotePath());*/\r
+        added.putExtra(EXTRA_FILE_PATH, download.getSavePath());\r
+        sendStickyBroadcast(added);\r
     }\r
 \r
 }\r