Better handle download errors from DownloadManager

Rebased and squashed from !141.
This commit is contained in:
Toby Kurien 2015-09-23 22:23:41 -07:00 committed by Daniel Martí
parent 6eddfb8b97
commit 2642c14cb7
3 changed files with 88 additions and 22 deletions

View File

@ -344,6 +344,7 @@
<string name="uninstall_update_confirm">Do you want to replace this app with the factory version?</string>
<string name="uninstall_confirm">Do you want to uninstall this app?</string>
<string name="tap_to_install">Download completed, tap to install</string>
<string name="download_error">Download unsuccessful</string>
<string name="perms_new_perm_prefix">New: </string>
<string name="perms_description_app">Provided by %1$s.</string>

View File

@ -12,7 +12,9 @@ import android.os.Build;
import android.os.ParcelFileDescriptor;
import android.support.v4.content.LocalBroadcastManager;
import android.text.TextUtils;
import android.util.Log;
import org.fdroid.fdroid.R;
import org.fdroid.fdroid.Utils;
import java.io.File;
@ -68,9 +70,22 @@ public class AsyncDownloaderFromAndroid implements AsyncDownloader {
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
if ((downloadManagerId = isDownloadComplete(context, uniqueDownloadId)) > 0) {
// clear the notification
// clear the download
dm.remove(downloadManagerId);
try {
@ -313,6 +328,35 @@ public class AsyncDownloaderFromAndroid implements AsyncDownloader {
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
} else {
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
*/

View File

@ -1,5 +1,6 @@
package org.fdroid.fdroid.receiver;
import android.annotation.TargetApi;
import android.app.DownloadManager;
import android.app.Notification;
import android.app.NotificationManager;
@ -7,16 +8,20 @@ import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
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.AsyncDownloader;
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) {
@ -30,28 +35,18 @@ public class DownloadManagerReceiver extends BroadcastReceiver {
}
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);
int status = AsyncDownloaderFromAndroid.validDownload(context, downloadId);
if (status == 0) {
// successful download
showNotification(context, appId, intent, downloadId, R.string.tap_to_install);
} else {
// download failed!
showNotification(context, appId, intent, downloadId, R.string.download_error);
PendingIntent pi = PendingIntent.getActivity(
context, 1, appDetails, PendingIntent.FLAG_ONE_SHOT);
// launch LocalRepoActivity if the user selects this notification
String downloadTitle = AsyncDownloaderFromAndroid.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);
// 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);
@ -62,4 +57,30 @@ public class DownloadManagerReceiver extends BroadcastReceiver {
context.startActivity(appDetails);
}
}
private void showNotification(Context context, String appId, 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.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 = 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);
}
}