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
+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
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
\r
private static final String TAG = "FileDownloader";\r
\r
- private NotificationManager mNotificationMngr;\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 int mLastPercent;\r
private long mTotalDownloadSize;\r
private long mCurrentDownloadSize;\r
+ */\r
+ \r
+ private NotificationManager mNotificationMngr;\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
- 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
- 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
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
// 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
thread.start();\r
mServiceLooper = thread.getLooper();\r
mServiceHandler = new ServiceHandler(mServiceLooper);\r
+ mBinder = new FileDownloaderBinder();\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
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
- 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
+ \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
- private void downloadFile() {\r
- boolean downloadResult = false;\r
+ @Override\r
+ public IBinder onBind(Intent arg0) {\r
+ return mBinder;\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
- /// 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
- /// 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
+ * 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
- /// 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
- 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
- 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
- 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
- } finally {\r
- mDownloadsInProgress.remove(buildRemoteName(mAccount.name, mRemotePath));\r
- }\r
-\r
+ \r
+ /*} finally {\r
+ mDownloadsInProgress.remove(buildRemoteName(mLastAccount.name, mCurrentDownload.getRemotePath()));\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
- 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
* 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
- 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
- \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
+ * 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
- * @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
- 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
- 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
private static final String TAG = FileUploader.class.getSimpleName();
- private NotificationManager mNotificationManager;
private Looper mServiceLooper;
private ServiceHandler mServiceHandler;
+
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 RemoteViews mDefaultNotificationContentView;
private long mTotalDataToSend, mSendData;
- private int mTotalFilesToSend;
- private int mCurrentIndexUpload, mPreviousPercent;
+ private int mTotalFilesToSend, mPreviousPercent;
private int mSuccessCounter;
- private RemoteViews mDefaultNotificationContentView;
+
/**
* Static map with the files being download and the path to the temporal file were are download
- @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();
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)) {
/**
+ * 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() {
* 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) {
}
mPreviousPercent = percent;
}
+
+ @Override
+ public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer, String fileName) {
+ // TODO Maybe replace the other transferProgress with this
+ }
+
+
}
--- /dev/null
+/* 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;
+ }
+ }
+
+}
--- /dev/null
+package com.owncloud.android.operations;
+
+public class OperationCancelledException extends Exception {
+
+}
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;
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) {
return mSuccess;
}
+ public boolean isCancelled() {
+ return mCode == ResultCode.CANCELLED;
+ }
+
public int getHttpCode() {
return mHttpCode;
}
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) {
} 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") + ")";
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());
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.ServiceConnection;\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 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
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
\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
- \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
\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
+ 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
// nothing to do here!\r
}\r
\r
+ \r
+ /**\r
+ * {@inheritDoc}\r
+ */\r
+ @Override\r
+ public FileDownloaderBinder getFileDownloaderBinder() {\r
+ return mDownloaderBinder;\r
+ }\r
+ \r
}\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.ServiceConnection;\r
import android.content.SharedPreferences;\r
import android.content.pm.PackageInfo;\r
import android.content.pm.PackageManager.NameNotFoundException;\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 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
private SyncBroadcastReceiver mSyncBroadcastReceiver;\r
private UploadFinishReceiver mUploadFinishReceiver;\r
private DownloadFinishReceiver mDownloadFinishReceiver;\r
+ private FileDownloaderBinder mDownloaderBinder = null;\r
\r
private OCFileListFragment mFileList;\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
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
@Override\r
public boolean onCreateOptionsMenu(Menu menu) {\r
MenuInflater inflater = getSherlock().getMenuInflater();\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
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
\r
- \r
+\r
}\r
--- /dev/null
+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();
+
+
+}
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.ui.activity.TransferServiceGetter;\r
\r
import com.owncloud.android.R;\r
\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
- Context context) {\r
+ Context context, TransferServiceGetter transferServiceGetter) {\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
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
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.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
* @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
}\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
mPreview = (ImageView)mView.findViewById(R.id.fdPreview);\r
}\r
\r
- updateFileDetails();\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
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
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
* \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
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.activity.TransferServiceGetter;
import com.owncloud.android.ui.adapter.FileListListAdapter;
import android.app.Activity;
}
+ /**
+ * {@inheritDoc}
+ */
@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) {
* Calls {@link OCFileListFragment#listDirectory(OCFile)} with a null parameter
*/
public void listDirectory(){
+ int position = mList.getFirstVisiblePosition();
listDirectory(null);
+ mList.setSelectionFromTop(position, 0);
}
/**
mFile = directory;
mAdapter.swapDirectory(mFile);
mList.setSelectionFromTop(0, 0);
+ mList.invalidate();
}
*
* @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
out.write(mBuffer.array(), 0, readCount);
mBuffer.clear();
if (mListener != null)
- mListener.transferProgress(readCount);
+ mListener.onTransferProgress(readCount);
}
} catch (IOException io) {
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;
final File mFile;
final String mContentType;
- OnDatatransferProgressListener mListener;
+ Set<OnDatatransferProgressListener> mListeners = new HashSet<OnDatatransferProgressListener>();
public FileRequestEntity(final File file, final String contentType) {
super();
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];
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 {
- //while ((i = instream.read(tmp)) >= 0) {
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 {
- //instream.close();
lock.release();
channel.close();
raf.close();
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);
}
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
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