diff options
-rw-r--r-- | app/src/main/java/de/danoeh/antennapod/fragment/InboxFragment.java | 4 | ||||
-rw-r--r-- | app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java | 195 | ||||
-rw-r--r-- | app/src/main/res/layout/list_container_fragment.xml (renamed from app/src/main/res/layout/inbox_fragment.xml) | 4 | ||||
-rw-r--r-- | core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java | 23 | ||||
-rw-r--r-- | core/src/test/java/de/danoeh/antennapod/core/storage/DbReaderTest.java | 760 | ||||
-rw-r--r-- | storage/database/src/main/java/de/danoeh/antennapod/storage/database/PodDBAdapter.java | 9 |
6 files changed, 476 insertions, 519 deletions
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/InboxFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/InboxFragment.java index 067e7466c..9d5376c2a 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/InboxFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/InboxFragment.java @@ -49,9 +49,9 @@ public class InboxFragment extends EpisodesListFragment implements Toolbar.OnMen @NonNull @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View inboxContainer = View.inflate(getContext(), R.layout.inbox_fragment, null); + View inboxContainer = View.inflate(getContext(), R.layout.list_container_fragment, null); View root = super.onCreateView(inflater, container, savedInstanceState); - ((FrameLayout) inboxContainer.findViewById(R.id.inboxContent)).addView(root); + ((FrameLayout) inboxContainer.findViewById(R.id.listContent)).addView(root); emptyView.setTitle(R.string.no_inbox_head_label); emptyView.setMessage(R.string.no_inbox_label); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java index 857b59967..8bcef4181 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java @@ -1,57 +1,36 @@ package de.danoeh.antennapod.fragment; import android.os.Bundle; -import android.util.Log; import android.view.ContextMenu; -import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.widget.ProgressBar; +import android.widget.FrameLayout; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.widget.Toolbar; -import androidx.fragment.app.Fragment; + import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.MainActivity; import de.danoeh.antennapod.adapter.EpisodeItemListAdapter; -import de.danoeh.antennapod.core.event.DownloadEvent; -import de.danoeh.antennapod.core.event.DownloaderUpdate; import de.danoeh.antennapod.core.menuhandler.MenuItemUtils; -import de.danoeh.antennapod.event.FeedItemEvent; import de.danoeh.antennapod.event.playback.PlaybackHistoryEvent; -import de.danoeh.antennapod.event.playback.PlaybackPositionEvent; import de.danoeh.antennapod.event.PlayerStatusEvent; import de.danoeh.antennapod.event.UnreadItemsUpdateEvent; import de.danoeh.antennapod.model.feed.FeedItem; import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.storage.DBWriter; -import de.danoeh.antennapod.core.util.FeedItemUtil; -import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler; -import de.danoeh.antennapod.view.EmptyViewHandler; -import de.danoeh.antennapod.view.EpisodeItemListRecyclerView; -import de.danoeh.antennapod.view.viewholder.EpisodeItemViewHolder; -import io.reactivex.Observable; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.disposables.Disposable; -import io.reactivex.schedulers.Schedulers; -import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; import java.util.List; -public class PlaybackHistoryFragment extends Fragment implements Toolbar.OnMenuItemClickListener { +public class PlaybackHistoryFragment extends EpisodesListFragment implements Toolbar.OnMenuItemClickListener { public static final String TAG = "PlaybackHistoryFragment"; private static final String KEY_UP_ARROW = "up_arrow"; - private List<FeedItem> playbackHistory; - private PlaybackHistoryListAdapter adapter; - private Disposable disposable; - private EpisodeItemListRecyclerView recyclerView; - private EmptyViewHandler emptyView; - private ProgressBar progressBar; private Toolbar toolbar; private boolean displayUpArrow; @@ -64,8 +43,12 @@ public class PlaybackHistoryFragment extends Fragment implements Toolbar.OnMenuI @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - View root = inflater.inflate(R.layout.simple_list_fragment, container, false); - toolbar = root.findViewById(R.id.toolbar); + View historyContainer = View.inflate(getContext(), R.layout.list_container_fragment, null); + View root = super.onCreateView(inflater, container, savedInstanceState); + + ((FrameLayout) historyContainer.findViewById(R.id.listContent)).addView(root); + + toolbar = historyContainer.findViewById(R.id.toolbar); toolbar.setTitle(R.string.playback_history_label); toolbar.setOnMenuItemClickListener(this); displayUpArrow = getParentFragmentManager().getBackStackEntryCount() != 0; @@ -76,34 +59,14 @@ public class PlaybackHistoryFragment extends Fragment implements Toolbar.OnMenuI toolbar.inflateMenu(R.menu.playback_history); refreshToolbarState(); - recyclerView = root.findViewById(R.id.recyclerView); - recyclerView.setRecycledViewPool(((MainActivity) getActivity()).getRecycledViewPool()); - adapter = new PlaybackHistoryListAdapter((MainActivity) getActivity()); - recyclerView.setAdapter(adapter); - progressBar = root.findViewById(R.id.progLoading); + listAdapter = new PlaybackHistoryListAdapter((MainActivity) getActivity()); + recyclerView.setAdapter(listAdapter); - emptyView = new EmptyViewHandler(getActivity()); emptyView.setIcon(R.drawable.ic_history); emptyView.setTitle(R.string.no_history_head_label); emptyView.setMessage(R.string.no_history_label); - emptyView.attachToRecyclerView(recyclerView); - return root; - } - - @Override - public void onStart() { - super.onStart(); - EventBus.getDefault().register(this); - loadItems(); - } - @Override - public void onStop() { - super.onStop(); - EventBus.getDefault().unregister(this); - if (disposable != null) { - disposable.dispose(); - } + return historyContainer; } @Override @@ -112,55 +75,8 @@ public class PlaybackHistoryFragment extends Fragment implements Toolbar.OnMenuI super.onSaveInstanceState(outState); } - @Subscribe(threadMode = ThreadMode.MAIN) - public void onEventMainThread(FeedItemEvent event) { - Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]"); - if (playbackHistory == null) { - return; - } else if (adapter == null) { - loadItems(); - return; - } - for (int i = 0, size = event.items.size(); i < size; i++) { - FeedItem item = event.items.get(i); - int pos = FeedItemUtil.indexOfItemWithId(playbackHistory, item.getId()); - if (pos >= 0) { - playbackHistory.remove(pos); - playbackHistory.add(pos, item); - adapter.notifyItemChangedCompat(pos); - } - } - } - - @Subscribe(sticky = true, threadMode = ThreadMode.MAIN) - public void onEventMainThread(DownloadEvent event) { - Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]"); - DownloaderUpdate update = event.update; - if (adapter != null && update.mediaIds.length > 0) { - for (long mediaId : update.mediaIds) { - int pos = FeedItemUtil.indexOfItemWithMediaId(playbackHistory, mediaId); - if (pos >= 0) { - adapter.notifyItemChangedCompat(pos); - } - } - } - } - - @Subscribe(threadMode = ThreadMode.MAIN) - public void onEventMainThread(PlaybackPositionEvent event) { - if (adapter != null) { - for (int i = 0; i < adapter.getItemCount(); i++) { - EpisodeItemViewHolder holder = (EpisodeItemViewHolder) recyclerView.findViewHolderForAdapterPosition(i); - if (holder != null && holder.isCurrentlyPlayingItem()) { - holder.notifyPlaybackPositionUpdated(event); - break; - } - } - } - } - public void refreshToolbarState() { - boolean hasHistory = playbackHistory != null && !playbackHistory.isEmpty(); + boolean hasHistory = episodes != null && !episodes.isEmpty(); toolbar.getMenu().findItem(R.id.clear_history_item).setVisible(hasHistory); } @@ -173,33 +89,6 @@ public class PlaybackHistoryFragment extends Fragment implements Toolbar.OnMenuI return false; } - @Override - public boolean onContextItemSelected(@NonNull MenuItem item) { - FeedItem selectedItem = adapter.getLongPressedItem(); - if (selectedItem == null) { - Log.i(TAG, "Selected item at current position was null, ignoring selection"); - return super.onContextItemSelected(item); - } - return FeedItemMenuHandler.onMenuItemClicked(this, item.getItemId(), selectedItem); - } - - @Subscribe(threadMode = ThreadMode.MAIN) - public void onKeyUp(KeyEvent event) { - if (!isAdded() || !isVisible() || !isMenuVisible()) { - return; - } - switch (event.getKeyCode()) { - case KeyEvent.KEYCODE_T: - recyclerView.smoothScrollToPosition(0); - break; - case KeyEvent.KEYCODE_B: - recyclerView.smoothScrollToPosition(adapter.getItemCount() - 1); - break; - default: - break; - } - } - @Subscribe(threadMode = ThreadMode.MAIN) public void onHistoryUpdated(PlaybackHistoryEvent event) { loadItems(); @@ -212,41 +101,20 @@ public class PlaybackHistoryFragment extends Fragment implements Toolbar.OnMenuI refreshToolbarState(); } + @Override @Subscribe(threadMode = ThreadMode.MAIN) public void onUnreadItemsChanged(UnreadItemsUpdateEvent event) { loadItems(); refreshToolbarState(); } - private void onFragmentLoaded() { - adapter.notifyDataSetChanged(); + @Override + protected void onFragmentLoaded(List<FeedItem> episodes) { + super.onFragmentLoaded(episodes); + listAdapter.notifyDataSetChanged(); refreshToolbarState(); } - private void loadItems() { - if (disposable != null) { - disposable.dispose(); - } - progressBar.setVisibility(View.VISIBLE); - emptyView.hide(); - disposable = Observable.fromCallable(this::loadData) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(result -> { - progressBar.setVisibility(View.GONE); - playbackHistory = result; - adapter.updateItems(playbackHistory); - onFragmentLoaded(); - }, error -> Log.e(TAG, Log.getStackTraceString(error))); - } - - @NonNull - private List<FeedItem> loadData() { - List<FeedItem> history = DBReader.getPlaybackHistory(); - DBReader.loadAdditionalFeedItemListData(history); - return history; - } - private class PlaybackHistoryListAdapter extends EpisodeItemListAdapter { public PlaybackHistoryListAdapter(MainActivity mainActivity) { @@ -254,17 +122,26 @@ public class PlaybackHistoryFragment extends Fragment implements Toolbar.OnMenuI } @Override - protected void afterBindViewHolder(EpisodeItemViewHolder holder, int pos) { - // played items shouldn't be transparent for this fragment since, *all* items - // in this fragment will, by definition, be played. So it serves no purpose and can make - // it harder to read. - holder.itemView.setAlpha(1.0f); - } - - @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { super.onCreateContextMenu(menu, v, menuInfo); MenuItemUtils.setOnClickListeners(menu, PlaybackHistoryFragment.this::onContextItemSelected); } } + + @NonNull + @Override + protected List<FeedItem> loadData() { + return DBReader.getPlaybackHistory(0, page * EPISODES_PER_PAGE); + } + + @NonNull + @Override + protected List<FeedItem> loadMoreData(int page) { + return DBReader.getPlaybackHistory((page - 1) * EPISODES_PER_PAGE, EPISODES_PER_PAGE); + } + + @Override + protected int loadTotalItemCount() { + return (int) DBReader.getPlaybackHistoryLength(); + } } diff --git a/app/src/main/res/layout/inbox_fragment.xml b/app/src/main/res/layout/list_container_fragment.xml index fbacf7827..1b6debb13 100644 --- a/app/src/main/res/layout/inbox_fragment.xml +++ b/app/src/main/res/layout/list_container_fragment.xml @@ -9,13 +9,13 @@ android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_alignParentTop="true" android:minHeight="?attr/actionBarSize" android:theme="?attr/actionBarTheme" - android:layout_alignParentTop="true" app:title="@string/inbox_label" /> <FrameLayout - android:id="@+id/inboxContent" + android:id="@+id/listContent" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_below="@+id/toolbar" /> diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java index cffcbf32f..e7e8e9587 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java @@ -46,11 +46,6 @@ public final class DBReader { private static final String TAG = "DBReader"; /** - * Maximum size of the list returned by {@link #getPlaybackHistory()}. - */ - public static final int PLAYBACK_HISTORY_SIZE = 50; - - /** * Maximum size of the list returned by {@link #getDownloadLog()}. */ private static final int DOWNLOAD_LOG_SIZE = 200; @@ -419,11 +414,12 @@ public final class DBReader { * Loads the playback history from the database. A FeedItem is in the playback history if playback of the correpsonding episode * has been completed at least once. * + * @param limit The maximum number of items to return. + * * @return The playback history. The FeedItems are sorted by their media's playbackCompletionDate in descending order. - * The size of the returned list is limited by {@link #PLAYBACK_HISTORY_SIZE}. */ @NonNull - public static List<FeedItem> getPlaybackHistory() { + public static List<FeedItem> getPlaybackHistory(int offset, int limit) { Log.d(TAG, "getPlaybackHistory() called"); PodDBAdapter adapter = PodDBAdapter.getInstance(); @@ -432,7 +428,7 @@ public final class DBReader { Cursor mediaCursor = null; Cursor itemCursor = null; try { - mediaCursor = adapter.getCompletedMediaCursor(PLAYBACK_HISTORY_SIZE); + mediaCursor = adapter.getCompletedMediaCursor(offset, limit); String[] itemIds = new String[mediaCursor.getCount()]; for (int i = 0; i < itemIds.length && mediaCursor.moveToPosition(i); i++) { int index = mediaCursor.getColumnIndex(PodDBAdapter.KEY_FEEDITEM); @@ -454,6 +450,17 @@ public final class DBReader { } } + public static long getPlaybackHistoryLength() { + PodDBAdapter adapter = PodDBAdapter.getInstance(); + adapter.open(); + + try { + return adapter.getCompletedMediaLength(); + } finally { + adapter.close(); + } + } + /** * Loads the download log from the database. * diff --git a/core/src/test/java/de/danoeh/antennapod/core/storage/DbReaderTest.java b/core/src/test/java/de/danoeh/antennapod/core/storage/DbReaderTest.java index 834442fdd..bb3dd4b40 100644 --- a/core/src/test/java/de/danoeh/antennapod/core/storage/DbReaderTest.java +++ b/core/src/test/java/de/danoeh/antennapod/core/storage/DbReaderTest.java @@ -3,6 +3,8 @@ package de.danoeh.antennapod.core.storage; import android.content.Context; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.Date; import java.util.List; import java.util.Random; @@ -16,8 +18,11 @@ import de.danoeh.antennapod.core.util.LongList; import de.danoeh.antennapod.storage.database.PodDBAdapter; import org.junit.After; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; +import org.junit.experimental.runners.Enclosed; import org.junit.runner.RunWith; +import org.robolectric.ParameterizedRobolectricTestRunner; import org.robolectric.RobolectricTestRunner; import static de.danoeh.antennapod.core.storage.DbTestUtils.saveFeedlist; @@ -31,410 +36,473 @@ import static org.junit.Assert.assertTrue; * Test class for DBReader. */ @SuppressWarnings("ConstantConditions") -@RunWith(RobolectricTestRunner.class) +@RunWith(Enclosed.class) public class DbReaderTest { + @Ignore("Not a test") + public static class TestBase { + @Before + public void setUp() { + Context context = InstrumentationRegistry.getInstrumentation().getContext(); + UserPreferences.init(context); + + PodDBAdapter.init(context); + PodDBAdapter.deleteDatabase(); + PodDBAdapter adapter = PodDBAdapter.getInstance(); + adapter.open(); + adapter.close(); + } - @Before - public void setUp() { - Context context = InstrumentationRegistry.getInstrumentation().getContext(); - UserPreferences.init(context); - - PodDBAdapter.init(context); - PodDBAdapter.deleteDatabase(); - PodDBAdapter adapter = PodDBAdapter.getInstance(); - adapter.open(); - adapter.close(); - } - - @After - public void tearDown() { - PodDBAdapter.tearDownTests(); - DBWriter.tearDownTests(); - } - - @Test - public void testGetFeedList() { - List<Feed> feeds = saveFeedlist(10, 0, false); - List<Feed> savedFeeds = DBReader.getFeedList(); - assertNotNull(savedFeeds); - assertEquals(feeds.size(), savedFeeds.size()); - for (int i = 0; i < feeds.size(); i++) { - assertEquals(feeds.get(i).getId(), savedFeeds.get(i).getId()); + @After + public void tearDown() { + PodDBAdapter.tearDownTests(); + DBWriter.tearDownTests(); } } - @Test - public void testGetFeedListSortOrder() { - PodDBAdapter adapter = PodDBAdapter.getInstance(); - adapter.open(); - - Feed feed1 = new Feed(0, null, "A", "link", "d", null, null, null, "rss", "A", null, "", "", true); - Feed feed2 = new Feed(0, null, "b", "link", "d", null, null, null, "rss", "b", null, "", "", true); - Feed feed3 = new Feed(0, null, "C", "link", "d", null, null, null, "rss", "C", null, "", "", true); - Feed feed4 = new Feed(0, null, "d", "link", "d", null, null, null, "rss", "d", null, "", "", true); - adapter.setCompleteFeed(feed1); - adapter.setCompleteFeed(feed2); - adapter.setCompleteFeed(feed3); - adapter.setCompleteFeed(feed4); - assertTrue(feed1.getId() != 0); - assertTrue(feed2.getId() != 0); - assertTrue(feed3.getId() != 0); - assertTrue(feed4.getId() != 0); - - adapter.close(); - - List<Feed> saved = DBReader.getFeedList(); - assertNotNull(saved); - assertEquals("Wrong size: ", 4, saved.size()); - - assertEquals("Wrong id of feed 1: ", feed1.getId(), saved.get(0).getId()); - assertEquals("Wrong id of feed 2: ", feed2.getId(), saved.get(1).getId()); - assertEquals("Wrong id of feed 3: ", feed3.getId(), saved.get(2).getId()); - assertEquals("Wrong id of feed 4: ", feed4.getId(), saved.get(3).getId()); - } + @RunWith(RobolectricTestRunner.class) + public static class SingleTests extends TestBase { + @Test + public void testGetFeedList() { + List<Feed> feeds = saveFeedlist(10, 0, false); + List<Feed> savedFeeds = DBReader.getFeedList(); + assertNotNull(savedFeeds); + assertEquals(feeds.size(), savedFeeds.size()); + for (int i = 0; i < feeds.size(); i++) { + assertEquals(feeds.get(i).getId(), savedFeeds.get(i).getId()); + } + } - @Test - public void testFeedListDownloadUrls() { - List<Feed> feeds = saveFeedlist(10, 0, false); - List<String> urls = DBReader.getFeedListDownloadUrls(); - assertNotNull(urls); - assertEquals(feeds.size(), urls.size()); - for (int i = 0; i < urls.size(); i++) { - assertEquals(urls.get(i), feeds.get(i).getDownload_url()); + @Test + public void testGetFeedListSortOrder() { + PodDBAdapter adapter = PodDBAdapter.getInstance(); + adapter.open(); + + Feed feed1 = new Feed(0, null, "A", "link", "d", null, null, null, "rss", "A", null, "", "", true); + Feed feed2 = new Feed(0, null, "b", "link", "d", null, null, null, "rss", "b", null, "", "", true); + Feed feed3 = new Feed(0, null, "C", "link", "d", null, null, null, "rss", "C", null, "", "", true); + Feed feed4 = new Feed(0, null, "d", "link", "d", null, null, null, "rss", "d", null, "", "", true); + adapter.setCompleteFeed(feed1); + adapter.setCompleteFeed(feed2); + adapter.setCompleteFeed(feed3); + adapter.setCompleteFeed(feed4); + assertTrue(feed1.getId() != 0); + assertTrue(feed2.getId() != 0); + assertTrue(feed3.getId() != 0); + assertTrue(feed4.getId() != 0); + + adapter.close(); + + List<Feed> saved = DBReader.getFeedList(); + assertNotNull(saved); + assertEquals("Wrong size: ", 4, saved.size()); + + assertEquals("Wrong id of feed 1: ", feed1.getId(), saved.get(0).getId()); + assertEquals("Wrong id of feed 2: ", feed2.getId(), saved.get(1).getId()); + assertEquals("Wrong id of feed 3: ", feed3.getId(), saved.get(2).getId()); + assertEquals("Wrong id of feed 4: ", feed4.getId(), saved.get(3).getId()); } - } - @Test - public void testLoadFeedDataOfFeedItemlist() { - final int numFeeds = 10; - final int numItems = 1; - List<Feed> feeds = saveFeedlist(numFeeds, numItems, false); - List<FeedItem> items = new ArrayList<>(); - for (Feed f : feeds) { - for (FeedItem item : f.getItems()) { - item.setFeed(null); - item.setFeedId(f.getId()); - items.add(item); + @Test + public void testFeedListDownloadUrls() { + List<Feed> feeds = saveFeedlist(10, 0, false); + List<String> urls = DBReader.getFeedListDownloadUrls(); + assertNotNull(urls); + assertEquals(feeds.size(), urls.size()); + for (int i = 0; i < urls.size(); i++) { + assertEquals(urls.get(i), feeds.get(i).getDownload_url()); } } - DBReader.loadAdditionalFeedItemListData(items); - for (int i = 0; i < numFeeds; i++) { - for (int j = 0; j < numItems; j++) { - FeedItem item = feeds.get(i).getItems().get(j); - assertNotNull(item.getFeed()); - assertEquals(feeds.get(i).getId(), item.getFeed().getId()); - assertEquals(item.getFeed().getId(), item.getFeedId()); + + @Test + public void testLoadFeedDataOfFeedItemlist() { + final int numFeeds = 10; + final int numItems = 1; + List<Feed> feeds = saveFeedlist(numFeeds, numItems, false); + List<FeedItem> items = new ArrayList<>(); + for (Feed f : feeds) { + for (FeedItem item : f.getItems()) { + item.setFeed(null); + item.setFeedId(f.getId()); + items.add(item); + } + } + DBReader.loadAdditionalFeedItemListData(items); + for (int i = 0; i < numFeeds; i++) { + for (int j = 0; j < numItems; j++) { + FeedItem item = feeds.get(i).getItems().get(j); + assertNotNull(item.getFeed()); + assertEquals(feeds.get(i).getId(), item.getFeed().getId()); + assertEquals(item.getFeed().getId(), item.getFeedId()); + } } } - } - @Test - public void testGetFeedItemList() { - final int numFeeds = 1; - final int numItems = 10; - Feed feed = saveFeedlist(numFeeds, numItems, false).get(0); - List<FeedItem> items = feed.getItems(); - feed.setItems(null); - List<FeedItem> savedItems = DBReader.getFeedItemList(feed); - assertNotNull(savedItems); - assertEquals(items.size(), savedItems.size()); - for (int i = 0; i < savedItems.size(); i++) { - assertEquals(savedItems.get(i).getId(), items.get(i).getId()); + @Test + public void testGetFeedItemList() { + final int numFeeds = 1; + final int numItems = 10; + Feed feed = saveFeedlist(numFeeds, numItems, false).get(0); + List<FeedItem> items = feed.getItems(); + feed.setItems(null); + List<FeedItem> savedItems = DBReader.getFeedItemList(feed); + assertNotNull(savedItems); + assertEquals(items.size(), savedItems.size()); + for (int i = 0; i < savedItems.size(); i++) { + assertEquals(savedItems.get(i).getId(), items.get(i).getId()); + } } - } - @SuppressWarnings("SameParameterValue") - private List<FeedItem> saveQueue(int numItems) { - if (numItems <= 0) { - throw new IllegalArgumentException("numItems<=0"); - } - List<Feed> feeds = saveFeedlist(numItems, numItems, false); - List<FeedItem> allItems = new ArrayList<>(); - for (Feed f : feeds) { - allItems.addAll(f.getItems()); + @SuppressWarnings("SameParameterValue") + private List<FeedItem> saveQueue(int numItems) { + if (numItems <= 0) { + throw new IllegalArgumentException("numItems<=0"); + } + List<Feed> feeds = saveFeedlist(numItems, numItems, false); + List<FeedItem> allItems = new ArrayList<>(); + for (Feed f : feeds) { + allItems.addAll(f.getItems()); + } + // take random items from every feed + Random random = new Random(); + List<FeedItem> queue = new ArrayList<>(); + while (queue.size() < numItems) { + int index = random.nextInt(numItems); + if (!queue.contains(allItems.get(index))) { + queue.add(allItems.get(index)); + } + } + PodDBAdapter adapter = PodDBAdapter.getInstance(); + adapter.open(); + adapter.setQueue(queue); + adapter.close(); + return queue; } - // take random items from every feed - Random random = new Random(); - List<FeedItem> queue = new ArrayList<>(); - while (queue.size() < numItems) { - int index = random.nextInt(numItems); - if (!queue.contains(allItems.get(index))) { - queue.add(allItems.get(index)); + + @Test + public void testGetQueueIdList() { + final int numItems = 10; + List<FeedItem> queue = saveQueue(numItems); + LongList ids = DBReader.getQueueIDList(); + assertNotNull(ids); + assertEquals(ids.size(), queue.size()); + for (int i = 0; i < queue.size(); i++) { + assertTrue(ids.get(i) != 0); + assertEquals(ids.get(i), queue.get(i).getId()); } } - PodDBAdapter adapter = PodDBAdapter.getInstance(); - adapter.open(); - adapter.setQueue(queue); - adapter.close(); - return queue; - } - @Test - public void testGetQueueIdList() { - final int numItems = 10; - List<FeedItem> queue = saveQueue(numItems); - LongList ids = DBReader.getQueueIDList(); - assertNotNull(ids); - assertEquals(ids.size(), queue.size()); - for (int i = 0; i < queue.size(); i++) { - assertTrue(ids.get(i) != 0); - assertEquals(ids.get(i), queue.get(i).getId()); + @Test + public void testGetQueue() { + final int numItems = 10; + List<FeedItem> queue = saveQueue(numItems); + List<FeedItem> savedQueue = DBReader.getQueue(); + assertNotNull(savedQueue); + assertEquals(savedQueue.size(), queue.size()); + for (int i = 0; i < queue.size(); i++) { + assertTrue(savedQueue.get(i).getId() != 0); + assertEquals(savedQueue.get(i).getId(), queue.get(i).getId()); + } } - } - @Test - public void testGetQueue() { - final int numItems = 10; - List<FeedItem> queue = saveQueue(numItems); - List<FeedItem> savedQueue = DBReader.getQueue(); - assertNotNull(savedQueue); - assertEquals(savedQueue.size(), queue.size()); - for (int i = 0; i < queue.size(); i++) { - assertTrue(savedQueue.get(i).getId() != 0); - assertEquals(savedQueue.get(i).getId(), queue.get(i).getId()); + @SuppressWarnings("SameParameterValue") + private List<FeedItem> saveDownloadedItems(int numItems) { + if (numItems <= 0) { + throw new IllegalArgumentException("numItems<=0"); + } + List<Feed> feeds = saveFeedlist(numItems, numItems, true); + List<FeedItem> items = new ArrayList<>(); + for (Feed f : feeds) { + items.addAll(f.getItems()); + } + List<FeedItem> downloaded = new ArrayList<>(); + Random random = new Random(); + + while (downloaded.size() < numItems) { + int i = random.nextInt(numItems); + if (!downloaded.contains(items.get(i))) { + FeedItem item = items.get(i); + item.getMedia().setDownloaded(true); + item.getMedia().setFile_url("file" + i); + downloaded.add(item); + } + } + PodDBAdapter adapter = PodDBAdapter.getInstance(); + adapter.open(); + adapter.storeFeedItemlist(downloaded); + adapter.close(); + return downloaded; } - } - @SuppressWarnings("SameParameterValue") - private List<FeedItem> saveDownloadedItems(int numItems) { - if (numItems <= 0) { - throw new IllegalArgumentException("numItems<=0"); + @Test + public void testGetDownloadedItems() { + final int numItems = 10; + List<FeedItem> downloaded = saveDownloadedItems(numItems); + List<FeedItem> downloadedSaved = DBReader.getDownloadedItems(); + assertNotNull(downloadedSaved); + assertEquals(downloaded.size(), downloadedSaved.size()); + for (FeedItem item : downloadedSaved) { + assertNotNull(item.getMedia()); + assertTrue(item.getMedia().isDownloaded()); + assertNotNull(item.getMedia().getDownload_url()); + } } - List<Feed> feeds = saveFeedlist(numItems, numItems, true); - List<FeedItem> items = new ArrayList<>(); - for (Feed f : feeds) { - items.addAll(f.getItems()); + + @SuppressWarnings("SameParameterValue") + private List<FeedItem> saveNewItems(int numItems) { + List<Feed> feeds = saveFeedlist(numItems, numItems, true); + List<FeedItem> items = new ArrayList<>(); + for (Feed f : feeds) { + items.addAll(f.getItems()); + } + List<FeedItem> newItems = new ArrayList<>(); + Random random = new Random(); + + while (newItems.size() < numItems) { + int i = random.nextInt(numItems); + if (!newItems.contains(items.get(i))) { + FeedItem item = items.get(i); + item.setNew(); + newItems.add(item); + } + } + PodDBAdapter adapter = PodDBAdapter.getInstance(); + adapter.open(); + adapter.storeFeedItemlist(newItems); + adapter.close(); + return newItems; } - List<FeedItem> downloaded = new ArrayList<>(); - Random random = new Random(); - - while (downloaded.size() < numItems) { - int i = random.nextInt(numItems); - if (!downloaded.contains(items.get(i))) { - FeedItem item = items.get(i); - item.getMedia().setDownloaded(true); - item.getMedia().setFile_url("file" + i); - downloaded.add(item); + + @Test + public void testGetNewItemIds() { + final int numItems = 10; + + List<FeedItem> newItems = saveNewItems(numItems); + long[] unreadIds = new long[newItems.size()]; + for (int i = 0; i < newItems.size(); i++) { + unreadIds[i] = newItems.get(i).getId(); + } + List<FeedItem> newItemsSaved = DBReader.getNewItemsList(0, Integer.MAX_VALUE); + assertNotNull(newItemsSaved); + assertEquals(newItemsSaved.size(), newItems.size()); + for (FeedItem feedItem : newItemsSaved) { + long savedId = feedItem.getId(); + boolean found = false; + for (long id : unreadIds) { + if (id == savedId) { + found = true; + break; + } + } + assertTrue(found); } } - PodDBAdapter adapter = PodDBAdapter.getInstance(); - adapter.open(); - adapter.storeFeedItemlist(downloaded); - adapter.close(); - return downloaded; - } - @Test - public void testGetDownloadedItems() { - final int numItems = 10; - List<FeedItem> downloaded = saveDownloadedItems(numItems); - List<FeedItem> downloadedSaved = DBReader.getDownloadedItems(); - assertNotNull(downloadedSaved); - assertEquals(downloaded.size(), downloadedSaved.size()); - for (FeedItem item : downloadedSaved) { - assertNotNull(item.getMedia()); - assertTrue(item.getMedia().isDownloaded()); - assertNotNull(item.getMedia().getDownload_url()); + @Test + public void testGetPlaybackHistoryLength() { + final int totalItems = 100; + + Feed feed = DbTestUtils.saveFeedlist(1, totalItems, true).get(0); + + PodDBAdapter adapter = PodDBAdapter.getInstance(); + for (int playedItems : Arrays.asList(0, 1, 20, 100)) { + adapter.open(); + for (int i = 0; i < playedItems; ++i) { + FeedMedia m = feed.getItems().get(i).getMedia(); + m.setPlaybackCompletionDate(new Date(i + 1)); + + adapter.setFeedMediaPlaybackCompletionDate(m); + } + adapter.close(); + + long len = DBReader.getPlaybackHistoryLength(); + assertEquals("Wrong size: ", (int) len, playedItems); + } + } - } - @SuppressWarnings("SameParameterValue") - private List<FeedItem> saveNewItems(int numItems) { - List<Feed> feeds = saveFeedlist(numItems, numItems, true); - List<FeedItem> items = new ArrayList<>(); - for (Feed f : feeds) { - items.addAll(f.getItems()); + @Test + public void testGetNavDrawerDataQueueEmptyNoUnreadItems() { + final int numFeeds = 10; + final int numItems = 10; + DbTestUtils.saveFeedlist(numFeeds, numItems, true); + NavDrawerData navDrawerData = DBReader.getNavDrawerData(); + assertEquals(numFeeds, navDrawerData.items.size()); + assertEquals(0, navDrawerData.numNewItems); + assertEquals(0, navDrawerData.queueSize); } - List<FeedItem> newItems = new ArrayList<>(); - Random random = new Random(); - while (newItems.size() < numItems) { - int i = random.nextInt(numItems); - if (!newItems.contains(items.get(i))) { - FeedItem item = items.get(i); + @Test + public void testGetNavDrawerDataQueueNotEmptyWithUnreadItems() { + final int numFeeds = 10; + final int numItems = 10; + final int numQueue = 1; + final int numNew = 2; + List<Feed> feeds = DbTestUtils.saveFeedlist(numFeeds, numItems, true); + PodDBAdapter adapter = PodDBAdapter.getInstance(); + adapter.open(); + for (int i = 0; i < numNew; i++) { + FeedItem item = feeds.get(0).getItems().get(i); item.setNew(); - newItems.add(item); + adapter.setSingleFeedItem(item); } - } - PodDBAdapter adapter = PodDBAdapter.getInstance(); - adapter.open(); - adapter.storeFeedItemlist(newItems); - adapter.close(); - return newItems; - } + List<FeedItem> queue = new ArrayList<>(); + for (int i = 0; i < numQueue; i++) { + FeedItem item = feeds.get(1).getItems().get(i); + queue.add(item); + } + adapter.setQueue(queue); - @Test - public void testGetNewItemIds() { - final int numItems = 10; + adapter.close(); - List<FeedItem> newItems = saveNewItems(numItems); - long[] unreadIds = new long[newItems.size()]; - for (int i = 0; i < newItems.size(); i++) { - unreadIds[i] = newItems.get(i).getId(); + NavDrawerData navDrawerData = DBReader.getNavDrawerData(); + assertEquals(numFeeds, navDrawerData.items.size()); + assertEquals(numNew, navDrawerData.numNewItems); + assertEquals(numQueue, navDrawerData.queueSize); } - List<FeedItem> newItemsSaved = DBReader.getNewItemsList(0, Integer.MAX_VALUE); - assertNotNull(newItemsSaved); - assertEquals(newItemsSaved.size(), newItems.size()); - for (FeedItem feedItem : newItemsSaved) { - long savedId = feedItem.getId(); - boolean found = false; - for (long id : unreadIds) { - if (id == savedId) { - found = true; - break; + + @Test + public void testGetFeedItemlistCheckChaptersFalse() { + List<Feed> feeds = DbTestUtils.saveFeedlist(10, 10, false, false, 0); + for (Feed feed : feeds) { + for (FeedItem item : feed.getItems()) { + assertFalse(item.hasChapters()); } } - assertTrue(found); } - } - @Test - public void testGetPlaybackHistory() { - final int numItems = (DBReader.PLAYBACK_HISTORY_SIZE + 1) * 2; - final int playedItems = DBReader.PLAYBACK_HISTORY_SIZE + 1; - final int numReturnedItems = Math.min(playedItems, DBReader.PLAYBACK_HISTORY_SIZE); - final int numFeeds = 1; - - Feed feed = DbTestUtils.saveFeedlist(numFeeds, numItems, true).get(0); - long[] ids = new long[playedItems]; - - PodDBAdapter adapter = PodDBAdapter.getInstance(); - adapter.open(); - for (int i = 0; i < playedItems; i++) { - FeedMedia m = feed.getItems().get(i).getMedia(); - m.setPlaybackCompletionDate(new Date(i + 1)); - adapter.setFeedMediaPlaybackCompletionDate(m); - ids[ids.length - 1 - i] = m.getItem().getId(); - } - adapter.close(); - - List<FeedItem> saved = DBReader.getPlaybackHistory(); - assertNotNull(saved); - assertEquals("Wrong size: ", numReturnedItems, saved.size()); - for (int i = 0; i < numReturnedItems; i++) { - FeedItem item = saved.get(i); - assertNotNull(item.getMedia().getPlaybackCompletionDate()); - assertEquals("Wrong sort order: ", item.getId(), ids[i]); + @Test + public void testGetFeedItemlistCheckChaptersTrue() { + List<Feed> feeds = saveFeedlist(10, 10, false, true, 10); + for (Feed feed : feeds) { + for (FeedItem item : feed.getItems()) { + assertTrue(item.hasChapters()); + } + } } - } - @Test - public void testGetNavDrawerDataQueueEmptyNoUnreadItems() { - final int numFeeds = 10; - final int numItems = 10; - DbTestUtils.saveFeedlist(numFeeds, numItems, true); - NavDrawerData navDrawerData = DBReader.getNavDrawerData(); - assertEquals(numFeeds, navDrawerData.items.size()); - assertEquals(0, navDrawerData.numNewItems); - assertEquals(0, navDrawerData.queueSize); - } + @Test + public void testLoadChaptersOfFeedItemNoChapters() { + List<Feed> feeds = saveFeedlist(1, 3, false, false, 0); + saveFeedlist(1, 3, false, true, 3); + for (Feed feed : feeds) { + for (FeedItem item : feed.getItems()) { + assertFalse(item.hasChapters()); + item.setChapters(DBReader.loadChaptersOfFeedItem(item)); + assertFalse(item.hasChapters()); + assertNull(item.getChapters()); + } + } + } - @Test - public void testGetNavDrawerDataQueueNotEmptyWithUnreadItems() { - final int numFeeds = 10; - final int numItems = 10; - final int numQueue = 1; - final int numNew = 2; - List<Feed> feeds = DbTestUtils.saveFeedlist(numFeeds, numItems, true); - PodDBAdapter adapter = PodDBAdapter.getInstance(); - adapter.open(); - for (int i = 0; i < numNew; i++) { - FeedItem item = feeds.get(0).getItems().get(i); - item.setNew(); - adapter.setSingleFeedItem(item); + @Test + public void testLoadChaptersOfFeedItemWithChapters() { + final int numChapters = 3; + DbTestUtils.saveFeedlist(1, 3, false, false, 0); + List<Feed> feeds = saveFeedlist(1, 3, false, true, numChapters); + for (Feed feed : feeds) { + for (FeedItem item : feed.getItems()) { + assertTrue(item.hasChapters()); + item.setChapters(DBReader.loadChaptersOfFeedItem(item)); + assertTrue(item.hasChapters()); + assertNotNull(item.getChapters()); + assertEquals(numChapters, item.getChapters().size()); + } + } } - List<FeedItem> queue = new ArrayList<>(); - for (int i = 0; i < numQueue; i++) { - FeedItem item = feeds.get(1).getItems().get(i); - queue.add(item); + + @Test + public void testGetItemWithChapters() { + final int numChapters = 3; + List<Feed> feeds = saveFeedlist(1, 1, false, true, numChapters); + FeedItem item1 = feeds.get(0).getItems().get(0); + FeedItem item2 = DBReader.getFeedItem(item1.getId()); + item2.setChapters(DBReader.loadChaptersOfFeedItem(item2)); + assertTrue(item2.hasChapters()); + assertEquals(item1.getChapters(), item2.getChapters()); } - adapter.setQueue(queue); - adapter.close(); + @Test + public void testGetItemByEpisodeUrl() { + List<Feed> feeds = saveFeedlist(1, 1, true); + FeedItem item1 = feeds.get(0).getItems().get(0); + FeedItem feedItemByEpisodeUrl = DBReader.getFeedItemByGuidOrEpisodeUrl(null, + item1.getMedia().getDownload_url()); + assertEquals(item1.getItemIdentifier(), feedItemByEpisodeUrl.getItemIdentifier()); + } - NavDrawerData navDrawerData = DBReader.getNavDrawerData(); - assertEquals(numFeeds, navDrawerData.items.size()); - assertEquals(numNew, navDrawerData.numNewItems); - assertEquals(numQueue, navDrawerData.queueSize); - } + @Test + public void testGetItemByGuid() { + List<Feed> feeds = saveFeedlist(1, 1, true); + FeedItem item1 = feeds.get(0).getItems().get(0); - @Test - public void testGetFeedItemlistCheckChaptersFalse() { - List<Feed> feeds = DbTestUtils.saveFeedlist(10, 10, false, false, 0); - for (Feed feed : feeds) { - for (FeedItem item : feed.getItems()) { - assertFalse(item.hasChapters()); - } + FeedItem feedItemByGuid = DBReader.getFeedItemByGuidOrEpisodeUrl(item1.getItemIdentifier(), + item1.getMedia().getDownload_url()); + assertEquals(item1.getItemIdentifier(), feedItemByGuid.getItemIdentifier()); } - } - @Test - public void testGetFeedItemlistCheckChaptersTrue() { - List<Feed> feeds = saveFeedlist(10, 10, false, true, 10); - for (Feed feed : feeds) { - for (FeedItem item : feed.getItems()) { - assertTrue(item.hasChapters()); - } - } } - @Test - public void testLoadChaptersOfFeedItemNoChapters() { - List<Feed> feeds = saveFeedlist(1, 3, false, false, 0); - saveFeedlist(1, 3, false, true, 3); - for (Feed feed : feeds) { - for (FeedItem item : feed.getItems()) { - assertFalse(item.hasChapters()); - item.setChapters(DBReader.loadChaptersOfFeedItem(item)); - assertFalse(item.hasChapters()); - assertNull(item.getChapters()); + @RunWith(ParameterizedRobolectricTestRunner.class) + public static class PlaybackHistoryTest extends TestBase { + + private int paramOffset; + private int paramLimit; + + @ParameterizedRobolectricTestRunner.Parameters + public static Collection<Object[]> data() { + List<Integer> limits = Arrays.asList(1, 20, 100); + List<Integer> offsets = Arrays.asList(0, 10, 20); + Object[][] rv = new Object[limits.size() * offsets.size()][2]; + int i = 0; + for (int offset : offsets) { + for (int limit : limits) { + rv[i][0] = offset; + rv[i][1] = limit; + i++; + } } - } - } - @Test - public void testLoadChaptersOfFeedItemWithChapters() { - final int numChapters = 3; - DbTestUtils.saveFeedlist(1, 3, false, false, 0); - List<Feed> feeds = saveFeedlist(1, 3, false, true, numChapters); - for (Feed feed : feeds) { - for (FeedItem item : feed.getItems()) { - assertTrue(item.hasChapters()); - item.setChapters(DBReader.loadChaptersOfFeedItem(item)); - assertTrue(item.hasChapters()); - assertNotNull(item.getChapters()); - assertEquals(numChapters, item.getChapters().size()); - } + return Arrays.asList(rv); } - } - - @Test - public void testGetItemWithChapters() { - final int numChapters = 3; - List<Feed> feeds = saveFeedlist(1, 1, false, true, numChapters); - FeedItem item1 = feeds.get(0).getItems().get(0); - FeedItem item2 = DBReader.getFeedItem(item1.getId()); - item2.setChapters(DBReader.loadChaptersOfFeedItem(item2)); - assertTrue(item2.hasChapters()); - assertEquals(item1.getChapters(), item2.getChapters()); - } - @Test - public void testGetItemByEpisodeUrl() { - List<Feed> feeds = saveFeedlist(1, 1, true); - FeedItem item1 = feeds.get(0).getItems().get(0); - FeedItem feedItemByEpisodeUrl = DBReader.getFeedItemByGuidOrEpisodeUrl(null, - item1.getMedia().getDownload_url()); - assertEquals(item1.getItemIdentifier(), feedItemByEpisodeUrl.getItemIdentifier()); - } + public PlaybackHistoryTest(int offset, int limit) { + this.paramOffset = offset; + this.paramLimit = limit; - @Test - public void testGetItemByGuid() { - List<Feed> feeds = saveFeedlist(1, 1, true); - FeedItem item1 = feeds.get(0).getItems().get(0); + } - FeedItem feedItemByGuid = DBReader.getFeedItemByGuidOrEpisodeUrl(item1.getItemIdentifier(), - item1.getMedia().getDownload_url()); - assertEquals(item1.getItemIdentifier(), feedItemByGuid.getItemIdentifier()); + @Test + public void testGetPlaybackHistory() { + final int numItems = (paramLimit + 1) * 2; + final int playedItems = paramLimit + 1; + final int numReturnedItems = Math.min(Math.max(playedItems - paramOffset, 0), paramLimit); + final int numFeeds = 1; + + Feed feed = DbTestUtils.saveFeedlist(numFeeds, numItems, true).get(0); + long[] ids = new long[playedItems]; + + PodDBAdapter adapter = PodDBAdapter.getInstance(); + adapter.open(); + for (int i = 0; i < playedItems; i++) { + FeedMedia m = feed.getItems().get(i).getMedia(); + m.setPlaybackCompletionDate(new Date(i + 1)); + adapter.setFeedMediaPlaybackCompletionDate(m); + ids[ids.length - 1 - i] = m.getItem().getId(); + } + adapter.close(); + + List<FeedItem> saved = DBReader.getPlaybackHistory(paramOffset, paramLimit); + assertNotNull(saved); + assertEquals(String.format("Wrong size with offset %d and limit %d: ", + paramOffset, paramLimit), + numReturnedItems, saved.size()); + for (int i = 0; i < numReturnedItems; i++) { + FeedItem item = saved.get(i); + assertNotNull(item.getMedia().getPlaybackCompletionDate()); + assertEquals(String.format("Wrong sort order with offset %d and limit %d: ", + paramOffset, paramLimit), + item.getId(), ids[paramOffset + i]); + } + } } } 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 41c8e051a..453d1c184 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 @@ -1076,18 +1076,23 @@ public class PodDBAdapter { * Returns a cursor which contains feed media objects with a playback * completion date in ascending order. * + * @param offset The row to start at. * @param limit The maximum row count of the returned cursor. Must be an * integer >= 0. * @throws IllegalArgumentException if limit < 0 */ - public final Cursor getCompletedMediaCursor(int limit) { + public final Cursor getCompletedMediaCursor(int offset, int limit) { if (limit < 0) { throw new IllegalArgumentException("Limit must be >= 0"); } return db.query(TABLE_NAME_FEED_MEDIA, null, KEY_PLAYBACK_COMPLETION_DATE + " > 0", null, null, - null, String.format(Locale.US, "%s DESC LIMIT %d", KEY_PLAYBACK_COMPLETION_DATE, limit)); + null, String.format(Locale.US, "%s DESC LIMIT %d, %d", KEY_PLAYBACK_COMPLETION_DATE, offset, limit)); + } + + public final long getCompletedMediaLength() { + return DatabaseUtils.queryNumEntries(db, TABLE_NAME_FEED_MEDIA, KEY_PLAYBACK_COMPLETION_DATE + "> 0"); } public final Cursor getSingleFeedMediaCursor(long id) { |