Add Notifications for episodes (#4646)
Co-authored-by: ByteHamster <info@bytehamster.com>
This commit is contained in:
parent
7bd20ae406
commit
03c71ee6c5
|
@ -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;
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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;");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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,6 +1197,8 @@ public class PodDBAdapter {
|
|||
}
|
||||
|
||||
private LongIntMap conditionalFeedCounterRead(String whereRead, long... feedIds) {
|
||||
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();
|
||||
|
@ -1195,17 +1206,17 @@ public class PodDBAdapter {
|
|||
builder.append(id);
|
||||
builder.append(',');
|
||||
}
|
||||
if (feedIds.length > 0) {
|
||||
// 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());
|
||||
|
|
|
@ -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,
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 1.0 KiB |
Binary file not shown.
After Width: | Height: | Size: 688 B |
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
Binary file not shown.
After Width: | Height: | Size: 2.2 KiB |
Binary file not shown.
After Width: | Height: | Size: 2.9 KiB |
|
@ -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>
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue