Add retry button to download failed notifications (#6333)

This commit is contained in:
ByteHamster 2023-02-20 23:15:56 +01:00 committed by GitHub
parent c98194f519
commit a5d4864776
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 97 additions and 49 deletions

View File

@ -14,6 +14,7 @@ import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.core.app.NotificationManagerCompat;
import androidx.core.app.ServiceCompat;
import de.danoeh.antennapod.core.R;
@ -82,6 +83,7 @@ public class DownloadService extends Service {
private final ExecutorService downloadEnqueueExecutor;
private final List<DownloadStatus> reportQueue = new ArrayList<>();
private final List<DownloadRequest> failedRequestsForReport = new ArrayList<>();
private DownloadServiceNotification notificationManager;
private final NewEpisodesNotification newEpisodesNotification;
private NotificationUpdater notificationUpdater;
@ -176,11 +178,15 @@ public class DownloadService extends Service {
if (intent != null && intent.hasExtra(EXTRA_REQUESTS)) {
Notification notification = notificationManager.updateNotifications(downloads);
startForeground(R.id.notification_downloading, notification);
NotificationManagerCompat.from(this).cancel(R.id.notification_download_report);
NotificationManagerCompat.from(this).cancel(R.id.notification_auto_download_report);
setupNotificationUpdaterIfNecessary();
downloadEnqueueExecutor.execute(() -> onDownloadQueued(intent));
} else if (intent != null && intent.getBooleanExtra(EXTRA_REFRESH_ALL, false)) {
Notification notification = notificationManager.updateNotifications(downloads);
startForeground(R.id.notification_downloading, notification);
NotificationManagerCompat.from(this).cancel(R.id.notification_download_report);
NotificationManagerCompat.from(this).cancel(R.id.notification_auto_download_report);
setupNotificationUpdaterIfNecessary();
downloadEnqueueExecutor.execute(() -> enqueueAll(intent));
} else if (downloads.size() == 0) {
@ -198,8 +204,9 @@ public class DownloadService extends Service {
boolean showAutoDownloadReport = UserPreferences.showAutoDownloadReport();
if (UserPreferences.showDownloadReport() || showAutoDownloadReport) {
notificationManager.updateReport(reportQueue, showAutoDownloadReport);
notificationManager.updateReport(reportQueue, showAutoDownloadReport, failedRequestsForReport);
reportQueue.clear();
failedRequestsForReport.clear();
}
unregisterReceiver(cancelDownloadReceiver);
@ -283,7 +290,7 @@ public class DownloadService extends Service {
// we create a 'successful' download log if the feed's last refresh failed
List<DownloadStatus> log = DBReader.getFeedDownloadLog(request.getFeedfileId());
if (log.size() > 0 && !log.get(0).isSuccessful()) {
saveDownloadStatus(feedSyncTask.getDownloadStatus());
saveDownloadStatus(feedSyncTask.getDownloadStatus(), downloader.getDownloadRequest());
}
if (!request.isInitiatedByUser()) {
// Was stored in the database before and not initiated manually
@ -296,13 +303,13 @@ public class DownloadService extends Service {
}
} else {
DBWriter.setFeedLastUpdateFailed(request.getFeedfileId(), true);
saveDownloadStatus(feedSyncTask.getDownloadStatus());
saveDownloadStatus(feedSyncTask.getDownloadStatus(), downloader.getDownloadRequest());
}
} else if (type == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
Log.d(TAG, "Handling completed FeedMedia Download");
MediaDownloadedHandler handler = new MediaDownloadedHandler(DownloadService.this, status, request);
handler.run();
saveDownloadStatus(handler.getUpdatedStatus());
saveDownloadStatus(handler.getUpdatedStatus(), downloader.getDownloadRequest());
}
}
@ -321,7 +328,7 @@ public class DownloadService extends Service {
DownloadServiceInterface.get().download(this, false, downloader.getDownloadRequest());
} else {
Log.e(TAG, "Download failed");
saveDownloadStatus(status);
saveDownloadStatus(status, downloader.getDownloadRequest());
new FailedDownloadHandler(downloader.getDownloadRequest()).run();
if (type == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
@ -509,8 +516,11 @@ public class DownloadService extends Service {
*
* @param status the download that is going to be saved
*/
private void saveDownloadStatus(@NonNull DownloadStatus status) {
private void saveDownloadStatus(@NonNull DownloadStatus status, @NonNull DownloadRequest request) {
reportQueue.add(status);
if (!status.isSuccessful() && !status.isCancelled()) {
failedRequestsForReport.add(request);
}
DBWriter.addDownloadStatus(status);
}

View File

@ -16,6 +16,13 @@ public class DownloadServiceInterfaceImpl extends DownloadServiceInterface {
private static final String TAG = "DownloadServiceInterface";
public void download(Context context, boolean cleanupMedia, DownloadRequest... requests) {
Intent intent = makeDownloadIntent(context, cleanupMedia, requests);
if (intent != null) {
ContextCompat.startForegroundService(context, intent);
}
}
public Intent makeDownloadIntent(Context context, boolean cleanupMedia, DownloadRequest... requests) {
ArrayList<DownloadRequest> requestsToSend = new ArrayList<>();
for (DownloadRequest request : requests) {
if (!isDownloadingFile(request.getSource())) {
@ -23,7 +30,7 @@ public class DownloadServiceInterfaceImpl extends DownloadServiceInterface {
}
}
if (requestsToSend.isEmpty()) {
return;
return null;
} else if (requestsToSend.size() > 100) {
if (BuildConfig.DEBUG) {
throw new IllegalArgumentException("Android silently drops intent payloads that are too large");
@ -38,7 +45,7 @@ public class DownloadServiceInterfaceImpl extends DownloadServiceInterface {
if (cleanupMedia) {
launchIntent.putExtra(DownloadService.EXTRA_CLEANUP_MEDIA, true);
}
ContextCompat.startForegroundService(context, launchIntent);
return launchIntent;
}
public void refreshAllFeeds(Context context, boolean initiatedByUser) {

View File

@ -15,6 +15,7 @@ import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.core.util.gui.NotificationUtils;
import de.danoeh.antennapod.net.download.serviceinterface.DownloadRequest;
import de.danoeh.antennapod.net.download.serviceinterface.DownloadServiceInterface;
import de.danoeh.antennapod.ui.appstartintent.DownloadAuthenticationActivityStarter;
import de.danoeh.antennapod.ui.appstartintent.MainActivityStarter;
@ -153,7 +154,8 @@ public class DownloadServiceNotification {
* user about the number of completed downloads. A report will only be
* created if there is at least one failed download excluding images
*/
public void updateReport(List<DownloadStatus> reportQueue, boolean showAutoDownloadReport) {
public void updateReport(List<DownloadStatus> reportQueue, boolean showAutoDownloadReport,
List<DownloadRequest> failedRequests) {
// check if report should be created
boolean createReport = false;
int failedDownloads = 0;
@ -173,48 +175,68 @@ public class DownloadServiceNotification {
}
}
if (createReport) {
Log.d(TAG, "Creating report");
// create notification object
String channelId;
int titleId;
int iconId;
int id;
String content;
PendingIntent intent;
if (failedDownloads == 0) {
// We are generating an auto-download report
channelId = NotificationUtils.CHANNEL_ID_AUTO_DOWNLOAD;
titleId = R.string.auto_download_report_title;
iconId = R.drawable.ic_notification_new;
intent = getAutoDownloadReportNotificationContentIntent(context);
id = R.id.notification_auto_download_report;
content = createAutoDownloadNotificationContent(reportQueue);
} else {
channelId = NotificationUtils.CHANNEL_ID_DOWNLOAD_ERROR;
titleId = R.string.download_report_title;
iconId = R.drawable.ic_notification_sync_error;
intent = getReportNotificationContentIntent(context);
id = R.id.notification_download_report;
content = createFailedDownloadNotificationContent(reportQueue);
}
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, channelId);
builder.setTicker(context.getString(titleId))
.setContentTitle(context.getString(titleId))
.setContentText(content)
.setStyle(new NotificationCompat.BigTextStyle().bigText(content))
.setSmallIcon(iconId)
.setContentIntent(intent)
.setAutoCancel(true);
builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
nm.notify(id, builder.build());
Log.d(TAG, "Download report notification was posted");
} else {
if (!createReport) {
Log.d(TAG, "No report is created");
return;
}
Log.d(TAG, "Creating report");
if (failedDownloads == 0) {
createAutoDownloadReportNotification(reportQueue);
} else {
createDownloadFailedNotification(reportQueue, failedRequests);
}
Log.d(TAG, "Download report notification was posted");
}
private void createAutoDownloadReportNotification(List<DownloadStatus> reportQueue) {
PendingIntent intent = getAutoDownloadReportNotificationContentIntent(context);
String content = createAutoDownloadNotificationContent(reportQueue);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context,
NotificationUtils.CHANNEL_ID_AUTO_DOWNLOAD);
builder.setTicker(context.getString(R.string.auto_download_report_title))
.setContentTitle(context.getString(R.string.auto_download_report_title))
.setContentText(content)
.setStyle(new NotificationCompat.BigTextStyle().bigText(content))
.setSmallIcon(R.drawable.ic_notification_new)
.setContentIntent(intent)
.setAutoCancel(true)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
nm.notify(R.id.notification_auto_download_report, builder.build());
}
private void createDownloadFailedNotification(List<DownloadStatus> reportQueue,
List<DownloadRequest> failedRequests) {
Intent retryIntent = DownloadServiceInterface.get().makeDownloadIntent(context,
false, failedRequests.toArray(new DownloadRequest[0]));
PendingIntent retryPendingIntent = null;
if (retryIntent != null && Build.VERSION.SDK_INT >= 26) {
retryPendingIntent = PendingIntent.getForegroundService(context, R.id.pending_intent_download_service_retry,
retryIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
} else if (retryIntent != null) {
retryPendingIntent = PendingIntent.getService(context,
R.id.pending_intent_download_service_retry, retryIntent,
PendingIntent.FLAG_UPDATE_CURRENT
| (Build.VERSION.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0));
}
PendingIntent intent = getReportNotificationContentIntent(context);
String content = createFailedDownloadNotificationContent(reportQueue);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context,
NotificationUtils.CHANNEL_ID_DOWNLOAD_ERROR);
builder.setTicker(context.getString(R.string.download_report_title))
.setContentTitle(context.getString(R.string.download_report_title))
.setContentText(content)
.setStyle(new NotificationCompat.BigTextStyle().bigText(content))
.setSmallIcon(R.drawable.ic_notification_sync_error)
.setContentIntent(intent)
.setAutoCancel(true);
if (retryPendingIntent != null) {
builder.addAction(new NotificationCompat.Action(
R.drawable.ic_notification_sync, context.getString(R.string.retry_label), retryPendingIntent));
}
builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
nm.notify(R.id.notification_download_report, builder.build());
}
public void postAuthenticationNotification(final DownloadRequest downloadRequest) {

View File

@ -1,6 +1,7 @@
package de.danoeh.antennapod.net.download.serviceinterface;
import android.content.Context;
import android.content.Intent;
public abstract class DownloadServiceInterface {
private static DownloadServiceInterface impl;
@ -15,6 +16,8 @@ public abstract class DownloadServiceInterface {
public abstract void download(Context context, boolean cleanupMedia, DownloadRequest... requests);
public abstract Intent makeDownloadIntent(Context context, boolean cleanupMedia, DownloadRequest... requests);
public abstract void refreshAllFeeds(Context context, boolean initiatedByUser);
public abstract void cancel(Context context, String url);

View File

@ -1,12 +1,17 @@
package de.danoeh.antennapod.net.download.serviceinterface;
import android.content.Context;
import android.content.Intent;
public class DownloadServiceInterfaceStub extends DownloadServiceInterface {
public void download(Context context, boolean cleanupMedia, DownloadRequest... requests) {
}
public Intent makeDownloadIntent(Context context, boolean cleanupMedia, DownloadRequest... requests) {
return null;
}
public void refreshAllFeeds(Context context, boolean initiatedByUser) {
}

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="pending_intent_download_service_notification" type="id"/>
<item name="pending_intent_download_service_retry" type="id"/>
<item name="pending_intent_download_service_auth" type="id"/>
<item name="pending_intent_download_service_report" type="id"/>
<item name="pending_intent_download_service_autodownload_report" type="id"/>