Searching descriptions should now work

This commit is contained in:
daniel oeh 2012-10-28 15:19:29 +01:00
parent a51e371fa4
commit 1cac6a2de9
4 changed files with 181 additions and 84 deletions

View File

@ -149,7 +149,7 @@ public class SearchActivity extends SherlockListActivity {
protected ArrayList<SearchResult> doInBackground(String... params) { protected ArrayList<SearchResult> doInBackground(String... params) {
if (AppConfig.DEBUG) if (AppConfig.DEBUG)
Log.d(TAG, "Starting background work"); Log.d(TAG, "Starting background work");
return FeedSearcher.performSearch(params[0], selectedFeed); return FeedSearcher.performSearch(SearchActivity.this, params[0], selectedFeed);
} }
@Override @Override

View File

@ -1012,6 +1012,19 @@ public class FeedManager {
return null; return null;
} }
/** Get a FeedItem by its id and the id of its feed. */
public FeedItem getFeedItem(long itemId, long feedId) {
Feed feed = getFeed(feedId);
if (feed != null && feed.getItems() != null) {
for (FeedItem item : feed.getItems()) {
if (item.getId() == itemId) {
return item;
}
}
}
return null;
}
/** Get a FeedMedia object by the id of the Media object and the feed object */ /** Get a FeedMedia object by the id of the Media object and the feed object */
public FeedMedia getFeedMedia(long id, Feed feed) { public FeedMedia getFeedMedia(long id, Feed feed) {
if (feed != null) { if (feed != null) {
@ -1346,32 +1359,28 @@ public class FeedManager {
public void searchFeedItemDescription(final Context context, public void searchFeedItemDescription(final Context context,
final Feed feed, final String query, final Feed feed, final String query,
FeedManager.TaskCallback callback) { FeedManager.QueryTaskCallback callback) {
dbExec.execute(new FeedManager.Task(new Handler(), callback) { dbExec.execute(new FeedManager.QueryTask(context, new Handler(), callback) {
@Override @Override
public void execute() { public void execute(PodDBAdapter adapter) {
PodDBAdapter adapter = new PodDBAdapter(context); Cursor searchResult = adapter.searchItemDescriptions(feed,
adapter.open(); query);
Cursor searchResult = adapter.searchItemDescriptions(feed, query);
setResult(searchResult); setResult(searchResult);
adapter.close();
} }
}); });
} }
public void searchFeedItemContentEncoded(final Context context, public void searchFeedItemContentEncoded(final Context context,
final Feed feed, final String query, final Feed feed, final String query,
FeedManager.TaskCallback callback) { FeedManager.QueryTaskCallback callback) {
dbExec.execute(new FeedManager.Task(new Handler(), callback) { dbExec.execute(new FeedManager.QueryTask(context, new Handler(), callback) {
@Override @Override
public void execute() { public void execute(PodDBAdapter adapter) {
PodDBAdapter adapter = new PodDBAdapter(context); Cursor searchResult = adapter.searchItemContentEncoded(feed,
adapter.open(); query);
Cursor searchResult = adapter.searchItemContentEncoded(feed, query);
setResult(searchResult); setResult(searchResult);
adapter.close();
} }
}); });
} }
@ -1400,15 +1409,18 @@ public class FeedManager {
public interface TaskCallback { public interface TaskCallback {
void onCompletion(Cursor result); void onCompletion(Cursor result);
} }
/** Is called by a FeedManager.QueryTask after completion. */
public interface QueryTaskCallback {
void handleResult(Cursor result);
void onCompletion();
}
/** A runnable that can post a callback to a handler after completion. */ /** A runnable that can post a callback to a handler after completion. */
abstract class Task implements Runnable { abstract class Task implements Runnable {
private Handler handler; private Handler handler;
private TaskCallback callback; private TaskCallback callback;
/** Can be used for returning database query results. */
private Cursor result;
/** /**
* Standard contructor. No callbacks are going to be posted to a * Standard contructor. No callbacks are going to be posted to a
* handler. * handler.
@ -1434,7 +1446,7 @@ public class FeedManager {
handler.post(new Runnable() { handler.post(new Runnable() {
@Override @Override
public void run() { public void run() {
callback.onCompletion(result); callback.onCompletion(null);
} }
}); });
} }
@ -1442,11 +1454,53 @@ public class FeedManager {
/** This method will be executed in the same thread as the run() method. */ /** This method will be executed in the same thread as the run() method. */
public abstract void execute(); public abstract void execute();
}
/**
* A runnable which should be used for database queries. The onCompletion
* method is executed on the database executor to handle Cursors correctly.
* This class automatically creates a PodDBAdapter object and closes it when
* it is no longer in use.
*/
abstract class QueryTask implements Runnable {
private QueryTaskCallback callback;
private Cursor result;
private Context context;
private Handler handler;
public QueryTask(Context context, Handler handler, QueryTaskCallback callback) {
this.callback = callback;
this.context = context;
this.handler = handler;
}
@Override
public final void run() {
PodDBAdapter adapter = new PodDBAdapter(context);
adapter.open();
execute(adapter);
callback.handleResult(result);
if (result != null && !result.isClosed()) {
result.close();
}
adapter.close();
if (handler != null && callback != null) {
handler.post(new Runnable() {
@Override
public void run() {
callback.onCompletion();
}
});
}
}
public abstract void execute(PodDBAdapter adapter);
protected void setResult(Cursor c) { protected void setResult(Cursor c) {
result = c; result = c;
} }
} }
} }

View File

@ -5,10 +5,14 @@ import java.util.Collections;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import android.content.Context;
import android.database.Cursor;
import android.os.Looper;
import android.util.Log; import android.util.Log;
import de.danoeh.antennapod.AppConfig; import de.danoeh.antennapod.AppConfig;
import de.danoeh.antennapod.PodcastApp; import de.danoeh.antennapod.PodcastApp;
import de.danoeh.antennapod.R; import de.danoeh.antennapod.R;
import de.danoeh.antennapod.storage.PodDBAdapter;
import de.danoeh.antennapod.util.comparator.SearchResultValueComparator; import de.danoeh.antennapod.util.comparator.SearchResultValueComparator;
/** Performs search on Feeds and FeedItems */ /** Performs search on Feeds and FeedItems */
@ -23,10 +27,10 @@ public class FeedSearcher {
private static final int VALUE_WORD_MATCH = 4; private static final int VALUE_WORD_MATCH = 4;
/** Performs a search in all feeds or one specific feed. */ /** Performs a search in all feeds or one specific feed. */
public static ArrayList<SearchResult> performSearch(final String query, public static ArrayList<SearchResult> performSearch(final Context context,
Feed selectedFeed) { final String query, final Feed selectedFeed) {
String lcQuery = query.toLowerCase(); final String lcQuery = query.toLowerCase();
ArrayList<SearchResult> result = new ArrayList<SearchResult>(); final ArrayList<SearchResult> result = new ArrayList<SearchResult>();
if (selectedFeed == null) { if (selectedFeed == null) {
if (AppConfig.DEBUG) if (AppConfig.DEBUG)
Log.d(TAG, "Performing global search"); Log.d(TAG, "Performing global search");
@ -45,14 +49,40 @@ public class FeedSearcher {
Log.d(TAG, "Searching item-chaptertitles"); Log.d(TAG, "Searching item-chaptertitles");
searchFeedItemChapters(lcQuery, result, selectedFeed); searchFeedItemChapters(lcQuery, result, selectedFeed);
if (AppConfig.DEBUG) final FeedManager manager = FeedManager.getInstance();
Log.d(TAG, "Searching item descriptions"); Looper.prepare();
searchFeedItemDescription(lcQuery, result, selectedFeed); manager.searchFeedItemDescription(context, selectedFeed, lcQuery,
new FeedManager.QueryTaskCallback() {
if (AppConfig.DEBUG) @Override
Log.d(TAG, "Searching item content encoded data"); public void handleResult(Cursor cResult) {
searchFeedItemContentEncoded(lcQuery, result, selectedFeed); searchFeedItemContentEncodedCursor(lcQuery, result,
selectedFeed, cResult);
}
@Override
public void onCompletion() {
manager.searchFeedItemContentEncoded(context,
selectedFeed, lcQuery,
new FeedManager.QueryTaskCallback() {
@Override
public void handleResult(Cursor cResult) {
searchFeedItemDescriptionCursor(
lcQuery, result, selectedFeed,
cResult);
}
@Override
public void onCompletion() {
Looper.myLooper().quit();
}
});
}
});
Looper.loop();
if (AppConfig.DEBUG) if (AppConfig.DEBUG)
Log.d(TAG, "Sorting results"); Log.d(TAG, "Sorting results");
Collections.sort(result, new SearchResultValueComparator()); Collections.sort(result, new SearchResultValueComparator());
@ -127,61 +157,74 @@ public class FeedSearcher {
} }
} }
private static void searchFeedItemDescription(String query, private static void searchFeedItemDescriptionCursor(String query,
ArrayList<SearchResult> destination, Feed selectedFeed) { ArrayList<SearchResult> destination, Feed feed, Cursor cursor) {
FeedManager manager = FeedManager.getInstance(); FeedManager manager = FeedManager.getInstance();
if (selectedFeed == null) { if (cursor.moveToFirst()) {
for (Feed feed : manager.getFeeds()) { do {
searchFeedItemDescriptionSingleFeed(query, destination, feed); final long itemId = cursor
} .getLong(PodDBAdapter.IDX_FI_EXTRA_ID);
} else { String content = cursor
searchFeedItemDescriptionSingleFeed(query, destination, .getString(PodDBAdapter.IDX_FI_EXTRA_DESCRIPTION);
selectedFeed); if (content != null) {
} content = content.toLowerCase();
} final long feedId = cursor
.getLong(PodDBAdapter.IDX_FI_EXTRA_FEED);
FeedItem item = null;
if (feed == null) {
item = manager.getFeedItem(itemId, feedId);
} else {
item = manager.getFeedItem(itemId, feed);
}
if (item != null) {
SearchResult searchResult = createSearchResult(item,
query, content, VALUE_ITEM_DESCRIPTION);
if (searchResult != null) {
searchResult.setSubtitle(PodcastApp.getInstance()
.getString(
R.string.found_in_shownotes_label));
destination.add(searchResult);
private static void searchFeedItemDescriptionSingleFeed(String query, }
ArrayList<SearchResult> destination, Feed feed) { }
for (FeedItem item : feed.getItems()) {
if (item.getDescription() != null) {
SearchResult result = createSearchResult(item, query, item
.getDescription().toLowerCase(), VALUE_ITEM_DESCRIPTION);
if (result != null) {
result.setSubtitle(PodcastApp.getInstance().getString(
R.string.found_in_shownotes_label));
destination.add(result);
} }
} } while (cursor.moveToNext());
} }
} }
private static void searchFeedItemContentEncoded(String query, private static void searchFeedItemContentEncodedCursor(String query,
ArrayList<SearchResult> destination, Feed selectedFeed) { ArrayList<SearchResult> destination, Feed feed, Cursor cursor) {
FeedManager manager = FeedManager.getInstance(); FeedManager manager = FeedManager.getInstance();
if (selectedFeed == null) { if (cursor.moveToFirst()) {
for (Feed feed : manager.getFeeds()) { do {
searchFeedItemContentEncodedSingleFeed(query, destination, feed); final long itemId = cursor
} .getLong(PodDBAdapter.IDX_FI_EXTRA_ID);
} else { String content = cursor
searchFeedItemContentEncodedSingleFeed(query, destination, .getString(PodDBAdapter.IDX_FI_EXTRA_CONTENT_ENCODED);
selectedFeed); if (content != null) {
} content = content.toLowerCase();
}
private static void searchFeedItemContentEncodedSingleFeed(String query, final long feedId = cursor
ArrayList<SearchResult> destination, Feed feed) { .getLong(PodDBAdapter.IDX_FI_EXTRA_FEED);
for (FeedItem item : feed.getItems()) { FeedItem item = null;
if (!destination.contains(item) && item.getContentEncoded() != null) { if (feed == null) {
SearchResult result = createSearchResult(item, query, item item = manager.getFeedItem(itemId, feedId);
.getContentEncoded().toLowerCase(), } else {
VALUE_ITEM_DESCRIPTION); item = manager.getFeedItem(itemId, feed);
if (result != null) { }
result.setSubtitle(PodcastApp.getInstance().getString( if (item != null) {
R.string.found_in_shownotes_label)); SearchResult searchResult = createSearchResult(item,
destination.add(result); query, content, VALUE_ITEM_DESCRIPTION);
if (searchResult != null) {
searchResult.setSubtitle(PodcastApp.getInstance()
.getString(
R.string.found_in_shownotes_label));
destination.add(searchResult);
}
}
} }
} } while (cursor.moveToNext());
} }
} }

View File

@ -660,12 +660,12 @@ public class PodDBAdapter {
if (feed != null) { if (feed != null) {
// search items in specific feed // search items in specific feed
return db.query(TABLE_NAME_FEED_ITEMS, SEL_FI_EXTRA, KEY_FEED return db.query(TABLE_NAME_FEED_ITEMS, SEL_FI_EXTRA, KEY_FEED
+ "=? AND " + KEY_DESCRIPTION + " LIKE ?", new String[] { + "=? AND " + KEY_DESCRIPTION + " LIKE '%" + query + "%'", new String[] {
String.valueOf(feed.getId()), query }, null, null, null); String.valueOf(feed.getId()) }, null, null, null);
} else { } else {
// search through all items // search through all items
return db.query(TABLE_NAME_FEED_ITEMS, SEL_FI_EXTRA, return db.query(TABLE_NAME_FEED_ITEMS, SEL_FI_EXTRA,
KEY_DESCRIPTION + " LIKE ?", new String[] { query }, null, KEY_DESCRIPTION + " LIKE '%" + query + "%'", null, null,
null, null); null, null);
} }
} }
@ -680,13 +680,13 @@ public class PodDBAdapter {
if (feed != null) { if (feed != null) {
// search items in specific feed // search items in specific feed
return db.query(TABLE_NAME_FEED_ITEMS, SEL_FI_EXTRA, KEY_FEED return db.query(TABLE_NAME_FEED_ITEMS, SEL_FI_EXTRA, KEY_FEED
+ "=? AND " + KEY_CONTENT_ENCODED + " LIKE ?", + "=? AND " + KEY_CONTENT_ENCODED + " LIKE '%" + query + "%'",
new String[] { String.valueOf(feed.getId()), query }, null, new String[] { String.valueOf(feed.getId())}, null,
null, null); null, null);
} else { } else {
// search through all items // search through all items
return db.query(TABLE_NAME_FEED_ITEMS, SEL_FI_EXTRA, return db.query(TABLE_NAME_FEED_ITEMS, SEL_FI_EXTRA,
KEY_CONTENT_ENCODED + " LIKE ?", new String[] { query }, KEY_CONTENT_ENCODED + " LIKE '%" + query + "%'", null,
null, null, null); null, null, null);
} }
} }