added asyndownloader to use DownloadManager when possible

This commit is contained in:
Toby Kurien 2015-09-03 17:55:58 +02:00 committed by Peter Serwylo
parent 13c90e6c4a
commit dbd4c467f8
3 changed files with 157 additions and 5 deletions

View File

@ -21,8 +21,8 @@
package org.fdroid.fdroid.net;
import android.content.Context;
import android.os.Build;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.content.LocalBroadcastManager;
@ -38,6 +38,7 @@ import org.fdroid.fdroid.data.SanitizedFile;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.security.NoSuchAlgorithmException;
/**
@ -193,11 +194,17 @@ public class ApkDownloader implements AsyncDownloadWrapper.Listener {
Utils.DebugLog(TAG, "Downloading apk from " + remoteAddress + " to " + localFile);
try {
if (canUseDownloadManager(new URL(remoteAddress))) {
// If we can use Android's DownloadManager, let's use it, because
// of better OS integration, reliability, and async ability
dlWrapper = new AsyncDownloader(context, this, curApk.apkName, remoteAddress, localFile);
} else {
Downloader downloader = DownloaderFactory.create(context, remoteAddress, localFile);
dlWrapper = new AsyncDownloadWrapper(downloader, this);
}
dlWrapper.download();
return true;
} catch (IOException e) {
onErrorDownloading(e.getLocalizedMessage());
}
@ -205,6 +212,17 @@ public class ApkDownloader implements AsyncDownloadWrapper.Listener {
return false;
}
/**
* Tests to see if we can use Android's DownloadManager to download the APK, instead of
* a downloader returned from DownloadFactory.
* @param url
* @return
*/
private boolean canUseDownloadManager(URL url) {
return Build.VERSION.SDK_INT > Build.VERSION_CODES.FROYO
&& !DownloaderFactory.isOnionAddress(url);
}
private void sendMessage(String type) {
sendProgressEvent(new ProgressListener.Event(type));
}

View File

@ -0,0 +1,134 @@
package org.fdroid.fdroid.net;
import android.annotation.TargetApi;
import android.app.DownloadManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.os.Build;
import android.os.ParcelFileDescriptor;
import org.fdroid.fdroid.data.SanitizedFile;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* A downloader that uses Android's DownloadManager to perform a download.
*/
@TargetApi(Build.VERSION_CODES.GINGERBREAD)
public class AsyncDownloader extends AsyncDownloadWrapper {
private final Context context;
private final DownloadManager dm;
private SanitizedFile localFile;
private String remoteAddress;
private String appName;
private Listener listener;
private long downloadId = -1;
/**
* Normally the listener would be provided using a setListener method.
* However for the purposes of this async downloader, it doesn't make
* sense to have an async task without any way to notify the outside
* world about completion. Therefore, we require the listener as a
* parameter to the constructor.
*
* @param listener
*/
public AsyncDownloader(Context context, Listener listener, String appName, String remoteAddress, SanitizedFile localFile) {
super(null, listener);
this.context = context;
this.appName = appName;
this.remoteAddress = remoteAddress;
this.listener = listener;
this.localFile = localFile;
if (appName == null || appName.trim().length() == 0) {
this.appName = remoteAddress;
}
dm = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
}
@Override
public void download() {
// set up download request
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(remoteAddress));
request.setTitle(appName);
if (listener != null) {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
intentFilter.addAction(DownloadManager.ACTION_NOTIFICATION_CLICKED);
context.registerReceiver(downloadReceiver, intentFilter);
}
downloadId = dm.enqueue(request);
}
@Override
public int getBytesRead() {
return 0;
}
@Override
public int getTotalBytes() {
return 0;
}
@Override
public void attemptCancel() {
if (downloadId >= 0) dm.remove(downloadId);
}
// Broadcast receiver to receive broadcasts from the download manager
private BroadcastReceiver downloadReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (listener == null) return; // no point if no-one is listening
if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(intent.getAction())) {
long id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
if (id == downloadId) {
context.unregisterReceiver(this);
// clear the notification
dm.remove(id);
try {
// write the downloaded file to the expected location
ParcelFileDescriptor fd = dm.openDownloadedFile(id);
InputStream is = new FileInputStream(fd.getFileDescriptor());
OutputStream os = new FileOutputStream(localFile);
byte[] buffer = new byte[1024];
int count = 0;
while ((count = is.read(buffer, 0, buffer.length)) > 0) {
os.write(buffer, 0, count);
}
os.close();
listener.onDownloadComplete();
} catch (IOException e) {
listener.onErrorDownloading(e.getLocalizedMessage());
return;
}
}
}
if (DownloadManager.ACTION_NOTIFICATION_CLICKED.equals(intent.getAction())) {
long id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
if (id == downloadId) {
// TODO - display app details screen for this app
}
}
}
};
}

View File

@ -51,7 +51,7 @@ public class DownloaderFactory {
return "bluetooth".equalsIgnoreCase(url.getProtocol());
}
private static boolean isOnionAddress(URL url) {
static boolean isOnionAddress(URL url) {
return url.getHost().endsWith(".onion");
}
}