diff options
author | H. Lehmann <ByteHamster@users.noreply.github.com> | 2020-03-26 19:22:04 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-03-26 19:22:04 +0100 |
commit | 38c1b559e3d3a7714a4fd0b1fe41ed94a1bcd526 (patch) | |
tree | e3cd6f1eb757e742195c4d7f077787800c66072e /core | |
parent | a6accec122297c414b7f00b3e5444bd39f94b390 (diff) | |
parent | 13ca1a8dbc04958eda4a488642139972637f6d2f (diff) | |
download | AntennaPod-38c1b559e3d3a7714a4fd0b1fe41ed94a1bcd526.zip |
Merge pull request #3839 from shortspider/NotificationForAutoDownloads
Notification for Auto Downloads
Diffstat (limited to 'core')
17 files changed, 213 insertions, 170 deletions
diff --git a/core/src/androidTest/java/de/danoeh/antennapod/core/service/download/DownloadRequestTest.java b/core/src/androidTest/java/de/danoeh/antennapod/core/service/download/DownloadRequestTest.java index 71212e6ec..fc7e26820 100644 --- a/core/src/androidTest/java/de/danoeh/antennapod/core/service/download/DownloadRequestTest.java +++ b/core/src/androidTest/java/de/danoeh/antennapod/core/service/download/DownloadRequestTest.java @@ -45,13 +45,13 @@ public class DownloadRequestTest { FeedFile item1 = createFeedItem(1); Bundle arg1 = new Bundle(); arg1.putString("arg1", "value1"); - DownloadRequest request1 = new DownloadRequest.Builder(destStr, item1) + DownloadRequest request1 = new DownloadRequest.Builder(destStr, item1, false) .withAuthentication(username1, password1) .withArguments(arg1) .build(); FeedFile item2 = createFeedItem(2); - DownloadRequest request2 = new DownloadRequest.Builder(destStr, item2) + DownloadRequest request2 = new DownloadRequest.Builder(destStr, item2, true) .withAuthentication(username2, password2) .build(); diff --git a/core/src/main/java/de/danoeh/antennapod/core/DownloadServiceCallbacks.java b/core/src/main/java/de/danoeh/antennapod/core/DownloadServiceCallbacks.java index dbaece14a..ad3fb8d42 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/DownloadServiceCallbacks.java +++ b/core/src/main/java/de/danoeh/antennapod/core/DownloadServiceCallbacks.java @@ -42,9 +42,19 @@ public interface DownloadServiceCallbacks { PendingIntent getReportNotificationContentIntent(Context context); /** + * Returns a PendingIntent for notification that notifies the user about the episodes that have been automatically + * downloaded. + * <p/> + * The PendingIntent takes users to an activity where they can look at their episode queue. + * + * @return A non-null PendingIntent for the notification or null if shouldCreateReport()==false + */ + PendingIntent getAutoDownloadReportNotificationContentIntent(Context context); + + /** * Returns true if the DownloadService should create a report that shows the number of failed * downloads when the service shuts down. - * */ + */ boolean shouldCreateReport(); } diff --git a/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java b/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java index b2ea1fa62..f7b6ccb2a 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java +++ b/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java @@ -60,6 +60,7 @@ public class UserPreferences { public static final String PREF_COMPACT_NOTIFICATION_BUTTONS = "prefCompactNotificationButtons"; public static final String PREF_LOCKSCREEN_BACKGROUND = "prefLockscreenBackground"; private static final String PREF_SHOW_DOWNLOAD_REPORT = "prefShowDownloadReport"; + private static final String PREF_SHOW_AUTO_DOWNLOAD_REPORT = "prefShowAutoDownloadReport"; public static final String PREF_BACK_BUTTON_BEHAVIOR = "prefBackButtonBehavior"; private static final String PREF_BACK_BUTTON_GO_TO_PAGE = "prefBackButtonGoToPage"; @@ -292,6 +293,10 @@ public class UserPreferences { return prefs.getBoolean(PREF_SHOW_DOWNLOAD_REPORT, true); } + public static boolean showAutoDownloadReport() { + return prefs.getBoolean(PREF_SHOW_AUTO_DOWNLOAD_REPORT, false); + } + public static boolean enqueueDownloadedEpisodes() { return prefs.getBoolean(PREF_ENQUEUE_DOWNLOADED, true); } diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/FeedUpdateWorker.java b/core/src/main/java/de/danoeh/antennapod/core/service/FeedUpdateWorker.java index 4443319e9..8811faf8a 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/FeedUpdateWorker.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/FeedUpdateWorker.java @@ -31,7 +31,7 @@ public class FeedUpdateWorker extends Worker { ClientConfig.initialize(getApplicationContext()); if (NetworkUtils.networkAvailable() && NetworkUtils.isFeedRefreshAllowed()) { - DBTasks.refreshAllFeeds(getApplicationContext()); + DBTasks.refreshAllFeeds(getApplicationContext(), false); } else { Log.d(TAG, "Blocking automatic update: no wifi available / no mobile updates allowed"); } diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadRequest.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadRequest.java index 2dd46cf96..78c4d3f48 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadRequest.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadRequest.java @@ -29,59 +29,42 @@ public class DownloadRequest implements Parcelable { private long size; private int statusMsg; private boolean mediaEnqueued; + private boolean initiatedByUser; - public DownloadRequest(@NonNull String destination, - @NonNull String source, - @NonNull String title, - long feedfileId, - int feedfileType, - String username, - String password, - boolean deleteOnFailure, - Bundle arguments) { + public DownloadRequest(@NonNull String destination, @NonNull String source, @NonNull String title, long feedfileId, + int feedfileType, String username, String password, boolean deleteOnFailure, + Bundle arguments, boolean initiatedByUser) { + this(destination, source, title, feedfileId, feedfileType, null, deleteOnFailure, username, password, false, + arguments, initiatedByUser); + } + + private DownloadRequest(Builder builder) { + this(builder.destination, builder.source, builder.title, builder.feedfileId, builder.feedfileType, + builder.lastModified, builder.deleteOnFailure, builder.username, builder.password, false, + builder.arguments != null ? builder.arguments : new Bundle(), builder.initiatedByUser); + } + private DownloadRequest(Parcel in) { + this(in.readString(), in.readString(), in.readString(), in.readLong(), in.readInt(), in.readString(), + in.readByte() > 0, nullIfEmpty(in.readString()), nullIfEmpty(in.readString()), in.readByte() > 0, + in.readBundle(), in.readByte() > 0); + } + + private DownloadRequest(String destination, String source, String title, long feedfileId, int feedfileType, + String lastModified, boolean deleteOnFailure, String username, String password, + boolean mediaEnqueued, Bundle arguments, boolean initiatedByUser) { this.destination = destination; this.source = source; this.title = title; this.feedfileId = feedfileId; this.feedfileType = feedfileType; + this.lastModified = lastModified; + this.deleteOnFailure = deleteOnFailure; this.username = username; this.password = password; - this.deleteOnFailure = deleteOnFailure; - this.mediaEnqueued = false; - this.arguments = (arguments != null) ? arguments : new Bundle(); - } - - public DownloadRequest(String destination, String source, String title, - long feedfileId, int feedfileType) { - this(destination, source, title, feedfileId, feedfileType, null, null, true, null); - } - - private DownloadRequest(Builder builder) { - this.destination = builder.destination; - this.source = builder.source; - this.title = builder.title; - this.feedfileId = builder.feedfileId; - this.feedfileType = builder.feedfileType; - this.username = builder.username; - this.password = builder.password; - this.lastModified = builder.lastModified; - this.deleteOnFailure = builder.deleteOnFailure; - this.arguments = (builder.arguments != null) ? builder.arguments : new Bundle(); - } - - private DownloadRequest(Parcel in) { - destination = in.readString(); - source = in.readString(); - title = in.readString(); - feedfileId = in.readLong(); - feedfileType = in.readInt(); - lastModified = in.readString(); - deleteOnFailure = (in.readByte() > 0); - username = nullIfEmpty(in.readString()); - password = nullIfEmpty(in.readString()); - mediaEnqueued = (in.readByte() > 0); - arguments = in.readBundle(); + this.mediaEnqueued = mediaEnqueued; + this.arguments = arguments; + this.initiatedByUser = initiatedByUser; } @Override @@ -107,6 +90,7 @@ public class DownloadRequest implements Parcelable { dest.writeString(nonNullString(password)); dest.writeByte((mediaEnqueued) ? (byte) 1 : 0); dest.writeBundle(arguments); + dest.writeByte(initiatedByUser ? (byte) 1 : 0); } private static String nonNullString(String str) { @@ -153,6 +137,7 @@ public class DownloadRequest implements Parcelable { if (username != null ? !username.equals(that.username) : that.username != null) return false; if (mediaEnqueued != that.mediaEnqueued) return false; + if (initiatedByUser != that.initiatedByUser) return false; return true; } @@ -258,6 +243,10 @@ public class DownloadRequest implements Parcelable { return mediaEnqueued; } + public boolean isInitiatedByUser() { + return initiatedByUser; + } + /** * Set to true if the media is enqueued because of this download. * The state is helpful if the download is cancelled, and undoing the enqueue is needed. @@ -281,13 +270,15 @@ public class DownloadRequest implements Parcelable { private final long feedfileId; private final int feedfileType; private Bundle arguments; + private boolean initiatedByUser; - public Builder(@NonNull String destination, @NonNull FeedFile item) { + public Builder(@NonNull String destination, @NonNull FeedFile item, boolean initiatedByUser) { this.destination = destination; this.source = URLChecker.prepareURL(item.getDownload_url()); this.title = item.getHumanReadableIdentifier(); this.feedfileId = item.getId(); this.feedfileType = item.getTypeAsInt(); + this.initiatedByUser = initiatedByUser; } public Builder deleteOnFailure(boolean deleteOnFailure) { diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java index d4177df46..ee34746fc 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java @@ -205,9 +205,10 @@ public class DownloadService extends Service { Log.d(TAG, "Service shutting down"); isRunning = false; + boolean showAutoDownloadReport = UserPreferences.showAutoDownloadReport(); if (ClientConfig.downloadServiceCallbacks.shouldCreateReport() - && UserPreferences.showDownloadReport()) { - notificationManager.updateReport(reportQueue); + && (UserPreferences.showDownloadReport() || showAutoDownloadReport)) { + notificationManager.updateReport(reportQueue, showAutoDownloadReport); reportQueue.clear(); } diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadServiceNotification.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadServiceNotification.java index f49257174..b6f7e0b8f 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadServiceNotification.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadServiceNotification.java @@ -2,6 +2,7 @@ package de.danoeh.antennapod.core.service.download; import android.app.Notification; import android.app.NotificationManager; +import android.app.PendingIntent; import android.content.Context; import android.os.Build; import android.text.TextUtils; @@ -19,6 +20,7 @@ import java.util.List; public class DownloadServiceNotification { private static final String TAG = "DownloadSvcNotification"; private static final int REPORT_ID = 3; + private static final int AUTO_REPORT_ID = 4; private final Context context; private NotificationCompat.Builder notificationCompatBuilder; @@ -91,12 +93,26 @@ public class DownloadServiceNotification { return TextUtils.join("\n", lines); } + private static String createAutoDownloadNotificationContent(List<DownloadStatus> statuses) { + int length = statuses.size(); + StringBuilder sb = new StringBuilder(); + + for (int i = 0; i < length; i++) { + sb.append("• ").append(statuses.get(i).getTitle()); + if (i != length - 1) { + sb.append("\n"); + } + } + + return sb.toString(); + } + /** * 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) { + public void updateReport(List<DownloadStatus> reportQueue, boolean showAutoDownloadReport) { // check if report should be created boolean createReport = false; int successfulDownloads = 0; @@ -107,34 +123,53 @@ public class DownloadServiceNotification { for (DownloadStatus status : reportQueue) { if (status.isSuccessful()) { successfulDownloads++; + createReport |= showAutoDownloadReport && !status.isInitiatedByUser() && status.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA; } else if (!status.isCancelled()) { - createReport = true; failedDownloads++; + createReport = true; } } 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); + 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.auto_download_complete; + intent = ClientConfig.downloadServiceCallbacks.getAutoDownloadReportNotificationContentIntent(context); + id = AUTO_REPORT_ID; + content = createAutoDownloadNotificationContent(reportQueue); + } else { + channelId = NotificationUtils.CHANNEL_ID_ERROR; + titleId = R.string.download_report_title; + iconId = R.drawable.stat_notify_sync_error; + intent = ClientConfig.downloadServiceCallbacks.getReportNotificationContentIntent(context); + id = REPORT_ID; + content = String.format(context.getString(R.string.download_report_content), successfulDownloads, failedDownloads); + } + + 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); 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()); + nm.notify(id, builder.build()); } else { Log.d(TAG, "No report is created"); } diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadStatus.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadStatus.java index 675115bc7..bad2ba1ef 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadStatus.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadStatus.java @@ -41,62 +41,23 @@ public class DownloadStatus { * FEEDFILETYPE_FEEDIMAGE or FEEDFILETYPE_FEEDMEDIA */ private final int feedfileType; + private final boolean initiatedByUser; // ------------------------------------ NOT STORED IN DB private boolean done; private boolean cancelled; - /** Constructor for restoring Download status entries from DB. */ - private DownloadStatus(long id, String title, long feedfileId, - int feedfileType, boolean successful, DownloadError reason, - Date completionDate, String reasonDetailed) { - this.id = id; - this.title = title; - this.done = true; - this.feedfileId = feedfileId; - this.reason = reason; - this.successful = successful; - this.completionDate = (Date) completionDate.clone(); - this.reasonDetailed = reasonDetailed; - this.feedfileType = feedfileType; - } - - public DownloadStatus(@NonNull DownloadRequest request, DownloadError reason, - boolean successful, boolean cancelled, String reasonDetailed) { - this.title = request.getTitle(); - this.feedfileId = request.getFeedfileId(); - this.feedfileType = request.getFeedfileType(); - this.reason = reason; - this.successful = successful; - this.cancelled = cancelled; - this.reasonDetailed = reasonDetailed; - this.completionDate = new Date(); + public DownloadStatus(@NonNull DownloadRequest request, DownloadError reason, boolean successful, boolean cancelled, + String reasonDetailed) { + this(0, request.getTitle(), request.getFeedfileId(), request.getFeedfileType(), successful, cancelled, false, + reason, new Date(), reasonDetailed, request.isInitiatedByUser()); } /** Constructor for creating new completed downloads. */ - public DownloadStatus(@NonNull FeedFile feedfile, String title, DownloadError reason, - boolean successful, String reasonDetailed) { - this.title = title; - this.done = true; - this.feedfileId = feedfile.getId(); - this.feedfileType = feedfile.getTypeAsInt(); - this.reason = reason; - this.successful = successful; - this.completionDate = new Date(); - this.reasonDetailed = reasonDetailed; - } - - /** Constructor for creating new completed downloads. */ - public DownloadStatus(long feedfileId, int feedfileType, String title, - DownloadError reason, boolean successful, String reasonDetailed) { - this.title = title; - this.done = true; - this.feedfileId = feedfileId; - this.feedfileType = feedfileType; - this.reason = reason; - this.successful = successful; - this.completionDate = new Date(); - this.reasonDetailed = reasonDetailed; + public DownloadStatus(@NonNull FeedFile feedfile, String title, DownloadError reason, boolean successful, + String reasonDetailed, boolean initiatedByUser) { + this(0, title, feedfile.getId(), feedfile.getTypeAsInt(), successful, false, true, reason, new Date(), + reasonDetailed, initiatedByUser); } public static DownloadStatus fromCursor(Cursor cursor) { @@ -109,18 +70,27 @@ public class DownloadStatus { int indexCompletionDate = cursor.getColumnIndex(PodDBAdapter.KEY_COMPLETION_DATE); int indexReasonDetailed = cursor.getColumnIndex(PodDBAdapter.KEY_REASON_DETAILED); - long id = cursor.getLong(indexId); - String title = cursor.getString(indexTitle); - long feedfileId = cursor.getLong(indexFeedFile); - int feedfileType = cursor.getInt(indexFileFileType); - boolean successful = cursor.getInt(indexSuccessful) > 0; - int reason = cursor.getInt(indexReason); - Date completionDate = new Date(cursor.getLong(indexCompletionDate)); - String reasonDetailed = cursor.getString(indexReasonDetailed); - - return new DownloadStatus(id, title, feedfileId, - feedfileType, successful, DownloadError.fromCode(reason), completionDate, - reasonDetailed); + return new DownloadStatus(cursor.getLong(indexId), cursor.getString(indexTitle), cursor.getLong(indexFeedFile), + cursor.getInt(indexFileFileType), cursor.getInt(indexSuccessful) > 0, false, true, + DownloadError.fromCode(cursor.getInt(indexReason)), + new Date(cursor.getLong(indexCompletionDate)), + cursor.getString(indexReasonDetailed), false); + } + + private DownloadStatus(long id, String title, long feedfileId, int feedfileType, boolean successful, + boolean cancelled, boolean done, DownloadError reason, Date completionDate, + String reasonDetailed, boolean initiatedByUser) { + this.id = id; + this.title = title; + this.feedfileId = feedfileId; + this.reason = reason; + this.successful = successful; + this.cancelled = cancelled; + this.done = done; + this.completionDate = (Date) completionDate.clone(); + this.reasonDetailed = reasonDetailed; + this.feedfileType = feedfileType; + this.initiatedByUser = initiatedByUser; } @Override @@ -165,6 +135,8 @@ public class DownloadStatus { return feedfileType; } + public boolean isInitiatedByUser() { return initiatedByUser; } + public boolean isDone() { return done; } diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedParserTask.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedParserTask.java index c418db214..05e602db8 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedParserTask.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedParserTask.java @@ -78,12 +78,12 @@ public class FeedParserTask implements Callable<FeedHandlerResult> { } if (successful) { - downloadStatus = new DownloadStatus(feed, feed.getHumanReadableIdentifier(), - DownloadError.SUCCESS, successful, reasonDetailed); + downloadStatus = new DownloadStatus(feed, feed.getHumanReadableIdentifier(), DownloadError.SUCCESS, + successful, reasonDetailed, request.isInitiatedByUser()); return result; } else { - downloadStatus = new DownloadStatus(feed, request.getTitle(), - reason, successful, reasonDetailed); + downloadStatus = new DownloadStatus(feed, feed.getTitle(), reason, successful, + reasonDetailed, request.isInitiatedByUser()); return null; } } diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/MediaDownloadedHandler.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/MediaDownloadedHandler.java index 9ecabd14b..26a0e416e 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/MediaDownloadedHandler.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/MediaDownloadedHandler.java @@ -96,7 +96,7 @@ public class MediaDownloadedHandler implements Runnable { } catch (ExecutionException e) { Log.e(TAG, "ExecutionException in MediaHandlerThread: " + e.getMessage()); updatedStatus = new DownloadStatus(media, media.getEpisodeTitle(), - DownloadError.ERROR_DB_ACCESS_ERROR, false, e.getMessage()); + DownloadError.ERROR_DB_ACCESS_ERROR, false, e.getMessage(), request.isInitiatedByUser()); } if (GpodnetPreferences.loggedIn() && item != null) { diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/APDownloadAlgorithm.java b/core/src/main/java/de/danoeh/antennapod/core/storage/APDownloadAlgorithm.java index 6e4054009..7ec4db5dd 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/APDownloadAlgorithm.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/APDownloadAlgorithm.java @@ -92,7 +92,7 @@ public class APDownloadAlgorithm implements AutomaticDownloadAlgorithm { Log.d(TAG, "Enqueueing " + itemsToDownload.length + " items for download"); try { - DownloadRequester.getInstance().downloadMedia(false, context, itemsToDownload); + DownloadRequester.getInstance().downloadMedia(false, context, false, itemsToDownload); } catch (DownloadRequestException e) { e.printStackTrace(); } diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java index 5b7e62964..0695b1d04 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java @@ -105,8 +105,9 @@ public final class DBTasks { * enqueuing Feeds for download from a previous call * * @param context Might be used for accessing the database + * @param initiatedByUser a boolean indicating if the refresh was triggered by user action. */ - public static void refreshAllFeeds(final Context context) { + public static void refreshAllFeeds(final Context context, boolean initiatedByUser) { if (!isRefreshing.compareAndSet(false, true)) { Log.d(TAG, "Ignoring request to refresh all feeds: Refresh lock is locked"); return; @@ -116,7 +117,7 @@ public final class DBTasks { throw new IllegalStateException("DBTasks.refreshAllFeeds() must not be called from the main thread."); } - refreshFeeds(context, DBReader.getFeedList()); + refreshFeeds(context, DBReader.getFeedList(), initiatedByUser); isRefreshing.set(false); SharedPreferences prefs = context.getSharedPreferences(PREF_NAME, MODE_PRIVATE); @@ -134,9 +135,11 @@ public final class DBTasks { /** * @param context * @param feedList the list of feeds to refresh + * @param initiatedByUser a boolean indicating if the refresh was triggered by user action. */ private static void refreshFeeds(final Context context, - final List<Feed> feedList) { + final List<Feed> feedList, + boolean initiatedByUser) { for (Feed feed : feedList) { FeedPreferences prefs = feed.getPreferences(); @@ -148,11 +151,12 @@ public final class DBTasks { } catch (DownloadRequestException e) { e.printStackTrace(); DBWriter.addDownloadStatus( - new DownloadStatus(feed, feed - .getHumanReadableIdentifier(), - DownloadError.ERROR_REQUEST_ERROR, false, e - .getMessage() - ) + new DownloadStatus(feed, + feed.getHumanReadableIdentifier(), + DownloadError.ERROR_REQUEST_ERROR, + false, + e.getMessage(), + initiatedByUser) ); } } @@ -168,15 +172,16 @@ public final class DBTasks { */ public static void forceRefreshCompleteFeed(final Context context, final Feed feed) { try { - refreshFeed(context, feed, true, true); + refreshFeed(context, feed, true, true, false); } catch (DownloadRequestException e) { e.printStackTrace(); DBWriter.addDownloadStatus( - new DownloadStatus(feed, feed - .getHumanReadableIdentifier(), - DownloadError.ERROR_REQUEST_ERROR, false, e - .getMessage() - ) + new DownloadStatus(feed, + feed.getHumanReadableIdentifier(), + DownloadError.ERROR_REQUEST_ERROR, + false, + e.getMessage(), + false) ); } } @@ -196,7 +201,7 @@ public final class DBTasks { nextFeed.setPageNr(pageNr); nextFeed.setPaged(true); nextFeed.setId(feed.getId()); - DownloadRequester.getInstance().downloadFeed(context, nextFeed, loadAllPages, false); + DownloadRequester.getInstance().downloadFeed(context, nextFeed, loadAllPages, false, true); } else { Log.e(TAG, "loadNextPageOfFeed: Feed was either not paged or contained no nextPageLink"); } @@ -212,7 +217,7 @@ public final class DBTasks { private static void refreshFeed(Context context, Feed feed) throws DownloadRequestException { Log.d(TAG, "refreshFeed(feed.id: " + feed.getId() +")"); - refreshFeed(context, feed, false, false); + refreshFeed(context, feed, false, false, false); } /** @@ -221,13 +226,13 @@ public final class DBTasks { * @param context Used for requesting the download. * @param feed The Feed object. */ - public static void forceRefreshFeed(Context context, Feed feed) + public static void forceRefreshFeed(Context context, Feed feed, boolean initiatedByUser) throws DownloadRequestException { Log.d(TAG, "refreshFeed(feed.id: " + feed.getId() +")"); - refreshFeed(context, feed, false, true); + refreshFeed(context, feed, false, true, initiatedByUser); } - private static void refreshFeed(Context context, Feed feed, boolean loadAllPages, boolean force) + private static void refreshFeed(Context context, Feed feed, boolean loadAllPages, boolean force, boolean initiatedByUser) throws DownloadRequestException { Feed f; String lastUpdate = feed.hasLastUpdateFailed() ? null : feed.getLastUpdate(); @@ -238,7 +243,7 @@ public final class DBTasks { feed.getPreferences().getUsername(), feed.getPreferences().getPassword()); } f.setId(feed.getId()); - DownloadRequester.getInstance().downloadFeed(context, f, loadAllPages, force); + DownloadRequester.getInstance().downloadFeed(context, f, loadAllPages, force, initiatedByUser); } /** diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DownloadRequester.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DownloadRequester.java index 8bd9afe38..6f9e6b056 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/DownloadRequester.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DownloadRequester.java @@ -113,9 +113,9 @@ public class DownloadRequester implements DownloadStateProvider { } @Nullable - private DownloadRequest createRequest(FeedFile item, FeedFile container, File dest, - boolean overwriteIfExists, String username, String password, - String lastModified, boolean deleteOnFailure, Bundle arguments) { + private DownloadRequest createRequest(FeedFile item, FeedFile container, File dest, boolean overwriteIfExists, + String username, String password, String lastModified, + boolean deleteOnFailure, Bundle arguments, boolean initiatedByUser) { final boolean partiallyDownloadedFileExists = item.getFile_url() != null && new File(item.getFile_url()).exists(); Log.d(TAG, "partiallyDownloadedFileExists: " + partiallyDownloadedFileExists); @@ -156,7 +156,7 @@ public class DownloadRequester implements DownloadStateProvider { String baseUrl = (container != null) ? container.getDownload_url() : null; item.setDownload_url(URLChecker.prepareURL(item.getDownload_url(), baseUrl)); - DownloadRequest.Builder builder = new DownloadRequest.Builder(dest.toString(), item) + DownloadRequest.Builder builder = new DownloadRequest.Builder(dest.toString(), item, initiatedByUser) .withAuthentication(username, password) .lastModified(lastModified) .deleteOnFailure(deleteOnFailure) @@ -191,7 +191,7 @@ public class DownloadRequester implements DownloadStateProvider { * @param loadAllPages Set to true to download all pages */ public synchronized void downloadFeed(Context context, Feed feed, boolean loadAllPages, - boolean force) + boolean force, boolean initiatedByUser) throws DownloadRequestException { if (feedFileValid(feed)) { String username = (feed.getPreferences() != null) ? feed.getPreferences().getUsername() : null; @@ -203,7 +203,8 @@ public class DownloadRequester implements DownloadStateProvider { args.putBoolean(REQUEST_ARG_LOAD_ALL_PAGES, loadAllPages); DownloadRequest request = createRequest(feed, null, new File(getFeedfilePath(), getFeedfileName(feed)), - true, username, password, lastModified, true, args); + true, username, password, lastModified, true, args, initiatedByUser + ); if (request != null) { download(context, request); } @@ -211,18 +212,18 @@ public class DownloadRequester implements DownloadStateProvider { } public synchronized void downloadFeed(Context context, Feed feed) throws DownloadRequestException { - downloadFeed(context, feed, false, false); + downloadFeed(context, feed, false, false, true); } - public synchronized void downloadMedia(@NonNull Context context, FeedItem... feedItems) + public synchronized void downloadMedia(@NonNull Context context, boolean initiatedByUser, FeedItem... feedItems) throws DownloadRequestException { - downloadMedia(true, context, feedItems); + downloadMedia(true, context, initiatedByUser, feedItems); } @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) public synchronized void downloadMedia(boolean performAutoCleanup, @NonNull Context context, - FeedItem... items) + boolean initiatedByUser, FeedItem... items) throws DownloadRequestException { Log.d(TAG, "downloadMedia() called with: performAutoCleanup = [" + performAutoCleanup + "], #items = [" + items.length + "]"); @@ -230,7 +231,7 @@ public class DownloadRequester implements DownloadStateProvider { List<DownloadRequest> requests = new ArrayList<>(items.length); for (FeedItem item : items) { try { - DownloadRequest request = createRequest(item.getMedia()); + DownloadRequest request = createRequest(item.getMedia(), initiatedByUser); if (request != null) { requests.add(request); } @@ -246,7 +247,7 @@ public class DownloadRequester implements DownloadStateProvider { .getMedia() .getHumanReadableIdentifier(), DownloadError.ERROR_REQUEST_ERROR, - false, e.getMessage() + false, e.getMessage(), initiatedByUser ) ); } @@ -256,7 +257,7 @@ public class DownloadRequester implements DownloadStateProvider { } @Nullable - private DownloadRequest createRequest(@Nullable FeedMedia feedmedia) + private DownloadRequest createRequest(@Nullable FeedMedia feedmedia, boolean initiatedByUser) throws DownloadRequestException { if (!feedFileValid(feedmedia)) { return null; @@ -278,8 +279,7 @@ public class DownloadRequester implements DownloadStateProvider { } else { dest = new File(getMediafilePath(feedmedia), getMediafilename(feedmedia)); } - return createRequest(feedmedia, feed, - dest, false, username, password, null, false, null); + return createRequest(feedmedia, feed, dest, false, username, password, null, false, null, initiatedByUser); } /** diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/download/AutoUpdateManager.java b/core/src/main/java/de/danoeh/antennapod/core/util/download/AutoUpdateManager.java index e093a7e00..fc04d82cc 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/download/AutoUpdateManager.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/download/AutoUpdateManager.java @@ -115,7 +115,7 @@ public class AutoUpdateManager { public static void runImmediate(@NonNull Context context) { Log.d(TAG, "Run auto update immediately in background."); new Thread(() -> { - DBTasks.refreshAllFeeds(context.getApplicationContext()); + DBTasks.refreshAllFeeds(context.getApplicationContext(), true); }, "ManualRefreshAllFeeds").start(); } diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/gui/NotificationUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/gui/NotificationUtils.java index 02e98ba84..f546ca019 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/gui/NotificationUtils.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/gui/NotificationUtils.java @@ -14,6 +14,7 @@ public class NotificationUtils { public static final String CHANNEL_ID_DOWNLOADING = "downloading"; public static final String CHANNEL_ID_PLAYING = "playing"; public static final String CHANNEL_ID_ERROR = "error"; + public static final String CHANNEL_ID_AUTO_DOWNLOAD = "auto_download"; public static void createChannels(Context context) { if (android.os.Build.VERSION.SDK_INT < 26) { @@ -26,6 +27,7 @@ public class NotificationUtils { mNotificationManager.createNotificationChannel(createChannelDownloading(context)); mNotificationManager.createNotificationChannel(createChannelPlaying(context)); mNotificationManager.createNotificationChannel(createChannelError(context)); + mNotificationManager.createNotificationChannel(createChannelAutoDownload(context)); } } @@ -62,4 +64,12 @@ public class NotificationUtils { mChannel.setDescription(c.getString(R.string.notification_channel_error_description)); return mChannel; } + + @RequiresApi(api = Build.VERSION_CODES.O) + private static NotificationChannel createChannelAutoDownload(Context c) { + NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID_AUTO_DOWNLOAD, + c.getString(R.string.notification_channel_auto_download), NotificationManager.IMPORTANCE_DEFAULT); + mChannel.setDescription(c.getString(R.string.notification_channel_episode_auto_download)); + return mChannel; + } } diff --git a/core/src/main/res/drawable/auto_download_complete.xml b/core/src/main/res/drawable/auto_download_complete.xml new file mode 100644 index 000000000..500d1e156 --- /dev/null +++ b/core/src/main/res/drawable/auto_download_complete.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="48dp" + android:height="48dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM16.59,7.58L10,14.17l-2.59,-2.58L6,13l4,4 8,-8z" + android:fillColor="#ffffff"/> +</vector> diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml index c65aed322..2f80ac416 100644 --- a/core/src/main/res/values/strings.xml +++ b/core/src/main/res/values/strings.xml @@ -237,6 +237,7 @@ <string name="download_canceled_msg">Download canceled</string> <string name="download_canceled_autodownload_enabled_msg">Download canceled\nDisabled <i>Auto Download</i> for this item</string> <string name="download_report_title">Downloads completed with error(s)</string> + <string name="auto_download_report_title">Auto-downloads completed</string> <string name="download_report_content_title">Download Report</string> <string name="download_error_malformed_url">Malformed URL</string> <string name="download_error_io_error">IO Error</string> @@ -470,6 +471,8 @@ <string name="pref_lockscreen_background_sum">Set the lockscreen background to the current episode\'s image. As a side effect, this will also show the image in third party apps.</string> <string name="pref_showDownloadReport_title">Show Download Report</string> <string name="pref_showDownloadReport_sum">If downloads fail, generate a report that shows the details of the failure.</string> + <string name="pref_showAutoDownloadReport_title">Show Auto Download Report</string> + <string name="pref_showAutoDownloadReport_sum">Show a notification for automatically downloaded episodes.</string> <string name="pref_expand_notify_unsupport_toast">Android versions before 4.1 do not support expanded notifications.</string> <string name="pref_enqueue_location_title">Enqueue Location</string> <string name="pref_enqueue_location_sum">Add episodes to: %1$s</string> @@ -781,6 +784,8 @@ <string name="notification_channel_playing_description">Allows to control playback. This is the main notification you see while playing a podcast.</string> <string name="notification_channel_error">Errors</string> <string name="notification_channel_error_description">Shown if something went wrong, for example if download or gpodder sync fails.</string> + <string name="notification_channel_auto_download">Auto Downloads</string> + <string name="notification_channel_episode_auto_download">Shown when episodes have been automatically downloaded.</string> <!-- Widget settings --> <string name="widget_settings">Widget settings</string> |