Add option to automatically download queue (#7627)
We already added the queue to the auto-download candidates. Now that auto-download was rewritten to not be a "master switch", the code is called even if auto-download is turned off for all subscriptions. This lead to queued episodes being downloaded for users who had auto-download disabled. Convert the feature to an explicit setting to avoid behavior changes for users. Also, this implements a setting to auto-download the queue, which users have requested because they did not know that AntennaPod already does this. Finally, it should solve user confusion where they automatically add episodes to the queue but set the auto-download filter to ignore specific episodes.
This commit is contained in:
parent
97ed826687
commit
3ed5b0bfa4
@ -5,6 +5,7 @@ import android.util.Log;
|
|||||||
|
|
||||||
import androidx.annotation.PluralsRes;
|
import androidx.annotation.PluralsRes;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import de.danoeh.antennapod.R;
|
import de.danoeh.antennapod.R;
|
||||||
@ -48,14 +49,14 @@ public class EpisodeMultiSelectActionHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void queueChecked(List<FeedItem> items) {
|
private void queueChecked(List<FeedItem> items) {
|
||||||
// Check if an episode actually contains any media files before adding it to queue
|
// Count here to give accurate number in snackbar
|
||||||
LongList toQueue = new LongList(items.size());
|
List<FeedItem> toQueue = new ArrayList<>();
|
||||||
for (FeedItem episode : items) {
|
for (FeedItem episode : items) {
|
||||||
if (episode.hasMedia()) {
|
if (episode.hasMedia() && !episode.isTagged(FeedItem.TAG_QUEUE)) {
|
||||||
toQueue.add(episode.getId());
|
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());
|
showMessage(R.plurals.added_to_queue_batch_label, toQueue.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ public class RemoveFromQueueSwipeAction implements SwipeAction {
|
|||||||
fragment.getResources().getQuantityString(R.plurals.removed_from_queue_batch_label, 1, 1),
|
fragment.getResources().getQuantityString(R.plurals.removed_from_queue_batch_label, 1, 1),
|
||||||
Snackbar.LENGTH_LONG)
|
Snackbar.LENGTH_LONG)
|
||||||
.setAction(fragment.getString(R.string.undo), v ->
|
.setAction(fragment.getString(R.string.undo), v ->
|
||||||
DBWriter.addQueueItemAt(fragment.requireActivity(), item.getId(), position, false));
|
DBWriter.addQueueItemAt(fragment.requireActivity(), item.getId(), position));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,12 +50,9 @@ public class AutomaticDownloadAlgorithm {
|
|||||||
|
|
||||||
Log.d(TAG, "Performing auto-dl of undownloaded episodes");
|
Log.d(TAG, "Performing auto-dl of undownloaded episodes");
|
||||||
|
|
||||||
List<FeedItem> candidates;
|
|
||||||
final List<FeedItem> queue = DBReader.getQueue();
|
|
||||||
final List<FeedItem> newItems = DBReader.getEpisodes(0, Integer.MAX_VALUE,
|
final List<FeedItem> newItems = DBReader.getEpisodes(0, Integer.MAX_VALUE,
|
||||||
new FeedItemFilter(FeedItemFilter.NEW), SortOrder.DATE_NEW_OLD);
|
new FeedItemFilter(FeedItemFilter.NEW), SortOrder.DATE_NEW_OLD);
|
||||||
candidates = new ArrayList<>(queue.size() + newItems.size());
|
final List<FeedItem> candidates = new ArrayList<>();
|
||||||
candidates.addAll(queue);
|
|
||||||
for (FeedItem newItem : newItems) {
|
for (FeedItem newItem : newItems) {
|
||||||
FeedPreferences feedPrefs = newItem.getFeed().getPreferences();
|
FeedPreferences feedPrefs = newItem.getFeed().getPreferences();
|
||||||
if (feedPrefs.isAutoDownload(UserPreferences.isEnableAutodownloadGlobal())
|
if (feedPrefs.isAutoDownload(UserPreferences.isEnableAutodownloadGlobal())
|
||||||
@ -65,6 +62,15 @@ public class AutomaticDownloadAlgorithm {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (UserPreferences.isEnableAutodownloadQueue()) {
|
||||||
|
final List<FeedItem> queue = DBReader.getQueue();
|
||||||
|
for (FeedItem item : queue) {
|
||||||
|
if (!candidates.contains(item)) {
|
||||||
|
candidates.add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// filter items that are not auto downloadable
|
// filter items that are not auto downloadable
|
||||||
Iterator<FeedItem> it = candidates.iterator();
|
Iterator<FeedItem> it = candidates.iterator();
|
||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
|
@ -51,7 +51,7 @@ public class DownloadServiceInterfaceImpl extends DownloadServiceInterface {
|
|||||||
.addTag(DownloadServiceInterface.WORK_TAG)
|
.addTag(DownloadServiceInterface.WORK_TAG)
|
||||||
.addTag(DownloadServiceInterface.WORK_TAG_EPISODE_URL + item.getMedia().getDownloadUrl());
|
.addTag(DownloadServiceInterface.WORK_TAG_EPISODE_URL + item.getMedia().getDownloadUrl());
|
||||||
if (!item.isTagged(FeedItem.TAG_QUEUE) && UserPreferences.enqueueDownloadedEpisodes()) {
|
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.addTag(DownloadServiceInterface.WORK_DATA_WAS_QUEUED);
|
||||||
}
|
}
|
||||||
workRequest.setInputData(new Data.Builder().putLong(WORK_DATA_MEDIA_ID, item.getMedia().getId()).build());
|
workRequest.setInputData(new Data.Builder().putLong(WORK_DATA_MEDIA_ID, item.getMedia().getId()).build());
|
||||||
|
@ -1801,8 +1801,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
|
|||||||
|
|
||||||
private void addPlayableToQueue(Playable playable) {
|
private void addPlayableToQueue(Playable playable) {
|
||||||
if (playable instanceof FeedMedia) {
|
if (playable instanceof FeedMedia) {
|
||||||
long itemId = ((FeedMedia) playable).getItem().getId();
|
DBWriter.addQueueItem(this, ((FeedMedia) playable).getItem());
|
||||||
DBWriter.addQueueItem(this, false, true, itemId);
|
|
||||||
notifyChildrenChanged(getString(R.string.queue_label));
|
notifyChildrenChanged(getString(R.string.queue_label));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -334,20 +334,16 @@ public class DBWriter {
|
|||||||
* @param context A context that is used for opening a database connection.
|
* @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 itemId ID of the FeedItem that should be added to the queue.
|
||||||
* @param index Destination index. Must be in range 0..queue.size()
|
* @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()
|
* @throws IndexOutOfBoundsException if index < 0 || index >= queue.size()
|
||||||
*/
|
*/
|
||||||
public static Future<?> addQueueItemAt(final Context context, final long itemId,
|
public static Future<?> addQueueItemAt(final Context context, final long itemId, final int index) {
|
||||||
final int index, final boolean performAutoDownload) {
|
|
||||||
return runOnDbThread(() -> {
|
return runOnDbThread(() -> {
|
||||||
final PodDBAdapter adapter = PodDBAdapter.getInstance();
|
final PodDBAdapter adapter = PodDBAdapter.getInstance();
|
||||||
adapter.open();
|
adapter.open();
|
||||||
final List<FeedItem> queue = DBReader.getQueue();
|
final List<FeedItem> queue = DBReader.getQueue();
|
||||||
FeedItem item;
|
|
||||||
|
|
||||||
if (queue != null) {
|
|
||||||
if (!itemListContains(queue, itemId)) {
|
if (!itemListContains(queue, itemId)) {
|
||||||
item = DBReader.getFeedItem(itemId);
|
FeedItem item = DBReader.getFeedItem(itemId);
|
||||||
if (item != null) {
|
if (item != null) {
|
||||||
queue.add(index, item);
|
queue.add(index, item);
|
||||||
adapter.setQueue(queue);
|
adapter.setQueue(queue);
|
||||||
@ -359,59 +355,22 @@ public class DBWriter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
adapter.close();
|
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) {
|
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(() -> {
|
return runOnDbThread(() -> {
|
||||||
if (itemIds.length < 1) {
|
if (items.length < 1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -419,7 +378,6 @@ public class DBWriter {
|
|||||||
adapter.open();
|
adapter.open();
|
||||||
final List<FeedItem> queue = DBReader.getQueue();
|
final List<FeedItem> queue = DBReader.getQueue();
|
||||||
|
|
||||||
boolean queueModified = false;
|
|
||||||
LongList markAsUnplayedIds = new LongList();
|
LongList markAsUnplayedIds = new LongList();
|
||||||
List<QueueEvent> events = new ArrayList<>();
|
List<QueueEvent> events = new ArrayList<>();
|
||||||
List<FeedItem> updatedItems = new ArrayList<>();
|
List<FeedItem> updatedItems = new ArrayList<>();
|
||||||
@ -427,38 +385,35 @@ public class DBWriter {
|
|||||||
new ItemEnqueuePositionCalculator(UserPreferences.getEnqueueLocation());
|
new ItemEnqueuePositionCalculator(UserPreferences.getEnqueueLocation());
|
||||||
Playable currentlyPlaying = DBReader.getFeedMedia(PlaybackPreferences.getCurrentlyPlayingFeedMediaId());
|
Playable currentlyPlaying = DBReader.getFeedMedia(PlaybackPreferences.getCurrentlyPlayingFeedMediaId());
|
||||||
int insertPosition = positionCalculator.calcPosition(queue, currentlyPlaying);
|
int insertPosition = positionCalculator.calcPosition(queue, currentlyPlaying);
|
||||||
for (long itemId : itemIds) {
|
for (FeedItem item : items) {
|
||||||
if (!itemListContains(queue, itemId)) {
|
if (itemListContains(queue, item.getId())) {
|
||||||
final FeedItem item = DBReader.getFeedItem(itemId);
|
continue;
|
||||||
if (item != null) {
|
} else if (!item.hasMedia()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
queue.add(insertPosition, item);
|
queue.add(insertPosition, item);
|
||||||
events.add(QueueEvent.added(item, insertPosition));
|
events.add(QueueEvent.added(item, insertPosition));
|
||||||
|
|
||||||
item.addTag(FeedItem.TAG_QUEUE);
|
item.addTag(FeedItem.TAG_QUEUE);
|
||||||
updatedItems.add(item);
|
updatedItems.add(item);
|
||||||
queueModified = true;
|
|
||||||
if (item.isNew()) {
|
if (item.isNew()) {
|
||||||
markAsUnplayedIds.add(item.getId());
|
markAsUnplayedIds.add(item.getId());
|
||||||
}
|
}
|
||||||
insertPosition++;
|
insertPosition++;
|
||||||
}
|
}
|
||||||
}
|
if (!updatedItems.isEmpty()) {
|
||||||
}
|
|
||||||
if (queueModified) {
|
|
||||||
applySortOrder(queue, events);
|
applySortOrder(queue, events);
|
||||||
adapter.setQueue(queue);
|
adapter.setQueue(queue);
|
||||||
for (QueueEvent event : events) {
|
for (QueueEvent event : events) {
|
||||||
EventBus.getDefault().post(event);
|
EventBus.getDefault().post(event);
|
||||||
}
|
}
|
||||||
EventBus.getDefault().post(FeedItemEvent.updated(updatedItems));
|
EventBus.getDefault().post(FeedItemEvent.updated(updatedItems));
|
||||||
if (markAsUnplayed && markAsUnplayedIds.size() > 0) {
|
if (markAsUnplayedIds.size() > 0) {
|
||||||
DBWriter.markItemPlayed(FeedItem.UNPLAYED, markAsUnplayedIds.toArray());
|
DBWriter.markItemPlayed(FeedItem.UNPLAYED, markAsUnplayedIds.toArray());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
adapter.close();
|
adapter.close();
|
||||||
if (performAutoDownload) {
|
|
||||||
AutoDownloadManager.getInstance().autodownloadUndownloadedItems(context);
|
AutoDownloadManager.getInstance().autodownloadUndownloadedItems(context);
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,9 +13,7 @@ import de.danoeh.antennapod.storage.preferences.UserPreferences.EnqueueLocation;
|
|||||||
import de.danoeh.antennapod.model.playback.Playable;
|
import de.danoeh.antennapod.model.playback.Playable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see de.danoeh.antennapod.storage.database.DBWriter#addQueueItem(android.content.Context, boolean, long...)
|
* Determine the positions of the new {@link FeedItem} in the queue.
|
||||||
* it uses the class to determine
|
|
||||||
* the positions of the {@link FeedItem} in the queue.
|
|
||||||
*/
|
*/
|
||||||
public class ItemEnqueuePositionCalculator {
|
public class ItemEnqueuePositionCalculator {
|
||||||
|
|
||||||
|
@ -99,6 +99,7 @@ public abstract class UserPreferences {
|
|||||||
public static final String PREF_EPISODE_CLEANUP = "prefEpisodeCleanup";
|
public static final String PREF_EPISODE_CLEANUP = "prefEpisodeCleanup";
|
||||||
public static final String PREF_EPISODE_CACHE_SIZE = "prefEpisodeCacheSize";
|
public static final String PREF_EPISODE_CACHE_SIZE = "prefEpisodeCacheSize";
|
||||||
public static final String PREF_AUTODL_GLOBAL = "prefEnableAutoDl";
|
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";
|
public static final String PREF_ENABLE_AUTODL_ON_BATTERY = "prefEnableAutoDownloadOnBattery";
|
||||||
private static final String PREF_PROXY_TYPE = "prefProxyType";
|
private static final String PREF_PROXY_TYPE = "prefProxyType";
|
||||||
private static final String PREF_PROXY_HOST = "prefProxyHost";
|
private static final String PREF_PROXY_HOST = "prefProxyHost";
|
||||||
@ -539,6 +540,10 @@ public abstract class UserPreferences {
|
|||||||
return prefs.getBoolean(PREF_AUTODL_GLOBAL, false);
|
return prefs.getBoolean(PREF_AUTODL_GLOBAL, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isEnableAutodownloadQueue() {
|
||||||
|
return prefs.getBoolean(PREF_AUTODL_QUEUE, false);
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean isEnableAutodownloadOnBattery() {
|
public static boolean isEnableAutodownloadOnBattery() {
|
||||||
return prefs.getBoolean(PREF_ENABLE_AUTODL_ON_BATTERY, true);
|
return prefs.getBoolean(PREF_ENABLE_AUTODL_ON_BATTERY, true);
|
||||||
}
|
}
|
||||||
|
@ -481,6 +481,8 @@
|
|||||||
<string name="pref_nav_drawer_feed_counter_sum">Change the information displayed by the subscription counter. Also affects the sorting of subscriptions if \'Subscription Order\' is set to \'Counter\'.</string>
|
<string name="pref_nav_drawer_feed_counter_sum">Change the information displayed by the subscription counter. Also affects the sorting of subscriptions if \'Subscription Order\' is set to \'Counter\'.</string>
|
||||||
<string name="pref_automatic_download_title">Automatic download</string>
|
<string name="pref_automatic_download_title">Automatic download</string>
|
||||||
<string name="pref_automatic_download_global_description">Automatically download new episodes. Can be overridden per podcast.</string>
|
<string name="pref_automatic_download_global_description">Automatically download new episodes. Can be overridden per podcast.</string>
|
||||||
|
<string name="pref_automatic_download_queue_title">Download queued</string>
|
||||||
|
<string name="pref_automatic_download_queue_description">Automatically download queued episodes</string>
|
||||||
<string name="pref_automatic_download_sum">Configure the automatic download of episodes</string>
|
<string name="pref_automatic_download_sum">Configure the automatic download of episodes</string>
|
||||||
<string name="pref_automatic_download_on_battery_title">Download when not charging</string>
|
<string name="pref_automatic_download_on_battery_title">Download when not charging</string>
|
||||||
<string name="pref_automatic_download_on_battery_sum">Allow automatic download when the battery is not charging</string>
|
<string name="pref_automatic_download_on_battery_sum">Allow automatic download when the battery is not charging</string>
|
||||||
|
@ -6,6 +6,11 @@
|
|||||||
android:key="prefEnableAutoDl"
|
android:key="prefEnableAutoDl"
|
||||||
android:title="@string/pref_automatic_download_title"
|
android:title="@string/pref_automatic_download_title"
|
||||||
android:summary="@string/pref_automatic_download_global_description"/>
|
android:summary="@string/pref_automatic_download_global_description"/>
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
android:defaultValue="false"
|
||||||
|
android:key="prefEnableAutoDlQueue"
|
||||||
|
android:title="@string/pref_automatic_download_queue_title"
|
||||||
|
android:summary="@string/pref_automatic_download_queue_description"/>
|
||||||
<de.danoeh.antennapod.ui.preferences.preference.MaterialListPreference
|
<de.danoeh.antennapod.ui.preferences.preference.MaterialListPreference
|
||||||
android:defaultValue="25"
|
android:defaultValue="25"
|
||||||
android:entries="@array/episode_cache_size_entries"
|
android:entries="@array/episode_cache_size_entries"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user