mirror of
https://github.com/nextcloud/android.git
synced 2024-11-27 17:46:37 +03:00
Download service refactoring: multiple downloads and cancellation support
This commit is contained in:
parent
7edbe05bb5
commit
68ce2e7a38
16 changed files with 712 additions and 173 deletions
|
@ -1,14 +1,18 @@
|
|||
package com.owncloud.android.files.services;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.AbstractList;
|
||||
import java.util.Iterator;
|
||||
import java.util.Vector;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import com.owncloud.android.db.ProviderMeta.ProviderTableMeta;
|
||||
import eu.alefzero.webdav.OnDatatransferProgressListener;
|
||||
|
||||
import com.owncloud.android.network.OwnCloudClientUtils;
|
||||
import com.owncloud.android.operations.DownloadFileOperation;
|
||||
import com.owncloud.android.operations.RemoteOperationResult;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.app.Notification;
|
||||
|
@ -18,6 +22,7 @@ import android.app.Service;
|
|||
import android.content.ContentValues;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Binder;
|
||||
import android.os.Environment;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
|
@ -41,28 +46,40 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
|
|||
|
||||
private static final String TAG = "FileDownloader";
|
||||
|
||||
private NotificationManager mNotificationMngr;
|
||||
private Looper mServiceLooper;
|
||||
private ServiceHandler mServiceHandler;
|
||||
private IBinder mBinder;
|
||||
private WebdavClient mDownloadClient = null;
|
||||
private Account mLastAccount = null;
|
||||
|
||||
//private AbstractList<Account> mAccounts = new Vector<Account>();
|
||||
private ConcurrentMap<String, DownloadFileOperation> mPendingDownloads = new ConcurrentHashMap<String, DownloadFileOperation>();
|
||||
private DownloadFileOperation mCurrentDownload = null;
|
||||
|
||||
/*
|
||||
private Account mAccount;
|
||||
private String mFilePath;
|
||||
private String mRemotePath;
|
||||
private int mLastPercent;
|
||||
private long mTotalDownloadSize;
|
||||
private long mCurrentDownloadSize;
|
||||
*/
|
||||
|
||||
private NotificationManager mNotificationMngr;
|
||||
private Notification mNotification;
|
||||
private int mLastPercent;
|
||||
|
||||
|
||||
/**
|
||||
* Static map with the files being download and the path to the temporal file were are download
|
||||
*/
|
||||
private static Map<String, String> mDownloadsInProgress = Collections.synchronizedMap(new HashMap<String, String>());
|
||||
//private static Set<String> mDownloadsInProgress = Collections.synchronizedSet(new HashSet<String>());
|
||||
|
||||
/**
|
||||
* Returns True when the file referred by 'remotePath' in the ownCloud account 'account' is downloading
|
||||
*/
|
||||
public static boolean isDownloading(Account account, String remotePath) {
|
||||
return (mDownloadsInProgress.get(buildRemoteName(account.name, remotePath)) != null);
|
||||
}
|
||||
/*public static boolean isDownloading(Account account, String remotePath) {
|
||||
return (mDownloadsInProgress.contains(buildRemoteName(account.name, remotePath)));
|
||||
}*/
|
||||
|
||||
/**
|
||||
* Builds a key for mDownloadsInProgress from the accountName and remotePath
|
||||
|
@ -71,19 +88,6 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
|
|||
return accountName + remotePath;
|
||||
}
|
||||
|
||||
|
||||
private final class ServiceHandler extends Handler {
|
||||
public ServiceHandler(Looper looper) {
|
||||
super(looper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
downloadFile();
|
||||
stopSelf(msg.arg1);
|
||||
}
|
||||
}
|
||||
|
||||
public static final String getSavePath(String accountName) {
|
||||
File sdCard = Environment.getExternalStorageDirectory();
|
||||
return sdCard.getAbsolutePath() + "/owncloud/" + Uri.encode(accountName, "@");
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Service initialization
|
||||
*/
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
@ -105,13 +113,16 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
|
|||
thread.start();
|
||||
mServiceLooper = thread.getLooper();
|
||||
mServiceHandler = new ServiceHandler(mServiceLooper);
|
||||
mBinder = new FileDownloaderBinder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent arg0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Entry point to add one or several files to the queue of downloads.
|
||||
*
|
||||
* New downloads are added calling to startService(), resulting in a call to this method. This ensures the service will keep on working
|
||||
* although the caller activity goes away.
|
||||
*/
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
if ( !intent.hasExtra(EXTRA_ACCOUNT) ||
|
||||
|
@ -121,116 +132,241 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
|
|||
Log.e(TAG, "Not enough information provided in intent");
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
mAccount = intent.getParcelableExtra(EXTRA_ACCOUNT);
|
||||
mFilePath = intent.getStringExtra(EXTRA_FILE_PATH);
|
||||
mRemotePath = intent.getStringExtra(EXTRA_REMOTE_PATH);
|
||||
mTotalDownloadSize = intent.getLongExtra(EXTRA_FILE_SIZE, -1);
|
||||
mCurrentDownloadSize = mLastPercent = 0;
|
||||
Account account = intent.getParcelableExtra(EXTRA_ACCOUNT);
|
||||
String filePath = intent.getStringExtra(EXTRA_FILE_PATH);
|
||||
String remotePath = intent.getStringExtra(EXTRA_REMOTE_PATH);
|
||||
long totalDownloadSize = intent.getLongExtra(EXTRA_FILE_SIZE, -1);
|
||||
|
||||
Message msg = mServiceHandler.obtainMessage();
|
||||
msg.arg1 = startId;
|
||||
mServiceHandler.sendMessage(msg);
|
||||
AbstractList<String> requestedDownloads = new Vector<String>(); // dvelasco: now this will always contain just one element, but that can change in a near future
|
||||
String downloadKey = buildRemoteName(account.name, remotePath);
|
||||
try {
|
||||
DownloadFileOperation newDownload = new DownloadFileOperation(account, filePath, remotePath, (String)null, totalDownloadSize, false);
|
||||
mPendingDownloads.putIfAbsent(downloadKey, newDownload);
|
||||
newDownload.addDatatransferProgressListener(this);
|
||||
requestedDownloads.add(downloadKey);
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
Log.e(TAG, "Not enough information provided in intent: " + e.getMessage());
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
if (requestedDownloads.size() > 0) {
|
||||
Message msg = mServiceHandler.obtainMessage();
|
||||
msg.arg1 = startId;
|
||||
msg.obj = requestedDownloads;
|
||||
mServiceHandler.sendMessage(msg);
|
||||
}
|
||||
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Core download method: requests the file to download and stores it.
|
||||
* Provides a binder object that clients can use to perform operations on the queue of downloads, excepting the addition of new files.
|
||||
*
|
||||
* Implemented to perform cancellation, pause and resume of existing downloads.
|
||||
*/
|
||||
private void downloadFile() {
|
||||
boolean downloadResult = false;
|
||||
|
||||
/// prepare client object to send the request to the ownCloud server
|
||||
WebdavClient wdc = OwnCloudClientUtils.createOwnCloudClient(mAccount, getApplicationContext());
|
||||
wdc.setDataTransferProgressListener(this);
|
||||
|
||||
/// download will be in a temporal file
|
||||
File tmpFile = new File(getTemporalPath(mAccount.name) + mFilePath);
|
||||
|
||||
/// create status notification to show the download progress
|
||||
mNotification = new Notification(R.drawable.icon, getString(R.string.downloader_download_in_progress_ticker), System.currentTimeMillis());
|
||||
mNotification.flags |= Notification.FLAG_ONGOING_EVENT;
|
||||
mNotification.contentView = new RemoteViews(getApplicationContext().getPackageName(), R.layout.progressbar_layout);
|
||||
mNotification.contentView.setProgressBar(R.id.status_progress, 100, 0, mTotalDownloadSize == -1);
|
||||
mNotification.contentView.setTextViewText(R.id.status_text, String.format(getString(R.string.downloader_download_in_progress_content), 0, tmpFile.getName()));
|
||||
mNotification.contentView.setImageViewResource(R.id.status_icon, R.drawable.icon);
|
||||
// TODO put something smart in the contentIntent below
|
||||
mNotification.contentIntent = PendingIntent.getActivity(getApplicationContext(), 0, new Intent(), PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
mNotificationMngr.notify(R.string.downloader_download_in_progress_ticker, mNotification);
|
||||
@Override
|
||||
public IBinder onBind(Intent arg0) {
|
||||
return mBinder;
|
||||
}
|
||||
|
||||
|
||||
/// perform the download
|
||||
tmpFile.getParentFile().mkdirs();
|
||||
mDownloadsInProgress.put(buildRemoteName(mAccount.name, mRemotePath), tmpFile.getAbsolutePath());
|
||||
File newFile = null;
|
||||
try {
|
||||
if (wdc.downloadFile(mRemotePath, tmpFile)) {
|
||||
newFile = new File(getSavePath(mAccount.name) + mFilePath);
|
||||
newFile.getParentFile().mkdirs();
|
||||
boolean moved = tmpFile.renameTo(newFile);
|
||||
/**
|
||||
* Binder to let client components to perform operations on the queue of downloads.
|
||||
*
|
||||
* It provides by itself the available operations.
|
||||
*/
|
||||
public class FileDownloaderBinder extends Binder {
|
||||
|
||||
if (moved) {
|
||||
/**
|
||||
* Cancels a pending or current download of a remote file.
|
||||
*
|
||||
* @param account Owncloud account where the remote file is stored.
|
||||
* @param remotePath URL to the remote file in the queue of downloads.
|
||||
*/
|
||||
public void cancel(Account account, String remotePath) {
|
||||
synchronized (mPendingDownloads) {
|
||||
DownloadFileOperation download = mPendingDownloads.remove(buildRemoteName(account.name, remotePath));
|
||||
if (download != null) {
|
||||
download.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns True when the file referred by 'remotePath' in the ownCloud account 'account' is downloading
|
||||
*
|
||||
* @param account Owncloud account where the remote file is stored.
|
||||
* @param remotePath URL to the remote file in the queue of downloads.
|
||||
*/
|
||||
public boolean isDownloading(Account account, String remotePath) {
|
||||
synchronized (mPendingDownloads) {
|
||||
return (mPendingDownloads.containsKey(buildRemoteName(account.name, remotePath)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Download worker. Performs the pending downloads in the order they were requested.
|
||||
*
|
||||
* Created with the Looper of a new thread, started in {@link FileUploader#onCreate()}.
|
||||
*/
|
||||
private final class ServiceHandler extends Handler {
|
||||
public ServiceHandler(Looper looper) {
|
||||
super(looper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
@SuppressWarnings("unchecked")
|
||||
AbstractList<String> requestedDownloads = (AbstractList<String>) msg.obj;
|
||||
if (msg.obj != null) {
|
||||
Iterator<String> it = requestedDownloads.iterator();
|
||||
while (it.hasNext()) {
|
||||
downloadFile(it.next());
|
||||
}
|
||||
}
|
||||
stopSelf(msg.arg1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Core download method: requests a file to download and stores it.
|
||||
*
|
||||
* @param downloadKey Key to access the download to perform, contained in mPendingDownloads
|
||||
*/
|
||||
private void downloadFile(String downloadKey) {
|
||||
|
||||
synchronized(mPendingDownloads) {
|
||||
mCurrentDownload = mPendingDownloads.get(downloadKey);
|
||||
}
|
||||
|
||||
if (mCurrentDownload != null) {
|
||||
|
||||
notifyDownloadStart(mCurrentDownload);
|
||||
|
||||
/// prepare client object to send the request to the ownCloud server
|
||||
if (mDownloadClient == null || mLastAccount != mCurrentDownload.getAccount()) {
|
||||
mLastAccount = mCurrentDownload.getAccount();
|
||||
mDownloadClient = OwnCloudClientUtils.createOwnCloudClient(mLastAccount, getApplicationContext());
|
||||
}
|
||||
|
||||
/// perform the download
|
||||
//mDownloadsInProgress.add(buildRemoteName(mLastAccount.name, mCurrentDownload.getRemotePath()));
|
||||
RemoteOperationResult downloadResult = null;
|
||||
File newLocalFile = null;
|
||||
//try {
|
||||
downloadResult = mCurrentDownload.execute(mDownloadClient);
|
||||
if (downloadResult.isSuccess()) {
|
||||
ContentValues cv = new ContentValues();
|
||||
cv.put(ProviderTableMeta.FILE_STORAGE_PATH, newFile.getAbsolutePath());
|
||||
newLocalFile = new File(getSavePath(mCurrentDownload.getAccount().name) + mCurrentDownload.getLocalPath());
|
||||
cv.put(ProviderTableMeta.FILE_STORAGE_PATH, newLocalFile.getAbsolutePath());
|
||||
getContentResolver().update(
|
||||
ProviderTableMeta.CONTENT_URI,
|
||||
cv,
|
||||
ProviderTableMeta.FILE_NAME + "=? AND "
|
||||
+ ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?",
|
||||
new String[] {
|
||||
mFilePath.substring(mFilePath.lastIndexOf('/') + 1),
|
||||
mAccount.name });
|
||||
downloadResult = true;
|
||||
new String[] {
|
||||
mCurrentDownload.getLocalPath().substring(mCurrentDownload.getLocalPath().lastIndexOf('/') + 1),
|
||||
mLastAccount.name });
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
mDownloadsInProgress.remove(buildRemoteName(mAccount.name, mRemotePath));
|
||||
|
||||
/*} finally {
|
||||
mDownloadsInProgress.remove(buildRemoteName(mLastAccount.name, mCurrentDownload.getRemotePath()));
|
||||
}*/
|
||||
|
||||
mPendingDownloads.remove(downloadKey);
|
||||
|
||||
/// notify result
|
||||
notifyDownloadResult(mCurrentDownload, downloadResult);
|
||||
|
||||
sendFinalBroadcast(mCurrentDownload, downloadResult, (downloadResult.isSuccess())? newLocalFile.getAbsolutePath():null);
|
||||
}
|
||||
|
||||
|
||||
/// notify result
|
||||
mNotificationMngr.cancel(R.string.downloader_download_in_progress_ticker);
|
||||
int tickerId = (downloadResult) ? R.string.downloader_download_succeeded_ticker : R.string.downloader_download_failed_ticker;
|
||||
int contentId = (downloadResult) ? R.string.downloader_download_succeeded_content : R.string.downloader_download_failed_content;
|
||||
Notification finalNotification = new Notification(R.drawable.icon, getString(tickerId), System.currentTimeMillis());
|
||||
finalNotification.flags |= Notification.FLAG_AUTO_CANCEL;
|
||||
// TODO put something smart in the contentIntent below
|
||||
finalNotification.contentIntent = PendingIntent.getActivity(getApplicationContext(), 0, new Intent(), PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
finalNotification.setLatestEventInfo(getApplicationContext(), getString(tickerId), String.format(getString(contentId), tmpFile.getName()), finalNotification.contentIntent);
|
||||
mNotificationMngr.notify(tickerId, finalNotification);
|
||||
|
||||
sendFinalBroadcast(downloadResult, (downloadResult)?newFile.getAbsolutePath():null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Callback method to update the progress bar in the status notification.
|
||||
*/
|
||||
@Override
|
||||
public void transferProgress(long progressRate) {
|
||||
mCurrentDownloadSize += progressRate;
|
||||
int percent = (int)(100.0*((double)mCurrentDownloadSize)/((double)mTotalDownloadSize));
|
||||
public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer, String fileName) {
|
||||
int percent = (int)(100.0*((double)totalTransferredSoFar)/((double)totalToTransfer));
|
||||
if (percent != mLastPercent) {
|
||||
mNotification.contentView.setProgressBar(R.id.status_progress, 100, (int)(100*mCurrentDownloadSize/mTotalDownloadSize), mTotalDownloadSize == -1);
|
||||
mNotification.contentView.setTextViewText(R.id.status_text, String.format(getString(R.string.downloader_download_in_progress_content), percent, new File(mFilePath).getName()));
|
||||
mNotification.contentView.setProgressBar(R.id.status_progress, 100, percent, totalToTransfer == -1);
|
||||
mNotification.contentView.setTextViewText(R.id.status_text, String.format(getString(R.string.downloader_download_in_progress_content), percent, fileName));
|
||||
mNotificationMngr.notify(R.string.downloader_download_in_progress_ticker, mNotification);
|
||||
}
|
||||
|
||||
mLastPercent = percent;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Callback method to update the progress bar in the status notification (old version)
|
||||
*/
|
||||
@Override
|
||||
public void onTransferProgress(long progressRate) {
|
||||
// NOTHING TO DO HERE ANYMORE
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a status notification to show the download progress
|
||||
*
|
||||
* @param download Download operation starting.
|
||||
*/
|
||||
private void notifyDownloadStart(DownloadFileOperation download) {
|
||||
/// create status notification to show the download progress
|
||||
mLastPercent = 0;
|
||||
mNotification = new Notification(R.drawable.icon, getString(R.string.downloader_download_in_progress_ticker), System.currentTimeMillis());
|
||||
mNotification.flags |= Notification.FLAG_ONGOING_EVENT;
|
||||
mNotification.contentView = new RemoteViews(getApplicationContext().getPackageName(), R.layout.progressbar_layout);
|
||||
mNotification.contentView.setProgressBar(R.id.status_progress, 100, 0, download.getSize() == -1);
|
||||
mNotification.contentView.setTextViewText(R.id.status_text, String.format(getString(R.string.downloader_download_in_progress_content), 0, new File(download.getLocalPath()).getName()));
|
||||
mNotification.contentView.setImageViewResource(R.id.status_icon, R.drawable.icon);
|
||||
// TODO put something smart in the contentIntent below
|
||||
mNotification.contentIntent = PendingIntent.getActivity(getApplicationContext(), 0, new Intent(), PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
mNotificationMngr.notify(R.string.downloader_download_in_progress_ticker, mNotification);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Updates the status notification with the result of a download operation.
|
||||
*
|
||||
* @param downloadResult Result of the download operation.
|
||||
* @param download Finished download operation
|
||||
*/
|
||||
private void notifyDownloadResult(DownloadFileOperation download, RemoteOperationResult downloadResult) {
|
||||
mNotificationMngr.cancel(R.string.downloader_download_in_progress_ticker);
|
||||
if (!downloadResult.isCancelled()) {
|
||||
int tickerId = (downloadResult.isSuccess()) ? R.string.downloader_download_succeeded_ticker : R.string.downloader_download_failed_ticker;
|
||||
int contentId = (downloadResult.isSuccess()) ? R.string.downloader_download_succeeded_content : R.string.downloader_download_failed_content;
|
||||
Notification finalNotification = new Notification(R.drawable.icon, getString(tickerId), System.currentTimeMillis());
|
||||
finalNotification.flags |= Notification.FLAG_AUTO_CANCEL;
|
||||
// TODO put something smart in the contentIntent below
|
||||
finalNotification.contentIntent = PendingIntent.getActivity(getApplicationContext(), 0, new Intent(), PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
finalNotification.setLatestEventInfo(getApplicationContext(), getString(tickerId), String.format(getString(contentId), new File(download.getLocalPath()).getName()), finalNotification.contentIntent);
|
||||
mNotificationMngr.notify(tickerId, finalNotification);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sends a broadcast in order to the interested activities can update their view
|
||||
*
|
||||
* @param downloadResult 'True' if the download was successful
|
||||
* @param newFilePath Absolute path to the download file
|
||||
* @param download Finished download operation
|
||||
* @param downloadResult Result of the download operation
|
||||
* @param newFilePath Absolute path to the downloaded file
|
||||
*/
|
||||
private void sendFinalBroadcast(boolean downloadResult, String newFilePath) {
|
||||
private void sendFinalBroadcast(DownloadFileOperation download, RemoteOperationResult downloadResult, String newFilePath) {
|
||||
Intent end = new Intent(DOWNLOAD_FINISH_MESSAGE);
|
||||
end.putExtra(EXTRA_DOWNLOAD_RESULT, downloadResult);
|
||||
end.putExtra(ACCOUNT_NAME, mAccount.name);
|
||||
end.putExtra(EXTRA_REMOTE_PATH, mRemotePath);
|
||||
if (downloadResult) {
|
||||
end.putExtra(EXTRA_DOWNLOAD_RESULT, downloadResult.isSuccess());
|
||||
end.putExtra(ACCOUNT_NAME, download.getAccount().name);
|
||||
end.putExtra(EXTRA_REMOTE_PATH, download.getRemotePath());
|
||||
if (downloadResult.isSuccess()) {
|
||||
end.putExtra(EXTRA_FILE_PATH, newFilePath);
|
||||
}
|
||||
sendBroadcast(end);
|
||||
|
|
|
@ -62,17 +62,20 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
|
|||
|
||||
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 int mCurrentIndexUpload;
|
||||
|
||||
private NotificationManager mNotificationManager;
|
||||
private Notification mNotification;
|
||||
private long mTotalDataToSend, mSendData;
|
||||
private int mTotalFilesToSend;
|
||||
private int mCurrentIndexUpload, mPreviousPercent;
|
||||
private int mSuccessCounter;
|
||||
private RemoteViews mDefaultNotificationContentView;
|
||||
private long mTotalDataToSend, mSendData;
|
||||
private int mTotalFilesToSend, mPreviousPercent;
|
||||
private int mSuccessCounter;
|
||||
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
@ -135,6 +124,13 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
|
|||
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)) {
|
||||
|
@ -191,6 +187,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
|
||||
*/
|
||||
|
@ -381,7 +408,7 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
|
|||
* 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) {
|
||||
|
@ -392,4 +419,11 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
|
|||
}
|
||||
mPreviousPercent = percent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer, String fileName) {
|
||||
// TODO Maybe replace the other transferProgress with this
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
213
src/com/owncloud/android/operations/DownloadFileOperation.java
Normal file
213
src/com/owncloud/android/operations/DownloadFileOperation.java
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package com.owncloud.android.operations;
|
||||
|
||||
public class OperationCancelledException extends Exception {
|
||||
|
||||
}
|
|
@ -57,7 +57,9 @@ public class RemoteOperationResult {
|
|||
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;
|
||||
|
@ -94,7 +96,10 @@ public class RemoteOperationResult {
|
|||
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) {
|
||||
|
@ -132,6 +137,10 @@ public class RemoteOperationResult {
|
|||
return mSuccess;
|
||||
}
|
||||
|
||||
public boolean isCancelled() {
|
||||
return mCode == ResultCode.CANCELLED;
|
||||
}
|
||||
|
||||
public int getHttpCode() {
|
||||
return mHttpCode;
|
||||
}
|
||||
|
@ -169,7 +178,10 @@ public class RemoteOperationResult {
|
|||
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) {
|
||||
|
@ -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.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") + ")";
|
||||
|
|
|
@ -144,7 +144,7 @@ public class UploadFileOperation extends RemoteOperation {
|
|||
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());
|
||||
|
|
|
@ -20,9 +20,13 @@ package com.owncloud.android.ui.activity;
|
|||
import android.accounts.Account;
|
||||
import android.app.Dialog;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.support.v4.app.FragmentTransaction;
|
||||
|
||||
import com.actionbarsherlock.app.ActionBar;
|
||||
|
@ -30,6 +34,7 @@ import com.actionbarsherlock.app.SherlockFragmentActivity;
|
|||
import com.actionbarsherlock.view.MenuItem;
|
||||
import com.owncloud.android.datamodel.OCFile;
|
||||
import com.owncloud.android.files.services.FileDownloader;
|
||||
import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
|
||||
import com.owncloud.android.ui.fragment.FileDetailFragment;
|
||||
|
||||
import com.owncloud.android.R;
|
||||
|
@ -46,6 +51,7 @@ public class FileDetailActivity extends SherlockFragmentActivity implements File
|
|||
public static final int DIALOG_SHORT_WAIT = 0;
|
||||
|
||||
private boolean mConfigurationChangedToLandscape = false;
|
||||
private FileDownloaderBinder mDownloaderBinder = null;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
|
@ -58,6 +64,8 @@ public class FileDetailActivity extends SherlockFragmentActivity implements File
|
|||
);
|
||||
|
||||
if (!mConfigurationChangedToLandscape) {
|
||||
bindService(new Intent(this, FileDownloader.class), mConnection, Context.BIND_AUTO_CREATE);
|
||||
|
||||
setContentView(R.layout.file_activity_details);
|
||||
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
|
@ -78,6 +86,32 @@ public class FileDetailActivity extends SherlockFragmentActivity implements File
|
|||
|
||||
}
|
||||
|
||||
|
||||
/** Defines callbacks for service binding, passed to bindService() */
|
||||
private ServiceConnection mConnection = new ServiceConnection() {
|
||||
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName className, IBinder service) {
|
||||
mDownloaderBinder = (FileDownloaderBinder) service;
|
||||
FileDetailFragment fragment = (FileDetailFragment) getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);
|
||||
if (fragment != null)
|
||||
fragment.updateFileDetails(); // a new chance to get the mDownloadBinder through getDownloadBinder()
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName arg0) {
|
||||
mDownloaderBinder = null;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
unbindService(mConnection);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
boolean returnValue = false;
|
||||
|
@ -141,4 +175,13 @@ public class FileDetailActivity extends SherlockFragmentActivity implements File
|
|||
// nothing to do here!
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public FileDownloaderBinder getFileDownloaderBinder() {
|
||||
return mDownloaderBinder;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -26,12 +26,14 @@ import android.app.ProgressDialog;
|
|||
import android.app.AlertDialog.Builder;
|
||||
import android.app.Dialog;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.DialogInterface.OnClickListener;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.ServiceConnection;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
|
@ -40,6 +42,7 @@ import android.database.Cursor;
|
|||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.provider.MediaStore;
|
||||
import android.support.v4.app.FragmentTransaction;
|
||||
|
@ -64,6 +67,7 @@ import com.owncloud.android.datamodel.DataStorageManager;
|
|||
import com.owncloud.android.datamodel.FileDataStorageManager;
|
||||
import com.owncloud.android.datamodel.OCFile;
|
||||
import com.owncloud.android.files.services.FileDownloader;
|
||||
import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
|
||||
import com.owncloud.android.files.services.FileUploader;
|
||||
import com.owncloud.android.network.OwnCloudClientUtils;
|
||||
import com.owncloud.android.syncadapter.FileSyncService;
|
||||
|
@ -90,6 +94,7 @@ public class FileDisplayActivity extends SherlockFragmentActivity implements
|
|||
private SyncBroadcastReceiver mSyncBroadcastReceiver;
|
||||
private UploadFinishReceiver mUploadFinishReceiver;
|
||||
private DownloadFinishReceiver mDownloadFinishReceiver;
|
||||
private FileDownloaderBinder mDownloaderBinder = null;
|
||||
|
||||
private OCFileListFragment mFileList;
|
||||
|
||||
|
@ -123,6 +128,7 @@ public class FileDisplayActivity extends SherlockFragmentActivity implements
|
|||
|
||||
} else { /// at least an account is available
|
||||
|
||||
bindService(new Intent(this, FileDownloader.class), mConnection, Context.BIND_AUTO_CREATE);
|
||||
initDataFromCurrentAccount();
|
||||
|
||||
}
|
||||
|
@ -206,6 +212,13 @@ public class FileDisplayActivity extends SherlockFragmentActivity implements
|
|||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
unbindService(mConnection);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
MenuInflater inflater = getSherlock().getMenuInflater();
|
||||
|
@ -421,7 +434,7 @@ public class FileDisplayActivity extends SherlockFragmentActivity implements
|
|||
registerReceiver(mDownloadFinishReceiver, downloadIntentFilter);
|
||||
|
||||
// List current directory
|
||||
//mFileList.listDirectory(mCurrentDir);
|
||||
mFileList.listDirectory(mCurrentDir); // we should find the way to avoid the need of this
|
||||
|
||||
} else {
|
||||
|
||||
|
@ -873,6 +886,40 @@ public class FileDisplayActivity extends SherlockFragmentActivity implements
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public FileDownloaderBinder getFileDownloaderBinder() {
|
||||
return mDownloaderBinder;
|
||||
}
|
||||
|
||||
|
||||
/** Defines callbacks for service binding, passed to bindService() */
|
||||
private ServiceConnection mConnection = new ServiceConnection() {
|
||||
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName className, IBinder service) {
|
||||
mDownloaderBinder = (FileDownloaderBinder) service;
|
||||
// a new chance to get the mDownloadBinder through getDownloadBinder() - THIS IS A MESS
|
||||
mFileList.listDirectory();
|
||||
if (mDualPane) {
|
||||
FileDetailFragment fragment = (FileDetailFragment) getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);
|
||||
if (fragment != null)
|
||||
fragment.updateFileDetails();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName arg0) {
|
||||
mDownloaderBinder = null;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Launch an intent to request the PIN code to the user before letting him use the app
|
||||
*/
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
||||
}
|
|
@ -23,8 +23,9 @@ import com.owncloud.android.AccountUtils;
|
|||
import com.owncloud.android.DisplayUtils;
|
||||
import com.owncloud.android.datamodel.DataStorageManager;
|
||||
import com.owncloud.android.datamodel.OCFile;
|
||||
import com.owncloud.android.files.services.FileDownloader;
|
||||
import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
|
||||
import com.owncloud.android.files.services.FileUploader;
|
||||
import com.owncloud.android.ui.activity.TransferServiceGetter;
|
||||
|
||||
import com.owncloud.android.R;
|
||||
|
||||
|
@ -52,12 +53,14 @@ public class FileListListAdapter extends BaseAdapter implements ListAdapter {
|
|||
private Vector<OCFile> mFiles = null;
|
||||
private DataStorageManager mStorageManager;
|
||||
private Account mAccount;
|
||||
private TransferServiceGetter mTransferServiceGetter;
|
||||
|
||||
public FileListListAdapter(OCFile file, DataStorageManager storage_man,
|
||||
Context context) {
|
||||
Context context, TransferServiceGetter transferServiceGetter) {
|
||||
mStorageManager = storage_man;
|
||||
mContext = context;
|
||||
mAccount = AccountUtils.getCurrentOwnCloudAccount(mContext);
|
||||
mTransferServiceGetter = transferServiceGetter;
|
||||
swapDirectory(file);
|
||||
/*mFile = file;
|
||||
mFiles = mStorageManager.getDirectoryContent(mFile);*/
|
||||
|
@ -118,7 +121,9 @@ public class FileListListAdapter extends BaseAdapter implements ListAdapter {
|
|||
fileIcon.setImageResource(R.drawable.ic_menu_archive);
|
||||
}
|
||||
ImageView localStateView = (ImageView) view.findViewById(R.id.imageView2);
|
||||
if (FileDownloader.isDownloading(mAccount, file.getRemotePath())) {
|
||||
//if (FileDownloader.isDownloading(mAccount, file.getRemotePath())) {
|
||||
FileDownloaderBinder downloaderBinder = mTransferServiceGetter.getFileDownloaderBinder();
|
||||
if (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, file.getRemotePath())) {
|
||||
localStateView.setImageResource(R.drawable.downloading_file_indicator);
|
||||
localStateView.setVisibility(View.VISIBLE);
|
||||
} else if (FileUploader.isUploading(mAccount, file.getRemotePath())) {
|
||||
|
|
|
@ -79,9 +79,12 @@ import com.owncloud.android.datamodel.FileDataStorageManager;
|
|||
import com.owncloud.android.datamodel.OCFile;
|
||||
import com.owncloud.android.files.services.FileDownloader;
|
||||
import com.owncloud.android.files.services.FileUploader;
|
||||
import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
|
||||
import com.owncloud.android.network.OwnCloudClientUtils;
|
||||
import com.owncloud.android.ui.activity.FileDetailActivity;
|
||||
import com.owncloud.android.ui.activity.FileDisplayActivity;
|
||||
import com.owncloud.android.ui.activity.TransferServiceGetter;
|
||||
import com.owncloud.android.ui.fragment.OCFileListFragment.ContainerActivity;
|
||||
import com.owncloud.android.utils.OwnCloudVersion;
|
||||
|
||||
import com.owncloud.android.R;
|
||||
|
@ -136,7 +139,7 @@ public class FileDetailFragment extends SherlockFragment implements
|
|||
* @param fileToDetail An {@link OCFile} to show in the fragment
|
||||
* @param ocAccount An ownCloud account; needed to start downloads
|
||||
*/
|
||||
public FileDetailFragment(OCFile fileToDetail, Account ocAccount){
|
||||
public FileDetailFragment(OCFile fileToDetail, Account ocAccount) {
|
||||
mFile = fileToDetail;
|
||||
mAccount = ocAccount;
|
||||
mLayout = R.layout.file_details_empty;
|
||||
|
@ -147,20 +150,6 @@ public class FileDetailFragment extends SherlockFragment implements
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void onAttach(Activity activity) {
|
||||
super.onAttach(activity);
|
||||
try {
|
||||
mContainerActivity = (ContainerActivity) activity;
|
||||
} catch (ClassCastException e) {
|
||||
throw new ClassCastException(activity.toString() + " must implement FileListFragment.ContainerActivity");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
|
@ -185,11 +174,24 @@ public class FileDetailFragment extends SherlockFragment implements
|
|||
mPreview = (ImageView)mView.findViewById(R.id.fdPreview);
|
||||
}
|
||||
|
||||
updateFileDetails();
|
||||
return view;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void onAttach(Activity activity) {
|
||||
super.onAttach(activity);
|
||||
try {
|
||||
mContainerActivity = (ContainerActivity) activity;
|
||||
} catch (ClassCastException e) {
|
||||
throw new ClassCastException(activity.toString() + " must implement " + FileDetailFragment.ContainerActivity.class.getCanonicalName());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
Log.i(getClass().toString(), "onSaveInstanceState() start");
|
||||
|
@ -242,10 +244,10 @@ public class FileDetailFragment extends SherlockFragment implements
|
|||
public void onClick(View v) {
|
||||
switch (v.getId()) {
|
||||
case R.id.fdDownloadBtn: {
|
||||
if (FileDownloader.isDownloading(mAccount, mFile.getRemotePath())) {
|
||||
|
||||
// TODO cancelar descarga
|
||||
|
||||
//if (FileDownloader.isDownloading(mAccount, mFile.getRemotePath())) {
|
||||
FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder();
|
||||
if (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, mFile.getRemotePath())) {
|
||||
downloaderBinder.cancel(mAccount, mFile.getRemotePath());
|
||||
if (mFile.isDown()) {
|
||||
setButtonsForDown();
|
||||
} else {
|
||||
|
@ -440,7 +442,9 @@ public class FileDetailFragment extends SherlockFragment implements
|
|||
cb.setChecked(mFile.keepInSync());
|
||||
|
||||
// configure UI for depending upon local state of the file
|
||||
if (FileDownloader.isDownloading(mAccount, mFile.getRemotePath()) || FileUploader.isUploading(mAccount, mFile.getRemotePath())) {
|
||||
//if (FileDownloader.isDownloading(mAccount, mFile.getRemotePath()) || FileUploader.isUploading(mAccount, mFile.getRemotePath())) {
|
||||
FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder();
|
||||
if ((downloaderBinder != null && downloaderBinder.isDownloading(mAccount, mFile.getRemotePath())) || FileUploader.isUploading(mAccount, mFile.getRemotePath())) {
|
||||
setButtonsForTransferring();
|
||||
|
||||
} else if (mFile.isDown()) {
|
||||
|
@ -585,7 +589,7 @@ public class FileDetailFragment extends SherlockFragment implements
|
|||
*
|
||||
* @author David A. Velasco
|
||||
*/
|
||||
public interface ContainerActivity {
|
||||
public interface ContainerActivity extends TransferServiceGetter {
|
||||
|
||||
/**
|
||||
* Callback method invoked when the detail fragment wants to notice its container
|
||||
|
|
|
@ -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.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;
|
||||
|
@ -76,12 +78,15 @@ public class OCFileListFragment extends FragmentListView {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@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) {
|
||||
|
@ -154,7 +159,9 @@ public class OCFileListFragment extends FragmentListView {
|
|||
* Calls {@link OCFileListFragment#listDirectory(OCFile)} with a null parameter
|
||||
*/
|
||||
public void listDirectory(){
|
||||
int position = mList.getFirstVisiblePosition();
|
||||
listDirectory(null);
|
||||
mList.setSelectionFromTop(position, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -187,6 +194,7 @@ public class OCFileListFragment extends FragmentListView {
|
|||
mFile = directory;
|
||||
mAdapter.swapDirectory(mFile);
|
||||
mList.setSelectionFromTop(0, 0);
|
||||
mList.invalidate();
|
||||
}
|
||||
|
||||
|
||||
|
@ -196,7 +204,7 @@ public class OCFileListFragment extends FragmentListView {
|
|||
*
|
||||
* @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
|
||||
|
|
|
@ -95,7 +95,7 @@ public class ChunkFromFileChannelRequestEntity implements RequestEntity {
|
|||
out.write(mBuffer.array(), 0, readCount);
|
||||
mBuffer.clear();
|
||||
if (mListener != null)
|
||||
mListener.transferProgress(readCount);
|
||||
mListener.onTransferProgress(readCount);
|
||||
}
|
||||
|
||||
} catch (IOException io) {
|
||||
|
|
|
@ -7,6 +7,10 @@ import java.io.RandomAccessFile;
|
|||
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;
|
||||
|
||||
|
@ -23,7 +27,7 @@ public class FileRequestEntity implements RequestEntity {
|
|||
|
||||
final File mFile;
|
||||
final String mContentType;
|
||||
OnDatatransferProgressListener mListener;
|
||||
Set<OnDatatransferProgressListener> mListeners = new HashSet<OnDatatransferProgressListener>();
|
||||
|
||||
public FileRequestEntity(final File file, final String contentType) {
|
||||
super();
|
||||
|
@ -49,10 +53,19 @@ public class FileRequestEntity implements RequestEntity {
|
|||
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];
|
||||
|
@ -64,22 +77,22 @@ public class FileRequestEntity implements RequestEntity {
|
|||
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();
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
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);
|
||||
}
|
||||
|
|
|
@ -98,7 +98,7 @@ public class WebdavClient extends HttpClient {
|
|||
int readResult;
|
||||
while ((readResult = bis.read(bytes)) != -1) {
|
||||
if (mDataTransferListener != null)
|
||||
mDataTransferListener.transferProgress(readResult);
|
||||
mDataTransferListener.onTransferProgress(readResult);
|
||||
fos.write(bytes, 0, readResult);
|
||||
}
|
||||
fos.close();
|
||||
|
@ -165,7 +165,7 @@ public class WebdavClient extends HttpClient {
|
|||
try {
|
||||
File f = new File(localFile);
|
||||
FileRequestEntity entity = new FileRequestEntity(f, contentType);
|
||||
entity.setOnDatatransferProgressListener(mDataTransferListener);
|
||||
entity.addOnDatatransferProgressListener(mDataTransferListener);
|
||||
put.setRequestEntity(entity);
|
||||
status = executeMethod(put);
|
||||
|
||||
|
|
Loading…
Reference in a new issue