diff --git a/F-Droid/res/values/strings.xml b/F-Droid/res/values/strings.xml
index 737cbdc04..5326ce92c 100644
--- a/F-Droid/res/values/strings.xml
+++ b/F-Droid/res/values/strings.xml
@@ -389,6 +389,7 @@
this may cost you money
Do you want to replace this app with the factory version?
Do you want to uninstall this app?
+ Download completed, tap to install
NEW:
Provided by %1$s.
diff --git a/F-Droid/src/org/fdroid/fdroid/net/AsyncDownloader.java b/F-Droid/src/org/fdroid/fdroid/net/AsyncDownloader.java
index 57046e0c1..0f49c3e1d 100644
--- a/F-Droid/src/org/fdroid/fdroid/net/AsyncDownloader.java
+++ b/F-Droid/src/org/fdroid/fdroid/net/AsyncDownloader.java
@@ -10,6 +10,8 @@ import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.ParcelFileDescriptor;
+import android.support.v4.content.LocalBroadcastManager;
+import android.util.Log;
import org.fdroid.fdroid.AppDetails;
import org.fdroid.fdroid.ProgressListener;
@@ -68,6 +70,7 @@ public class AsyncDownloader extends AsyncDownloadWrapper {
@Override
public void download() {
+ // Check if the download is complete
if ((downloadId = isDownloadComplete(context, appId)) > 0) {
// clear the notification
dm.remove(downloadId);
@@ -80,19 +83,25 @@ public class AsyncDownloader extends AsyncDownloadWrapper {
} catch (IOException e) {
listener.onErrorDownloading(e.getLocalizedMessage());
}
-
return;
}
- downloadId = isDownloading(context, appId);
- if (downloadId >= 0) return;
+ // Check if the download is still in progress
+ if (downloadId < 0) {
+ downloadId = isDownloading(context, appId);
+ }
- // set up download request
- DownloadManager.Request request = new DownloadManager.Request(Uri.parse(remoteAddress));
- request.setTitle(appName);
- request.setDescription(appId); // we will retrieve this later from the description field
+ // Start a new download
+ if (downloadId < 0) {
+ // set up download request
+ DownloadManager.Request request = new DownloadManager.Request(Uri.parse(remoteAddress));
+ request.setTitle(appName);
+ request.setDescription(appId); // we will retrieve this later from the description field
+ this.downloadId = dm.enqueue(request);
+ }
- this.downloadId = dm.enqueue(request);
+ context.registerReceiver(receiver,
+ new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
}
/**
@@ -161,6 +170,12 @@ public class AsyncDownloader extends AsyncDownloadWrapper {
@Override
public void attemptCancel(boolean userRequested) {
+ try {
+ context.unregisterReceiver(receiver);
+ } catch (Exception e) {
+ // ignore if receiver already unregistered
+ }
+
if (userRequested && downloadId >= 0) {
dm.remove(downloadId);
}
@@ -191,6 +206,31 @@ public class AsyncDownloader extends AsyncDownloadWrapper {
return null;
}
+ /**
+ * Extract the download title from a given download id.
+ * @param context
+ * @param downloadId
+ * @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()) {
+ // we use the description column to store the app id
+ int columnIndex = c.getColumnIndex(DownloadManager.COLUMN_TITLE);
+ return c.getString(columnIndex);
+ }
+ } finally {
+ c.close();
+ }
+
+ return null;
+ }
+
/**
* Get the downloadId from an Intent sent by the DownloadManagerReceiver
* @param intent
@@ -267,4 +307,29 @@ public class AsyncDownloader extends AsyncDownloadWrapper {
return -1;
}
+
+ /**
+ * Broadcast receiver to listen for ACTION_DOWNLOAD_COMPLETE broadcasts
+ */
+ 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 appId = getAppId(context, dId);
+ if (listener != null && dId == downloadId && appId != 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/F-Droid/src/org/fdroid/fdroid/receiver/DownloadManagerReceiver.java b/F-Droid/src/org/fdroid/fdroid/receiver/DownloadManagerReceiver.java
index 0a689b40d..0194a0aaf 100644
--- a/F-Droid/src/org/fdroid/fdroid/receiver/DownloadManagerReceiver.java
+++ b/F-Droid/src/org/fdroid/fdroid/receiver/DownloadManagerReceiver.java
@@ -1,15 +1,21 @@
package org.fdroid.fdroid.receiver;
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.v4.app.NotificationCompat;
import org.fdroid.fdroid.AppDetails;
+import org.fdroid.fdroid.R;
import org.fdroid.fdroid.net.AsyncDownloader;
/**
- * Receive notifications from the Android DownloadManager
+ * Receive notifications from the Android DownloadManager and pass them onto the
+ * AppDetails activity
*/
public class DownloadManagerReceiver extends BroadcastReceiver {
@Override
@@ -18,12 +24,42 @@ public class DownloadManagerReceiver extends BroadcastReceiver {
long downloadId = AsyncDownloader.getDownloadId(intent);
String appId = AsyncDownloader.getAppId(context, downloadId);
- // pass the download manager broadcast 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, appId);
- context.startActivity(appDetails);
+ if (appId == null) {
+ // bogus broadcast (e.g. download cancelled, but system sent a DOWNLOAD_COMPLETE)
+ return;
+ }
+
+ if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(intent.getAction())) {
+ // 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.putExtras(intent.getExtras());
+ appDetails.putExtra(AppDetails.EXTRA_APPID, appId);
+
+ PendingIntent pi = PendingIntent.getActivity(
+ context, 1, appDetails, PendingIntent.FLAG_ONE_SHOT);
+
+ // launch LocalRepoActivity if the user selects this notification
+ String downloadTitle = AsyncDownloader.getDownloadTitle(context, downloadId);
+ Notification notif = new NotificationCompat.Builder(context)
+ .setContentTitle(downloadTitle)
+ .setContentText(context.getString(R.string.tap_to_install))
+ .setSmallIcon(R.drawable.ic_stat_notify)
+ .setContentIntent(pi)
+ .setAutoCancel(true)
+ .build();
+
+ NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+ nm.notify((int)downloadId, notif);
+ } 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, appId);
+ context.startActivity(appDetails);
+ }
}
}