From f3bca9d9e40f2a30a8d579452a78c048cd1d49bd Mon Sep 17 00:00:00 2001 From: ByteHamster Date: Sat, 13 Apr 2024 19:18:13 +0200 Subject: [PATCH] Add lazy loading to feed item list (#7091) --- .../screen/download/DownloadLogAdapter.java | 2 +- .../download/DownloadLogDetailsDialog.java | 2 +- .../ui/screen/feed/FeedInfoFragment.java | 2 +- .../ui/screen/feed/FeedItemlistFragment.java | 116 +++++++++--------- .../ui/screen/feed/SingleFeedSortDialog.java | 48 ++++++++ .../preferences/FeedSettingsFragment.java | 4 +- .../OnlineFeedViewActivity.java | 2 +- .../service/feed/FeedUpdateWorker.java | 2 +- .../episode/autodownload/DbReaderTest.java | 2 +- .../episode/autodownload/DbTasksTest.java | 8 +- .../episode/autodownload/DbWriterTest.java | 2 +- .../feed/local/LocalFeedUpdaterTest.java | 6 +- .../playback/service/PlaybackService.java | 8 +- .../antennapod/storage/database/DBReader.java | 40 +++--- .../antennapod/storage/database/DBWriter.java | 2 +- .../storage/database/FeedDatabaseWriter.java | 5 +- .../storage/database/PodDBAdapter.java | 15 ++- .../feed/FeedStatisticsFragment.java | 2 +- 18 files changed, 167 insertions(+), 101 deletions(-) create mode 100644 app/src/main/java/de/danoeh/antennapod/ui/screen/feed/SingleFeedSortDialog.java diff --git a/app/src/main/java/de/danoeh/antennapod/ui/screen/download/DownloadLogAdapter.java b/app/src/main/java/de/danoeh/antennapod/ui/screen/download/DownloadLogAdapter.java index 88472bcbf..b7cbd818f 100644 --- a/app/src/main/java/de/danoeh/antennapod/ui/screen/download/DownloadLogAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/ui/screen/download/DownloadLogAdapter.java @@ -98,7 +98,7 @@ public class DownloadLogAdapter extends BaseAdapter { if (status.getFeedfileType() == Feed.FEEDFILETYPE_FEED) { holder.secondaryActionButton.setOnClickListener(v -> { holder.secondaryActionButton.setVisibility(View.INVISIBLE); - Feed feed = DBReader.getFeed(status.getFeedfileId()); + Feed feed = DBReader.getFeed(status.getFeedfileId(), false, 0, 0); if (feed == null) { Log.e(TAG, "Could not find feed for feed id: " + status.getFeedfileId()); return; diff --git a/app/src/main/java/de/danoeh/antennapod/ui/screen/download/DownloadLogDetailsDialog.java b/app/src/main/java/de/danoeh/antennapod/ui/screen/download/DownloadLogDetailsDialog.java index a035f58ff..3c58dece2 100644 --- a/app/src/main/java/de/danoeh/antennapod/ui/screen/download/DownloadLogDetailsDialog.java +++ b/app/src/main/java/de/danoeh/antennapod/ui/screen/download/DownloadLogDetailsDialog.java @@ -28,7 +28,7 @@ public class DownloadLogDetailsDialog extends MaterialAlertDialogBuilder { url = media.getDownloadUrl(); } } else if (status.getFeedfileType() == Feed.FEEDFILETYPE_FEED) { - Feed feed = DBReader.getFeed(status.getFeedfileId()); + Feed feed = DBReader.getFeed(status.getFeedfileId(), false, 0, 0); if (feed != null) { url = feed.getDownloadUrl(); } diff --git a/app/src/main/java/de/danoeh/antennapod/ui/screen/feed/FeedInfoFragment.java b/app/src/main/java/de/danoeh/antennapod/ui/screen/feed/FeedInfoFragment.java index 929a68fff..f46f685f9 100644 --- a/app/src/main/java/de/danoeh/antennapod/ui/screen/feed/FeedInfoFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/ui/screen/feed/FeedInfoFragment.java @@ -168,7 +168,7 @@ public class FeedInfoFragment extends Fragment implements MaterialToolbar.OnMenu public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { long feedId = getArguments().getLong(EXTRA_FEED_ID); disposable = Maybe.create((MaybeOnSubscribe) emitter -> { - Feed feed = DBReader.getFeed(feedId); + Feed feed = DBReader.getFeed(feedId, false, 0, 0); if (feed != null) { emitter.onSuccess(feed); } else { diff --git a/app/src/main/java/de/danoeh/antennapod/ui/screen/feed/FeedItemlistFragment.java b/app/src/main/java/de/danoeh/antennapod/ui/screen/feed/FeedItemlistFragment.java index b4a2556d1..94c6f7944 100644 --- a/app/src/main/java/de/danoeh/antennapod/ui/screen/feed/FeedItemlistFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/ui/screen/feed/FeedItemlistFragment.java @@ -17,6 +17,7 @@ import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.content.res.AppCompatResources; +import androidx.core.util.Pair; import androidx.fragment.app.Fragment; import androidx.recyclerview.widget.RecyclerView; @@ -49,7 +50,6 @@ import de.danoeh.antennapod.event.FeedEvent; import de.danoeh.antennapod.ui.MenuItemUtils; import de.danoeh.antennapod.storage.database.DBReader; import de.danoeh.antennapod.storage.database.DBWriter; -import de.danoeh.antennapod.storage.database.FeedItemPermutors; import de.danoeh.antennapod.ui.common.IntentUtils; import de.danoeh.antennapod.ui.share.ShareUtils; import de.danoeh.antennapod.ui.episodeslist.MoreContentListFooterUtil; @@ -73,7 +73,6 @@ import de.danoeh.antennapod.model.download.DownloadResult; import de.danoeh.antennapod.model.feed.Feed; import de.danoeh.antennapod.model.feed.FeedItem; import de.danoeh.antennapod.model.feed.FeedItemFilter; -import de.danoeh.antennapod.model.feed.SortOrder; import de.danoeh.antennapod.storage.preferences.UserPreferences; import de.danoeh.antennapod.ui.glide.FastBlurTransformation; import de.danoeh.antennapod.ui.episodeslist.EpisodeItemViewHolder; @@ -91,6 +90,10 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem public static final String TAG = "ItemlistFragment"; private static final String ARGUMENT_FEED_ID = "argument.de.danoeh.antennapod.feed_id"; private static final String KEY_UP_ARROW = "up_arrow"; + protected static final int EPISODES_PER_PAGE = 150; + protected int page = 1; + protected boolean isLoadingMore = false; + protected boolean hasMoreItems = false; private FeedItemListAdapter adapter; private SwipeActions swipeActions; @@ -147,6 +150,7 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem } ((MainActivity) getActivity()).setupToolbarToggle(viewBinding.toolbar, displayUpArrow); updateToolbar(); + setupLoadMoreScrollListener(); viewBinding.recyclerView.setRecycledViewPool(((MainActivity) getActivity()).getRecycledViewPool()); adapter = new FeedItemListAdapter((MainActivity) getActivity()); @@ -317,6 +321,21 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem return FeedItemMenuHandler.onMenuItemClicked(this, item.getItemId(), selectedItem); } + private void setupLoadMoreScrollListener() { + viewBinding.recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { + @Override + public void onScrolled(@NonNull RecyclerView view, int deltaX, int deltaY) { + super.onScrolled(view, deltaX, deltaY); + if (!isLoadingMore && hasMoreItems && viewBinding.recyclerView.isScrolledToBottom()) { + /* The end of the list has been reached. Load more data. */ + page++; + loadMoreItems(); + isLoadingMore = true; + } + } + }); + } + @Override public void onItemClick(AdapterView parent, View view, int position, long id) { MainActivity activity = (MainActivity) getActivity(); @@ -535,17 +554,23 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem if (disposable != null) { disposable.dispose(); } - disposable = Observable.fromCallable(this::loadData) + disposable = Observable.fromCallable( + () -> { + feed = DBReader.getFeed(feedID, true, 0, page * EPISODES_PER_PAGE); + int count = DBReader.getFeedEpisodeCount(feed.getId(), feed.getItemFilter()); + return new Pair<>(feed, count); + }) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( result -> { - feed = result; + hasMoreItems = !(page == 1 && feed.getItems().size() < EPISODES_PER_PAGE); swipeActions.setFilter(feed.getItemFilter()); refreshHeaderView(); viewBinding.progressBar.setVisibility(View.GONE); adapter.setDummyViews(0); adapter.updateItems(feed.getItems()); + adapter.setTotalNumberOfItems(result.second); updateToolbar(); }, error -> { feed = null; @@ -557,19 +582,37 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem }); } - @Nullable - private Feed loadData() { - Feed feed = DBReader.getFeed(feedID, true); - if (feed == null) { - return null; + private void loadMoreItems() { + if (disposable != null) { + disposable.dispose(); } - DBReader.loadAdditionalFeedItemListData(feed.getItems()); - if (feed.getSortOrder() != null) { - List feedItems = feed.getItems(); - FeedItemPermutors.getPermutor(feed.getSortOrder()).reorder(feedItems); - feed.setItems(feedItems); - } - return feed; + isLoadingMore = true; + adapter.setDummyViews(1); + adapter.notifyItemInserted(adapter.getItemCount() - 1); + disposable = Observable.fromCallable(() -> DBReader.getFeed(feedID, true, + (page - 1) * EPISODES_PER_PAGE, EPISODES_PER_PAGE)) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + data -> { + if (data.getItems().size() < EPISODES_PER_PAGE) { + hasMoreItems = false; + } + feed.getItems().addAll(data.getItems()); + adapter.setDummyViews(0); + adapter.updateItems(feed.getItems()); + if (adapter.shouldSelectLazyLoadedItems()) { + adapter.setSelected(feed.getItems().size() - data.getItems().size(), + feed.getItems().size(), true); + } + }, error -> { + adapter.setDummyViews(0); + adapter.updateItems(Collections.emptyList()); + Log.e(TAG, Log.getStackTraceString(error)); + }, () -> { + // Make sure to not always load 2 pages at once + viewBinding.recyclerView.post(() -> isLoadingMore = false); + }); } @Subscribe(threadMode = ThreadMode.MAIN) @@ -608,45 +651,4 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem MenuItemUtils.setOnClickListeners(menu, FeedItemlistFragment.this::onContextItemSelected); } } - - public static class SingleFeedSortDialog extends ItemSortDialog { - private static final String ARG_FEED_ID = "feedId"; - private static final String ARG_FEED_IS_LOCAL = "isLocal"; - private static final String ARG_SORT_ORDER = "sortOrder"; - - private static SingleFeedSortDialog newInstance(Feed feed) { - Bundle bundle = new Bundle(); - bundle.putLong(ARG_FEED_ID, feed.getId()); - bundle.putBoolean(ARG_FEED_IS_LOCAL, feed.isLocalFeed()); - if (feed.getSortOrder() == null) { - bundle.putString(ARG_SORT_ORDER, String.valueOf(SortOrder.DATE_NEW_OLD.code)); - } else { - bundle.putString(ARG_SORT_ORDER, String.valueOf(feed.getSortOrder().code)); - } - SingleFeedSortDialog dialog = new SingleFeedSortDialog(); - dialog.setArguments(bundle); - return dialog; - } - - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - sortOrder = SortOrder.fromCodeString(getArguments().getString(ARG_SORT_ORDER)); - } - - @Override - protected void onAddItem(int title, SortOrder ascending, SortOrder descending, boolean ascendingIsDefault) { - if (ascending == SortOrder.DATE_OLD_NEW || ascending == SortOrder.DURATION_SHORT_LONG - || ascending == SortOrder.EPISODE_TITLE_A_Z - || (getArguments().getBoolean(ARG_FEED_IS_LOCAL) && ascending == SortOrder.EPISODE_FILENAME_A_Z)) { - super.onAddItem(title, ascending, descending, ascendingIsDefault); - } - } - - @Override - protected void onSelectionChanged() { - super.onSelectionChanged(); - DBWriter.setFeedItemSortOrder(getArguments().getLong(ARG_FEED_ID), sortOrder); - } - } } diff --git a/app/src/main/java/de/danoeh/antennapod/ui/screen/feed/SingleFeedSortDialog.java b/app/src/main/java/de/danoeh/antennapod/ui/screen/feed/SingleFeedSortDialog.java new file mode 100644 index 000000000..d9766bbfa --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/ui/screen/feed/SingleFeedSortDialog.java @@ -0,0 +1,48 @@ +package de.danoeh.antennapod.ui.screen.feed; + +import android.os.Bundle; +import androidx.annotation.Nullable; +import de.danoeh.antennapod.model.feed.Feed; +import de.danoeh.antennapod.model.feed.SortOrder; +import de.danoeh.antennapod.storage.database.DBWriter; + +public class SingleFeedSortDialog extends ItemSortDialog { + private static final String ARG_FEED_ID = "feedId"; + private static final String ARG_FEED_IS_LOCAL = "isLocal"; + private static final String ARG_SORT_ORDER = "sortOrder"; + + public static SingleFeedSortDialog newInstance(Feed feed) { + Bundle bundle = new Bundle(); + bundle.putLong(ARG_FEED_ID, feed.getId()); + bundle.putBoolean(ARG_FEED_IS_LOCAL, feed.isLocalFeed()); + if (feed.getSortOrder() == null) { + bundle.putString(ARG_SORT_ORDER, String.valueOf(SortOrder.DATE_NEW_OLD.code)); + } else { + bundle.putString(ARG_SORT_ORDER, String.valueOf(feed.getSortOrder().code)); + } + SingleFeedSortDialog dialog = new SingleFeedSortDialog(); + dialog.setArguments(bundle); + return dialog; + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + sortOrder = SortOrder.fromCodeString(getArguments().getString(ARG_SORT_ORDER)); + } + + @Override + protected void onAddItem(int title, SortOrder ascending, SortOrder descending, boolean ascendingIsDefault) { + if (ascending == SortOrder.DATE_OLD_NEW || ascending == SortOrder.DURATION_SHORT_LONG + || ascending == SortOrder.EPISODE_TITLE_A_Z + || (getArguments().getBoolean(ARG_FEED_IS_LOCAL) && ascending == SortOrder.EPISODE_FILENAME_A_Z)) { + super.onAddItem(title, ascending, descending, ascendingIsDefault); + } + } + + @Override + protected void onSelectionChanged() { + super.onSelectionChanged(); + DBWriter.setFeedItemSortOrder(getArguments().getLong(ARG_FEED_ID), sortOrder); + } +} diff --git a/app/src/main/java/de/danoeh/antennapod/ui/screen/feed/preferences/FeedSettingsFragment.java b/app/src/main/java/de/danoeh/antennapod/ui/screen/feed/preferences/FeedSettingsFragment.java index 4ac844479..4438f80cc 100644 --- a/app/src/main/java/de/danoeh/antennapod/ui/screen/feed/preferences/FeedSettingsFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/ui/screen/feed/preferences/FeedSettingsFragment.java @@ -81,7 +81,7 @@ public class FeedSettingsFragment extends Fragment { .commitAllowingStateLoss(); disposable = Maybe.create((MaybeOnSubscribe) emitter -> { - Feed feed = DBReader.getFeed(feedId); + Feed feed = DBReader.getFeed(feedId, false, 0, 0); if (feed != null) { emitter.onSuccess(feed); } else { @@ -163,7 +163,7 @@ public class FeedSettingsFragment extends Fragment { long feedId = getArguments().getLong(EXTRA_FEED_ID); disposable = Maybe.create((MaybeOnSubscribe) emitter -> { - Feed feed = DBReader.getFeed(feedId); + Feed feed = DBReader.getFeed(feedId, false, 0, 0); if (feed != null) { emitter.onSuccess(feed); } else { diff --git a/app/src/main/java/de/danoeh/antennapod/ui/screen/onlinefeedview/OnlineFeedViewActivity.java b/app/src/main/java/de/danoeh/antennapod/ui/screen/onlinefeedview/OnlineFeedViewActivity.java index 799366ecb..7b8558c3d 100644 --- a/app/src/main/java/de/danoeh/antennapod/ui/screen/onlinefeedview/OnlineFeedViewActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/ui/screen/onlinefeedview/OnlineFeedViewActivity.java @@ -526,7 +526,7 @@ public class OnlineFeedViewActivity extends AppCompatActivity { if (didPressSubscribe) { didPressSubscribe = false; - Feed feed1 = DBReader.getFeed(getFeedId()); + Feed feed1 = DBReader.getFeed(getFeedId(), false, 0, 0); FeedPreferences feedPreferences = feed1.getPreferences(); if (UserPreferences.isEnableAutodownload()) { boolean autoDownload = viewBinding.autoDownloadCheckBox.isChecked(); diff --git a/net/download/service/src/main/java/de/danoeh/antennapod/net/download/service/feed/FeedUpdateWorker.java b/net/download/service/src/main/java/de/danoeh/antennapod/net/download/service/feed/FeedUpdateWorker.java index c924fa3fd..c835f08c6 100644 --- a/net/download/service/src/main/java/de/danoeh/antennapod/net/download/service/feed/FeedUpdateWorker.java +++ b/net/download/service/src/main/java/de/danoeh/antennapod/net/download/service/feed/FeedUpdateWorker.java @@ -76,7 +76,7 @@ public class FeedUpdateWorker extends Worker { } Collections.shuffle(toUpdate); // If the worker gets cancelled early, every feed has a chance to be updated } else { - Feed feed = DBReader.getFeed(feedId); + Feed feed = DBReader.getFeed(feedId, false, 0, Integer.MAX_VALUE); if (feed == null) { return Result.success(); } diff --git a/net/download/service/src/test/java/de/danoeh/antennapod/net/download/service/episode/autodownload/DbReaderTest.java b/net/download/service/src/test/java/de/danoeh/antennapod/net/download/service/episode/autodownload/DbReaderTest.java index 0f70f3617..2776696ea 100644 --- a/net/download/service/src/test/java/de/danoeh/antennapod/net/download/service/episode/autodownload/DbReaderTest.java +++ b/net/download/service/src/test/java/de/danoeh/antennapod/net/download/service/episode/autodownload/DbReaderTest.java @@ -154,7 +154,7 @@ public class DbReaderTest { List items = feed.getItems(); feed.setItems(null); List savedItems = DBReader.getFeedItemList(feed, - FeedItemFilter.unfiltered(), SortOrder.DATE_NEW_OLD); + FeedItemFilter.unfiltered(), SortOrder.DATE_NEW_OLD, 0, Integer.MAX_VALUE); assertNotNull(savedItems); assertEquals(items.size(), savedItems.size()); for (int i = 0; i < savedItems.size(); i++) { diff --git a/net/download/service/src/test/java/de/danoeh/antennapod/net/download/service/episode/autodownload/DbTasksTest.java b/net/download/service/src/test/java/de/danoeh/antennapod/net/download/service/episode/autodownload/DbTasksTest.java index 776319acf..e5e41c64c 100644 --- a/net/download/service/src/test/java/de/danoeh/antennapod/net/download/service/episode/autodownload/DbTasksTest.java +++ b/net/download/service/src/test/java/de/danoeh/antennapod/net/download/service/episode/autodownload/DbTasksTest.java @@ -133,7 +133,7 @@ public class DbTasksTest { updatedFeedTest(newFeed, feedID, itemIDs, numItemsOld, numItemsNew); - final Feed feedFromDB = DBReader.getFeed(newFeed.getId()); + final Feed feedFromDB = DBReader.getFeed(newFeed.getId(), false, 0, Integer.MAX_VALUE); assertNotNull(feedFromDB); assertEquals(newFeed.getId(), feedFromDB.getId()); updatedFeedTest(feedFromDB, feedID, itemIDs, numItemsOld, numItemsNew); @@ -163,7 +163,7 @@ public class DbTasksTest { final Feed newFeed = FeedDatabaseWriter.updateFeed(context, feed, false); assertNotSame(newFeed, feed); - final Feed feedFromDB = DBReader.getFeed(newFeed.getId()); + final Feed feedFromDB = DBReader.getFeed(newFeed.getId(), false, 0, Integer.MAX_VALUE); final FeedItem feedItemFromDB = feedFromDB.getItems().get(0); assertTrue(feedItemFromDB.isNew()); } @@ -186,7 +186,7 @@ public class DbTasksTest { Feed newFeed = FeedDatabaseWriter.updateFeed(context, feed, true); assertEquals(8, newFeed.getItems().size()); // 10 - 2 = 8 items - Feed feedFromDB = DBReader.getFeed(newFeed.getId()); + Feed feedFromDB = DBReader.getFeed(newFeed.getId(), false, 0, Integer.MAX_VALUE); assertEquals(8, feedFromDB.getItems().size()); // 10 - 2 = 8 items } @@ -213,7 +213,7 @@ public class DbTasksTest { Feed newFeed = FeedDatabaseWriter.updateFeed(context, feed, false); assertEquals(10, newFeed.getItems().size()); // id 1-duplicate replaces because the stream url is the same - Feed feedFromDB = DBReader.getFeed(newFeed.getId()); + Feed feedFromDB = DBReader.getFeed(newFeed.getId(), false, 0, Integer.MAX_VALUE); assertEquals(10, feedFromDB.getItems().size()); // id1-duplicate should override id 1 FeedItem updatedItem = feedFromDB.getItemAtIndex(9); diff --git a/net/download/service/src/test/java/de/danoeh/antennapod/net/download/service/episode/autodownload/DbWriterTest.java b/net/download/service/src/test/java/de/danoeh/antennapod/net/download/service/episode/autodownload/DbWriterTest.java index fa96759c1..287c29913 100644 --- a/net/download/service/src/test/java/de/danoeh/antennapod/net/download/service/episode/autodownload/DbWriterTest.java +++ b/net/download/service/src/test/java/de/danoeh/antennapod/net/download/service/episode/autodownload/DbWriterTest.java @@ -771,7 +771,7 @@ public class DbWriterTest { DBWriter.removeAllNewFlags().get(); List loadedItems = DBReader.getFeedItemList(feed, - FeedItemFilter.unfiltered(), SortOrder.DATE_NEW_OLD); + FeedItemFilter.unfiltered(), SortOrder.DATE_NEW_OLD, 0, Integer.MAX_VALUE); for (FeedItem item : loadedItems) { assertFalse(item.isNew()); } diff --git a/net/download/service/src/test/java/de/danoeh/antennapod/net/download/service/feed/local/LocalFeedUpdaterTest.java b/net/download/service/src/test/java/de/danoeh/antennapod/net/download/service/feed/local/LocalFeedUpdaterTest.java index 4fb686ca5..fa975f3fb 100644 --- a/net/download/service/src/test/java/de/danoeh/antennapod/net/download/service/feed/local/LocalFeedUpdaterTest.java +++ b/net/download/service/src/test/java/de/danoeh/antennapod/net/download/service/feed/local/LocalFeedUpdaterTest.java @@ -169,7 +169,8 @@ public class LocalFeedUpdaterTest { callUpdateFeed(LOCAL_FEED_DIR1); Feed feed = verifySingleFeedInDatabase(); - List feedItems = DBReader.getFeedItemList(feed, FeedItemFilter.unfiltered(), SortOrder.DATE_NEW_OLD); + List feedItems = DBReader.getFeedItemList(feed, FeedItemFilter.unfiltered(), + SortOrder.DATE_NEW_OLD, 0, Integer.MAX_VALUE); assertEquals("track1.mp3", feedItems.get(0).getTitle()); } @@ -283,7 +284,8 @@ public class LocalFeedUpdaterTest { */ private static void verifySingleFeedInDatabaseAndItemCount(int expectedItemCount) { Feed feed = verifySingleFeedInDatabase(); - List feedItems = DBReader.getFeedItemList(feed, FeedItemFilter.unfiltered(), SortOrder.DATE_NEW_OLD); + List feedItems = DBReader.getFeedItemList(feed, FeedItemFilter.unfiltered(), + SortOrder.DATE_NEW_OLD, 0, Integer.MAX_VALUE); assertEquals(expectedItemCount, feedItems.size()); } diff --git a/playback/service/src/main/java/de/danoeh/antennapod/playback/service/PlaybackService.java b/playback/service/src/main/java/de/danoeh/antennapod/playback/service/PlaybackService.java index 7f213a598..39ee4c3ad 100644 --- a/playback/service/src/main/java/de/danoeh/antennapod/playback/service/PlaybackService.java +++ b/playback/service/src/main/java/de/danoeh/antennapod/playback/service/PlaybackService.java @@ -94,7 +94,6 @@ import de.danoeh.antennapod.model.feed.FeedItem; import de.danoeh.antennapod.model.feed.FeedItemFilter; import de.danoeh.antennapod.model.feed.FeedMedia; import de.danoeh.antennapod.model.feed.FeedPreferences; -import de.danoeh.antennapod.model.feed.SortOrder; import de.danoeh.antennapod.model.playback.MediaType; import de.danoeh.antennapod.model.playback.Playable; import de.danoeh.antennapod.playback.base.PlaybackServiceMediaPlayer; @@ -452,12 +451,7 @@ public class PlaybackService extends MediaBrowserServiceCompat { new FeedItemFilter(FeedItemFilter.UNPLAYED), UserPreferences.getAllEpisodesSortOrder()); } else if (parentId.startsWith("FeedId:")) { long feedId = Long.parseLong(parentId.split(":")[1]); - Feed feed = DBReader.getFeed(feedId); - SortOrder sortOrder = feed.getSortOrder(); - if (sortOrder == null) { - sortOrder = SortOrder.DATE_NEW_OLD; - } - feedItems = DBReader.getFeedItemList(feed, FeedItemFilter.unfiltered(), sortOrder); + feedItems = DBReader.getFeed(feedId, true, 0, MAX_ANDROID_AUTO_EPISODES_PER_FEED).getItems(); } else if (parentId.equals(getString(R.string.current_playing_episode))) { FeedMedia playable = DBReader.getFeedMedia(PlaybackPreferences.getCurrentlyPlayingFeedMediaId()); if (playable != null) { diff --git a/storage/database/src/main/java/de/danoeh/antennapod/storage/database/DBReader.java b/storage/database/src/main/java/de/danoeh/antennapod/storage/database/DBReader.java index a6669601e..432990379 100644 --- a/storage/database/src/main/java/de/danoeh/antennapod/storage/database/DBReader.java +++ b/storage/database/src/main/java/de/danoeh/antennapod/storage/database/DBReader.java @@ -149,12 +149,14 @@ public final class DBReader { * @param feed The Feed whose items should be loaded * @return A list with the FeedItems of the Feed. The Feed-attribute of the FeedItems will already be set correctly. */ - public static List getFeedItemList(final Feed feed, final FeedItemFilter filter, SortOrder sortOrder) { + public static List getFeedItemList(final Feed feed, final FeedItemFilter filter, SortOrder sortOrder, + int offset, int limit) { Log.d(TAG, "getFeedItemList() called with: " + "feed = [" + feed + "]"); PodDBAdapter adapter = PodDBAdapter.getInstance(); adapter.open(); - try (FeedItemCursor cursor = new FeedItemCursor(adapter.getItemsOfFeedCursor(feed, filter, sortOrder))) { + try (FeedItemCursor cursor = new FeedItemCursor(adapter.getItemsOfFeedCursor( + feed, filter, sortOrder, offset, limit))) { List items = extractItemlistFromCursor(cursor); feed.setItems(items); for (FeedItem item : items) { @@ -266,6 +268,19 @@ public final class DBReader { } } + public static int getFeedEpisodeCount(long feedId, FeedItemFilter filter) { + PodDBAdapter adapter = PodDBAdapter.getInstance(); + adapter.open(); + try (Cursor cursor = adapter.getFeedEpisodeCountCursor(feedId, filter)) { + if (cursor.moveToFirst()) { + return cursor.getInt(0); + } + return -1; + } finally { + adapter.close(); + } + } + public static List getRandomEpisodes(int limit, int seed) { PodDBAdapter adapter = PodDBAdapter.getInstance(); adapter.open(); @@ -324,18 +339,6 @@ public final class DBReader { } } - /** - * Loads a specific Feed from the database. - * - * @param feedId The ID of the Feed - * @return The Feed or null if the Feed could not be found. The Feeds FeedItems will also be loaded from the - * database and the items-attribute will be set correctly. - */ - @Nullable - public static Feed getFeed(final long feedId) { - return getFeed(feedId, false); - } - /** * Loads a specific Feed from the database. * @@ -345,7 +348,7 @@ public final class DBReader { * database and the items-attribute will be set correctly. */ @Nullable - public static Feed getFeed(final long feedId, boolean filtered) { + public static Feed getFeed(final long feedId, boolean filtered, int offset, int limit) { Log.d(TAG, "getFeed() called with: " + "feedId = [" + feedId + "]"); PodDBAdapter adapter = PodDBAdapter.getInstance(); adapter.open(); @@ -354,7 +357,12 @@ public final class DBReader { if (cursor.moveToNext()) { feed = cursor.getFeed(); FeedItemFilter filter = filtered ? feed.getItemFilter() : FeedItemFilter.unfiltered(); - feed.setItems(getFeedItemList(feed, filter, feed.getSortOrder())); + List items = getFeedItemList(feed, filter, feed.getSortOrder(), offset, limit); + for (FeedItem item : items) { + item.setFeed(feed); + } + loadTagsOfFeedItemList(items); + feed.setItems(items); } else { Log.e(TAG, "getFeed could not find feed with id " + feedId); } diff --git a/storage/database/src/main/java/de/danoeh/antennapod/storage/database/DBWriter.java b/storage/database/src/main/java/de/danoeh/antennapod/storage/database/DBWriter.java index 5d1b7f74c..5f684b398 100644 --- a/storage/database/src/main/java/de/danoeh/antennapod/storage/database/DBWriter.java +++ b/storage/database/src/main/java/de/danoeh/antennapod/storage/database/DBWriter.java @@ -161,7 +161,7 @@ public class DBWriter { */ public static Future deleteFeed(final Context context, final long feedId) { return runOnDbThread(() -> { - final Feed feed = DBReader.getFeed(feedId, false); + final Feed feed = DBReader.getFeed(feedId, false, 0, Integer.MAX_VALUE); if (feed == null) { return; } diff --git a/storage/database/src/main/java/de/danoeh/antennapod/storage/database/FeedDatabaseWriter.java b/storage/database/src/main/java/de/danoeh/antennapod/storage/database/FeedDatabaseWriter.java index 0a61007aa..7269fcd55 100644 --- a/storage/database/src/main/java/de/danoeh/antennapod/storage/database/FeedDatabaseWriter.java +++ b/storage/database/src/main/java/de/danoeh/antennapod/storage/database/FeedDatabaseWriter.java @@ -31,12 +31,13 @@ public abstract class FeedDatabaseWriter { private static Feed searchFeedByIdentifyingValueOrID(Feed feed) { if (feed.getId() != 0) { - return DBReader.getFeed(feed.getId()); + return DBReader.getFeed(feed.getId(), false, 0, Integer.MAX_VALUE); } else { List feeds = DBReader.getFeedList(); for (Feed f : feeds) { if (f.getIdentifyingValue().equals(feed.getIdentifyingValue())) { - f.setItems(DBReader.getFeedItemList(f, FeedItemFilter.unfiltered(), SortOrder.DATE_NEW_OLD)); + f.setItems(DBReader.getFeedItemList(f, FeedItemFilter.unfiltered(), + SortOrder.DATE_NEW_OLD, 0, Integer.MAX_VALUE)); return f; } } diff --git a/storage/database/src/main/java/de/danoeh/antennapod/storage/database/PodDBAdapter.java b/storage/database/src/main/java/de/danoeh/antennapod/storage/database/PodDBAdapter.java index 89cd481d4..2cbf27509 100644 --- a/storage/database/src/main/java/de/danoeh/antennapod/storage/database/PodDBAdapter.java +++ b/storage/database/src/main/java/de/danoeh/antennapod/storage/database/PodDBAdapter.java @@ -954,14 +954,16 @@ public class PodDBAdapter { * @param feed The feed you want to get the FeedItems from. * @return The cursor of the query */ - public final Cursor getItemsOfFeedCursor(final Feed feed, FeedItemFilter filter, SortOrder sortOrder) { + public final Cursor getItemsOfFeedCursor(final Feed feed, FeedItemFilter filter, SortOrder sortOrder, + int offset, int limit) { String orderByQuery = FeedItemSortQuery.generateFrom(sortOrder); String filterQuery = FeedItemFilterQuery.generateFrom(filter); String whereClauseAnd = "".equals(filterQuery) ? "" : " AND " + filterQuery; final String query = SELECT_FEED_ITEMS_AND_MEDIA + " WHERE " + TABLE_NAME_FEED_ITEMS + "." + KEY_FEED + "=" + feed.getId() + whereClauseAnd - + " ORDER BY " + orderByQuery; + + " ORDER BY " + orderByQuery + + " LIMIT " + offset + ", " + limit; return db.rawQuery(query, null); } @@ -1082,6 +1084,15 @@ public class PodDBAdapter { return db.rawQuery(query, null); } + public final Cursor getFeedEpisodeCountCursor(long feedId, FeedItemFilter filter) { + String filterQuery = FeedItemFilterQuery.generateFrom(filter); + String whereAndClause = "".equals(filterQuery) ? "" : " AND " + filterQuery; + final String query = "SELECT count(" + TABLE_NAME_FEED_ITEMS + "." + KEY_ID + ") FROM " + TABLE_NAME_FEED_ITEMS + + JOIN_FEED_ITEM_AND_MEDIA + + " WHERE " + TABLE_NAME_FEED_ITEMS + "." + KEY_FEED + "=" + feedId + whereAndClause; + return db.rawQuery(query, null); + } + public Cursor getRandomEpisodesCursor(int limit, int seed) { final String allItemsRandomOrder = SELECT_FEED_ITEMS_AND_MEDIA + " WHERE (" + KEY_READ + " = " + FeedItem.NEW + " OR " + KEY_READ + " = " + FeedItem.UNPLAYED + ") " diff --git a/ui/statistics/src/main/java/de/danoeh/antennapod/ui/statistics/feed/FeedStatisticsFragment.java b/ui/statistics/src/main/java/de/danoeh/antennapod/ui/statistics/feed/FeedStatisticsFragment.java index aacd1294d..c35c411f7 100644 --- a/ui/statistics/src/main/java/de/danoeh/antennapod/ui/statistics/feed/FeedStatisticsFragment.java +++ b/ui/statistics/src/main/java/de/danoeh/antennapod/ui/statistics/feed/FeedStatisticsFragment.java @@ -78,7 +78,7 @@ public class FeedStatisticsFragment extends Fragment { for (StatisticsItem statisticsItem : statisticsData.feedTime) { if (statisticsItem.feed.getId() == feedId) { List items = DBReader.getFeedItemList(statisticsItem.feed, - FeedItemFilter.unfiltered(), SortOrder.DATE_OLD_NEW); + FeedItemFilter.unfiltered(), SortOrder.DATE_OLD_NEW, 0, Integer.MAX_VALUE); List dates = new ArrayList<>(); for (FeedItem item : items) { dates.add(item.getPubDate());