Add retry button to download failed notifications (#6333)
This commit is contained in:
parent
c98194f519
commit
a5d4864776
@ -14,6 +14,7 @@ import android.util.Log;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.VisibleForTesting;
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
import androidx.core.app.NotificationManagerCompat;
|
||||||
import androidx.core.app.ServiceCompat;
|
import androidx.core.app.ServiceCompat;
|
||||||
|
|
||||||
import de.danoeh.antennapod.core.R;
|
import de.danoeh.antennapod.core.R;
|
||||||
@ -82,6 +83,7 @@ public class DownloadService extends Service {
|
|||||||
private final ExecutorService downloadEnqueueExecutor;
|
private final ExecutorService downloadEnqueueExecutor;
|
||||||
|
|
||||||
private final List<DownloadStatus> reportQueue = new ArrayList<>();
|
private final List<DownloadStatus> reportQueue = new ArrayList<>();
|
||||||
|
private final List<DownloadRequest> failedRequestsForReport = new ArrayList<>();
|
||||||
private DownloadServiceNotification notificationManager;
|
private DownloadServiceNotification notificationManager;
|
||||||
private final NewEpisodesNotification newEpisodesNotification;
|
private final NewEpisodesNotification newEpisodesNotification;
|
||||||
private NotificationUpdater notificationUpdater;
|
private NotificationUpdater notificationUpdater;
|
||||||
@ -176,11 +178,15 @@ public class DownloadService extends Service {
|
|||||||
if (intent != null && intent.hasExtra(EXTRA_REQUESTS)) {
|
if (intent != null && intent.hasExtra(EXTRA_REQUESTS)) {
|
||||||
Notification notification = notificationManager.updateNotifications(downloads);
|
Notification notification = notificationManager.updateNotifications(downloads);
|
||||||
startForeground(R.id.notification_downloading, notification);
|
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();
|
setupNotificationUpdaterIfNecessary();
|
||||||
downloadEnqueueExecutor.execute(() -> onDownloadQueued(intent));
|
downloadEnqueueExecutor.execute(() -> onDownloadQueued(intent));
|
||||||
} else if (intent != null && intent.getBooleanExtra(EXTRA_REFRESH_ALL, false)) {
|
} else if (intent != null && intent.getBooleanExtra(EXTRA_REFRESH_ALL, false)) {
|
||||||
Notification notification = notificationManager.updateNotifications(downloads);
|
Notification notification = notificationManager.updateNotifications(downloads);
|
||||||
startForeground(R.id.notification_downloading, notification);
|
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();
|
setupNotificationUpdaterIfNecessary();
|
||||||
downloadEnqueueExecutor.execute(() -> enqueueAll(intent));
|
downloadEnqueueExecutor.execute(() -> enqueueAll(intent));
|
||||||
} else if (downloads.size() == 0) {
|
} else if (downloads.size() == 0) {
|
||||||
@ -198,8 +204,9 @@ public class DownloadService extends Service {
|
|||||||
|
|
||||||
boolean showAutoDownloadReport = UserPreferences.showAutoDownloadReport();
|
boolean showAutoDownloadReport = UserPreferences.showAutoDownloadReport();
|
||||||
if (UserPreferences.showDownloadReport() || showAutoDownloadReport) {
|
if (UserPreferences.showDownloadReport() || showAutoDownloadReport) {
|
||||||
notificationManager.updateReport(reportQueue, showAutoDownloadReport);
|
notificationManager.updateReport(reportQueue, showAutoDownloadReport, failedRequestsForReport);
|
||||||
reportQueue.clear();
|
reportQueue.clear();
|
||||||
|
failedRequestsForReport.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
unregisterReceiver(cancelDownloadReceiver);
|
unregisterReceiver(cancelDownloadReceiver);
|
||||||
@ -283,7 +290,7 @@ public class DownloadService extends Service {
|
|||||||
// we create a 'successful' download log if the feed's last refresh failed
|
// we create a 'successful' download log if the feed's last refresh failed
|
||||||
List<DownloadStatus> log = DBReader.getFeedDownloadLog(request.getFeedfileId());
|
List<DownloadStatus> log = DBReader.getFeedDownloadLog(request.getFeedfileId());
|
||||||
if (log.size() > 0 && !log.get(0).isSuccessful()) {
|
if (log.size() > 0 && !log.get(0).isSuccessful()) {
|
||||||
saveDownloadStatus(feedSyncTask.getDownloadStatus());
|
saveDownloadStatus(feedSyncTask.getDownloadStatus(), downloader.getDownloadRequest());
|
||||||
}
|
}
|
||||||
if (!request.isInitiatedByUser()) {
|
if (!request.isInitiatedByUser()) {
|
||||||
// Was stored in the database before and not initiated manually
|
// Was stored in the database before and not initiated manually
|
||||||
@ -296,13 +303,13 @@ public class DownloadService extends Service {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
DBWriter.setFeedLastUpdateFailed(request.getFeedfileId(), true);
|
DBWriter.setFeedLastUpdateFailed(request.getFeedfileId(), true);
|
||||||
saveDownloadStatus(feedSyncTask.getDownloadStatus());
|
saveDownloadStatus(feedSyncTask.getDownloadStatus(), downloader.getDownloadRequest());
|
||||||
}
|
}
|
||||||
} else if (type == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
|
} else if (type == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
|
||||||
Log.d(TAG, "Handling completed FeedMedia Download");
|
Log.d(TAG, "Handling completed FeedMedia Download");
|
||||||
MediaDownloadedHandler handler = new MediaDownloadedHandler(DownloadService.this, status, request);
|
MediaDownloadedHandler handler = new MediaDownloadedHandler(DownloadService.this, status, request);
|
||||||
handler.run();
|
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());
|
DownloadServiceInterface.get().download(this, false, downloader.getDownloadRequest());
|
||||||
} else {
|
} else {
|
||||||
Log.e(TAG, "Download failed");
|
Log.e(TAG, "Download failed");
|
||||||
saveDownloadStatus(status);
|
saveDownloadStatus(status, downloader.getDownloadRequest());
|
||||||
new FailedDownloadHandler(downloader.getDownloadRequest()).run();
|
new FailedDownloadHandler(downloader.getDownloadRequest()).run();
|
||||||
|
|
||||||
if (type == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
|
if (type == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
|
||||||
@ -509,8 +516,11 @@ public class DownloadService extends Service {
|
|||||||
*
|
*
|
||||||
* @param status the download that is going to be saved
|
* @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);
|
reportQueue.add(status);
|
||||||
|
if (!status.isSuccessful() && !status.isCancelled()) {
|
||||||
|
failedRequestsForReport.add(request);
|
||||||
|
}
|
||||||
DBWriter.addDownloadStatus(status);
|
DBWriter.addDownloadStatus(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,6 +16,13 @@ public class DownloadServiceInterfaceImpl extends DownloadServiceInterface {
|
|||||||
private static final String TAG = "DownloadServiceInterface";
|
private static final String TAG = "DownloadServiceInterface";
|
||||||
|
|
||||||
public void download(Context context, boolean cleanupMedia, DownloadRequest... requests) {
|
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<>();
|
ArrayList<DownloadRequest> requestsToSend = new ArrayList<>();
|
||||||
for (DownloadRequest request : requests) {
|
for (DownloadRequest request : requests) {
|
||||||
if (!isDownloadingFile(request.getSource())) {
|
if (!isDownloadingFile(request.getSource())) {
|
||||||
@ -23,7 +30,7 @@ public class DownloadServiceInterfaceImpl extends DownloadServiceInterface {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (requestsToSend.isEmpty()) {
|
if (requestsToSend.isEmpty()) {
|
||||||
return;
|
return null;
|
||||||
} else if (requestsToSend.size() > 100) {
|
} else if (requestsToSend.size() > 100) {
|
||||||
if (BuildConfig.DEBUG) {
|
if (BuildConfig.DEBUG) {
|
||||||
throw new IllegalArgumentException("Android silently drops intent payloads that are too large");
|
throw new IllegalArgumentException("Android silently drops intent payloads that are too large");
|
||||||
@ -38,7 +45,7 @@ public class DownloadServiceInterfaceImpl extends DownloadServiceInterface {
|
|||||||
if (cleanupMedia) {
|
if (cleanupMedia) {
|
||||||
launchIntent.putExtra(DownloadService.EXTRA_CLEANUP_MEDIA, true);
|
launchIntent.putExtra(DownloadService.EXTRA_CLEANUP_MEDIA, true);
|
||||||
}
|
}
|
||||||
ContextCompat.startForegroundService(context, launchIntent);
|
return launchIntent;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void refreshAllFeeds(Context context, boolean initiatedByUser) {
|
public void refreshAllFeeds(Context context, boolean initiatedByUser) {
|
||||||
|
@ -15,6 +15,7 @@ import de.danoeh.antennapod.model.feed.Feed;
|
|||||||
import de.danoeh.antennapod.model.feed.FeedMedia;
|
import de.danoeh.antennapod.model.feed.FeedMedia;
|
||||||
import de.danoeh.antennapod.core.util.gui.NotificationUtils;
|
import de.danoeh.antennapod.core.util.gui.NotificationUtils;
|
||||||
import de.danoeh.antennapod.net.download.serviceinterface.DownloadRequest;
|
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.DownloadAuthenticationActivityStarter;
|
||||||
import de.danoeh.antennapod.ui.appstartintent.MainActivityStarter;
|
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
|
* user about the number of completed downloads. A report will only be
|
||||||
* created if there is at least one failed download excluding images
|
* 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
|
// check if report should be created
|
||||||
boolean createReport = false;
|
boolean createReport = false;
|
||||||
int failedDownloads = 0;
|
int failedDownloads = 0;
|
||||||
@ -173,48 +175,68 @@ public class DownloadServiceNotification {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (createReport) {
|
if (!createReport) {
|
||||||
|
Log.d(TAG, "No report is created");
|
||||||
|
return;
|
||||||
|
}
|
||||||
Log.d(TAG, "Creating report");
|
Log.d(TAG, "Creating report");
|
||||||
|
|
||||||
// create notification object
|
|
||||||
String channelId;
|
|
||||||
int titleId;
|
|
||||||
int iconId;
|
|
||||||
int id;
|
|
||||||
String content;
|
|
||||||
PendingIntent intent;
|
|
||||||
if (failedDownloads == 0) {
|
if (failedDownloads == 0) {
|
||||||
// We are generating an auto-download report
|
createAutoDownloadReportNotification(reportQueue);
|
||||||
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 {
|
} else {
|
||||||
channelId = NotificationUtils.CHANNEL_ID_DOWNLOAD_ERROR;
|
createDownloadFailedNotification(reportQueue, failedRequests);
|
||||||
titleId = R.string.download_report_title;
|
}
|
||||||
iconId = R.drawable.ic_notification_sync_error;
|
Log.d(TAG, "Download report notification was posted");
|
||||||
intent = getReportNotificationContentIntent(context);
|
|
||||||
id = R.id.notification_download_report;
|
|
||||||
content = createFailedDownloadNotificationContent(reportQueue);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, channelId);
|
private void createAutoDownloadReportNotification(List<DownloadStatus> reportQueue) {
|
||||||
builder.setTicker(context.getString(titleId))
|
PendingIntent intent = getAutoDownloadReportNotificationContentIntent(context);
|
||||||
.setContentTitle(context.getString(titleId))
|
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)
|
.setContentText(content)
|
||||||
.setStyle(new NotificationCompat.BigTextStyle().bigText(content))
|
.setStyle(new NotificationCompat.BigTextStyle().bigText(content))
|
||||||
.setSmallIcon(iconId)
|
.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)
|
.setContentIntent(intent)
|
||||||
.setAutoCancel(true);
|
.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);
|
builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
|
||||||
NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||||
nm.notify(id, builder.build());
|
nm.notify(R.id.notification_download_report, builder.build());
|
||||||
Log.d(TAG, "Download report notification was posted");
|
|
||||||
} else {
|
|
||||||
Log.d(TAG, "No report is created");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void postAuthenticationNotification(final DownloadRequest downloadRequest) {
|
public void postAuthenticationNotification(final DownloadRequest downloadRequest) {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package de.danoeh.antennapod.net.download.serviceinterface;
|
package de.danoeh.antennapod.net.download.serviceinterface;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
|
||||||
public abstract class DownloadServiceInterface {
|
public abstract class DownloadServiceInterface {
|
||||||
private static DownloadServiceInterface impl;
|
private static DownloadServiceInterface impl;
|
||||||
@ -15,6 +16,8 @@ public abstract class DownloadServiceInterface {
|
|||||||
|
|
||||||
public abstract void download(Context context, boolean cleanupMedia, DownloadRequest... requests);
|
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 refreshAllFeeds(Context context, boolean initiatedByUser);
|
||||||
|
|
||||||
public abstract void cancel(Context context, String url);
|
public abstract void cancel(Context context, String url);
|
||||||
|
@ -1,12 +1,17 @@
|
|||||||
package de.danoeh.antennapod.net.download.serviceinterface;
|
package de.danoeh.antennapod.net.download.serviceinterface;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
|
||||||
public class DownloadServiceInterfaceStub extends DownloadServiceInterface {
|
public class DownloadServiceInterfaceStub extends DownloadServiceInterface {
|
||||||
|
|
||||||
public void download(Context context, boolean cleanupMedia, DownloadRequest... requests) {
|
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) {
|
public void refreshAllFeeds(Context context, boolean initiatedByUser) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<item name="pending_intent_download_service_notification" type="id"/>
|
<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_auth" type="id"/>
|
||||||
<item name="pending_intent_download_service_report" type="id"/>
|
<item name="pending_intent_download_service_report" type="id"/>
|
||||||
<item name="pending_intent_download_service_autodownload_report" type="id"/>
|
<item name="pending_intent_download_service_autodownload_report" type="id"/>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user