Split up filter model and database handling
This commit is contained in:
parent
cdf59a1c8e
commit
f610ceffc2
|
@ -11,15 +11,11 @@ import org.junit.Rule;
|
|||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import androidx.test.espresso.intent.rule.IntentsTestRule;
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.activity.MainActivity;
|
||||
import de.danoeh.antennapod.core.feed.FeedItem;
|
||||
import de.danoeh.antennapod.core.storage.DBReader;
|
||||
import de.danoeh.antennapod.fragment.EpisodesFragment;
|
||||
import de.test.antennapod.EspressoTestUtils;
|
||||
import de.test.antennapod.ui.UITestUtils;
|
||||
|
@ -70,7 +66,6 @@ public class ShareDialogTest {
|
|||
onView(withText(R.string.all_episodes_short_label)).perform(click());
|
||||
|
||||
Matcher<View> allEpisodesMatcher;
|
||||
final List<FeedItem> episodes = DBReader.getRecentlyPublishedEpisodes(0, 10);
|
||||
allEpisodesMatcher = Matchers.allOf(withId(android.R.id.list), isDisplayed(), hasMinimumChildCount(2));
|
||||
onView(isRoot()).perform(waitForView(allEpisodesMatcher, 1000));
|
||||
onView(allEpisodesMatcher).perform(actionOnItemAtPosition(0, click()));
|
||||
|
|
|
@ -10,6 +10,7 @@ import androidx.test.filters.LargeTest;
|
|||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
import androidx.test.rule.ActivityTestRule;
|
||||
|
||||
import de.danoeh.antennapod.core.feed.FeedItemFilter;
|
||||
import org.awaitility.Awaitility;
|
||||
import org.hamcrest.Matcher;
|
||||
import org.junit.After;
|
||||
|
@ -252,7 +253,7 @@ public class PlaybackTest {
|
|||
onView(isRoot()).perform(waitForView(withText(R.string.all_episodes_short_label), 1000));
|
||||
onView(withText(R.string.all_episodes_short_label)).perform(click());
|
||||
|
||||
final List<FeedItem> episodes = DBReader.getRecentlyPublishedEpisodes(0, 10);
|
||||
final List<FeedItem> episodes = DBReader.getRecentlyPublishedEpisodes(0, 10, FeedItemFilter.unfiltered());
|
||||
Matcher<View> allEpisodesMatcher = allOf(withId(android.R.id.list), isDisplayed(), hasMinimumChildCount(2));
|
||||
onView(isRoot()).perform(waitForView(allEpisodesMatcher, 1000));
|
||||
onView(allEpisodesMatcher).perform(actionOnItemAtPosition(0, clickChildViewWithId(R.id.secondaryActionButton)));
|
||||
|
@ -287,7 +288,7 @@ public class PlaybackTest {
|
|||
uiTestUtils.addLocalFeedData(true);
|
||||
DBWriter.clearQueue().get();
|
||||
activityTestRule.launchActivity(new Intent());
|
||||
final List<FeedItem> episodes = DBReader.getRecentlyPublishedEpisodes(0, 10);
|
||||
final List<FeedItem> episodes = DBReader.getRecentlyPublishedEpisodes(0, 10, FeedItemFilter.unfiltered());
|
||||
|
||||
startLocalPlayback();
|
||||
FeedMedia media = episodes.get(0).getMedia();
|
||||
|
|
|
@ -104,12 +104,12 @@ public class AllEpisodesFragment extends EpisodesListFragment {
|
|||
@NonNull
|
||||
@Override
|
||||
protected List<FeedItem> loadData() {
|
||||
return DBReader.getRecentlyPublishedEpisodesFiltered(0, page * EPISODES_PER_PAGE, feedItemFilter);
|
||||
return DBReader.getRecentlyPublishedEpisodes(0, page * EPISODES_PER_PAGE, feedItemFilter);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected List<FeedItem> loadMoreData() {
|
||||
return DBReader.getRecentlyPublishedEpisodesFiltered((page - 1) * EPISODES_PER_PAGE, EPISODES_PER_PAGE, feedItemFilter);
|
||||
return DBReader.getRecentlyPublishedEpisodes((page - 1) * EPISODES_PER_PAGE, EPISODES_PER_PAGE, feedItemFilter);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,127 +3,68 @@ package de.danoeh.antennapod.core.feed;
|
|||
import android.text.TextUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import de.danoeh.antennapod.core.storage.DBReader;
|
||||
import de.danoeh.antennapod.core.storage.PodDBAdapter;
|
||||
import de.danoeh.antennapod.core.util.LongList;
|
||||
|
||||
import static de.danoeh.antennapod.core.feed.FeedItem.TAG_FAVORITE;
|
||||
|
||||
public class FeedItemFilter {
|
||||
|
||||
private final String[] mProperties;
|
||||
private final String mQuery;
|
||||
private final String[] properties;
|
||||
|
||||
private boolean showPlayed = false;
|
||||
private boolean showUnplayed = false;
|
||||
private boolean showPaused = false;
|
||||
private boolean showNotPaused = false;
|
||||
private boolean showQueued = false;
|
||||
private boolean showNotQueued = false;
|
||||
private boolean showDownloaded = false;
|
||||
private boolean showNotDownloaded = false;
|
||||
private boolean showHasMedia = false;
|
||||
private boolean showNoMedia = false;
|
||||
private boolean showIsFavorite = false;
|
||||
private boolean showNotFavorite = false;
|
||||
public final boolean showPlayed;
|
||||
public final boolean showUnplayed;
|
||||
public final boolean showPaused;
|
||||
public final boolean showNotPaused;
|
||||
public final boolean showQueued;
|
||||
public final boolean showNotQueued;
|
||||
public final boolean showDownloaded;
|
||||
public final boolean showNotDownloaded;
|
||||
public final boolean showHasMedia;
|
||||
public final boolean showNoMedia;
|
||||
public final boolean showIsFavorite;
|
||||
public final boolean showNotFavorite;
|
||||
|
||||
public static FeedItemFilter unfiltered() {
|
||||
return new FeedItemFilter("");
|
||||
}
|
||||
|
||||
public FeedItemFilter(String properties) {
|
||||
this(TextUtils.split(properties, ","));
|
||||
}
|
||||
|
||||
public FeedItemFilter(String[] properties) {
|
||||
this.mProperties = properties;
|
||||
for (String property : properties) {
|
||||
// see R.arrays.feed_filter_values
|
||||
switch (property) {
|
||||
case "unplayed":
|
||||
showUnplayed = true;
|
||||
break;
|
||||
case "paused":
|
||||
showPaused = true;
|
||||
break;
|
||||
case "not_paused":
|
||||
showNotPaused = true;
|
||||
break;
|
||||
case "played":
|
||||
showPlayed = true;
|
||||
break;
|
||||
case "queued":
|
||||
showQueued = true;
|
||||
break;
|
||||
case "not_queued":
|
||||
showNotQueued = true;
|
||||
break;
|
||||
case "downloaded":
|
||||
showDownloaded = true;
|
||||
break;
|
||||
case "not_downloaded":
|
||||
showNotDownloaded = true;
|
||||
break;
|
||||
case "has_media":
|
||||
showHasMedia = true;
|
||||
break;
|
||||
case "no_media":
|
||||
showNoMedia = true;
|
||||
break;
|
||||
case "is_favorite":
|
||||
showIsFavorite = true;
|
||||
break;
|
||||
case "not_favorite":
|
||||
showNotFavorite = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.properties = properties;
|
||||
|
||||
mQuery = makeQuery();
|
||||
// see R.arrays.feed_filter_values
|
||||
showUnplayed = hasProperty("unplayed");
|
||||
showPaused = hasProperty("paused");
|
||||
showNotPaused = hasProperty("not_paused");
|
||||
showPlayed = hasProperty("played");
|
||||
showQueued = hasProperty("queued");
|
||||
showNotQueued = hasProperty("not_queued");
|
||||
showDownloaded = hasProperty("downloaded");
|
||||
showNotDownloaded = hasProperty("not_downloaded");
|
||||
showHasMedia = hasProperty("has_media");
|
||||
showNoMedia = hasProperty("no_media");
|
||||
showIsFavorite = hasProperty("is_favorite");
|
||||
showNotFavorite = hasProperty("not_favorite");
|
||||
}
|
||||
|
||||
private String makeQuery() {
|
||||
// The keys used within this method, but explicitly combined with their table
|
||||
String keyRead = PodDBAdapter.TABLE_NAME_FEED_ITEMS + "." + PodDBAdapter.KEY_READ;
|
||||
String keyPosition = PodDBAdapter.TABLE_NAME_FEED_MEDIA + "." + PodDBAdapter.KEY_POSITION;
|
||||
String keyDownloaded = PodDBAdapter.TABLE_NAME_FEED_MEDIA + "." + PodDBAdapter.KEY_DOWNLOADED;
|
||||
String keyMediaId = PodDBAdapter.TABLE_NAME_FEED_MEDIA + "." + PodDBAdapter.KEY_ID;
|
||||
String keyItemId = PodDBAdapter.TABLE_NAME_FEED_ITEMS + "." + PodDBAdapter.KEY_ID;
|
||||
String keyFeedItem = PodDBAdapter.KEY_FEEDITEM;
|
||||
String tableQueue = PodDBAdapter.TABLE_NAME_QUEUE;
|
||||
String tableFavorites = PodDBAdapter.TABLE_NAME_FAVORITES;
|
||||
|
||||
List<String> statements = new ArrayList<>();
|
||||
if (showPlayed) statements.add(keyRead + " = 1 ");
|
||||
if (showUnplayed) statements.add(" NOT " + keyRead + " = 1 "); // Match "New" items (read = -1) as well
|
||||
if (showPaused) statements.add(" (" + keyPosition + " NOT NULL AND " + keyPosition + " > 0 " + ") ");
|
||||
if (showNotPaused) statements.add(" (" + keyPosition + " IS NULL OR " + keyPosition + " = 0 " + ") ");
|
||||
if (showQueued) statements.add(keyItemId + " IN (SELECT " + keyFeedItem + " FROM " + tableQueue + ") ");
|
||||
if (showNotQueued) statements.add(keyItemId + " NOT IN (SELECT " + keyFeedItem + " FROM " + tableQueue + ") ");
|
||||
if (showDownloaded) statements.add(keyDownloaded + " = 1 ");
|
||||
if (showNotDownloaded) statements.add(keyDownloaded + " = 0 ");
|
||||
if (showHasMedia) statements.add(keyMediaId + " NOT NULL ");
|
||||
if (showNoMedia) statements.add(keyMediaId + " IS NULL ");
|
||||
if (showIsFavorite) statements.add(keyItemId + " IN (SELECT " + keyFeedItem + " FROM " + tableFavorites + ") ");
|
||||
if (showNotFavorite) statements.add(keyItemId + " NOT IN (SELECT " + keyFeedItem + " FROM " + tableFavorites + ") ");
|
||||
|
||||
if (statements.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
StringBuilder query = new StringBuilder(" (" + statements.get(0));
|
||||
for (String r : statements.subList(1, statements.size())) {
|
||||
query.append(" AND ");
|
||||
query.append(r);
|
||||
}
|
||||
query.append(") ");
|
||||
return query.toString();
|
||||
private boolean hasProperty(String property) {
|
||||
return Arrays.asList(properties).contains(property);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a list of feed items through the filter.
|
||||
*/
|
||||
public List<FeedItem> filter(List<FeedItem> items) {
|
||||
if(mProperties.length == 0) return items;
|
||||
if (properties.length == 0) {
|
||||
return items;
|
||||
}
|
||||
|
||||
List<FeedItem> result = new ArrayList<>();
|
||||
|
||||
|
@ -164,23 +105,11 @@ public class FeedItemFilter {
|
|||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Express this filter using an SQL boolean statement that can be inserted into an SQL WHERE clause
|
||||
* to yield output filtered according to the rules of this filter.
|
||||
*
|
||||
* @return An SQL boolean statement that matches the desired items,
|
||||
* empty string if there is nothing to filter
|
||||
*/
|
||||
public String getQuery() {
|
||||
return mQuery;
|
||||
}
|
||||
|
||||
public String[] getValues() {
|
||||
return mProperties.clone();
|
||||
return properties.clone();
|
||||
}
|
||||
|
||||
public boolean isShowDownloaded() {
|
||||
return showDownloaded;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -366,27 +366,6 @@ public final class DBReader {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a list of FeedItems sorted by pubDate in descending order.
|
||||
*
|
||||
* @param offset The first episode that should be loaded.
|
||||
* @param limit The maximum number of episodes that should be loaded.
|
||||
*/
|
||||
@NonNull
|
||||
public static List<FeedItem> getRecentlyPublishedEpisodes(int offset, int limit) {
|
||||
Log.d(TAG, "getRecentlyPublishedEpisodes() called with: " + "offset = [" + offset + "]" + " limit = [" + limit + "]" );
|
||||
|
||||
PodDBAdapter adapter = PodDBAdapter.getInstance();
|
||||
adapter.open();
|
||||
try (Cursor cursor = adapter.getRecentlyPublishedItemsCursor(offset, limit)) {
|
||||
List<FeedItem> items = extractItemlistFromCursor(adapter, cursor);
|
||||
loadAdditionalFeedItemListData(items);
|
||||
return items;
|
||||
} finally {
|
||||
adapter.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a filtered list of FeedItems sorted by pubDate in descending order.
|
||||
*
|
||||
|
@ -395,14 +374,12 @@ public final class DBReader {
|
|||
* @param filter The filter describing which episodes to filter out.
|
||||
*/
|
||||
@NonNull
|
||||
public static List<FeedItem> getRecentlyPublishedEpisodesFiltered(int offset, int limit,
|
||||
FeedItemFilter filter) {
|
||||
Log.d(TAG, "getRecentlyPublishedEpisodesFiltered() called with: "
|
||||
+ "offset = [" + offset + "]" + " limit = [" + limit + "]" + " filter = [" + filter.getQuery() + "]");
|
||||
public static List<FeedItem> getRecentlyPublishedEpisodes(int offset, int limit, FeedItemFilter filter) {
|
||||
Log.d(TAG, "getRecentlyPublishedEpisodes() called with: offset=" + offset + ", limit=" + limit);
|
||||
|
||||
PodDBAdapter adapter = PodDBAdapter.getInstance();
|
||||
adapter.open();
|
||||
try (Cursor cursor = adapter.getRecentlyPublishedItemsCursorFiltered(offset, limit, filter)) {
|
||||
try (Cursor cursor = adapter.getRecentlyPublishedItemsCursor(offset, limit, filter)) {
|
||||
List<FeedItem> items = extractItemlistFromCursor(adapter, cursor);
|
||||
loadAdditionalFeedItemListData(items);
|
||||
return items;
|
||||
|
|
|
@ -16,6 +16,7 @@ import android.util.Log;
|
|||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import de.danoeh.antennapod.core.storage.mapper.FeedItemFilterQuery;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
|
||||
import java.io.File;
|
||||
|
@ -1045,16 +1046,9 @@ public class PodDBAdapter {
|
|||
return db.rawQuery(query, null);
|
||||
}
|
||||
|
||||
public final Cursor getRecentlyPublishedItemsCursor(int offset, int limit) {
|
||||
final String query = SELECT_FEED_ITEMS_AND_MEDIA
|
||||
+ "ORDER BY " + KEY_PUBDATE + " DESC LIMIT " + offset + ", " + limit;
|
||||
return db.rawQuery(query, null);
|
||||
}
|
||||
|
||||
public final Cursor getRecentlyPublishedItemsCursorFiltered(int offset, int limit,
|
||||
FeedItemFilter filter) {
|
||||
String filterQuery = filter.getQuery();
|
||||
String whereClause = "".equals(filterQuery) ? "" : " WHERE " + filter.getQuery();
|
||||
public final Cursor getRecentlyPublishedItemsCursor(int offset, int limit, FeedItemFilter filter) {
|
||||
String filterQuery = FeedItemFilterQuery.generateFrom(filter);
|
||||
String whereClause = "".equals(filterQuery) ? "" : " WHERE " + filterQuery;
|
||||
final String query = SELECT_FEED_ITEMS_AND_MEDIA + whereClause
|
||||
+ " ORDER BY " + KEY_PUBDATE + " DESC LIMIT " + offset + ", " + limit;
|
||||
return db.rawQuery(query, null);
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
package de.danoeh.antennapod.core.storage.mapper;
|
||||
|
||||
import de.danoeh.antennapod.core.feed.FeedItemFilter;
|
||||
import de.danoeh.antennapod.core.storage.PodDBAdapter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class FeedItemFilterQuery {
|
||||
private FeedItemFilterQuery() {
|
||||
// Must not be instantiated
|
||||
}
|
||||
|
||||
/**
|
||||
* Express the filter using an SQL boolean statement that can be inserted into an SQL WHERE clause
|
||||
* to yield output filtered according to the rules of this filter.
|
||||
*
|
||||
* @return An SQL boolean statement that matches the desired items,
|
||||
* empty string if there is nothing to filter
|
||||
*/
|
||||
public static String generateFrom(FeedItemFilter filter) {
|
||||
// The keys used within this method, but explicitly combined with their table
|
||||
String keyRead = PodDBAdapter.TABLE_NAME_FEED_ITEMS + "." + PodDBAdapter.KEY_READ;
|
||||
String keyPosition = PodDBAdapter.TABLE_NAME_FEED_MEDIA + "." + PodDBAdapter.KEY_POSITION;
|
||||
String keyDownloaded = PodDBAdapter.TABLE_NAME_FEED_MEDIA + "." + PodDBAdapter.KEY_DOWNLOADED;
|
||||
String keyMediaId = PodDBAdapter.TABLE_NAME_FEED_MEDIA + "." + PodDBAdapter.KEY_ID;
|
||||
String keyItemId = PodDBAdapter.TABLE_NAME_FEED_ITEMS + "." + PodDBAdapter.KEY_ID;
|
||||
String keyFeedItem = PodDBAdapter.KEY_FEEDITEM;
|
||||
String tableQueue = PodDBAdapter.TABLE_NAME_QUEUE;
|
||||
String tableFavorites = PodDBAdapter.TABLE_NAME_FAVORITES;
|
||||
|
||||
List<String> statements = new ArrayList<>();
|
||||
if (filter.showPlayed) {
|
||||
statements.add(keyRead + " = 1 ");
|
||||
} else if (filter.showUnplayed) {
|
||||
statements.add(" NOT " + keyRead + " = 1 "); // Match "New" items (read = -1) as well
|
||||
}
|
||||
if (filter.showPaused) {
|
||||
statements.add(" (" + keyPosition + " NOT NULL AND " + keyPosition + " > 0 " + ") ");
|
||||
} else if (filter.showNotPaused) {
|
||||
statements.add(" (" + keyPosition + " IS NULL OR " + keyPosition + " = 0 " + ") ");
|
||||
}
|
||||
if (filter.showQueued) {
|
||||
statements.add(keyItemId + " IN (SELECT " + keyFeedItem + " FROM " + tableQueue + ") ");
|
||||
} else if (filter.showNotQueued) {
|
||||
statements.add(keyItemId + " NOT IN (SELECT " + keyFeedItem + " FROM " + tableQueue + ") ");
|
||||
}
|
||||
if (filter.showDownloaded) {
|
||||
statements.add(keyDownloaded + " = 1 ");
|
||||
} else if (filter.showNotDownloaded) {
|
||||
statements.add(keyDownloaded + " = 0 ");
|
||||
}
|
||||
if (filter.showHasMedia) {
|
||||
statements.add(keyMediaId + " NOT NULL ");
|
||||
} else if (filter.showNoMedia) {
|
||||
statements.add(keyMediaId + " IS NULL ");
|
||||
}
|
||||
if (filter.showIsFavorite) {
|
||||
statements.add(keyItemId + " IN (SELECT " + keyFeedItem + " FROM " + tableFavorites + ") ");
|
||||
} else if (filter.showNotFavorite) {
|
||||
statements.add(keyItemId + " NOT IN (SELECT " + keyFeedItem + " FROM " + tableFavorites + ") ");
|
||||
}
|
||||
|
||||
if (statements.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
StringBuilder query = new StringBuilder(" (" + statements.get(0));
|
||||
for (String r : statements.subList(1, statements.size())) {
|
||||
query.append(" AND ");
|
||||
query.append(r);
|
||||
}
|
||||
query.append(") ");
|
||||
return query.toString();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue