diff --git a/app/src/main/java/de/danoeh/antennapod/ui/episodeslist/EpisodeMultiSelectActionHandler.java b/app/src/main/java/de/danoeh/antennapod/ui/episodeslist/EpisodeMultiSelectActionHandler.java index cc098053b..a3ef938ff 100644 --- a/app/src/main/java/de/danoeh/antennapod/ui/episodeslist/EpisodeMultiSelectActionHandler.java +++ b/app/src/main/java/de/danoeh/antennapod/ui/episodeslist/EpisodeMultiSelectActionHandler.java @@ -5,6 +5,7 @@ import android.util.Log; import androidx.annotation.PluralsRes; +import java.util.ArrayList; import java.util.List; import de.danoeh.antennapod.R; @@ -48,14 +49,14 @@ public class EpisodeMultiSelectActionHandler { } private void queueChecked(List items) { - // Check if an episode actually contains any media files before adding it to queue - LongList toQueue = new LongList(items.size()); + // Count here to give accurate number in snackbar + List toQueue = new ArrayList<>(); for (FeedItem episode : items) { - if (episode.hasMedia()) { - toQueue.add(episode.getId()); + if (episode.hasMedia() && !episode.isTagged(FeedItem.TAG_QUEUE)) { + toQueue.add(episode); } } - DBWriter.addQueueItem(activity, true, toQueue.toArray()); + DBWriter.addQueueItem(activity, toQueue.toArray(new FeedItem[0])); showMessage(R.plurals.added_to_queue_batch_label, toQueue.size()); } diff --git a/app/src/main/java/de/danoeh/antennapod/ui/swipeactions/RemoveFromQueueSwipeAction.java b/app/src/main/java/de/danoeh/antennapod/ui/swipeactions/RemoveFromQueueSwipeAction.java index dbdc3126b..ac715b0cc 100644 --- a/app/src/main/java/de/danoeh/antennapod/ui/swipeactions/RemoveFromQueueSwipeAction.java +++ b/app/src/main/java/de/danoeh/antennapod/ui/swipeactions/RemoveFromQueueSwipeAction.java @@ -46,7 +46,7 @@ public class RemoveFromQueueSwipeAction implements SwipeAction { fragment.getResources().getQuantityString(R.plurals.removed_from_queue_batch_label, 1, 1), Snackbar.LENGTH_LONG) .setAction(fragment.getString(R.string.undo), v -> - DBWriter.addQueueItemAt(fragment.requireActivity(), item.getId(), position, false)); + DBWriter.addQueueItemAt(fragment.requireActivity(), item.getId(), position)); } } diff --git a/net/download/service/src/main/java/de/danoeh/antennapod/net/download/service/episode/autodownload/AutomaticDownloadAlgorithm.java b/net/download/service/src/main/java/de/danoeh/antennapod/net/download/service/episode/autodownload/AutomaticDownloadAlgorithm.java index 468fb60af..1371d3a44 100644 --- a/net/download/service/src/main/java/de/danoeh/antennapod/net/download/service/episode/autodownload/AutomaticDownloadAlgorithm.java +++ b/net/download/service/src/main/java/de/danoeh/antennapod/net/download/service/episode/autodownload/AutomaticDownloadAlgorithm.java @@ -50,12 +50,9 @@ public class AutomaticDownloadAlgorithm { Log.d(TAG, "Performing auto-dl of undownloaded episodes"); - List candidates; - final List queue = DBReader.getQueue(); final List newItems = DBReader.getEpisodes(0, Integer.MAX_VALUE, new FeedItemFilter(FeedItemFilter.NEW), SortOrder.DATE_NEW_OLD); - candidates = new ArrayList<>(queue.size() + newItems.size()); - candidates.addAll(queue); + final List candidates = new ArrayList<>(); for (FeedItem newItem : newItems) { FeedPreferences feedPrefs = newItem.getFeed().getPreferences(); if (feedPrefs.isAutoDownload(UserPreferences.isEnableAutodownloadGlobal()) @@ -65,6 +62,15 @@ public class AutomaticDownloadAlgorithm { } } + if (UserPreferences.isEnableAutodownloadQueue()) { + final List queue = DBReader.getQueue(); + for (FeedItem item : queue) { + if (!candidates.contains(item)) { + candidates.add(item); + } + } + } + // filter items that are not auto downloadable Iterator it = candidates.iterator(); while (it.hasNext()) { diff --git a/net/download/service/src/main/java/de/danoeh/antennapod/net/download/service/feed/DownloadServiceInterfaceImpl.java b/net/download/service/src/main/java/de/danoeh/antennapod/net/download/service/feed/DownloadServiceInterfaceImpl.java index 0b5be92b8..9348eaa8d 100644 --- a/net/download/service/src/main/java/de/danoeh/antennapod/net/download/service/feed/DownloadServiceInterfaceImpl.java +++ b/net/download/service/src/main/java/de/danoeh/antennapod/net/download/service/feed/DownloadServiceInterfaceImpl.java @@ -51,7 +51,7 @@ public class DownloadServiceInterfaceImpl extends DownloadServiceInterface { .addTag(DownloadServiceInterface.WORK_TAG) .addTag(DownloadServiceInterface.WORK_TAG_EPISODE_URL + item.getMedia().getDownloadUrl()); if (!item.isTagged(FeedItem.TAG_QUEUE) && UserPreferences.enqueueDownloadedEpisodes()) { - DBWriter.addQueueItem(context, false, item.getId()); + DBWriter.addQueueItem(context, item); workRequest.addTag(DownloadServiceInterface.WORK_DATA_WAS_QUEUED); } workRequest.setInputData(new Data.Builder().putLong(WORK_DATA_MEDIA_ID, item.getMedia().getId()).build()); diff --git a/playback/service/src/main/java/de/danoeh/antennapod/playback/service/PlaybackService.java b/playback/service/src/main/java/de/danoeh/antennapod/playback/service/PlaybackService.java index d2939c21b..5c9125f0c 100644 --- a/playback/service/src/main/java/de/danoeh/antennapod/playback/service/PlaybackService.java +++ b/playback/service/src/main/java/de/danoeh/antennapod/playback/service/PlaybackService.java @@ -1801,8 +1801,7 @@ public class PlaybackService extends MediaBrowserServiceCompat { private void addPlayableToQueue(Playable playable) { if (playable instanceof FeedMedia) { - long itemId = ((FeedMedia) playable).getItem().getId(); - DBWriter.addQueueItem(this, false, true, itemId); + DBWriter.addQueueItem(this, ((FeedMedia) playable).getItem()); notifyChildrenChanged(getString(R.string.queue_label)); } } diff --git a/storage/database/src/main/java/de/danoeh/antennapod/storage/database/DBWriter.java b/storage/database/src/main/java/de/danoeh/antennapod/storage/database/DBWriter.java index b8e114c15..1bf8c4900 100644 --- a/storage/database/src/main/java/de/danoeh/antennapod/storage/database/DBWriter.java +++ b/storage/database/src/main/java/de/danoeh/antennapod/storage/database/DBWriter.java @@ -334,84 +334,43 @@ public class DBWriter { * @param context A context that is used for opening a database connection. * @param itemId ID of the FeedItem that should be added to the queue. * @param index Destination index. Must be in range 0..queue.size() - * @param performAutoDownload True if an auto-download process should be started after the operation * @throws IndexOutOfBoundsException if index < 0 || index >= queue.size() */ - public static Future addQueueItemAt(final Context context, final long itemId, - final int index, final boolean performAutoDownload) { + public static Future addQueueItemAt(final Context context, final long itemId, final int index) { return runOnDbThread(() -> { final PodDBAdapter adapter = PodDBAdapter.getInstance(); adapter.open(); final List queue = DBReader.getQueue(); - FeedItem item; - if (queue != null) { - if (!itemListContains(queue, itemId)) { - item = DBReader.getFeedItem(itemId); - if (item != null) { - queue.add(index, item); - adapter.setQueue(queue); - item.addTag(FeedItem.TAG_QUEUE); - EventBus.getDefault().post(QueueEvent.added(item, index)); - EventBus.getDefault().post(FeedItemEvent.updated(item)); - if (item.isNew()) { - DBWriter.markItemPlayed(FeedItem.UNPLAYED, item.getId()); - } + if (!itemListContains(queue, itemId)) { + FeedItem item = DBReader.getFeedItem(itemId); + if (item != null) { + queue.add(index, item); + adapter.setQueue(queue); + item.addTag(FeedItem.TAG_QUEUE); + EventBus.getDefault().post(QueueEvent.added(item, index)); + EventBus.getDefault().post(FeedItemEvent.updated(item)); + if (item.isNew()) { + DBWriter.markItemPlayed(FeedItem.UNPLAYED, item.getId()); } } } adapter.close(); - if (performAutoDownload) { - AutoDownloadManager.getInstance().autodownloadUndownloadedItems(context); - } - + AutoDownloadManager.getInstance().autodownloadUndownloadedItems(context); }); - } + /** + * Appends FeedItem objects to the end of the queue. The 'read'-attribute of all items will be set to true. + * If a FeedItem is already in the queue, the FeedItem will not change its position in the queue. + * + * @param context A context that is used for opening a database connection. + * @param items FeedItem objects that should be added to the queue. + */ public static Future addQueueItem(final Context context, final FeedItem... items) { - return addQueueItem(context, true, items); - } - - public static Future addQueueItem(final Context context, boolean markAsUnplayed, final FeedItem... items) { - LongList itemIds = new LongList(items.length); - for (FeedItem item : items) { - if (!item.hasMedia()) { - continue; - } - itemIds.add(item.getId()); - item.addTag(FeedItem.TAG_QUEUE); - } - return addQueueItem(context, false, markAsUnplayed, itemIds.toArray()); - } - - /** - * Appends FeedItem objects to the end of the queue. The 'read'-attribute of all items will be set to true. - * If a FeedItem is already in the queue, the FeedItem will not change its position in the queue. - * - * @param context A context that is used for opening a database connection. - * @param performAutoDownload true if an auto-download process should be started after the operation. - * @param itemIds IDs of the FeedItem objects that should be added to the queue. - */ - public static Future addQueueItem(final Context context, final boolean performAutoDownload, - final long... itemIds) { - return addQueueItem(context, performAutoDownload, true, itemIds); - } - - /** - * Appends FeedItem objects to the end of the queue. The 'read'-attribute of all items will be set to true. - * If a FeedItem is already in the queue, the FeedItem will not change its position in the queue. - * - * @param context A context that is used for opening a database connection. - * @param performAutoDownload true if an auto-download process should be started after the operation. - * @param markAsUnplayed true if the items should be marked as unplayed when enqueueing - * @param itemIds IDs of the FeedItem objects that should be added to the queue. - */ - public static Future addQueueItem(final Context context, final boolean performAutoDownload, - final boolean markAsUnplayed, final long... itemIds) { return runOnDbThread(() -> { - if (itemIds.length < 1) { + if (items.length < 1) { return; } @@ -419,7 +378,6 @@ public class DBWriter { adapter.open(); final List queue = DBReader.getQueue(); - boolean queueModified = false; LongList markAsUnplayedIds = new LongList(); List events = new ArrayList<>(); List updatedItems = new ArrayList<>(); @@ -427,38 +385,35 @@ public class DBWriter { new ItemEnqueuePositionCalculator(UserPreferences.getEnqueueLocation()); Playable currentlyPlaying = DBReader.getFeedMedia(PlaybackPreferences.getCurrentlyPlayingFeedMediaId()); int insertPosition = positionCalculator.calcPosition(queue, currentlyPlaying); - for (long itemId : itemIds) { - if (!itemListContains(queue, itemId)) { - final FeedItem item = DBReader.getFeedItem(itemId); - if (item != null) { - queue.add(insertPosition, item); - events.add(QueueEvent.added(item, insertPosition)); - - item.addTag(FeedItem.TAG_QUEUE); - updatedItems.add(item); - queueModified = true; - if (item.isNew()) { - markAsUnplayedIds.add(item.getId()); - } - insertPosition++; - } + for (FeedItem item : items) { + if (itemListContains(queue, item.getId())) { + continue; + } else if (!item.hasMedia()) { + continue; } + queue.add(insertPosition, item); + events.add(QueueEvent.added(item, insertPosition)); + + item.addTag(FeedItem.TAG_QUEUE); + updatedItems.add(item); + if (item.isNew()) { + markAsUnplayedIds.add(item.getId()); + } + insertPosition++; } - if (queueModified) { + if (!updatedItems.isEmpty()) { applySortOrder(queue, events); adapter.setQueue(queue); for (QueueEvent event : events) { EventBus.getDefault().post(event); } EventBus.getDefault().post(FeedItemEvent.updated(updatedItems)); - if (markAsUnplayed && markAsUnplayedIds.size() > 0) { + if (markAsUnplayedIds.size() > 0) { DBWriter.markItemPlayed(FeedItem.UNPLAYED, markAsUnplayedIds.toArray()); } } adapter.close(); - if (performAutoDownload) { - AutoDownloadManager.getInstance().autodownloadUndownloadedItems(context); - } + AutoDownloadManager.getInstance().autodownloadUndownloadedItems(context); }); } diff --git a/storage/database/src/main/java/de/danoeh/antennapod/storage/database/ItemEnqueuePositionCalculator.java b/storage/database/src/main/java/de/danoeh/antennapod/storage/database/ItemEnqueuePositionCalculator.java index 316356715..7504043c5 100644 --- a/storage/database/src/main/java/de/danoeh/antennapod/storage/database/ItemEnqueuePositionCalculator.java +++ b/storage/database/src/main/java/de/danoeh/antennapod/storage/database/ItemEnqueuePositionCalculator.java @@ -13,9 +13,7 @@ import de.danoeh.antennapod.storage.preferences.UserPreferences.EnqueueLocation; import de.danoeh.antennapod.model.playback.Playable; /** - * @see de.danoeh.antennapod.storage.database.DBWriter#addQueueItem(android.content.Context, boolean, long...) - * it uses the class to determine - * the positions of the {@link FeedItem} in the queue. + * Determine the positions of the new {@link FeedItem} in the queue. */ public class ItemEnqueuePositionCalculator { diff --git a/storage/preferences/src/main/java/de/danoeh/antennapod/storage/preferences/UserPreferences.java b/storage/preferences/src/main/java/de/danoeh/antennapod/storage/preferences/UserPreferences.java index 9e49de4ee..cbbedd92b 100644 --- a/storage/preferences/src/main/java/de/danoeh/antennapod/storage/preferences/UserPreferences.java +++ b/storage/preferences/src/main/java/de/danoeh/antennapod/storage/preferences/UserPreferences.java @@ -99,6 +99,7 @@ public abstract class UserPreferences { public static final String PREF_EPISODE_CLEANUP = "prefEpisodeCleanup"; public static final String PREF_EPISODE_CACHE_SIZE = "prefEpisodeCacheSize"; public static final String PREF_AUTODL_GLOBAL = "prefEnableAutoDl"; + public static final String PREF_AUTODL_QUEUE = "prefEnableAutoDlQueue"; public static final String PREF_ENABLE_AUTODL_ON_BATTERY = "prefEnableAutoDownloadOnBattery"; private static final String PREF_PROXY_TYPE = "prefProxyType"; private static final String PREF_PROXY_HOST = "prefProxyHost"; @@ -539,6 +540,10 @@ public abstract class UserPreferences { return prefs.getBoolean(PREF_AUTODL_GLOBAL, false); } + public static boolean isEnableAutodownloadQueue() { + return prefs.getBoolean(PREF_AUTODL_QUEUE, false); + } + public static boolean isEnableAutodownloadOnBattery() { return prefs.getBoolean(PREF_ENABLE_AUTODL_ON_BATTERY, true); } diff --git a/ui/i18n/src/main/res/values/strings.xml b/ui/i18n/src/main/res/values/strings.xml index 6c59ef1fe..4dd36af08 100644 --- a/ui/i18n/src/main/res/values/strings.xml +++ b/ui/i18n/src/main/res/values/strings.xml @@ -481,6 +481,8 @@ Change the information displayed by the subscription counter. Also affects the sorting of subscriptions if \'Subscription Order\' is set to \'Counter\'. Automatic download Automatically download new episodes. Can be overridden per podcast. + Download queued + Automatically download queued episodes Configure the automatic download of episodes Download when not charging Allow automatic download when the battery is not charging diff --git a/ui/preferences/src/main/res/xml/preferences_autodownload.xml b/ui/preferences/src/main/res/xml/preferences_autodownload.xml index 5c1c43d30..456da18f3 100644 --- a/ui/preferences/src/main/res/xml/preferences_autodownload.xml +++ b/ui/preferences/src/main/res/xml/preferences_autodownload.xml @@ -6,6 +6,11 @@ android:key="prefEnableAutoDl" android:title="@string/pref_automatic_download_title" android:summary="@string/pref_automatic_download_global_description"/> +