Extracted notification from DownloadService

This commit is contained in:
ByteHamster 2019-10-29 23:58:44 +01:00
parent 056d7db16b
commit 798868db9c
2 changed files with 182 additions and 147 deletions

View File

@ -98,10 +98,8 @@ public class DownloadService extends Service {
private DownloadRequester requester; private DownloadRequester requester;
private DownloadServiceNotification notificationManager;
private NotificationCompat.Builder notificationCompatBuilder; public static final int NOTIFICATION_ID = 2;
private static final int NOTIFICATION_ID = 2;
private static final int REPORT_ID = 3;
/** /**
* Currently running downloads. * Currently running downloads.
@ -169,7 +167,7 @@ public class DownloadService extends Service {
numberOfDownloads.decrementAndGet(); numberOfDownloads.decrementAndGet();
if (!status.isCancelled()) { if (!status.isCancelled()) {
if (status.getReason() == DownloadError.ERROR_UNAUTHORIZED) { if (status.getReason() == DownloadError.ERROR_UNAUTHORIZED) {
postAuthenticationNotification(downloader.getDownloadRequest()); notificationManager.postAuthenticationNotification(downloader.getDownloadRequest());
} else if (status.getReason() == DownloadError.ERROR_HTTP_DATA_ERROR } else if (status.getReason() == DownloadError.ERROR_HTTP_DATA_ERROR
&& Integer.parseInt(status.getReasonDetailed()) == 416) { && Integer.parseInt(status.getReasonDetailed()) == 416) {
@ -288,9 +286,11 @@ public class DownloadService extends Service {
}); });
feedSyncThread.start(); feedSyncThread.start();
setupNotificationBuilders();
requester = DownloadRequester.getInstance(); requester = DownloadRequester.getInstance();
startForeground(NOTIFICATION_ID, updateNotifications()); notificationManager = new DownloadServiceNotification(this);
Notification notification = notificationManager.updateNotifications(
requester.getNumberOfDownloads(), downloads);
startForeground(NOTIFICATION_ID, notification);
} }
@Override @Override
@ -303,9 +303,10 @@ public class DownloadService extends Service {
Log.d(TAG, "Service shutting down"); Log.d(TAG, "Service shutting down");
isRunning = false; isRunning = false;
if (ClientConfig.downloadServiceCallbacks.shouldCreateReport() && if (ClientConfig.downloadServiceCallbacks.shouldCreateReport()
UserPreferences.showDownloadReport()) { && UserPreferences.showDownloadReport()) {
updateReport(); notificationManager.updateReport(reportQueue);
reportQueue.clear();
} }
postHandler.removeCallbacks(postDownloaderTask); postHandler.removeCallbacks(postDownloaderTask);
@ -334,40 +335,6 @@ public class DownloadService extends Service {
DBTasks.autodownloadUndownloadedItems(getApplicationContext()); DBTasks.autodownloadUndownloadedItems(getApplicationContext());
} }
private void setupNotificationBuilders() {
notificationCompatBuilder = new NotificationCompat.Builder(this, NotificationUtils.CHANNEL_ID_DOWNLOADING)
.setOngoing(true)
.setContentIntent(ClientConfig.downloadServiceCallbacks.getNotificationContentIntent(this))
.setSmallIcon(R.drawable.stat_notify_sync);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
notificationCompatBuilder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
}
Log.d(TAG, "Notification set up");
}
/**
* Updates the contents of the service's notifications. Should be called
* after setupNotificationBuilders.
*/
private Notification updateNotifications() {
if (notificationCompatBuilder == null) {
return null;
}
String contentTitle = getString(R.string.download_notification_title);
int numDownloads = requester.getNumberOfDownloads();
String downloadsLeft = (numDownloads > 0) ?
getResources().getQuantityString(R.plurals.downloads_left, numDownloads, numDownloads) :
getString(R.string.downloads_processing);
String bigText = compileNotificationString(downloads);
notificationCompatBuilder.setContentTitle(contentTitle);
notificationCompatBuilder.setContentText(downloadsLeft);
notificationCompatBuilder.setStyle(new NotificationCompat.BigTextStyle().bigText(bigText));
return notificationCompatBuilder.build();
}
private Downloader getDownloader(String downloadUrl) { private Downloader getDownloader(String downloadUrl) {
for (Downloader downloader : downloads) { for (Downloader downloader : downloads) {
if (downloader.getDownloadRequest().getSource().equals(downloadUrl)) { if (downloader.getDownloadRequest().getSource().equals(downloadUrl)) {
@ -502,55 +469,6 @@ public class DownloadService extends Service {
DBWriter.addDownloadStatus(status); DBWriter.addDownloadStatus(status);
} }
/**
* Creates a notification at the end of the service lifecycle to notify the
* user about the number of completed downloads. A report will only be
* created if there is at least one failed download excluding images
*/
private void updateReport() {
// check if report should be created
boolean createReport = false;
int successfulDownloads = 0;
int failedDownloads = 0;
// a download report is created if at least one download has failed
// (excluding failed image downloads)
for (DownloadStatus status : reportQueue) {
if (status.isSuccessful()) {
successfulDownloads++;
} else if (!status.isCancelled()) {
createReport = true;
failedDownloads++;
}
}
if (createReport) {
Log.d(TAG, "Creating report");
// create notification object
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, NotificationUtils.CHANNEL_ID_ERROR)
.setTicker(getString(R.string.download_report_title))
.setContentTitle(getString(R.string.download_report_content_title))
.setContentText(
String.format(
getString(R.string.download_report_content),
successfulDownloads, failedDownloads)
)
.setSmallIcon(R.drawable.stat_notify_sync_error)
.setContentIntent(
ClientConfig.downloadServiceCallbacks.getReportNotificationContentIntent(this)
)
.setAutoCancel(true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
}
NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
nm.notify(REPORT_ID, builder.build());
} else {
Log.d(TAG, "No report is created");
}
reportQueue.clear();
}
/** /**
* Calls query downloads on the services main thread. This method should be used instead of queryDownloads if it is * Calls query downloads on the services main thread. This method should be used instead of queryDownloads if it is
* used from a thread other than the main thread. * used from a thread other than the main thread.
@ -570,32 +488,12 @@ public class DownloadService extends Service {
stopSelf(); stopSelf();
} else { } else {
setupNotificationUpdater(); setupNotificationUpdater();
startForeground(NOTIFICATION_ID, updateNotifications()); Notification notification = notificationManager.updateNotifications(
requester.getNumberOfDownloads(), downloads);
startForeground(NOTIFICATION_ID, notification);
} }
} }
private void postAuthenticationNotification(final DownloadRequest downloadRequest) {
handler.post(() -> {
final String resourceTitle = (downloadRequest.getTitle() != null) ?
downloadRequest.getTitle() : downloadRequest.getSource();
NotificationCompat.Builder builder = new NotificationCompat.Builder(DownloadService.this, NotificationUtils.CHANNEL_ID_USER_ACTION);
builder.setTicker(getText(R.string.authentication_notification_title))
.setContentTitle(getText(R.string.authentication_notification_title))
.setContentText(getText(R.string.authentication_notification_msg))
.setStyle(new NotificationCompat.BigTextStyle().bigText(getText(R.string.authentication_notification_msg)
+ ": " + resourceTitle))
.setSmallIcon(R.drawable.ic_notification_key)
.setAutoCancel(true)
.setContentIntent(ClientConfig.downloadServiceCallbacks.getAuthentificationNotificationContentIntent(DownloadService.this, downloadRequest));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
}
NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
nm.notify(downloadRequest.getSource().hashCode(), builder.build());
});
}
@Nullable @Nullable
private FeedItem getFeedItemFromId(long id) { private FeedItem getFeedItemFromId(long id) {
FeedMedia media = DBReader.getFeedMedia(id); FeedMedia media = DBReader.getFeedMedia(id);
@ -667,7 +565,8 @@ public class DownloadService extends Service {
private class NotificationUpdater implements Runnable { private class NotificationUpdater implements Runnable {
public void run() { public void run() {
handler.post(() -> { handler.post(() -> {
Notification n = updateNotifications(); Notification n = notificationManager.updateNotifications(
requester.getNumberOfDownloads(), downloads);
if (n != null) { if (n != null) {
NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
nm.notify(NOTIFICATION_ID, n); nm.notify(NOTIFICATION_ID, n);
@ -703,34 +602,4 @@ public class DownloadService extends Service {
lastPost = now; lastPost = now;
} }
} }
private static String compileNotificationString(List<Downloader> downloads) {
List<String> lines = new ArrayList<>(downloads.size());
for (Downloader downloader : downloads) {
if (downloader.cancelled) {
continue;
}
StringBuilder line = new StringBuilder("");
DownloadRequest request = downloader.getDownloadRequest();
switch (request.getFeedfileType()) {
case Feed.FEEDFILETYPE_FEED:
if (request.getTitle() != null) {
line.append(request.getTitle());
}
break;
case FeedMedia.FEEDFILETYPE_FEEDMEDIA:
if (request.getTitle() != null) {
line.append(request.getTitle())
.append(" (")
.append(request.getProgressPercent())
.append("%)");
}
break;
default:
line.append("Unknown: ").append(request.getFeedfileType());
}
lines.add(line.toString());
}
return TextUtils.join("\n", lines);
}
} }

View File

@ -0,0 +1,166 @@
package de.danoeh.antennapod.core.service.download;
import android.app.Notification;
import android.app.NotificationManager;
import android.content.Context;
import android.os.Build;
import android.text.TextUtils;
import android.util.Log;
import androidx.core.app.NotificationCompat;
import de.danoeh.antennapod.core.ClientConfig;
import de.danoeh.antennapod.core.R;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.util.gui.NotificationUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
public class DownloadServiceNotification {
private static final String TAG = "DownloadSvcNotification";
private static final int REPORT_ID = 3;
private final Context context;
private NotificationCompat.Builder notificationCompatBuilder;
public DownloadServiceNotification(Context context) {
this.context = context;
setupNotificationBuilders();
}
private void setupNotificationBuilders() {
notificationCompatBuilder = new NotificationCompat.Builder(context, NotificationUtils.CHANNEL_ID_DOWNLOADING)
.setOngoing(true)
.setContentIntent(ClientConfig.downloadServiceCallbacks.getNotificationContentIntent(context))
.setSmallIcon(R.drawable.stat_notify_sync);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
notificationCompatBuilder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
}
Log.d(TAG, "Notification set up");
}
/**
* Updates the contents of the service's notifications. Should be called
* after setupNotificationBuilders.
*/
public Notification updateNotifications(int numDownloads, List<Downloader> downloads) {
if (notificationCompatBuilder == null) {
return null;
}
String contentTitle = context.getString(R.string.download_notification_title);
String downloadsLeft = (numDownloads > 0)
? context.getResources().getQuantityString(R.plurals.downloads_left, numDownloads, numDownloads)
: context.getString(R.string.downloads_processing);
String bigText = compileNotificationString(downloads);
notificationCompatBuilder.setContentTitle(contentTitle);
notificationCompatBuilder.setContentText(downloadsLeft);
notificationCompatBuilder.setStyle(new NotificationCompat.BigTextStyle().bigText(bigText));
return notificationCompatBuilder.build();
}
private static String compileNotificationString(List<Downloader> downloads) {
List<String> lines = new ArrayList<>(downloads.size());
for (Downloader downloader : downloads) {
if (downloader.cancelled) {
continue;
}
StringBuilder line = new StringBuilder("");
DownloadRequest request = downloader.getDownloadRequest();
switch (request.getFeedfileType()) {
case Feed.FEEDFILETYPE_FEED:
if (request.getTitle() != null) {
line.append(request.getTitle());
}
break;
case FeedMedia.FEEDFILETYPE_FEEDMEDIA:
if (request.getTitle() != null) {
line.append(request.getTitle())
.append(" (")
.append(request.getProgressPercent())
.append("%)");
}
break;
default:
line.append("Unknown: ").append(request.getFeedfileType());
}
lines.add(line.toString());
}
return TextUtils.join("\n", lines);
}
/**
* Creates a notification at the end of the service lifecycle to notify the
* 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) {
// check if report should be created
boolean createReport = false;
int successfulDownloads = 0;
int failedDownloads = 0;
// a download report is created if at least one download has failed
// (excluding failed image downloads)
for (DownloadStatus status : reportQueue) {
if (status.isSuccessful()) {
successfulDownloads++;
} else if (!status.isCancelled()) {
createReport = true;
failedDownloads++;
}
}
if (createReport) {
Log.d(TAG, "Creating report");
// create notification object
NotificationCompat.Builder builder = new NotificationCompat.Builder(context,
NotificationUtils.CHANNEL_ID_ERROR)
.setTicker(context.getString(R.string.download_report_title))
.setContentTitle(context.getString(R.string.download_report_content_title))
.setContentText(
String.format(
context.getString(R.string.download_report_content),
successfulDownloads, failedDownloads)
)
.setSmallIcon(R.drawable.stat_notify_sync_error)
.setContentIntent(
ClientConfig.downloadServiceCallbacks.getReportNotificationContentIntent(context)
)
.setAutoCancel(true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
}
NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
nm.notify(REPORT_ID, builder.build());
} else {
Log.d(TAG, "No report is created");
}
}
public void postAuthenticationNotification(final DownloadRequest downloadRequest) {
final String resourceTitle = (downloadRequest.getTitle() != null) ?
downloadRequest.getTitle() : downloadRequest.getSource();
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, NotificationUtils.CHANNEL_ID_USER_ACTION);
builder.setTicker(context.getText(R.string.authentication_notification_title))
.setContentTitle(context.getText(R.string.authentication_notification_title))
.setContentText(context.getText(R.string.authentication_notification_msg))
.setStyle(new NotificationCompat.BigTextStyle().bigText(context.getText(R.string.authentication_notification_msg)
+ ": " + resourceTitle))
.setSmallIcon(R.drawable.ic_notification_key)
.setAutoCancel(true)
.setContentIntent(ClientConfig.downloadServiceCallbacks.getAuthentificationNotificationContentIntent(context, downloadRequest));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
}
NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
nm.notify(downloadRequest.getSource().hashCode(), builder.build());
}
}