diff options
18 files changed, 263 insertions, 54 deletions
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java index 568b56304..fb9ac4a59 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java @@ -159,6 +159,7 @@ public class FeedSettingsFragment extends Fragment { setupEpisodeFilterPreference(); setupPlaybackSpeedPreference(); setupFeedAutoSkipPreference(); + setupEpisodeNotificationPreference(); updateAutoDeleteSummary(); updateVolumeReductionValue(); @@ -394,6 +395,19 @@ public class FeedSettingsFragment extends Fragment { } } + private void setupEpisodeNotificationPreference() { + SwitchPreferenceCompat pref = findPreference("episodeNotification"); + + pref.setChecked(feedPreferences.getShowEpisodeNotification()); + pref.setOnPreferenceChangeListener((preference, newValue) -> { + boolean checked = newValue == Boolean.TRUE; + feedPreferences.setShowEpisodeNotification(checked); + feed.savePreferences(); + pref.setChecked(checked); + return false; + }); + } + private class ApplyToEpisodesDialog extends ConfirmationDialog { private final boolean autoDownload; diff --git a/app/src/main/res/xml/feed_settings.xml b/app/src/main/res/xml/feed_settings.xml index 9d5ed5e8b..13288fbda 100644 --- a/app/src/main/res/xml/feed_settings.xml +++ b/app/src/main/res/xml/feed_settings.xml @@ -9,6 +9,14 @@ android:title="@string/keep_updated" android:summary="@string/keep_updated_summary"/> + <SwitchPreferenceCompat + android:key="episodeNotification" + android:defaultValue="false" + android:dependency="keepUpdated" + android:icon="?attr/ic_notifications" + android:title="@string/episode_notification" + android:summary="@string/episode_notification_summary"/> + <Preference android:key="authentication" android:icon="?attr/ic_key" 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<Downloader> 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<Feed> 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<Feed> feeds = subscriptionsFilter.filter(getFeedList(adapter), feedCounters); Comparator<Feed> 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)); } } @@ -111,6 +113,15 @@ public class NotificationUtils { } @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, c.getString(R.string.notification_group_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 Binary files differnew file mode 100644 index 000000000..28a8446e4 --- /dev/null +++ b/core/src/main/res/drawable-hdpi/ic_notification_new.png diff --git a/core/src/main/res/drawable-mdpi/ic_notification_new.png b/core/src/main/res/drawable-mdpi/ic_notification_new.png Binary files differnew file mode 100644 index 000000000..02530f5e4 --- /dev/null +++ b/core/src/main/res/drawable-mdpi/ic_notification_new.png diff --git a/core/src/main/res/drawable-xhdpi/ic_notification_new.png b/core/src/main/res/drawable-xhdpi/ic_notification_new.png Binary files differnew file mode 100644 index 000000000..49c696798 --- /dev/null +++ b/core/src/main/res/drawable-xhdpi/ic_notification_new.png diff --git a/core/src/main/res/drawable-xxhdpi/ic_notification_new.png b/core/src/main/res/drawable-xxhdpi/ic_notification_new.png Binary files differnew file mode 100644 index 000000000..ec6ef4f1e --- /dev/null +++ b/core/src/main/res/drawable-xxhdpi/ic_notification_new.png diff --git a/core/src/main/res/drawable-xxxhdpi/ic_notification_new.png b/core/src/main/res/drawable-xxxhdpi/ic_notification_new.png Binary files differnew file mode 100644 index 000000000..66f968872 --- /dev/null +++ b/core/src/main/res/drawable-xxxhdpi/ic_notification_new.png 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 @@ -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="24dp" - android:height="24dp" - 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 a52b30512..64554bc38 100644 --- a/core/src/main/res/values/strings.xml +++ b/core/src/main/res/values/strings.xml @@ -132,6 +132,17 @@ <item quantity="other">%d episodes</item> </plurals> <string name="loading_more">Loading moreā¦</string> + <string name="episode_notification">Episode Notifications</string> + <string name="episode_notification_summary">Show a notification when a new episode is released.</string> + <plurals name="new_episode_notification_message"> + <item quantity="one">%2$s has a new episode</item> + <item quantity="other">%2$s has %1$d new episodes</item> + </plurals> + <plurals name="new_episode_notification_title"> + <item quantity="one">New Episode</item> + <item quantity="other">New Episodes</item> + </plurals> + <string name="new_episode_notification_group_text">Your subscriptions have new epsiodes.</string> <!-- Actions on feeds --> <string name="mark_all_read_label">Mark all as played</string> @@ -870,6 +881,8 @@ <string name="notification_channel_sync_error_description">Shown when gpodder synchronization fails.</string> <string name="notification_channel_auto_download">Automatic download completed</string> <string name="notification_channel_episode_auto_download">Shown when episodes have been automatically downloaded.</string> + <string name="notification_channel_new_episode">New Episode</string> + <string name="notification_channel_new_episode_description">Shown when a new episode of a podcast was found, where notifications are enabled</string> <!-- Widget settings --> <string name="widget_settings">Widget settings</string> |