Merge pull request #3315 from damoasda/Queue-Sort-Order2
Queue sort order - with keep sorted checkbox
This commit is contained in:
commit
4e79ddb51b
|
@ -49,6 +49,7 @@ import de.danoeh.antennapod.core.util.Converter;
|
||||||
import de.danoeh.antennapod.core.util.FeedItemUtil;
|
import de.danoeh.antennapod.core.util.FeedItemUtil;
|
||||||
import de.danoeh.antennapod.core.util.LongList;
|
import de.danoeh.antennapod.core.util.LongList;
|
||||||
import de.danoeh.antennapod.core.util.QueueSorter;
|
import de.danoeh.antennapod.core.util.QueueSorter;
|
||||||
|
import de.danoeh.antennapod.core.util.SortOrder;
|
||||||
import de.danoeh.antennapod.dialog.EpisodesApplyActionFragment;
|
import de.danoeh.antennapod.dialog.EpisodesApplyActionFragment;
|
||||||
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
|
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
|
||||||
import de.danoeh.antennapod.menuhandler.MenuItemUtils;
|
import de.danoeh.antennapod.menuhandler.MenuItemUtils;
|
||||||
|
@ -276,6 +277,19 @@ public class QueueFragment extends Fragment {
|
||||||
|
|
||||||
MenuItemUtils.refreshLockItem(getActivity(), menu);
|
MenuItemUtils.refreshLockItem(getActivity(), menu);
|
||||||
|
|
||||||
|
// Show Lock Item only if queue is sorted manually
|
||||||
|
boolean keepSorted = UserPreferences.isQueueKeepSorted();
|
||||||
|
MenuItem lockItem = menu.findItem(R.id.queue_lock);
|
||||||
|
lockItem.setVisible(!keepSorted);
|
||||||
|
|
||||||
|
// Random sort is not supported in keep sorted mode
|
||||||
|
MenuItem sortRandomItem = menu.findItem(R.id.queue_sort_random);
|
||||||
|
sortRandomItem.setVisible(!keepSorted);
|
||||||
|
|
||||||
|
// Set keep sorted checkbox
|
||||||
|
MenuItem keepSortedItem = menu.findItem(R.id.queue_keep_sorted);
|
||||||
|
keepSortedItem.setChecked(keepSorted);
|
||||||
|
|
||||||
isUpdatingFeeds = MenuItemUtils.updateRefreshMenuItem(menu, R.id.refresh_item, updateRefreshMenuItemChecker);
|
isUpdatingFeeds = MenuItemUtils.updateRefreshMenuItem(menu, R.id.refresh_item, updateRefreshMenuItemChecker);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -325,37 +339,50 @@ public class QueueFragment extends Fragment {
|
||||||
EpisodesApplyActionFragment.newInstance(queue, ACTION_DELETE | ACTION_REMOVE_FROM_QUEUE));
|
EpisodesApplyActionFragment.newInstance(queue, ACTION_DELETE | ACTION_REMOVE_FROM_QUEUE));
|
||||||
return true;
|
return true;
|
||||||
case R.id.queue_sort_episode_title_asc:
|
case R.id.queue_sort_episode_title_asc:
|
||||||
QueueSorter.sort(getActivity(), QueueSorter.Rule.EPISODE_TITLE_ASC, true);
|
setSortOrder(SortOrder.EPISODE_TITLE_A_Z);
|
||||||
return true;
|
return true;
|
||||||
case R.id.queue_sort_episode_title_desc:
|
case R.id.queue_sort_episode_title_desc:
|
||||||
QueueSorter.sort(getActivity(), QueueSorter.Rule.EPISODE_TITLE_DESC, true);
|
setSortOrder(SortOrder.EPISODE_TITLE_Z_A);
|
||||||
return true;
|
return true;
|
||||||
case R.id.queue_sort_date_asc:
|
case R.id.queue_sort_date_asc:
|
||||||
QueueSorter.sort(getActivity(), QueueSorter.Rule.DATE_ASC, true);
|
setSortOrder(SortOrder.DATE_OLD_NEW);
|
||||||
return true;
|
return true;
|
||||||
case R.id.queue_sort_date_desc:
|
case R.id.queue_sort_date_desc:
|
||||||
QueueSorter.sort(getActivity(), QueueSorter.Rule.DATE_DESC, true);
|
setSortOrder(SortOrder.DATE_NEW_OLD);
|
||||||
return true;
|
return true;
|
||||||
case R.id.queue_sort_duration_asc:
|
case R.id.queue_sort_duration_asc:
|
||||||
QueueSorter.sort(getActivity(), QueueSorter.Rule.DURATION_ASC, true);
|
setSortOrder(SortOrder.DURATION_SHORT_LONG);
|
||||||
return true;
|
return true;
|
||||||
case R.id.queue_sort_duration_desc:
|
case R.id.queue_sort_duration_desc:
|
||||||
QueueSorter.sort(getActivity(), QueueSorter.Rule.DURATION_DESC, true);
|
setSortOrder(SortOrder.DURATION_LONG_SHORT);
|
||||||
return true;
|
return true;
|
||||||
case R.id.queue_sort_feed_title_asc:
|
case R.id.queue_sort_feed_title_asc:
|
||||||
QueueSorter.sort(getActivity(), QueueSorter.Rule.FEED_TITLE_ASC, true);
|
setSortOrder(SortOrder.FEED_TITLE_A_Z);
|
||||||
return true;
|
return true;
|
||||||
case R.id.queue_sort_feed_title_desc:
|
case R.id.queue_sort_feed_title_desc:
|
||||||
QueueSorter.sort(getActivity(), QueueSorter.Rule.FEED_TITLE_DESC, true);
|
setSortOrder(SortOrder.FEED_TITLE_Z_A);
|
||||||
return true;
|
return true;
|
||||||
case R.id.queue_sort_random:
|
case R.id.queue_sort_random:
|
||||||
QueueSorter.sort(getActivity(), QueueSorter.Rule.RANDOM, true);
|
setSortOrder(SortOrder.RANDOM);
|
||||||
return true;
|
return true;
|
||||||
case R.id.queue_sort_smart_shuffle_asc:
|
case R.id.queue_sort_smart_shuffle_asc:
|
||||||
QueueSorter.sort(getActivity(), QueueSorter.Rule.SMART_SHUFFLE_ASC, true);
|
setSortOrder(SortOrder.SMART_SHUFFLE_OLD_NEW);
|
||||||
return true;
|
return true;
|
||||||
case R.id.queue_sort_smart_shuffle_desc:
|
case R.id.queue_sort_smart_shuffle_desc:
|
||||||
QueueSorter.sort(getActivity(), QueueSorter.Rule.SMART_SHUFFLE_DESC, true);
|
setSortOrder(SortOrder.SMART_SHUFFLE_NEW_OLD);
|
||||||
|
return true;
|
||||||
|
case R.id.queue_keep_sorted:
|
||||||
|
boolean keepSortedOld = UserPreferences.isQueueKeepSorted();
|
||||||
|
boolean keepSortedNew = !keepSortedOld;
|
||||||
|
UserPreferences.setQueueKeepSorted(keepSortedNew);
|
||||||
|
if (keepSortedNew) {
|
||||||
|
SortOrder sortOrder = UserPreferences.getQueueKeepSortedOrder();
|
||||||
|
QueueSorter.sort(sortOrder, true);
|
||||||
|
recyclerAdapter.setLocked(true);
|
||||||
|
} else {
|
||||||
|
recyclerAdapter.setLocked(UserPreferences.isQueueLocked());
|
||||||
|
}
|
||||||
|
getActivity().invalidateOptionsMenu();
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
|
@ -365,6 +392,15 @@ public class QueueFragment extends Fragment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called if the user clicks on a sort order menu item.
|
||||||
|
*
|
||||||
|
* @param sortOrder New sort order.
|
||||||
|
*/
|
||||||
|
private void setSortOrder(SortOrder sortOrder) {
|
||||||
|
UserPreferences.setQueueKeepSortedOrder(sortOrder);
|
||||||
|
QueueSorter.sort(sortOrder, true);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onContextItemSelected(MenuItem item) {
|
public boolean onContextItemSelected(MenuItem item) {
|
||||||
|
@ -670,5 +706,4 @@ public class QueueFragment extends Fragment {
|
||||||
}
|
}
|
||||||
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
|
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,6 @@ import de.danoeh.antennapod.core.gpoddernet.model.GpodnetEpisodeAction.Action;
|
||||||
import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
|
import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
|
||||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||||
import de.danoeh.antennapod.core.service.playback.PlaybackService;
|
import de.danoeh.antennapod.core.service.playback.PlaybackService;
|
||||||
import de.danoeh.antennapod.core.storage.DBTasks;
|
|
||||||
import de.danoeh.antennapod.core.storage.DBWriter;
|
import de.danoeh.antennapod.core.storage.DBWriter;
|
||||||
import de.danoeh.antennapod.core.util.FeedItemUtil;
|
import de.danoeh.antennapod.core.util.FeedItemUtil;
|
||||||
import de.danoeh.antennapod.core.util.IntentUtils;
|
import de.danoeh.antennapod.core.util.IntentUtils;
|
||||||
|
@ -68,16 +67,17 @@ public class FeedItemMenuHandler {
|
||||||
}
|
}
|
||||||
boolean hasMedia = selectedItem.getMedia() != null;
|
boolean hasMedia = selectedItem.getMedia() != null;
|
||||||
boolean isPlaying = hasMedia && selectedItem.getState() == FeedItem.State.PLAYING;
|
boolean isPlaying = hasMedia && selectedItem.getState() == FeedItem.State.PLAYING;
|
||||||
|
boolean keepSorted = UserPreferences.isQueueKeepSorted();
|
||||||
|
|
||||||
if (!isPlaying) {
|
if (!isPlaying) {
|
||||||
mi.setItemVisibility(R.id.skip_episode_item, false);
|
mi.setItemVisibility(R.id.skip_episode_item, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isInQueue = selectedItem.isTagged(FeedItem.TAG_QUEUE);
|
boolean isInQueue = selectedItem.isTagged(FeedItem.TAG_QUEUE);
|
||||||
if(queueAccess == null || queueAccess.size() == 0 || queueAccess.get(0) == selectedItem.getId()) {
|
if (queueAccess == null || queueAccess.size() == 0 || queueAccess.get(0) == selectedItem.getId() || keepSorted) {
|
||||||
mi.setItemVisibility(R.id.move_to_top_item, false);
|
mi.setItemVisibility(R.id.move_to_top_item, false);
|
||||||
}
|
}
|
||||||
if(queueAccess == null || queueAccess.size() == 0 || queueAccess.get(queueAccess.size()-1) == selectedItem.getId()) {
|
if (queueAccess == null || queueAccess.size() == 0 || queueAccess.get(queueAccess.size()-1) == selectedItem.getId() || keepSorted) {
|
||||||
mi.setItemVisibility(R.id.move_to_bottom_item, false);
|
mi.setItemVisibility(R.id.move_to_bottom_item, false);
|
||||||
}
|
}
|
||||||
if (!isInQueue) {
|
if (!isInQueue) {
|
||||||
|
|
|
@ -102,6 +102,11 @@
|
||||||
android:title="@string/sort_new_to_old"/>
|
android:title="@string/sort_new_to_old"/>
|
||||||
</menu>
|
</menu>
|
||||||
</item>
|
</item>
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/queue_keep_sorted"
|
||||||
|
android:title="@string/keep_sorted"
|
||||||
|
android:checkable="true" />
|
||||||
</menu>
|
</menu>
|
||||||
</item>
|
</item>
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@ import de.danoeh.antennapod.core.storage.APNullCleanupAlgorithm;
|
||||||
import de.danoeh.antennapod.core.storage.APQueueCleanupAlgorithm;
|
import de.danoeh.antennapod.core.storage.APQueueCleanupAlgorithm;
|
||||||
import de.danoeh.antennapod.core.storage.EpisodeCleanupAlgorithm;
|
import de.danoeh.antennapod.core.storage.EpisodeCleanupAlgorithm;
|
||||||
import de.danoeh.antennapod.core.util.Converter;
|
import de.danoeh.antennapod.core.util.Converter;
|
||||||
|
import de.danoeh.antennapod.core.util.SortOrder;
|
||||||
import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
|
import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -59,6 +60,8 @@ public class UserPreferences {
|
||||||
|
|
||||||
// Queue
|
// Queue
|
||||||
private static final String PREF_QUEUE_ADD_TO_FRONT = "prefQueueAddToFront";
|
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
|
// Playback
|
||||||
public static final String PREF_PAUSE_ON_HEADSET_DISCONNECT = "prefPauseOnHeadsetDisconnect";
|
public static final String PREF_PAUSE_ON_HEADSET_DISCONNECT = "prefPauseOnHeadsetDisconnect";
|
||||||
|
@ -534,7 +537,8 @@ public class UserPreferences {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isQueueLocked() {
|
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) {
|
public static void setFastForwardSecs(int secs) {
|
||||||
|
@ -909,4 +913,49 @@ public class UserPreferences {
|
||||||
public static boolean timeRespectsSpeed() {
|
public static boolean timeRespectsSpeed() {
|
||||||
return prefs.getBoolean(PREF_TIME_RESPECTS_SPEED, false);
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,11 +11,8 @@ import android.util.Log;
|
||||||
import org.greenrobot.eventbus.EventBus;
|
import org.greenrobot.eventbus.EventBus;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.UnsupportedEncodingException;
|
|
||||||
import java.net.URLEncoder;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
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.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.QueueSorter;
|
||||||
|
import de.danoeh.antennapod.core.util.SortOrder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides methods for writing data to AntennaPod's database.
|
* Provides methods for writing data to AntennaPod's database.
|
||||||
|
@ -383,6 +382,7 @@ public class DBWriter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (queueModified) {
|
if (queueModified) {
|
||||||
|
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);
|
||||||
|
@ -401,6 +401,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.
|
* Removes all FeedItem objects from the queue.
|
||||||
*
|
*
|
||||||
|
@ -850,31 +877,8 @@ public class DBWriter {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sort the FeedItems in the queue with the given Comparator.
|
* Sort the FeedItems in the queue with the given Permutor.
|
||||||
* @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.
|
|
||||||
* @param permutor Encapsulates whole-Queue reordering logic.
|
* @param permutor Encapsulates whole-Queue reordering logic.
|
||||||
* @param broadcastUpdate <code>true</code> if this operation should trigger a
|
* @param broadcastUpdate <code>true</code> if this operation should trigger a
|
||||||
* QueueUpdateBroadcast. This option should be set to <code>false</code>
|
* QueueUpdateBroadcast. This option should be set to <code>false</code>
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
package de.danoeh.antennapod.core.util;
|
package de.danoeh.antennapod.core.util;
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
@ -17,38 +15,48 @@ import de.danoeh.antennapod.core.storage.DBWriter;
|
||||||
* Provides method for sorting the queue according to rules.
|
* Provides method for sorting the queue according to rules.
|
||||||
*/
|
*/
|
||||||
public class QueueSorter {
|
public class QueueSorter {
|
||||||
public enum Rule {
|
|
||||||
EPISODE_TITLE_ASC,
|
/**
|
||||||
EPISODE_TITLE_DESC,
|
* Sorts the queue by the given sort order and sends a broadcast update.
|
||||||
DATE_ASC,
|
*
|
||||||
DATE_DESC,
|
* @param sortOrder Sort order.
|
||||||
DURATION_ASC,
|
* @param broadcastUpdate Send broadcast update?
|
||||||
DURATION_DESC,
|
*/
|
||||||
FEED_TITLE_ASC,
|
public static void sort(SortOrder sortOrder, boolean broadcastUpdate) {
|
||||||
FEED_TITLE_DESC,
|
Permutor<FeedItem> permutor = getPermutor(sortOrder);
|
||||||
RANDOM,
|
if (permutor != null) {
|
||||||
SMART_SHUFFLE_ASC,
|
DBWriter.reorderQueue(permutor, broadcastUpdate);
|
||||||
SMART_SHUFFLE_DESC
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
Comparator<FeedItem> comparator = null;
|
||||||
Permutor<FeedItem> permutor = null;
|
Permutor<FeedItem> permutor = null;
|
||||||
|
|
||||||
switch (rule) {
|
switch (sortOrder) {
|
||||||
case EPISODE_TITLE_ASC:
|
case EPISODE_TITLE_A_Z:
|
||||||
comparator = (f1, f2) -> f1.getTitle().compareTo(f2.getTitle());
|
comparator = (f1, f2) -> f1.getTitle().compareTo(f2.getTitle());
|
||||||
break;
|
break;
|
||||||
case EPISODE_TITLE_DESC:
|
case EPISODE_TITLE_Z_A:
|
||||||
comparator = (f1, f2) -> f2.getTitle().compareTo(f1.getTitle());
|
comparator = (f1, f2) -> f2.getTitle().compareTo(f1.getTitle());
|
||||||
break;
|
break;
|
||||||
case DATE_ASC:
|
case DATE_OLD_NEW:
|
||||||
comparator = (f1, f2) -> f1.getPubDate().compareTo(f2.getPubDate());
|
comparator = (f1, f2) -> f1.getPubDate().compareTo(f2.getPubDate());
|
||||||
break;
|
break;
|
||||||
case DATE_DESC:
|
case DATE_NEW_OLD:
|
||||||
comparator = (f1, f2) -> f2.getPubDate().compareTo(f1.getPubDate());
|
comparator = (f1, f2) -> f2.getPubDate().compareTo(f1.getPubDate());
|
||||||
break;
|
break;
|
||||||
case DURATION_ASC:
|
case DURATION_SHORT_LONG:
|
||||||
comparator = (f1, f2) -> {
|
comparator = (f1, f2) -> {
|
||||||
FeedMedia f1Media = f1.getMedia();
|
FeedMedia f1Media = f1.getMedia();
|
||||||
FeedMedia f2Media = f2.getMedia();
|
FeedMedia f2Media = f2.getMedia();
|
||||||
|
@ -61,7 +69,7 @@ public class QueueSorter {
|
||||||
return duration1 - duration2;
|
return duration1 - duration2;
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
case DURATION_DESC:
|
case DURATION_LONG_SHORT:
|
||||||
comparator = (f1, f2) -> {
|
comparator = (f1, f2) -> {
|
||||||
FeedMedia f1Media = f1.getMedia();
|
FeedMedia f1Media = f1.getMedia();
|
||||||
FeedMedia f2Media = f2.getMedia();
|
FeedMedia f2Media = f2.getMedia();
|
||||||
|
@ -71,29 +79,28 @@ public class QueueSorter {
|
||||||
return -1 * (duration1 - duration2);
|
return -1 * (duration1 - duration2);
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
case FEED_TITLE_ASC:
|
case FEED_TITLE_A_Z:
|
||||||
comparator = (f1, f2) -> f1.getFeed().getTitle().compareTo(f2.getFeed().getTitle());
|
comparator = (f1, f2) -> f1.getFeed().getTitle().compareTo(f2.getFeed().getTitle());
|
||||||
break;
|
break;
|
||||||
case FEED_TITLE_DESC:
|
case FEED_TITLE_Z_A:
|
||||||
comparator = (f1, f2) -> f2.getFeed().getTitle().compareTo(f1.getFeed().getTitle());
|
comparator = (f1, f2) -> f2.getFeed().getTitle().compareTo(f1.getFeed().getTitle());
|
||||||
break;
|
break;
|
||||||
case RANDOM:
|
case RANDOM:
|
||||||
permutor = Collections::shuffle;
|
permutor = Collections::shuffle;
|
||||||
break;
|
break;
|
||||||
case SMART_SHUFFLE_ASC:
|
case SMART_SHUFFLE_OLD_NEW:
|
||||||
permutor = (queue) -> smartShuffle(queue, true);
|
permutor = (queue) -> smartShuffle(queue, true);
|
||||||
break;
|
break;
|
||||||
case SMART_SHUFFLE_DESC:
|
case SMART_SHUFFLE_NEW_OLD:
|
||||||
permutor = (queue) -> smartShuffle(queue, false);
|
permutor = (queue) -> smartShuffle(queue, false);
|
||||||
break;
|
break;
|
||||||
default:
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (comparator != null) {
|
if (comparator != null) {
|
||||||
DBWriter.sortQueue(comparator, broadcastUpdate);
|
final Comparator<FeedItem> comparator2 = comparator;
|
||||||
} else if (permutor != null) {
|
permutor = (queue) -> Collections.sort(queue, comparator2);
|
||||||
DBWriter.reorderQueue(permutor, broadcastUpdate);
|
|
||||||
}
|
}
|
||||||
|
return permutor;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -276,6 +276,7 @@
|
||||||
<string name="move_to_top_label">Zum Anfang verschieben</string>
|
<string name="move_to_top_label">Zum Anfang verschieben</string>
|
||||||
<string name="move_to_bottom_label">Zum Ende verschieben</string>
|
<string name="move_to_bottom_label">Zum Ende verschieben</string>
|
||||||
<string name="sort">Sortieren</string>
|
<string name="sort">Sortieren</string>
|
||||||
|
<string name="keep_sorted">Automatisch sortieren</string>
|
||||||
<string name="date">Datum</string>
|
<string name="date">Datum</string>
|
||||||
<string name="duration">Dauer</string>
|
<string name="duration">Dauer</string>
|
||||||
<string name="episode_title">Episodentitel</string>
|
<string name="episode_title">Episodentitel</string>
|
||||||
|
@ -285,6 +286,8 @@
|
||||||
<string name="ascending">Aufsteigend</string>
|
<string name="ascending">Aufsteigend</string>
|
||||||
<string name="descending">Absteigend</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="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-->
|
<!--Flattr-->
|
||||||
<string name="flattr_auth_label">Flattr Anmeldung</string>
|
<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>
|
<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>
|
||||||
|
|
|
@ -296,6 +296,7 @@
|
||||||
<string name="move_to_top_label">Move to top</string>
|
<string name="move_to_top_label">Move to top</string>
|
||||||
<string name="move_to_bottom_label">Move to bottom</string>
|
<string name="move_to_bottom_label">Move to bottom</string>
|
||||||
<string name="sort">Sort</string>
|
<string name="sort">Sort</string>
|
||||||
|
<string name="keep_sorted">Keep sorted</string>
|
||||||
<string name="date">Date</string>
|
<string name="date">Date</string>
|
||||||
<string name="duration">Duration</string>
|
<string name="duration">Duration</string>
|
||||||
<string name="episode_title">Episode title</string>
|
<string name="episode_title">Episode title</string>
|
||||||
|
|
|
@ -29,4 +29,13 @@ public class TextUtils {
|
||||||
return false;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue