Add sort option to episodes screen (#6286)
This commit is contained in:
parent
50eb1e9cf9
commit
25ddd73f24
|
@ -13,6 +13,7 @@ import androidx.test.rule.ActivityTestRule;
|
||||||
|
|
||||||
import de.danoeh.antennapod.core.receiver.MediaButtonReceiver;
|
import de.danoeh.antennapod.core.receiver.MediaButtonReceiver;
|
||||||
import de.danoeh.antennapod.model.feed.FeedItemFilter;
|
import de.danoeh.antennapod.model.feed.FeedItemFilter;
|
||||||
|
import de.danoeh.antennapod.model.feed.SortOrder;
|
||||||
import de.danoeh.antennapod.playback.base.PlayerStatus;
|
import de.danoeh.antennapod.playback.base.PlayerStatus;
|
||||||
import org.awaitility.Awaitility;
|
import org.awaitility.Awaitility;
|
||||||
import org.hamcrest.Matcher;
|
import org.hamcrest.Matcher;
|
||||||
|
@ -252,7 +253,8 @@ public class PlaybackTest {
|
||||||
openNavDrawer();
|
openNavDrawer();
|
||||||
onDrawerItem(withText(R.string.episodes_label)).perform(click());
|
onDrawerItem(withText(R.string.episodes_label)).perform(click());
|
||||||
|
|
||||||
final List<FeedItem> episodes = DBReader.getRecentlyPublishedEpisodes(0, 10, FeedItemFilter.unfiltered());
|
final List<FeedItem> episodes = DBReader.getRecentlyPublishedEpisodes(0, 10,
|
||||||
|
FeedItemFilter.unfiltered(), SortOrder.DATE_NEW_OLD);
|
||||||
Matcher<View> allEpisodesMatcher = allOf(withId(R.id.recyclerView), isDisplayed(), hasMinimumChildCount(2));
|
Matcher<View> allEpisodesMatcher = allOf(withId(R.id.recyclerView), isDisplayed(), hasMinimumChildCount(2));
|
||||||
onView(isRoot()).perform(waitForView(allEpisodesMatcher, 1000));
|
onView(isRoot()).perform(waitForView(allEpisodesMatcher, 1000));
|
||||||
onView(allEpisodesMatcher).perform(actionOnItemAtPosition(0, clickChildViewWithId(R.id.secondaryActionButton)));
|
onView(allEpisodesMatcher).perform(actionOnItemAtPosition(0, clickChildViewWithId(R.id.secondaryActionButton)));
|
||||||
|
@ -287,7 +289,8 @@ public class PlaybackTest {
|
||||||
uiTestUtils.addLocalFeedData(true);
|
uiTestUtils.addLocalFeedData(true);
|
||||||
DBWriter.clearQueue().get();
|
DBWriter.clearQueue().get();
|
||||||
activityTestRule.launchActivity(new Intent());
|
activityTestRule.launchActivity(new Intent());
|
||||||
final List<FeedItem> episodes = DBReader.getRecentlyPublishedEpisodes(0, 10, FeedItemFilter.unfiltered());
|
final List<FeedItem> episodes = DBReader.getRecentlyPublishedEpisodes(0, 10,
|
||||||
|
FeedItemFilter.unfiltered(), SortOrder.DATE_NEW_OLD);
|
||||||
|
|
||||||
startLocalPlayback();
|
startLocalPlayback();
|
||||||
FeedMedia media = episodes.get(0).getMedia();
|
FeedMedia media = episodes.get(0).getMedia();
|
||||||
|
|
|
@ -9,13 +9,17 @@ import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import de.danoeh.antennapod.R;
|
import de.danoeh.antennapod.R;
|
||||||
|
import de.danoeh.antennapod.core.storage.DBReader;
|
||||||
import de.danoeh.antennapod.dialog.AllEpisodesFilterDialog;
|
import de.danoeh.antennapod.dialog.AllEpisodesFilterDialog;
|
||||||
|
import de.danoeh.antennapod.model.feed.FeedItem;
|
||||||
import de.danoeh.antennapod.model.feed.FeedItemFilter;
|
import de.danoeh.antennapod.model.feed.FeedItemFilter;
|
||||||
|
import de.danoeh.antennapod.model.feed.SortOrder;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.greenrobot.eventbus.Subscribe;
|
import org.greenrobot.eventbus.Subscribe;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows all episodes (possibly filtered by user).
|
* Shows all episodes (possibly filtered by user).
|
||||||
|
@ -24,20 +28,55 @@ public class AllEpisodesFragment extends EpisodesListFragment {
|
||||||
public static final String TAG = "EpisodesFragment";
|
public static final String TAG = "EpisodesFragment";
|
||||||
private static final String PREF_NAME = "PrefAllEpisodesFragment";
|
private static final String PREF_NAME = "PrefAllEpisodesFragment";
|
||||||
private static final String PREF_FILTER = "filter";
|
private static final String PREF_FILTER = "filter";
|
||||||
|
public static final String PREF_SORT = "prefEpisodesSort";
|
||||||
|
private SharedPreferences prefs;
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
final View root = super.onCreateView(inflater, container, savedInstanceState);
|
final View root = super.onCreateView(inflater, container, savedInstanceState);
|
||||||
toolbar.inflateMenu(R.menu.episodes);
|
toolbar.inflateMenu(R.menu.episodes);
|
||||||
|
inflateSortMenu();
|
||||||
toolbar.setTitle(R.string.episodes_label);
|
toolbar.setTitle(R.string.episodes_label);
|
||||||
updateToolbar();
|
updateToolbar();
|
||||||
updateFilterUi();
|
updateFilterUi();
|
||||||
|
prefs = getActivity().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
|
||||||
txtvInformation.setOnClickListener(
|
txtvInformation.setOnClickListener(
|
||||||
v -> AllEpisodesFilterDialog.newInstance(getFilter()).show(getChildFragmentManager(), null));
|
v -> AllEpisodesFilterDialog.newInstance(getFilter()).show(getChildFragmentManager(), null));
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void inflateSortMenu() {
|
||||||
|
MenuItem sortItem = toolbar.getMenu().findItem(R.id.episodes_sort);
|
||||||
|
getActivity().getMenuInflater().inflate(R.menu.sort_menu, sortItem.getSubMenu());
|
||||||
|
|
||||||
|
// Remove the sorting options that are not needed in this fragment
|
||||||
|
toolbar.getMenu().findItem(R.id.sort_episode_title).setVisible(false);
|
||||||
|
toolbar.getMenu().findItem(R.id.sort_feed_title).setVisible(false);
|
||||||
|
toolbar.getMenu().findItem(R.id.sort_random).setVisible(false);
|
||||||
|
toolbar.getMenu().findItem(R.id.sort_smart_shuffle).setVisible(false);
|
||||||
|
toolbar.getMenu().findItem(R.id.keep_sorted).setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
protected List<FeedItem> loadData() {
|
||||||
|
return DBReader.getRecentlyPublishedEpisodes(0, page * EPISODES_PER_PAGE,
|
||||||
|
getFilter(), getSortOrder());
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
protected List<FeedItem> loadMoreData(int page) {
|
||||||
|
return DBReader.getRecentlyPublishedEpisodes((page - 1) * EPISODES_PER_PAGE,
|
||||||
|
EPISODES_PER_PAGE, getFilter(), getSortOrder());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int loadTotalItemCount() {
|
||||||
|
return DBReader.getTotalEpisodeCount(getFilter());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected FeedItemFilter getFilter() {
|
protected FeedItemFilter getFilter() {
|
||||||
SharedPreferences prefs = getActivity().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
|
SharedPreferences prefs = getActivity().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
|
||||||
|
@ -71,13 +110,23 @@ public class AllEpisodesFragment extends EpisodesListFragment {
|
||||||
}
|
}
|
||||||
onFilterChanged(new AllEpisodesFilterDialog.AllEpisodesFilterChangedEvent(new HashSet<>(filter)));
|
onFilterChanged(new AllEpisodesFilterDialog.AllEpisodesFilterChangedEvent(new HashSet<>(filter)));
|
||||||
return true;
|
return true;
|
||||||
|
} else {
|
||||||
|
SortOrder sortOrder = MenuItemToSortOrderConverter.convert(item);
|
||||||
|
if (sortOrder != null) {
|
||||||
|
saveSortOrderAndRefresh(sortOrder);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void saveSortOrderAndRefresh(SortOrder type) {
|
||||||
|
prefs.edit().putString(PREF_SORT, "" + type.code).apply();
|
||||||
|
loadItems();
|
||||||
|
}
|
||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
public void onFilterChanged(AllEpisodesFilterDialog.AllEpisodesFilterChangedEvent event) {
|
public void onFilterChanged(AllEpisodesFilterDialog.AllEpisodesFilterChangedEvent event) {
|
||||||
SharedPreferences prefs = getActivity().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
|
|
||||||
prefs.edit().putString(PREF_FILTER, StringUtils.join(event.filterValues, ",")).apply();
|
prefs.edit().putString(PREF_FILTER, StringUtils.join(event.filterValues, ",")).apply();
|
||||||
updateFilterUi();
|
updateFilterUi();
|
||||||
page = 1;
|
page = 1;
|
||||||
|
@ -96,4 +145,8 @@ public class AllEpisodesFragment extends EpisodesListFragment {
|
||||||
toolbar.getMenu().findItem(R.id.action_favorites).setIcon(
|
toolbar.getMenu().findItem(R.id.action_favorites).setIcon(
|
||||||
getFilter().showIsFavorite ? R.drawable.ic_star : R.drawable.ic_star_border);
|
getFilter().showIsFavorite ? R.drawable.ic_star : R.drawable.ic_star_border);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private SortOrder getSortOrder() {
|
||||||
|
return SortOrder.fromCodeString(prefs.getString(PREF_SORT, "" + SortOrder.DATE_NEW_OLD.code));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,6 @@ import de.danoeh.antennapod.core.event.DownloadEvent;
|
||||||
import de.danoeh.antennapod.core.event.DownloaderUpdate;
|
import de.danoeh.antennapod.core.event.DownloaderUpdate;
|
||||||
import de.danoeh.antennapod.core.menuhandler.MenuItemUtils;
|
import de.danoeh.antennapod.core.menuhandler.MenuItemUtils;
|
||||||
import de.danoeh.antennapod.core.service.download.DownloadService;
|
import de.danoeh.antennapod.core.service.download.DownloadService;
|
||||||
import de.danoeh.antennapod.core.storage.DBReader;
|
|
||||||
import de.danoeh.antennapod.core.util.FeedItemUtil;
|
import de.danoeh.antennapod.core.util.FeedItemUtil;
|
||||||
import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
|
import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
|
||||||
import de.danoeh.antennapod.event.FeedItemEvent;
|
import de.danoeh.antennapod.event.FeedItemEvent;
|
||||||
|
@ -442,18 +441,12 @@ public abstract class EpisodesListFragment extends Fragment
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
protected List<FeedItem> loadData() {
|
protected abstract List<FeedItem> loadData();
|
||||||
return DBReader.getRecentlyPublishedEpisodes(0, page * EPISODES_PER_PAGE, getFilter());
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
protected List<FeedItem> loadMoreData(int page) {
|
protected abstract List<FeedItem> loadMoreData(int page);
|
||||||
return DBReader.getRecentlyPublishedEpisodes((page - 1) * EPISODES_PER_PAGE, EPISODES_PER_PAGE, getFilter());
|
|
||||||
}
|
|
||||||
|
|
||||||
protected int loadTotalItemCount() {
|
protected abstract int loadTotalItemCount();
|
||||||
return DBReader.getTotalEpisodeCount(getFilter());
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract FeedItemFilter getFilter();
|
protected abstract FeedItemFilter getFilter();
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
android:icon="@drawable/ic_filter"
|
android:icon="@drawable/ic_filter"
|
||||||
android:menuCategory="container"
|
android:menuCategory="container"
|
||||||
android:title="@string/filter"
|
android:title="@string/filter"
|
||||||
custom:showAsAction="always"/>
|
custom:showAsAction="ifRoom"/>
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_favorites"
|
android:id="@+id/action_favorites"
|
||||||
|
@ -30,4 +30,11 @@
|
||||||
android:title="@string/favorite_episodes_label"
|
android:title="@string/favorite_episodes_label"
|
||||||
custom:showAsAction="always"/>
|
custom:showAsAction="always"/>
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/episodes_sort"
|
||||||
|
android:title="@string/sort"
|
||||||
|
custom:showAsAction="never">
|
||||||
|
<menu />
|
||||||
|
</item>
|
||||||
|
|
||||||
</menu>
|
</menu>
|
||||||
|
|
|
@ -18,6 +18,7 @@ import de.danoeh.antennapod.core.export.ExportWriter;
|
||||||
import de.danoeh.antennapod.model.feed.Feed;
|
import de.danoeh.antennapod.model.feed.Feed;
|
||||||
import de.danoeh.antennapod.model.feed.FeedItem;
|
import de.danoeh.antennapod.model.feed.FeedItem;
|
||||||
import de.danoeh.antennapod.core.storage.DBReader;
|
import de.danoeh.antennapod.core.storage.DBReader;
|
||||||
|
import de.danoeh.antennapod.model.feed.SortOrder;
|
||||||
|
|
||||||
/** Writes saved favorites to file. */
|
/** Writes saved favorites to file. */
|
||||||
public class FavoritesWriter implements ExportWriter {
|
public class FavoritesWriter implements ExportWriter {
|
||||||
|
@ -43,7 +44,7 @@ public class FavoritesWriter implements ExportWriter {
|
||||||
String feedTemplate = IOUtils.toString(feedTemplateStream, UTF_8);
|
String feedTemplate = IOUtils.toString(feedTemplateStream, UTF_8);
|
||||||
|
|
||||||
List<FeedItem> allFavorites = DBReader.getRecentlyPublishedEpisodes(0, Integer.MAX_VALUE,
|
List<FeedItem> allFavorites = DBReader.getRecentlyPublishedEpisodes(0, Integer.MAX_VALUE,
|
||||||
new FeedItemFilter(FeedItemFilter.IS_FAVORITE));
|
new FeedItemFilter(FeedItemFilter.IS_FAVORITE), SortOrder.DATE_NEW_OLD);
|
||||||
Map<Long, List<FeedItem>> favoriteByFeed = getFeedMap(allFavorites);
|
Map<Long, List<FeedItem>> favoriteByFeed = getFeedMap(allFavorites);
|
||||||
|
|
||||||
writer.append(templateParts[0]);
|
writer.append(templateParts[0]);
|
||||||
|
|
|
@ -52,6 +52,7 @@ import de.danoeh.antennapod.event.playback.PlaybackServiceEvent;
|
||||||
import de.danoeh.antennapod.event.PlayerErrorEvent;
|
import de.danoeh.antennapod.event.PlayerErrorEvent;
|
||||||
import de.danoeh.antennapod.event.playback.SleepTimerUpdatedEvent;
|
import de.danoeh.antennapod.event.playback.SleepTimerUpdatedEvent;
|
||||||
import de.danoeh.antennapod.model.feed.FeedItemFilter;
|
import de.danoeh.antennapod.model.feed.FeedItemFilter;
|
||||||
|
import de.danoeh.antennapod.model.feed.SortOrder;
|
||||||
import de.danoeh.antennapod.playback.base.PlaybackServiceMediaPlayer;
|
import de.danoeh.antennapod.playback.base.PlaybackServiceMediaPlayer;
|
||||||
import de.danoeh.antennapod.playback.base.PlayerStatus;
|
import de.danoeh.antennapod.playback.base.PlayerStatus;
|
||||||
import de.danoeh.antennapod.playback.cast.CastPsmp;
|
import de.danoeh.antennapod.playback.cast.CastPsmp;
|
||||||
|
@ -417,7 +418,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
|
||||||
} else if (parentId.equals(getResources().getString(R.string.episodes_label))) {
|
} else if (parentId.equals(getResources().getString(R.string.episodes_label))) {
|
||||||
feedItems = DBReader.getRecentlyPublishedEpisodes(0,
|
feedItems = DBReader.getRecentlyPublishedEpisodes(0,
|
||||||
MAX_ANDROID_AUTO_EPISODES_PER_FEED,
|
MAX_ANDROID_AUTO_EPISODES_PER_FEED,
|
||||||
new FeedItemFilter(FeedItemFilter.UNPLAYED));
|
new FeedItemFilter(FeedItemFilter.UNPLAYED), SortOrder.DATE_NEW_OLD);
|
||||||
} else if (parentId.startsWith("FeedId:")) {
|
} else if (parentId.startsWith("FeedId:")) {
|
||||||
long feedId = Long.parseLong(parentId.split(":")[1]);
|
long feedId = Long.parseLong(parentId.split(":")[1]);
|
||||||
feedItems = DBReader.getFeedItemList(DBReader.getFeed(feedId));
|
feedItems = DBReader.getFeedItemList(DBReader.getFeed(feedId));
|
||||||
|
|
|
@ -366,12 +366,12 @@ public final class DBReader {
|
||||||
* @param filter The filter describing which episodes to filter out.
|
* @param filter The filter describing which episodes to filter out.
|
||||||
*/
|
*/
|
||||||
@NonNull
|
@NonNull
|
||||||
public static List<FeedItem> getRecentlyPublishedEpisodes(int offset, int limit, FeedItemFilter filter) {
|
public static List<FeedItem> getRecentlyPublishedEpisodes(int offset, int limit,
|
||||||
|
FeedItemFilter filter, SortOrder sortOrder) {
|
||||||
Log.d(TAG, "getRecentlyPublishedEpisodes() called with: offset=" + offset + ", limit=" + limit);
|
Log.d(TAG, "getRecentlyPublishedEpisodes() called with: offset=" + offset + ", limit=" + limit);
|
||||||
|
|
||||||
PodDBAdapter adapter = PodDBAdapter.getInstance();
|
PodDBAdapter adapter = PodDBAdapter.getInstance();
|
||||||
adapter.open();
|
adapter.open();
|
||||||
try (Cursor cursor = adapter.getRecentlyPublishedItemsCursor(offset, limit, filter)) {
|
try (Cursor cursor = adapter.getRecentlyPublishedItemsCursor(offset, limit, filter, sortOrder)) {
|
||||||
List<FeedItem> items = extractItemlistFromCursor(adapter, cursor);
|
List<FeedItem> items = extractItemlistFromCursor(adapter, cursor);
|
||||||
loadAdditionalFeedItemListData(items);
|
loadAdditionalFeedItemListData(items);
|
||||||
return items;
|
return items;
|
||||||
|
|
|
@ -45,6 +45,8 @@ import org.apache.commons.io.FileUtils;
|
||||||
import static de.danoeh.antennapod.model.feed.FeedPreferences.SPEED_USE_GLOBAL;
|
import static de.danoeh.antennapod.model.feed.FeedPreferences.SPEED_USE_GLOBAL;
|
||||||
import static de.danoeh.antennapod.model.feed.SortOrder.toCodeString;
|
import static de.danoeh.antennapod.model.feed.SortOrder.toCodeString;
|
||||||
|
|
||||||
|
import de.danoeh.antennapod.storage.database.mapper.FeedItemSortQuery;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements methods for accessing the database
|
* Implements methods for accessing the database
|
||||||
*/
|
*/
|
||||||
|
@ -1059,11 +1061,13 @@ public class PodDBAdapter {
|
||||||
return db.rawQuery(query, null);
|
return db.rawQuery(query, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final Cursor getRecentlyPublishedItemsCursor(int offset, int limit, FeedItemFilter filter) {
|
public final Cursor getRecentlyPublishedItemsCursor(int offset, int limit,
|
||||||
|
FeedItemFilter filter, SortOrder sortOrder) {
|
||||||
|
String orderByQuery = FeedItemSortQuery.generateFrom(sortOrder);
|
||||||
String filterQuery = FeedItemFilterQuery.generateFrom(filter);
|
String filterQuery = FeedItemFilterQuery.generateFrom(filter);
|
||||||
String whereClause = "".equals(filterQuery) ? "" : " WHERE " + filterQuery;
|
String whereClause = "".equals(filterQuery) ? "" : " WHERE " + filterQuery;
|
||||||
final String query = SELECT_FEED_ITEMS_AND_MEDIA + whereClause
|
final String query = SELECT_FEED_ITEMS_AND_MEDIA + whereClause
|
||||||
+ " ORDER BY " + KEY_PUBDATE + " DESC LIMIT " + offset + ", " + limit;
|
+ "ORDER BY " + orderByQuery + " LIMIT " + offset + ", " + limit;
|
||||||
return db.rawQuery(query, null);
|
return db.rawQuery(query, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue