Download service refactoring: multiple downloads and cancellation support
authorDavid A. Velasco <dvelasco@solidgear.es>
Tue, 9 Oct 2012 12:53:25 +0000 (14:53 +0200)
committerDavid A. Velasco <dvelasco@solidgear.es>
Tue, 9 Oct 2012 12:53:25 +0000 (14:53 +0200)
16 files changed:
src/com/owncloud/android/files/services/FileDownloader.java
src/com/owncloud/android/files/services/FileUploader.java
src/com/owncloud/android/operations/DownloadFileOperation.java [new file with mode: 0644]
src/com/owncloud/android/operations/OperationCancelledException.java [new file with mode: 0644]
src/com/owncloud/android/operations/RemoteOperationResult.java
src/com/owncloud/android/operations/UploadFileOperation.java
src/com/owncloud/android/ui/activity/FileDetailActivity.java
src/com/owncloud/android/ui/activity/FileDisplayActivity.java
src/com/owncloud/android/ui/activity/TransferServiceGetter.java [new file with mode: 0644]
src/com/owncloud/android/ui/adapter/FileListListAdapter.java
src/com/owncloud/android/ui/fragment/FileDetailFragment.java
src/com/owncloud/android/ui/fragment/OCFileListFragment.java
src/eu/alefzero/webdav/ChunkFromFileChannelRequestEntity.java
src/eu/alefzero/webdav/FileRequestEntity.java
src/eu/alefzero/webdav/OnDatatransferProgressListener.java
src/eu/alefzero/webdav/WebdavClient.java

index a1881df..2e0c686 100644 (file)
@@ -1,14 +1,18 @@
 package com.owncloud.android.files.services;\r
 \r
 import java.io.File;\r
 package com.owncloud.android.files.services;\r
 \r
 import java.io.File;\r
-import java.util.Collections;\r
-import java.util.HashMap;\r
-import java.util.Map;\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.db.ProviderMeta.ProviderTableMeta;\r
 import eu.alefzero.webdav.OnDatatransferProgressListener;\r
 \r
 import com.owncloud.android.network.OwnCloudClientUtils;\r
 \r
 import com.owncloud.android.db.ProviderMeta.ProviderTableMeta;\r
 import eu.alefzero.webdav.OnDatatransferProgressListener;\r
 \r
 import com.owncloud.android.network.OwnCloudClientUtils;\r
+import com.owncloud.android.operations.DownloadFileOperation;\r
+import com.owncloud.android.operations.RemoteOperationResult;\r
 \r
 import android.accounts.Account;\r
 import android.app.Notification;\r
 \r
 import android.accounts.Account;\r
 import android.app.Notification;\r
@@ -18,6 +22,7 @@ import android.app.Service;
 import android.content.ContentValues;\r
 import android.content.Intent;\r
 import android.net.Uri;\r
 import android.content.ContentValues;\r
 import android.content.Intent;\r
 import android.net.Uri;\r
+import android.os.Binder;\r
 import android.os.Environment;\r
 import android.os.Handler;\r
 import android.os.HandlerThread;\r
 import android.os.Environment;\r
 import android.os.Handler;\r
 import android.os.HandlerThread;\r
@@ -41,28 +46,40 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
     \r
     private static final String TAG = "FileDownloader";\r
 \r
     \r
     private static final String TAG = "FileDownloader";\r
 \r
-    private NotificationManager mNotificationMngr;\r
     private Looper mServiceLooper;\r
     private ServiceHandler mServiceHandler;\r
     private Looper mServiceLooper;\r
     private ServiceHandler mServiceHandler;\r
+    private IBinder mBinder;\r
+    private WebdavClient mDownloadClient = null;\r
+    private Account mLastAccount = null;\r
+    \r
+    //private AbstractList<Account> mAccounts = new Vector<Account>();\r
+    private ConcurrentMap<String, DownloadFileOperation> mPendingDownloads = new ConcurrentHashMap<String, DownloadFileOperation>();\r
+    private DownloadFileOperation mCurrentDownload = null;\r
+    \r
+    /*\r
     private Account mAccount;\r
     private String mFilePath;\r
     private String mRemotePath;\r
     private Account mAccount;\r
     private String mFilePath;\r
     private String mRemotePath;\r
-    private int mLastPercent;\r
     private long mTotalDownloadSize;\r
     private long mCurrentDownloadSize;\r
     private long mTotalDownloadSize;\r
     private long mCurrentDownloadSize;\r
+    */\r
+    \r
+    private NotificationManager mNotificationMngr;\r
     private Notification mNotification;\r
     private Notification mNotification;\r
+    private int mLastPercent;\r
+    \r
     \r
     /**\r
      * Static map with the files being download and the path to the temporal file were are download\r
      */\r
     \r
     /**\r
      * Static map with the files being download and the path to the temporal file were are download\r
      */\r
-    private static Map<String, String> mDownloadsInProgress = Collections.synchronizedMap(new HashMap<String, String>());\r
+    //private static Set<String> mDownloadsInProgress = Collections.synchronizedSet(new HashSet<String>());\r
     \r
     /**\r
      * Returns True when the file referred by 'remotePath' in the ownCloud account 'account' is downloading\r
      */\r
     \r
     /**\r
      * Returns True when the file referred by 'remotePath' in the ownCloud account 'account' is downloading\r
      */\r
-    public static boolean isDownloading(Account account, String remotePath) {\r
-        return (mDownloadsInProgress.get(buildRemoteName(account.name, remotePath)) != null);\r
-    }\r
+    /*public static boolean isDownloading(Account account, String remotePath) {\r
+        return (mDownloadsInProgress.contains(buildRemoteName(account.name, remotePath)));\r
+    }*/\r
     \r
     /**\r
      * Builds a key for mDownloadsInProgress from the accountName and remotePath\r
     \r
     /**\r
      * Builds a key for mDownloadsInProgress from the accountName and remotePath\r
@@ -70,19 +87,6 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
     private static String buildRemoteName(String accountName, String remotePath) {\r
         return accountName + remotePath;\r
     }\r
     private static String buildRemoteName(String accountName, String remotePath) {\r
         return accountName + remotePath;\r
     }\r
-\r
-    \r
-    private final class ServiceHandler extends Handler {\r
-        public ServiceHandler(Looper looper) {\r
-            super(looper);\r
-        }\r
-\r
-        @Override\r
-        public void handleMessage(Message msg) {\r
-            downloadFile();\r
-            stopSelf(msg.arg1);\r
-        }\r
-    }\r
     \r
     public static final String getSavePath(String accountName) {\r
         File sdCard = Environment.getExternalStorageDirectory();\r
     \r
     public static final String getSavePath(String accountName) {\r
         File sdCard = Environment.getExternalStorageDirectory();\r
@@ -96,6 +100,10 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
             // URL encoding is an 'easy fix' to overcome that NTFS and FAT32 don't allow ":" in file names, that can be in the accountName since 0.1.190B\r
     }\r
 \r
             // URL encoding is an 'easy fix' to overcome that NTFS and FAT32 don't allow ":" in file names, that can be in the accountName since 0.1.190B\r
     }\r
 \r
+    \r
+    /**\r
+     * Service initialization\r
+     */\r
     @Override\r
     public void onCreate() {\r
         super.onCreate();\r
     @Override\r
     public void onCreate() {\r
         super.onCreate();\r
@@ -105,13 +113,16 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
         thread.start();\r
         mServiceLooper = thread.getLooper();\r
         mServiceHandler = new ServiceHandler(mServiceLooper);\r
         thread.start();\r
         mServiceLooper = thread.getLooper();\r
         mServiceHandler = new ServiceHandler(mServiceLooper);\r
+        mBinder = new FileDownloaderBinder();\r
     }\r
 \r
     }\r
 \r
-    @Override\r
-    public IBinder onBind(Intent arg0) {\r
-        return null;\r
-    }\r
-\r
+    \r
+    /**\r
+     * Entry point to add one or several files to the queue of downloads.\r
+     * \r
+     * New downloads are added calling to startService(), resulting in a call to this method. This ensures the service will keep on working \r
+     * although the caller activity goes away.\r
+     */\r
     @Override\r
     public int onStartCommand(Intent intent, int flags, int startId) {\r
         if (    !intent.hasExtra(EXTRA_ACCOUNT) ||\r
     @Override\r
     public int onStartCommand(Intent intent, int flags, int startId) {\r
         if (    !intent.hasExtra(EXTRA_ACCOUNT) ||\r
@@ -121,116 +132,241 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
             Log.e(TAG, "Not enough information provided in intent");\r
             return START_NOT_STICKY;\r
         }\r
             Log.e(TAG, "Not enough information provided in intent");\r
             return START_NOT_STICKY;\r
         }\r
-        mAccount = intent.getParcelableExtra(EXTRA_ACCOUNT);\r
-        mFilePath = intent.getStringExtra(EXTRA_FILE_PATH);\r
-        mRemotePath = intent.getStringExtra(EXTRA_REMOTE_PATH);\r
-        mTotalDownloadSize = intent.getLongExtra(EXTRA_FILE_SIZE, -1);\r
-        mCurrentDownloadSize = mLastPercent = 0;\r
+        Account account = intent.getParcelableExtra(EXTRA_ACCOUNT);\r
+        String filePath = intent.getStringExtra(EXTRA_FILE_PATH);\r
+        String remotePath = intent.getStringExtra(EXTRA_REMOTE_PATH);\r
+        long totalDownloadSize = intent.getLongExtra(EXTRA_FILE_SIZE, -1);\r
 \r
 \r
-        Message msg = mServiceHandler.obtainMessage();\r
-        msg.arg1 = startId;\r
-        mServiceHandler.sendMessage(msg);\r
+        AbstractList<String> requestedDownloads = new Vector<String>(); // dvelasco: now this will always contain just one element, but that can change in a near future\r
+        String downloadKey = buildRemoteName(account.name, remotePath);\r
+        try {\r
+            DownloadFileOperation newDownload = new DownloadFileOperation(account, filePath, remotePath, (String)null, totalDownloadSize, false); \r
+            mPendingDownloads.putIfAbsent(downloadKey, newDownload);\r
+            newDownload.addDatatransferProgressListener(this);\r
+            requestedDownloads.add(downloadKey);\r
+            \r
+        } catch (IllegalArgumentException e) {\r
+            Log.e(TAG, "Not enough information provided in intent: " + e.getMessage());\r
+            return START_NOT_STICKY;\r
+        }\r
+        \r
+        if (requestedDownloads.size() > 0) {\r
+            Message msg = mServiceHandler.obtainMessage();\r
+            msg.arg1 = startId;\r
+            msg.obj = requestedDownloads;\r
+            mServiceHandler.sendMessage(msg);\r
+        }\r
 \r
         return START_NOT_STICKY;\r
     }\r
 \r
         return START_NOT_STICKY;\r
     }\r
-\r
+    \r
+    \r
     /**\r
     /**\r
-     * Core download method: requests the file to download and stores it.\r
+     * Provides a binder object that clients can use to perform operations on the queue of downloads, excepting the addition of new files. \r
+     * \r
+     * Implemented to perform cancellation, pause and resume of existing downloads.\r
      */\r
      */\r
-    private void downloadFile() {\r
-        boolean downloadResult = false;\r
+    @Override\r
+    public IBinder onBind(Intent arg0) {\r
+        return mBinder;\r
+    }\r
 \r
 \r
-        /// prepare client object to send the request to the ownCloud server\r
-        WebdavClient wdc = OwnCloudClientUtils.createOwnCloudClient(mAccount, getApplicationContext());\r
-        wdc.setDataTransferProgressListener(this);\r
+    \r
+    /**\r
+     *  Binder to let client components to perform operations on the queue of downloads.\r
+     * \r
+     *  It provides by itself the available operations.\r
+     */\r
+    public class FileDownloaderBinder extends Binder {\r
         \r
         \r
-        /// download will be in a temporal file\r
-        File tmpFile = new File(getTemporalPath(mAccount.name) + mFilePath);\r
+        /**\r
+         * Cancels a pending or current download of a remote file.\r
+         * \r
+         * @param account       Owncloud account where the remote file is stored.\r
+         * @param remotePath    URL to the remote file in the queue of downloads.\r
+         */\r
+        public void cancel(Account account, String remotePath) {\r
+            synchronized (mPendingDownloads) {\r
+                DownloadFileOperation download = mPendingDownloads.remove(buildRemoteName(account.name, remotePath));\r
+                if (download != null) {\r
+                    download.cancel();\r
+                }\r
+            }\r
+        }\r
         \r
         \r
-        /// create status notification to show the download progress\r
-        mNotification = new Notification(R.drawable.icon, getString(R.string.downloader_download_in_progress_ticker), System.currentTimeMillis());\r
-        mNotification.flags |= Notification.FLAG_ONGOING_EVENT;\r
-        mNotification.contentView = new RemoteViews(getApplicationContext().getPackageName(), R.layout.progressbar_layout);\r
-        mNotification.contentView.setProgressBar(R.id.status_progress, 100, 0, mTotalDownloadSize == -1);\r
-        mNotification.contentView.setTextViewText(R.id.status_text, String.format(getString(R.string.downloader_download_in_progress_content), 0, tmpFile.getName()));\r
-        mNotification.contentView.setImageViewResource(R.id.status_icon, R.drawable.icon);\r
-        // TODO put something smart in the contentIntent below\r
-        mNotification.contentIntent = PendingIntent.getActivity(getApplicationContext(), 0, new Intent(), PendingIntent.FLAG_UPDATE_CURRENT);\r
-        mNotificationMngr.notify(R.string.downloader_download_in_progress_ticker, mNotification);\r
         \r
         \r
+        /**\r
+         * Returns True when the file referred by 'remotePath' in the ownCloud account 'account' is downloading\r
+         * \r
+         * @param account       Owncloud account where the remote file is stored.\r
+         * @param remotePath    URL to the remote file in the queue of downloads.\r
+         */\r
+        public boolean isDownloading(Account account, String remotePath) {\r
+            synchronized (mPendingDownloads) {\r
+                return (mPendingDownloads.containsKey(buildRemoteName(account.name, remotePath)));\r
+            }\r
+        }\r
+    }\r
+    \r
+    /** \r
+     * Download worker. Performs the pending downloads in the order they were requested. \r
+     * \r
+     * Created with the Looper of a new thread, started in {@link FileUploader#onCreate()}. \r
+     */\r
+    private final class ServiceHandler extends Handler {\r
+        public ServiceHandler(Looper looper) {\r
+            super(looper);\r
+        }\r
 \r
 \r
-        /// perform the download\r
-        tmpFile.getParentFile().mkdirs();\r
-        mDownloadsInProgress.put(buildRemoteName(mAccount.name, mRemotePath), tmpFile.getAbsolutePath());\r
-        File newFile = null;\r
-        try {\r
-            if (wdc.downloadFile(mRemotePath, tmpFile)) {\r
-                newFile = new File(getSavePath(mAccount.name) + mFilePath);\r
-                newFile.getParentFile().mkdirs();\r
-                boolean moved = tmpFile.renameTo(newFile);\r
+        @Override\r
+        public void handleMessage(Message msg) {\r
+            @SuppressWarnings("unchecked")\r
+            AbstractList<String> requestedDownloads = (AbstractList<String>) msg.obj;\r
+            if (msg.obj != null) {\r
+                Iterator<String> it = requestedDownloads.iterator();\r
+                while (it.hasNext()) {\r
+                    downloadFile(it.next());\r
+                }\r
+            }\r
+            stopSelf(msg.arg1);\r
+        }\r
+    }\r
+    \r
+    \r
+\r
+    /**\r
+     * Core download method: requests a file to download and stores it.\r
+     * \r
+     * @param downloadKey   Key to access the download to perform, contained in mPendingDownloads \r
+     */\r
+    private void downloadFile(String downloadKey) {\r
+        \r
+        synchronized(mPendingDownloads) {\r
+            mCurrentDownload = mPendingDownloads.get(downloadKey);\r
+        }\r
+        \r
+        if (mCurrentDownload != null) {\r
             \r
             \r
-                if (moved) {\r
+            notifyDownloadStart(mCurrentDownload);\r
+\r
+            /// prepare client object to send the request to the ownCloud server\r
+            if (mDownloadClient == null || mLastAccount != mCurrentDownload.getAccount()) {\r
+                mLastAccount = mCurrentDownload.getAccount();\r
+                mDownloadClient = OwnCloudClientUtils.createOwnCloudClient(mLastAccount, getApplicationContext());\r
+            }\r
+\r
+            /// perform the download\r
+            //mDownloadsInProgress.add(buildRemoteName(mLastAccount.name, mCurrentDownload.getRemotePath()));\r
+            RemoteOperationResult downloadResult = null;\r
+            File newLocalFile = null;\r
+            //try {\r
+                downloadResult = mCurrentDownload.execute(mDownloadClient);\r
+                if (downloadResult.isSuccess()) {\r
                     ContentValues cv = new ContentValues();\r
                     ContentValues cv = new ContentValues();\r
-                    cv.put(ProviderTableMeta.FILE_STORAGE_PATH, newFile.getAbsolutePath());\r
+                    newLocalFile = new File(getSavePath(mCurrentDownload.getAccount().name) + mCurrentDownload.getLocalPath());\r
+                    cv.put(ProviderTableMeta.FILE_STORAGE_PATH, newLocalFile.getAbsolutePath());\r
                     getContentResolver().update(\r
                             ProviderTableMeta.CONTENT_URI,\r
                             cv,\r
                             ProviderTableMeta.FILE_NAME + "=? AND "\r
                                     + ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?",\r
                     getContentResolver().update(\r
                             ProviderTableMeta.CONTENT_URI,\r
                             cv,\r
                             ProviderTableMeta.FILE_NAME + "=? AND "\r
                                     + ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?",\r
-                            new String[] {\r
-                                mFilePath.substring(mFilePath.lastIndexOf('/') + 1),\r
-                                mAccount.name });\r
-                    downloadResult = true;\r
+                                    new String[] {\r
+                                    mCurrentDownload.getLocalPath().substring(mCurrentDownload.getLocalPath().lastIndexOf('/') + 1),\r
+                                    mLastAccount.name });\r
                 }\r
                 }\r
-            }\r
-        } finally {\r
-            mDownloadsInProgress.remove(buildRemoteName(mAccount.name, mRemotePath));\r
-        }\r
-\r
+            \r
+            /*} finally {\r
+                mDownloadsInProgress.remove(buildRemoteName(mLastAccount.name, mCurrentDownload.getRemotePath()));\r
+            }*/\r
         \r
         \r
-        /// notify result\r
-        mNotificationMngr.cancel(R.string.downloader_download_in_progress_ticker);\r
-        int tickerId = (downloadResult) ? R.string.downloader_download_succeeded_ticker : R.string.downloader_download_failed_ticker;\r
-        int contentId = (downloadResult) ? 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(), 0, new Intent(), PendingIntent.FLAG_UPDATE_CURRENT);\r
-        finalNotification.setLatestEventInfo(getApplicationContext(), getString(tickerId), String.format(getString(contentId), tmpFile.getName()), finalNotification.contentIntent);\r
-        mNotificationMngr.notify(tickerId, finalNotification);\r
+            mPendingDownloads.remove(downloadKey);\r
             \r
             \r
-        sendFinalBroadcast(downloadResult, (downloadResult)?newFile.getAbsolutePath():null);\r
+            /// notify result\r
+            notifyDownloadResult(mCurrentDownload, downloadResult);\r
+            \r
+            sendFinalBroadcast(mCurrentDownload, downloadResult, (downloadResult.isSuccess())? newLocalFile.getAbsolutePath():null);\r
+        }\r
     }\r
 \r
     }\r
 \r
+    \r
     /**\r
      * Callback method to update the progress bar in the status notification.\r
      */\r
     @Override\r
     /**\r
      * Callback method to update the progress bar in the status notification.\r
      */\r
     @Override\r
-    public void transferProgress(long progressRate) {\r
-        mCurrentDownloadSize += progressRate;\r
-        int percent = (int)(100.0*((double)mCurrentDownloadSize)/((double)mTotalDownloadSize));\r
+    public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer, String fileName) {\r
+        int percent = (int)(100.0*((double)totalTransferredSoFar)/((double)totalToTransfer));\r
         if (percent != mLastPercent) {\r
         if (percent != mLastPercent) {\r
-          mNotification.contentView.setProgressBar(R.id.status_progress, 100, (int)(100*mCurrentDownloadSize/mTotalDownloadSize), mTotalDownloadSize == -1);\r
-          mNotification.contentView.setTextViewText(R.id.status_text, String.format(getString(R.string.downloader_download_in_progress_content), percent, new File(mFilePath).getName()));\r
+          mNotification.contentView.setProgressBar(R.id.status_progress, 100, percent, totalToTransfer == -1);\r
+          mNotification.contentView.setTextViewText(R.id.status_text, String.format(getString(R.string.downloader_download_in_progress_content), percent, fileName));\r
           mNotificationMngr.notify(R.string.downloader_download_in_progress_ticker, mNotification);\r
         }\r
           mNotificationMngr.notify(R.string.downloader_download_in_progress_ticker, mNotification);\r
         }\r
-        \r
         mLastPercent = percent;\r
     }\r
     \r
         mLastPercent = percent;\r
     }\r
     \r
+    \r
+    /**\r
+     * Callback method to update the progress bar in the status notification (old version)\r
+     */\r
+    @Override\r
+    public void onTransferProgress(long progressRate) {\r
+        // NOTHING TO DO HERE ANYMORE\r
+    }\r
+    \r
+\r
+    /**\r
+     * Creates a status notification to show the download progress\r
+     * \r
+     * @param download  Download operation starting.\r
+     */\r
+    private void notifyDownloadStart(DownloadFileOperation download) {\r
+        /// create status notification to show the download progress\r
+        mLastPercent = 0;\r
+        mNotification = new Notification(R.drawable.icon, getString(R.string.downloader_download_in_progress_ticker), System.currentTimeMillis());\r
+        mNotification.flags |= Notification.FLAG_ONGOING_EVENT;\r
+        mNotification.contentView = new RemoteViews(getApplicationContext().getPackageName(), R.layout.progressbar_layout);\r
+        mNotification.contentView.setProgressBar(R.id.status_progress, 100, 0, download.getSize() == -1);\r
+        mNotification.contentView.setTextViewText(R.id.status_text, String.format(getString(R.string.downloader_download_in_progress_content), 0, new File(download.getLocalPath()).getName()));\r
+        mNotification.contentView.setImageViewResource(R.id.status_icon, R.drawable.icon);\r
+        // TODO put something smart in the contentIntent below\r
+        mNotification.contentIntent = PendingIntent.getActivity(getApplicationContext(), 0, new Intent(), PendingIntent.FLAG_UPDATE_CURRENT);\r
+        mNotificationMngr.notify(R.string.downloader_download_in_progress_ticker, mNotification);\r
+    }\r
 \r
 \r
+    \r
+    /**\r
+     * Updates the status notification with the result of a download operation.\r
+     * \r
+     * @param downloadResult    Result of the download operation.\r
+     * @param download          Finished download operation\r
+     */\r
+    private void notifyDownloadResult(DownloadFileOperation download, RemoteOperationResult downloadResult) {\r
+        mNotificationMngr.cancel(R.string.downloader_download_in_progress_ticker);\r
+        if (!downloadResult.isCancelled()) {\r
+            int tickerId = (downloadResult.isSuccess()) ? R.string.downloader_download_succeeded_ticker : R.string.downloader_download_failed_ticker;\r
+            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(), 0, new Intent(), PendingIntent.FLAG_UPDATE_CURRENT);\r
+            finalNotification.setLatestEventInfo(getApplicationContext(), getString(tickerId), String.format(getString(contentId), new File(download.getLocalPath()).getName()), finalNotification.contentIntent);\r
+            mNotificationMngr.notify(tickerId, finalNotification);\r
+        }\r
+    }\r
+    \r
+    \r
     /**\r
      * Sends a broadcast in order to the interested activities can update their view\r
      * \r
     /**\r
      * Sends a broadcast in order to the interested activities can update their view\r
      * \r
-     * @param downloadResult        'True' if the download was successful\r
-     * @param newFilePath           Absolute path to the download file\r
+     * @param download          Finished download operation\r
+     * @param downloadResult    Result of the download operation\r
+     * @param newFilePath       Absolute path to the downloaded file\r
      */\r
      */\r
-    private void sendFinalBroadcast(boolean downloadResult, String newFilePath) {\r
+    private void sendFinalBroadcast(DownloadFileOperation download, RemoteOperationResult downloadResult, String newFilePath) {\r
         Intent end = new Intent(DOWNLOAD_FINISH_MESSAGE);\r
         Intent end = new Intent(DOWNLOAD_FINISH_MESSAGE);\r
-        end.putExtra(EXTRA_DOWNLOAD_RESULT, downloadResult);\r
-        end.putExtra(ACCOUNT_NAME, mAccount.name);\r
-        end.putExtra(EXTRA_REMOTE_PATH, mRemotePath);\r
-        if (downloadResult) {\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
+        if (downloadResult.isSuccess()) {\r
             end.putExtra(EXTRA_FILE_PATH, newFilePath);\r
         }\r
         sendBroadcast(end);\r
             end.putExtra(EXTRA_FILE_PATH, newFilePath);\r
         }\r
         sendBroadcast(end);\r
index ad8356d..4a849eb 100644 (file)
@@ -62,17 +62,20 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
 
     private static final String TAG = FileUploader.class.getSimpleName();
     
 
     private static final String TAG = FileUploader.class.getSimpleName();
     
-    private NotificationManager mNotificationManager;
     private Looper mServiceLooper;
     private ServiceHandler mServiceHandler;
     private Looper mServiceLooper;
     private ServiceHandler mServiceHandler;
+
     private AbstractList<Account> mAccounts = new Vector<Account>();
     private AbstractList<Account> mAccounts = new Vector<Account>();
-    private AbstractList<UploadFileOperation> mUploads = new Vector<UploadFileOperation>(); 
+    private AbstractList<UploadFileOperation> mUploads = new Vector<UploadFileOperation>();
+    private int mCurrentIndexUpload;
+    
+    private NotificationManager mNotificationManager;
     private Notification mNotification;
     private Notification mNotification;
+    private RemoteViews mDefaultNotificationContentView;
     private long mTotalDataToSend, mSendData;
     private long mTotalDataToSend, mSendData;
-    private int mTotalFilesToSend;
-    private int mCurrentIndexUpload, mPreviousPercent;
+    private int mTotalFilesToSend, mPreviousPercent;
     private int mSuccessCounter;
     private int mSuccessCounter;
-    private RemoteViews mDefaultNotificationContentView;
+    
     
     /**
      * Static map with the files being download and the path to the temporal file were are download
     
     /**
      * Static map with the files being download and the path to the temporal file were are download
@@ -107,23 +110,9 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
 
     
 
 
     
 
-    @Override
-    public IBinder onBind(Intent arg0) {
-        return null;
-    }
-
-    private final class ServiceHandler extends Handler {
-        public ServiceHandler(Looper looper) {
-            super(looper);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            uploadFile();
-            stopSelf(msg.arg1);
-        }
-    }
-
+    /**
+     * Service initialization
+     */
     @Override
     public void onCreate() {
         super.onCreate();
     @Override
     public void onCreate() {
         super.onCreate();
@@ -135,6 +124,13 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
         mServiceHandler = new ServiceHandler(mServiceLooper);
     }
 
         mServiceHandler = new ServiceHandler(mServiceLooper);
     }
 
+    
+    /**
+     * 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) {
         if (!intent.hasExtra(KEY_ACCOUNT) && !intent.hasExtra(KEY_UPLOAD_TYPE)) {
     @Override
     public int onStartCommand(Intent intent, int flags, int startId) {
         if (!intent.hasExtra(KEY_ACCOUNT) && !intent.hasExtra(KEY_UPLOAD_TYPE)) {
@@ -192,6 +188,37 @@ 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.
+     */
+    @Override
+    public IBinder onBind(Intent arg0) {
+        return null;
+    }
+
+    
+    /** 
+     * 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()}. 
+     */
+    private final class ServiceHandler extends Handler {
+        public ServiceHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            uploadFile();
+            stopSelf(msg.arg1);
+        }
+    }
+
+    
+    
+    
+    /**
      * Core upload method: sends the file(s) to upload
      */
     public void uploadFile() {
      * Core upload method: sends the file(s) to upload
      */
     public void uploadFile() {
@@ -381,7 +408,7 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
      * Callback method to update the progress bar in the status notification.
      */
     @Override
      * Callback method to update the progress bar in the status notification.
      */
     @Override
-    public void transferProgress(long progressRate) {
+    public void onTransferProgress(long progressRate) {
         mSendData += progressRate;
         int percent = (int)(100*((double)mSendData)/((double)mTotalDataToSend));
         if (percent != mPreviousPercent) {
         mSendData += progressRate;
         int percent = (int)(100*((double)mSendData)/((double)mTotalDataToSend));
         if (percent != mPreviousPercent) {
@@ -392,4 +419,11 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
         }
         mPreviousPercent = percent;
     }
         }
         mPreviousPercent = percent;
     }
+
+    @Override
+    public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer, String fileName) {
+        // TODO Maybe replace the other transferProgress with this
+    }
+    
+    
 }
 }
diff --git a/src/com/owncloud/android/operations/DownloadFileOperation.java b/src/com/owncloud/android/operations/DownloadFileOperation.java
new file mode 100644 (file)
index 0000000..fbeedd8
--- /dev/null
@@ -0,0 +1,213 @@
+/* ownCloud Android client application
+ *   Copyright (C) 2012 Bartek Przybylski
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation, either version 3 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package com.owncloud.android.operations;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.apache.commons.httpclient.HttpException;
+import org.apache.commons.httpclient.methods.GetMethod;
+import org.apache.http.HttpStatus;
+
+import com.owncloud.android.files.services.FileDownloader;
+import com.owncloud.android.operations.RemoteOperation;
+import com.owncloud.android.operations.RemoteOperationResult;
+
+import eu.alefzero.webdav.OnDatatransferProgressListener;
+import eu.alefzero.webdav.WebdavClient;
+import eu.alefzero.webdav.WebdavUtils;
+import android.accounts.Account;
+import android.util.Log;
+import android.webkit.MimeTypeMap;
+
+/**
+ * Remote operation performing the download of a file to an ownCloud server
+ * 
+ * @author David A. Velasco
+ */
+public class DownloadFileOperation extends RemoteOperation {
+    
+    private static final String TAG = DownloadFileOperation.class.getCanonicalName();
+
+    private Account mAccount = null;
+    private String mLocalPath = null;
+    private String mRemotePath = null;
+    private String mMimeType = null;
+    private long mSize = -1;
+    private Boolean mCancellationRequested = false;
+    
+    private Set<OnDatatransferProgressListener> mDataTransferListeners = new HashSet<OnDatatransferProgressListener>();
+
+    
+    public Account getAccount() {
+        return mAccount;
+    }
+
+    public String getLocalPath() {
+        return mLocalPath;
+    }
+    
+    public String getRemotePath() {
+        return mRemotePath;
+    }
+
+    public String getMimeType() {
+        return mMimeType;
+    }
+    
+    public long getSize() {
+        return mSize;
+    }
+    
+    
+    public DownloadFileOperation( Account account, 
+                                String localPath, 
+                                String remotePath, 
+                                String mimeType, 
+                                long size,
+                                boolean forceOverwrite) {
+        
+        if (account == null)
+            throw new IllegalArgumentException("Illegal null account in DownloadFileOperation creation");
+        if (localPath == null)
+            throw new IllegalArgumentException("Illegal null local path in DownloadFileOperation creation");
+        if (remotePath == null)
+            throw new IllegalArgumentException("Illegal null remote path in DownloadFileOperation creation");
+        
+        mAccount = account;
+        mLocalPath = localPath;
+        mRemotePath = remotePath;
+        mMimeType = mimeType;
+        if (mMimeType == null) {
+            try {
+                mMimeType = MimeTypeMap.getSingleton()
+                    .getMimeTypeFromExtension(
+                            localPath.substring(localPath.lastIndexOf('.') + 1));
+            } catch (IndexOutOfBoundsException e) {
+                Log.e(TAG, "Trying to find out MIME type of a file without extension: " + localPath);
+            }
+        }
+        if (mMimeType == null) {
+            mMimeType = "application/octet-stream";
+        }
+        mSize = size;
+    }
+    
+    public void addDatatransferProgressListener (OnDatatransferProgressListener listener) {
+        mDataTransferListeners.add(listener);
+    }
+    
+    
+    
+    @Override
+    protected RemoteOperationResult run(WebdavClient client) {
+        RemoteOperationResult result = null;
+        File newFile = null;
+        boolean moved = false;
+        
+        /// download will be in a temporal file
+        File tmpFile = new File(FileDownloader.getTemporalPath(mAccount.name) + mLocalPath);
+        
+        /// perform the download
+        try {
+            tmpFile.getParentFile().mkdirs();
+            int status = downloadFile(client, tmpFile);
+            if (isSuccess(status)) {
+                newFile = new File(FileDownloader.getSavePath(mAccount.name) + mLocalPath);
+                newFile.getParentFile().mkdirs();
+                moved = tmpFile.renameTo(newFile);
+            }
+            if (!moved)
+                result = new RemoteOperationResult(RemoteOperationResult.ResultCode.STORAGE_ERROR_MOVING_FROM_TMP);
+            else
+                result = new RemoteOperationResult(isSuccess(status), status);
+            Log.i(TAG, "Download of " + mLocalPath + " to " + mRemotePath + ": " + result.getLogMessage());
+            
+        } catch (Exception e) {
+            result = new RemoteOperationResult(e);
+            Log.e(TAG, "Download of " + mRemotePath + " to " + mLocalPath + ": " + result.getLogMessage(), e);
+        }
+        
+        return result;
+    }
+
+    
+    public boolean isSuccess(int status) {
+        return (status == HttpStatus.SC_OK);
+    }
+    
+    
+    protected int downloadFile(WebdavClient client, File targetFile) throws HttpException, IOException, OperationCancelledException {
+        int status = -1;
+        boolean savedFile = false;
+        GetMethod get = new GetMethod(client.getBaseUri() + WebdavUtils.encodePath(mRemotePath));
+        Iterator<OnDatatransferProgressListener> it = null;
+        
+        try {
+            status = client.executeMethod(get);
+            if (isSuccess(status)) {
+                targetFile.createNewFile();
+                BufferedInputStream bis = new BufferedInputStream(get.getResponseBodyAsStream());
+                FileOutputStream fos = new FileOutputStream(targetFile);
+                long transferred = 0;
+
+                byte[] bytes = new byte[4096];
+                int readResult = 0;
+                while ((readResult = bis.read(bytes)) != -1) {
+                    synchronized(mCancellationRequested) {
+                        if (mCancellationRequested) {
+                            throw new OperationCancelledException();
+                        }
+                    }
+                    fos.write(bytes, 0, readResult);
+                    transferred += readResult;
+                    it = mDataTransferListeners.iterator();
+                    while (it.hasNext()) {
+                        it.next().onTransferProgress(readResult, transferred, mSize, targetFile.getName());
+                    }
+                }
+                fos.close();
+                savedFile = true;
+                
+            } else {
+                client.exhaustResponse(get.getResponseBodyAsStream());
+            }
+                
+        } finally {
+            if (!savedFile && targetFile.exists()) {
+                targetFile.delete();
+            }
+            get.releaseConnection();    // let the connection available for other methods
+        }
+        return status;
+    }
+
+    
+    public void cancel() {
+        synchronized(mCancellationRequested) {
+            mCancellationRequested = true;
+        }
+    }
+    
+}
diff --git a/src/com/owncloud/android/operations/OperationCancelledException.java b/src/com/owncloud/android/operations/OperationCancelledException.java
new file mode 100644 (file)
index 0000000..938d559
--- /dev/null
@@ -0,0 +1,5 @@
+package com.owncloud.android.operations;
+
+public class OperationCancelledException extends Exception {
+
+}
index b95f734..0618b86 100644 (file)
@@ -57,7 +57,9 @@ public class RemoteOperationResult {
         NO_NETWORK_CONNECTION, 
         SSL_ERROR,
         SSL_RECOVERABLE_PEER_UNVERIFIED,
         NO_NETWORK_CONNECTION, 
         SSL_ERROR,
         SSL_RECOVERABLE_PEER_UNVERIFIED,
-        BAD_OC_VERSION 
+        BAD_OC_VERSION,
+        STORAGE_ERROR_MOVING_FROM_TMP,
+        CANCELLED
     }
 
     private boolean mSuccess = false;
     }
 
     private boolean mSuccess = false;
@@ -94,7 +96,10 @@ public class RemoteOperationResult {
     public RemoteOperationResult(Exception e) {
         mException = e; 
         
     public RemoteOperationResult(Exception e) {
         mException = e; 
         
-        if (e instanceof SocketException) {  
+        if (e instanceof OperationCancelledException) {
+            mCode = ResultCode.CANCELLED;
+            
+        } else if (e instanceof SocketException) {  
             mCode = ResultCode.WRONG_CONNECTION;
         
         } else if (e instanceof SocketTimeoutException) {
             mCode = ResultCode.WRONG_CONNECTION;
         
         } else if (e instanceof SocketTimeoutException) {
@@ -132,6 +137,10 @@ public class RemoteOperationResult {
         return mSuccess;
     }
     
         return mSuccess;
     }
     
+    public boolean isCancelled() {
+        return mCode == ResultCode.CANCELLED;
+    }
+    
     public int getHttpCode() {
         return mHttpCode;
     }
     public int getHttpCode() {
         return mHttpCode;
     }
@@ -169,7 +178,10 @@ public class RemoteOperationResult {
     public String getLogMessage() {
         
         if (mException != null) {
     public String getLogMessage() {
         
         if (mException != null) {
-            if (mException instanceof SocketException) {  
+            if (mException instanceof OperationCancelledException) {
+                return "Operation cancelled by the caller";
+                
+            } else if (mException instanceof SocketException) {  
                 return "Socket exception";
         
             } else if (mException instanceof SocketTimeoutException) {
                 return "Socket exception";
         
             } else if (mException instanceof SocketTimeoutException) {
@@ -209,6 +221,9 @@ public class RemoteOperationResult {
             
         } else if (mCode == ResultCode.BAD_OC_VERSION) {
             return "No valid ownCloud version was found at the server";
             
         } else if (mCode == ResultCode.BAD_OC_VERSION) {
             return "No valid ownCloud version was found at the server";
+            
+        } else if (mCode == ResultCode.STORAGE_ERROR_MOVING_FROM_TMP) {
+            return "Error while moving file from temporal to final directory";
         }
         
         return "Operation finished with HTTP status code " + mHttpCode + " (" + (isSuccess()?"success":"fail") + ")";
         }
         
         return "Operation finished with HTTP status code " + mHttpCode + " (" + (isSuccess()?"success":"fail") + ")";
index 38be5f9..f4e1c11 100644 (file)
@@ -144,7 +144,7 @@ public class UploadFileOperation extends RemoteOperation {
         try {
             File f = new File(mLocalPath);
             FileRequestEntity entity = new FileRequestEntity(f, mMimeType);
         try {
             File f = new File(mLocalPath);
             FileRequestEntity entity = new FileRequestEntity(f, mMimeType);
-            entity.setOnDatatransferProgressListener(mDataTransferListener);
+            entity.addOnDatatransferProgressListener(mDataTransferListener);
             put.setRequestEntity(entity);
             status = client.executeMethod(put);
             client.exhaustResponse(put.getResponseBodyAsStream());
             put.setRequestEntity(entity);
             status = client.executeMethod(put);
             client.exhaustResponse(put.getResponseBodyAsStream());
index 3c80a48..539cf26 100644 (file)
@@ -20,9 +20,13 @@ package com.owncloud.android.ui.activity;
 import android.accounts.Account;\r
 import android.app.Dialog;\r
 import android.app.ProgressDialog;\r
 import android.accounts.Account;\r
 import android.app.Dialog;\r
 import android.app.ProgressDialog;\r
+import android.content.ComponentName;\r
+import android.content.Context;\r
 import android.content.Intent;\r
 import android.content.Intent;\r
+import android.content.ServiceConnection;\r
 import android.content.res.Configuration;\r
 import android.os.Bundle;\r
 import android.content.res.Configuration;\r
 import android.os.Bundle;\r
+import android.os.IBinder;\r
 import android.support.v4.app.FragmentTransaction;\r
 \r
 import com.actionbarsherlock.app.ActionBar;\r
 import android.support.v4.app.FragmentTransaction;\r
 \r
 import com.actionbarsherlock.app.ActionBar;\r
@@ -30,6 +34,7 @@ import com.actionbarsherlock.app.SherlockFragmentActivity;
 import com.actionbarsherlock.view.MenuItem;\r
 import com.owncloud.android.datamodel.OCFile;\r
 import com.owncloud.android.files.services.FileDownloader;\r
 import com.actionbarsherlock.view.MenuItem;\r
 import com.owncloud.android.datamodel.OCFile;\r
 import com.owncloud.android.files.services.FileDownloader;\r
+import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;\r
 import com.owncloud.android.ui.fragment.FileDetailFragment;\r
 \r
 import com.owncloud.android.R;\r
 import com.owncloud.android.ui.fragment.FileDetailFragment;\r
 \r
 import com.owncloud.android.R;\r
@@ -46,6 +51,7 @@ public class FileDetailActivity extends SherlockFragmentActivity implements File
     public static final int DIALOG_SHORT_WAIT = 0;\r
     \r
     private boolean mConfigurationChangedToLandscape = false;\r
     public static final int DIALOG_SHORT_WAIT = 0;\r
     \r
     private boolean mConfigurationChangedToLandscape = false;\r
+    private FileDownloaderBinder mDownloaderBinder = null;\r
 \r
     @Override\r
     protected void onCreate(Bundle savedInstanceState) {\r
 \r
     @Override\r
     protected void onCreate(Bundle savedInstanceState) {\r
@@ -58,11 +64,13 @@ public class FileDetailActivity extends SherlockFragmentActivity implements File
                                            );\r
 \r
         if (!mConfigurationChangedToLandscape) {\r
                                            );\r
 \r
         if (!mConfigurationChangedToLandscape) {\r
+            bindService(new Intent(this, FileDownloader.class), mConnection, Context.BIND_AUTO_CREATE);\r
+            \r
             setContentView(R.layout.file_activity_details);\r
         \r
             ActionBar actionBar = getSupportActionBar();\r
             actionBar.setDisplayHomeAsUpEnabled(true);\r
             setContentView(R.layout.file_activity_details);\r
         \r
             ActionBar actionBar = getSupportActionBar();\r
             actionBar.setDisplayHomeAsUpEnabled(true);\r
-        \r
+\r
             OCFile file = getIntent().getParcelableExtra(FileDetailFragment.EXTRA_FILE);\r
             Account account = getIntent().getParcelableExtra(FileDownloader.EXTRA_ACCOUNT);\r
             FileDetailFragment mFileDetail = new FileDetailFragment(file, account);\r
             OCFile file = getIntent().getParcelableExtra(FileDetailFragment.EXTRA_FILE);\r
             Account account = getIntent().getParcelableExtra(FileDownloader.EXTRA_ACCOUNT);\r
             FileDetailFragment mFileDetail = new FileDetailFragment(file, account);\r
@@ -77,8 +85,34 @@ public class FileDetailActivity extends SherlockFragmentActivity implements File
         \r
         \r
     }\r
         \r
         \r
     }\r
+    \r
+    \r
+    /** Defines callbacks for service binding, passed to bindService() */\r
+    private ServiceConnection mConnection = new ServiceConnection() {\r
+\r
+        @Override\r
+        public void onServiceConnected(ComponentName className, IBinder service) {\r
+            mDownloaderBinder = (FileDownloaderBinder) service;\r
+            FileDetailFragment fragment = (FileDetailFragment) getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);\r
+            if (fragment != null)\r
+                fragment.updateFileDetails();   // a new chance to get the mDownloadBinder through getDownloadBinder()\r
+        }\r
+\r
+        @Override\r
+        public void onServiceDisconnected(ComponentName arg0) {\r
+            mDownloaderBinder = null;\r
+        }\r
+    };    \r
+    \r
 \r
     @Override\r
 \r
     @Override\r
+    public void onDestroy() {\r
+        super.onDestroy();\r
+        unbindService(mConnection);\r
+    }\r
+    \r
+    \r
+    @Override\r
     public boolean onOptionsItemSelected(MenuItem item) {\r
         boolean returnValue = false;\r
         \r
     public boolean onOptionsItemSelected(MenuItem item) {\r
         boolean returnValue = false;\r
         \r
@@ -141,4 +175,13 @@ public class FileDetailActivity extends SherlockFragmentActivity implements File
         // nothing to do here!\r
     }\r
 \r
         // nothing to do here!\r
     }\r
 \r
+    \r
+    /**\r
+     * {@inheritDoc}\r
+     */\r
+    @Override\r
+    public FileDownloaderBinder getFileDownloaderBinder() {\r
+        return mDownloaderBinder;\r
+    }\r
+    \r
 }\r
 }\r
index 64098f7..50b8361 100644 (file)
@@ -26,12 +26,14 @@ import android.app.ProgressDialog;
 import android.app.AlertDialog.Builder;\r
 import android.app.Dialog;\r
 import android.content.BroadcastReceiver;\r
 import android.app.AlertDialog.Builder;\r
 import android.app.Dialog;\r
 import android.content.BroadcastReceiver;\r
+import android.content.ComponentName;\r
 import android.content.ContentResolver;\r
 import android.content.Context;\r
 import android.content.DialogInterface;\r
 import android.content.DialogInterface.OnClickListener;\r
 import android.content.Intent;\r
 import android.content.IntentFilter;\r
 import android.content.ContentResolver;\r
 import android.content.Context;\r
 import android.content.DialogInterface;\r
 import android.content.DialogInterface.OnClickListener;\r
 import android.content.Intent;\r
 import android.content.IntentFilter;\r
+import android.content.ServiceConnection;\r
 import android.content.SharedPreferences;\r
 import android.content.pm.PackageInfo;\r
 import android.content.pm.PackageManager.NameNotFoundException;\r
 import android.content.SharedPreferences;\r
 import android.content.pm.PackageInfo;\r
 import android.content.pm.PackageManager.NameNotFoundException;\r
@@ -40,6 +42,7 @@ import android.database.Cursor;
 import android.net.Uri;\r
 import android.os.Bundle;\r
 import android.os.Handler;\r
 import android.net.Uri;\r
 import android.os.Bundle;\r
 import android.os.Handler;\r
+import android.os.IBinder;\r
 import android.preference.PreferenceManager;\r
 import android.provider.MediaStore;\r
 import android.support.v4.app.FragmentTransaction;\r
 import android.preference.PreferenceManager;\r
 import android.provider.MediaStore;\r
 import android.support.v4.app.FragmentTransaction;\r
@@ -64,6 +67,7 @@ import com.owncloud.android.datamodel.DataStorageManager;
 import com.owncloud.android.datamodel.FileDataStorageManager;\r
 import com.owncloud.android.datamodel.OCFile;\r
 import com.owncloud.android.files.services.FileDownloader;\r
 import com.owncloud.android.datamodel.FileDataStorageManager;\r
 import com.owncloud.android.datamodel.OCFile;\r
 import com.owncloud.android.files.services.FileDownloader;\r
+import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;\r
 import com.owncloud.android.files.services.FileUploader;\r
 import com.owncloud.android.network.OwnCloudClientUtils;\r
 import com.owncloud.android.syncadapter.FileSyncService;\r
 import com.owncloud.android.files.services.FileUploader;\r
 import com.owncloud.android.network.OwnCloudClientUtils;\r
 import com.owncloud.android.syncadapter.FileSyncService;\r
@@ -90,6 +94,7 @@ public class FileDisplayActivity extends SherlockFragmentActivity implements
     private SyncBroadcastReceiver mSyncBroadcastReceiver;\r
     private UploadFinishReceiver mUploadFinishReceiver;\r
     private DownloadFinishReceiver mDownloadFinishReceiver;\r
     private SyncBroadcastReceiver mSyncBroadcastReceiver;\r
     private UploadFinishReceiver mUploadFinishReceiver;\r
     private DownloadFinishReceiver mDownloadFinishReceiver;\r
+    private FileDownloaderBinder mDownloaderBinder = null;\r
     \r
     private OCFileListFragment mFileList;\r
     \r
     \r
     private OCFileListFragment mFileList;\r
     \r
@@ -123,6 +128,7 @@ public class FileDisplayActivity extends SherlockFragmentActivity implements
             \r
         } else {    /// at least an account is available\r
             \r
             \r
         } else {    /// at least an account is available\r
             \r
+            bindService(new Intent(this, FileDownloader.class), mConnection, Context.BIND_AUTO_CREATE);\r
             initDataFromCurrentAccount();\r
             \r
         }\r
             initDataFromCurrentAccount();\r
             \r
         }\r
@@ -205,7 +211,14 @@ public class FileDisplayActivity extends SherlockFragmentActivity implements
             mCurrentDir = mStorageManager.getFileByPath("/");   // this will return NULL if the database has not ever synchronized\r
     }\r
         \r
             mCurrentDir = mStorageManager.getFileByPath("/");   // this will return NULL if the database has not ever synchronized\r
     }\r
         \r
+    \r
+    @Override\r
+    public void onDestroy() {\r
+        super.onDestroy();\r
+        unbindService(mConnection);\r
+    }\r
 \r
 \r
+    \r
     @Override\r
     public boolean onCreateOptionsMenu(Menu menu) {\r
         MenuInflater inflater = getSherlock().getMenuInflater();\r
     @Override\r
     public boolean onCreateOptionsMenu(Menu menu) {\r
         MenuInflater inflater = getSherlock().getMenuInflater();\r
@@ -421,7 +434,7 @@ public class FileDisplayActivity extends SherlockFragmentActivity implements
             registerReceiver(mDownloadFinishReceiver, downloadIntentFilter);\r
         \r
             // List current directory\r
             registerReceiver(mDownloadFinishReceiver, downloadIntentFilter);\r
         \r
             // List current directory\r
-            //mFileList.listDirectory(mCurrentDir);\r
+            mFileList.listDirectory(mCurrentDir);   // we should find the way to avoid the need of this\r
             \r
         } else {\r
             \r
             \r
         } else {\r
             \r
@@ -872,6 +885,40 @@ public class FileDisplayActivity extends SherlockFragmentActivity implements
             fileListFragment.listDirectory();\r
         }\r
     }\r
             fileListFragment.listDirectory();\r
         }\r
     }\r
+\r
+    \r
+    /**\r
+     * {@inheritDoc}\r
+     */\r
+    @Override\r
+    public FileDownloaderBinder getFileDownloaderBinder() {\r
+        return mDownloaderBinder;\r
+    }\r
+    \r
+    \r
+    /** Defines callbacks for service binding, passed to bindService() */\r
+    private ServiceConnection mConnection = new ServiceConnection() {\r
+\r
+        @Override\r
+        public void onServiceConnected(ComponentName className, IBinder service) {\r
+            mDownloaderBinder = (FileDownloaderBinder) service;\r
+            // a new chance to get the mDownloadBinder through getDownloadBinder() - THIS IS A MESS\r
+            mFileList.listDirectory();\r
+            if (mDualPane) {\r
+                FileDetailFragment fragment = (FileDetailFragment) getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);\r
+                if (fragment != null)\r
+                    fragment.updateFileDetails();\r
+            }\r
+            \r
+        }\r
+\r
+        @Override\r
+        public void onServiceDisconnected(ComponentName arg0) {\r
+            mDownloaderBinder = null;\r
+        }\r
+    };    \r
+\r
+    \r
     \r
     /**\r
      * Launch an intent to request the PIN code to the user before letting him use the app\r
     \r
     /**\r
      * Launch an intent to request the PIN code to the user before letting him use the app\r
@@ -887,5 +934,5 @@ public class FileDisplayActivity extends SherlockFragmentActivity implements
         }\r
     }\r
 \r
         }\r
     }\r
 \r
-    \r
+\r
 }\r
 }\r
diff --git a/src/com/owncloud/android/ui/activity/TransferServiceGetter.java b/src/com/owncloud/android/ui/activity/TransferServiceGetter.java
new file mode 100644 (file)
index 0000000..3a9f336
--- /dev/null
@@ -0,0 +1,15 @@
+package com.owncloud.android.ui.activity;
+
+import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
+
+public interface TransferServiceGetter {
+
+    /**
+     * Callback method invoked when the parent activity is fully created to get a reference to the FileDownloader service API.
+     * 
+     * @return  Directory to list firstly. Can be NULL.
+     */
+    public FileDownloaderBinder getFileDownloaderBinder();
+
+
+}
index d998985..6b28f09 100644 (file)
@@ -23,8 +23,9 @@ import com.owncloud.android.AccountUtils;
 import com.owncloud.android.DisplayUtils;\r
 import com.owncloud.android.datamodel.DataStorageManager;\r
 import com.owncloud.android.datamodel.OCFile;\r
 import com.owncloud.android.DisplayUtils;\r
 import com.owncloud.android.datamodel.DataStorageManager;\r
 import com.owncloud.android.datamodel.OCFile;\r
-import com.owncloud.android.files.services.FileDownloader;\r
+import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;\r
 import com.owncloud.android.files.services.FileUploader;\r
 import com.owncloud.android.files.services.FileUploader;\r
+import com.owncloud.android.ui.activity.TransferServiceGetter;\r
 \r
 import com.owncloud.android.R;\r
 \r
 \r
 import com.owncloud.android.R;\r
 \r
@@ -52,12 +53,14 @@ public class FileListListAdapter extends BaseAdapter implements ListAdapter {
     private Vector<OCFile> mFiles = null;\r
     private DataStorageManager mStorageManager;\r
     private Account mAccount;\r
     private Vector<OCFile> mFiles = null;\r
     private DataStorageManager mStorageManager;\r
     private Account mAccount;\r
+    private TransferServiceGetter mTransferServiceGetter;\r
 \r
     public FileListListAdapter(OCFile file, DataStorageManager storage_man,\r
 \r
     public FileListListAdapter(OCFile file, DataStorageManager storage_man,\r
-            Context context) {\r
+            Context context, TransferServiceGetter transferServiceGetter) {\r
         mStorageManager = storage_man;\r
         mContext = context;\r
         mAccount = AccountUtils.getCurrentOwnCloudAccount(mContext);\r
         mStorageManager = storage_man;\r
         mContext = context;\r
         mAccount = AccountUtils.getCurrentOwnCloudAccount(mContext);\r
+        mTransferServiceGetter = transferServiceGetter;\r
         swapDirectory(file);\r
         /*mFile = file;\r
         mFiles = mStorageManager.getDirectoryContent(mFile);*/\r
         swapDirectory(file);\r
         /*mFile = file;\r
         mFiles = mStorageManager.getDirectoryContent(mFile);*/\r
@@ -118,7 +121,9 @@ public class FileListListAdapter extends BaseAdapter implements ListAdapter {
                 fileIcon.setImageResource(R.drawable.ic_menu_archive);\r
             }\r
             ImageView localStateView = (ImageView) view.findViewById(R.id.imageView2);\r
                 fileIcon.setImageResource(R.drawable.ic_menu_archive);\r
             }\r
             ImageView localStateView = (ImageView) view.findViewById(R.id.imageView2);\r
-            if (FileDownloader.isDownloading(mAccount, file.getRemotePath())) {\r
+            //if (FileDownloader.isDownloading(mAccount, file.getRemotePath())) {\r
+            FileDownloaderBinder downloaderBinder = mTransferServiceGetter.getFileDownloaderBinder();\r
+            if (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, file.getRemotePath())) {\r
                 localStateView.setImageResource(R.drawable.downloading_file_indicator);\r
                 localStateView.setVisibility(View.VISIBLE);\r
             } else if (FileUploader.isUploading(mAccount, file.getRemotePath())) {\r
                 localStateView.setImageResource(R.drawable.downloading_file_indicator);\r
                 localStateView.setVisibility(View.VISIBLE);\r
             } else if (FileUploader.isUploading(mAccount, file.getRemotePath())) {\r
index 5b76c01..d0c2c74 100644 (file)
@@ -79,9 +79,12 @@ import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.OCFile;\r
 import com.owncloud.android.files.services.FileDownloader;\r
 import com.owncloud.android.files.services.FileUploader;\r
 import com.owncloud.android.datamodel.OCFile;\r
 import com.owncloud.android.files.services.FileDownloader;\r
 import com.owncloud.android.files.services.FileUploader;\r
+import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;\r
 import com.owncloud.android.network.OwnCloudClientUtils;\r
 import com.owncloud.android.ui.activity.FileDetailActivity;\r
 import com.owncloud.android.ui.activity.FileDisplayActivity;\r
 import com.owncloud.android.network.OwnCloudClientUtils;\r
 import com.owncloud.android.ui.activity.FileDetailActivity;\r
 import com.owncloud.android.ui.activity.FileDisplayActivity;\r
+import com.owncloud.android.ui.activity.TransferServiceGetter;\r
+import com.owncloud.android.ui.fragment.OCFileListFragment.ContainerActivity;\r
 import com.owncloud.android.utils.OwnCloudVersion;\r
 \r
 import com.owncloud.android.R;\r
 import com.owncloud.android.utils.OwnCloudVersion;\r
 \r
 import com.owncloud.android.R;\r
@@ -136,7 +139,7 @@ public class FileDetailFragment extends SherlockFragment implements
      * @param fileToDetail      An {@link OCFile} to show in the fragment\r
      * @param ocAccount         An ownCloud account; needed to start downloads\r
      */\r
      * @param fileToDetail      An {@link OCFile} to show in the fragment\r
      * @param ocAccount         An ownCloud account; needed to start downloads\r
      */\r
-    public FileDetailFragment(OCFile fileToDetail, Account ocAccount){\r
+    public FileDetailFragment(OCFile fileToDetail, Account ocAccount) {\r
         mFile = fileToDetail;\r
         mAccount = ocAccount;\r
         mLayout = R.layout.file_details_empty;\r
         mFile = fileToDetail;\r
         mAccount = ocAccount;\r
         mLayout = R.layout.file_details_empty;\r
@@ -147,20 +150,6 @@ public class FileDetailFragment extends SherlockFragment implements
     }\r
     \r
 \r
     }\r
     \r
 \r
-    /**\r
-     * {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void onAttach(Activity activity) {\r
-        super.onAttach(activity);\r
-        try {\r
-            mContainerActivity = (ContainerActivity) activity;\r
-        } catch (ClassCastException e) {\r
-            throw new ClassCastException(activity.toString() + " must implement FileListFragment.ContainerActivity");\r
-        }\r
-    }\r
-    \r
-    \r
     @Override\r
     public View onCreateView(LayoutInflater inflater, ViewGroup container,\r
             Bundle savedInstanceState) {\r
     @Override\r
     public View onCreateView(LayoutInflater inflater, ViewGroup container,\r
             Bundle savedInstanceState) {\r
@@ -185,11 +174,24 @@ public class FileDetailFragment extends SherlockFragment implements
             mPreview = (ImageView)mView.findViewById(R.id.fdPreview);\r
         }\r
         \r
             mPreview = (ImageView)mView.findViewById(R.id.fdPreview);\r
         }\r
         \r
-        updateFileDetails();\r
         return view;\r
     }\r
     \r
 \r
         return view;\r
     }\r
     \r
 \r
+    /**\r
+     * {@inheritDoc}\r
+     */\r
+    @Override\r
+    public void onAttach(Activity activity) {\r
+        super.onAttach(activity);\r
+        try {\r
+            mContainerActivity = (ContainerActivity) activity;\r
+        } catch (ClassCastException e) {\r
+            throw new ClassCastException(activity.toString() + " must implement " + FileDetailFragment.ContainerActivity.class.getCanonicalName());\r
+        }\r
+    }\r
+        \r
+\r
     @Override\r
     public void onSaveInstanceState(Bundle outState) {\r
         Log.i(getClass().toString(), "onSaveInstanceState() start");\r
     @Override\r
     public void onSaveInstanceState(Bundle outState) {\r
         Log.i(getClass().toString(), "onSaveInstanceState() start");\r
@@ -242,10 +244,10 @@ public class FileDetailFragment extends SherlockFragment implements
     public void onClick(View v) {\r
         switch (v.getId()) {\r
             case R.id.fdDownloadBtn: {\r
     public void onClick(View v) {\r
         switch (v.getId()) {\r
             case R.id.fdDownloadBtn: {\r
-                if (FileDownloader.isDownloading(mAccount, mFile.getRemotePath())) {\r
-                    \r
-                    // TODO cancelar descarga\r
-                    \r
+                //if (FileDownloader.isDownloading(mAccount, mFile.getRemotePath())) {\r
+                FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder();\r
+                if (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, mFile.getRemotePath())) {\r
+                    downloaderBinder.cancel(mAccount, mFile.getRemotePath());\r
                     if (mFile.isDown()) {\r
                         setButtonsForDown();\r
                     } else {\r
                     if (mFile.isDown()) {\r
                         setButtonsForDown();\r
                     } else {\r
@@ -440,7 +442,9 @@ public class FileDetailFragment extends SherlockFragment implements
             cb.setChecked(mFile.keepInSync());\r
 \r
             // configure UI for depending upon local state of the file\r
             cb.setChecked(mFile.keepInSync());\r
 \r
             // configure UI for depending upon local state of the file\r
-            if (FileDownloader.isDownloading(mAccount, mFile.getRemotePath()) || FileUploader.isUploading(mAccount, mFile.getRemotePath())) {\r
+            //if (FileDownloader.isDownloading(mAccount, mFile.getRemotePath()) || FileUploader.isUploading(mAccount, mFile.getRemotePath())) {\r
+            FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder();\r
+            if ((downloaderBinder != null && downloaderBinder.isDownloading(mAccount, mFile.getRemotePath())) || FileUploader.isUploading(mAccount, mFile.getRemotePath())) {\r
                 setButtonsForTransferring();\r
                 \r
             } else if (mFile.isDown()) {\r
                 setButtonsForTransferring();\r
                 \r
             } else if (mFile.isDown()) {\r
@@ -585,7 +589,7 @@ public class FileDetailFragment extends SherlockFragment implements
      * \r
      * @author David A. Velasco\r
      */\r
      * \r
      * @author David A. Velasco\r
      */\r
-    public interface ContainerActivity {\r
+    public interface ContainerActivity extends TransferServiceGetter {\r
 \r
         /**\r
          * Callback method invoked when the detail fragment wants to notice its container \r
 \r
         /**\r
          * Callback method invoked when the detail fragment wants to notice its container \r
index 77cd78a..740f66e 100644 (file)
@@ -19,7 +19,9 @@ package com.owncloud.android.ui.fragment;
 
 import com.owncloud.android.datamodel.DataStorageManager;
 import com.owncloud.android.datamodel.OCFile;
 
 import com.owncloud.android.datamodel.DataStorageManager;
 import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
 import com.owncloud.android.ui.FragmentListView;
 import com.owncloud.android.ui.FragmentListView;
+import com.owncloud.android.ui.activity.TransferServiceGetter;
 import com.owncloud.android.ui.adapter.FileListListAdapter;
 
 import android.app.Activity;
 import com.owncloud.android.ui.adapter.FileListListAdapter;
 
 import android.app.Activity;
@@ -76,12 +78,15 @@ public class OCFileListFragment extends FragmentListView {
     }    
 
 
     }    
 
 
+    /**
+     * {@inheritDoc}
+     */
     @Override
     public void onActivityCreated(Bundle savedInstanceState) {
         Log.i(TAG, "onActivityCreated() start");
         
     @Override
     public void onActivityCreated(Bundle savedInstanceState) {
         Log.i(TAG, "onActivityCreated() start");
         
-        super.onCreate(savedInstanceState);
-        mAdapter = new FileListListAdapter(mContainerActivity.getInitialDirectory(), mContainerActivity.getStorageManager(), getActivity());
+        super.onActivityCreated(savedInstanceState);
+        mAdapter = new FileListListAdapter(mContainerActivity.getInitialDirectory(), mContainerActivity.getStorageManager(), getActivity(), mContainerActivity);
         setListAdapter(mAdapter);
         
         if (savedInstanceState != null) {
         setListAdapter(mAdapter);
         
         if (savedInstanceState != null) {
@@ -154,7 +159,9 @@ public class OCFileListFragment extends FragmentListView {
      * Calls {@link OCFileListFragment#listDirectory(OCFile)} with a null parameter
      */
     public void listDirectory(){
      * Calls {@link OCFileListFragment#listDirectory(OCFile)} with a null parameter
      */
     public void listDirectory(){
+        int position = mList.getFirstVisiblePosition();
         listDirectory(null);
         listDirectory(null);
+        mList.setSelectionFromTop(position, 0);
     }
     
     /**
     }
     
     /**
@@ -187,6 +194,7 @@ public class OCFileListFragment extends FragmentListView {
         mFile = directory;
         mAdapter.swapDirectory(mFile);
         mList.setSelectionFromTop(0, 0);
         mFile = directory;
         mAdapter.swapDirectory(mFile);
         mList.setSelectionFromTop(0, 0);
+        mList.invalidate();
     }
     
     
     }
     
     
@@ -196,7 +204,7 @@ public class OCFileListFragment extends FragmentListView {
      * 
      * @author David A. Velasco
      */
      * 
      * @author David A. Velasco
      */
-    public interface ContainerActivity {
+    public interface ContainerActivity extends TransferServiceGetter {
 
         /**
          * Callback method invoked when a directory is clicked by the user on the files list
 
         /**
          * Callback method invoked when a directory is clicked by the user on the files list
index c3c9a69..da390a6 100644 (file)
@@ -95,7 +95,7 @@ public class ChunkFromFileChannelRequestEntity implements RequestEntity {
                 out.write(mBuffer.array(), 0, readCount);
                 mBuffer.clear();
                 if (mListener != null) 
                 out.write(mBuffer.array(), 0, readCount);
                 mBuffer.clear();
                 if (mListener != null) 
-                    mListener.transferProgress(readCount);
+                    mListener.onTransferProgress(readCount);
             }
             
         } catch (IOException io) {
             }
             
         } catch (IOException io) {
index df6bdff..0f774f7 100644 (file)
@@ -7,6 +7,10 @@ import java.io.RandomAccessFile;
 import java.nio.ByteBuffer;
 import java.nio.channels.FileChannel;
 import java.nio.channels.FileLock;
 import java.nio.ByteBuffer;
 import java.nio.channels.FileChannel;
 import java.nio.channels.FileLock;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
 
 import org.apache.commons.httpclient.methods.RequestEntity;
 
 
 import org.apache.commons.httpclient.methods.RequestEntity;
 
@@ -23,7 +27,7 @@ public class FileRequestEntity implements RequestEntity {
 
     final File mFile;
     final String mContentType;
 
     final File mFile;
     final String mContentType;
-    OnDatatransferProgressListener mListener;
+    Set<OnDatatransferProgressListener> mListeners = new HashSet<OnDatatransferProgressListener>();
 
     public FileRequestEntity(final File file, final String contentType) {
         super();
 
     public FileRequestEntity(final File file, final String contentType) {
         super();
@@ -49,10 +53,19 @@ public class FileRequestEntity implements RequestEntity {
         return true;
     }
     
         return true;
     }
     
-    public void setOnDatatransferProgressListener(OnDatatransferProgressListener listener) {
-        mListener = listener;
+    public void addOnDatatransferProgressListener(OnDatatransferProgressListener listener) {
+        mListeners.add(listener);
     }
     
     }
     
+    public void addOnDatatransferProgressListeners(Collection<OnDatatransferProgressListener> listeners) {
+        mListeners.addAll(listeners);
+    }
+    
+    public void removeOnDatatransferProgressListener(OnDatatransferProgressListener listener) {
+        mListeners.remove(listener);
+    }
+    
+    
     @Override
     public void writeRequest(final OutputStream out) throws IOException {
         //byte[] tmp = new byte[4096];
     @Override
     public void writeRequest(final OutputStream out) throws IOException {
         //byte[] tmp = new byte[4096];
@@ -64,22 +77,22 @@ public class FileRequestEntity implements RequestEntity {
         RandomAccessFile raf = new RandomAccessFile(mFile, "rw");
         FileChannel channel = raf.getChannel();
         FileLock lock = channel.tryLock();
         RandomAccessFile raf = new RandomAccessFile(mFile, "rw");
         FileChannel channel = raf.getChannel();
         FileLock lock = channel.tryLock();
-        //InputStream instream = new FileInputStream(this.file);
-        
+        Iterator<OnDatatransferProgressListener> it = null;
         try {
         try {
-            //while ((i = instream.read(tmp)) >= 0) {
             while ((i = channel.read(tmp)) >= 0) {
                 out.write(tmp.array(), 0, i);
                 tmp.clear();
             while ((i = channel.read(tmp)) >= 0) {
                 out.write(tmp.array(), 0, i);
                 tmp.clear();
-                if (mListener != null) 
-                    mListener.transferProgress(i);
+                it = mListeners.iterator();
+                while (it.hasNext()) {
+                    it.next().onTransferProgress(i);
+                }
             }
             }
+            
         } catch (IOException io) {
             Log.e("FileRequestException", io.getMessage());
             throw new RuntimeException("Ugly solution to workaround the default policy of retries when the server falls while uploading ; temporal fix; really", io);   
             
         } finally {
         } catch (IOException io) {
             Log.e("FileRequestException", io.getMessage());
             throw new RuntimeException("Ugly solution to workaround the default policy of retries when the server falls while uploading ; temporal fix; really", io);   
             
         } finally {
-            //instream.close();
             lock.release();
             channel.close();
             raf.close();
             lock.release();
             channel.close();
             raf.close();
index 7d6dda1..d65281e 100644 (file)
@@ -1,5 +1,6 @@
 package eu.alefzero.webdav;
 
 public interface OnDatatransferProgressListener {
 package eu.alefzero.webdav;
 
 public interface OnDatatransferProgressListener {
-    public void transferProgress(long progressRate);
+    public void onTransferProgress(long progressRate);
+    public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer, String fileName);
 }
 }
index f3aca3e..a451b3e 100644 (file)
@@ -98,7 +98,7 @@ public class WebdavClient extends HttpClient {
                 int readResult;\r
                 while ((readResult = bis.read(bytes)) != -1) {\r
                     if (mDataTransferListener != null)\r
                 int readResult;\r
                 while ((readResult = bis.read(bytes)) != -1) {\r
                     if (mDataTransferListener != null)\r
-                        mDataTransferListener.transferProgress(readResult);\r
+                        mDataTransferListener.onTransferProgress(readResult);\r
                     fos.write(bytes, 0, readResult);\r
                 }\r
                 fos.close();\r
                     fos.write(bytes, 0, readResult);\r
                 }\r
                 fos.close();\r
@@ -165,7 +165,7 @@ public class WebdavClient extends HttpClient {
         try {\r
             File f = new File(localFile);\r
             FileRequestEntity entity = new FileRequestEntity(f, contentType);\r
         try {\r
             File f = new File(localFile);\r
             FileRequestEntity entity = new FileRequestEntity(f, contentType);\r
-            entity.setOnDatatransferProgressListener(mDataTransferListener);\r
+            entity.addOnDatatransferProgressListener(mDataTransferListener);\r
             put.setRequestEntity(entity);\r
             status = executeMethod(put);\r
             \r
             put.setRequestEntity(entity);\r
             status = executeMethod(put);\r
             \r