From 03c71ee6c52820317b6cc2fdff10ebc624360507 Mon Sep 17 00:00:00 2001 From: Connectety Date: Fri, 22 Jan 2021 17:27:46 +0100 Subject: Add Notifications for episodes (#4646) Co-authored-by: ByteHamster --- .../antennapod/core/feed/FeedPreferences.java | 59 ++++++---- .../core/service/download/DownloadService.java | 11 +- .../download/DownloadServiceNotification.java | 2 +- .../service/download/NewEpisodesNotification.java | 131 +++++++++++++++++++++ .../service/download/handler/FeedSyncTask.java | 8 +- .../danoeh/antennapod/core/storage/DBReader.java | 12 +- .../danoeh/antennapod/core/storage/DBUpgrader.java | 4 + .../antennapod/core/storage/PodDBAdapter.java | 35 ++++-- .../core/util/gui/NotificationUtils.java | 11 ++ .../main/res/drawable-hdpi/ic_notification_new.png | Bin 0 -> 1054 bytes .../main/res/drawable-mdpi/ic_notification_new.png | Bin 0 -> 688 bytes .../res/drawable-xhdpi/ic_notification_new.png | Bin 0 -> 1402 bytes .../res/drawable-xxhdpi/ic_notification_new.png | Bin 0 -> 2207 bytes .../res/drawable-xxxhdpi/ic_notification_new.png | Bin 0 -> 3012 bytes .../ic_notification_auto_download_complete.xml | 9 -- core/src/main/res/values/strings.xml | 13 ++ 16 files changed, 241 insertions(+), 54 deletions(-) create mode 100644 core/src/main/java/de/danoeh/antennapod/core/service/download/NewEpisodesNotification.java create mode 100644 core/src/main/res/drawable-hdpi/ic_notification_new.png create mode 100644 core/src/main/res/drawable-mdpi/ic_notification_new.png create mode 100644 core/src/main/res/drawable-xhdpi/ic_notification_new.png create mode 100644 core/src/main/res/drawable-xxhdpi/ic_notification_new.png create mode 100644 core/src/main/res/drawable-xxxhdpi/ic_notification_new.png delete mode 100644 core/src/main/res/drawable/ic_notification_auto_download_complete.xml (limited to 'core/src/main') diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedPreferences.java b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedPreferences.java index 5ffee0d62..21474db89 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedPreferences.java +++ b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedPreferences.java @@ -16,37 +16,40 @@ public class FeedPreferences { public static final float SPEED_USE_GLOBAL = -1; - @NonNull - private FeedFilter filter; - private long feedID; - private boolean autoDownload; - private boolean keepUpdated; - public enum AutoDeleteAction { GLOBAL, YES, NO } - private AutoDeleteAction auto_delete_action; + @NonNull + private FeedFilter filter; + private long feedID; + private boolean autoDownload; + private boolean keepUpdated; + private AutoDeleteAction autoDeleteAction; private VolumeAdaptionSetting volumeAdaptionSetting; - private String username; private String password; private float feedPlaybackSpeed; private int feedSkipIntro; private int feedSkipEnding; + private boolean showEpisodeNotification; - public FeedPreferences(long feedID, boolean autoDownload, AutoDeleteAction auto_delete_action, VolumeAdaptionSetting volumeAdaptionSetting, String username, String password) { - this(feedID, autoDownload, true, auto_delete_action, volumeAdaptionSetting, - username, password, new FeedFilter(), SPEED_USE_GLOBAL, 0, 0); + public FeedPreferences(long feedID, boolean autoDownload, AutoDeleteAction autoDeleteAction, + VolumeAdaptionSetting volumeAdaptionSetting, String username, String password) { + this(feedID, autoDownload, true, autoDeleteAction, volumeAdaptionSetting, + username, password, new FeedFilter(), SPEED_USE_GLOBAL, 0, 0, false); } - private FeedPreferences(long feedID, boolean autoDownload, boolean keepUpdated, AutoDeleteAction auto_delete_action, VolumeAdaptionSetting volumeAdaptionSetting, String username, String password, @NonNull FeedFilter filter, float feedPlaybackSpeed, int feedSkipIntro, int feedSkipEnding) { + private FeedPreferences(long feedID, boolean autoDownload, boolean keepUpdated, + AutoDeleteAction autoDeleteAction, VolumeAdaptionSetting volumeAdaptionSetting, + String username, String password, @NonNull FeedFilter filter, float feedPlaybackSpeed, + int feedSkipIntro, int feedSkipEnding, boolean showEpisodeNotification) { this.feedID = feedID; this.autoDownload = autoDownload; this.keepUpdated = keepUpdated; - this.auto_delete_action = auto_delete_action; + this.autoDeleteAction = autoDeleteAction; this.volumeAdaptionSetting = volumeAdaptionSetting; this.username = username; this.password = password; @@ -54,6 +57,7 @@ public class FeedPreferences { this.feedPlaybackSpeed = feedPlaybackSpeed; this.feedSkipIntro = feedSkipIntro; this.feedSkipEnding = feedSkipEnding; + this.showEpisodeNotification = showEpisodeNotification; } public static FeedPreferences fromCursor(Cursor cursor) { @@ -69,6 +73,7 @@ public class FeedPreferences { int indexFeedPlaybackSpeed = cursor.getColumnIndex(PodDBAdapter.KEY_FEED_PLAYBACK_SPEED); int indexAutoSkipIntro = cursor.getColumnIndex(PodDBAdapter.KEY_FEED_SKIP_INTRO); int indexAutoSkipEnding = cursor.getColumnIndex(PodDBAdapter.KEY_FEED_SKIP_ENDING); + int indexEpisodeNotification = cursor.getColumnIndex(PodDBAdapter.KEY_EPISODE_NOTIFICATION); long feedId = cursor.getLong(indexId); boolean autoDownload = cursor.getInt(indexAutoDownload) > 0; @@ -84,6 +89,7 @@ public class FeedPreferences { float feedPlaybackSpeed = cursor.getFloat(indexFeedPlaybackSpeed); int feedAutoSkipIntro = cursor.getInt(indexAutoSkipIntro); int feedAutoSkipEnding = cursor.getInt(indexAutoSkipEnding); + boolean showNotification = cursor.getInt(indexEpisodeNotification) > 0; return new FeedPreferences(feedId, autoDownload, autoRefresh, @@ -94,7 +100,8 @@ public class FeedPreferences { new FeedFilter(includeFilter, excludeFilter), feedPlaybackSpeed, feedAutoSkipIntro, - feedAutoSkipEnding + feedAutoSkipEnding, + showNotification ); } @@ -168,15 +175,15 @@ public class FeedPreferences { } public AutoDeleteAction getAutoDeleteAction() { - return auto_delete_action; + return autoDeleteAction; } public VolumeAdaptionSetting getVolumeAdaptionSetting() { return volumeAdaptionSetting; } - public void setAutoDeleteAction(AutoDeleteAction auto_delete_action) { - this.auto_delete_action = auto_delete_action; + public void setAutoDeleteAction(AutoDeleteAction autoDeleteAction) { + this.autoDeleteAction = autoDeleteAction; } public void setVolumeAdaptionSetting(VolumeAdaptionSetting volumeAdaptionSetting) { @@ -184,17 +191,15 @@ public class FeedPreferences { } public boolean getCurrentAutoDelete() { - switch (auto_delete_action) { + switch (autoDeleteAction) { case GLOBAL: return UserPreferences.isAutoDelete(); - case YES: return true; - case NO: + default: // fall-through return false; } - return false; // TODO - add exceptions here } public void save(Context context) { @@ -240,4 +245,16 @@ public class FeedPreferences { public int getFeedSkipEnding() { return feedSkipEnding; } + + /** + * getter for preference if notifications should be display for new episodes. + * @return true for displaying notifications + */ + public boolean getShowEpisodeNotification() { + return showEpisodeNotification; + } + + public void setShowEpisodeNotification(boolean showEpisodeNotification) { + this.showEpisodeNotification = showEpisodeNotification; + } } 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 6dbc4a82f..025088273 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 @@ -95,6 +95,7 @@ public class DownloadService extends Service { private final CompletionService downloadExecutor; private final DownloadRequester requester; private DownloadServiceNotification notificationManager; + private final NewEpisodesNotification newEpisodesNotification; /** * Currently running downloads. @@ -117,7 +118,7 @@ public class DownloadService extends Service { private ScheduledFuture notificationUpdaterFuture; private ScheduledFuture downloadPostFuture; private static final int SCHED_EX_POOL_SIZE = 1; - private ScheduledThreadPoolExecutor schedExecutor; + private final ScheduledThreadPoolExecutor schedExecutor; private static DownloaderFactory downloaderFactory = new DefaultDownloaderFactory(); private final IBinder mBinder = new LocalBinder(); @@ -133,12 +134,16 @@ public class DownloadService extends Service { downloads = Collections.synchronizedList(new ArrayList<>()); numberOfDownloads = new AtomicInteger(0); requester = DownloadRequester.getInstance(); + newEpisodesNotification = new NewEpisodesNotification(); syncExecutor = Executors.newSingleThreadExecutor(r -> { Thread t = new Thread(r, "SyncThread"); t.setPriority(Thread.MIN_PRIORITY); return t; }); + // Must be the first runnable in syncExecutor + syncExecutor.execute(newEpisodesNotification::loadCountersBeforeRefresh); + Log.d(TAG, "parallel downloads: " + UserPreferences.getParallelDownloads()); downloadExecutor = new ExecutorCompletionService<>( Executors.newFixedThreadPool(UserPreferences.getParallelDownloads(), @@ -289,6 +294,10 @@ public class DownloadService extends Service { if (log.size() > 0 && !log.get(0).isSuccessful()) { saveDownloadStatus(task.getDownloadStatus()); } + if (request.getFeedfileId() != 0 && !request.isInitiatedByUser()) { + // Was stored in the database before and not initiated manually + newEpisodesNotification.showIfNeeded(DownloadService.this, task.getSavedFeed()); + } } else { DBWriter.setFeedLastUpdateFailed(request.getFeedfileId(), true); saveDownloadStatus(task.getDownloadStatus()); 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 fb6009c02..4fae9f34a 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 @@ -143,7 +143,7 @@ public class DownloadServiceNotification { // 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_auto_download_complete; + iconId = R.drawable.ic_notification_new; intent = ClientConfig.downloadServiceCallbacks.getAutoDownloadReportNotificationContentIntent(context); id = R.id.notification_auto_download_report; content = createAutoDownloadNotificationContent(reportQueue); diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/NewEpisodesNotification.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/NewEpisodesNotification.java new file mode 100644 index 000000000..749f7b136 --- /dev/null +++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/NewEpisodesNotification.java @@ -0,0 +1,131 @@ +package de.danoeh.antennapod.core.service.download; + +import android.app.Notification; +import android.app.PendingIntent; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; + +import android.graphics.Bitmap; +import android.util.Log; +import androidx.core.app.NotificationCompat; +import androidx.core.app.NotificationManagerCompat; + +import com.bumptech.glide.Glide; +import com.bumptech.glide.request.RequestOptions; +import de.danoeh.antennapod.core.R; +import de.danoeh.antennapod.core.feed.Feed; +import de.danoeh.antennapod.core.feed.FeedPreferences; +import de.danoeh.antennapod.core.feed.util.ImageResourceUtils; +import de.danoeh.antennapod.core.glide.ApGlideSettings; +import de.danoeh.antennapod.core.preferences.UserPreferences; +import de.danoeh.antennapod.core.storage.PodDBAdapter; +import de.danoeh.antennapod.core.util.LongIntMap; +import de.danoeh.antennapod.core.util.gui.NotificationUtils; + +public class NewEpisodesNotification { + private static final String TAG = "NewEpisodesNotification"; + private static final String GROUP_KEY = "de.danoeh.antennapod.EPISODES"; + + private LongIntMap countersBefore; + + public NewEpisodesNotification() { + } + + public void loadCountersBeforeRefresh() { + PodDBAdapter adapter = PodDBAdapter.getInstance(); + adapter.open(); + countersBefore = adapter.getFeedCounters(UserPreferences.FEED_COUNTER_SHOW_NEW); + adapter.close(); + } + + public void showIfNeeded(Context context, Feed feed) { + FeedPreferences prefs = feed.getPreferences(); + if (!prefs.getKeepUpdated() || !prefs.getShowEpisodeNotification()) { + return; + } + + int newEpisodesBefore = countersBefore.get(feed.getId()); + int newEpisodesAfter = getNewEpisodeCount(feed.getId()); + + Log.d(TAG, "New episodes before: " + newEpisodesBefore + ", after: " + newEpisodesAfter); + if (newEpisodesAfter > newEpisodesBefore) { + NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context); + showNotification(newEpisodesAfter, feed, context, notificationManager); + } + } + + private static void showNotification(int newEpisodes, Feed feed, Context context, + NotificationManagerCompat notificationManager) { + Resources res = context.getResources(); + String text = res.getQuantityString( + R.plurals.new_episode_notification_message, newEpisodes, newEpisodes, feed.getTitle() + ); + String title = res.getQuantityString(R.plurals.new_episode_notification_title, newEpisodes); + + Intent intent = new Intent(); + intent.setAction("NewEpisodes" + feed.getId()); + intent.setComponent(new ComponentName(context, "de.danoeh.antennapod.activity.MainActivity")); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + intent.putExtra("fragment_feed_id", feed.getId()); + PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0); + + Notification notification = new NotificationCompat.Builder( + context, NotificationUtils.CHANNEL_ID_EPISODE_NOTIFICATIONS) + .setSmallIcon(R.drawable.ic_notification_new) + .setContentTitle(title) + .setLargeIcon(loadIcon(context, feed)) + .setContentText(text) + .setContentIntent(pendingIntent) + .setGroup(GROUP_KEY) + .setAutoCancel(true) + .build(); + + notificationManager.notify(NotificationUtils.CHANNEL_ID_EPISODE_NOTIFICATIONS, feed.hashCode(), notification); + showGroupSummaryNotification(context, notificationManager); + } + + private static void showGroupSummaryNotification(Context context, NotificationManagerCompat notificationManager) { + Intent intent = new Intent(); + intent.setAction("NewEpisodes"); + intent.setComponent(new ComponentName(context, "de.danoeh.antennapod.activity.MainActivity")); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + intent.putExtra("fragment_tag", "EpisodesFragment"); + PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0); + + Notification notificationGroupSummary = new NotificationCompat.Builder( + context, NotificationUtils.CHANNEL_ID_EPISODE_NOTIFICATIONS) + .setSmallIcon(R.drawable.ic_notification_new) + .setContentTitle(context.getString(R.string.new_episode_notification_group_text)) + .setContentIntent(pendingIntent) + .setGroup(GROUP_KEY) + .setGroupSummary(true) + .setAutoCancel(true) + .build(); + notificationManager.notify(NotificationUtils.CHANNEL_ID_EPISODE_NOTIFICATIONS, 0, notificationGroupSummary); + } + + private static Bitmap loadIcon(Context context, Feed feed) { + int iconSize = (int) (128 * context.getResources().getDisplayMetrics().density); + try { + return Glide.with(context) + .asBitmap() + .load(ImageResourceUtils.getImageLocation(feed)) + .apply(RequestOptions.diskCacheStrategyOf(ApGlideSettings.AP_DISK_CACHE_STRATEGY)) + .apply(new RequestOptions().centerCrop()) + .submit(iconSize, iconSize) + .get(); + } catch (Throwable tr) { + return null; + } + } + + private static int getNewEpisodeCount(long feedId) { + PodDBAdapter adapter = PodDBAdapter.getInstance(); + adapter.open(); + int episodeCount = adapter.getFeedCounters(UserPreferences.FEED_COUNTER_SHOW_NEW, feedId).get(feedId); + adapter.close(); + return episodeCount; + } +} diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedSyncTask.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedSyncTask.java index 483a2aa56..e2d9ee614 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedSyncTask.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedSyncTask.java @@ -2,6 +2,7 @@ package de.danoeh.antennapod.core.service.download.handler; import android.content.Context; import android.util.Log; + import de.danoeh.antennapod.core.feed.Feed; import de.danoeh.antennapod.core.service.download.DownloadRequest; import de.danoeh.antennapod.core.service.download.DownloadStatus; @@ -15,6 +16,7 @@ public class FeedSyncTask { private final DownloadRequest request; private final Context context; private DownloadStatus downloadStatus; + private Feed savedFeed; public FeedSyncTask(Context context, DownloadRequest request) { this.request = request; @@ -30,7 +32,7 @@ public class FeedSyncTask { return false; } - Feed savedFeed = DBTasks.updateFeed(context, result.feed, false); + savedFeed = DBTasks.updateFeed(context, result.feed, false); // If loadAllPages=true, check if another page is available and queue it for download final boolean loadAllPages = request.getArguments().getBoolean(DownloadRequester.REQUEST_ARG_LOAD_ALL_PAGES); final Feed feed = result.feed; @@ -48,4 +50,8 @@ public class FeedSyncTask { public DownloadStatus getDownloadStatus() { return downloadStatus; } + + public Feed getSavedFeed() { + return savedFeed; + } } diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java index ee46f4e62..27f08243f 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java @@ -802,15 +802,9 @@ public final class DBReader { PodDBAdapter adapter = PodDBAdapter.getInstance(); adapter.open(); - List feeds = getFeedList(adapter); - long[] feedIds = new long[feeds.size()]; - for (int i = 0; i < feeds.size(); i++) { - feedIds[i] = feeds.get(i).getId(); - } - final LongIntMap feedCounters = adapter.getFeedCounters(feedIds); - + final LongIntMap feedCounters = adapter.getFeedCounters(); SubscriptionsFilter subscriptionsFilter = UserPreferences.getSubscriptionsFilter(); - feeds = subscriptionsFilter.filter(getFeedList(adapter), feedCounters); + List feeds = subscriptionsFilter.filter(getFeedList(adapter), feedCounters); Comparator comparator; int feedOrder = UserPreferences.getFeedOrder(); @@ -840,7 +834,7 @@ public final class DBReader { } }; } else if (feedOrder == UserPreferences.FEED_ORDER_MOST_PLAYED) { - final LongIntMap playedCounters = adapter.getPlayedEpisodesCounters(feedIds); + final LongIntMap playedCounters = adapter.getPlayedEpisodesCounters(); comparator = (lhs, rhs) -> { long counterLhs = playedCounters.get(lhs.getId()); diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DBUpgrader.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DBUpgrader.java index 8574ff33b..93bc74ea8 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/DBUpgrader.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DBUpgrader.java @@ -310,6 +310,10 @@ class DBUpgrader { db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS + " ADD COLUMN " + PodDBAdapter.KEY_FEED_SKIP_ENDING + " INTEGER DEFAULT 0;"); } + if (oldVersion < 2020000) { + db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS + + " ADD COLUMN " + PodDBAdapter.KEY_EPISODE_NOTIFICATION + " INTEGER DEFAULT 0;"); + } } } diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java b/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java index 4a06829ce..8d1352a10 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java @@ -51,7 +51,7 @@ public class PodDBAdapter { private static final String TAG = "PodDBAdapter"; public static final String DATABASE_NAME = "Antennapod.db"; - public static final int VERSION = 1090001; + public static final int VERSION = 2020000; /** * Maximum number of arguments for IN-operator. @@ -115,6 +115,7 @@ public class PodDBAdapter { public static final String KEY_FEED_PLAYBACK_SPEED = "feed_playback_speed"; public static final String KEY_FEED_SKIP_INTRO = "feed_skip_intro"; public static final String KEY_FEED_SKIP_ENDING = "feed_skip_ending"; + public static final String KEY_EPISODE_NOTIFICATION = "episode_notification"; // Table names public static final String TABLE_NAME_FEEDS = "Feeds"; @@ -152,7 +153,8 @@ public class PodDBAdapter { + KEY_FEED_PLAYBACK_SPEED + " REAL DEFAULT " + SPEED_USE_GLOBAL + "," + KEY_FEED_VOLUME_ADAPTION + " INTEGER DEFAULT 0," + KEY_FEED_SKIP_INTRO + " INTEGER DEFAULT 0," - + KEY_FEED_SKIP_ENDING + " INTEGER DEFAULT 0)"; + + KEY_FEED_SKIP_ENDING + " INTEGER DEFAULT 0," + + KEY_EPISODE_NOTIFICATION + " INTEGER DEFAULT 0)"; private static final String CREATE_TABLE_FEED_ITEMS = "CREATE TABLE " + TABLE_NAME_FEED_ITEMS + " (" + TABLE_PRIMARY_KEY + KEY_TITLE @@ -254,7 +256,8 @@ public class PodDBAdapter { TABLE_NAME_FEEDS + "." + KEY_EXCLUDE_FILTER, TABLE_NAME_FEEDS + "." + KEY_FEED_PLAYBACK_SPEED, TABLE_NAME_FEEDS + "." + KEY_FEED_SKIP_INTRO, - TABLE_NAME_FEEDS + "." + KEY_FEED_SKIP_ENDING + TABLE_NAME_FEEDS + "." + KEY_FEED_SKIP_ENDING, + TABLE_NAME_FEEDS + "." + KEY_EPISODE_NOTIFICATION }; /** @@ -446,6 +449,7 @@ public class PodDBAdapter { values.put(KEY_FEED_PLAYBACK_SPEED, prefs.getFeedPlaybackSpeed()); values.put(KEY_FEED_SKIP_INTRO, prefs.getFeedSkipIntro()); values.put(KEY_FEED_SKIP_ENDING, prefs.getFeedSkipEnding()); + values.put(KEY_EPISODE_NOTIFICATION, prefs.getShowEpisodeNotification()); db.update(TABLE_NAME_FEEDS, values, KEY_ID + "=?", new String[]{String.valueOf(prefs.getFeedID())}); } @@ -1164,6 +1168,11 @@ public class PodDBAdapter { public final LongIntMap getFeedCounters(long... feedIds) { int setting = UserPreferences.getFeedCounterSetting(); + + return getFeedCounters(setting, feedIds); + } + + public final LongIntMap getFeedCounters(int setting, long... feedIds) { String whereRead; switch (setting) { case UserPreferences.FEED_COUNTER_SHOW_NEW_UNPLAYED_SUM: @@ -1188,24 +1197,26 @@ public class PodDBAdapter { } private LongIntMap conditionalFeedCounterRead(String whereRead, long... feedIds) { - // work around TextUtils.join wanting only boxed items - // and StringUtils.join() causing NoSuchMethodErrors on MIUI - StringBuilder builder = new StringBuilder(); - for (long id : feedIds) { - builder.append(id); - builder.append(','); - } + String limitFeeds = ""; if (feedIds.length > 0) { + // work around TextUtils.join wanting only boxed items + // and StringUtils.join() causing NoSuchMethodErrors on MIUI + StringBuilder builder = new StringBuilder(); + for (long id : feedIds) { + builder.append(id); + builder.append(','); + } // there's an extra ',', get rid of it builder.deleteCharAt(builder.length() - 1); + limitFeeds = KEY_FEED + " IN (" + builder.toString() + ") AND "; } final String query = "SELECT " + KEY_FEED + ", COUNT(" + TABLE_NAME_FEED_ITEMS + "." + KEY_ID + ") AS count " + " FROM " + TABLE_NAME_FEED_ITEMS + " LEFT JOIN " + TABLE_NAME_FEED_MEDIA + " ON " + TABLE_NAME_FEED_ITEMS + "." + KEY_ID + "=" + TABLE_NAME_FEED_MEDIA + "." + KEY_FEEDITEM - + " WHERE " + KEY_FEED + " IN (" + builder.toString() + ") " - + " AND " + whereRead + " GROUP BY " + KEY_FEED; + + " WHERE " + limitFeeds + " " + + whereRead + " GROUP BY " + KEY_FEED; Cursor c = db.rawQuery(query, null); LongIntMap result = new LongIntMap(c.getCount()); 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 3101eac34..5895c5933 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 @@ -18,6 +18,7 @@ public class NotificationUtils { public static final String CHANNEL_ID_DOWNLOAD_ERROR = "error"; public static final String CHANNEL_ID_SYNC_ERROR = "sync_error"; public static final String CHANNEL_ID_AUTO_DOWNLOAD = "auto_download"; + public static final String CHANNEL_ID_EPISODE_NOTIFICATIONS = "episode_notifications"; public static final String GROUP_ID_ERRORS = "group_errors"; public static final String GROUP_ID_NEWS = "group_news"; @@ -38,6 +39,7 @@ public class NotificationUtils { mNotificationManager.createNotificationChannel(createChannelError(context)); mNotificationManager.createNotificationChannel(createChannelSyncError(context)); mNotificationManager.createNotificationChannel(createChannelAutoDownload(context)); + mNotificationManager.createNotificationChannel(createChannelEpisodeNotification(context)); } } @@ -110,6 +112,15 @@ public class NotificationUtils { return notificationChannel; } + @RequiresApi(api = Build.VERSION_CODES.O) + private static NotificationChannel createChannelEpisodeNotification(Context c) { + NotificationChannel channel = new NotificationChannel(CHANNEL_ID_EPISODE_NOTIFICATIONS, + c.getString(R.string.notification_channel_new_episode), NotificationManager.IMPORTANCE_DEFAULT); + channel.setDescription(c.getString(R.string.notification_channel_new_episode_description)); + channel.setGroup(GROUP_ID_NEWS); + return channel; + } + @RequiresApi(api = Build.VERSION_CODES.O) private static NotificationChannelGroup createGroupErrors(Context c) { return new NotificationChannelGroup(GROUP_ID_ERRORS, diff --git a/core/src/main/res/drawable-hdpi/ic_notification_new.png b/core/src/main/res/drawable-hdpi/ic_notification_new.png new file mode 100644 index 000000000..28a8446e4 Binary files /dev/null and b/core/src/main/res/drawable-hdpi/ic_notification_new.png differ diff --git a/core/src/main/res/drawable-mdpi/ic_notification_new.png b/core/src/main/res/drawable-mdpi/ic_notification_new.png new file mode 100644 index 000000000..02530f5e4 Binary files /dev/null and b/core/src/main/res/drawable-mdpi/ic_notification_new.png differ diff --git a/core/src/main/res/drawable-xhdpi/ic_notification_new.png b/core/src/main/res/drawable-xhdpi/ic_notification_new.png new file mode 100644 index 000000000..49c696798 Binary files /dev/null and b/core/src/main/res/drawable-xhdpi/ic_notification_new.png differ diff --git a/core/src/main/res/drawable-xxhdpi/ic_notification_new.png b/core/src/main/res/drawable-xxhdpi/ic_notification_new.png new file mode 100644 index 000000000..ec6ef4f1e Binary files /dev/null and b/core/src/main/res/drawable-xxhdpi/ic_notification_new.png differ diff --git a/core/src/main/res/drawable-xxxhdpi/ic_notification_new.png b/core/src/main/res/drawable-xxxhdpi/ic_notification_new.png new file mode 100644 index 000000000..66f968872 Binary files /dev/null and b/core/src/main/res/drawable-xxxhdpi/ic_notification_new.png differ diff --git a/core/src/main/res/drawable/ic_notification_auto_download_complete.xml b/core/src/main/res/drawable/ic_notification_auto_download_complete.xml deleted file mode 100644 index 0caf27836..000000000 --- a/core/src/main/res/drawable/ic_notification_auto_download_complete.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml index a52b30512..64554bc38 100644 --- a/core/src/main/res/values/strings.xml +++ b/core/src/main/res/values/strings.xml @@ -132,6 +132,17 @@ %d episodes Loading moreā€¦ + Episode Notifications + Show a notification when a new episode is released. + + %2$s has a new episode + %2$s has %1$d new episodes + + + New Episode + New Episodes + + Your subscriptions have new epsiodes. Mark all as played @@ -870,6 +881,8 @@ Shown when gpodder synchronization fails. Automatic download completed Shown when episodes have been automatically downloaded. + New Episode + Shown when a new episode of a podcast was found, where notifications are enabled Widget settings -- cgit v1.2.3