Add sort option to episodes screen (#6286)

This commit is contained in:
GitStart 2023-02-22 20:04:04 +01:00 committed by GitHub
parent 50eb1e9cf9
commit 25ddd73f24
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 83 additions and 21 deletions

View File

@ -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();

View File

@ -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));
}
} }

View File

@ -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();

View File

@ -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>

View File

@ -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]);

View File

@ -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));

View File

@ -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;

View File

@ -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);
} }