diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/SearchlistAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/SearchlistAdapter.java index 45cb4af87..ad23478d6 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/SearchlistAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/SearchlistAdapter.java @@ -93,10 +93,7 @@ public class SearchlistAdapter extends BaseAdapter { } else if (component.getClass() == FeedItem.class) { final FeedItem item = (FeedItem) component; holder.title.setText(item.getTitle()); - if (result.getSubtitle() != null) { - holder.subtitle.setVisibility(View.VISIBLE); - holder.subtitle.setText(result.getSubtitle()); - } + holder.subtitle.setText(result.getLocation().getDescription()); convertView.setAlpha(item.isPlayed() ? 0.5f : 1.0f); diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/SearchResult.java b/core/src/main/java/de/danoeh/antennapod/core/feed/SearchResult.java index ea8eb7871..062a6abac 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/feed/SearchResult.java +++ b/core/src/main/java/de/danoeh/antennapod/core/feed/SearchResult.java @@ -1,34 +1,22 @@ package de.danoeh.antennapod.core.feed; +import de.danoeh.antennapod.core.storage.SearchLocation; + public class SearchResult { - private final FeedComponent component; - /** Additional information (e.g. where it was found) */ - private String subtitle; - /** Higher value means more importance */ - private final int value; + private final FeedComponent component; + private SearchLocation location; - public SearchResult(FeedComponent component, int value, String subtitle) { - super(); - this.component = component; - this.value = value; - this.subtitle = subtitle; - } + public SearchResult(FeedComponent component, SearchLocation location) { + super(); + this.component = component; + this.location = location; + } - public FeedComponent getComponent() { - return component; - } - - public String getSubtitle() { - return subtitle; - } - - public void setSubtitle(String subtitle) { - this.subtitle = subtitle; - } - - public int getValue() { - return value; - } - + public FeedComponent getComponent() { + return component; + } + public SearchLocation getLocation() { + return location; + } } diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java index c4a06efa2..65e6bbbca 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java @@ -543,140 +543,20 @@ public final class DBTasks { } /** - * Searches the titles of FeedItems of a specific Feed for a given - * string. + * Searches the FeedItems of a specific Feed for a given string. * * @param context Used for accessing the DB. * @param feedID The id of the feed whose items should be searched. * @param query The search string. - * @return A FutureTask object that executes the search request and returns the search result as a List of FeedItems. + * @return A FutureTask object that executes the search request + * and returns the search result as a List of FeedItems. */ - public static FutureTask> searchFeedItemTitle(final Context context, + public static FutureTask> searchFeedItems(final Context context, final long feedID, final String query) { return new FutureTask<>(new QueryTask>(context) { @Override public void execute(PodDBAdapter adapter) { - Cursor searchResult = adapter.searchItemTitles(feedID, - query); - List items = DBReader.extractItemlistFromCursor(searchResult); - DBReader.loadAdditionalFeedItemListData(items); - setResult(items); - searchResult.close(); - } - }); - } - - /** - * Searches the authors of FeedItems of a specific Feed for a given - * string. - * - * @param context Used for accessing the DB. - * @param feedID The id of the feed whose items should be searched. - * @param query The search string. - * @return A FutureTask object that executes the search request and returns the search result as a List of FeedItems. - */ - public static FutureTask> searchFeedItemAuthor(final Context context, - final long feedID, final String query) { - return new FutureTask<>(new QueryTask>(context) { - @Override - public void execute(PodDBAdapter adapter) { - Cursor searchResult = adapter.searchItemAuthors(feedID, - query); - List items = DBReader.extractItemlistFromCursor(searchResult); - DBReader.loadAdditionalFeedItemListData(items); - setResult(items); - searchResult.close(); - } - }); - } - - /** - * Searches the feed identifiers of FeedItems of a specific Feed for a given - * string. - * - * @param context Used for accessing the DB. - * @param feedID The id of the feed whose items should be searched. - * @param query The search string. - * @return A FutureTask object that executes the search request and returns the search result as a List of FeedItems. - */ - public static FutureTask> searchFeedItemFeedIdentifier(final Context context, - final long feedID, final String query) { - return new FutureTask<>(new QueryTask>(context) { - @Override - public void execute(PodDBAdapter adapter) { - Cursor searchResult = adapter.searchItemFeedIdentifiers(feedID, - query); - List items = DBReader.extractItemlistFromCursor(searchResult); - DBReader.loadAdditionalFeedItemListData(items); - setResult(items); - searchResult.close(); - } - }); - } - - /** - * Searches the descriptions of FeedItems of a specific Feed for a given - * string. - * - * @param context Used for accessing the DB. - * @param feedID The id of the feed whose items should be searched. - * @param query The search string - * @return A FutureTask object that executes the search request and returns the search result as a List of FeedItems. - */ - public static FutureTask> searchFeedItemDescription(final Context context, - final long feedID, final String query) { - return new FutureTask<>(new QueryTask>(context) { - @Override - public void execute(PodDBAdapter adapter) { - Cursor searchResult = adapter.searchItemDescriptions(feedID, - query); - List items = DBReader.extractItemlistFromCursor(searchResult); - DBReader.loadAdditionalFeedItemListData(items); - setResult(items); - searchResult.close(); - } - }); - } - - /** - * Searches the contentEncoded-value of FeedItems of a specific Feed for a given - * string. - * - * @param context Used for accessing the DB. - * @param feedID The id of the feed whose items should be searched. - * @param query The search string - * @return A FutureTask object that executes the search request and returns the search result as a List of FeedItems. - */ - public static FutureTask> searchFeedItemContentEncoded(final Context context, - final long feedID, final String query) { - return new FutureTask<>(new QueryTask>(context) { - @Override - public void execute(PodDBAdapter adapter) { - Cursor searchResult = adapter.searchItemContentEncoded(feedID, - query); - List items = DBReader.extractItemlistFromCursor(searchResult); - DBReader.loadAdditionalFeedItemListData(items); - setResult(items); - searchResult.close(); - } - }); - } - - /** - * Searches chapters of the FeedItems of a specific Feed for a given string. - * - * @param context Used for accessing the DB. - * @param feedID The id of the feed whose items should be searched. - * @param query The search string - * @return A FutureTask object that executes the search request and returns the search result as a List of FeedItems. - */ - public static FutureTask> searchFeedItemChapters(final Context context, - final long feedID, final String query) { - return new FutureTask<>(new QueryTask>(context) { - @Override - public void execute(PodDBAdapter adapter) { - Cursor searchResult = adapter.searchItemChapters(feedID, - query); + Cursor searchResult = adapter.searchItems(feedID, query); List items = DBReader.extractItemlistFromCursor(searchResult); DBReader.loadAdditionalFeedItemListData(items); setResult(items); diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/FeedSearcher.java b/core/src/main/java/de/danoeh/antennapod/core/storage/FeedSearcher.java index 0c8d20007..1d9e33d0e 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/FeedSearcher.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/FeedSearcher.java @@ -3,6 +3,7 @@ package de.danoeh.antennapod.core.storage; import android.content.Context; import androidx.annotation.NonNull; +import de.danoeh.antennapod.core.feed.Chapter; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; @@ -17,13 +18,12 @@ import de.danoeh.antennapod.core.feed.SearchResult; import de.danoeh.antennapod.core.util.comparator.InReverseChronologicalOrder; /** - * Performs search on Feeds and FeedItems + * Performs search on Feeds and FeedItems. */ public class FeedSearcher { - private FeedSearcher(){} - - private static final String TAG = "FeedSearcher"; + private FeedSearcher() { + } /** * Search through a feed, or all feeds, for episodes that match the query in either the title, @@ -31,52 +31,54 @@ public class FeedSearcher { * show notes. The list of resulting episodes also describes where the first match occurred * (title, chapters, or show notes). * - * @param context + * @param context Used for database access * @param query search query * @param selectedFeed feed to search, 0 to search through all feeds * @return list of episodes containing the query */ @NonNull - public static List performSearch(final Context context, - final String query, final long selectedFeed) { - final int values[] = {2, 1, 0, 0, 0, 0}; - final String[] subtitles = {context.getString(R.string.found_in_title_label), - context.getString(R.string.found_in_chapters_label), - context.getString(R.string.found_in_shownotes_label), - context.getString(R.string.found_in_shownotes_label), - context.getString(R.string.found_in_authors_label), - context.getString(R.string.found_in_feeds_label)}; - + public static List performSearch(final Context context, final String query, final long selectedFeed) { final List result = new ArrayList<>(); - - List>> tasks = new ArrayList<>(); - tasks.add(DBTasks.searchFeedItemTitle(context, selectedFeed, query)); - tasks.add(DBTasks.searchFeedItemChapters(context, selectedFeed, query)); - tasks.add(DBTasks.searchFeedItemDescription(context, selectedFeed, query)); - tasks.add(DBTasks.searchFeedItemContentEncoded(context, selectedFeed, query)); - tasks.add(DBTasks.searchFeedItemAuthor(context, selectedFeed, query)); - tasks.add(DBTasks.searchFeedItemFeedIdentifier(context, selectedFeed, query)); - - for (FutureTask> task : tasks) { - task.run(); - } try { - Set set = new HashSet<>(); - - for (int i = 0; i < tasks.size(); i++) { - FutureTask> task = tasks.get(i); - List items = task.get(); - for (FeedItem item : items) { - if (!set.contains(item.getId())) { // to prevent duplicate results - result.add(new SearchResult(item, values[i], subtitles[i])); - set.add(item.getId()); - } + FutureTask> searchTask = DBTasks.searchFeedItems(context, selectedFeed, query); + searchTask.run(); + final List items = searchTask.get(); + for (FeedItem item : items) { + SearchLocation location; + if (safeContains(item.getTitle(), query)) { + location = SearchLocation.TITLE; + } else if (safeContains(item.getContentEncoded(), query)) { + location = SearchLocation.SHOWNOTES; + } else if (safeContains(item.getDescription(), query)) { + location = SearchLocation.SHOWNOTES; + } else if (safeContains(item.getChapters(), query)) { + location = SearchLocation.CHAPTERS; + } else if (safeContains(item.getFeed().getAuthor(), query)) { + location = SearchLocation.AUTHORS; + } else { + location = SearchLocation.FEED; } + result.add(new SearchResult(item, location)); } } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } - Collections.sort(result, new InReverseChronologicalOrder()); return result; } + + private static boolean safeContains(String haystack, String needle) { + return haystack != null && haystack.contains(needle); + } + + private static boolean safeContains(List haystack, String needle) { + if (haystack == null) { + return false; + } + for (Chapter chapter : haystack) { + if (safeContains(chapter.getTitle(), needle)) { + return true; + } + } + return false; + } } diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java b/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java index 1aff73564..96c3a46da 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java @@ -1290,133 +1290,39 @@ public class PodDBAdapter { } /** - * Searches for the given query in the description of all items or the items + * Searches for the given query in various values of all items or the items * of a specified feed. * * @return A cursor with all search results in SEL_FI_EXTRA selection. */ - public Cursor searchItemDescriptions(long feedID, String query) { + public Cursor searchItems(long feedID, String searchQuery) { + String preparedQuery = prepareSearchQuery(searchQuery); + + String queryFeedId = ""; if (feedID != 0) { // search items in specific feed - return db.query(TABLE_NAME_FEED_ITEMS, FEEDITEM_SEL_FI_SMALL, KEY_FEED - + "=? AND " + KEY_DESCRIPTION + " LIKE '%" - + prepareSearchQuery(query) + "%'", - new String[]{String.valueOf(feedID)}, null, null, - null - ); + queryFeedId = KEY_FEED + " = " + feedID; } else { // search through all items - return db.query(TABLE_NAME_FEED_ITEMS, FEEDITEM_SEL_FI_SMALL, - KEY_DESCRIPTION + " LIKE '%" + prepareSearchQuery(query) - + "%'", null, null, null, null - ); + queryFeedId = "1 = 1"; } - } - /** - * Searches for the given query in the content-encoded field of all items or - * the items of a specified feed. - * - * @return A cursor with all search results in SEL_FI_EXTRA selection. - */ - public Cursor searchItemContentEncoded(long feedID, String query) { - if (feedID != 0) { - // search items in specific feed - return db.query(TABLE_NAME_FEED_ITEMS, FEEDITEM_SEL_FI_SMALL, KEY_FEED - + "=? AND " + KEY_CONTENT_ENCODED + " LIKE '%" - + prepareSearchQuery(query) + "%'", - new String[]{String.valueOf(feedID)}, null, null, - null - ); - } else { - // search through all items - return db.query(TABLE_NAME_FEED_ITEMS, FEEDITEM_SEL_FI_SMALL, - KEY_CONTENT_ENCODED + " LIKE '%" - + prepareSearchQuery(query) + "%'", null, null, - null, null - ); - } - } - - public Cursor searchItemTitles(long feedID, String query) { - if (feedID != 0) { - // search items in specific feed - return db.query(TABLE_NAME_FEED_ITEMS, FEEDITEM_SEL_FI_SMALL, KEY_FEED - + "=? AND " + KEY_TITLE + " LIKE '%" - + prepareSearchQuery(query) + "%'", - new String[]{String.valueOf(feedID)}, null, null, - null - ); - } else { - // search through all items - return db.query(TABLE_NAME_FEED_ITEMS, FEEDITEM_SEL_FI_SMALL, - KEY_TITLE + " LIKE '%" - + prepareSearchQuery(query) + "%'", null, null, - null, null - ); - } - } - - public Cursor searchItemAuthors(long feedID, String query) { - if (feedID != 0) { - // search items in specific feed - return db.rawQuery("SELECT " + TextUtils.join(", ", FEEDITEM_SEL_FI_SMALL) + " FROM " + TABLE_NAME_FEED_ITEMS - + " JOIN " + TABLE_NAME_FEEDS + " ON " + TABLE_NAME_FEED_ITEMS + "." + KEY_FEED + "=" + TABLE_NAME_FEEDS + "." + KEY_ID - + " WHERE " + KEY_FEED - + "=? AND " + KEY_AUTHOR + " LIKE '%" - + prepareSearchQuery(query) + "%' ORDER BY " - + TABLE_NAME_FEED_ITEMS + "." + KEY_PUBDATE + " DESC", - new String[]{String.valueOf(feedID)} - ); - } else { - // search through all items - return db.rawQuery("SELECT " + TextUtils.join(", ", FEEDITEM_SEL_FI_SMALL) + " FROM " + TABLE_NAME_FEED_ITEMS - + " JOIN " + TABLE_NAME_FEEDS + " ON " + TABLE_NAME_FEED_ITEMS + "." + KEY_FEED + "=" + TABLE_NAME_FEEDS + "." + KEY_ID - + " WHERE " + KEY_AUTHOR + " LIKE '%" - + prepareSearchQuery(query) + "%' ORDER BY " - + TABLE_NAME_FEED_ITEMS + "." + KEY_PUBDATE + " DESC", - null - ); - } - } - - public Cursor searchItemFeedIdentifiers(long feedID, String query) { - if (feedID != 0) { - // search items in specific feed - return db.rawQuery("SELECT " + TextUtils.join(", ", FEEDITEM_SEL_FI_SMALL) + " FROM " + TABLE_NAME_FEED_ITEMS - + " JOIN " + TABLE_NAME_FEEDS + " ON " + TABLE_NAME_FEED_ITEMS + "." + KEY_FEED + "=" + TABLE_NAME_FEEDS + "." + KEY_ID - + " WHERE " + KEY_FEED - + "=? AND " + KEY_FEED_IDENTIFIER + " LIKE '%" - + prepareSearchQuery(query) + "%' ORDER BY " - + TABLE_NAME_FEED_ITEMS + "." + KEY_PUBDATE + " DESC", - new String[]{String.valueOf(feedID)} - ); - } else { - // search through all items - return db.rawQuery("SELECT " + TextUtils.join(", ", FEEDITEM_SEL_FI_SMALL) + " FROM " + TABLE_NAME_FEED_ITEMS - + " JOIN " + TABLE_NAME_FEEDS + " ON " + TABLE_NAME_FEED_ITEMS + "." + KEY_FEED + "=" + TABLE_NAME_FEEDS + "." + KEY_ID - + " WHERE " + KEY_FEED_IDENTIFIER + " LIKE '%" - + prepareSearchQuery(query) + "%' ORDER BY " - + TABLE_NAME_FEED_ITEMS + "." + KEY_PUBDATE + " DESC", - null - ); - } - } - - public Cursor searchItemChapters(long feedID, String searchQuery) { - final String query; - if (feedID != 0) { - query = "SELECT " + SEL_FI_SMALL_STR + " FROM " + TABLE_NAME_FEED_ITEMS + " INNER JOIN " + - TABLE_NAME_SIMPLECHAPTERS + " ON " + TABLE_NAME_SIMPLECHAPTERS + "." + KEY_FEEDITEM + "=" + - TABLE_NAME_FEED_ITEMS + "." + KEY_ID + " WHERE " + TABLE_NAME_FEED_ITEMS + "." + KEY_FEED + "=" + - feedID + " AND " + TABLE_NAME_SIMPLECHAPTERS + "." + KEY_TITLE + " LIKE '%" - + prepareSearchQuery(searchQuery) + "%'"; - } else { - query = "SELECT " + SEL_FI_SMALL_STR + " FROM " + TABLE_NAME_FEED_ITEMS + " INNER JOIN " + - TABLE_NAME_SIMPLECHAPTERS + " ON " + TABLE_NAME_SIMPLECHAPTERS + "." + KEY_FEEDITEM + "=" + - TABLE_NAME_FEED_ITEMS + "." + KEY_ID + " WHERE " + TABLE_NAME_SIMPLECHAPTERS + "." + KEY_TITLE + " LIKE '%" - + prepareSearchQuery(searchQuery) + "%'"; - } + String query = "SELECT " + SEL_FI_SMALL_STR + " FROM " + TABLE_NAME_FEED_ITEMS + + " LEFT JOIN " + TABLE_NAME_SIMPLECHAPTERS + + " ON " + TABLE_NAME_SIMPLECHAPTERS + "." + KEY_FEEDITEM + + "=" + TABLE_NAME_FEED_ITEMS + "." + KEY_ID + + " LEFT JOIN " + TABLE_NAME_FEEDS + + " ON " + TABLE_NAME_FEED_ITEMS + "." + KEY_FEED + + "=" + TABLE_NAME_FEEDS + "." + KEY_ID + + " WHERE " + queryFeedId + " AND (" + + TABLE_NAME_FEED_ITEMS + "." + KEY_DESCRIPTION + " LIKE '%" + preparedQuery + "%' OR " + + TABLE_NAME_FEED_ITEMS + "." + KEY_CONTENT_ENCODED + " LIKE '%" + preparedQuery + "%' OR " + + TABLE_NAME_FEED_ITEMS + "." + KEY_TITLE + " LIKE '%" + preparedQuery + "%' OR " + + TABLE_NAME_SIMPLECHAPTERS + "." + KEY_TITLE + " LIKE '%" + preparedQuery + "%' OR " + + TABLE_NAME_FEEDS + "." + KEY_AUTHOR + " LIKE '%" + preparedQuery + "%' OR " + + TABLE_NAME_FEEDS + "." + KEY_FEED_IDENTIFIER + " LIKE '%" + preparedQuery + "%'" + + ") ORDER BY " + KEY_PUBDATE + " DESC " + + "LIMIT 500"; return db.rawQuery(query, null); } diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/SearchLocation.java b/core/src/main/java/de/danoeh/antennapod/core/storage/SearchLocation.java new file mode 100644 index 000000000..fabe85b2c --- /dev/null +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/SearchLocation.java @@ -0,0 +1,21 @@ +package de.danoeh.antennapod.core.storage; + +import androidx.annotation.StringRes; +import de.danoeh.antennapod.core.R; + +public enum SearchLocation { + TITLE(R.string.found_in_title_label), + CHAPTERS(R.string.found_in_chapters_label), + SHOWNOTES(R.string.found_in_shownotes_label), + AUTHORS(R.string.found_in_authors_label), + FEED(R.string.found_in_feeds_label); + + private int description; + SearchLocation(@StringRes int description) { + this.description = description; + } + + public @StringRes int getDescription() { + return description; + } +}