diff --git a/app/build.gradle b/app/build.gradle
index 5fea53e9c..04108e0da 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -19,6 +19,7 @@ dependencies {
compile 'eu.chainfire:libsuperuser:1.0.0.201602271131'
compile 'cc.mvdan.accesspoint:library:0.1.3'
compile 'info.guardianproject.netcipher:netcipher:1.2.1'
+ compile 'commons-io:commons-io:2.4'
compile 'commons-net:commons-net:3.4'
compile 'org.openhab.jmdns:jmdns:3.4.2'
compile('ch.acra:acra:4.8.2') {
@@ -72,6 +73,7 @@ if (!hasProperty('sourceDeps')) {
'com.google.zxing:core:b4d82452e7a6bf6ec2698904b332431717ed8f9a850224f295aec89de80f2259',
'eu.chainfire:libsuperuser:018344ff19ee94d252c14b4a503ee8b519184db473a5af83513f5837c413b128',
'cc.mvdan.accesspoint:library:dc89a085d6bc40381078b8dd7776b12bde0dbaf8ffbcddb17ec4ebc3edecc7ba',
+ 'commons-io:commons-io:cc6a41dc3eaacc9e440a6bd0d2890b20d36b4ee408fe2d67122f328bb6e01581',
'commons-net:commons-net:38cf2eca826b8bcdb236fc1f2e79e0c6dd8e7e0f5c44a3b8e839a1065b2fbe2e',
'info.guardianproject.netcipher:netcipher:611ec5bde9d799fd57e1efec5c375f9f460de2cdda98918541decc9a7d02f2ad',
'org.openhab.jmdns:jmdns:7a4b34b5606bbd2aff7fdfe629edcb0416fccd367fb59a099f210b9aba4f0bce',
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index df55f5589..61ed38012 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -437,14 +437,6 @@
- * This is useful to wrap ServletInputStreams. The ServletInputStream will block
- * if you try to read content from it that isn't there, because it doesn't know
- * whether the content hasn't arrived yet or whether the content has finished.
- * So, one of these, initialized with the Content-length sent in the
- * ServletInputStream's header, will stop it blocking, providing it's been sent
- * with a correct content length.
- *
- * @version $Id: BoundedInputStream.java 1307462 2012-03-30 15:13:11Z ggregory $
- * @since 2.0
- */
-public class BoundedInputStream extends InputStream {
-
- /** the wrapped input stream */
- private final InputStream in;
-
- /** the max length to provide */
- private final long max;
-
- /** the number of bytes already returned */
- private long pos;
-
- /** the marked position */
- private long mark = -1;
-
- /** flag if close shoud be propagated */
- private boolean propagateClose = true;
-
- /**
- * Creates a new BoundedInputStream
that wraps the given input
- * stream and limits it to a certain size.
- *
- * @param in The wrapped input stream
- * @param size The maximum number of bytes to return
- */
- public BoundedInputStream(InputStream in, long size) {
- // Some badly designed methods - eg the servlet API - overload length
- // such that "-1" means stream finished
- this.max = size;
- this.in = in;
- }
-
- /**
- * Creates a new BoundedInputStream
that wraps the given input
- * stream and is unlimited.
- *
- * @param in The wrapped input stream
- */
- public BoundedInputStream(InputStream in) {
- this(in, -1);
- }
-
- /**
- * Invokes the delegate's read()
method if
- * the current position is less than the limit.
- * @return the byte read or -1 if the end of stream or
- * the limit has been reached.
- * @throws IOException if an I/O error occurs
- */
- @Override
- public int read() throws IOException {
- if (max >= 0 && pos >= max) {
- return -1;
- }
- int result = in.read();
- pos++;
- return result;
- }
-
- /**
- * Invokes the delegate's read(byte[])
method.
- * @param b the buffer to read the bytes into
- * @return the number of bytes read or -1 if the end of stream or
- * the limit has been reached.
- * @throws IOException if an I/O error occurs
- */
- @Override
- public int read(byte[] b) throws IOException {
- return this.read(b, 0, b.length);
- }
-
- /**
- * Invokes the delegate's read(byte[], int, int)
method.
- * @param b the buffer to read the bytes into
- * @param off The start offset
- * @param len The number of bytes to read
- * @return the number of bytes read or -1 if the end of stream or
- * the limit has been reached.
- * @throws IOException if an I/O error occurs
- */
- @Override
- public int read(byte[] b, int off, int len) throws IOException {
- if (max >= 0 && pos >= max) {
- return -1;
- }
- long maxRead = max >= 0 ? Math.min(len, max - pos) : len;
- int bytesRead = in.read(b, off, (int) maxRead);
-
- if (bytesRead == -1) {
- return -1;
- }
-
- pos += bytesRead;
- return bytesRead;
- }
-
- /**
- * Invokes the delegate's skip(long)
method.
- * @param n the number of bytes to skip
- * @return the actual number of bytes skipped
- * @throws IOException if an I/O error occurs
- */
- @Override
- public long skip(long n) throws IOException {
- long toSkip = max >= 0 ? Math.min(n, max - pos) : n;
- long skippedBytes = in.skip(toSkip);
- pos += skippedBytes;
- return skippedBytes;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public int available() throws IOException {
- if (max >= 0 && pos >= max) {
- return 0;
- }
- return in.available();
- }
-
- /**
- * Invokes the delegate's toString()
method.
- * @return the delegate's toString()
- */
- @Override
- public String toString() {
- return in.toString();
- }
-
- /**
- * Invokes the delegate's close()
method
- * if {@link #isPropagateClose()} is {@code true}.
- * @throws IOException if an I/O error occurs
- */
- @Override
- public void close() throws IOException {
- if (propagateClose) {
- in.close();
- }
- }
-
- /**
- * Invokes the delegate's reset()
method.
- * @throws IOException if an I/O error occurs
- */
- @Override
- public synchronized void reset() throws IOException {
- in.reset();
- pos = mark;
- }
-
- /**
- * Invokes the delegate's mark(int)
method.
- * @param readlimit read ahead limit
- */
- @Override
- public synchronized void mark(int readlimit) {
- in.mark(readlimit);
- mark = pos;
- }
-
- /**
- * Invokes the delegate's markSupported()
method.
- * @return true if mark is supported, otherwise false
- */
- @Override
- public boolean markSupported() {
- return in.markSupported();
- }
-
- /**
- * Indicates whether the {@link #close()} method
- * should propagate to the underling {@link InputStream}.
- *
- * @return {@code true} if calling {@link #close()}
- * propagates to the close()
method of the
- * underlying stream or {@code false} if it does not.
- */
- public boolean isPropagateClose() {
- return propagateClose;
- }
-
- /**
- * Set whether the {@link #close()} method
- * should propagate to the underling {@link InputStream}.
- *
- * @param propagateClose {@code true} if calling
- * {@link #close()} propagates to the close()
- * method of the underlying stream or
- * {@code false} if it does not.
- */
- public void setPropagateClose(boolean propagateClose) {
- this.propagateClose = propagateClose;
- }
-}
diff --git a/app/src/main/java/org/fdroid/fdroid/AppDetails.java b/app/src/main/java/org/fdroid/fdroid/AppDetails.java
index 9617cdf2c..6785e52ea 100644
--- a/app/src/main/java/org/fdroid/fdroid/AppDetails.java
+++ b/app/src/main/java/org/fdroid/fdroid/AppDetails.java
@@ -92,7 +92,6 @@ 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.ApkDownloader;
-import org.fdroid.fdroid.net.AsyncDownloaderFromAndroid;
import org.fdroid.fdroid.net.Downloader;
import java.io.File;
@@ -434,18 +433,6 @@ public class AppDetails extends AppCompatActivity implements ProgressListener, A
}
localBroadcastManager = LocalBroadcastManager.getInstance(this);
-
- // Check if a download is running for this app
- if (AsyncDownloaderFromAndroid.isDownloading(this, app.packageName) >= 0) {
- // call install() to re-setup the listeners and downloaders
- // the AsyncDownloader will not restart the download since the download is running,
- // and thus the version we pass to install() is not important
- refreshHeader();
- refreshApkList();
- final Apk apkToInstall = ApkProvider.Helper.find(this, app.packageName, app.suggestedVercode);
- install(apkToInstall);
- }
-
}
@Override
diff --git a/app/src/main/java/org/fdroid/fdroid/RepoUpdater.java b/app/src/main/java/org/fdroid/fdroid/RepoUpdater.java
index e1fd27d9c..f38c4c3df 100644
--- a/app/src/main/java/org/fdroid/fdroid/RepoUpdater.java
+++ b/app/src/main/java/org/fdroid/fdroid/RepoUpdater.java
@@ -102,7 +102,7 @@ public class RepoUpdater {
repo.getCredentials()
);
downloader.setCacheTag(repo.lastetag);
- downloader.downloadUninterrupted();
+ downloader.download();
if (downloader.isCached()) {
// The index is unchanged since we last read it. We just mark
@@ -118,6 +118,9 @@ public class RepoUpdater {
}
throw new UpdateException(repo, "Error getting index file", e);
+ } catch (InterruptedException e) {
+ // ignored if canceled, the local database just won't be updated
+ e.printStackTrace();
}
return downloader;
}
diff --git a/app/src/main/java/org/fdroid/fdroid/net/ApkDownloader.java b/app/src/main/java/org/fdroid/fdroid/net/ApkDownloader.java
index 7698c2ccd..0fa6811b6 100644
--- a/app/src/main/java/org/fdroid/fdroid/net/ApkDownloader.java
+++ b/app/src/main/java/org/fdroid/fdroid/net/ApkDownloader.java
@@ -51,15 +51,12 @@ public class ApkDownloader implements AsyncDownloader.Listener {
private static final String TAG = "ApkDownloader";
public static final String EVENT_APK_DOWNLOAD_COMPLETE = "apkDownloadComplete";
- public static final String EVENT_APK_DOWNLOAD_CANCELLED = "apkDownloadCancelled";
public static final String EVENT_ERROR = "apkDownloadError";
public static final String ACTION_STATUS = "apkDownloadStatus";
- public static final String EXTRA_TYPE = "apkDownloadStatusType";
public static final String EXTRA_URL = "apkDownloadUrl";
public static final int ERROR_HASH_MISMATCH = 101;
- public static final int ERROR_DOWNLOAD_FAILED = 102;
private static final String EVENT_SOURCE_ID = "sourceId";
private static long downloadIdCounter;
@@ -197,7 +194,7 @@ public class ApkDownloader implements AsyncDownloader.Listener {
Utils.debugLog(TAG, "Downloading apk from " + remoteAddress + " to " + localFile);
try {
- dlWrapper = DownloaderFactory.createAsync(context, remoteAddress, localFile, app.name + " " + curApk.version, curApk.packageName, credentials, this);
+ dlWrapper = DownloaderFactory.createAsync(context, remoteAddress, localFile, credentials, this);
dlWrapper.download();
return true;
} catch (IOException e) {
@@ -228,7 +225,6 @@ public class ApkDownloader implements AsyncDownloader.Listener {
Intent intent = new Intent(ACTION_STATUS);
intent.putExtras(event.getData());
- intent.putExtra(EXTRA_TYPE, event.type);
intent.putExtra(EXTRA_URL, Utils.getApkUrl(repoAddress, curApk));
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
}
@@ -236,7 +232,6 @@ public class ApkDownloader implements AsyncDownloader.Listener {
@Override
public void onErrorDownloading(String localisedExceptionDetails) {
Log.e(TAG, "Download failed: " + localisedExceptionDetails);
- sendError(ERROR_DOWNLOAD_FAILED);
delete(localFile);
}
@@ -261,11 +256,6 @@ public class ApkDownloader implements AsyncDownloader.Listener {
prepareApkFileAndSendCompleteMessage();
}
- @Override
- public void onDownloadCancelled() {
- sendMessage(EVENT_APK_DOWNLOAD_CANCELLED);
- }
-
@Override
public void onProgress(Event event) {
sendProgressEvent(event);
diff --git a/app/src/main/java/org/fdroid/fdroid/net/AsyncDownloadWrapper.java b/app/src/main/java/org/fdroid/fdroid/net/AsyncDownloadWrapper.java
index 84618bb7f..ff559e201 100644
--- a/app/src/main/java/org/fdroid/fdroid/net/AsyncDownloadWrapper.java
+++ b/app/src/main/java/org/fdroid/fdroid/net/AsyncDownloadWrapper.java
@@ -12,7 +12,6 @@ class AsyncDownloadWrapper extends Handler implements AsyncDownloader {
private static final String TAG = "AsyncDownloadWrapper";
private static final int MSG_DOWNLOAD_COMPLETE = 2;
- private static final int MSG_DOWNLOAD_CANCELLED = 3;
private static final int MSG_ERROR = 4;
private static final String MSG_DATA = "data";
@@ -61,9 +60,6 @@ class AsyncDownloadWrapper extends Handler implements AsyncDownloader {
case MSG_DOWNLOAD_COMPLETE:
listener.onDownloadComplete();
break;
- case MSG_DOWNLOAD_CANCELLED:
- listener.onDownloadCancelled();
- break;
case MSG_ERROR:
listener.onErrorDownloading(message.getData().getString(MSG_DATA));
break;
@@ -77,7 +73,7 @@ class AsyncDownloadWrapper extends Handler implements AsyncDownloader {
downloader.download();
sendMessage(MSG_DOWNLOAD_COMPLETE);
} catch (InterruptedException e) {
- sendMessage(MSG_DOWNLOAD_CANCELLED);
+ // ignored
} catch (IOException e) {
Log.e(TAG, "I/O exception in download thread", e);
Bundle data = new Bundle(1);
diff --git a/app/src/main/java/org/fdroid/fdroid/net/AsyncDownloader.java b/app/src/main/java/org/fdroid/fdroid/net/AsyncDownloader.java
index d0cfc77a4..0c70895f9 100644
--- a/app/src/main/java/org/fdroid/fdroid/net/AsyncDownloader.java
+++ b/app/src/main/java/org/fdroid/fdroid/net/AsyncDownloader.java
@@ -8,8 +8,6 @@ public interface AsyncDownloader {
void onErrorDownloading(String localisedExceptionDetails);
void onDownloadComplete();
-
- void onDownloadCancelled();
}
int getBytesRead();
diff --git a/app/src/main/java/org/fdroid/fdroid/net/AsyncDownloaderFromAndroid.java b/app/src/main/java/org/fdroid/fdroid/net/AsyncDownloaderFromAndroid.java
deleted file mode 100644
index 409e56e6f..000000000
--- a/app/src/main/java/org/fdroid/fdroid/net/AsyncDownloaderFromAndroid.java
+++ /dev/null
@@ -1,396 +0,0 @@
-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.database.Cursor;
-import android.net.Uri;
-import android.os.Build;
-import android.os.ParcelFileDescriptor;
-import android.support.v4.content.LocalBroadcastManager;
-import android.text.TextUtils;
-
-import org.fdroid.fdroid.R;
-import org.fdroid.fdroid.Utils;
-
-import java.io.File;
-import java.io.FileDescriptor;
-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 AsyncDownloaderFromAndroid implements AsyncDownloader {
- private final Context context;
- private final DownloadManager dm;
- private final LocalBroadcastManager localBroadcastManager;
- private final File localFile;
- private final String remoteAddress;
- private final String downloadTitle;
- private final String uniqueDownloadId;
- private final Listener listener;
- private boolean isCancelled;
-
- private long downloadManagerId = -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.
- */
- public AsyncDownloaderFromAndroid(Context context, Listener listener, String downloadTitle, String downloadId, String remoteAddress, File localFile) {
- this.context = context;
- this.uniqueDownloadId = downloadId;
- this.remoteAddress = remoteAddress;
- this.listener = listener;
- this.localFile = localFile;
-
- if (TextUtils.isEmpty(downloadTitle)) {
- this.downloadTitle = remoteAddress;
- } else {
- this.downloadTitle = downloadTitle;
- }
-
- dm = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
- localBroadcastManager = LocalBroadcastManager.getInstance(context);
- }
-
- @Override
- public void download() {
- isCancelled = false;
-
- // check if download failed
- if (downloadManagerId >= 0) {
- int status = validDownload(context, downloadManagerId);
- if (status > 0) {
- // error downloading
- dm.remove(downloadManagerId);
- if (listener != null) {
- listener.onErrorDownloading(context.getString(R.string.download_error));
- }
- return;
- }
- }
-
- // Check if the download is complete
- downloadManagerId = isDownloadComplete(context, uniqueDownloadId);
- if (downloadManagerId > 0) {
- // clear the download
- dm.remove(downloadManagerId);
-
- try {
- // write the downloaded file to the expected location
- ParcelFileDescriptor fd = dm.openDownloadedFile(downloadManagerId);
- copyFile(fd.getFileDescriptor(), localFile);
- listener.onDownloadComplete();
- } catch (IOException e) {
- listener.onErrorDownloading(e.getLocalizedMessage());
- }
- return;
- }
-
- // Check if the download is still in progress
- if (downloadManagerId < 0) {
- downloadManagerId = isDownloading(context, uniqueDownloadId);
- }
-
- // Start a new download
- if (downloadManagerId < 0) {
- // set up download request
- DownloadManager.Request request = new DownloadManager.Request(Uri.parse(remoteAddress));
- request.setTitle(downloadTitle);
- request.setDescription(uniqueDownloadId); // we will retrieve this later from the description field
- this.downloadManagerId = dm.enqueue(request);
- }
-
- context.registerReceiver(receiver,
- new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
-
- Thread progressThread = new Thread() {
- @Override
- public void run() {
- while (!isCancelled && isDownloading(context, uniqueDownloadId) >= 0) {
- try {
- Thread.sleep(1000);
- } catch (Exception e) {
- // ignore
- }
- sendProgress(getBytesRead(), getTotalBytes());
- }
- }
- };
- progressThread.start();
- }
-
- /**
- * Copy input file to output file
- * @throws IOException
- */
- private void copyFile(FileDescriptor inputFile, File outputFile) throws IOException {
- InputStream input = null;
- OutputStream output = null;
- try {
- input = new FileInputStream(inputFile);
- output = new FileOutputStream(outputFile);
- Utils.copy(input, output);
- } finally {
- Utils.closeQuietly(output);
- Utils.closeQuietly(input);
- }
- }
-
- @Override
- public int getBytesRead() {
- if (downloadManagerId < 0) return 0;
-
- DownloadManager.Query query = new DownloadManager.Query();
- query.setFilterById(downloadManagerId);
- Cursor c = dm.query(query);
-
- try {
- if (c.moveToFirst()) {
- // we use the description column to store the unique id of this download
- int columnIndex = c.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR);
- return c.getInt(columnIndex);
- }
- } finally {
- c.close();
- }
-
- return 0;
- }
-
- @Override
- public int getTotalBytes() {
- if (downloadManagerId < 0) return 0;
-
- DownloadManager.Query query = new DownloadManager.Query();
- query.setFilterById(downloadManagerId);
- Cursor c = dm.query(query);
-
- try {
- if (c.moveToFirst()) {
- // we use the description column to store the unique id for this download
- int columnIndex = c.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES);
- return c.getInt(columnIndex);
- }
- } finally {
- c.close();
- }
-
- return 0;
- }
-
- private void sendProgress(int bytesRead, int totalBytes) {
- Intent intent = new Intent(Downloader.LOCAL_ACTION_PROGRESS);
- intent.putExtra(Downloader.EXTRA_ADDRESS, remoteAddress);
- intent.putExtra(Downloader.EXTRA_BYTES_READ, bytesRead);
- intent.putExtra(Downloader.EXTRA_TOTAL_BYTES, totalBytes);
- localBroadcastManager.sendBroadcast(intent);
- }
-
- @Override
- public void attemptCancel(boolean userRequested) {
- isCancelled = true;
- try {
- context.unregisterReceiver(receiver);
- } catch (Exception e) {
- // ignore if receiver already unregistered
- }
-
- if (userRequested && downloadManagerId >= 0) {
- dm.remove(downloadManagerId);
- }
- }
-
- /**
- * Extract the uniqueDownloadId from a given download id.
- * @return - uniqueDownloadId or null if not found
- */
- public static String getDownloadId(Context context, long downloadId) {
- DownloadManager dm = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
- DownloadManager.Query query = new DownloadManager.Query();
- query.setFilterById(downloadId);
- Cursor c = dm.query(query);
-
- try {
- if (c.moveToFirst()) {
- // we use the description column to store the unique id for this download
- int columnIndex = c.getColumnIndex(DownloadManager.COLUMN_DESCRIPTION);
- return c.getString(columnIndex);
- }
- } finally {
- c.close();
- }
-
- return null;
- }
-
- /**
- * Extract the download title from a given download id.
- * @return - title or null if not found
- */
- public static String getDownloadTitle(Context context, long downloadId) {
- DownloadManager dm = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
- DownloadManager.Query query = new DownloadManager.Query();
- query.setFilterById(downloadId);
- Cursor c = dm.query(query);
-
- try {
- if (c.moveToFirst()) {
- int columnIndex = c.getColumnIndex(DownloadManager.COLUMN_TITLE);
- return c.getString(columnIndex);
- }
- } finally {
- c.close();
- }
-
- return null;
- }
-
- /**
- * Get the downloadManagerId from an Intent sent by the DownloadManagerReceiver
- */
- @TargetApi(Build.VERSION_CODES.HONEYCOMB)
- public static long getDownloadId(Intent intent) {
- if (intent != null) {
- if (intent.hasExtra(DownloadManager.EXTRA_DOWNLOAD_ID)) {
- // we have been passed a DownloadManager download id, so get the unique id for that download
- return intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
- }
-
- if (intent.hasExtra(DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS)) {
- // we have been passed multiple download id's - just return the first one
- long[] downloadIds = intent.getLongArrayExtra(DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS);
- if (downloadIds != null && downloadIds.length > 0) {
- return downloadIds[0];
- }
- }
- }
-
- return -1;
- }
-
- /**
- * Check if a download is running for the specified id
- * @return -1 if not downloading, else the id from the Android download manager
- */
- public static long isDownloading(Context context, String uniqueDownloadId) {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.GINGERBREAD) {
- // TODO: remove. This is necessary because AppDetails calls this
- // static method directly, without using the whole pipe through
- // DownloaderFactory. This shouldn't be called at all on android-8
- // devices, since AppDetails is really using the old downloader,
- // not this one.
- return -1;
- }
- DownloadManager dm = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
- DownloadManager.Query query = new DownloadManager.Query();
- Cursor c = dm.query(query);
- if (c == null) {
- // TODO: same as above.
- return -1;
- }
- int columnUniqueDownloadId = c.getColumnIndex(DownloadManager.COLUMN_DESCRIPTION);
- int columnId = c.getColumnIndex(DownloadManager.COLUMN_ID);
-
- try {
- while (c.moveToNext()) {
- if (uniqueDownloadId.equals(c.getString(columnUniqueDownloadId))) {
- return c.getLong(columnId);
- }
- }
- } finally {
- c.close();
- }
-
- return -1;
- }
-
- /**
- * Check if a specific download is complete.
- * @return -1 if download is not complete, otherwise the download id
- */
- private static long isDownloadComplete(Context context, String uniqueDownloadId) {
- DownloadManager dm = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
- DownloadManager.Query query = new DownloadManager.Query();
- query.setFilterByStatus(DownloadManager.STATUS_SUCCESSFUL);
- Cursor c = dm.query(query);
- int columnUniqueDownloadId = c.getColumnIndex(DownloadManager.COLUMN_DESCRIPTION);
- int columnId = c.getColumnIndex(DownloadManager.COLUMN_ID);
-
- try {
- while (c.moveToNext()) {
- if (uniqueDownloadId.equals(c.getString(columnUniqueDownloadId))) {
- return c.getLong(columnId);
- }
- }
- } finally {
- c.close();
- }
-
- return -1;
- }
-
- /**
- * Check if download was valid, see issue
- * http://code.google.com/p/android/issues/detail?id=18462
- * From http://stackoverflow.com/questions/8937817/downloadmanager-action-download-complete-broadcast-receiver-receiving-same-downl
- * @return 0 if successful, -1 if download doesn't exist, else the DownloadManager.ERROR_... code
- */
- public static int validDownload(Context context, long downloadId) {
- //Verify if download is a success
- DownloadManager dm = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
- Cursor c = dm.query(new DownloadManager.Query().setFilterById(downloadId));
-
- try {
- if (c.moveToFirst()) {
- int status = c.getInt(c.getColumnIndex(DownloadManager.COLUMN_STATUS));
-
- if (status == DownloadManager.STATUS_SUCCESSFUL) {
- return 0; // Download is valid, celebrate
- }
- return c.getInt(c.getColumnIndex(DownloadManager.COLUMN_REASON));
- }
- } finally {
- c.close();
- }
-
- return -1; // download doesn't exist
- }
-
- /**
- * Broadcast receiver to listen for ACTION_DOWNLOAD_COMPLETE broadcasts
- */
- private final BroadcastReceiver receiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(intent.getAction())) {
- long dId = getDownloadId(intent);
- String downloadId = getDownloadId(context, dId);
- if (listener != null && dId == AsyncDownloaderFromAndroid.this.downloadManagerId && downloadId != null) {
- // our current download has just completed, so let's throw up install dialog
- // immediately
- try {
- context.unregisterReceiver(receiver);
- } catch (Exception e) {
- // ignore if receiver already unregistered
- }
-
- // call download() to copy the file and start the installer
- download();
- }
- }
- }
- };
-}
diff --git a/app/src/main/java/org/fdroid/fdroid/net/Downloader.java b/app/src/main/java/org/fdroid/fdroid/net/Downloader.java
index b66dcc7ec..e66b793ad 100644
--- a/app/src/main/java/org/fdroid/fdroid/net/Downloader.java
+++ b/app/src/main/java/org/fdroid/fdroid/net/Downloader.java
@@ -91,20 +91,6 @@ public abstract class Downloader {
protected abstract int totalDownloadSize();
- /**
- * Helper function for synchronous downloads (i.e. those *not* using AsyncDownloadWrapper),
- * which don't really want to bother dealing with an InterruptedException.
- * The InterruptedException thrown from download() is there to enable cancelling asynchronous
- * downloads, but regular synchronous downloads cannot be cancelled because download() will
- * block until completed.
- * @throws IOException
- */
- public void downloadUninterrupted() throws IOException {
- try {
- download();
- } catch (InterruptedException ignored) { }
- }
-
public abstract void download() throws IOException, InterruptedException;
public abstract boolean isCached();
diff --git a/app/src/main/java/org/fdroid/fdroid/net/DownloaderFactory.java b/app/src/main/java/org/fdroid/fdroid/net/DownloaderFactory.java
index 2ac93be4d..d50896a43 100644
--- a/app/src/main/java/org/fdroid/fdroid/net/DownloaderFactory.java
+++ b/app/src/main/java/org/fdroid/fdroid/net/DownloaderFactory.java
@@ -1,14 +1,9 @@
package org.fdroid.fdroid.net;
-import android.annotation.TargetApi;
-import android.app.DownloadManager;
import android.content.Context;
import android.content.Intent;
-import android.database.Cursor;
-import android.os.Build;
import android.support.v4.content.LocalBroadcastManager;
-import org.fdroid.fdroid.Utils;
import org.fdroid.fdroid.data.Credentials;
import java.io.File;
@@ -27,29 +22,9 @@ public class DownloaderFactory {
*/
public static Downloader create(Context context, String urlString)
throws IOException {
- return create(context, new URL(urlString));
- }
-
- /**
- * Downloads to a temporary file, which *you must delete yourself when
- * you are done. It is stored in {@link Context#getCacheDir()} and starts
- * with the prefix {@code dl-}.
- */
- public static Downloader create(Context context, URL url)
- throws IOException {
File destFile = File.createTempFile("dl-", "", context.getCacheDir());
destFile.deleteOnExit(); // this probably does nothing, but maybe...
- return create(context, url, destFile);
- }
-
- public static Downloader create(Context context, String urlString, File destFile)
- throws IOException {
- return create(context, new URL(urlString), destFile);
- }
-
- public static Downloader create(Context context, URL url, File destFile)
- throws IOException {
- return create(context, url, destFile, null);
+ return create(context, new URL(urlString), destFile, null);
}
public static Downloader create(Context context, URL url, File destFile, Credentials credentials)
@@ -89,63 +64,13 @@ public class DownloaderFactory {
return "file".equalsIgnoreCase(url.getProtocol());
}
- public static AsyncDownloader createAsync(Context context, String urlString, File destFile, String title, String id, Credentials credentials, AsyncDownloader.Listener listener) throws IOException {
- return createAsync(context, new URL(urlString), destFile, title, id, credentials, listener);
- }
-
- public static AsyncDownloader createAsync(Context context, URL url, File destFile, String title, String id, Credentials credentials, AsyncDownloader.Listener listener)
+ public static AsyncDownloader createAsync(Context context, String urlString, File destFile, Credentials credentials, AsyncDownloader.Listener listener)
throws IOException {
- // To re-enable, fix the following:
- // * https://gitlab.com/fdroid/fdroidclient/issues/445
- // * https://gitlab.com/fdroid/fdroidclient/issues/459
- if (false && canUseDownloadManager(context, url)) {
- Utils.debugLog(TAG, "Using AsyncDownloaderFromAndroid");
- return new AsyncDownloaderFromAndroid(context, listener, title, id, url.toString(), destFile);
- }
- Utils.debugLog(TAG, "Using AsyncDownloadWrapper");
+ URL url = new URL(urlString);
return new AsyncDownloadWrapper(create(context, url, destFile, credentials), listener);
}
private static boolean isOnionAddress(URL url) {
return url.getHost().endsWith(".onion");
}
-
- @TargetApi(Build.VERSION_CODES.GINGERBREAD)
- private static boolean hasDownloadManager(Context context) {
- DownloadManager dm = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
- if (dm == null) {
- // Service was not found
- return false;
- }
- DownloadManager.Query query = new DownloadManager.Query();
- Cursor c = dm.query(query);
- if (c == null) {
- // Download Manager was disabled
- return false;
- }
- c.close();
- return true;
- }
-
- /**
- * Tests to see if we can use Android's DownloadManager to download the APK, instead of
- * a downloader returned from DownloadFactory.
- */
- private static boolean canUseDownloadManager(Context context, URL url) {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
- // No HTTPS support on 2.3, no DownloadManager on 2.2. Don't have
- // 3.0 devices to test on, so require 4.0.
- return false;
- }
- if (isOnionAddress(url)) {
- // We support onion addresses through our own downloader.
- return false;
- }
- if (isBluetoothAddress(url)) {
- // Completely differnet protocol not understood by the download manager.
- return false;
- }
- return hasDownloadManager(context);
- }
-
}
diff --git a/app/src/main/java/org/fdroid/fdroid/receiver/DownloadManagerReceiver.java b/app/src/main/java/org/fdroid/fdroid/receiver/DownloadManagerReceiver.java
deleted file mode 100644
index 1da9af76a..000000000
--- a/app/src/main/java/org/fdroid/fdroid/receiver/DownloadManagerReceiver.java
+++ /dev/null
@@ -1,85 +0,0 @@
-package org.fdroid.fdroid.receiver;
-
-import android.annotation.TargetApi;
-import android.app.DownloadManager;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.support.annotation.StringRes;
-import android.support.v4.app.NotificationCompat;
-
-import org.fdroid.fdroid.AppDetails;
-import org.fdroid.fdroid.R;
-import org.fdroid.fdroid.net.AsyncDownloaderFromAndroid;
-
-/**
- * Receive notifications from the Android DownloadManager and pass them onto the
- * AppDetails activity
- */
-@TargetApi(9)
-public class DownloadManagerReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- // work out the package name to send to the AppDetails Screen
- long downloadId = AsyncDownloaderFromAndroid.getDownloadId(intent);
- String packageName = AsyncDownloaderFromAndroid.getDownloadId(context, downloadId);
-
- if (packageName == null) {
- // bogus broadcast (e.g. download cancelled, but system sent a DOWNLOAD_COMPLETE)
- return;
- }
-
- if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(intent.getAction())) {
- int status = AsyncDownloaderFromAndroid.validDownload(context, downloadId);
- if (status == 0) {
- // successful download
- showNotification(context, packageName, intent, downloadId, R.string.tap_to_install);
- } else {
- // download failed!
- showNotification(context, packageName, intent, downloadId, R.string.download_error);
-
- // clear the download to allow user to download again
- DownloadManager dm = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
- dm.remove(downloadId);
- }
- } else if (DownloadManager.ACTION_NOTIFICATION_CLICKED.equals(intent.getAction())) {
- // pass the notification click onto the AppDetails screen and let it handle it
- Intent appDetails = new Intent(context, AppDetails.class);
- appDetails.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
- appDetails.setAction(intent.getAction());
- appDetails.putExtras(intent.getExtras());
- appDetails.putExtra(AppDetails.EXTRA_APPID, packageName);
- context.startActivity(appDetails);
- }
- }
-
- private void showNotification(Context context, String packageName, Intent intent, long downloadId,
- @StringRes int messageResId) {
- // show a notification the user can click to install the app
- Intent appDetails = new Intent(context, AppDetails.class);
- appDetails.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
- appDetails.setAction(intent.getAction());
- appDetails.putExtra(DownloadManager.EXTRA_DOWNLOAD_ID, downloadId);
- appDetails.putExtra(AppDetails.EXTRA_APPID, packageName);
-
- // set separate pending intents per download id
- PendingIntent pi = PendingIntent.getActivity(
- context, (int) downloadId, appDetails, PendingIntent.FLAG_ONE_SHOT);
-
- // build & show notification
- String downloadTitle = AsyncDownloaderFromAndroid.getDownloadTitle(context, downloadId);
- Notification notif = new NotificationCompat.Builder(context)
- .setContentTitle(downloadTitle)
- .setContentText(context.getString(messageResId))
- .setSmallIcon(R.drawable.ic_stat_notify)
- .setContentIntent(pi)
- .setAutoCancel(true)
- .build();
-
- NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
- nm.notify((int) downloadId, notif);
- }
-}
diff --git a/app/src/main/java/org/fdroid/fdroid/views/swap/SwapAppsView.java b/app/src/main/java/org/fdroid/fdroid/views/swap/SwapAppsView.java
index 2598c76a2..664884a20 100644
--- a/app/src/main/java/org/fdroid/fdroid/views/swap/SwapAppsView.java
+++ b/app/src/main/java/org/fdroid/fdroid/views/swap/SwapAppsView.java
@@ -279,18 +279,8 @@ public class SwapAppsView extends ListView implements
// apkToInstall. This way, we can wait until we receive an incoming intent (if
// at all) and then lazily load the apk to install.
String broadcastUrl = intent.getStringExtra(ApkDownloader.EXTRA_URL);
- if (!TextUtils.equals(Utils.getApkUrl(apk.repoAddress, apk), broadcastUrl)) {
- return;
- }
-
- switch (intent.getStringExtra(ApkDownloader.EXTRA_TYPE)) {
- // Fallthrough for each of these "downloader no longer going" events...
- case ApkDownloader.EVENT_APK_DOWNLOAD_COMPLETE:
- case ApkDownloader.EVENT_APK_DOWNLOAD_CANCELLED:
- case ApkDownloader.EVENT_ERROR:
- case ApkDownloader.EVENT_DATA_ERROR_TYPE:
- resetView();
- break;
+ if (TextUtils.equals(Utils.getApkUrl(apk.repoAddress, apk), broadcastUrl)) {
+ resetView();
}
}
};
diff --git a/app/src/main/java/org/fdroid/fdroid/views/swap/SwapWorkflowActivity.java b/app/src/main/java/org/fdroid/fdroid/views/swap/SwapWorkflowActivity.java
index b5f8ed5c0..a3b521f21 100644
--- a/app/src/main/java/org/fdroid/fdroid/views/swap/SwapWorkflowActivity.java
+++ b/app/src/main/java/org/fdroid/fdroid/views/swap/SwapWorkflowActivity.java
@@ -789,8 +789,6 @@ public class SwapWorkflowActivity extends AppCompatActivity {
case ApkDownloader.EVENT_APK_DOWNLOAD_COMPLETE:
handleDownloadComplete(downloader.localFile(), app.packageName);
break;
- case ApkDownloader.EVENT_ERROR:
- break;
}
}
});