Implemented refresh, auto-download, auto-cleanup methods
This commit is contained in:
parent
a704a33e2b
commit
c6545a5643
|
@ -47,6 +47,26 @@ public final class DBReader {
|
|||
feedlistCursor.close();
|
||||
return feeds;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public static void loadFeedDataOfFeedItemlist(Context context,
|
||||
List<FeedItem> items) {
|
||||
|
@ -271,6 +291,25 @@ public final class DBReader {
|
|||
adapter.close();
|
||||
return items;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
public static List<FeedItem> getUnreadItemsList(Context context) {
|
||||
if (AppConfig.DEBUG)
|
||||
|
@ -410,6 +449,14 @@ public final class DBReader {
|
|||
return item;
|
||||
|
||||
}
|
||||
|
||||
public static int getNumberOfDownloadedEpisodes(final Context context) {
|
||||
PodDBAdapter adapter = new PodDBAdapter(context);
|
||||
adapter.open();
|
||||
final int result = adapter.getNumberOfDownloadedEpisodes();
|
||||
adapter.close();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches the DB for a FeedImage of the given id.
|
||||
|
|
|
@ -1,16 +1,28 @@
|
|||
package de.danoeh.antennapod.storage;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.util.Log;
|
||||
import de.danoeh.antennapod.AppConfig;
|
||||
import de.danoeh.antennapod.feed.EventDistributor;
|
||||
import de.danoeh.antennapod.feed.Feed;
|
||||
import de.danoeh.antennapod.feed.FeedImage;
|
||||
import de.danoeh.antennapod.feed.FeedItem;
|
||||
import de.danoeh.antennapod.feed.FeedMedia;
|
||||
import de.danoeh.antennapod.preferences.UserPreferences;
|
||||
import de.danoeh.antennapod.service.PlaybackService;
|
||||
import de.danoeh.antennapod.service.download.DownloadStatus;
|
||||
import de.danoeh.antennapod.util.DownloadError;
|
||||
import de.danoeh.antennapod.util.NetworkUtils;
|
||||
import de.danoeh.antennapod.util.exception.MediaFileNotFoundException;
|
||||
|
||||
public final class DBTasks {
|
||||
|
@ -55,47 +67,308 @@ public final class DBTasks {
|
|||
}
|
||||
}
|
||||
|
||||
public static void refreshAllFeeds(final Context context) {
|
||||
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 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. */
|
||||
private 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 long imageId) {
|
||||
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 refreshFeed(final Context context, final long feedId) {
|
||||
public static void downloadFeedItems(final Context context,
|
||||
FeedItem... items) throws DownloadRequestException {
|
||||
downloadFeedItems(true, context, items);
|
||||
}
|
||||
|
||||
public static void downloadFeedItem(final Context context, long... itemIds) {
|
||||
}
|
||||
|
||||
static void downloadFeedItem(boolean performAutoCleanup,
|
||||
final Context context, final long... itemIds)
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void autodownloadUndownloadedItems(Context context) {
|
||||
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;
|
||||
}
|
||||
|
||||
private static int getPerformAutoCleanupArgs(final int episodeNumber) {
|
||||
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) {
|
||||
return 0;
|
||||
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) {
|
||||
|
@ -132,7 +405,7 @@ public final class DBTasks {
|
|||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/** Get a FeedItem by its identifying value. */
|
||||
private static FeedItem searchFeedItemByIdentifyingValue(Feed feed,
|
||||
String identifier) {
|
||||
|
@ -144,8 +417,8 @@ public final class DBTasks {
|
|||
return null;
|
||||
}
|
||||
|
||||
|
||||
public static synchronized Feed updateFeed(final Context context, final Feed newFeed) {
|
||||
public static synchronized Feed updateFeed(final Context context,
|
||||
final Feed newFeed) {
|
||||
// Look up feed in the feedslist
|
||||
final Feed savedFeed = searchFeedByIdentifyingValue(context,
|
||||
newFeed.getIdentifyingValue());
|
||||
|
@ -161,7 +434,7 @@ public final class DBTasks {
|
|||
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)
|
||||
|
|
|
@ -506,7 +506,7 @@ public class DBWriter {
|
|||
|
||||
}
|
||||
|
||||
private static void setFeedMedia(final Context context,
|
||||
static void setFeedMedia(final Context context,
|
||||
final FeedMedia media) {
|
||||
dbExec.submit(new Runnable() {
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import android.database.DatabaseUtils;
|
|||
import android.database.MergeCursor;
|
||||
import android.database.SQLException;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteStatement;
|
||||
import android.database.sqlite.SQLiteDatabase.CursorFactory;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.util.Log;
|
||||
|
@ -574,6 +575,14 @@ public class PodDBAdapter {
|
|||
return c;
|
||||
}
|
||||
|
||||
public final Cursor getExpiredFeedsCursor(long expirationTime) {
|
||||
open();
|
||||
Cursor c = db.query(TABLE_NAME_FEEDS, null, "?<?", new String[] {
|
||||
KEY_LASTUPDATE, Long.toString(expirationTime) }, null, null,
|
||||
null);
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a cursor with all FeedItems of a Feed. Uses SEL_FI_SMALL
|
||||
*
|
||||
|
@ -680,6 +689,17 @@ public class PodDBAdapter {
|
|||
|
||||
}
|
||||
|
||||
public Cursor getDownloadedItemsCursor() {
|
||||
open();
|
||||
Cursor c = db.rawQuery("SELECT ? FROM " + TABLE_NAME_FEED_ITEMS
|
||||
+ "FULL JOIN " + TABLE_NAME_FEED_MEDIA + " ON "
|
||||
+ TABLE_NAME_FEED_ITEMS + "." + KEY_ID + "="
|
||||
+ TABLE_NAME_FEED_MEDIA + "." + KEY_ID + " WHERE "
|
||||
+ TABLE_NAME_FEED_MEDIA + "." + KEY_DOWNLOADED + ">0",
|
||||
SEL_FI_SMALL);
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a cursor which contains feed media objects with a playback
|
||||
* completion date in descending order.
|
||||
|
@ -766,6 +786,16 @@ public class PodDBAdapter {
|
|||
|
||||
}
|
||||
|
||||
public final int getNumberOfDownloadedEpisodes() {
|
||||
|
||||
Cursor c = db.rawQuery(
|
||||
"SELECT COUNT(DISTINCT ?) AS count FROM ? WHERE ?>0",
|
||||
new String[] { KEY_ID, TABLE_NAME_FEED_MEDIA, KEY_DOWNLOADED });
|
||||
final int result = c.getInt(0);
|
||||
c.close();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses DatabaseUtils to escape a search query and removes ' at the
|
||||
* beginning and the end of the string returned by the escape method.
|
||||
|
|
Loading…
Reference in New Issue