diff --git a/src/de/danoeh/antennapod/activity/DownloadLogActivity.java b/src/de/danoeh/antennapod/activity/DownloadLogActivity.java index af2cdb65a..ec5b0c980 100644 --- a/src/de/danoeh/antennapod/activity/DownloadLogActivity.java +++ b/src/de/danoeh/antennapod/activity/DownloadLogActivity.java @@ -17,8 +17,7 @@ import de.danoeh.antennapod.storage.DBReader; import java.util.List; /** - * Displays completed and failed downloads in a list. The data comes from the - * FeedManager. + * Displays completed and failed downloads in a list. */ public class DownloadLogActivity extends SherlockListActivity { private static final String TAG = "DownloadLogActivity"; diff --git a/src/de/danoeh/antennapod/feed/EventDistributor.java b/src/de/danoeh/antennapod/feed/EventDistributor.java index 1fc7e2c35..c538808e2 100644 --- a/src/de/danoeh/antennapod/feed/EventDistributor.java +++ b/src/de/danoeh/antennapod/feed/EventDistributor.java @@ -92,7 +92,7 @@ public class EventDistributor extends Observable { super.addObserver(observer); if (!(observer instanceof EventListener)) { throw new IllegalArgumentException( - "Observer must be instance of FeedManager.EventListener"); + "Observer must be instance of EventListener"); } } diff --git a/src/de/danoeh/antennapod/feed/FeedManager.java b/src/de/danoeh/antennapod/feed/FeedManager.java deleted file mode 100644 index 149f303cc..000000000 --- a/src/de/danoeh/antennapod/feed/FeedManager.java +++ /dev/null @@ -1,2022 +0,0 @@ -package de.danoeh.antennapod.feed; - -import java.io.File; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Collections; -import java.util.Date; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.TreeMap; -import java.util.Comparator; -import java.util.concurrent.Executor; -import java.util.concurrent.Executors; -import java.util.concurrent.ThreadFactory; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.database.Cursor; -import android.os.AsyncTask; -import android.os.Handler; -import android.preference.PreferenceManager; -import android.util.Log; -import de.danoeh.antennapod.AppConfig; -import de.danoeh.antennapod.preferences.PlaybackPreferences; -import de.danoeh.antennapod.preferences.UserPreferences; -import de.danoeh.antennapod.service.PlaybackService; -import de.danoeh.antennapod.service.download.DownloadStatus; -import de.danoeh.antennapod.storage.DBReader; -import de.danoeh.antennapod.storage.DownloadRequestException; -import de.danoeh.antennapod.storage.DownloadRequester; -import de.danoeh.antennapod.storage.PodDBAdapter; -import de.danoeh.antennapod.util.DownloadError; -import de.danoeh.antennapod.util.EpisodeFilter; -import de.danoeh.antennapod.util.FeedtitleComparator; -import de.danoeh.antennapod.util.NetworkUtils; -import de.danoeh.antennapod.util.comparator.DownloadStatusComparator; -import de.danoeh.antennapod.util.comparator.FeedItemPubdateComparator; -import de.danoeh.antennapod.util.comparator.PlaybackCompletionDateComparator; -import de.danoeh.antennapod.util.exception.MediaFileNotFoundException; - -/** - * Singleton class that - provides access to all Feeds and FeedItems and to - * several lists of FeedItems. - provides methods for modifying the - * application's data - takes care of updating the information stored in the - * database when something is modified - * - * An instance of this class can be retrieved via getInstance(). - * */ -public class FeedManager { - private static final String TAG = "FeedManager"; - - /** Number of completed Download status entries to store. */ - private static final int DOWNLOAD_LOG_SIZE = 50; - - private static FeedManager singleton; - - private List feeds; - - /** Contains all items where 'read' is false */ - private List unreadItems; - - /** Contains completed Download status entries */ - private List downloadLog; - - /** Contains the queue of items to be played. */ - private List queue; - - /** Contains the last played items */ - private List playbackHistory; - - /** Maximum number of items in the playback history. */ - private static final int PLAYBACK_HISTORY_SIZE = 15; - - private DownloadRequester requester = DownloadRequester.getInstance(); - private EventDistributor eventDist = EventDistributor.getInstance(); - - /** - * Should be used to change the content of the arrays from another thread to - * ensure that arrays are only modified on the main thread. - */ - private Handler contentChanger; - - /** Ensures that there are no parallel db operations. */ - private Executor dbExec; - - /** Prevents user from starting several feed updates at the same time. */ - private static boolean isStartingFeedRefresh = false; - - private FeedManager() { - feeds = Collections.synchronizedList(new ArrayList()); - unreadItems = Collections.synchronizedList(new ArrayList()); - downloadLog = new ArrayList(); - queue = Collections.synchronizedList(new ArrayList()); - playbackHistory = Collections - .synchronizedList(new ArrayList()); - contentChanger = new Handler(); - dbExec = Executors.newSingleThreadExecutor(new ThreadFactory() { - - @Override - public Thread newThread(Runnable r) { - Thread t = new Thread(r); - t.setPriority(Thread.MIN_PRIORITY); - return t; - } - }); - } - - /** Creates a new instance of this class if necessary and returns it. */ - public static FeedManager getInstance() { - if (singleton == null) { - singleton = new FeedManager(); - } - return singleton; - } - - /** - * Play FeedMedia and start the playback service + launch Mediaplayer - * Activity. The FeedItem will be added at the top of the queue if it isn't - * in there yet. - * - * @param context - * for starting the playbackservice - * @param media - * that shall be played - * @param showPlayer - * if Mediaplayer activity shall be started - * @param startWhenPrepared - * if Mediaplayer shall be started after it has been prepared - * @param shouldStream - * if Mediaplayer should stream the file - */ - public void playMedia(Context context, 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)); - } - if (!queue.contains(media.getItem())) { - addQueueItemAt(context, media.getItem(), 0, false); - } - } catch (MediaFileNotFoundException e) { - e.printStackTrace(); - if (media.isPlaying()) { - context.sendBroadcast(new Intent( - PlaybackService.ACTION_SHUTDOWN_PLAYBACK_SERVICE)); - } - notifyMissingFeedMediaFile(context, media); - } - } - - /** Remove media item that has been downloaded. */ - public boolean deleteFeedMedia(Context context, FeedMedia media) { - boolean result = false; - if (media.isDownloaded()) { - File mediaFile = new File(media.file_url); - if (mediaFile.exists()) { - result = mediaFile.delete(); - } - media.setDownloaded(false); - media.setFile_url(null); - setFeedMedia(context, media); - - SharedPreferences prefs = PreferenceManager - .getDefaultSharedPreferences(context); - if (PlaybackPreferences.getCurrentlyPlayingMedia() == FeedMedia.PLAYABLE_TYPE_FEEDMEDIA) { - if (media.getId() == PlaybackPreferences - .getCurrentlyPlayingFeedMediaId()) { - SharedPreferences.Editor editor = prefs.edit(); - editor.putBoolean( - PlaybackPreferences.PREF_CURRENT_EPISODE_IS_STREAM, - true); - editor.commit(); - } - if (PlaybackPreferences.getCurrentlyPlayingFeedMediaId() == media - .getId()) { - context.sendBroadcast(new Intent( - PlaybackService.ACTION_SHUTDOWN_PLAYBACK_SERVICE)); - } - } - } - if (AppConfig.DEBUG) - Log.d(TAG, "Deleting File. Result: " + result); - return result; - } - - /** Remove a feed with all its items and media files and its image. */ - public void deleteFeed(final Context context, final Feed feed) { - SharedPreferences prefs = PreferenceManager - .getDefaultSharedPreferences(context.getApplicationContext()); - if (PlaybackPreferences.getCurrentlyPlayingMedia() == FeedMedia.PLAYABLE_TYPE_FEEDMEDIA - && PlaybackPreferences.getLastPlayedFeedId() == feed.getId()) { - context.sendBroadcast(new Intent( - PlaybackService.ACTION_SHUTDOWN_PLAYBACK_SERVICE)); - SharedPreferences.Editor editor = prefs.edit(); - editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEED_ID, - -1); - editor.commit(); - } - - contentChanger.post(new Runnable() { - - @Override - public void run() { - feeds.remove(feed); - dbExec.execute(new Runnable() { - - @Override - public void run() { - PodDBAdapter adapter = new PodDBAdapter(context); - DownloadRequester requester = DownloadRequester - .getInstance(); - adapter.open(); - // delete image file - if (feed.getImage() != null) { - if (feed.getImage().isDownloaded() - && feed.getImage().getFile_url() != null) { - File imageFile = new File(feed.getImage() - .getFile_url()); - imageFile.delete(); - } else if (requester.isDownloadingFile(feed - .getImage())) { - requester.cancelDownload(context, - feed.getImage()); - } - } - // delete stored media files and mark them as read - for (FeedItem item : feed.getItems()) { - if (item.getState() == FeedItem.State.NEW) { - unreadItems.remove(item); - } - if (queue.contains(item)) { - removeQueueItem(item, adapter); - } - removeItemFromPlaybackHistory(context, item); - if (item.getMedia() != null - && item.getMedia().isDownloaded()) { - File mediaFile = new File(item.getMedia() - .getFile_url()); - mediaFile.delete(); - } else if (item.getMedia() != null - && requester.isDownloadingFile(item - .getMedia())) { - requester.cancelDownload(context, - item.getMedia()); - } - } - - adapter.removeFeed(feed); - adapter.close(); - eventDist.sendFeedUpdateBroadcast(); - } - - }); - } - }); - - } - - /** - * Makes sure that playback history is sorted and is not larger than - * PLAYBACK_HISTORY_SIZE. - * - * @return an array of all feeditems that were remove from the playback - * history or null if no items were removed. - */ - private FeedItem[] cleanupPlaybackHistory() { - if (AppConfig.DEBUG) - Log.d(TAG, "Cleaning up playback history."); - - Collections.sort(playbackHistory, - new PlaybackCompletionDateComparator()); - final int initialSize = playbackHistory.size(); - if (initialSize > PLAYBACK_HISTORY_SIZE) { - FeedItem[] removed = new FeedItem[initialSize - - PLAYBACK_HISTORY_SIZE]; - - for (int i = 0; i < removed.length; i++) { - removed[i] = playbackHistory.remove(playbackHistory.size() - 1); - } - if (AppConfig.DEBUG) - Log.d(TAG, "Removed " + removed.length - + " items from playback history."); - return removed; - } - return null; - } - - /** - * Executes cleanupPlaybackHistory and deletes the playbackCompletionDate of - * all item that were removed from the history. - */ - private void cleanupPlaybackHistoryWithDBCleanup(final Context context) { - final FeedItem[] removedItems = cleanupPlaybackHistory(); - if (removedItems != null) { - dbExec.execute(new Runnable() { - - @Override - public void run() { - PodDBAdapter adapter = new PodDBAdapter(context); - adapter.open(); - for (FeedItem item : removedItems) { - if (item.getMedia() != null) { - item.getMedia().setPlaybackCompletionDate(null); - adapter.setMedia(item.getMedia()); - } - } - adapter.close(); - } - }); - } - } - - /** Removes all items from the playback history. */ - public void clearPlaybackHistory(final Context context) { - if (!playbackHistory.isEmpty()) { - if (AppConfig.DEBUG) - Log.d(TAG, "Clearing playback history."); - final FeedItem[] items = playbackHistory - .toArray(new FeedItem[playbackHistory.size()]); - playbackHistory.clear(); - eventDist.sendPlaybackHistoryUpdateBroadcast(); - dbExec.execute(new Runnable() { - - @Override - public void run() { - PodDBAdapter adapter = new PodDBAdapter(context); - adapter.open(); - for (FeedItem item : items) { - if (item.getMedia() != null - && item.getMedia().getPlaybackCompletionDate() != null) { - item.getMedia().setPlaybackCompletionDate(null); - adapter.setMedia(item.getMedia()); - } - } - adapter.close(); - } - }); - } - } - - /** Adds a FeedItem to the playback history. */ - public void addItemToPlaybackHistory(Context context, FeedItem item) { - if (item.getMedia() != null - && item.getMedia().getPlaybackCompletionDate() != null) { - if (AppConfig.DEBUG) - Log.d(TAG, "Adding new item to playback history"); - if (!playbackHistory.contains(item)) { - playbackHistory.add(item); - } - cleanupPlaybackHistoryWithDBCleanup(context); - eventDist.sendPlaybackHistoryUpdateBroadcast(); - } - } - - private void removeItemFromPlaybackHistory(Context context, FeedItem item) { - playbackHistory.remove(item); - eventDist.sendPlaybackHistoryUpdateBroadcast(); - } - - /** - * Sets the 'read'-attribute of a FeedItem. Should be used by all Classes - * instead of the setters of FeedItem. - */ - public void markItemRead(final Context context, final FeedItem item, - final boolean read, boolean resetMediaPosition) { - if (AppConfig.DEBUG) - Log.d(TAG, "Setting item with title " + item.getTitle() - + " as read/unread"); - - item.setRead(read); - if (item.hasMedia() && resetMediaPosition) { - item.getMedia().setPosition(0); - } - setFeedItem(context, item); - if (item.hasMedia() && resetMediaPosition) - setFeedMedia(context, item.getMedia()); - - contentChanger.post(new Runnable() { - - @Override - public void run() { - if (read == true) { - unreadItems.remove(item); - } else { - unreadItems.add(item); - Collections.sort(unreadItems, - new FeedItemPubdateComparator()); - } - eventDist.sendUnreadItemsUpdateBroadcast(); - } - }); - - } - - /** - * Sets the 'read' attribute of all FeedItems of a specific feed to true - */ - public void markFeedRead(Context context, Feed feed) { - for (FeedItem item : feed.getItems()) { - if (unreadItems.contains(item)) { - markItemRead(context, item, true, false); - } - } - } - - /** Marks all items in the unread items list as read */ - public void markAllItemsRead(final Context context) { - if (AppConfig.DEBUG) - Log.d(TAG, "marking all items as read"); - for (FeedItem item : unreadItems) { - item.setRead(true); - } - final ArrayList unreadItemsCopy = new ArrayList( - unreadItems); - unreadItems.clear(); - eventDist.sendUnreadItemsUpdateBroadcast(); - dbExec.execute(new Runnable() { - - @Override - public void run() { - PodDBAdapter adapter = new PodDBAdapter(context); - adapter.open(); - for (FeedItem item : unreadItemsCopy) { - setFeedItem(item, adapter); - if (item.hasMedia()) - setFeedMedia(context, item.getMedia()); - } - adapter.close(); - } - }); - - } - - /** Updates all feeds in the feed list. */ - public void refreshAllFeeds(final Context context) { - if (AppConfig.DEBUG) - Log.d(TAG, "Refreshing all feeds."); - refreshFeeds(context, feeds); - } - - /** Updates all feeds in the feed list. */ - public void refreshExpiredFeeds(final Context context) { - long millis = UserPreferences.getUpdateInterval(); - - if (AppConfig.DEBUG) - Log.d(TAG, "Refreshing expired feeds, " + millis + " ms"); - - if (millis > 0) { - List feedList = new ArrayList(); - long now = Calendar.getInstance().getTime().getTime(); - - // Allow a 10 minute window - millis -= 10 * 60 * 1000; - for (Feed feed : feeds) { - Date date = feed.getLastUpdate(); - if (date != null) { - if (date.getTime() + millis <= now) { - if (AppConfig.DEBUG) { - Log.d(TAG, "Adding expired feed " + feed.getTitle()); - } - feedList.add(feed); - } else { - if (AppConfig.DEBUG) { - Log.d(TAG, "Skipping feed " + feed.getTitle()); - } - } - } - } - if (feedList.size() > 0) { - refreshFeeds(context, feedList); - } - } - } - - @SuppressLint("NewApi") - private void refreshFeeds(final Context context, final List feedList) { - if (!isStartingFeedRefresh) { - isStartingFeedRefresh = true; - AsyncTask updateWorker = new AsyncTask() { - - @Override - protected void onPostExecute(Void result) { - if (AppConfig.DEBUG) - Log.d(TAG, - "All feeds have been sent to the downloadmanager"); - isStartingFeedRefresh = false; - } - - @Override - protected Void doInBackground(Void... params) { - for (Feed feed : feedList) { - try { - refreshFeed(context, feed); - } catch (DownloadRequestException e) { - e.printStackTrace(); - addDownloadStatus( - context, - new DownloadStatus(feed, feed - .getHumanReadableIdentifier(), - DownloadError.ERROR_REQUEST_ERROR, - false, e.getMessage())); - } - } - return null; - } - - }; - if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.GINGERBREAD_MR1) { - updateWorker.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } else { - updateWorker.execute(); - } - } - - } - - /** - * Notifies the feed manager that the an image file is invalid. It will try - * to redownload it - */ - public void notifyInvalidImageFile(Context context, FeedImage image) { - Log.i(TAG, - "The feedmanager was notified about an invalid image download. It will now try to redownload the image file"); - try { - requester.downloadImage(context, image); - } catch (DownloadRequestException e) { - e.printStackTrace(); - Log.w(TAG, "Failed to download invalid feed image"); - } - } - - /** - * Notifies the feed manager that a downloaded episode doesn't exist - * anymore. It will update the values of the FeedMedia object accordingly. - */ - public void notifyMissingFeedMediaFile(Context context, 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); - setFeedMedia(context, media); - eventDist.sendFeedUpdateBroadcast(); - } - - /** Updates a specific feed. */ - public void refreshFeed(Context context, Feed feed) - throws DownloadRequestException { - requester.downloadFeed(context, new Feed(feed.getDownload_url(), - new Date(), feed.getTitle())); - } - - /** Adds a download status object to the download log. */ - public void addDownloadStatus(final Context context, - final DownloadStatus status) { - contentChanger.post(new Runnable() { - - @Override - public void run() { - downloadLog.add(status); - Collections.sort(downloadLog, new DownloadStatusComparator()); - final DownloadStatus removedStatus; - if (downloadLog.size() > DOWNLOAD_LOG_SIZE) { - removedStatus = downloadLog.remove(downloadLog.size() - 1); - } else { - removedStatus = null; - } - eventDist.sendDownloadLogUpdateBroadcast(); - dbExec.execute(new Runnable() { - - @Override - public void run() { - PodDBAdapter adapter = new PodDBAdapter(context); - adapter.open(); - if (removedStatus != null) { - adapter.removeDownloadStatus(removedStatus); - } - adapter.setDownloadStatus(status); - adapter.close(); - } - }); - } - }); - - } - - /** Downloads all items in the queue that have not been downloaded yet. */ - public void downloadAllItemsInQueue(final Context context) { - if (!queue.isEmpty()) { - try { - downloadFeedItem(context, - queue.toArray(new FeedItem[queue.size()])); - } catch (DownloadRequestException e) { - e.printStackTrace(); - } - } - } - - public void downloadFeedItem(final Context context, FeedItem... items) - throws DownloadRequestException { - downloadFeedItem(true, context, items); - } - - /** Downloads FeedItems if they have not been downloaded yet. */ - private void downloadFeedItem(boolean performAutoCleanup, - final Context context, final FeedItem... items) - throws DownloadRequestException { - if (performAutoCleanup) { - new Thread() { - - @Override - public void run() { - performAutoCleanup(context, - getPerformAutoCleanupArgs(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(); - addDownloadStatus(context, - new DownloadStatus(item.getMedia(), item - .getMedia() - .getHumanReadableIdentifier(), - DownloadError.ERROR_REQUEST_ERROR, - false, e.getMessage())); - } - } else { - requester.downloadMedia(context, item.getMedia()); - } - } - } - } - - /** - * This method will try to download undownloaded items in the queue or the - * unread items list. If not enough space is available, an episode cleanup - * will be performed first. - * - * This method will not try to download the currently playing item. - */ - public void autodownloadUndownloadedItems(Context context) { - if (AppConfig.DEBUG) - Log.d(TAG, "Performing auto-dl of undownloaded episodes"); - if (NetworkUtils.autodownloadNetworkAvailable(context) - && UserPreferences.isEnableAutodownload()) { - int undownloadedEpisodes = getNumberOfUndownloadedEpisodes(); - int downloadedEpisodes = getNumberOfDownloadedEpisodes(); - int deletedEpisodes = performAutoCleanup(context, - getPerformAutoCleanupArgs(undownloadedEpisodes)); - int episodeSpaceLeft = undownloadedEpisodes; - boolean cacheIsUnlimited = UserPreferences.getEpisodeCacheSize() == UserPreferences - .getEpisodeCacheSizeUnlimited(); - - if (!cacheIsUnlimited - && UserPreferences.getEpisodeCacheSize() < downloadedEpisodes - + undownloadedEpisodes) { - episodeSpaceLeft = UserPreferences.getEpisodeCacheSize() - - (downloadedEpisodes - deletedEpisodes); - } - - List itemsToDownload = new ArrayList(); - 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 { - downloadFeedItem(false, context, - itemsToDownload.toArray(new FeedItem[itemsToDownload - .size()])); - } catch (DownloadRequestException e) { - e.printStackTrace(); - } - - } - } - - /** - * This method will determine the number of episodes that have to be deleted - * depending on a given number of episodes. - * - * @return The argument that has to be passed to performAutoCleanup() so - * that the number of episodes fits into the episode cache. - * */ - private int getPerformAutoCleanupArgs(final int episodeNumber) { - if (episodeNumber >= 0 - && UserPreferences.getEpisodeCacheSize() != UserPreferences - .getEpisodeCacheSizeUnlimited()) { - int downloadedEpisodes = getNumberOfDownloadedEpisodes(); - if (downloadedEpisodes + episodeNumber >= UserPreferences - .getEpisodeCacheSize()) { - - return downloadedEpisodes + episodeNumber - - UserPreferences.getEpisodeCacheSize(); - } - } - return 0; - } - - /** - * Performs an auto-cleanup so that the number of downloaded episodes is - * below or equal to the episode cache size. The method will be executed in - * the caller's thread. - */ - public void performAutoCleanup(Context context) { - performAutoCleanup(context, getPerformAutoCleanupArgs(0)); - } - - /** - * This method will try to delete a given number of episodes. An episode - * will only be deleted if it is not in the queue. - * - * @return The number of episodes that were actually deleted - * */ - private int performAutoCleanup(Context context, final int episodeNumber) { - List candidates = new ArrayList(); - List delete; - for (Feed feed : feeds) { - for (FeedItem item : feed.getItems()) { - if (item.hasMedia() && item.getMedia().isDownloaded() - && !isInQueue(item) && item.isRead()) { - candidates.add(item); - } - } - } - - Collections.sort(candidates, new Comparator() { - @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) { - deleteFeedMedia(context, item.getMedia()); - } - - int counter = delete.size(); - - if (AppConfig.DEBUG) - Log.d(TAG, String.format( - "Auto-delete deleted %d episodes (%d requested)", counter, - episodeNumber)); - - return counter; - } - - /** - * Counts items in the queue and the unread items list which haven't been - * downloaded yet. - * - * This method will not count the playing item - */ - private int getNumberOfUndownloadedEpisodes() { - 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; - - } - - /** Counts all downloaded items. */ - private int getNumberOfDownloadedEpisodes() { - int counter = 0; - for (Feed feed : feeds) { - for (FeedItem item : feed.getItems()) { - if (item.hasMedia() && item.getMedia().isDownloaded()) { - counter++; - } - } - } - if (AppConfig.DEBUG) - Log.d(TAG, "Number of downloaded episodes: " + counter); - return counter; - } - - /** - * Enqueues all items that are currently in the unreadItems list and marks - * them as 'read'. - */ - public void enqueueAllNewItems(final Context context) { - if (!unreadItems.isEmpty()) { - addQueueItem(context, - unreadItems.toArray(new FeedItem[unreadItems.size()])); - markAllItemsRead(context); - } - } - - /** - * Adds a feeditem to the queue at the specified index if it is not in the - * queue yet. The item is marked as 'read'. - */ - public void addQueueItemAt(final Context context, final FeedItem item, - final int index, final boolean performAutoDownload) { - contentChanger.post(new Runnable() { - - @Override - public void run() { - if (!queue.contains(item)) { - queue.add(index, item); - if (!item.isRead()) { - markItemRead(context, item, true, false); - } - } - eventDist.sendQueueUpdateBroadcast(); - - dbExec.execute(new Runnable() { - - @Override - public void run() { - PodDBAdapter adapter = new PodDBAdapter(context); - adapter.open(); - adapter.setQueue(queue); - adapter.close(); - } - }); - if (performAutoDownload) { - new Thread() { - @Override - public void run() { - autodownloadUndownloadedItems(context); - } - }.start(); - } - } - }); - - } - - /** - * Adds FeedItems to the queue if they are not in the queue yet. The items - * are marked as 'read'. - */ - public void addQueueItem(final Context context, final FeedItem... items) { - if (items.length > 0) { - contentChanger.post(new Runnable() { - - @Override - public void run() { - for (FeedItem item : items) { - if (!queue.contains(item)) { - queue.add(item); - if (!item.isRead()) { - markItemRead(context, item, true, false); - } - } - } - eventDist.sendQueueUpdateBroadcast(); - dbExec.execute(new Runnable() { - - @Override - public void run() { - PodDBAdapter adapter = new PodDBAdapter(context); - adapter.open(); - adapter.setQueue(queue); - adapter.close(); - } - }); - new Thread() { - @Override - public void run() { - autodownloadUndownloadedItems(context); - } - }.start(); - } - }); - } - - } - - /** - * Return the item that comes after this item in the queue or null if this - * item is not in the queue or if this item has no successor. - */ - public FeedItem getQueueSuccessorOfItem(FeedItem item) { - if (isInQueue(item)) { - int itemIndex = queue.indexOf(item); - if (itemIndex != -1 && itemIndex < (queue.size() - 1)) { - return queue.get(itemIndex + 1); - } - } - return null; - } - - /** Removes all items in queue */ - public void clearQueue(final Context context) { - if (AppConfig.DEBUG) - Log.d(TAG, "Clearing queue"); - Iterator iter = queue.iterator(); - while (iter.hasNext()) { - FeedItem item = iter.next(); - if (item.getState() != FeedItem.State.PLAYING) { - iter.remove(); - } else { - if (AppConfig.DEBUG) - Log.d(TAG, - "FeedItem is playing and is therefore not removed from the queue"); - } - } - eventDist.sendQueueUpdateBroadcast(); - dbExec.execute(new Runnable() { - - @Override - public void run() { - PodDBAdapter adapter = new PodDBAdapter(context); - adapter.open(); - adapter.setQueue(queue); - adapter.close(); - } - }); - - } - - /** Removes a FeedItem from the queue. Uses external PodDBAdapter. */ - private void removeQueueItem(FeedItem item, PodDBAdapter adapter) { - boolean removed = queue.remove(item); - if (removed) { - adapter.setQueue(queue); - } - } - - /** Removes a FeedItem from the queue. */ - public void removeQueueItem(final Context context, FeedItem item, - final boolean performAutoDownload) { - boolean removed = queue.remove(item); - if (removed) { - dbExec.execute(new Runnable() { - - @Override - public void run() { - PodDBAdapter adapter = new PodDBAdapter(context); - adapter.open(); - adapter.setQueue(queue); - adapter.close(); - } - }); - - } - if (performAutoDownload) { - new Thread() { - @Override - public void run() { - autodownloadUndownloadedItems(context); - } - }.start(); - } - eventDist.sendQueueUpdateBroadcast(); - } - - /** - * Moves the queue item at the specified index to another position. If the - * indices are out of range, no operation will be performed. - * - * @param from - * index of the item that is going to be moved - * @param to - * destination index of item - * @param broadcastUpdate - * true if the method should send a queue update broadcast after - * the operation has been performed. This should be set to false - * if the order of the queue is changed through drag & drop - * reordering to avoid visual glitches. - */ - public void moveQueueItem(final Context context, int from, int to, - boolean broadcastUpdate) { - if (AppConfig.DEBUG) - Log.d(TAG, "Moving queue item from index " + from + " to index " - + to); - if (from >= 0 && from < queue.size() && to >= 0 && to < queue.size()) { - FeedItem item = queue.remove(from); - queue.add(to, item); - dbExec.execute(new Runnable() { - @Override - public void run() { - PodDBAdapter adapter = new PodDBAdapter(context); - adapter.open(); - adapter.setQueue(queue); - adapter.close(); - } - }); - if (broadcastUpdate) { - eventDist.sendQueueUpdateBroadcast(); - } - } - } - - /** Returns true if the specified item is in the queue. */ - public boolean isInQueue(FeedItem item) { - return queue.contains(item); - } - - /** - * Returns the FeedItem at the beginning of the queue or null if the queue - * is empty. - */ - public FeedItem getFirstQueueItem() { - if (queue.isEmpty()) { - return null; - } else { - return queue.get(0); - } - } - - private void addNewFeed(final Context context, final Feed feed) { - contentChanger.post(new Runnable() { - - @Override - public void run() { - feeds.add(feed); - Collections.sort(feeds, new FeedtitleComparator()); - eventDist.sendFeedUpdateBroadcast(); - } - }); - setCompleteFeed(context, feed); - } - - /** - * Updates an existing feed or adds it as a new one if it doesn't exist. - * - * @return The saved Feed with a database ID - */ - public Feed updateFeed(final Context context, final Feed newFeed) { - // Look up feed in the feedslist - final Feed savedFeed = searchFeedByIdentifyingValue(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 - addNewFeed(context, newFeed); - return newFeed; - } else { - if (AppConfig.DEBUG) - Log.d(TAG, "Feed with title " + newFeed.getTitle() - + " already exists. Syncing new with existing one."); - 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); - contentChanger.post(new Runnable() { - @Override - public void run() { - savedFeed.getItems().add(i, item); - - } - }); - markItemRead(context, item, false, false); - } else { - oldItem.updateFromOther(item); - } - } - // update attributes - savedFeed.setLastUpdate(newFeed.getLastUpdate()); - savedFeed.setType(newFeed.getType()); - setCompleteFeed(context, savedFeed); - new Thread() { - @Override - public void run() { - autodownloadUndownloadedItems(context); - } - }.start(); - return savedFeed; - } - - } - - /** Get a Feed by its identifying value. */ - private Feed searchFeedByIdentifyingValue(String identifier) { - for (Feed feed : feeds) { - if (feed.getIdentifyingValue().equals(identifier)) { - return feed; - } - } - return null; - } - - /** - * Returns true if a feed with the given download link is already in the - * feedlist. - */ - public boolean feedExists(String downloadUrl) { - for (Feed feed : feeds) { - if (feed.getDownload_url().equals(downloadUrl)) { - return true; - } - } - return false; - } - - /** Get a FeedItem by its identifying value. */ - private FeedItem searchFeedItemByIdentifyingValue(Feed feed, - String identifier) { - for (FeedItem item : feed.getItems()) { - if (item.getIdentifyingValue().equals(identifier)) { - return item; - } - } - return null; - } - - /** Updates Information of an existing Feed. Uses external adapter. */ - private void setFeed(Feed feed, PodDBAdapter adapter) { - if (adapter != null) { - adapter.setFeed(feed); - feed.cacheDescriptionsOfItems(); - } else { - Log.w(TAG, "Adapter in setFeed was null"); - } - } - - /** Updates Information of an existing Feeditem. Uses external adapter. */ - private void setFeedItem(FeedItem item, PodDBAdapter adapter) { - if (adapter != null) { - adapter.setSingleFeedItem(item); - } else { - Log.w(TAG, "Adapter in setFeedItem was null"); - } - } - - /** Updates Information of an existing Feedimage. Uses external adapter. */ - private void setFeedImage(FeedImage image, PodDBAdapter adapter) { - if (adapter != null) { - adapter.setImage(image); - } else { - Log.w(TAG, "Adapter in setFeedImage was null"); - } - } - - /** - * Updates Information of an existing Feedmedia object. Uses external - * adapter. - */ - private void setFeedImage(FeedMedia media, PodDBAdapter adapter) { - if (adapter != null) { - adapter.setMedia(media); - } else { - Log.w(TAG, "Adapter in setFeedMedia was null"); - } - } - - /** - * Updates Information of an existing Feed. Creates and opens its own - * adapter. - */ - public void setFeed(final Context context, final Feed feed) { - dbExec.execute(new Runnable() { - - @Override - public void run() { - PodDBAdapter adapter = new PodDBAdapter(context); - adapter.open(); - adapter.setFeed(feed); - feed.cacheDescriptionsOfItems(); - adapter.close(); - } - }); - - } - - /** - * Updates Information of an existing Feed and its FeedItems. Creates and - * opens its own adapter. - */ - public void setCompleteFeed(final Context context, final Feed feed) { - dbExec.execute(new Runnable() { - - @Override - public void run() { - PodDBAdapter adapter = new PodDBAdapter(context); - adapter.open(); - adapter.setCompleteFeed(feed); - feed.cacheDescriptionsOfItems(); - adapter.close(); - } - }); - - } - - /** - * Updates information of an existing FeedItem. Creates and opens its own - * adapter. - */ - public void setFeedItem(final Context context, final FeedItem item) { - dbExec.execute(new Runnable() { - - @Override - public void run() { - PodDBAdapter adapter = new PodDBAdapter(context); - adapter.open(); - adapter.setSingleFeedItem(item); - adapter.close(); - } - }); - - } - - /** - * Updates information of an existing FeedImage. Creates and opens its own - * adapter. - */ - public void setFeedImage(final Context context, final FeedImage image) { - dbExec.execute(new Runnable() { - - @Override - public void run() { - PodDBAdapter adapter = new PodDBAdapter(context); - adapter.open(); - adapter.setImage(image); - adapter.close(); - } - }); - - } - - /** - * Updates information of an existing FeedMedia object. Creates and opens - * its own adapter. - */ - public void setFeedMedia(final Context context, final FeedMedia media) { - dbExec.execute(new Runnable() { - - @Override - public void run() { - PodDBAdapter adapter = new PodDBAdapter(context); - adapter.open(); - adapter.setMedia(media); - adapter.close(); - } - }); - - } - - /** Get a Feed by its id */ - public Feed getFeed(long id) { - for (Feed f : feeds) { - if (f.id == id) { - return f; - } - } - Log.e(TAG, "Couldn't find Feed with id " + id); - return null; - } - - /** Get a Feed Image by its id */ - public FeedImage getFeedImage(long id) { - for (Feed f : feeds) { - FeedImage image = f.getImage(); - if (image != null && image.getId() == id) { - return image; - } - } - return null; - } - - /** Get a Feed Item by its id and its feed */ - public FeedItem getFeedItem(long id, Feed feed) { - if (feed != null) { - for (FeedItem item : feed.getItems()) { - if (item.getId() == id) { - return item; - } - } - } - Log.e(TAG, "Couldn't find FeedItem with id " + id); - return null; - } - - /** Get a FeedItem by its id and the id of its feed. */ - public FeedItem getFeedItem(long itemId, long feedId) { - Feed feed = getFeed(feedId); - if (feed != null && feed.getItems() != null) { - for (FeedItem item : feed.getItems()) { - if (item.getId() == itemId) { - return item; - } - } - } - return null; - } - - /** Get a FeedMedia object by the id of the Media object and the feed object */ - public FeedMedia getFeedMedia(long id, Feed feed) { - if (feed != null) { - for (FeedItem item : feed.getItems()) { - if (item.getMedia() != null && item.getMedia().getId() == id) { - return item.getMedia(); - } - } - } - Log.e(TAG, "Couldn't find FeedMedia with id " + id); - if (feed == null) - Log.e(TAG, "Feed was null"); - return null; - } - - /** Get a FeedMedia object by the id of the Media object. */ - public FeedMedia getFeedMedia(long id) { - for (Feed feed : feeds) { - for (FeedItem item : feed.getItems()) { - if (item.getMedia() != null && item.getMedia().getId() == id) { - return item.getMedia(); - } - } - } - Log.w(TAG, "Couldn't find FeedMedia with id " + id); - return null; - } - - /** Get a download status object from the download log by its FeedFile. */ - public DownloadStatus getDownloadStatus(FeedFile feedFile) { - return null; - } - - /** Reads the database */ - public void loadDBData(Context context) { - feeds.clear(); - PodDBAdapter adapter = new PodDBAdapter(context); - adapter.open(); - extractFeedlistFromCursor(context, adapter); - extractDownloadLogFromCursor(context, adapter); - extractQueueFromCursor(context, adapter); - adapter.close(); - Collections.sort(feeds, new FeedtitleComparator()); - Collections.sort(unreadItems, new FeedItemPubdateComparator()); - cleanupPlaybackHistory(); - } - - private void extractFeedlistFromCursor(Context context, PodDBAdapter adapter) { - if (AppConfig.DEBUG) - Log.d(TAG, "Extracting Feedlist"); - Cursor feedlistCursor = adapter.getAllFeedsCursor(); - if (feedlistCursor.moveToFirst()) { - do { - Date lastUpdate = new Date( - feedlistCursor - .getLong(PodDBAdapter.KEY_LAST_UPDATE_INDEX)); - Feed feed = new Feed(lastUpdate); - - feed.id = feedlistCursor.getLong(PodDBAdapter.KEY_ID_INDEX); - feed.setTitle(feedlistCursor - .getString(PodDBAdapter.KEY_TITLE_INDEX)); - feed.setLink(feedlistCursor - .getString(PodDBAdapter.KEY_LINK_INDEX)); - feed.setDescription(feedlistCursor - .getString(PodDBAdapter.KEY_DESCRIPTION_INDEX)); - feed.setPaymentLink(feedlistCursor - .getString(PodDBAdapter.KEY_PAYMENT_LINK_INDEX)); - feed.setAuthor(feedlistCursor - .getString(PodDBAdapter.KEY_AUTHOR_INDEX)); - feed.setLanguage(feedlistCursor - .getString(PodDBAdapter.KEY_LANGUAGE_INDEX)); - feed.setType(feedlistCursor - .getString(PodDBAdapter.KEY_TYPE_INDEX)); - feed.setFeedIdentifier(feedlistCursor - .getString(PodDBAdapter.KEY_FEED_IDENTIFIER_INDEX)); - long imageIndex = feedlistCursor - .getLong(PodDBAdapter.KEY_IMAGE_INDEX); - if (imageIndex != 0) { - feed.setImage(DBReader.getFeedImage(context, imageIndex)); - feed.getImage().setFeed(feed); - } - feed.file_url = feedlistCursor - .getString(PodDBAdapter.KEY_FILE_URL_INDEX); - feed.download_url = feedlistCursor - .getString(PodDBAdapter.KEY_DOWNLOAD_URL_INDEX); - feed.setDownloaded(feedlistCursor - .getInt(PodDBAdapter.KEY_DOWNLOADED_INDEX) > 0); - // Get FeedItem-Object - Cursor itemlistCursor = adapter.getAllItemsOfFeedCursor(feed); - feed.setItems(extractFeedItemsFromCursor(context, feed, - itemlistCursor, adapter)); - itemlistCursor.close(); - - feeds.add(feed); - } while (feedlistCursor.moveToNext()); - } - feedlistCursor.close(); - - } - - private ArrayList extractFeedItemsFromCursor(Context context, - Feed feed, Cursor itemlistCursor, PodDBAdapter adapter) { - if (AppConfig.DEBUG) - Log.d(TAG, "Extracting Feeditems of feed " + feed.getTitle()); - ArrayList items = new ArrayList(); - ArrayList mediaIds = new ArrayList(); - - if (itemlistCursor.moveToFirst()) { - do { - FeedItem item = new FeedItem(); - - item.id = itemlistCursor.getLong(PodDBAdapter.IDX_FI_SMALL_ID); - item.setFeed(feed); - item.setTitle(itemlistCursor - .getString(PodDBAdapter.IDX_FI_SMALL_TITLE)); - item.setLink(itemlistCursor - .getString(PodDBAdapter.IDX_FI_SMALL_LINK)); - item.setPubDate(new Date(itemlistCursor - .getLong(PodDBAdapter.IDX_FI_SMALL_PUBDATE))); - item.setPaymentLink(itemlistCursor - .getString(PodDBAdapter.IDX_FI_SMALL_PAYMENT_LINK)); - long mediaId = itemlistCursor - .getLong(PodDBAdapter.IDX_FI_SMALL_MEDIA); - if (mediaId != 0) { - mediaIds.add(String.valueOf(mediaId)); - item.setMedia(new FeedMedia(mediaId, item)); - } - item.setRead((itemlistCursor - .getInt(PodDBAdapter.IDX_FI_SMALL_READ) > 0) ? true - : false); - item.setItemIdentifier(itemlistCursor - .getString(PodDBAdapter.IDX_FI_SMALL_ITEM_IDENTIFIER)); - if (item.getState() == FeedItem.State.NEW) { - unreadItems.add(item); - } - - // extract chapters - boolean hasSimpleChapters = itemlistCursor - .getInt(PodDBAdapter.IDX_FI_SMALL_HAS_CHAPTERS) > 0; - if (hasSimpleChapters) { - Cursor chapterCursor = adapter - .getSimpleChaptersOfFeedItemCursor(item); - if (chapterCursor.moveToFirst()) { - item.setChapters(new ArrayList()); - do { - int chapterType = chapterCursor - .getInt(PodDBAdapter.KEY_CHAPTER_TYPE_INDEX); - Chapter chapter = null; - long start = chapterCursor - .getLong(PodDBAdapter.KEY_CHAPTER_START_INDEX); - String title = chapterCursor - .getString(PodDBAdapter.KEY_TITLE_INDEX); - String link = chapterCursor - .getString(PodDBAdapter.KEY_CHAPTER_LINK_INDEX); - - switch (chapterType) { - case SimpleChapter.CHAPTERTYPE_SIMPLECHAPTER: - chapter = new SimpleChapter(start, title, item, - link); - break; - case ID3Chapter.CHAPTERTYPE_ID3CHAPTER: - chapter = new ID3Chapter(start, title, item, - link); - break; - case VorbisCommentChapter.CHAPTERTYPE_VORBISCOMMENT_CHAPTER: - chapter = new VorbisCommentChapter(start, - title, item, link); - break; - } - chapter.setId(chapterCursor - .getLong(PodDBAdapter.KEY_ID_INDEX)); - item.getChapters().add(chapter); - } while (chapterCursor.moveToNext()); - } - chapterCursor.close(); - } - items.add(item); - } while (itemlistCursor.moveToNext()); - } - extractMediafromFeedItemlist(adapter, items, mediaIds); - Collections.sort(items, new FeedItemPubdateComparator()); - return items; - } - - private void extractMediafromFeedItemlist(PodDBAdapter adapter, - ArrayList items, ArrayList mediaIds) { - ArrayList itemsCopy = new ArrayList(items); - Cursor cursor = adapter.getFeedMediaCursorByItemID(mediaIds - .toArray(new String[mediaIds.size()])); - if (cursor.moveToFirst()) { - do { - long mediaId = cursor.getLong(PodDBAdapter.KEY_ID_INDEX); - // find matching feed item - FeedItem item = getMatchingItemForMedia(mediaId, itemsCopy); - itemsCopy.remove(item); - if (item != null) { - Date playbackCompletionDate = null; - long playbackCompletionTime = cursor - .getLong(PodDBAdapter.KEY_PLAYBACK_COMPLETION_DATE_INDEX); - if (playbackCompletionTime > 0) { - playbackCompletionDate = new Date( - playbackCompletionTime); - } - - item.setMedia(new FeedMedia( - mediaId, - item, - cursor.getInt(PodDBAdapter.KEY_DURATION_INDEX), - cursor.getInt(PodDBAdapter.KEY_POSITION_INDEX), - cursor.getLong(PodDBAdapter.KEY_SIZE_INDEX), - cursor.getString(PodDBAdapter.KEY_MIME_TYPE_INDEX), - cursor.getString(PodDBAdapter.KEY_FILE_URL_INDEX), - cursor.getString(PodDBAdapter.KEY_DOWNLOAD_URL_INDEX), - cursor.getInt(PodDBAdapter.KEY_DOWNLOADED_INDEX) > 0, - playbackCompletionDate)); - if (playbackCompletionDate != null) { - playbackHistory.add(item); - } - - } - } while (cursor.moveToNext()); - cursor.close(); - } - } - - private FeedItem getMatchingItemForMedia(long mediaId, - ArrayList items) { - for (FeedItem item : items) { - if (item.getMedia() != null && item.getMedia().getId() == mediaId) { - return item; - } - } - return null; - } - - private void extractDownloadLogFromCursor(Context context, - PodDBAdapter adapter) { - if (AppConfig.DEBUG) - Log.d(TAG, "Extracting DownloadLog"); - Cursor logCursor = adapter.getDownloadLogCursor(); - if (logCursor.moveToFirst()) { - do { - long id = logCursor.getLong(PodDBAdapter.KEY_ID_INDEX); - FeedFile feedfile = null; - - long feedfileId = logCursor - .getLong(PodDBAdapter.KEY_FEEDFILE_INDEX); - int feedfileType = logCursor - .getInt(PodDBAdapter.KEY_FEEDFILETYPE_INDEX); - if (feedfileId != 0) { - switch (feedfileType) { - case Feed.FEEDFILETYPE_FEED: - feedfile = getFeed(feedfileId); - break; - case FeedImage.FEEDFILETYPE_FEEDIMAGE: - feedfile = getFeedImage(feedfileId); - break; - case FeedMedia.FEEDFILETYPE_FEEDMEDIA: - feedfile = getFeedMedia(feedfileId); - } - } - boolean successful = logCursor - .getInt(PodDBAdapter.KEY_SUCCESSFUL_INDEX) > 0; - int reason = logCursor.getInt(PodDBAdapter.KEY_REASON_INDEX); - String reasonDetailed = logCursor - .getString(PodDBAdapter.KEY_REASON_DETAILED_INDEX); - String title = logCursor - .getString(PodDBAdapter.KEY_DOWNLOADSTATUS_TITLE_INDEX); - Date completionDate = new Date( - logCursor - .getLong(PodDBAdapter.KEY_COMPLETION_DATE_INDEX)); - downloadLog.add(new DownloadStatus(id, title, feedfileId, - feedfileType, successful, reason, completionDate, - reasonDetailed)); - - } while (logCursor.moveToNext()); - } - logCursor.close(); - Collections.sort(downloadLog, new DownloadStatusComparator()); - } - - private void extractQueueFromCursor(Context context, PodDBAdapter adapter) { - if (AppConfig.DEBUG) - Log.d(TAG, "Extracting Queue"); - Cursor cursor = adapter.getQueueCursor(); - - // Sort cursor results by ID with TreeMap - TreeMap map = new TreeMap(); - - if (cursor.moveToFirst()) { - do { - int index = cursor.getInt(PodDBAdapter.KEY_ID_INDEX); - Feed feed = getFeed(cursor - .getLong(PodDBAdapter.KEY_QUEUE_FEED_INDEX)); - if (feed != null) { - FeedItem item = getFeedItem( - cursor.getLong(PodDBAdapter.KEY_FEEDITEM_INDEX), - feed); - if (item != null) { - map.put(index, item); - } - } - } while (cursor.moveToNext()); - } - cursor.close(); - - for (Map.Entry entry : map.entrySet()) { - FeedItem item = entry.getValue(); - queue.add(item); - } - } - - /** - * Loads description and contentEncoded values from the database and caches - * it in the feeditem. The task callback will contain a String-array with - * the description at index 0 and the value of contentEncoded at index 1. - */ - public void loadExtraInformationOfItem(final Context context, - final FeedItem item, FeedManager.TaskCallback callback) { - if (AppConfig.DEBUG) { - Log.d(TAG, - "Loading extra information of item with id " + item.getId()); - if (item.getTitle() != null) { - Log.d(TAG, "Title: " + item.getTitle()); - } - } - dbExec.execute(new FeedManager.Task(new Handler(), callback) { - - @Override - public void execute() { - PodDBAdapter adapter = new PodDBAdapter(context); - adapter.open(); - Cursor extraCursor = adapter.getExtraInformationOfItem(item); - if (extraCursor.moveToFirst()) { - String description = extraCursor - .getString(PodDBAdapter.IDX_FI_EXTRA_DESCRIPTION); - String contentEncoded = extraCursor - .getString(PodDBAdapter.IDX_FI_EXTRA_CONTENT_ENCODED); - item.setCachedDescription(description); - item.setCachedContentEncoded(contentEncoded); - setResult(new String[] { description, contentEncoded }); - } - adapter.close(); - } - }); - } -/* - - */ -/** - * 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, - FeedManager.QueryTaskCallback callback) { - dbExec.execute(new FeedManager.QueryTask(context, new Handler(), - callback) { - - @Override - public void execute(PodDBAdapter adapter) { - Cursor searchResult = adapter.searchItemDescriptions(feed, - query); - setResult(searchResult); - } - }); - } -*/ -/* - - *//* - -*/ -/** - * 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, - FeedManager.QueryTaskCallback callback) { - dbExec.execute(new FeedManager.QueryTask(context, new Handler(), - callback) { - - @Override - public void execute(PodDBAdapter adapter) { - Cursor searchResult = adapter.searchItemContentEncoded(feed, - query); - setResult(searchResult); - } - }); - } -*//* - - - /** Returns the number of feeds that are currently in the feeds list. */ - public int getFeedsSize() { - return feeds.size(); - } - - /** Returns the feed at the specified index of the feeds list. */ - public Feed getFeedAtIndex(int index) { - return feeds.get(index); - } - - /** Returns an array that contains all feeds of the feed manager. */ - public Feed[] getFeedsArray() { - return feeds.toArray(new Feed[feeds.size()]); - } - - List getFeeds() { - return feeds; - } - - /** - * Returns the number of items that are currently in the queue. - * - * @param enableEpisodeFilter - * true if items without episodes should be ignored by this - * method if the episode filter was enabled by the user. - * */ - public int getQueueSize(boolean enableEpisodeFilter) { - if (UserPreferences.isDisplayOnlyEpisodes() && enableEpisodeFilter) { - return EpisodeFilter.countItemsWithEpisodes(queue); - } else { - return queue.size(); - } - } - - /** - * Returns the FeedItem at the specified index of the queue. - * - * @param enableEpisodeFilter - * true if items without episodes should be ignored by this - * method if the episode filter was enabled by the user. - * - * @throws IndexOutOfBoundsException - * if index is out of range - * */ - public FeedItem getQueueItemAtIndex(int index, boolean enableEpisodeFilter) { - if (UserPreferences.isDisplayOnlyEpisodes() && enableEpisodeFilter) { - return EpisodeFilter.accessEpisodeByIndex(queue, index); - } else { - return queue.get(index); - } - } - - /** - * Returns the index of the episode that is currently being played in the - * queue or -1 if the queue is empty or no episode in the queue is being - * played. - * */ - public int getQueuePlayingEpisodeIndex() { - FeedManager manager = FeedManager.getInstance(); - int queueSize = manager.getQueueSize(true); - if (queueSize == 0) { - return -1; - } else { - for (int x = 0; x < queueSize; x++) { - FeedItem item = getQueueItemAtIndex(x, true); - if (item.getState() == FeedItem.State.PLAYING) { - return x; - } - } - return -1; - } - } - - /** - * Returns the number of unread items. - * - * @param enableEpisodeFilter - * true if items without episodes should be ignored by this - * method if the episode filter was enabled by the user. - * */ - public int getUnreadItemsSize(boolean enableEpisodeFilter) { - if (UserPreferences.isDisplayOnlyEpisodes() && enableEpisodeFilter) { - return EpisodeFilter.countItemsWithEpisodes(unreadItems); - } else { - return unreadItems.size(); - } - } - - /** - * Returns the FeedItem at the specified index of the unread items list. - * - * @param enableEpisodeFilter - * true if items without episodes should be ignored by this - * method if the episode filter was enabled by the user. - * - * @throws IndexOutOfBoundsException - * if index is out of range - * */ - public FeedItem getUnreadItemAtIndex(int index, boolean enableEpisodeFilter) { - if (UserPreferences.isDisplayOnlyEpisodes() && enableEpisodeFilter) { - return EpisodeFilter.accessEpisodeByIndex(unreadItems, index); - } else { - return unreadItems.get(index); - } - } - - /** - * Returns the number of items in the playback history. - * */ - public int getPlaybackHistorySize() { - return playbackHistory.size(); - } - - /** - * Returns the FeedItem at the specified index of the playback history. - * - * @throws IndexOutOfBoundsException - * if index is out of range - * */ - public FeedItem getPlaybackHistoryItemIndex(int index) { - return playbackHistory.get(index); - } - - /** Returns the number of items in the download log */ - public int getDownloadLogSize() { - return downloadLog.size(); - } - - /** Returns the download status at the specified index of the download log. */ - public DownloadStatus getDownloadStatusFromLogAtIndex(int index) { - return downloadLog.get(index); - } - - /** Is called by a FeedManagerTask after completion. */ - public interface TaskCallback { - 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 implements Runnable { - private Handler handler; - private TaskCallback 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 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; - } - } - -} diff --git a/src/de/danoeh/antennapod/storage/FeedSearcher.java b/src/de/danoeh/antennapod/storage/FeedSearcher.java index a16430056..e7aa93f83 100644 --- a/src/de/danoeh/antennapod/storage/FeedSearcher.java +++ b/src/de/danoeh/antennapod/storage/FeedSearcher.java @@ -2,7 +2,6 @@ package de.danoeh.antennapod.storage; import android.content.Context; import de.danoeh.antennapod.R; -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; @@ -55,227 +54,4 @@ public class FeedSearcher { Collections.sort(result, new SearchResultValueComparator()); return result; } - /* - *//** Performs a search in all feeds or one specific feed. *//* - public static ArrayList performSearch(final Context context, - final String query, final Feed selectedFeed) { - final String lcQuery = query.toLowerCase(); - final ArrayList result = new ArrayList(); - if (selectedFeed == null) { - if (AppConfig.DEBUG) - Log.d(TAG, "Performing global search"); - if (AppConfig.DEBUG) - Log.d(TAG, "Searching Feed titles"); - searchFeedtitles(lcQuery, result); - } else if (AppConfig.DEBUG) { - Log.d(TAG, "Performing search on specific feed"); - } - - if (AppConfig.DEBUG) - Log.d(TAG, "Searching Feeditem titles"); - searchFeedItemTitles(lcQuery, result, selectedFeed); - - if (AppConfig.DEBUG) - Log.d(TAG, "Searching item-chaptertitles"); - searchFeedItemChapters(lcQuery, result, selectedFeed); - - Looper.prepare(); - DBTasks.searchFeedItemDescription(context, selectedFeed, lcQuery, - new DBTasks.QueryTaskCallback() { - - @Override - public void handleResult(Cursor cResult) { - searchFeedItemContentEncodedCursor(context, lcQuery, result, - selectedFeed, cResult); - - } - - @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) - Log.d(TAG, "Sorting results"); - Collections.sort(result, new SearchResultValueComparator()); - - return result; - } - - private static void searchFeedtitles(String query, - ArrayList destination) { - FeedManager manager = FeedManager.getInstance(); - for (Feed feed : manager.getFeeds()) { - SearchResult result = createSearchResult(feed, query, feed - .getTitle().toLowerCase(), VALUE_FEED_TITLE); - if (result != null) { - destination.add(result); - } - } - } - - private static void searchFeedItemTitles(String query, - ArrayList destination, Feed selectedFeed) { - FeedManager manager = FeedManager.getInstance(); - if (selectedFeed == null) { - for (Feed feed : manager.getFeeds()) { - searchFeedItemTitlesSingleFeed(query, destination, feed); - } - } else { - searchFeedItemTitlesSingleFeed(query, destination, selectedFeed); - } - } - - private static void searchFeedItemTitlesSingleFeed(String query, - ArrayList destination, Feed feed) { - for (FeedItem item : feed.getItems()) { - SearchResult result = createSearchResult(item, query, item - .getTitle().toLowerCase(), VALUE_ITEM_TITLE); - if (result != null) { - result.setSubtitle(PodcastApp.getInstance().getString( - R.string.found_in_title_label)); - destination.add(result); - } - - } - } - - private static void searchFeedItemChapters(String query, - ArrayList destination, Feed selectedFeed) { - if (selectedFeed == null) { - for (Feed feed : manager.getFeeds()) { - searchFeedItemChaptersSingleFeed(query, destination, feed); - } - } else { - searchFeedItemChaptersSingleFeed(query, destination, selectedFeed); - } - } - - private static void searchFeedItemChaptersSingleFeed(String query, - ArrayList destination, Feed feed) { - for (FeedItem item : feed.getItems()) { - if (item.getChapters() != null) { - for (Chapter sc : item.getChapters()) { - SearchResult result = createSearchResult(item, query, sc - .getTitle().toLowerCase(), VALUE_ITEM_CHAPTER); - if (result != null) { - result.setSubtitle(PodcastApp.getInstance().getString( - R.string.found_in_chapters_label)); - destination.add(result); - } - } - } - } - } - - private static void searchFeedItemDescriptionCursor(Context context, String query, - ArrayList destination, Feed feed, Cursor cursor) { - FeedManager manager = FeedManager.getInstance(); - List items = DBReader.extractItemlistFromCursor(cursor); - if (cursor.moveToFirst()) { - do { - final long itemId = cursor - .getLong(PodDBAdapter.IDX_FI_EXTRA_ID); - String content = cursor - .getString(PodDBAdapter.IDX_FI_EXTRA_DESCRIPTION); - if (content != null) { - content = content.toLowerCase(); - final long feedId = cursor - .getLong(PodDBAdapter.IDX_FI_EXTRA_FEED); - FeedItem item = null; - if (feed == null) { - item = manager.getFeedItem(itemId, feedId); - } else { - item = manager.getFeedItem(itemId, feed); - } - if (item != null) { - SearchResult searchResult = createSearchResult(item, - query, content, VALUE_ITEM_DESCRIPTION); - if (searchResult != null) { - searchResult.setSubtitle(PodcastApp.getInstance() - .getString( - R.string.found_in_shownotes_label)); - destination.add(searchResult); - - } - } - } - - } while (cursor.moveToNext()); - } - } - - private static void searchFeedItemContentEncodedCursor(Context context, String query, - ArrayList destination, Feed feed, Cursor cursor) { - if (cursor.moveToFirst()) { - do { - final long itemId = cursor - .getLong(PodDBAdapter.IDX_FI_EXTRA_ID); - String content = cursor - .getString(PodDBAdapter.IDX_FI_EXTRA_CONTENT_ENCODED); - if (content != null) { - content = content.toLowerCase(); - - final long feedId = cursor - .getLong(PodDBAdapter.IDX_FI_EXTRA_FEED); - FeedItem item = null; - if (feed == null) { - item = manager.getFeedItem(itemId, feedId); - } else { - item = manager.getFeedItem(itemId, feed); - } - if (item != null) { - SearchResult searchResult = createSearchResult(item, - query, content, VALUE_ITEM_DESCRIPTION); - if (searchResult != null) { - searchResult.setSubtitle(PodcastApp.getInstance() - .getString( - R.string.found_in_shownotes_label)); - destination.add(searchResult); - } - } - } - } while (cursor.moveToNext()); - } - } - - private static SearchResult createSearchResult(FeedComponent component, - String query, String text, int baseValue) { - int bonus = 0; - boolean found = false; - // try word search - Pattern word = Pattern.compile("\b" + query + "\b"); - Matcher matcher = word.matcher(text); - found = matcher.find(); - if (found) { - bonus = VALUE_WORD_MATCH; - } else { - // search for other occurence - found = text.contains(query); - } - - if (found) { - return new SearchResult(component, baseValue + bonus); - } else { - return null; - } - }*/ - }