diff options
author | H. Lehmann <ByteHamster@users.noreply.github.com> | 2019-08-11 14:48:01 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-08-11 14:48:01 +0200 |
commit | 4e79ddb51bf4b281ce637bb883f6b5d3de12432b (patch) | |
tree | f2635e1f0c712de74849141beb2f9b943e8332fb /core/src | |
parent | 3ceb99d2e242244ab62f5ed4edc09ff416f2d965 (diff) | |
parent | c4399de8cb15de5f3e6c9c194cebb2521b9352f7 (diff) | |
download | AntennaPod-4e79ddb51bf4b281ce637bb883f6b5d3de12432b.zip |
Merge pull request #3315 from damoasda/Queue-Sort-Order2
Queue sort order - with keep sorted checkbox
Diffstat (limited to 'core/src')
8 files changed, 318 insertions, 59 deletions
diff --git a/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java b/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java index bcc4f533e..8d8c6e65d 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java +++ b/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java @@ -29,6 +29,7 @@ import de.danoeh.antennapod.core.storage.APNullCleanupAlgorithm; import de.danoeh.antennapod.core.storage.APQueueCleanupAlgorithm; import de.danoeh.antennapod.core.storage.EpisodeCleanupAlgorithm; import de.danoeh.antennapod.core.util.Converter; +import de.danoeh.antennapod.core.util.SortOrder; import de.danoeh.antennapod.core.util.download.AutoUpdateManager; /** @@ -59,6 +60,8 @@ public class UserPreferences { // Queue private static final String PREF_QUEUE_ADD_TO_FRONT = "prefQueueAddToFront"; + public static final String PREF_QUEUE_KEEP_SORTED = "prefQueueKeepSorted"; + public static final String PREF_QUEUE_KEEP_SORTED_ORDER = "prefQueueKeepSortedOrder"; // Playback public static final String PREF_PAUSE_ON_HEADSET_DISCONNECT = "prefPauseOnHeadsetDisconnect"; @@ -534,7 +537,8 @@ public class UserPreferences { } public static boolean isQueueLocked() { - return prefs.getBoolean(PREF_QUEUE_LOCKED, false); + return prefs.getBoolean(PREF_QUEUE_LOCKED, false) + || isQueueKeepSorted(); } public static void setFastForwardSecs(int secs) { @@ -909,4 +913,49 @@ public class UserPreferences { public static boolean timeRespectsSpeed() { return prefs.getBoolean(PREF_TIME_RESPECTS_SPEED, false); } + + /** + * Returns if the queue is in keep sorted mode. + * + * @see #getQueueKeepSortedOrder() + */ + public static boolean isQueueKeepSorted() { + return prefs.getBoolean(PREF_QUEUE_KEEP_SORTED, false); + } + + /** + * Enables/disables the keep sorted mode of the queue. + * + * @see #setQueueKeepSortedOrder(SortOrder) + */ + public static void setQueueKeepSorted(boolean keepSorted) { + prefs.edit() + .putBoolean(PREF_QUEUE_KEEP_SORTED, keepSorted) + .apply(); + } + + /** + * Returns the sort order for the queue keep sorted mode. + * Note: This value is stored independently from the keep sorted state. + * + * @see #isQueueKeepSorted() + */ + public static SortOrder getQueueKeepSortedOrder() { + String sortOrderStr = prefs.getString(PREF_QUEUE_KEEP_SORTED_ORDER, "use-default"); + return SortOrder.parseWithDefault(sortOrderStr, SortOrder.DATE_NEW_OLD); + } + + /** + * Sets the sort order for the queue keep sorted mode. + * + * @see #setQueueKeepSorted(boolean) + */ + public static void setQueueKeepSortedOrder(SortOrder sortOrder) { + if (sortOrder == null) { + return; + } + prefs.edit() + .putString(PREF_QUEUE_KEEP_SORTED_ORDER, sortOrder.name()) + .apply(); + } } diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java index 49fbdcbfd..7d525a376 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java @@ -11,11 +11,8 @@ import android.util.Log; import org.greenrobot.eventbus.EventBus; import java.io.File; -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; import java.util.ArrayList; import java.util.Collections; -import java.util.Comparator; import java.util.Date; import java.util.List; import java.util.Set; @@ -44,6 +41,8 @@ import de.danoeh.antennapod.core.service.playback.PlaybackService; import de.danoeh.antennapod.core.util.IntentUtils; import de.danoeh.antennapod.core.util.LongList; import de.danoeh.antennapod.core.util.Permutor; +import de.danoeh.antennapod.core.util.QueueSorter; +import de.danoeh.antennapod.core.util.SortOrder; /** * Provides methods for writing data to AntennaPod's database. @@ -383,6 +382,7 @@ public class DBWriter { } } if (queueModified) { + applySortOrder(queue, events); adapter.setQueue(queue); for (QueueEvent event : events) { EventBus.getDefault().post(event); @@ -402,6 +402,33 @@ public class DBWriter { } /** + * Sorts the queue depending on the configured sort order. + * If the queue is not in keep sorted mode, nothing happens. + * + * @param queue The queue to be sorted. + * @param events Replaces the events by a single SORT event if the list has to be sorted automatically. + */ + private static void applySortOrder(List<FeedItem> queue, List<QueueEvent> events) { + if (!UserPreferences.isQueueKeepSorted()) { + // queue is not in keep sorted mode, there's nothing to do + return; + } + + // Sort queue by configured sort order + SortOrder sortOrder = UserPreferences.getQueueKeepSortedOrder(); + if (sortOrder == SortOrder.RANDOM) { + // do not shuffle the list on every change + return; + } + Permutor<FeedItem> permutor = QueueSorter.getPermutor(sortOrder); + permutor.reorder(queue); + + // Replace ADDED events by a single SORTED event + events.clear(); + events.add(QueueEvent.sorted(queue)); + } + + /** * Removes all FeedItem objects from the queue. * */ @@ -850,31 +877,8 @@ public class DBWriter { } /** - * Sort the FeedItems in the queue with the given Comparator. - * @param comparator FeedItem comparator - * @param broadcastUpdate true if this operation should trigger a QueueUpdateBroadcast. This option should be set to - */ - public static Future<?> sortQueue(final Comparator<FeedItem> comparator, final boolean broadcastUpdate) { - return dbExec.submit(() -> { - final PodDBAdapter adapter = PodDBAdapter.getInstance(); - adapter.open(); - final List<FeedItem> queue = DBReader.getQueue(adapter); - - if (queue != null) { - Collections.sort(queue, comparator); - adapter.setQueue(queue); - if (broadcastUpdate) { - EventBus.getDefault().post(QueueEvent.sorted(queue)); - } - } else { - Log.e(TAG, "sortQueue: Could not load queue"); - } - adapter.close(); - }); - } - - /** - * Similar to sortQueue, but allows more complex reordering by providing whole-queue context. + * Sort the FeedItems in the queue with the given Permutor. + * * @param permutor Encapsulates whole-Queue reordering logic. * @param broadcastUpdate <code>true</code> if this operation should trigger a * QueueUpdateBroadcast. This option should be set to <code>false</code> diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/QueueSorter.java b/core/src/main/java/de/danoeh/antennapod/core/util/QueueSorter.java index 8680b2d2e..37172d042 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/QueueSorter.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/QueueSorter.java @@ -1,7 +1,5 @@ package de.danoeh.antennapod.core.util; -import android.content.Context; - import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -17,38 +15,48 @@ import de.danoeh.antennapod.core.storage.DBWriter; * Provides method for sorting the queue according to rules. */ public class QueueSorter { - public enum Rule { - EPISODE_TITLE_ASC, - EPISODE_TITLE_DESC, - DATE_ASC, - DATE_DESC, - DURATION_ASC, - DURATION_DESC, - FEED_TITLE_ASC, - FEED_TITLE_DESC, - RANDOM, - SMART_SHUFFLE_ASC, - SMART_SHUFFLE_DESC + + /** + * Sorts the queue by the given sort order and sends a broadcast update. + * + * @param sortOrder Sort order. + * @param broadcastUpdate Send broadcast update? + */ + public static void sort(SortOrder sortOrder, boolean broadcastUpdate) { + Permutor<FeedItem> permutor = getPermutor(sortOrder); + if (permutor != null) { + DBWriter.reorderQueue(permutor, broadcastUpdate); + } } - public static void sort(final Context context, final Rule rule, final boolean broadcastUpdate) { + /** + * Returns a Permutor that sorts a list appropriate to the given sort order. + * + * @param sortOrder Sort order. + * @return Permutor that sorts a list appropriate to the given sort order. <code>null</code> if the order is unknown or <code>null</code>. + */ + public static Permutor<FeedItem> getPermutor(SortOrder sortOrder) { + if (sortOrder == null) { + return null; + } + Comparator<FeedItem> comparator = null; Permutor<FeedItem> permutor = null; - switch (rule) { - case EPISODE_TITLE_ASC: + switch (sortOrder) { + case EPISODE_TITLE_A_Z: comparator = (f1, f2) -> f1.getTitle().compareTo(f2.getTitle()); break; - case EPISODE_TITLE_DESC: + case EPISODE_TITLE_Z_A: comparator = (f1, f2) -> f2.getTitle().compareTo(f1.getTitle()); break; - case DATE_ASC: + case DATE_OLD_NEW: comparator = (f1, f2) -> f1.getPubDate().compareTo(f2.getPubDate()); break; - case DATE_DESC: + case DATE_NEW_OLD: comparator = (f1, f2) -> f2.getPubDate().compareTo(f1.getPubDate()); break; - case DURATION_ASC: + case DURATION_SHORT_LONG: comparator = (f1, f2) -> { FeedMedia f1Media = f1.getMedia(); FeedMedia f2Media = f2.getMedia(); @@ -61,7 +69,7 @@ public class QueueSorter { return duration1 - duration2; }; break; - case DURATION_DESC: + case DURATION_LONG_SHORT: comparator = (f1, f2) -> { FeedMedia f1Media = f1.getMedia(); FeedMedia f2Media = f2.getMedia(); @@ -71,29 +79,28 @@ public class QueueSorter { return -1 * (duration1 - duration2); }; break; - case FEED_TITLE_ASC: + case FEED_TITLE_A_Z: comparator = (f1, f2) -> f1.getFeed().getTitle().compareTo(f2.getFeed().getTitle()); break; - case FEED_TITLE_DESC: + case FEED_TITLE_Z_A: comparator = (f1, f2) -> f2.getFeed().getTitle().compareTo(f1.getFeed().getTitle()); break; case RANDOM: permutor = Collections::shuffle; break; - case SMART_SHUFFLE_ASC: + case SMART_SHUFFLE_OLD_NEW: permutor = (queue) -> smartShuffle(queue, true); break; - case SMART_SHUFFLE_DESC: + case SMART_SHUFFLE_NEW_OLD: permutor = (queue) -> smartShuffle(queue, false); break; - default: } if (comparator != null) { - DBWriter.sortQueue(comparator, broadcastUpdate); - } else if (permutor != null) { - DBWriter.reorderQueue(permutor, broadcastUpdate); + final Comparator<FeedItem> comparator2 = comparator; + permutor = (queue) -> Collections.sort(queue, comparator2); } + return permutor; } /** diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/SortOrder.java b/core/src/main/java/de/danoeh/antennapod/core/util/SortOrder.java new file mode 100644 index 000000000..ae6fceb47 --- /dev/null +++ b/core/src/main/java/de/danoeh/antennapod/core/util/SortOrder.java @@ -0,0 +1,30 @@ +package de.danoeh.antennapod.core.util; + +/** + * Provides sort orders to sort a list of episodes. + */ +public enum SortOrder { + EPISODE_TITLE_A_Z, + EPISODE_TITLE_Z_A, + DATE_OLD_NEW, + DATE_NEW_OLD, + DURATION_SHORT_LONG, + DURATION_LONG_SHORT, + FEED_TITLE_A_Z, + FEED_TITLE_Z_A, + RANDOM, + SMART_SHUFFLE_OLD_NEW, + SMART_SHUFFLE_NEW_OLD; + + /** + * Converts the string representation to its enum value. If the string value is unknown, + * the given default value is returned. + */ + public static SortOrder parseWithDefault(String value, SortOrder defaultValue) { + try { + return valueOf(value); + } catch (IllegalArgumentException e) { + return defaultValue; + } + } +} diff --git a/core/src/main/res/values-de/strings.xml b/core/src/main/res/values-de/strings.xml index a58885975..f05b03fc4 100644 --- a/core/src/main/res/values-de/strings.xml +++ b/core/src/main/res/values-de/strings.xml @@ -276,6 +276,7 @@ <string name="move_to_top_label">Zum Anfang verschieben</string> <string name="move_to_bottom_label">Zum Ende verschieben</string> <string name="sort">Sortieren</string> + <string name="keep_sorted">Automatisch sortieren</string> <string name="date">Datum</string> <string name="duration">Dauer</string> <string name="episode_title">Episodentitel</string> @@ -285,6 +286,8 @@ <string name="ascending">Aufsteigend</string> <string name="descending">Absteigend</string> <string name="clear_queue_confirmation_msg">Bitte bestätige, dass ALLE Episoden aus der Abspielliste entfernt werden sollen</string> + <string name="sort_new_to_old">Neu bis alt</string> + <string name="sort_old_to_new">Alt bis neu</string> <!--Flattr--> <string name="flattr_auth_label">Flattr Anmeldung</string> <string name="flattr_auth_explanation">Drücke den Button unten, um den Authentifizierungsprozess zu starten. Du wirst zur Flattr-Anmeldeseite weitergeleitet. Hier wirst du gefragt, AntennaPod die Erlaubnis zu geben, Dinge zu flattrn. Nachdem du die Erlaubnis erteilt hast, kehrst du automatisch zu diesem Bildschirm zurück.</string> diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml index a6ba0740e..a4084b76b 100644 --- a/core/src/main/res/values/strings.xml +++ b/core/src/main/res/values/strings.xml @@ -296,6 +296,7 @@ <string name="move_to_top_label">Move to top</string> <string name="move_to_bottom_label">Move to bottom</string> <string name="sort">Sort</string> + <string name="keep_sorted">Keep sorted</string> <string name="date">Date</string> <string name="duration">Duration</string> <string name="episode_title">Episode title</string> diff --git a/core/src/test/java/android/text/TextUtils.java b/core/src/test/java/android/text/TextUtils.java index c31234171..eda31c3b5 100644 --- a/core/src/test/java/android/text/TextUtils.java +++ b/core/src/test/java/android/text/TextUtils.java @@ -29,4 +29,13 @@ public class TextUtils { return false; } + /** + * Returns <code>true</code> if the string is <code>null</code> or has zero length. + * + * @param str The string to be examined, can be <code>null</code>. + * @return <code>true</code> if the string is <code>null</code> or has zero length. + */ + public static boolean isEmpty(CharSequence str) { + return str == null || str.length() == 0; + } } diff --git a/core/src/test/java/de/danoeh/antennapod/core/util/QueueSorterTest.java b/core/src/test/java/de/danoeh/antennapod/core/util/QueueSorterTest.java new file mode 100644 index 000000000..791b6a75b --- /dev/null +++ b/core/src/test/java/de/danoeh/antennapod/core/util/QueueSorterTest.java @@ -0,0 +1,156 @@ +package de.danoeh.antennapod.core.util; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.List; + +import de.danoeh.antennapod.core.feed.Feed; +import de.danoeh.antennapod.core.feed.FeedItem; +import de.danoeh.antennapod.core.feed.FeedMedia; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertNull; + +/** + * Test class for QueueSorter. + */ +public class QueueSorterTest { + + @Test + public void testPermutorForRule_null() { + assertNull(QueueSorter.getPermutor(null)); + } + + @Test + public void testPermutorForRule_EPISODE_TITLE_ASC() { + Permutor<FeedItem> permutor = QueueSorter.getPermutor(SortOrder.EPISODE_TITLE_A_Z); + + List<FeedItem> itemList = getTestList(); + assertTrue(checkIdOrder(itemList, 1, 3, 2)); // before sorting + permutor.reorder(itemList); + assertTrue(checkIdOrder(itemList, 1, 2, 3)); // after sorting + } + + @Test + public void testPermutorForRule_EPISODE_TITLE_DESC() { + Permutor<FeedItem> permutor = QueueSorter.getPermutor(SortOrder.EPISODE_TITLE_Z_A); + + List<FeedItem> itemList = getTestList(); + assertTrue(checkIdOrder(itemList, 1, 3, 2)); // before sorting + permutor.reorder(itemList); + assertTrue(checkIdOrder(itemList, 3, 2, 1)); // after sorting + } + + @Test + public void testPermutorForRule_DATE_ASC() { + Permutor<FeedItem> permutor = QueueSorter.getPermutor(SortOrder.DATE_OLD_NEW); + + List<FeedItem> itemList = getTestList(); + assertTrue(checkIdOrder(itemList, 1, 3, 2)); // before sorting + permutor.reorder(itemList); + assertTrue(checkIdOrder(itemList, 1, 2, 3)); // after sorting + } + + @Test + public void testPermutorForRule_DATE_DESC() { + Permutor<FeedItem> permutor = QueueSorter.getPermutor(SortOrder.DATE_NEW_OLD); + + List<FeedItem> itemList = getTestList(); + assertTrue(checkIdOrder(itemList, 1, 3, 2)); // before sorting + permutor.reorder(itemList); + assertTrue(checkIdOrder(itemList, 3, 2, 1)); // after sorting + } + + @Test + public void testPermutorForRule_DURATION_ASC() { + Permutor<FeedItem> permutor = QueueSorter.getPermutor(SortOrder.DURATION_SHORT_LONG); + + List<FeedItem> itemList = getTestList(); + assertTrue(checkIdOrder(itemList, 1, 3, 2)); // before sorting + permutor.reorder(itemList); + assertTrue(checkIdOrder(itemList, 1, 2, 3)); // after sorting + } + + @Test + public void testPermutorForRule_DURATION_DESC() { + Permutor<FeedItem> permutor = QueueSorter.getPermutor(SortOrder.DURATION_LONG_SHORT); + + List<FeedItem> itemList = getTestList(); + assertTrue(checkIdOrder(itemList, 1, 3, 2)); // before sorting + permutor.reorder(itemList); + assertTrue(checkIdOrder(itemList, 3, 2, 1)); // after sorting + } + + @Test + public void testPermutorForRule_FEED_TITLE_ASC() { + Permutor<FeedItem> permutor = QueueSorter.getPermutor(SortOrder.FEED_TITLE_A_Z); + + List<FeedItem> itemList = getTestList(); + assertTrue(checkIdOrder(itemList, 1, 3, 2)); // before sorting + permutor.reorder(itemList); + assertTrue(checkIdOrder(itemList, 1, 2, 3)); // after sorting + } + + @Test + public void testPermutorForRule_FEED_TITLE_DESC() { + Permutor<FeedItem> permutor = QueueSorter.getPermutor(SortOrder.FEED_TITLE_Z_A); + + List<FeedItem> itemList = getTestList(); + assertTrue(checkIdOrder(itemList, 1, 3, 2)); // before sorting + permutor.reorder(itemList); + assertTrue(checkIdOrder(itemList, 3, 2, 1)); // after sorting + } + + /** + * Generates a list with test data. + */ + private List<FeedItem> getTestList() { + List<FeedItem> itemList = new ArrayList<>(); + + Calendar calendar = Calendar.getInstance(); + calendar.set(2019, 0, 1); // January 1st + Feed feed1 = new Feed(null, null, "Feed title 1"); + FeedItem feedItem1 = new FeedItem(1, "Title 1", null, null, calendar.getTime(), 0, feed1); + FeedMedia feedMedia1 = new FeedMedia(0, feedItem1, 1000, 0, 0, null, null, null, true, null, 0, 0); + feedItem1.setMedia(feedMedia1); + itemList.add(feedItem1); + + calendar.set(2019, 2, 1); // March 1st + Feed feed2 = new Feed(null, null, "Feed title 3"); + FeedItem feedItem2 = new FeedItem(3, "Title 3", null, null, calendar.getTime(), 0, feed2); + FeedMedia feedMedia2 = new FeedMedia(0, feedItem2, 3000, 0, 0, null, null, null, true, null, 0, 0); + feedItem2.setMedia(feedMedia2); + itemList.add(feedItem2); + + calendar.set(2019, 1, 1); // February 1st + Feed feed3 = new Feed(null, null, "Feed title 2"); + FeedItem feedItem3 = new FeedItem(2, "Title 2", null, null, calendar.getTime(), 0, feed3); + FeedMedia feedMedia3 = new FeedMedia(0, feedItem3, 2000, 0, 0, null, null, null, true, null, 0, 0); + feedItem3.setMedia(feedMedia3); + itemList.add(feedItem3); + + return itemList; + } + + /** + * Checks if both lists have the same size and the same ID order. + * + * @param itemList Item list. + * @param ids List of IDs. + * @return <code>true</code> if both lists have the same size and the same ID order. + */ + private boolean checkIdOrder(List<FeedItem> itemList, long... ids) { + if (itemList.size() != ids.length) { + return false; + } + + for (int i = 0; i < ids.length; i++) { + if (itemList.get(i).getId() != ids[i]) { + return false; + } + } + return true; + } +} |