enqueue location: use the new 3-value settings

This commit is contained in:
orionlee 2019-10-28 14:26:10 -07:00
parent 52521ecddb
commit bddd2bfa2e
8 changed files with 162 additions and 154 deletions

View File

@ -189,7 +189,7 @@ public class DBTasksTest {
public void testAddQueueItemsInDownload_EnqueueEnabled() throws Exception { public void testAddQueueItemsInDownload_EnqueueEnabled() throws Exception {
// Setup test data / environment // Setup test data / environment
UserPreferences.setEnqueueDownloadedEpisodes(true); UserPreferences.setEnqueueDownloadedEpisodes(true);
UserPreferences.setEnqueueAtFront(false); UserPreferences.setEnqueueLocation(UserPreferences.EnqueueLocation.BACK);
List<FeedItem> fis1 = createSavedFeed("Feed 1", 2).getItems(); List<FeedItem> fis1 = createSavedFeed("Feed 1", 2).getItems();
List<FeedItem> fis2 = createSavedFeed("Feed 2", 3).getItems(); List<FeedItem> fis2 = createSavedFeed("Feed 2", 3).getItems();

View File

@ -5,6 +5,7 @@ import android.content.SharedPreferences;
import android.content.res.Resources; import android.content.res.Resources;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import androidx.annotation.StringRes;
import androidx.test.filters.LargeTest; import androidx.test.filters.LargeTest;
import androidx.test.rule.ActivityTestRule; import androidx.test.rule.ActivityTestRule;
@ -21,6 +22,7 @@ import java.util.concurrent.TimeUnit;
import de.danoeh.antennapod.R; import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.PreferenceActivity; import de.danoeh.antennapod.activity.PreferenceActivity;
import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.preferences.UserPreferences.EnqueueLocation;
import de.danoeh.antennapod.core.storage.APCleanupAlgorithm; import de.danoeh.antennapod.core.storage.APCleanupAlgorithm;
import de.danoeh.antennapod.core.storage.APNullCleanupAlgorithm; import de.danoeh.antennapod.core.storage.APNullCleanupAlgorithm;
import de.danoeh.antennapod.core.storage.APQueueCleanupAlgorithm; import de.danoeh.antennapod.core.storage.APQueueCleanupAlgorithm;
@ -129,7 +131,17 @@ public class PreferencesTest {
@Test @Test
public void testEnqueueLocation() { public void testEnqueueLocation() {
clickPreference(R.string.playback_pref); clickPreference(R.string.playback_pref);
// TODO-2652: implement the test doTestEnqueueLocation(R.string.enqueue_location_after_current, EnqueueLocation.AFTER_CURRENTLY_PLAYING);
doTestEnqueueLocation(R.string.enqueue_location_front, EnqueueLocation.FRONT);
doTestEnqueueLocation(R.string.enqueue_location_back, EnqueueLocation.BACK);
}
private void doTestEnqueueLocation(@StringRes int optionResId, EnqueueLocation expected) {
clickPreference(R.string.pref_enqueue_location_title);
onView(withText(optionResId)).perform(click());
assertTrue(solo.waitForCondition(
() -> expected == UserPreferences.getEnqueueLocation(),
Timeout.getLargeTimeout()));
} }
@Test @Test

View File

@ -7,6 +7,7 @@ import android.preference.PreferenceManager;
import de.danoeh.antennapod.BuildConfig; import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R; import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.preferences.UserPreferences.EnqueueLocation;
import de.danoeh.antennapod.core.util.download.AutoUpdateManager; import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
import de.danoeh.antennapod.core.util.gui.NotificationUtils; import de.danoeh.antennapod.core.util.gui.NotificationUtils;
@ -75,6 +76,13 @@ public class PreferenceUpgrader {
} }
UserPreferences.setQueueLocked(false); UserPreferences.setQueueLocked(false);
if (!prefs.contains(UserPreferences.PREF_ENQUEUE_LOCATION)) {
final String keyOldPrefEnqueueFront = "prefQueueAddToFront";
boolean enqueueAtFront = prefs.getBoolean(keyOldPrefEnqueueFront, false);
EnqueueLocation enqueueLocation = enqueueAtFront ? EnqueueLocation.FRONT : EnqueueLocation.BACK;
UserPreferences.setEnqueueLocation(enqueueLocation);
}
} }
} }
} }

View File

@ -63,9 +63,6 @@ public class UserPreferences {
public static final String PREF_BACK_BUTTON_BEHAVIOR = "prefBackButtonBehavior"; public static final String PREF_BACK_BUTTON_BEHAVIOR = "prefBackButtonBehavior";
private static final String PREF_BACK_BUTTON_GO_TO_PAGE = "prefBackButtonGoToPage"; private static final String PREF_BACK_BUTTON_GO_TO_PAGE = "prefBackButtonGoToPage";
// Queue
public static final String PREF_QUEUE_ADD_TO_FRONT = "prefQueueAddToFront";
public static final String PREF_QUEUE_KEEP_IN_PROGESS_AT_FRONT = "prefQueueKeepInProgressAtFront";
public static final String PREF_QUEUE_KEEP_SORTED = "prefQueueKeepSorted"; public static final String PREF_QUEUE_KEEP_SORTED = "prefQueueKeepSorted";
public static final String PREF_QUEUE_KEEP_SORTED_ORDER = "prefQueueKeepSortedOrder"; public static final String PREF_QUEUE_KEEP_SORTED_ORDER = "prefQueueKeepSortedOrder";
@ -299,6 +296,7 @@ public class UserPreferences {
BACK, FRONT, AFTER_CURRENTLY_PLAYING; BACK, FRONT, AFTER_CURRENTLY_PLAYING;
} }
@NonNull
public static EnqueueLocation getEnqueueLocation() { public static EnqueueLocation getEnqueueLocation() {
String valStr = prefs.getString(PREF_ENQUEUE_LOCATION, EnqueueLocation.BACK.name()); String valStr = prefs.getString(PREF_ENQUEUE_LOCATION, EnqueueLocation.BACK.name());
try { try {
@ -310,29 +308,12 @@ public class UserPreferences {
} }
} }
// TODO-2652: migrate settings public static void setEnqueueLocation(@NonNull EnqueueLocation location) {
public static boolean enqueueAtFront() { // TODO-2652: migrate to the new settings
return prefs.getBoolean(PREF_QUEUE_ADD_TO_FRONT, false);
}
@VisibleForTesting
public static void setEnqueueAtFront(boolean enqueueAtFront) { // TODO-2652: migrate to the new settings
prefs.edit() prefs.edit()
.putBoolean(PREF_QUEUE_ADD_TO_FRONT, enqueueAtFront) .putString(PREF_ENQUEUE_LOCATION, location.name())
.apply(); .apply();
} }
/**
*
* @return {@code true} if in enqueuing items/podcast episodes, when the existing front item is
* in-progress, i.e., the user has played part of it, such item remains at the front of the
* queue; {@code false} otherwise.
*/
public static boolean keepInProgressAtFront() { // TODO-2652: migrate to the new settings
return prefs.getBoolean(PREF_QUEUE_KEEP_IN_PROGESS_AT_FRONT, false);
}
public static boolean isPauseOnHeadsetDisconnect() { public static boolean isPauseOnHeadsetDisconnect() {
return prefs.getBoolean(PREF_PAUSE_ON_HEADSET_DISCONNECT, true); return prefs.getBoolean(PREF_PAUSE_ON_HEADSET_DISCONNECT, true);
} }

View File

@ -46,6 +46,7 @@ import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.LongList; import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.core.util.Permutor; import de.danoeh.antennapod.core.util.Permutor;
import de.danoeh.antennapod.core.util.SortOrder; import de.danoeh.antennapod.core.util.SortOrder;
import de.danoeh.antennapod.core.util.playback.Playable;
/** /**
* Provides methods for writing data to AntennaPod's database. * Provides methods for writing data to AntennaPod's database.
@ -326,19 +327,15 @@ public class DBWriter {
List<QueueEvent> events = new ArrayList<>(); List<QueueEvent> events = new ArrayList<>();
List<FeedItem> updatedItems = new ArrayList<>(); List<FeedItem> updatedItems = new ArrayList<>();
ItemEnqueuePositionCalculator positionCalculator = ItemEnqueuePositionCalculator positionCalculator =
new ItemEnqueuePositionCalculator( new ItemEnqueuePositionCalculator(UserPreferences.getEnqueueLocation());
new ItemEnqueuePositionCalculator.Options() Playable currentlyPlaying = Playable.PlayableUtils.createInstanceFromPreferences(context);
.setEnqueueAtFront(UserPreferences.enqueueAtFront())
.setKeepInProgressAtFront(UserPreferences.keepInProgressAtFront())
);
for (int i = 0; i < itemIds.length; i++) { for (int i = 0; i < itemIds.length; i++) {
if (!itemListContains(queue, itemIds[i])) { if (!itemListContains(queue, itemIds[i])) {
final FeedItem item = DBReader.getFeedItem(itemIds[i]); final FeedItem item = DBReader.getFeedItem(itemIds[i]);
if (item != null) { if (item != null) {
int insertPosition = positionCalculator.calcPosition(i, item, queue); int insertPosition = positionCalculator.calcPosition(i, item, queue, currentlyPlaying);
queue.add(insertPosition, item); queue.add(insertPosition, item);
events.add(QueueEvent.added(item, insertPosition)); events.add(QueueEvent.added(item, insertPosition));

View File

@ -3,11 +3,15 @@ package de.danoeh.antennapod.core.storage;
import android.content.Context; import android.content.Context;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import java.util.List; import java.util.List;
import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.preferences.UserPreferences.EnqueueLocation;
import de.danoeh.antennapod.core.util.playback.Playable;
/** /**
* @see DBWriter#addQueueItem(Context, boolean, long...) it uses the class to determine * @see DBWriter#addQueueItem(Context, boolean, long...) it uses the class to determine
@ -15,65 +19,41 @@ import de.danoeh.antennapod.core.feed.FeedItem;
*/ */
class ItemEnqueuePositionCalculator { class ItemEnqueuePositionCalculator {
public static class Options {
private boolean enqueueAtFront = false;
private boolean keepInProgressAtFront = false;
public boolean isEnqueueAtFront() {
return enqueueAtFront;
}
public Options setEnqueueAtFront(boolean enqueueAtFront) {
this.enqueueAtFront = enqueueAtFront;
return this;
}
public boolean isKeepInProgressAtFront() {
return keepInProgressAtFront;
}
public Options setKeepInProgressAtFront(boolean keepInProgressAtFront) {
this.keepInProgressAtFront = keepInProgressAtFront;
return this;
}
}
@NonNull @NonNull
private final Options options; private final EnqueueLocation enqueueLocation;
@VisibleForTesting @VisibleForTesting
DownloadStateProvider downloadStateProvider = DownloadRequester.getInstance(); DownloadStateProvider downloadStateProvider = DownloadRequester.getInstance();
public ItemEnqueuePositionCalculator(@NonNull Options options) { public ItemEnqueuePositionCalculator(@NonNull EnqueueLocation enqueueLocation) {
this.options = options; this.enqueueLocation = enqueueLocation;
} }
/** /**
*
* @param positionAmongToAdd Typically, the callers has a list of items to be inserted to * @param positionAmongToAdd Typically, the callers has a list of items to be inserted to
* the queue. This parameter indicates the position (0-based) of * the queue. This parameter indicates the position (0-based) of
* the item among the one to inserted. E.g., it is needed for * the item among the one to inserted. E.g., it is needed for
* enqueue at front option. * enqueue at front option.
* * @param item the item to be inserted
* @param item the item to be inserted * @param curQueue the queue to which the item is to be inserted
* @param curQueue the queue to which the item is to be inserted * @param currentPlaying the currently playing media
* @return the position (0-based) the item should be inserted to the named queu * @return the position (0-based) the item should be inserted to the named queue
*/ */
public int calcPosition(int positionAmongToAdd, FeedItem item, List<FeedItem> curQueue) { public int calcPosition(int positionAmongToAdd, @NonNull FeedItem item,
if (options.isEnqueueAtFront()) { @NonNull List<FeedItem> curQueue, @Nullable Playable currentPlaying) {
if (options.isKeepInProgressAtFront() && switch (enqueueLocation) {
curQueue.size() > 0 && case BACK:
curQueue.get(0).getMedia() != null && return curQueue.size();
curQueue.get(0).getMedia().isInProgress()) { case FRONT:
// leave the front in progress item at the front
return getPositionOfFirstNonDownloadingItem(positionAmongToAdd + 1, curQueue);
} else { // typical case
// return NOT 0, so that when a list of items are inserted, the items inserted // return NOT 0, so that when a list of items are inserted, the items inserted
// keep the same order. Returning 0 will reverse the order // keep the same order. Returning 0 will reverse the order
return getPositionOfFirstNonDownloadingItem(positionAmongToAdd, curQueue); return getPositionOfFirstNonDownloadingItem(positionAmongToAdd, curQueue);
} case AFTER_CURRENTLY_PLAYING:
} else { int currentlyPlayingPosition = getCurrentlyPlayingPosition(curQueue, currentPlaying);
return curQueue.size(); return getPositionOfFirstNonDownloadingItem(
currentlyPlayingPosition + (1 + positionAmongToAdd), curQueue);
default:
throw new AssertionError("calcPosition() : unrecognized enqueueLocation option: " + enqueueLocation);
} }
} }
@ -103,4 +83,18 @@ class ItemEnqueuePositionCalculator {
return false; return false;
} }
} }
private static int getCurrentlyPlayingPosition(@NonNull List<FeedItem> curQueue,
@Nullable Playable currentPlaying) {
if (!(currentPlaying instanceof FeedMedia)) {
return -1;
}
final long curPlayingItemId = ((FeedMedia) currentPlaying).getItem().getId();
for (int i = 0; i < curQueue.size(); i++) {
if (curPlayingItemId == curQueue.get(i).getId()) {
return i;
}
}
return -1;
}
} }

View File

@ -455,10 +455,10 @@
<string name="pref_showDownloadReport_sum">If downloads fail, generate a report that shows the details of the failure.</string> <string name="pref_showDownloadReport_sum">If downloads fail, generate a report that shows the details of the failure.</string>
<string name="pref_expand_notify_unsupport_toast">Android versions before 4.1 do not support expanded notifications.</string> <string name="pref_expand_notify_unsupport_toast">Android versions before 4.1 do not support expanded notifications.</string>
<string name="pref_enqueue_location_title">Enqueue Location</string> <string name="pref_enqueue_location_title">Enqueue Location</string>
<string name="pref_enqueue_location_sum">Add episodes to: %1$s.</string> <string name="pref_enqueue_location_sum">Add episodes to: %1$s</string>
<string name="enqueue_location_back">back of the queue</string> <string name="enqueue_location_back">Back</string>
<string name="enqueue_location_front">front of the queue</string> <string name="enqueue_location_front">Front</string>
<string name="enqueue_location_after_current">after current episode</string> <string name="enqueue_location_after_current">After current episode</string>
<string name="pref_smart_mark_as_played_disabled">Disabled</string> <string name="pref_smart_mark_as_played_disabled">Disabled</string>
<string name="pref_image_cache_size_title">Image Cache Size</string> <string name="pref_image_cache_size_title">Image Cache Size</string>
<string name="pref_image_cache_size_sum">Size of the disk cache for images.</string> <string name="pref_image_cache_size_sum">Size of the disk cache for images.</string>

View File

@ -16,9 +16,16 @@ import java.util.stream.Collectors;
import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.FeedMother; import de.danoeh.antennapod.core.feed.FeedMother;
import de.danoeh.antennapod.core.storage.ItemEnqueuePositionCalculator.Options; import de.danoeh.antennapod.core.feed.MediaType;
import de.danoeh.antennapod.core.util.CollectionTestUtil; import de.danoeh.antennapod.core.preferences.UserPreferences.EnqueueLocation;
import de.danoeh.antennapod.core.util.playback.ExternalMedia;
import de.danoeh.antennapod.core.util.playback.Playable;
import static de.danoeh.antennapod.core.preferences.UserPreferences.EnqueueLocation.AFTER_CURRENTLY_PLAYING;
import static de.danoeh.antennapod.core.preferences.UserPreferences.EnqueueLocation.BACK;
import static de.danoeh.antennapod.core.preferences.UserPreferences.EnqueueLocation.FRONT;
import static de.danoeh.antennapod.core.util.CollectionTestUtil.concat;
import static de.danoeh.antennapod.core.util.CollectionTestUtil.list;
import static de.danoeh.antennapod.core.util.FeedItemUtil.getIdList; import static de.danoeh.antennapod.core.util.FeedItemUtil.getIdList;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.any; import static org.mockito.Mockito.any;
@ -31,28 +38,25 @@ public class ItemEnqueuePositionCalculatorTest {
public static class BasicTest { public static class BasicTest {
@Parameters(name = "{index}: case<{0}>, expected:{1}") @Parameters(name = "{index}: case<{0}>, expected:{1}")
public static Iterable<Object[]> data() { public static Iterable<Object[]> data() {
Options optDefault = new Options();
Options optEnqAtFront = new Options().setEnqueueAtFront(true);
return Arrays.asList(new Object[][]{ return Arrays.asList(new Object[][]{
{"case default, i.e., add to the end", {"case default, i.e., add to the end",
CollectionTestUtil.concat(QUEUE_DEFAULT_IDS, TFI_ID), concat(QUEUE_DEFAULT_IDS, TFI_ID),
optDefault, 0, QUEUE_DEFAULT}, BACK, 0, QUEUE_DEFAULT},
{"case default (2nd item)", {"case default (2nd item)",
CollectionTestUtil.concat(QUEUE_DEFAULT_IDS, TFI_ID), concat(QUEUE_DEFAULT_IDS, TFI_ID),
optDefault, 1, QUEUE_DEFAULT}, BACK, 1, QUEUE_DEFAULT},
{"case option enqueue at front", {"case option enqueue at front",
CollectionTestUtil.concat(TFI_ID, QUEUE_DEFAULT_IDS), concat(TFI_ID, QUEUE_DEFAULT_IDS),
optEnqAtFront, 0, QUEUE_DEFAULT}, FRONT, 0, QUEUE_DEFAULT},
{"case option enqueue at front (2nd item)", {"case option enqueue at front (2nd item)",
CollectionTestUtil.list(11L, TFI_ID, 12L, 13L, 14L), list(11L, TFI_ID, 12L, 13L, 14L),
optEnqAtFront, 1, QUEUE_DEFAULT}, FRONT, 1, QUEUE_DEFAULT},
{"case empty queue, option default", {"case empty queue, option default",
CollectionTestUtil.list(TFI_ID), list(TFI_ID),
optDefault, 0, QUEUE_EMPTY}, BACK, 0, QUEUE_EMPTY},
{"case empty queue, option enqueue at front", {"case empty queue, option enqueue at front",
CollectionTestUtil.list(TFI_ID), list(TFI_ID),
optEnqAtFront, 0, QUEUE_EMPTY}, FRONT, 0, QUEUE_EMPTY},
}); });
} }
@ -63,7 +67,7 @@ public class ItemEnqueuePositionCalculatorTest {
public List<Long> idsExpected; public List<Long> idsExpected;
@Parameter(2) @Parameter(2)
public Options options; public EnqueueLocation options;
@Parameter(3) @Parameter(3)
public int posAmongAdded; // the position of feed item to be inserted among the list to be inserted. public int posAmongAdded; // the position of feed item to be inserted among the list to be inserted.
@ -71,7 +75,6 @@ public class ItemEnqueuePositionCalculatorTest {
@Parameter(4) @Parameter(4)
public List<FeedItem> curQueue; public List<FeedItem> curQueue;
public static final long TFI_ID = 101; public static final long TFI_ID = 101;
/** /**
@ -85,49 +88,62 @@ public class ItemEnqueuePositionCalculatorTest {
List<FeedItem> queue = new ArrayList<>(curQueue); List<FeedItem> queue = new ArrayList<>(curQueue);
FeedItem tFI = tFI(TFI_ID); FeedItem tFI = tFI(TFI_ID);
doAddToQueueAndAssertResult(message, doAddToQueueAndAssertResult(message,
calculator, posAmongAdded, tFI, queue, calculator, posAmongAdded, tFI, queue, getCurrentlyPlaying(),
idsExpected); idsExpected);
} }
Playable getCurrentlyPlaying() { return null; }
} }
@RunWith(Parameterized.class) @RunWith(Parameterized.class)
public static class KeepInProgressAtFrontTest extends BasicTest { public static class AfterCurrentlyPlayingTest extends BasicTest {
@Parameters(name = "{index}: case<{0}>, expected:{1}") @Parameters(name = "{index}: case<{0}>, expected:{1}")
public static Iterable<Object[]> data() { public static Iterable<Object[]> data() {
Options optKeepInProgressAtFront =
new Options().setEnqueueAtFront(true).setKeepInProgressAtFront(true);
// edge case: keep in progress without enabling enqueue at front is meaningless
Options optKeepInProgressAtFrontWithNoEnqueueAtFront =
new Options().setKeepInProgressAtFront(true);
return Arrays.asList(new Object[][]{ return Arrays.asList(new Object[][]{
{"case option keep in progress at front", {"case option after currently playing",
CollectionTestUtil.list(11L, TFI_ID, 12L, 13L), list(11L, TFI_ID, 12L, 13L, 14L),
optKeepInProgressAtFront, 0, QUEUE_FRONT_IN_PROGRESS}, AFTER_CURRENTLY_PLAYING, 0, QUEUE_DEFAULT, 11L},
{"case option keep in progress at front (2nd item)", {"case option after currently playing (2nd item)",
CollectionTestUtil.list(11L, 12L, TFI_ID, 13L), list(11L, 12L, TFI_ID, 13L, 14L),
optKeepInProgressAtFront, 1, QUEUE_FRONT_IN_PROGRESS}, AFTER_CURRENTLY_PLAYING, 1, QUEUE_DEFAULT, 11L},
{"case option keep in progress at front, front item not in progress", {"case option after currently playing, currently playing in the middle of the queue",
CollectionTestUtil.concat(TFI_ID, QUEUE_DEFAULT_IDS), list(11L, 12L, 13L, TFI_ID, 14L),
optKeepInProgressAtFront, 0, QUEUE_DEFAULT}, AFTER_CURRENTLY_PLAYING, 0, QUEUE_DEFAULT, 13L},
{"case option keep in progress at front, front item no media at all", {"case option after currently playing, currently playing is not in queue",
CollectionTestUtil.concat(TFI_ID, QUEUE_FRONT_NO_MEDIA_IDS), concat(TFI_ID, QUEUE_DEFAULT_IDS),
optKeepInProgressAtFront, 0, QUEUE_FRONT_NO_MEDIA}, // No media should not cause any exception AFTER_CURRENTLY_PLAYING, 0, QUEUE_DEFAULT, 99L},
{"case option keep in progress at front, but enqueue at front is disabled", {"case option after currently playing, no currentlyPlaying is null",
CollectionTestUtil.concat(QUEUE_FRONT_IN_PROGRESS_IDS, TFI_ID), concat(TFI_ID, QUEUE_DEFAULT_IDS),
optKeepInProgressAtFrontWithNoEnqueueAtFront, 0, QUEUE_FRONT_IN_PROGRESS}, AFTER_CURRENTLY_PLAYING, 0, QUEUE_DEFAULT, ID_CURRENTLY_PLAYING_NULL},
{"case empty queue, option keep in progress at front", {"case option after currently playing, currentlyPlaying is externalMedia",
CollectionTestUtil.list(TFI_ID), concat(TFI_ID, QUEUE_DEFAULT_IDS),
optKeepInProgressAtFront, 0, QUEUE_EMPTY}, AFTER_CURRENTLY_PLAYING, 0, QUEUE_DEFAULT, ID_CURRENTLY_PLAYING_NOT_FEEDMEDIA},
{"case empty queue, option after currently playing",
list(TFI_ID),
AFTER_CURRENTLY_PLAYING, 0, QUEUE_EMPTY, ID_CURRENTLY_PLAYING_NULL},
}); });
} }
private static final List<FeedItem> QUEUE_FRONT_IN_PROGRESS = Arrays.asList(tFI(11, 60000), tFI(12), tFI(13)); @Parameter(5)
private static final List<Long> QUEUE_FRONT_IN_PROGRESS_IDS = getIdList(QUEUE_FRONT_IN_PROGRESS); public long idCurrentlyPlaying = -1;
private static final List<FeedItem> QUEUE_FRONT_NO_MEDIA = Arrays.asList(tFINoMedia(11), tFI(12), tFI(13)); @Override
private static final List<Long> QUEUE_FRONT_NO_MEDIA_IDS = getIdList(QUEUE_FRONT_NO_MEDIA); Playable getCurrentlyPlaying() {
if (ID_CURRENTLY_PLAYING_NOT_FEEDMEDIA == idCurrentlyPlaying) {
return externalMedia();
}
if (ID_CURRENTLY_PLAYING_NULL == idCurrentlyPlaying) {
return null;
}
return tFI(idCurrentlyPlaying).getMedia();
}
private static Playable externalMedia() {
return new ExternalMedia("http://example.com/episode.mp3", MediaType.AUDIO);
}
private static final long ID_CURRENTLY_PLAYING_NULL = -1L;
private static final long ID_CURRENTLY_PLAYING_NOT_FEEDMEDIA = -9999L;
} }
@ -136,24 +152,21 @@ public class ItemEnqueuePositionCalculatorTest {
@Parameters(name = "{index}: case<{0}>") @Parameters(name = "{index}: case<{0}>")
public static Iterable<Object[]> data() { public static Iterable<Object[]> data() {
Options optDefault = new Options();
Options optEnqAtFront = new Options().setEnqueueAtFront(true);
// Attempts to make test more readable by showing the expected list of ids // Attempts to make test more readable by showing the expected list of ids
// (rather than the expected positions) // (rather than the expected positions)
return Arrays.asList(new Object[][] { return Arrays.asList(new Object[][] {
{"download order test, enqueue default", {"download order test, enqueue default",
CollectionTestUtil.concat(QUEUE_DEFAULT_IDS, 101L), concat(QUEUE_DEFAULT_IDS, 101L),
CollectionTestUtil.concat(QUEUE_DEFAULT_IDS, CollectionTestUtil.list(101L, 102L)), concat(QUEUE_DEFAULT_IDS, list(101L, 102L)),
CollectionTestUtil.concat(QUEUE_DEFAULT_IDS, CollectionTestUtil.list(101L, 102L, 201L)), concat(QUEUE_DEFAULT_IDS, list(101L, 102L, 201L)),
CollectionTestUtil.concat(QUEUE_DEFAULT_IDS, CollectionTestUtil.list(101L, 102L, 201L, 202L)), concat(QUEUE_DEFAULT_IDS, list(101L, 102L, 201L, 202L)),
optDefault, QUEUE_DEFAULT}, BACK, QUEUE_DEFAULT},
{"download order test, enqueue at front", {"download order test, enqueue at front",
CollectionTestUtil.concat(101L, QUEUE_DEFAULT_IDS), concat(101L, QUEUE_DEFAULT_IDS),
CollectionTestUtil.concat(CollectionTestUtil.list(101L, 102L), QUEUE_DEFAULT_IDS), concat(list(101L, 102L), QUEUE_DEFAULT_IDS),
CollectionTestUtil.concat(CollectionTestUtil.list(101L, 102L, 201L), QUEUE_DEFAULT_IDS), concat(list(101L, 102L, 201L), QUEUE_DEFAULT_IDS),
CollectionTestUtil.concat(CollectionTestUtil.list(101L, 102L, 201L, 202L), QUEUE_DEFAULT_IDS), concat(list(101L, 102L, 201L, 202L), QUEUE_DEFAULT_IDS),
optEnqAtFront, QUEUE_DEFAULT}, FRONT, QUEUE_DEFAULT},
}); });
} }
@ -174,7 +187,7 @@ public class ItemEnqueuePositionCalculatorTest {
public List<Long> idsExpectedAfter202; public List<Long> idsExpectedAfter202;
@Parameter(5) @Parameter(5)
public Options options; public EnqueueLocation options;
@Parameter(6) @Parameter(6)
public List<FeedItem> queueInitial; public List<FeedItem> queueInitial;
@ -217,7 +230,7 @@ public class ItemEnqueuePositionCalculatorTest {
FeedItem tFI202 = tFI_isDownloading(202, stubDownloadStateProvider); FeedItem tFI202 = tFI_isDownloading(202, stubDownloadStateProvider);
doAddToQueueAndAssertResult(message + " (bulk insertion, 2nd item)", doAddToQueueAndAssertResult(message + " (bulk insertion, 2nd item)",
calculator, 1, tFI202, queue, calculator, 1, tFI202, queue, null,
idsExpectedAfter202); idsExpectedAfter202);
// TODO: simulate download failure cases. // TODO: simulate download failure cases.
@ -249,31 +262,34 @@ public class ItemEnqueuePositionCalculatorTest {
FeedItem itemToAdd, FeedItem itemToAdd,
List<FeedItem> queue, List<FeedItem> queue,
List<Long> idsExpected) { List<Long> idsExpected) {
int posActual = calculator.calcPosition(positionAmongAdd, itemToAdd, queue); doAddToQueueAndAssertResult(message, calculator, positionAmongAdd, itemToAdd, queue, null, idsExpected);
}
static void doAddToQueueAndAssertResult(String message,
ItemEnqueuePositionCalculator calculator,
int positionAmongAdd,
FeedItem itemToAdd,
List<FeedItem> queue,
Playable currentlyPlaying,
List<Long> idsExpected) {
int posActual = calculator.calcPosition(positionAmongAdd, itemToAdd, queue, currentlyPlaying);
queue.add(posActual, itemToAdd); queue.add(posActual, itemToAdd);
assertEquals(message, idsExpected, getIdList(queue)); assertEquals(message, idsExpected, getIdList(queue));
} }
static final List<FeedItem> QUEUE_EMPTY = Collections.unmodifiableList(Arrays.asList()); static final List<FeedItem> QUEUE_EMPTY = Collections.unmodifiableList(Arrays.asList());
static final List<FeedItem> QUEUE_DEFAULT = Collections.unmodifiableList(Arrays.asList(tFI(11), tFI(12), tFI(13), tFI(14))); static final List<FeedItem> QUEUE_DEFAULT =
static final List<Long> QUEUE_DEFAULT_IDS = QUEUE_DEFAULT.stream().map(fi -> fi.getId()).collect(Collectors.toList()); Collections.unmodifiableList(Arrays.asList(tFI(11), tFI(12), tFI(13), tFI(14)));
static final List<Long> QUEUE_DEFAULT_IDS =
QUEUE_DEFAULT.stream().map(fi -> fi.getId()).collect(Collectors.toList());
static FeedItem tFI(long id) { static FeedItem tFI(long id) {
return tFI(id, -1);
}
static FeedItem tFI(long id, int position) {
FeedItem item = tFINoMedia(id); FeedItem item = tFINoMedia(id);
FeedMedia media = new FeedMedia(item, "download_url", 1234567, "audio/mpeg"); FeedMedia media = new FeedMedia(item, "download_url", 1234567, "audio/mpeg");
media.setId(item.getId()); media.setId(item.getId());
item.setMedia(media); item.setMedia(media);
if (position >= 0) {
media.setPosition(position);
}
return item; return item;
} }