summaryrefslogtreecommitdiff
path: root/src/de/danoeh
diff options
context:
space:
mode:
Diffstat (limited to 'src/de/danoeh')
-rw-r--r--src/de/danoeh/antennapod/PodcastApp.java3
-rw-r--r--src/de/danoeh/antennapod/activity/MainActivity.java12
-rw-r--r--src/de/danoeh/antennapod/activity/SearchActivity.java22
-rw-r--r--src/de/danoeh/antennapod/feed/FeedManager.java21
-rw-r--r--src/de/danoeh/antennapod/feed/FeedMedia.java13
-rw-r--r--src/de/danoeh/antennapod/feed/SearchResult.java3
-rw-r--r--src/de/danoeh/antennapod/storage/DBReader.java16
-rw-r--r--src/de/danoeh/antennapod/storage/DBTasks.java1166
-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.java69
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 {
/**