Add lazy loading to feed item list (#7091)

This commit is contained in:
ByteHamster 2024-04-13 19:18:13 +02:00 committed by GitHub
parent 04fab47072
commit f3bca9d9e4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 167 additions and 101 deletions

View File

@ -98,7 +98,7 @@ public class DownloadLogAdapter extends BaseAdapter {
if (status.getFeedfileType() == Feed.FEEDFILETYPE_FEED) { if (status.getFeedfileType() == Feed.FEEDFILETYPE_FEED) {
holder.secondaryActionButton.setOnClickListener(v -> { holder.secondaryActionButton.setOnClickListener(v -> {
holder.secondaryActionButton.setVisibility(View.INVISIBLE); holder.secondaryActionButton.setVisibility(View.INVISIBLE);
Feed feed = DBReader.getFeed(status.getFeedfileId()); Feed feed = DBReader.getFeed(status.getFeedfileId(), false, 0, 0);
if (feed == null) { if (feed == null) {
Log.e(TAG, "Could not find feed for feed id: " + status.getFeedfileId()); Log.e(TAG, "Could not find feed for feed id: " + status.getFeedfileId());
return; return;

View File

@ -28,7 +28,7 @@ public class DownloadLogDetailsDialog extends MaterialAlertDialogBuilder {
url = media.getDownloadUrl(); url = media.getDownloadUrl();
} }
} else if (status.getFeedfileType() == Feed.FEEDFILETYPE_FEED) { } 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) { if (feed != null) {
url = feed.getDownloadUrl(); url = feed.getDownloadUrl();
} }

View File

@ -168,7 +168,7 @@ public class FeedInfoFragment extends Fragment implements MaterialToolbar.OnMenu
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
long feedId = getArguments().getLong(EXTRA_FEED_ID); long feedId = getArguments().getLong(EXTRA_FEED_ID);
disposable = Maybe.create((MaybeOnSubscribe<Feed>) emitter -> { disposable = Maybe.create((MaybeOnSubscribe<Feed>) emitter -> {
Feed feed = DBReader.getFeed(feedId); Feed feed = DBReader.getFeed(feedId, false, 0, 0);
if (feed != null) { if (feed != null) {
emitter.onSuccess(feed); emitter.onSuccess(feed);
} else { } else {

View File

@ -17,6 +17,7 @@ import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.content.res.AppCompatResources; import androidx.appcompat.content.res.AppCompatResources;
import androidx.core.util.Pair;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.RecyclerView; 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.ui.MenuItemUtils;
import de.danoeh.antennapod.storage.database.DBReader; import de.danoeh.antennapod.storage.database.DBReader;
import de.danoeh.antennapod.storage.database.DBWriter; 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.common.IntentUtils;
import de.danoeh.antennapod.ui.share.ShareUtils; import de.danoeh.antennapod.ui.share.ShareUtils;
import de.danoeh.antennapod.ui.episodeslist.MoreContentListFooterUtil; 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.Feed;
import de.danoeh.antennapod.model.feed.FeedItem; 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 de.danoeh.antennapod.storage.preferences.UserPreferences; import de.danoeh.antennapod.storage.preferences.UserPreferences;
import de.danoeh.antennapod.ui.glide.FastBlurTransformation; import de.danoeh.antennapod.ui.glide.FastBlurTransformation;
import de.danoeh.antennapod.ui.episodeslist.EpisodeItemViewHolder; 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"; public static final String TAG = "ItemlistFragment";
private static final String ARGUMENT_FEED_ID = "argument.de.danoeh.antennapod.feed_id"; private static final String ARGUMENT_FEED_ID = "argument.de.danoeh.antennapod.feed_id";
private static final String KEY_UP_ARROW = "up_arrow"; 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 FeedItemListAdapter adapter;
private SwipeActions swipeActions; private SwipeActions swipeActions;
@ -147,6 +150,7 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem
} }
((MainActivity) getActivity()).setupToolbarToggle(viewBinding.toolbar, displayUpArrow); ((MainActivity) getActivity()).setupToolbarToggle(viewBinding.toolbar, displayUpArrow);
updateToolbar(); updateToolbar();
setupLoadMoreScrollListener();
viewBinding.recyclerView.setRecycledViewPool(((MainActivity) getActivity()).getRecycledViewPool()); viewBinding.recyclerView.setRecycledViewPool(((MainActivity) getActivity()).getRecycledViewPool());
adapter = new FeedItemListAdapter((MainActivity) getActivity()); adapter = new FeedItemListAdapter((MainActivity) getActivity());
@ -317,6 +321,21 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem
return FeedItemMenuHandler.onMenuItemClicked(this, item.getItemId(), selectedItem); 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 @Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) { public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
MainActivity activity = (MainActivity) getActivity(); MainActivity activity = (MainActivity) getActivity();
@ -535,17 +554,23 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem
if (disposable != null) { if (disposable != null) {
disposable.dispose(); 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()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe( .subscribe(
result -> { result -> {
feed = result; hasMoreItems = !(page == 1 && feed.getItems().size() < EPISODES_PER_PAGE);
swipeActions.setFilter(feed.getItemFilter()); swipeActions.setFilter(feed.getItemFilter());
refreshHeaderView(); refreshHeaderView();
viewBinding.progressBar.setVisibility(View.GONE); viewBinding.progressBar.setVisibility(View.GONE);
adapter.setDummyViews(0); adapter.setDummyViews(0);
adapter.updateItems(feed.getItems()); adapter.updateItems(feed.getItems());
adapter.setTotalNumberOfItems(result.second);
updateToolbar(); updateToolbar();
}, error -> { }, error -> {
feed = null; feed = null;
@ -557,19 +582,37 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem
}); });
} }
@Nullable private void loadMoreItems() {
private Feed loadData() { if (disposable != null) {
Feed feed = DBReader.getFeed(feedID, true); disposable.dispose();
if (feed == null) {
return null;
} }
DBReader.loadAdditionalFeedItemListData(feed.getItems()); isLoadingMore = true;
if (feed.getSortOrder() != null) { adapter.setDummyViews(1);
List<FeedItem> feedItems = feed.getItems(); adapter.notifyItemInserted(adapter.getItemCount() - 1);
FeedItemPermutors.getPermutor(feed.getSortOrder()).reorder(feedItems); disposable = Observable.fromCallable(() -> DBReader.getFeed(feedID, true,
feed.setItems(feedItems); (page - 1) * EPISODES_PER_PAGE, EPISODES_PER_PAGE))
} .subscribeOn(Schedulers.io())
return feed; .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) @Subscribe(threadMode = ThreadMode.MAIN)
@ -608,45 +651,4 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem
MenuItemUtils.setOnClickListeners(menu, FeedItemlistFragment.this::onContextItemSelected); 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);
}
}
} }

View File

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

View File

@ -81,7 +81,7 @@ public class FeedSettingsFragment extends Fragment {
.commitAllowingStateLoss(); .commitAllowingStateLoss();
disposable = Maybe.create((MaybeOnSubscribe<Feed>) emitter -> { disposable = Maybe.create((MaybeOnSubscribe<Feed>) emitter -> {
Feed feed = DBReader.getFeed(feedId); Feed feed = DBReader.getFeed(feedId, false, 0, 0);
if (feed != null) { if (feed != null) {
emitter.onSuccess(feed); emitter.onSuccess(feed);
} else { } else {
@ -163,7 +163,7 @@ public class FeedSettingsFragment extends Fragment {
long feedId = getArguments().getLong(EXTRA_FEED_ID); long feedId = getArguments().getLong(EXTRA_FEED_ID);
disposable = Maybe.create((MaybeOnSubscribe<Feed>) emitter -> { disposable = Maybe.create((MaybeOnSubscribe<Feed>) emitter -> {
Feed feed = DBReader.getFeed(feedId); Feed feed = DBReader.getFeed(feedId, false, 0, 0);
if (feed != null) { if (feed != null) {
emitter.onSuccess(feed); emitter.onSuccess(feed);
} else { } else {

View File

@ -526,7 +526,7 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
if (didPressSubscribe) { if (didPressSubscribe) {
didPressSubscribe = false; didPressSubscribe = false;
Feed feed1 = DBReader.getFeed(getFeedId()); Feed feed1 = DBReader.getFeed(getFeedId(), false, 0, 0);
FeedPreferences feedPreferences = feed1.getPreferences(); FeedPreferences feedPreferences = feed1.getPreferences();
if (UserPreferences.isEnableAutodownload()) { if (UserPreferences.isEnableAutodownload()) {
boolean autoDownload = viewBinding.autoDownloadCheckBox.isChecked(); boolean autoDownload = viewBinding.autoDownloadCheckBox.isChecked();

View File

@ -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 Collections.shuffle(toUpdate); // If the worker gets cancelled early, every feed has a chance to be updated
} else { } else {
Feed feed = DBReader.getFeed(feedId); Feed feed = DBReader.getFeed(feedId, false, 0, Integer.MAX_VALUE);
if (feed == null) { if (feed == null) {
return Result.success(); return Result.success();
} }

View File

@ -154,7 +154,7 @@ public class DbReaderTest {
List<FeedItem> items = feed.getItems(); List<FeedItem> items = feed.getItems();
feed.setItems(null); feed.setItems(null);
List<FeedItem> savedItems = DBReader.getFeedItemList(feed, List<FeedItem> savedItems = DBReader.getFeedItemList(feed,
FeedItemFilter.unfiltered(), SortOrder.DATE_NEW_OLD); FeedItemFilter.unfiltered(), SortOrder.DATE_NEW_OLD, 0, Integer.MAX_VALUE);
assertNotNull(savedItems); assertNotNull(savedItems);
assertEquals(items.size(), savedItems.size()); assertEquals(items.size(), savedItems.size());
for (int i = 0; i < savedItems.size(); i++) { for (int i = 0; i < savedItems.size(); i++) {

View File

@ -133,7 +133,7 @@ public class DbTasksTest {
updatedFeedTest(newFeed, feedID, itemIDs, numItemsOld, numItemsNew); 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); assertNotNull(feedFromDB);
assertEquals(newFeed.getId(), feedFromDB.getId()); assertEquals(newFeed.getId(), feedFromDB.getId());
updatedFeedTest(feedFromDB, feedID, itemIDs, numItemsOld, numItemsNew); updatedFeedTest(feedFromDB, feedID, itemIDs, numItemsOld, numItemsNew);
@ -163,7 +163,7 @@ public class DbTasksTest {
final Feed newFeed = FeedDatabaseWriter.updateFeed(context, feed, false); final Feed newFeed = FeedDatabaseWriter.updateFeed(context, feed, false);
assertNotSame(newFeed, feed); 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); final FeedItem feedItemFromDB = feedFromDB.getItems().get(0);
assertTrue(feedItemFromDB.isNew()); assertTrue(feedItemFromDB.isNew());
} }
@ -186,7 +186,7 @@ public class DbTasksTest {
Feed newFeed = FeedDatabaseWriter.updateFeed(context, feed, true); Feed newFeed = FeedDatabaseWriter.updateFeed(context, feed, true);
assertEquals(8, newFeed.getItems().size()); // 10 - 2 = 8 items 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 assertEquals(8, feedFromDB.getItems().size()); // 10 - 2 = 8 items
} }
@ -213,7 +213,7 @@ public class DbTasksTest {
Feed newFeed = FeedDatabaseWriter.updateFeed(context, feed, false); Feed newFeed = FeedDatabaseWriter.updateFeed(context, feed, false);
assertEquals(10, newFeed.getItems().size()); // id 1-duplicate replaces because the stream url is the same 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 assertEquals(10, feedFromDB.getItems().size()); // id1-duplicate should override id 1
FeedItem updatedItem = feedFromDB.getItemAtIndex(9); FeedItem updatedItem = feedFromDB.getItemAtIndex(9);

View File

@ -771,7 +771,7 @@ public class DbWriterTest {
DBWriter.removeAllNewFlags().get(); DBWriter.removeAllNewFlags().get();
List<FeedItem> loadedItems = DBReader.getFeedItemList(feed, List<FeedItem> loadedItems = DBReader.getFeedItemList(feed,
FeedItemFilter.unfiltered(), SortOrder.DATE_NEW_OLD); FeedItemFilter.unfiltered(), SortOrder.DATE_NEW_OLD, 0, Integer.MAX_VALUE);
for (FeedItem item : loadedItems) { for (FeedItem item : loadedItems) {
assertFalse(item.isNew()); assertFalse(item.isNew());
} }

View File

@ -169,7 +169,8 @@ public class LocalFeedUpdaterTest {
callUpdateFeed(LOCAL_FEED_DIR1); callUpdateFeed(LOCAL_FEED_DIR1);
Feed feed = verifySingleFeedInDatabase(); Feed feed = verifySingleFeedInDatabase();
List<FeedItem> feedItems = DBReader.getFeedItemList(feed, FeedItemFilter.unfiltered(), SortOrder.DATE_NEW_OLD); List<FeedItem> feedItems = DBReader.getFeedItemList(feed, FeedItemFilter.unfiltered(),
SortOrder.DATE_NEW_OLD, 0, Integer.MAX_VALUE);
assertEquals("track1.mp3", feedItems.get(0).getTitle()); assertEquals("track1.mp3", feedItems.get(0).getTitle());
} }
@ -283,7 +284,8 @@ public class LocalFeedUpdaterTest {
*/ */
private static void verifySingleFeedInDatabaseAndItemCount(int expectedItemCount) { private static void verifySingleFeedInDatabaseAndItemCount(int expectedItemCount) {
Feed feed = verifySingleFeedInDatabase(); Feed feed = verifySingleFeedInDatabase();
List<FeedItem> feedItems = DBReader.getFeedItemList(feed, FeedItemFilter.unfiltered(), SortOrder.DATE_NEW_OLD); List<FeedItem> feedItems = DBReader.getFeedItemList(feed, FeedItemFilter.unfiltered(),
SortOrder.DATE_NEW_OLD, 0, Integer.MAX_VALUE);
assertEquals(expectedItemCount, feedItems.size()); assertEquals(expectedItemCount, feedItems.size());
} }

View File

@ -94,7 +94,6 @@ 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.FeedMedia; import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.model.feed.FeedPreferences; 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.MediaType;
import de.danoeh.antennapod.model.playback.Playable; import de.danoeh.antennapod.model.playback.Playable;
import de.danoeh.antennapod.playback.base.PlaybackServiceMediaPlayer; import de.danoeh.antennapod.playback.base.PlaybackServiceMediaPlayer;
@ -452,12 +451,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
new FeedItemFilter(FeedItemFilter.UNPLAYED), UserPreferences.getAllEpisodesSortOrder()); new FeedItemFilter(FeedItemFilter.UNPLAYED), UserPreferences.getAllEpisodesSortOrder());
} else if (parentId.startsWith("FeedId:")) { } else if (parentId.startsWith("FeedId:")) {
long feedId = Long.parseLong(parentId.split(":")[1]); long feedId = Long.parseLong(parentId.split(":")[1]);
Feed feed = DBReader.getFeed(feedId); feedItems = DBReader.getFeed(feedId, true, 0, MAX_ANDROID_AUTO_EPISODES_PER_FEED).getItems();
SortOrder sortOrder = feed.getSortOrder();
if (sortOrder == null) {
sortOrder = SortOrder.DATE_NEW_OLD;
}
feedItems = DBReader.getFeedItemList(feed, FeedItemFilter.unfiltered(), sortOrder);
} else if (parentId.equals(getString(R.string.current_playing_episode))) { } else if (parentId.equals(getString(R.string.current_playing_episode))) {
FeedMedia playable = DBReader.getFeedMedia(PlaybackPreferences.getCurrentlyPlayingFeedMediaId()); FeedMedia playable = DBReader.getFeedMedia(PlaybackPreferences.getCurrentlyPlayingFeedMediaId());
if (playable != null) { if (playable != null) {

View File

@ -149,12 +149,14 @@ public final class DBReader {
* @param feed The Feed whose items should be loaded * @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. * @return A list with the FeedItems of the Feed. The Feed-attribute of the FeedItems will already be set correctly.
*/ */
public static List<FeedItem> getFeedItemList(final Feed feed, final FeedItemFilter filter, SortOrder sortOrder) { public static List<FeedItem> getFeedItemList(final Feed feed, final FeedItemFilter filter, SortOrder sortOrder,
int offset, int limit) {
Log.d(TAG, "getFeedItemList() called with: " + "feed = [" + feed + "]"); Log.d(TAG, "getFeedItemList() called with: " + "feed = [" + feed + "]");
PodDBAdapter adapter = PodDBAdapter.getInstance(); PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open(); 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<FeedItem> items = extractItemlistFromCursor(cursor); List<FeedItem> items = extractItemlistFromCursor(cursor);
feed.setItems(items); feed.setItems(items);
for (FeedItem item : 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<FeedItem> getRandomEpisodes(int limit, int seed) { public static List<FeedItem> getRandomEpisodes(int limit, int seed) {
PodDBAdapter adapter = PodDBAdapter.getInstance(); PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open(); 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. * Loads a specific Feed from the database.
* *
@ -345,7 +348,7 @@ public final class DBReader {
* database and the items-attribute will be set correctly. * database and the items-attribute will be set correctly.
*/ */
@Nullable @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 + "]"); Log.d(TAG, "getFeed() called with: " + "feedId = [" + feedId + "]");
PodDBAdapter adapter = PodDBAdapter.getInstance(); PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open(); adapter.open();
@ -354,7 +357,12 @@ public final class DBReader {
if (cursor.moveToNext()) { if (cursor.moveToNext()) {
feed = cursor.getFeed(); feed = cursor.getFeed();
FeedItemFilter filter = filtered ? feed.getItemFilter() : FeedItemFilter.unfiltered(); FeedItemFilter filter = filtered ? feed.getItemFilter() : FeedItemFilter.unfiltered();
feed.setItems(getFeedItemList(feed, filter, feed.getSortOrder())); List<FeedItem> items = getFeedItemList(feed, filter, feed.getSortOrder(), offset, limit);
for (FeedItem item : items) {
item.setFeed(feed);
}
loadTagsOfFeedItemList(items);
feed.setItems(items);
} else { } else {
Log.e(TAG, "getFeed could not find feed with id " + feedId); Log.e(TAG, "getFeed could not find feed with id " + feedId);
} }

View File

@ -161,7 +161,7 @@ public class DBWriter {
*/ */
public static Future<?> deleteFeed(final Context context, final long feedId) { public static Future<?> deleteFeed(final Context context, final long feedId) {
return runOnDbThread(() -> { return runOnDbThread(() -> {
final Feed feed = DBReader.getFeed(feedId, false); final Feed feed = DBReader.getFeed(feedId, false, 0, Integer.MAX_VALUE);
if (feed == null) { if (feed == null) {
return; return;
} }

View File

@ -31,12 +31,13 @@ public abstract class FeedDatabaseWriter {
private static Feed searchFeedByIdentifyingValueOrID(Feed feed) { private static Feed searchFeedByIdentifyingValueOrID(Feed feed) {
if (feed.getId() != 0) { if (feed.getId() != 0) {
return DBReader.getFeed(feed.getId()); return DBReader.getFeed(feed.getId(), false, 0, Integer.MAX_VALUE);
} else { } else {
List<Feed> feeds = DBReader.getFeedList(); List<Feed> feeds = DBReader.getFeedList();
for (Feed f : feeds) { for (Feed f : feeds) {
if (f.getIdentifyingValue().equals(feed.getIdentifyingValue())) { 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; return f;
} }
} }

View File

@ -954,14 +954,16 @@ public class PodDBAdapter {
* @param feed The feed you want to get the FeedItems from. * @param feed The feed you want to get the FeedItems from.
* @return The cursor of the query * @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 orderByQuery = FeedItemSortQuery.generateFrom(sortOrder);
String filterQuery = FeedItemFilterQuery.generateFrom(filter); String filterQuery = FeedItemFilterQuery.generateFrom(filter);
String whereClauseAnd = "".equals(filterQuery) ? "" : " AND " + filterQuery; String whereClauseAnd = "".equals(filterQuery) ? "" : " AND " + filterQuery;
final String query = SELECT_FEED_ITEMS_AND_MEDIA final String query = SELECT_FEED_ITEMS_AND_MEDIA
+ " WHERE " + TABLE_NAME_FEED_ITEMS + "." + KEY_FEED + "=" + feed.getId() + " WHERE " + TABLE_NAME_FEED_ITEMS + "." + KEY_FEED + "=" + feed.getId()
+ whereClauseAnd + whereClauseAnd
+ " ORDER BY " + orderByQuery; + " ORDER BY " + orderByQuery
+ " LIMIT " + offset + ", " + limit;
return db.rawQuery(query, null); return db.rawQuery(query, null);
} }
@ -1082,6 +1084,15 @@ public class PodDBAdapter {
return db.rawQuery(query, null); 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) { public Cursor getRandomEpisodesCursor(int limit, int seed) {
final String allItemsRandomOrder = SELECT_FEED_ITEMS_AND_MEDIA final String allItemsRandomOrder = SELECT_FEED_ITEMS_AND_MEDIA
+ " WHERE (" + KEY_READ + " = " + FeedItem.NEW + " OR " + KEY_READ + " = " + FeedItem.UNPLAYED + ") " + " WHERE (" + KEY_READ + " = " + FeedItem.NEW + " OR " + KEY_READ + " = " + FeedItem.UNPLAYED + ") "

View File

@ -78,7 +78,7 @@ public class FeedStatisticsFragment extends Fragment {
for (StatisticsItem statisticsItem : statisticsData.feedTime) { for (StatisticsItem statisticsItem : statisticsData.feedTime) {
if (statisticsItem.feed.getId() == feedId) { if (statisticsItem.feed.getId() == feedId) {
List<FeedItem> items = DBReader.getFeedItemList(statisticsItem.feed, List<FeedItem> items = DBReader.getFeedItemList(statisticsItem.feed,
FeedItemFilter.unfiltered(), SortOrder.DATE_OLD_NEW); FeedItemFilter.unfiltered(), SortOrder.DATE_OLD_NEW, 0, Integer.MAX_VALUE);
List<Date> dates = new ArrayList<>(); List<Date> dates = new ArrayList<>();
for (FeedItem item : items) { for (FeedItem item : items) {
dates.add(item.getPubDate()); dates.add(item.getPubDate());