package de.danoeh.antennapodsp.storage;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.util.Log;
import de.danoeh.antennapodsp.AppConfig;
import de.danoeh.antennapodsp.feed.*;
import de.danoeh.antennapodsp.service.download.DownloadStatus;
import de.danoeh.antennapodsp.util.DownloadError;
import de.danoeh.antennapodsp.util.comparator.DownloadStatusComparator;
import de.danoeh.antennapodsp.util.comparator.FeedItemPubdateComparator;
import de.danoeh.antennapodsp.util.comparator.PlaybackCompletionDateComparator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
/**
* Provides methods for reading data from the AntennaPod database.
* In general, all database calls in DBReader-methods are executed on the caller's thread.
* This means that the caller should make sure that DBReader-methods are not executed on the GUI-thread.
* This class will use the {@link de.danoeh.antennapodsp.feed.EventDistributor} to notify listeners about changes in the database.
*/
public final class DBReader {
private static final String TAG = "DBReader";
/**
* Maximum size of the list returned by {@link #getPlaybackHistory(android.content.Context)}.
*/
public static final int PLAYBACK_HISTORY_SIZE = 50;
/**
* Maximum size of the list returned by {@link #getDownloadLog(android.content.Context)}.
*/
public static final int DOWNLOAD_LOG_SIZE = 200;
private DBReader() {
}
/**
* Returns a list of Feeds, sorted alphabetically by their title.
*
* @param context A context that is used for opening a database connection.
* @return A list of Feeds, sorted alphabetically by their title. A Feed-object
* of the returned list does NOT have its list of FeedItems yet. The FeedItem-list
* can be loaded separately with {@link #getFeedItemList(android.content.Context, de.danoeh.antennapodsp.feed.Feed)}.
*/
public static List<Feed> getFeedList(final Context context) {
if (AppConfig.DEBUG)
Log.d(TAG, "Extracting Feedlist");
PodDBAdapter adapter = new PodDBAdapter(context);
adapter.open();
Cursor feedlistCursor = adapter.getAllFeedsCursor();
List<Feed> feeds = new ArrayList<Feed>(feedlistCursor.getCount());
if (feedlistCursor.moveToFirst()) {
do {
Feed feed = extractFeedFromCursorRow(adapter, feedlistCursor);
feeds.add(feed);
} while (feedlistCursor.moveToNext());
}
feedlistCursor.close();
return feeds;
}
/**
* Returns a list with the download URLs of all feeds.
*
* @param context A context that is used for opening the database connection.
* @return A list of Strings with the download URLs of all feeds.
*/
public static List<String> getFeedListDownloadUrls(final Context context) {
PodDBAdapter adapter = new PodDBAdapter(context);
List<String> result = new ArrayList<String>();
adapter.open();
Cursor feeds = adapter.getFeedCursorDownloadUrls();
if (feeds.moveToFirst()) {
do {
result.add(feeds.getString(1));
} while (feeds.moveToNext());
}
feeds.close();
adapter.close();
return result;
}
/**
* Returns a list of 'expired Feeds', i.e. Feeds that have not been updated for a certain amount of time.
*
* @param context A context that is used for opening a database connection.
* @param expirationTime Time that is used for determining whether a feed is outdated or not.
* A Feed is considered expired if 'lastUpdate < (currentTime - expirationTime)' evaluates to true.
* @return A list of Feeds, sorted alphabetically by their title. A Feed-object
* of the returned list does NOT have its list of FeedItems yet. The FeedItem-list
* can be loaded separately with {@link #getFeedItemList(android.content.Context, de.danoeh.antennapodsp.feed.Feed)}.
*/
public static List<Feed> getExpiredFeedsList(final Context context, final long expirationTime) {
if (AppConfig.DEBUG)
Log.d(TAG, String.format("getExpiredFeedsList(%d)", expirationTime));
PodDBAdapter adapter = new PodDBAdapter(context);
adapter.open();
Cursor feedlistCursor = adapter.getExpiredFeedsCursor(expirationTime);
List<Feed> feeds = new ArrayList<Feed>(feedlistCursor.getCount());
if (feedlistCursor.moveToFirst()) {
do {
Feed feed = extractFeedFromCursorRow(adapter, feedlistCursor);
feeds.add(feed);
} while (feedlistCursor.moveToNext());
}
feedlistCursor.close();
return feeds;
}
/**
* Takes a list of FeedItems and loads their corresponding Feed-objects from the database.
* The feedID-attribute of a FeedItem must be set to the ID of its feed or the method will
* not find the correct feed of an item.
*
* @param context A context that is used for opening a database connection.
* @param items The FeedItems whose Feed-objects should be loaded.
*/
public static void loadFeedDataOfFeedItemlist(Context context,
List<FeedItem> items) {
List<Feed> feeds = getFeedList(context);
for (FeedItem item : items) {
for (Feed feed : feeds) {
if (feed.getId() == item.getFeedId()) {
item.setFeed(feed);
break;
}
}
if (item.getFeed() == null) {
Log.w(TAG, "No match found for item with ID " + item.getId() + ". Feed ID was " + item.getFeedId());
}
}
}
/**
* Loads the list of FeedItems for a certain Feed-object. This method should NOT be used if the FeedItems are not
* used. In order to get information ABOUT the list of FeedItems, consider using {@link #getFeedStatisticsList(android.content.Context)} instead.
*
* @param context A context that is used for opening a database connection.
* @param feed The Feed whose items should be loaded
* @return A list with the FeedItems of the Feed. The Feed-attribute of the FeedItems will already be set correctly.
* The method does NOT change the items-attribute of the feed.
*/
public static List<FeedItem> getFeedItemList(Context context,
final Feed feed) {
if (AppConfig.DEBUG)
Log.d(TAG, "Extracting Feeditems of feed " + feed.getTitle());
PodDBAdapter adapter = new PodDBAdapter(context);
adapter.open();
Cursor itemlistCursor = adapter.getAllItemsOfFeedCursor(feed);
List<FeedItem> items = extractItemlistFromCursor(adapter,
itemlistCursor);
itemlistCursor.close();
Collections.sort(items, new FeedItemPubdateComparator());
adapter.close();
for (FeedItem item : items) {
item.setFeed(feed);
}
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>();
List<FeedItem> items = new ArrayList<FeedItem>(
itemlistCursor.getCount());
if (itemlistCursor.moveToFirst()) {
do {
FeedItem item = new FeedItem();
item.setId(itemlistCursor.getLong(PodDBAdapter.IDX_FI_SMALL_ID));
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));
item.setFeedId(itemlistCursor
.getLong(PodDBAdapter.IDX_FI_SMALL_FEED));
itemIds.add(String.valueOf(item.getId()));
item.setRead((itemlistCursor
.getInt(PodDBAdapter.IDX_FI_SMALL_READ) > 0));
item.setItemIdentifier(itemlistCursor
.getString(PodDBAdapter.IDX_FI_SMALL_ITEM_IDENTIFIER));
final FeedImage image;
long imageIndex = itemlistCursor.getLong(PodDBAdapter.IDX_FI_SMALL_IMAGE);
if (imageIndex != 0) {
item.setImage(getFeedImage(adapter, imageIndex));
}
// 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<Chapter>());
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;
}
if (chapter != null) {
chapter.setId(chapterCursor
.getLong(PodDBAdapter.KEY_ID_INDEX));
item.getChapters().add(chapter);
}
} while (chapterCursor.moveToNext());
}
chapterCursor.close();
}
items.add(item);
} while (itemlistCursor.moveToNext());
}
extractMediafromItemlist(adapter, items, itemIds);
return items;
}
private static void extractMediafromItemlist(PodDBAdapter adapter,
List<FeedItem> items, ArrayList<String> itemIds) {
List<FeedItem> itemsCopy = new ArrayList<FeedItem>(items);
Cursor cursor = adapter.getFeedMediaCursorByItemID(itemIds
.toArray(new String[itemIds.size()]));
if (cursor.moveToFirst()) {
do {
long itemId = cursor.getLong(PodDBAdapter.KEY_MEDIA_FEEDITEM_INDEX);
// find matching feed item
FeedItem item = getMatchingItemForMedia(itemId, itemsCopy);
if (item != null) {
item.setMedia(extractFeedMediaFromCursorRow(cursor));
item.getMedia().setItem(item);
}
} while (cursor.moveToNext());
cursor.close();
}
}
private static FeedMedia extractFeedMediaFromCursorRow(final Cursor cursor) {
long mediaId = cursor.getLong(PodDBAdapter.KEY_ID_INDEX);
Date playbackCompletionDate = null;
long playbackCompletionTime = cursor
.getLong(PodDBAdapter.KEY_PLAYBACK_COMPLETION_DATE_INDEX);
if (playbackCompletionTime > 0) {
playbackCompletionDate = new Date(
playbackCompletionTime);
}
return new FeedMedia(
mediaId,
null,
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);
}
private static Feed extractFeedFromCursorRow(PodDBAdapter adapter,
Cursor cursor) {
Date lastUpdate = new Date(
cursor.getLong(PodDBAdapter.IDX_FEED_SEL_STD_LASTUPDATE));
final FeedImage image;
long imageIndex = cursor.getLong(PodDBAdapter.IDX_FEED_SEL_STD_IMAGE);
if (imageIndex != 0) {
image = getFeedImage(adapter, imageIndex);
} else {
image = null;
}
Feed feed = new Feed(cursor.getLong(PodDBAdapter.IDX_FEED_SEL_STD_ID),
lastUpdate,
cursor.getString(PodDBAdapter.IDX_FEED_SEL_STD_TITLE),
cursor.getString(PodDBAdapter.IDX_FEED_SEL_STD_LINK),
cursor.getString(PodDBAdapter.IDX_FEED_SEL_STD_DESCRIPTION),
cursor.getString(PodDBAdapter.IDX_FEED_SEL_STD_PAYMENT_LINK),
cursor.getString(PodDBAdapter.IDX_FEED_SEL_STD_AUTHOR),
cursor.getString(PodDBAdapter.IDX_FEED_SEL_STD_LANGUAGE),
cursor.getString(PodDBAdapter.IDX_FEED_SEL_STD_TYPE),
cursor.getString(PodDBAdapter.IDX_FEED_SEL_STD_FEED_IDENTIFIER),
image,
cursor.getString(PodDBAdapter.IDX_FEED_SEL_STD_FILE_URL),
cursor.getString(PodDBAdapter.IDX_FEED_SEL_STD_DOWNLOAD_URL),
cursor.getInt(PodDBAdapter.IDX_FEED_SEL_STD_DOWNLOADED) > 0);
if (image != null) {
image.setOwner(feed);
}
FeedPreferences preferences = new FeedPreferences(cursor.getLong(PodDBAdapter.IDX_FEED_SEL_STD_ID),
cursor.getInt(PodDBAdapter.IDX_FEED_SEL_PREFERENCES_AUTO_DOWNLOAD) > 0);
feed.setPreferences(preferences);
return feed;
}
private static FeedItem getMatchingItemForMedia(long itemId,
List<FeedItem> items) {
for (FeedItem item : items) {
if (item.getId() == itemId) {
return item;
}
}
return null;
}
static List<FeedItem> getQueue(Context context, PodDBAdapter adapter) {
if (AppConfig.DEBUG)
Log.d(TAG, "Extracting queue");
Cursor itemlistCursor = adapter.getQueueCursor();
List<FeedItem> items = extractItemlistFromCursor(adapter,
itemlistCursor);
itemlistCursor.close();
loadFeedDataOfFeedItemlist(context, items);
return items;
}
/**
* Loads the IDs of the FeedItems in the queue. This method should be preferred over
* {@link #getQueue(android.content.Context)} if the FeedItems of the queue are not needed.
*
* @param context A context that is used for opening a database connection.
* @return A list of IDs sorted by the same order as the queue. The caller can wrap the returned
* list in a {@link de.danoeh.antennapodsp.util.QueueAccess} object for easier access to the queue's properties.
*/
public static List<Long> getQueueIDList(Context context) {
PodDBAdapter adapter = new PodDBAdapter(context);
adapter.open();
List<Long> result = getQueueIDList(adapter);
adapter.close();
return result;
}
static List<Long> getQueueIDList(PodDBAdapter adapter) {
adapter.open();
Cursor queueCursor = adapter.getQueueIDCursor();
List<Long> queueIds = new ArrayList<Long>(queueCursor.getCount());
if (queueCursor.moveToFirst()) {
do {
queueIds.add(queueCursor.getLong(0));
} while (queueCursor.moveToNext());
}
return queueIds;
}
/**
* Loads a list of the FeedItems in the queue. If the FeedItems of the queue are not used directly, consider using
* {@link #getQueueIDList(android.content.Context)} instead.
*
* @param context A context that is used for opening a database connection.
* @return A list of FeedItems sorted by the same order as the queue. The caller can wrap the returned
* list in a {@link de.danoeh.antennapodsp.util.QueueAccess} object for easier access to the queue's properties.
*/
public static List<FeedItem> getQueue(Context context) {
if (AppConfig.DEBUG)
Log.d(TAG, "Extracting queue");
PodDBAdapter adapter = new PodDBAdapter(context);
adapter.open();
List<FeedItem> items = getQueue(context, adapter);
adapter.close();
return items;
}
/**
* Loads a list of FeedItems whose episode has been downloaded.
*
* @param context A context that is used for opening a database connection.
* @return A list of FeedItems whose episdoe has been downloaded.
*/
public static List<FeedItem> getDownloadedItems(Context context) {
if (AppConfig.DEBUG)
Log.d(TAG, "Extracting downloaded items");
PodDBAdapter adapter = new PodDBAdapter(context);
adapter.open();
Cursor itemlistCursor = adapter.getDownloadedItemsCursor();
List<FeedItem> items = extractItemlistFromCursor(adapter,
itemlistCursor);
itemlistCursor.close();
loadFeedDataOfFeedItemlist(context, items);
Collections.sort(items, new FeedItemPubdateComparator());
adapter.close();
return items;
}
/**
* Returns an unsorted list of all FeedItems that can be deleted by the auto-cleanup method in DBTasks.
*/
public static List<FeedItem> getAutoCleanupCandidates(Context context) {
PodDBAdapter adapter = new PodDBAdapter(context);
adapter.open();
Cursor itemlistCursor = adapter.getAutoCleanupCandidatesCursor();
List<FeedItem> items = extractItemlistFromCursor(adapter,
itemlistCursor);
itemlistCursor.close();
adapter.close();
return items;
}
/**
* Returns a list of all FeedItems that will be downloaded automatically.
*/
public static List<FeedItem> getNewAutomaticallyDownloadedFeedItems(Context context) {
PodDBAdapter adapter = new PodDBAdapter(context);
adapter.open();
Cursor itemlistCursor = adapter.getNewAutomaticallyDownloadedFeedItemsCursor();
List<FeedItem> items = extractItemlistFromCursor(adapter,
itemlistCursor);
DBReader.loadFeedDataOfFeedItemlist(context, items);
itemlistCursor.close();
adapter.close();
return items;
}
/**
* Loads a list of FeedItems whose 'read'-attribute is set to false.
*
* @param context A context that is used for opening a database connection.
* @return A list of FeedItems whose 'read'-attribute it set to false. If the FeedItems in the list are not used,
* consider using {@link #getUnreadItemIds(android.content.Context)} instead.
*/
public static List<FeedItem> getUnreadItemsList(Context context) {
if (AppConfig.DEBUG)
Log.d(TAG, "Extracting unread items list");
PodDBAdapter adapter = new PodDBAdapter(context);
adapter.open();
Cursor itemlistCursor = adapter.getUnreadItemsCursor();
List<FeedItem> items = extractItemlistFromCursor(adapter,
itemlistCursor);
itemlistCursor.close();
loadFeedDataOfFeedItemlist(context, items);
adapter.close();
return items;
}
/**
* Loads the IDs of the FeedItems whose 'read'-attribute is set to false.
*
* @param context A context that is used for opening a database connection.
* @return A list of IDs of the FeedItems whose 'read'-attribute is set to false. This method should be preferred
* over {@link #getUnreadItemsList(android.content.Context)} if the FeedItems in the UnreadItems list are not used.
*/
public static long[] getUnreadItemIds(Context context) {
PodDBAdapter adapter = new PodDBAdapter(context);
adapter.open();
Cursor cursor = adapter.getUnreadItemIdsCursor();
long[] itemIds = new long[cursor.getCount()];
int i = 0;
if (cursor.moveToFirst()) {
do {
itemIds[i] = cursor.getLong(PodDBAdapter.KEY_ID_INDEX);
i++;
} while (cursor.moveToNext());
}
return itemIds;
}
/**
* Loads the playback history from the database. A FeedItem is in the playback history if playback of the correpsonding episode
* has been completed at least once.
*
* @param context A context that is used for opening a database connection.
* @return The playback history. The FeedItems are sorted by their media's playbackCompletionDate in descending order.
* The size of the returned list is limited by {@link #PLAYBACK_HISTORY_SIZE}.
*/
public static List<FeedItem> getPlaybackHistory(final Context context) {
if (AppConfig.DEBUG)
Log.d(TAG, "Loading playback history");
final int PLAYBACK_HISTORY_SIZE = 50;
PodDBAdapter adapter = new PodDBAdapter(context);
adapter.open();
Cursor mediaCursor = adapter.getCompletedMediaCursor(PLAYBACK_HISTORY_SIZE);
String[] itemIds = new String[mediaCursor.getCount()];
for (int i = 0; i < itemIds.length && mediaCursor.moveToPosition(i); i++) {
itemIds[i] = Long.toString(mediaCursor.getLong(PodDBAdapter.KEY_MEDIA_FEEDITEM_INDEX));
}
mediaCursor.close();
Cursor itemCursor = adapter.getFeedItemCursor(itemIds);
List<FeedItem> items = extractItemlistFromCursor(adapter, itemCursor);
loadFeedDataOfFeedItemlist(context, items);
itemCursor.close();
adapter.close();
Collections.sort(items, new PlaybackCompletionDateComparator());
return items;
}
/**
* Loads the download log from the database.
*
* @param context A context that is used for opening a database connection.
* @return A list with DownloadStatus objects that represent the download log.
* The size of the returned list is limited by {@link #DOWNLOAD_LOG_SIZE}.
*/
public static List<DownloadStatus> getDownloadLog(Context context) {
if (AppConfig.DEBUG)
Log.d(TAG, "Extracting DownloadLog");
PodDBAdapter adapter = new PodDBAdapter(context);
adapter.open();
Cursor logCursor = adapter.getDownloadLogCursor(DOWNLOAD_LOG_SIZE);
List<DownloadStatus> downloadLog = new ArrayList<DownloadStatus>(
logCursor.getCount());
if (logCursor.moveToFirst()) {
do {
long id = logCursor.getLong(PodDBAdapter.KEY_ID_INDEX);
long feedfileId = logCursor
.getLong(PodDBAdapter.KEY_FEEDFILE_INDEX);
int feedfileType = logCursor
.getInt(PodDBAdapter.KEY_FEEDFILETYPE_INDEX);
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, DownloadError.fromCode(reason), completionDate,
reasonDetailed));
} while (logCursor.moveToNext());
}
logCursor.close();
Collections.sort(downloadLog, new DownloadStatusComparator());
return downloadLog;
}
/**
* Loads the FeedItemStatistics objects of all Feeds in the database. This method should be preferred over
* {@link #getFeedItemList(android.content.Context, de.danoeh.antennapodsp.feed.Feed)} if only metadata about
* the FeedItems is needed.
*
* @param context A context that is used for opening a database connection.
* @return A list of FeedItemStatistics objects sorted alphabetically by their Feed's title.
*/
public static List<FeedItemStatistics> getFeedStatisticsList(final Context context) {
PodDBAdapter adapter = new PodDBAdapter(context);
adapter.open();
List<FeedItemStatistics> result = new ArrayList<FeedItemStatistics>();
Cursor cursor = adapter.getFeedStatisticsCursor();
if (cursor.moveToFirst()) {
do {
result.add(new FeedItemStatistics(cursor.getLong(PodDBAdapter.IDX_FEEDSTATISTICS_FEED),
cursor.getInt(PodDBAdapter.IDX_FEEDSTATISTICS_NUM_ITEMS),
cursor.getInt(PodDBAdapter.IDX_FEEDSTATISTICS_NEW_ITEMS),
cursor.getInt(PodDBAdapter.IDX_FEEDSTATISTICS_IN_PROGRESS_EPISODES),
new Date(cursor.getLong(PodDBAdapter.IDX_FEEDSTATISTICS_LATEST_EPISODE))));
} while (cursor.moveToNext());
}
cursor.close();
adapter.close();
return result;
}
/**
* Loads a specific Feed from the database.
*
* @param context A context that is used for opening a database connection.
* @param feedId The ID of the Feed
* @return The Feed or null if the Feed could not be found. The Feeds FeedItems will also be loaded from the
* database and the items-attribute will be set correctly.
*/
public static Feed getFeed(final Context context, final long feedId) {
if (AppConfig.DEBUG)
Log.d(TAG, "Loading feed with id " + feedId);
Feed feed = null;
PodDBAdapter adapter = new PodDBAdapter(context);
adapter.open();
Cursor feedCursor = adapter.getFeedCursor(feedId);
if (feedCursor.moveToFirst()) {
feed = extractFeedFromCursorRow(adapter, feedCursor);
feed.setItems(getFeedItemList(context, feed));
} else {
Log.e(TAG, "getFeed could not find feed with id " + feedId);
}
feedCursor.close();
adapter.close();
return feed;
}
static FeedItem getFeedItem(final Context context, final long itemId, PodDBAdapter adapter) {
if (AppConfig.DEBUG)
Log.d(TAG, "Loading feeditem with id " + itemId);
FeedItem item = null;
Cursor itemCursor = adapter.getFeedItemCursor(Long.toString(itemId));
if (itemCursor.moveToFirst()) {
List<FeedItem> list = extractItemlistFromCursor(adapter, itemCursor);
if (list.size() > 0) {
item = list.get(0);
loadFeedDataOfFeedItemlist(context, list);
}
}
return item;
}
/**
* Loads a specific FeedItem from the database.
*
* @param context A context that is used for opening a database connection.
* @param itemId The ID of the FeedItem
* @return The FeedItem or null if the FeedItem could not be found. All FeedComponent-attributes of the FeedItem will
* also be loaded from the database.
*/
public static FeedItem getFeedItem(final Context context, final long itemId) {
if (AppConfig.DEBUG)
Log.d(TAG, "Loading feeditem with id " + itemId);
PodDBAdapter adapter = new PodDBAdapter(context);
adapter.open();
FeedItem item = getFeedItem(context, itemId, adapter);
adapter.close();
return item;
}
/**
* Loads additional information about a FeedItem, e.g. shownotes
*
* @param context A context that is used for opening a database connection.
* @param item The FeedItem
*/
public static void loadExtraInformationOfFeedItem(final Context context, final FeedItem item) {
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.setDescription(description);
item.setContentEncoded(contentEncoded);
}
adapter.close();
}
/**
* Returns the number of downloaded episodes.
*
* @param context A context that is used for opening a database connection.
* @return The number of downloaded episodes.
*/
public static int getNumberOfDownloadedEpisodes(final Context context) {
PodDBAdapter adapter = new PodDBAdapter(context);
adapter.open();
final int result = adapter.getNumberOfDownloadedEpisodes();
adapter.close();
return result;
}
/**
* Returns the number of unread items.
*
* @param context A context that is used for opening a database connection.
* @return The number of unread items.
*/
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.
*
* @param context A context that is used for opening a database connection.
* @param imageId The id of the object
* @return The found object
*/
public static FeedImage getFeedImage(final Context context, final long imageId) {
PodDBAdapter adapter = new PodDBAdapter(context);
adapter.open();
FeedImage result = getFeedImage(adapter, imageId);
adapter.close();
return result;
}
/**
* Searches the DB for a FeedImage of the given id.
*
* @param id The id of the object
* @return The found object
*/
static FeedImage getFeedImage(PodDBAdapter adapter, final long id) {
Cursor cursor = adapter.getImageOfFeedCursor(id);
if ((cursor.getCount() == 0) || !cursor.moveToFirst()) {
throw new SQLException("No FeedImage found at index: " + id);
}
FeedImage image = new FeedImage(id, cursor.getString(cursor
.getColumnIndex(PodDBAdapter.KEY_TITLE)),
cursor.getString(cursor
.getColumnIndex(PodDBAdapter.KEY_FILE_URL)),
cursor.getString(cursor
.getColumnIndex(PodDBAdapter.KEY_DOWNLOAD_URL)),
cursor.getInt(cursor
.getColumnIndex(PodDBAdapter.KEY_DOWNLOADED)) > 0);
cursor.close();
return image;
}
/**
* Searches the DB for a FeedMedia of the given id.
*
* @param context A context that is used for opening a database connection.
* @param mediaId The id of the object
* @return The found object
*/
public static FeedMedia getFeedMedia(final Context context, final long mediaId) {
PodDBAdapter adapter = new PodDBAdapter(context);
adapter.open();
Cursor mediaCursor = adapter.getSingleFeedMediaCursor(mediaId);
FeedMedia media = null;
if (mediaCursor.moveToFirst()) {
final long itemId = mediaCursor.getLong(PodDBAdapter.KEY_MEDIA_FEEDITEM_INDEX);
media = extractFeedMediaFromCursorRow(mediaCursor);
FeedItem item = getFeedItem(context, itemId);
if (media != null && item != null) {
media.setItem(item);
item.setMedia(media);
}
}
mediaCursor.close();
adapter.close();
return media;
}
public static long getDownloadedEpisodesSize(Context context) {
PodDBAdapter adapter = new PodDBAdapter(context);
adapter.open();
long result = adapter.getSizeOfAllDownloadedEpisodes();
adapter.close();
return result;
}
}