diff options
author | daniel oeh <daniel.oeh@gmail.com> | 2013-08-04 21:35:44 +0200 |
---|---|---|
committer | daniel oeh <daniel.oeh@gmail.com> | 2013-08-04 21:35:44 +0200 |
commit | 24c50f7840ffd6af0ff16aa1e73f43613696d637 (patch) | |
tree | 0930228f0048ddd713e41299d7441dcf4052b3d1 /src | |
parent | 355fc8114f61ed2ecde8f118c4d30d209ceb6198 (diff) | |
download | AntennaPod-24c50f7840ffd6af0ff16aa1e73f43613696d637.zip |
Ported search components to DB*-classes
Diffstat (limited to 'src')
-rw-r--r-- | src/de/danoeh/antennapod/PodcastApp.java | 3 | ||||
-rw-r--r-- | src/de/danoeh/antennapod/activity/MainActivity.java | 12 | ||||
-rw-r--r-- | src/de/danoeh/antennapod/activity/SearchActivity.java | 22 | ||||
-rw-r--r-- | src/de/danoeh/antennapod/feed/FeedManager.java | 21 | ||||
-rw-r--r-- | src/de/danoeh/antennapod/feed/FeedMedia.java | 13 | ||||
-rw-r--r-- | src/de/danoeh/antennapod/feed/SearchResult.java | 3 | ||||
-rw-r--r-- | src/de/danoeh/antennapod/storage/DBReader.java | 16 | ||||
-rw-r--r-- | src/de/danoeh/antennapod/storage/DBTasks.java | 1166 | ||||
-rw-r--r-- | src/de/danoeh/antennapod/storage/FeedSearcher.java (renamed from src/de/danoeh/antennapod/feed/FeedSearcher.java) | 138 | ||||
-rw-r--r-- | src/de/danoeh/antennapod/storage/PodDBAdapter.java | 69 |
10 files changed, 769 insertions, 694 deletions
diff --git a/src/de/danoeh/antennapod/PodcastApp.java b/src/de/danoeh/antennapod/PodcastApp.java index e9f46256b..2141f71e8 100644 --- a/src/de/danoeh/antennapod/PodcastApp.java +++ b/src/de/danoeh/antennapod/PodcastApp.java @@ -5,7 +5,6 @@ import android.content.res.Configuration; import android.util.Log; import de.danoeh.antennapod.asynctask.ImageLoader; import de.danoeh.antennapod.feed.EventDistributor; -import de.danoeh.antennapod.feed.FeedManager; import de.danoeh.antennapod.preferences.PlaybackPreferences; import de.danoeh.antennapod.preferences.UserPreferences; @@ -32,8 +31,6 @@ public class PodcastApp extends Application { UserPreferences.createInstance(this); PlaybackPreferences.createInstance(this); EventDistributor.getInstance(); - FeedManager manager = FeedManager.getInstance(); - manager.loadDBData(getApplicationContext()); } @Override diff --git a/src/de/danoeh/antennapod/activity/MainActivity.java b/src/de/danoeh/antennapod/activity/MainActivity.java index 410617b23..b42990224 100644 --- a/src/de/danoeh/antennapod/activity/MainActivity.java +++ b/src/de/danoeh/antennapod/activity/MainActivity.java @@ -22,13 +22,14 @@ import com.actionbarsherlock.view.Window; import de.danoeh.antennapod.AppConfig; import de.danoeh.antennapod.R; import de.danoeh.antennapod.feed.EventDistributor; -import de.danoeh.antennapod.feed.FeedManager; import de.danoeh.antennapod.fragment.EpisodesFragment; import de.danoeh.antennapod.fragment.ExternalPlayerFragment; import de.danoeh.antennapod.fragment.FeedlistFragment; import de.danoeh.antennapod.preferences.UserPreferences; import de.danoeh.antennapod.service.PlaybackService; import de.danoeh.antennapod.service.download.DownloadService; +import de.danoeh.antennapod.storage.DBReader; +import de.danoeh.antennapod.storage.DBTasks; import de.danoeh.antennapod.storage.DownloadRequester; import de.danoeh.antennapod.util.StorageUtils; @@ -39,7 +40,6 @@ public class MainActivity extends SherlockFragmentActivity { private static final int EVENTS = EventDistributor.DOWNLOAD_HANDLED | EventDistributor.DOWNLOAD_QUEUED; - private FeedManager manager; private ViewPager viewpager; private TabsAdapter pagerAdapter; private ExternalPlayerFragment externalPlayerFragment; @@ -51,7 +51,6 @@ public class MainActivity extends SherlockFragmentActivity { setTheme(UserPreferences.getTheme()); super.onCreate(savedInstanceState); StorageUtils.checkStorageAvailability(this); - manager = FeedManager.getInstance(); requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); setContentView(R.layout.main); @@ -80,7 +79,7 @@ public class MainActivity extends SherlockFragmentActivity { if (!appLaunched && getIntent().getAction() != null && getIntent().getAction().equals(Intent.ACTION_MAIN)) { appLaunched = true; - if (manager.getUnreadItemsSize(true) > 0) { + if (DBReader.getNumberOfUnreadItems(this) > 0) { // select 'episodes' tab getSupportActionBar().setSelectedNavigationItem(1); } @@ -142,7 +141,7 @@ public class MainActivity extends SherlockFragmentActivity { startActivity(new Intent(this, AddFeedActivity.class)); return true; case R.id.all_feed_refresh: - manager.refreshAllFeeds(this); + DBTasks.refreshAllFeeds(this, null); return true; case R.id.show_downloads: startActivity(new Intent(this, DownloadActivity.class)); @@ -173,9 +172,6 @@ public class MainActivity extends SherlockFragmentActivity { } else { refreshAll.setVisible(true); } - - boolean hasFeeds = manager.getFeedsSize() > 0; - menu.findItem(R.id.all_feed_refresh).setVisible(hasFeeds); return true; } diff --git a/src/de/danoeh/antennapod/activity/SearchActivity.java b/src/de/danoeh/antennapod/activity/SearchActivity.java index 152710112..6a20ed765 100644 --- a/src/de/danoeh/antennapod/activity/SearchActivity.java +++ b/src/de/danoeh/antennapod/activity/SearchActivity.java @@ -1,6 +1,6 @@ package de.danoeh.antennapod.activity; -import java.util.ArrayList; +import java.util.List; import android.annotation.SuppressLint; import android.app.SearchManager; @@ -20,8 +20,7 @@ import de.danoeh.antennapod.R; import de.danoeh.antennapod.adapter.SearchlistAdapter; import de.danoeh.antennapod.feed.Feed; import de.danoeh.antennapod.feed.FeedItem; -import de.danoeh.antennapod.feed.FeedManager; -import de.danoeh.antennapod.feed.FeedSearcher; +import de.danoeh.antennapod.storage.FeedSearcher; import de.danoeh.antennapod.feed.SearchResult; import de.danoeh.antennapod.fragment.FeedlistFragment; import de.danoeh.antennapod.fragment.ItemlistFragment; @@ -34,10 +33,10 @@ public class SearchActivity extends SherlockListActivity { public static final String EXTRA_FEED_ID = "de.danoeh.antennapod.searchactivity.extra.feedId"; private SearchlistAdapter searchAdapter; - private ArrayList<SearchResult> content; + private List<SearchResult> content; - /** Feed that is being searched or null if the search is global. */ - private Feed selectedFeed; + /** ID of the feed that is being searched or null if the search is global. */ + private long feedID; private TextView txtvStatus; @@ -65,8 +64,7 @@ public class SearchActivity extends SherlockListActivity { if (extra != null) { if (AppConfig.DEBUG) Log.d(TAG, "Found bundle extra"); - long feedId = extra.getLong(EXTRA_FEED_ID); - selectedFeed = FeedManager.getInstance().getFeed(feedId); + feedID = extra.getLong(EXTRA_FEED_ID); } if (AppConfig.DEBUG) Log.d(TAG, "Starting search"); @@ -109,9 +107,9 @@ public class SearchActivity extends SherlockListActivity { @Override public boolean onSearchRequested() { Bundle extra = null; - if (selectedFeed != null) { + if (feedID != 0) { extra = new Bundle(); - extra.putLong(EXTRA_FEED_ID, selectedFeed.getId()); + extra.putLong(EXTRA_FEED_ID, feedID); } startSearch(null, false, extra, false); return true; @@ -152,8 +150,8 @@ public class SearchActivity extends SherlockListActivity { @Override public void run() { Log.d(TAG, "Starting background work"); - final ArrayList<SearchResult> result = FeedSearcher - .performSearch(SearchActivity.this, query, selectedFeed); + final List<SearchResult> result = FeedSearcher + .performSearch(SearchActivity.this, query, feedID); if (SearchActivity.this != null) { SearchActivity.this.runOnUiThread(new Runnable() { diff --git a/src/de/danoeh/antennapod/feed/FeedManager.java b/src/de/danoeh/antennapod/feed/FeedManager.java index 343340a98..149f303cc 100644 --- a/src/de/danoeh/antennapod/feed/FeedManager.java +++ b/src/de/danoeh/antennapod/feed/FeedManager.java @@ -1715,8 +1715,10 @@ public class FeedManager { } }); } +/* - /** + */ +/** * Searches the descriptions of FeedItems of a specific feed for a given * string. * @@ -1726,7 +1728,8 @@ public class FeedManager { * The search string * @param callback * A callback which will be used to return the search result - * */ + * *//* + public void searchFeedItemDescription(final Context context, final Feed feed, final String query, FeedManager.QueryTaskCallback callback) { @@ -1741,8 +1744,13 @@ public class FeedManager { } }); } +*/ +/* - /** + *//* + +*/ +/** * Searches the 'contentEncoded' field of FeedItems of a specific feed for a * given string. * @@ -1752,7 +1760,10 @@ public class FeedManager { * The search string * @param callback * A callback which will be used to return the search result - * */ + * *//* +*/ +/* + public void searchFeedItemContentEncoded(final Context context, final Feed feed, final String query, FeedManager.QueryTaskCallback callback) { @@ -1767,6 +1778,8 @@ public class FeedManager { } }); } +*//* + /** Returns the number of feeds that are currently in the feeds list. */ public int getFeedsSize() { diff --git a/src/de/danoeh/antennapod/feed/FeedMedia.java b/src/de/danoeh/antennapod/feed/FeedMedia.java index 3b7a5ec4e..f9b9f9aa8 100644 --- a/src/de/danoeh/antennapod/feed/FeedMedia.java +++ b/src/de/danoeh/antennapod/feed/FeedMedia.java @@ -284,6 +284,14 @@ public class FeedMedia extends FeedFile implements Playable { } @Override + public String getPaymentLink() { + if (item == null) { + return null; + } + return getItem().getPaymentLink(); + } + + @Override public boolean localFileAvailable() { return isDownloaded() && file_url != null; } @@ -319,11 +327,6 @@ public class FeedMedia extends FeedFile implements Playable { } @Override - public String getPaymentLink() { - return getItem().getPaymentLink(); - } - - @Override public Callable<String> loadShownotes() { return new Callable<String>() { @Override diff --git a/src/de/danoeh/antennapod/feed/SearchResult.java b/src/de/danoeh/antennapod/feed/SearchResult.java index b4016f2e8..1cba389ec 100644 --- a/src/de/danoeh/antennapod/feed/SearchResult.java +++ b/src/de/danoeh/antennapod/feed/SearchResult.java @@ -7,10 +7,11 @@ public class SearchResult { /** Higher value means more importance */ private int value; - public SearchResult(FeedComponent component, int value) { + public SearchResult(FeedComponent component, int value, String subtitle) { super(); this.component = component; this.value = value; + this.subtitle = subtitle; } public FeedComponent getComponent() { diff --git a/src/de/danoeh/antennapod/storage/DBReader.java b/src/de/danoeh/antennapod/storage/DBReader.java index bf81ad7d4..5d6dd9756 100644 --- a/src/de/danoeh/antennapod/storage/DBReader.java +++ b/src/de/danoeh/antennapod/storage/DBReader.java @@ -108,6 +108,14 @@ public final class DBReader { return items; } + static List<FeedItem> extractItemlistFromCursor(Context context, Cursor itemlistCursor) { + PodDBAdapter adapter = new PodDBAdapter(context); + adapter.open(); + List<FeedItem> result = extractItemlistFromCursor(adapter, itemlistCursor); + adapter.close(); + return result; + } + private static List<FeedItem> extractItemlistFromCursor( PodDBAdapter adapter, Cursor itemlistCursor) { ArrayList<String> itemIds = new ArrayList<String>(); @@ -500,6 +508,14 @@ public final class DBReader { return result; } + public static int getNumberOfUnreadItems(final Context context) { + PodDBAdapter adapter = new PodDBAdapter(context); + adapter.open(); + final int result = adapter.getNumberOfUnreadItems(); + adapter.close(); + return result; + } + /** * Searches the DB for a FeedImage of the given id. * diff --git a/src/de/danoeh/antennapod/storage/DBTasks.java b/src/de/danoeh/antennapod/storage/DBTasks.java index 5853bbb02..bf615776d 100644 --- a/src/de/danoeh/antennapod/storage/DBTasks.java +++ b/src/de/danoeh/antennapod/storage/DBTasks.java @@ -7,7 +7,9 @@ import java.util.Comparator; import java.util.Date; import java.util.Iterator; import java.util.List; +import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; +import java.util.concurrent.FutureTask; import java.util.concurrent.locks.ReentrantLock; import android.content.Context; @@ -30,621 +32,593 @@ import de.danoeh.antennapod.util.QueueAccess; import de.danoeh.antennapod.util.exception.MediaFileNotFoundException; public final class DBTasks { - private static final String TAG = "DBTasks"; - - private DBTasks() { - } - - public static void playMedia(final Context context, final FeedMedia media, - boolean showPlayer, boolean startWhenPrepared, boolean shouldStream) { - try { - if (!shouldStream) { - if (media.fileExists() == false) { - throw new MediaFileNotFoundException( - "No episode was found at " + media.getFile_url(), - media); - } - } - // Start playback Service - Intent launchIntent = new Intent(context, PlaybackService.class); - launchIntent.putExtra(PlaybackService.EXTRA_PLAYABLE, media); - launchIntent.putExtra(PlaybackService.EXTRA_START_WHEN_PREPARED, - startWhenPrepared); - launchIntent.putExtra(PlaybackService.EXTRA_SHOULD_STREAM, - shouldStream); - launchIntent.putExtra(PlaybackService.EXTRA_PREPARE_IMMEDIATELY, - true); - context.startService(launchIntent); - if (showPlayer) { - // Launch Mediaplayer - context.startActivity(PlaybackService.getPlayerActivityIntent( - context, media)); - } - DBWriter.addQueueItemAt(context, media.getItem().getId(), 0, false); - } catch (MediaFileNotFoundException e) { - e.printStackTrace(); - if (media.isPlaying()) { - context.sendBroadcast(new Intent( - PlaybackService.ACTION_SHUTDOWN_PLAYBACK_SERVICE)); - } - notifyMissingFeedMediaFile(context, media); - } - } - - private static ReentrantLock refreshAllFeedsLock = new ReentrantLock(); - - public static void refreshAllFeeds(final Context context, - final List<Feed> feeds) { - if (refreshAllFeedsLock.tryLock()) { - new Thread() { - public void run() { - refreshFeeds(context, feeds); - refreshAllFeedsLock.unlock(); - } - }.start(); - } else { - if (AppConfig.DEBUG) - Log.d(TAG, - "Ignoring request to refresh all feeds: Refresh lock is locked"); - } - } - - public static void refreshExpiredFeeds(final Context context) { - if (AppConfig.DEBUG) - Log.d(TAG, "Refreshing expired feeds"); - - new Thread() { - public void run() { - long millis = UserPreferences.getUpdateInterval(); - - if (millis > 0) { - long now = Calendar.getInstance().getTime().getTime(); - - // Allow a 10 minute window - millis -= 10 * 60 * 1000; - List<Feed> feedList = DBReader.getExpiredFeedsList(context, - now - millis); - if (feedList.size() > 0) { - refreshFeeds(context, feedList); - } - } - } - }.start(); - } - - private static void refreshFeeds(final Context context, - final List<Feed> feedList) { - - for (Feed feed : feedList) { - try { - refreshFeed(context, feed); - } catch (DownloadRequestException e) { - e.printStackTrace(); - DBWriter.addDownloadStatus( - context, - new DownloadStatus(feed, feed - .getHumanReadableIdentifier(), - DownloadError.ERROR_REQUEST_ERROR, false, e - .getMessage())); - } - } - - } - - /** Updates a specific feed. */ - public static void refreshFeed(Context context, Feed feed) - throws DownloadRequestException { - DownloadRequester.getInstance().downloadFeed(context, - new Feed(feed.getDownload_url(), new Date(), feed.getTitle())); - } - - public static void notifyInvalidImageFile(final Context context, - final FeedImage image) { - Log.i(TAG, - "The feedmanager was notified about an invalid image download. It will now try to redownload the image file"); - try { - DownloadRequester.getInstance().downloadImage(context, image); - } catch (DownloadRequestException e) { - e.printStackTrace(); - Log.w(TAG, "Failed to download invalid feed image"); - } - } - - public static void notifyMissingFeedMediaFile(final Context context, - final FeedMedia media) { - Log.i(TAG, - "The feedmanager was notified about a missing episode. It will update its database now."); - media.setDownloaded(false); - media.setFile_url(null); - DBWriter.setFeedMedia(context, media); - EventDistributor.getInstance().sendFeedUpdateBroadcast(); - } - - public static void downloadAllItemsInQueue(final Context context) { - new Thread() { - public void run() { - List<FeedItem> queue = DBReader.getQueue(context); - if (!queue.isEmpty()) { - try { - downloadFeedItems(context, - queue.toArray(new FeedItem[queue.size()])); - } catch (DownloadRequestException e) { - e.printStackTrace(); - } - } - } - }.start(); - } - - public static void downloadFeedItems(final Context context, - FeedItem... items) throws DownloadRequestException { - downloadFeedItems(true, context, items); - } - - private static void downloadFeedItems(boolean performAutoCleanup, - final Context context, final FeedItem... items) - throws DownloadRequestException { - final DownloadRequester requester = DownloadRequester.getInstance(); - - if (performAutoCleanup) { - new Thread() { - - @Override - public void run() { - performAutoCleanup(context, - getPerformAutoCleanupArgs(context, items.length)); - } - - }.start(); - } - for (FeedItem item : items) { - if (item.getMedia() != null - && !requester.isDownloadingFile(item.getMedia()) - && !item.getMedia().isDownloaded()) { - if (items.length > 1) { - try { - requester.downloadMedia(context, item.getMedia()); - } catch (DownloadRequestException e) { - e.printStackTrace(); - DBWriter.addDownloadStatus(context, - new DownloadStatus(item.getMedia(), item - .getMedia() - .getHumanReadableIdentifier(), - DownloadError.ERROR_REQUEST_ERROR, - false, e.getMessage())); - } - } else { - requester.downloadMedia(context, item.getMedia()); - } - } - } - } - - private static int getNumberOfUndownloadedEpisodes( - final List<FeedItem> queue, final List<FeedItem> unreadItems) { - int counter = 0; - for (FeedItem item : queue) { - if (item.hasMedia() && !item.getMedia().isDownloaded() - && !item.getMedia().isPlaying()) { - counter++; - } - } - for (FeedItem item : unreadItems) { - if (item.hasMedia() && !item.getMedia().isDownloaded()) { - counter++; - } - } - return counter; - } - - public static void autodownloadUndownloadedItems(final Context context) { - if (AppConfig.DEBUG) - Log.d(TAG, "Performing auto-dl of undownloaded episodes"); - if (NetworkUtils.autodownloadNetworkAvailable(context) - && UserPreferences.isEnableAutodownload()) { - final List<FeedItem> queue = DBReader.getQueue(context); - final List<FeedItem> unreadItems = DBReader - .getUnreadItemsList(context); - - int undownloadedEpisodes = getNumberOfUndownloadedEpisodes(queue, - unreadItems); - int downloadedEpisodes = DBReader - .getNumberOfDownloadedEpisodes(context); - int deletedEpisodes = performAutoCleanup(context, - getPerformAutoCleanupArgs(context, undownloadedEpisodes)); - int episodeSpaceLeft = undownloadedEpisodes; - boolean cacheIsUnlimited = UserPreferences.getEpisodeCacheSize() == UserPreferences - .getEpisodeCacheSizeUnlimited(); - - if (!cacheIsUnlimited - && UserPreferences.getEpisodeCacheSize() < downloadedEpisodes - + undownloadedEpisodes) { - episodeSpaceLeft = UserPreferences.getEpisodeCacheSize() - - (downloadedEpisodes - deletedEpisodes); - } - - List<FeedItem> itemsToDownload = new ArrayList<FeedItem>(); - if (episodeSpaceLeft > 0 && undownloadedEpisodes > 0) { - for (int i = 0; i < queue.size(); i++) { // ignore playing item - FeedItem item = queue.get(i); - if (item.hasMedia() && !item.getMedia().isDownloaded() - && !item.getMedia().isPlaying()) { - itemsToDownload.add(item); - episodeSpaceLeft--; - undownloadedEpisodes--; - if (episodeSpaceLeft == 0 || undownloadedEpisodes == 0) { - break; - } - } - } - } - if (episodeSpaceLeft > 0 && undownloadedEpisodes > 0) { - for (FeedItem item : unreadItems) { - if (item.hasMedia() && !item.getMedia().isDownloaded()) { - itemsToDownload.add(item); - episodeSpaceLeft--; - undownloadedEpisodes--; - if (episodeSpaceLeft == 0 || undownloadedEpisodes == 0) { - break; - } - } - } - } - if (AppConfig.DEBUG) - Log.d(TAG, "Enqueueing " + itemsToDownload.size() - + " items for download"); - - try { - downloadFeedItems(false, context, - itemsToDownload.toArray(new FeedItem[itemsToDownload - .size()])); - } catch (DownloadRequestException e) { - e.printStackTrace(); - } - - } - } - - private static int getPerformAutoCleanupArgs(Context context, - final int episodeNumber) { - if (episodeNumber >= 0 - && UserPreferences.getEpisodeCacheSize() != UserPreferences - .getEpisodeCacheSizeUnlimited()) { - int downloadedEpisodes = DBReader - .getNumberOfDownloadedEpisodes(context); - if (downloadedEpisodes + episodeNumber >= UserPreferences - .getEpisodeCacheSize()) { - - return downloadedEpisodes + episodeNumber - - UserPreferences.getEpisodeCacheSize(); - } - } - return 0; - } - - public static void performAutoCleanup(final Context context) { - performAutoCleanup(context, getPerformAutoCleanupArgs(context, 0)); - } - - private static int performAutoCleanup(final Context context, - final int episodeNumber) { - List<FeedItem> candidates = DBReader.getDownloadedItems(context); - List<FeedItem> queue = DBReader.getQueue(context); - List<FeedItem> delete; - for (FeedItem item : candidates) { - if (item.hasMedia() && item.getMedia().isDownloaded() - && !queue.contains(item) && item.isRead()) { - candidates.add(item); - } - - } - - Collections.sort(candidates, new Comparator<FeedItem>() { - @Override - public int compare(FeedItem lhs, FeedItem rhs) { - Date l = lhs.getMedia().getPlaybackCompletionDate(); - Date r = rhs.getMedia().getPlaybackCompletionDate(); - - if (l == null) { - l = new Date(0); - } - if (r == null) { - r = new Date(0); - } - return l.compareTo(r); - } - }); - - if (candidates.size() > episodeNumber) { - delete = candidates.subList(0, episodeNumber); - } else { - delete = candidates; - } - - for (FeedItem item : delete) { - DBWriter.deleteFeedMediaOfItem(context, item.getId()); - } - - int counter = delete.size(); - - if (AppConfig.DEBUG) - Log.d(TAG, String.format( - "Auto-delete deleted %d episodes (%d requested)", counter, - episodeNumber)); - - return counter; - } - - public static void enqueueAllNewItems(final Context context) { - long[] unreadItems = DBReader.getUnreadItemIds(context); - DBWriter.addQueueItem(context, unreadItems); - } - - public static FeedItem getQueueSuccessorOfItem(Context context, - final long itemId, List<FeedItem> queue) { - FeedItem result = null; + private static final String TAG = "DBTasks"; + + private DBTasks() { + } + + public static void playMedia(final Context context, final FeedMedia media, + boolean showPlayer, boolean startWhenPrepared, boolean shouldStream) { + try { + if (!shouldStream) { + if (media.fileExists() == false) { + throw new MediaFileNotFoundException( + "No episode was found at " + media.getFile_url(), + media); + } + } + // Start playback Service + Intent launchIntent = new Intent(context, PlaybackService.class); + launchIntent.putExtra(PlaybackService.EXTRA_PLAYABLE, media); + launchIntent.putExtra(PlaybackService.EXTRA_START_WHEN_PREPARED, + startWhenPrepared); + launchIntent.putExtra(PlaybackService.EXTRA_SHOULD_STREAM, + shouldStream); + launchIntent.putExtra(PlaybackService.EXTRA_PREPARE_IMMEDIATELY, + true); + context.startService(launchIntent); + if (showPlayer) { + // Launch Mediaplayer + context.startActivity(PlaybackService.getPlayerActivityIntent( + context, media)); + } + DBWriter.addQueueItemAt(context, media.getItem().getId(), 0, false); + } catch (MediaFileNotFoundException e) { + e.printStackTrace(); + if (media.isPlaying()) { + context.sendBroadcast(new Intent( + PlaybackService.ACTION_SHUTDOWN_PLAYBACK_SERVICE)); + } + notifyMissingFeedMediaFile(context, media); + } + } + + private static ReentrantLock refreshAllFeedsLock = new ReentrantLock(); + + public static void refreshAllFeeds(final Context context, + final List<Feed> feeds) { + if (refreshAllFeedsLock.tryLock()) { + new Thread() { + public void run() { + if (feeds != null) { + refreshFeeds(context, feeds); + } else { + refreshFeeds(context, DBReader.getFeedList(context)); + } + refreshAllFeedsLock.unlock(); + } + }.start(); + } else { + if (AppConfig.DEBUG) + Log.d(TAG, + "Ignoring request to refresh all feeds: Refresh lock is locked"); + } + } + + public static void refreshExpiredFeeds(final Context context) { + if (AppConfig.DEBUG) + Log.d(TAG, "Refreshing expired feeds"); + + new Thread() { + public void run() { + long millis = UserPreferences.getUpdateInterval(); + + if (millis > 0) { + long now = Calendar.getInstance().getTime().getTime(); + + // Allow a 10 minute window + millis -= 10 * 60 * 1000; + List<Feed> feedList = DBReader.getExpiredFeedsList(context, + now - millis); + if (feedList.size() > 0) { + refreshFeeds(context, feedList); + } + } + } + }.start(); + } + + private static void refreshFeeds(final Context context, + final List<Feed> feedList) { + + for (Feed feed : feedList) { + try { + refreshFeed(context, feed); + } catch (DownloadRequestException e) { + e.printStackTrace(); + DBWriter.addDownloadStatus( + context, + new DownloadStatus(feed, feed + .getHumanReadableIdentifier(), + DownloadError.ERROR_REQUEST_ERROR, false, e + .getMessage())); + } + } + + } + + /** + * Updates a specific feed. + */ + public static void refreshFeed(Context context, Feed feed) + throws DownloadRequestException { + DownloadRequester.getInstance().downloadFeed(context, + new Feed(feed.getDownload_url(), new Date(), feed.getTitle())); + } + + public static void notifyInvalidImageFile(final Context context, + final FeedImage image) { + Log.i(TAG, + "The feedmanager was notified about an invalid image download. It will now try to redownload the image file"); + try { + DownloadRequester.getInstance().downloadImage(context, image); + } catch (DownloadRequestException e) { + e.printStackTrace(); + Log.w(TAG, "Failed to download invalid feed image"); + } + } + + public static void notifyMissingFeedMediaFile(final Context context, + final FeedMedia media) { + Log.i(TAG, + "The feedmanager was notified about a missing episode. It will update its database now."); + media.setDownloaded(false); + media.setFile_url(null); + DBWriter.setFeedMedia(context, media); + EventDistributor.getInstance().sendFeedUpdateBroadcast(); + } + + public static void downloadAllItemsInQueue(final Context context) { + new Thread() { + public void run() { + List<FeedItem> queue = DBReader.getQueue(context); + if (!queue.isEmpty()) { + try { + downloadFeedItems(context, + queue.toArray(new FeedItem[queue.size()])); + } catch (DownloadRequestException e) { + e.printStackTrace(); + } + } + } + }.start(); + } + + public static void downloadFeedItems(final Context context, + FeedItem... items) throws DownloadRequestException { + downloadFeedItems(true, context, items); + } + + private static void downloadFeedItems(boolean performAutoCleanup, + final Context context, final FeedItem... items) + throws DownloadRequestException { + final DownloadRequester requester = DownloadRequester.getInstance(); + + if (performAutoCleanup) { + new Thread() { + + @Override + public void run() { + performAutoCleanup(context, + getPerformAutoCleanupArgs(context, items.length)); + } + + }.start(); + } + for (FeedItem item : items) { + if (item.getMedia() != null + && !requester.isDownloadingFile(item.getMedia()) + && !item.getMedia().isDownloaded()) { + if (items.length > 1) { + try { + requester.downloadMedia(context, item.getMedia()); + } catch (DownloadRequestException e) { + e.printStackTrace(); + DBWriter.addDownloadStatus(context, + new DownloadStatus(item.getMedia(), item + .getMedia() + .getHumanReadableIdentifier(), + DownloadError.ERROR_REQUEST_ERROR, + false, e.getMessage())); + } + } else { + requester.downloadMedia(context, item.getMedia()); + } + } + } + } + + private static int getNumberOfUndownloadedEpisodes( + final List<FeedItem> queue, final List<FeedItem> unreadItems) { + int counter = 0; + for (FeedItem item : queue) { + if (item.hasMedia() && !item.getMedia().isDownloaded() + && !item.getMedia().isPlaying()) { + counter++; + } + } + for (FeedItem item : unreadItems) { + if (item.hasMedia() && !item.getMedia().isDownloaded()) { + counter++; + } + } + return counter; + } + + public static void autodownloadUndownloadedItems(final Context context) { + if (AppConfig.DEBUG) + Log.d(TAG, "Performing auto-dl of undownloaded episodes"); + if (NetworkUtils.autodownloadNetworkAvailable(context) + && UserPreferences.isEnableAutodownload()) { + final List<FeedItem> queue = DBReader.getQueue(context); + final List<FeedItem> unreadItems = DBReader + .getUnreadItemsList(context); + + int undownloadedEpisodes = getNumberOfUndownloadedEpisodes(queue, + unreadItems); + int downloadedEpisodes = DBReader + .getNumberOfDownloadedEpisodes(context); + int deletedEpisodes = performAutoCleanup(context, + getPerformAutoCleanupArgs(context, undownloadedEpisodes)); + int episodeSpaceLeft = undownloadedEpisodes; + boolean cacheIsUnlimited = UserPreferences.getEpisodeCacheSize() == UserPreferences + .getEpisodeCacheSizeUnlimited(); + + if (!cacheIsUnlimited + && UserPreferences.getEpisodeCacheSize() < downloadedEpisodes + + undownloadedEpisodes) { + episodeSpaceLeft = UserPreferences.getEpisodeCacheSize() + - (downloadedEpisodes - deletedEpisodes); + } + + List<FeedItem> itemsToDownload = new ArrayList<FeedItem>(); + if (episodeSpaceLeft > 0 && undownloadedEpisodes > 0) { + for (int i = 0; i < queue.size(); i++) { // ignore playing item + FeedItem item = queue.get(i); + if (item.hasMedia() && !item.getMedia().isDownloaded() + && !item.getMedia().isPlaying()) { + itemsToDownload.add(item); + episodeSpaceLeft--; + undownloadedEpisodes--; + if (episodeSpaceLeft == 0 || undownloadedEpisodes == 0) { + break; + } + } + } + } + if (episodeSpaceLeft > 0 && undownloadedEpisodes > 0) { + for (FeedItem item : unreadItems) { + if (item.hasMedia() && !item.getMedia().isDownloaded()) { + itemsToDownload.add(item); + episodeSpaceLeft--; + undownloadedEpisodes--; + if (episodeSpaceLeft == 0 || undownloadedEpisodes == 0) { + break; + } + } + } + } + if (AppConfig.DEBUG) + Log.d(TAG, "Enqueueing " + itemsToDownload.size() + + " items for download"); + + try { + downloadFeedItems(false, context, + itemsToDownload.toArray(new FeedItem[itemsToDownload + .size()])); + } catch (DownloadRequestException e) { + e.printStackTrace(); + } + + } + } + + private static int getPerformAutoCleanupArgs(Context context, + final int episodeNumber) { + if (episodeNumber >= 0 + && UserPreferences.getEpisodeCacheSize() != UserPreferences + .getEpisodeCacheSizeUnlimited()) { + int downloadedEpisodes = DBReader + .getNumberOfDownloadedEpisodes(context); + if (downloadedEpisodes + episodeNumber >= UserPreferences + .getEpisodeCacheSize()) { + + return downloadedEpisodes + episodeNumber + - UserPreferences.getEpisodeCacheSize(); + } + } + return 0; + } + + public static void performAutoCleanup(final Context context) { + performAutoCleanup(context, getPerformAutoCleanupArgs(context, 0)); + } + + private static int performAutoCleanup(final Context context, + final int episodeNumber) { + List<FeedItem> candidates = DBReader.getDownloadedItems(context); + List<FeedItem> queue = DBReader.getQueue(context); + List<FeedItem> delete; + for (FeedItem item : candidates) { + if (item.hasMedia() && item.getMedia().isDownloaded() + && !queue.contains(item) && item.isRead()) { + candidates.add(item); + } + + } + + Collections.sort(candidates, new Comparator<FeedItem>() { + @Override + public int compare(FeedItem lhs, FeedItem rhs) { + Date l = lhs.getMedia().getPlaybackCompletionDate(); + Date r = rhs.getMedia().getPlaybackCompletionDate(); + + if (l == null) { + l = new Date(0); + } + if (r == null) { + r = new Date(0); + } + return l.compareTo(r); + } + }); + + if (candidates.size() > episodeNumber) { + delete = candidates.subList(0, episodeNumber); + } else { + delete = candidates; + } + + for (FeedItem item : delete) { + DBWriter.deleteFeedMediaOfItem(context, item.getId()); + } + + int counter = delete.size(); + + if (AppConfig.DEBUG) + Log.d(TAG, String.format( + "Auto-delete deleted %d episodes (%d requested)", counter, + episodeNumber)); + + return counter; + } + + public static void enqueueAllNewItems(final Context context) { + long[] unreadItems = DBReader.getUnreadItemIds(context); + DBWriter.addQueueItem(context, unreadItems); + } + + public static FeedItem getQueueSuccessorOfItem(Context context, + final long itemId, List<FeedItem> queue) { + FeedItem result = null; if (queue == null) { - queue = DBReader.getQueue(context); + queue = DBReader.getQueue(context); + } + if (queue != null) { + Iterator<FeedItem> iterator = queue.iterator(); + while (iterator.hasNext()) { + FeedItem item = iterator.next(); + if (item.getId() == itemId) { + if (iterator.hasNext()) { + result = iterator.next(); + } + break; + } + } } - if (queue != null) { - Iterator<FeedItem> iterator = queue.iterator(); - while (iterator.hasNext()) { - FeedItem item = iterator.next(); - if (item.getId() == itemId) { - if (iterator.hasNext()) { - result = iterator.next(); - } - break; - } - } - } - return result; - } + return result; + } public static boolean isInQueue(Context context, final long feedItemId) { List<Long> queue = DBReader.getQueueIDList(context); return QueueAccess.IDListAccess(queue).contains(feedItemId); } - private static Feed searchFeedByIdentifyingValue(Context context, - String identifier) { - List<Feed> feeds = DBReader.getFeedList(context); - for (Feed feed : feeds) { - if (feed.getIdentifyingValue().equals(identifier)) { - return feed; - } - } - return null; - } - - /** Get a FeedItem by its identifying value. */ - private static FeedItem searchFeedItemByIdentifyingValue(Feed feed, - String identifier) { - for (FeedItem item : feed.getItems()) { - if (item.getIdentifyingValue().equals(identifier)) { - return item; - } - } - return null; - } - - public static synchronized Feed updateFeed(final Context context, - final Feed newFeed) { - // Look up feed in the feedslist - final Feed savedFeed = searchFeedByIdentifyingValue(context, - newFeed.getIdentifyingValue()); - if (savedFeed == null) { - if (AppConfig.DEBUG) - Log.d(TAG, - "Found no existing Feed with title " - + newFeed.getTitle() + ". Adding as new one."); - // Add a new Feed + private static Feed searchFeedByIdentifyingValue(Context context, + String identifier) { + List<Feed> feeds = DBReader.getFeedList(context); + for (Feed feed : feeds) { + if (feed.getIdentifyingValue().equals(identifier)) { + return feed; + } + } + return null; + } + + /** + * Get a FeedItem by its identifying value. + */ + private static FeedItem searchFeedItemByIdentifyingValue(Feed feed, + String identifier) { + for (FeedItem item : feed.getItems()) { + if (item.getIdentifyingValue().equals(identifier)) { + return item; + } + } + return null; + } + + public static synchronized Feed updateFeed(final Context context, + final Feed newFeed) { + // Look up feed in the feedslist + final Feed savedFeed = searchFeedByIdentifyingValue(context, + newFeed.getIdentifyingValue()); + if (savedFeed == null) { + if (AppConfig.DEBUG) + Log.d(TAG, + "Found no existing Feed with title " + + newFeed.getTitle() + ". Adding as new one."); + // Add a new Feed try { - DBWriter.addNewFeed(context, newFeed).get(); + DBWriter.addNewFeed(context, newFeed).get(); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } - return newFeed; - } else { - if (AppConfig.DEBUG) - Log.d(TAG, "Feed with title " + newFeed.getTitle() - + " already exists. Syncing new with existing one."); - - savedFeed.setItems(DBReader.getFeedItemList(context, savedFeed)); - if (savedFeed.compareWithOther(newFeed)) { - if (AppConfig.DEBUG) - Log.d(TAG, - "Feed has updated attribute values. Updating old feed's attributes"); - savedFeed.updateFromOther(newFeed); - } - // Look for new or updated Items - for (int idx = 0; idx < newFeed.getItems().size(); idx++) { - final FeedItem item = newFeed.getItems().get(idx); - FeedItem oldItem = searchFeedItemByIdentifyingValue(savedFeed, - item.getIdentifyingValue()); - if (oldItem == null) { - // item is new - final int i = idx; - item.setFeed(savedFeed); - savedFeed.getItems().add(i, item); - DBWriter.markItemRead(context, item.getId(), false); - } else { - oldItem.updateFromOther(item); - } - } - // update attributes - savedFeed.setLastUpdate(newFeed.getLastUpdate()); - savedFeed.setType(newFeed.getType()); + return newFeed; + } else { + if (AppConfig.DEBUG) + Log.d(TAG, "Feed with title " + newFeed.getTitle() + + " already exists. Syncing new with existing one."); + + savedFeed.setItems(DBReader.getFeedItemList(context, savedFeed)); + if (savedFeed.compareWithOther(newFeed)) { + if (AppConfig.DEBUG) + Log.d(TAG, + "Feed has updated attribute values. Updating old feed's attributes"); + savedFeed.updateFromOther(newFeed); + } + // Look for new or updated Items + for (int idx = 0; idx < newFeed.getItems().size(); idx++) { + final FeedItem item = newFeed.getItems().get(idx); + FeedItem oldItem = searchFeedItemByIdentifyingValue(savedFeed, + item.getIdentifyingValue()); + if (oldItem == null) { + // item is new + final int i = idx; + item.setFeed(savedFeed); + savedFeed.getItems().add(i, item); + DBWriter.markItemRead(context, item.getId(), false); + } else { + oldItem.updateFromOther(item); + } + } + // update attributes + savedFeed.setLastUpdate(newFeed.getLastUpdate()); + savedFeed.setType(newFeed.getType()); try { - DBWriter.setCompleteFeed(context, savedFeed).get(); + DBWriter.setCompleteFeed(context, savedFeed).get(); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } - new Thread() { - @Override - public void run() { - autodownloadUndownloadedItems(context); - } - }.start(); - return savedFeed; - } - } - - /** - * Searches the descriptions of FeedItems of a specific feed for a given - * string. - * - * @param feed - * The feed whose items should be searched. - * @param query - * The search string - * @param callback - * A callback which will be used to return the search result - * */ - public void searchFeedItemDescription(final Context context, - final Feed feed, final String query, QueryTaskCallback callback) { - new Thread((new QueryTask(context, new Handler(), callback) { - - @Override - public void execute(PodDBAdapter adapter) { - Cursor searchResult = adapter.searchItemDescriptions(feed, - query); - setResult(searchResult); - } - })).start(); - } - - /** - * Searches the 'contentEncoded' field of FeedItems of a specific feed for a - * given string. - * - * @param feed - * The feed whose items should be searched. - * @param query - * The search string - * @param callback - * A callback which will be used to return the search result - * */ - public void searchFeedItemContentEncoded(final Context context, - final Feed feed, final String query, QueryTaskCallback callback) { - new Thread((new QueryTask(context, new Handler(), callback) { - - @Override - public void execute(PodDBAdapter adapter) { - Cursor searchResult = adapter.searchItemContentEncoded(feed, - query); - setResult(searchResult); - } - })).start(); - } - - /** Is called by a FeedManagerTask after completion. */ - public interface TaskCallback<V> { - void onCompletion(V 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. */ - abstract class Task<V> implements Runnable { - private Handler handler; - private TaskCallback<V> callback; - private V result; - - /** - * Standard contructor. No callbacks are going to be posted to a - * handler. - */ - public Task() { - super(); - } - - /** - * The Task will post a Runnable to 'handler' that will execute the - * 'callback' after completion. - */ - public Task(Handler handler, TaskCallback<V> callback) { - super(); - this.handler = handler; - this.callback = callback; - } - - @Override - public final void run() { - execute(); - if (handler != null && callback != null) { - handler.post(new Runnable() { - @Override - public void run() { - callback.onCompletion(result); - } - }); - } - } - - /** This method will be executed in the same thread as the run() method. */ - public abstract void execute(); - - public void setResult(V result) { - this.result = result; - } - } - - /** - * 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) { - result = c; - } - } + new Thread() { + @Override + public void run() { + autodownloadUndownloadedItems(context); + } + }.start(); + return savedFeed; + } + } + + /** + * Searches the titles of FeedItems of a specific feed for a given + * string. + * + * @param feedID The id of the feed whose items should be searched. + * @param query The search string + */ + public static FutureTask<List<FeedItem>> searchFeedItemTitle(final Context context, + final long feedID, final String query) { + return new FutureTask<List<FeedItem>>(new QueryTask<List<FeedItem>>(context) { + @Override + public void execute(PodDBAdapter adapter) { + Cursor searchResult = adapter.searchItemTitles(feedID, + query); + List<FeedItem> items = DBReader.extractItemlistFromCursor(context, searchResult); + DBReader.loadFeedDataOfFeedItemlist(context, items); + setResult(items); + searchResult.close(); + } + }); + } + + /** + * Searches the descriptions of FeedItems of a specific feed for a given + * string. + * + * @param feedID The id of the feed whose items should be searched. + * @param query The search string + */ + public static FutureTask<List<FeedItem>> searchFeedItemDescription(final Context context, + final long feedID, final String query) { + return new FutureTask<List<FeedItem>>(new QueryTask<List<FeedItem>>(context) { + @Override + public void execute(PodDBAdapter adapter) { + Cursor searchResult = adapter.searchItemDescriptions(feedID, + query); + List<FeedItem> items = DBReader.extractItemlistFromCursor(context, searchResult); + DBReader.loadFeedDataOfFeedItemlist(context, items); + setResult(items); + searchResult.close(); + } + }); + } + + /** + * Searches the 'contentEncoded' field of FeedItems of a specific feed for a + * given string. + * + * @param feedID The id of the feed whose items should be searched. + * @param query The search string + */ + public static FutureTask<List<FeedItem>> searchFeedItemContentEncoded(final Context context, + final long feedID, final String query) { + return new FutureTask<List<FeedItem>>(new QueryTask<List<FeedItem>>(context) { + @Override + public void execute(PodDBAdapter adapter) { + Cursor searchResult = adapter.searchItemContentEncoded(feedID, + query); + List<FeedItem> items = DBReader.extractItemlistFromCursor(context, searchResult); + DBReader.loadFeedDataOfFeedItemlist(context, items); + setResult(items); + searchResult.close(); + } + }); + } + + /** + * Searches chapters for a given string. + * + * @param feedID The id of the feed whose items should be searched. + * @param query The search string + */ + public static FutureTask<List<FeedItem>> searchFeedItemChapters(final Context context, + final long feedID, final String query) { + return new FutureTask<List<FeedItem>>(new QueryTask<List<FeedItem>>(context) { + @Override + public void execute(PodDBAdapter adapter) { + Cursor searchResult = adapter.searchItemChapters(feedID, + query); + List<FeedItem> items = DBReader.extractItemlistFromCursor(context, searchResult); + DBReader.loadFeedDataOfFeedItemlist(context, items); + setResult(items); + searchResult.close(); + } + }); + } + + /** + * 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. + */ + static abstract class QueryTask<T> implements Callable<T> { + private T result; + private Context context; + + public QueryTask(Context context) { + this.context = context; + } + + @Override + public T call() throws Exception { + PodDBAdapter adapter = new PodDBAdapter(context); + adapter.open(); + execute(adapter); + adapter.close(); + return result; + } + + public abstract void execute(PodDBAdapter adapter); + + protected void setResult(T result) { + this.result = result; + } + } } diff --git a/src/de/danoeh/antennapod/feed/FeedSearcher.java b/src/de/danoeh/antennapod/storage/FeedSearcher.java index ab7c174bc..a16430056 100644 --- a/src/de/danoeh/antennapod/feed/FeedSearcher.java +++ b/src/de/danoeh/antennapod/storage/FeedSearcher.java @@ -1,32 +1,62 @@ -package de.danoeh.antennapod.feed; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +package de.danoeh.antennapod.storage; import android.content.Context; -import android.database.Cursor; -import android.os.Looper; -import android.util.Log; -import de.danoeh.antennapod.AppConfig; -import de.danoeh.antennapod.PodcastApp; import de.danoeh.antennapod.R; -import de.danoeh.antennapod.storage.PodDBAdapter; +import de.danoeh.antennapod.feed.Feed; +import de.danoeh.antennapod.feed.FeedItem; +import de.danoeh.antennapod.feed.SearchResult; import de.danoeh.antennapod.util.comparator.SearchResultValueComparator; -/** Performs search on Feeds and FeedItems */ +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.FutureTask; + +/** + * Performs search on Feeds and FeedItems + */ public class FeedSearcher { - private static final String TAG = "FeedSearcher"; + private static final String TAG = "FeedSearcher"; + + + /** + * Performs a search in all feeds or one specific feed. + */ + public static List<SearchResult> performSearch(final Context context, + final String query, final long selectedFeed) { + final int values[] = {0, 0, 1, 2}; + final String[] subtitles = {context.getString(R.string.found_in_shownotes_label), + context.getString(R.string.found_in_shownotes_label), + context.getString(R.string.found_in_chapters_label), + context.getString(R.string.found_in_title_label)}; + + List<SearchResult> result = new ArrayList<SearchResult>(); - // Search result values - private static final int VALUE_FEED_TITLE = 3; - private static final int VALUE_ITEM_TITLE = 2; - private static final int VALUE_ITEM_CHAPTER = 1; - private static final int VALUE_ITEM_DESCRIPTION = 0; - private static final int VALUE_WORD_MATCH = 4; + FutureTask<List<FeedItem>>[] tasks = new FutureTask[4]; + (tasks[0] = DBTasks.searchFeedItemContentEncoded(context, selectedFeed, query)).run(); + (tasks[1] = DBTasks.searchFeedItemDescription(context, selectedFeed, query)).run(); + (tasks[2] = DBTasks.searchFeedItemChapters(context, selectedFeed, query)).run(); + (tasks[3] = DBTasks.searchFeedItemTitle(context, selectedFeed, query)).run(); + try { + for (int i = 0; i < tasks.length; i++) { + FutureTask task = tasks[i]; + List<FeedItem> items = (List<FeedItem>) task.get(); + for (FeedItem item : items) { + result.add(new SearchResult(item, values[i], subtitles[i])); + } - /** Performs a search in all feeds or one specific feed. */ + } + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (ExecutionException e) { + e.printStackTrace(); + } + Collections.sort(result, new SearchResultValueComparator()); + return result; + } + /* + *//** Performs a search in all feeds or one specific feed. *//* public static ArrayList<SearchResult> performSearch(final Context context, final String query, final Feed selectedFeed) { final String lcQuery = query.toLowerCase(); @@ -49,38 +79,37 @@ public class FeedSearcher { Log.d(TAG, "Searching item-chaptertitles"); searchFeedItemChapters(lcQuery, result, selectedFeed); - final FeedManager manager = FeedManager.getInstance(); Looper.prepare(); - manager.searchFeedItemDescription(context, selectedFeed, lcQuery, - new FeedManager.QueryTaskCallback() { + DBTasks.searchFeedItemDescription(context, selectedFeed, lcQuery, + new DBTasks.QueryTaskCallback() { - @Override - public void handleResult(Cursor cResult) { - searchFeedItemContentEncodedCursor(lcQuery, result, - selectedFeed, cResult); + @Override + public void handleResult(Cursor cResult) { + searchFeedItemContentEncodedCursor(context, 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(); - } - }); - } - }); + @Override + public void onCompletion() { + DBTasks.searchFeedItemContentEncoded(context, + selectedFeed, lcQuery, + new DBTasks.QueryTaskCallback() { + + @Override + public void handleResult(Cursor cResult) { + searchFeedItemDescriptionCursor(context, + lcQuery, result, selectedFeed, + cResult); + } + + @Override + public void onCompletion() { + Looper.myLooper().quit(); + } + }); + } + }); Looper.loop(); if (AppConfig.DEBUG) @@ -130,7 +159,6 @@ public class FeedSearcher { private static void searchFeedItemChapters(String query, ArrayList<SearchResult> destination, Feed selectedFeed) { - FeedManager manager = FeedManager.getInstance(); if (selectedFeed == null) { for (Feed feed : manager.getFeeds()) { searchFeedItemChaptersSingleFeed(query, destination, feed); @@ -157,10 +185,11 @@ public class FeedSearcher { } } - private static void searchFeedItemDescriptionCursor(String query, + private static void searchFeedItemDescriptionCursor(Context context, String query, ArrayList<SearchResult> destination, Feed feed, Cursor cursor) { FeedManager manager = FeedManager.getInstance(); - if (cursor.moveToFirst()) { + List<FeedItem> items = DBReader.extractItemlistFromCursor(cursor); + if (cursor.moveToFirst()) { do { final long itemId = cursor .getLong(PodDBAdapter.IDX_FI_EXTRA_ID); @@ -193,9 +222,8 @@ public class FeedSearcher { } } - private static void searchFeedItemContentEncodedCursor(String query, + private static void searchFeedItemContentEncodedCursor(Context context, String query, ArrayList<SearchResult> destination, Feed feed, Cursor cursor) { - FeedManager manager = FeedManager.getInstance(); if (cursor.moveToFirst()) { do { final long itemId = cursor @@ -248,6 +276,6 @@ public class FeedSearcher { } else { return null; } - } + }*/ } diff --git a/src/de/danoeh/antennapod/storage/PodDBAdapter.java b/src/de/danoeh/antennapod/storage/PodDBAdapter.java index 4ef76fcd6..198d95d64 100644 --- a/src/de/danoeh/antennapod/storage/PodDBAdapter.java +++ b/src/de/danoeh/antennapod/storage/PodDBAdapter.java @@ -35,6 +35,9 @@ public class PodDBAdapter { /** Maximum number of arguments for IN-operator. */ public static final int IN_OPERATOR_MAXIMUM = 800; + /** Maximum number of entries per search request. */ + public static final int SEARCH_LIMIT = 30; + // ----------- Column indices // ----------- General indices public static final int KEY_ID_INDEX = 0; @@ -857,6 +860,18 @@ public class PodDBAdapter { } + public final int getNumberOfUnreadItems() { + final String query = "SELECT COUNT(DISTINCT " + KEY_ID + ") AS count FROM " + TABLE_NAME_FEED_ITEMS + + " WHERE " + KEY_READ + " = 0"; + Cursor c = db.rawQuery(query, null); + int result = 0; + if (c.moveToFirst()) { + result = c.getInt(0); + } + c.close(); + return result; + } + public final int getNumberOfDownloadedEpisodes() { final String query = "SELECT COUNT(DISTINCT " + KEY_ID + ") AS count FROM " + TABLE_NAME_FEED_MEDIA + " WHERE " + KEY_DOWNLOADED + " > 0"; @@ -888,17 +903,17 @@ public class PodDBAdapter { * * @return A cursor with all search results in SEL_FI_EXTRA selection. * */ - public Cursor searchItemDescriptions(Feed feed, String query) { - if (feed != null) { + public Cursor searchItemDescriptions(long feedID, String query) { + if (feedID != 0) { // 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_SMALL, KEY_FEED + "=? AND " + KEY_DESCRIPTION + " LIKE '%" + prepareSearchQuery(query) + "%'", - new String[] { String.valueOf(feed.getId()) }, null, null, + new String[] { String.valueOf(feedID) }, null, null, null); } else { // search through all items - return db.query(TABLE_NAME_FEED_ITEMS, SEL_FI_EXTRA, + return db.query(TABLE_NAME_FEED_ITEMS, SEL_FI_SMALL, KEY_DESCRIPTION + " LIKE '%" + prepareSearchQuery(query) + "%'", null, null, null, null); } @@ -910,23 +925,57 @@ public class PodDBAdapter { * * @return A cursor with all search results in SEL_FI_EXTRA selection. * */ - public Cursor searchItemContentEncoded(Feed feed, String query) { - if (feed != null) { + public Cursor searchItemContentEncoded(long feedID, String query) { + if (feedID != 0) { // 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_SMALL, KEY_FEED + "=? AND " + KEY_CONTENT_ENCODED + " LIKE '%" + prepareSearchQuery(query) + "%'", - new String[] { String.valueOf(feed.getId()) }, null, null, + new String[] { String.valueOf(feedID) }, null, null, null); } else { // search through all items - return db.query(TABLE_NAME_FEED_ITEMS, SEL_FI_EXTRA, + return db.query(TABLE_NAME_FEED_ITEMS, 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, 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, SEL_FI_SMALL, + KEY_TITLE + " LIKE '%" + + prepareSearchQuery(query) + "%'", null, null, + null, 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) + "%'"; + } + return db.rawQuery(query, null); + } + /** Helper class for opening the Antennapod database. */ private static class PodDBHelper extends SQLiteOpenHelper { /** |