Rough guess at what ApkDownloader refactor could look like.
This commit is contained in:
parent
c4b0eb9b51
commit
a44ce0e4e7
2
.gitignore
vendored
2
.gitignore
vendored
@ -6,6 +6,6 @@
|
||||
/build.xml
|
||||
*~
|
||||
/.idea/
|
||||
/*.iml
|
||||
*.iml
|
||||
out
|
||||
/.settings/
|
||||
|
@ -20,37 +20,29 @@
|
||||
|
||||
package org.fdroid.fdroid;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URL;
|
||||
import java.io.*;
|
||||
|
||||
import android.util.Log;
|
||||
import org.fdroid.fdroid.data.Apk;
|
||||
import org.fdroid.fdroid.net.HttpDownloader;
|
||||
|
||||
public class ApkDownloader extends Thread {
|
||||
|
||||
public static final int EVENT_APK_DOWNLOAD_COMPLETE = 100;
|
||||
public static final int EVENT_ERROR_HASH_MISMATCH = 101;
|
||||
public static final int EVENT_ERROR_DOWNLOAD_FAILED = 102;
|
||||
public static final int EVENT_ERROR_UNKNOWN = 103;
|
||||
private Apk curapk;
|
||||
private String repoaddress;
|
||||
private String filename;
|
||||
private File destdir;
|
||||
private File localfile;
|
||||
|
||||
public static enum Status {
|
||||
STARTING, RUNNING, ERROR, DONE, CANCELLED
|
||||
}
|
||||
private ProgressListener listener;
|
||||
|
||||
public static enum Error {
|
||||
CORRUPT, UNKNOWN
|
||||
public void setProgressListener(ProgressListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
private Status status = Status.STARTING;
|
||||
private Error error;
|
||||
private int progress;
|
||||
private int max;
|
||||
private String errorMessage;
|
||||
|
||||
// Constructor - creates a Downloader to download the given Apk,
|
||||
// which must have its detail populated.
|
||||
ApkDownloader(Apk apk, String repoaddress, File destdir) {
|
||||
@ -59,50 +51,19 @@ public class ApkDownloader extends Thread {
|
||||
this.destdir = destdir;
|
||||
}
|
||||
|
||||
public synchronized Status getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
// Current progress and maximum value for progress dialog
|
||||
public synchronized int getProgress() {
|
||||
return progress;
|
||||
}
|
||||
|
||||
public synchronized int getMax() {
|
||||
return max;
|
||||
}
|
||||
|
||||
// Error code and error message, only valid if status is ERROR
|
||||
public synchronized Error getErrorType() {
|
||||
return error;
|
||||
}
|
||||
|
||||
public synchronized String getErrorMessage() {
|
||||
return errorMessage;
|
||||
}
|
||||
|
||||
// The URL being downloaded or path to a cached file
|
||||
public synchronized String remoteFile() {
|
||||
return filename;
|
||||
}
|
||||
|
||||
// The downloaded APK. Valid only when getStatus() has returned STATUS.DONE.
|
||||
public File localFile() {
|
||||
return localfile;
|
||||
}
|
||||
|
||||
// The APK being downloaded
|
||||
public synchronized Apk getApk() {
|
||||
return curapk;
|
||||
public String remoteFile() {
|
||||
return repoaddress + "/" + curapk.apkName.replace(" ", "%20");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
localfile = new File(destdir, curapk.apkName);
|
||||
|
||||
InputStream input = null;
|
||||
OutputStream output = null;
|
||||
String apkname = curapk.apkName;
|
||||
localfile = new File(destdir, apkname);
|
||||
try {
|
||||
|
||||
// See if we already have this apk cached...
|
||||
@ -111,12 +72,7 @@ public class ApkDownloader extends Thread {
|
||||
Hasher hash = new Hasher(curapk.hashType, localfile);
|
||||
if (hash.match(curapk.hash)) {
|
||||
Log.d("FDroid", "Using cached apk at " + localfile);
|
||||
synchronized (this) {
|
||||
progress = 1;
|
||||
max = 1;
|
||||
status = Status.DONE;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
Log.d("FDroid", "Not using cached apk at " + localfile);
|
||||
localfile.delete();
|
||||
@ -124,72 +80,45 @@ public class ApkDownloader extends Thread {
|
||||
}
|
||||
|
||||
// If we haven't got the apk locally, we'll have to download it...
|
||||
String remotefile;
|
||||
remotefile = repoaddress + "/" + apkname.replace(" ", "%20");
|
||||
Log.d("FDroid", "Downloading apk from " + remotefile);
|
||||
synchronized (this) {
|
||||
filename = remotefile;
|
||||
progress = 0;
|
||||
max = curapk.size;
|
||||
status = Status.RUNNING;
|
||||
}
|
||||
|
||||
input = new URL(remotefile).openStream();
|
||||
output = new FileOutputStream(localfile);
|
||||
byte data[] = new byte[Utils.BUFFER_SIZE];
|
||||
while (true) {
|
||||
if (isInterrupted()) {
|
||||
Log.d("FDroid", "Download cancelled!");
|
||||
break;
|
||||
}
|
||||
int count = input.read(data);
|
||||
if (count == -1) {
|
||||
break;
|
||||
}
|
||||
output.write(data, 0, count);
|
||||
synchronized (this) {
|
||||
progress += count;
|
||||
}
|
||||
}
|
||||
HttpDownloader downloader = new HttpDownloader(remoteFile(), localfile);
|
||||
downloader.setProgressListener(listener);
|
||||
|
||||
if (isInterrupted()) {
|
||||
localfile.delete();
|
||||
synchronized (this) {
|
||||
status = Status.CANCELLED;
|
||||
}
|
||||
Log.d("FDroid", "Downloading apk from " + remoteFile());
|
||||
int httpStatus = downloader.downloadHttpFile();
|
||||
|
||||
if (httpStatus != 200 || !localfile.exists()) {
|
||||
sendProgress(EVENT_ERROR_DOWNLOAD_FAILED);
|
||||
return;
|
||||
}
|
||||
|
||||
Hasher hash = new Hasher(curapk.hashType, localfile);
|
||||
if (!hash.match(curapk.hash)) {
|
||||
synchronized (this) {
|
||||
Log.d("FDroid", "Downloaded file hash of " + hash.getHash()
|
||||
+ " did not match repo's " + curapk.hash);
|
||||
// No point keeping a bad file, whether we're
|
||||
// caching or not.
|
||||
localfile.delete();
|
||||
error = Error.CORRUPT;
|
||||
errorMessage = null;
|
||||
status = Status.ERROR;
|
||||
sendProgress(EVENT_ERROR_HASH_MISMATCH);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e("FDroid", "Download failed:\n" + Log.getStackTraceString(e));
|
||||
synchronized (this) {
|
||||
if (localfile.exists()) {
|
||||
localfile.delete();
|
||||
error = Error.UNKNOWN;
|
||||
errorMessage = e.toString();
|
||||
status = Status.ERROR;
|
||||
return;
|
||||
}
|
||||
} finally {
|
||||
Utils.closeQuietly(output);
|
||||
Utils.closeQuietly(input);
|
||||
sendProgress(EVENT_ERROR_UNKNOWN);
|
||||
return;
|
||||
}
|
||||
|
||||
Log.d("FDroid", "Download finished: " + localfile);
|
||||
synchronized (this) {
|
||||
status = Status.DONE;
|
||||
sendProgress(EVENT_APK_DOWNLOAD_COMPLETE);
|
||||
}
|
||||
|
||||
private void sendProgress(int type) {
|
||||
if (listener != null) {
|
||||
listener.onProgress(new ProgressListener.Event(type));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ import org.fdroid.fdroid.data.*;
|
||||
import org.fdroid.fdroid.installer.Installer;
|
||||
import org.fdroid.fdroid.installer.Installer.AndroidNotCompatibleException;
|
||||
import org.fdroid.fdroid.installer.Installer.InstallerCallback;
|
||||
import org.fdroid.fdroid.net.Downloader;
|
||||
import org.xml.sax.XMLReader;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
@ -394,10 +395,6 @@ public class AppDetails extends ListActivity {
|
||||
updateViews();
|
||||
|
||||
MenuManager.create(this).invalidateOptionsMenu();
|
||||
|
||||
if (downloadHandler != null) {
|
||||
downloadHandler.startUpdates();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -405,9 +402,6 @@ public class AppDetails extends ListActivity {
|
||||
if (myAppObserver != null) {
|
||||
getContentResolver().unregisterContentObserver(myAppObserver);
|
||||
}
|
||||
if (downloadHandler != null) {
|
||||
downloadHandler.stopUpdates();
|
||||
}
|
||||
if (app != null && (app.ignoreAllUpdates != startingIgnoreAll
|
||||
|| app.ignoreThisUpdate != startingIgnoreThis)) {
|
||||
setIgnoreUpdates(app.id, app.ignoreAllUpdates, app.ignoreThisUpdate);
|
||||
@ -1071,81 +1065,61 @@ public class AppDetails extends ListActivity {
|
||||
}
|
||||
|
||||
// Handler used to update the progress dialog while downloading.
|
||||
private class DownloadHandler extends Handler {
|
||||
private class DownloadHandler extends Handler implements ProgressListener {
|
||||
private ApkDownloader download;
|
||||
private ProgressDialog pd;
|
||||
private boolean updating;
|
||||
private String id;
|
||||
|
||||
public DownloadHandler(Apk apk, String repoaddress, File destdir) {
|
||||
id = apk.id;
|
||||
download = new ApkDownloader(apk, repoaddress, destdir);
|
||||
download.start();
|
||||
startUpdates();
|
||||
}
|
||||
|
||||
public DownloadHandler(DownloadHandler oldHandler) {
|
||||
if (oldHandler != null) {
|
||||
download = oldHandler.download;
|
||||
}
|
||||
startUpdates();
|
||||
}
|
||||
|
||||
public boolean updateProgress() {
|
||||
public void onProgress(ProgressListener.Event event) {
|
||||
boolean finished = false;
|
||||
switch (download.getStatus()) {
|
||||
case RUNNING:
|
||||
switch (event.type) {
|
||||
case Downloader.EVENT_PROGRESS:
|
||||
if (pd == null) {
|
||||
pd = createProgressDialog(download.remoteFile(),
|
||||
download.getProgress(), download.getMax());
|
||||
event.progress, event.total);
|
||||
} else {
|
||||
pd.setProgress(download.getProgress());
|
||||
pd.setProgress(event.progress);
|
||||
}
|
||||
break;
|
||||
case ERROR:
|
||||
if (pd != null)
|
||||
pd.dismiss();
|
||||
|
||||
case ApkDownloader.EVENT_ERROR_DOWNLOAD_FAILED:
|
||||
case ApkDownloader.EVENT_ERROR_HASH_MISMATCH:
|
||||
case ApkDownloader.EVENT_ERROR_UNKNOWN:
|
||||
String text;
|
||||
if (download.getErrorType() == ApkDownloader.Error.CORRUPT)
|
||||
if (event.type == ApkDownloader.EVENT_ERROR_HASH_MISMATCH)
|
||||
text = getString(R.string.corrupt_download);
|
||||
else
|
||||
text = download.getErrorMessage();
|
||||
text = getString(R.string.details_notinstalled);
|
||||
Toast.makeText(AppDetails.this, text, Toast.LENGTH_LONG).show();
|
||||
finished = true;
|
||||
break;
|
||||
case DONE:
|
||||
if (pd != null)
|
||||
pd.dismiss();
|
||||
|
||||
case ApkDownloader.EVENT_APK_DOWNLOAD_COMPLETE:
|
||||
installApk(download.localFile(), id);
|
||||
finished = true;
|
||||
break;
|
||||
case CANCELLED:
|
||||
Toast.makeText(AppDetails.this,
|
||||
getString(R.string.download_cancelled),
|
||||
Toast.LENGTH_SHORT).show();
|
||||
finished = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return finished;
|
||||
|
||||
}
|
||||
|
||||
public void startUpdates() {
|
||||
if (!updating) {
|
||||
updating = true;
|
||||
sendEmptyMessage(0);
|
||||
if (finished) {
|
||||
destroy();
|
||||
}
|
||||
}
|
||||
|
||||
public void stopUpdates() {
|
||||
updating = false;
|
||||
removeMessages(0);
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
if (download != null)
|
||||
download.interrupt();
|
||||
// TODO: Re-implement...
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
@ -1155,23 +1129,9 @@ public class AppDetails extends ListActivity {
|
||||
pd.dismiss();
|
||||
pd = null;
|
||||
}
|
||||
// Cancel any scheduled updates so that we don't
|
||||
// accidentally recreate the progress dialog.
|
||||
stopUpdates();
|
||||
}
|
||||
}
|
||||
|
||||
// Repeatedly run updateProgress() until it's finished.
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
if (download == null)
|
||||
return;
|
||||
boolean finished = updateProgress();
|
||||
if (finished)
|
||||
download = null;
|
||||
else
|
||||
sendMessageDelayed(obtainMessage(), 50);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
|
@ -10,15 +10,28 @@ public abstract class Downloader {
|
||||
private OutputStream outputStream;
|
||||
private ProgressListener progressListener = null;
|
||||
private ProgressListener.Event progressEvent = null;
|
||||
private final File outputFile;
|
||||
private File outputFile;
|
||||
|
||||
public static final int EVENT_PROGRESS = 1;
|
||||
|
||||
public abstract InputStream inputStream() throws IOException;
|
||||
|
||||
// The context is required for opening the file to write to.
|
||||
public Downloader(String destFile, Context ctx)
|
||||
throws FileNotFoundException, MalformedURLException {
|
||||
outputStream = ctx.openFileOutput(destFile, Context.MODE_PRIVATE);
|
||||
outputFile = new File(ctx.getFilesDir() + File.separator + destFile);
|
||||
this(new File(ctx.getFilesDir() + File.separator + destFile));
|
||||
}
|
||||
|
||||
// The context is required for opening the file to write to.
|
||||
public Downloader(Context ctx) throws IOException {
|
||||
this(File.createTempFile("dl-", "", ctx.getCacheDir()));
|
||||
}
|
||||
|
||||
public Downloader(File destFile)
|
||||
throws FileNotFoundException, MalformedURLException {
|
||||
// http://developer.android.com/guide/topics/data/data-storage.html#InternalCache
|
||||
outputFile = destFile;
|
||||
outputStream = new FileOutputStream(outputFile);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -26,10 +39,7 @@ public abstract class Downloader {
|
||||
* you are done*.
|
||||
* @see org.fdroid.fdroid.net.Downloader#getFile()
|
||||
*/
|
||||
public Downloader(Context ctx) throws IOException {
|
||||
// http://developer.android.com/guide/topics/data/data-storage.html#InternalCache
|
||||
outputFile = File.createTempFile("dl-", "", ctx.getCacheDir());
|
||||
outputStream = new FileOutputStream(outputFile);
|
||||
public Downloader(File destFile, Context ctx) throws IOException {
|
||||
}
|
||||
|
||||
public Downloader(OutputStream output)
|
||||
@ -38,6 +48,11 @@ public abstract class Downloader {
|
||||
outputFile = null;
|
||||
}
|
||||
|
||||
public void setProgressListener(ProgressListener listener) {
|
||||
this.progressListener = listener;
|
||||
this.progressEvent = new ProgressListener.Event(EVENT_PROGRESS, totalDownloadSize());
|
||||
}
|
||||
|
||||
public void setProgressListener(ProgressListener progressListener,
|
||||
ProgressListener.Event progressEvent) {
|
||||
this.progressListener = progressListener;
|
||||
@ -61,7 +76,7 @@ public abstract class Downloader {
|
||||
|
||||
protected abstract int totalDownloadSize();
|
||||
|
||||
public void download() throws IOException {
|
||||
protected void download() throws IOException {
|
||||
setupProgressListener();
|
||||
InputStream input = null;
|
||||
try {
|
||||
|
@ -24,6 +24,13 @@ public class HttpDownloader extends Downloader {
|
||||
sourceUrl = new URL(source);
|
||||
}
|
||||
|
||||
// The context is required for opening the file to write to.
|
||||
public HttpDownloader(String source, File destFile)
|
||||
throws FileNotFoundException, MalformedURLException {
|
||||
super(destFile);
|
||||
sourceUrl = new URL(source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads to a temporary file, which *you must delete yourself when
|
||||
* you are done*.
|
||||
|
Loading…
x
Reference in New Issue
Block a user