Made auto-cleanup and auto-download methods changeable

This commit is contained in:
daniel oeh 2015-01-15 14:37:35 +01:00
parent 066cab8da5
commit 3c473c490b
11 changed files with 548 additions and 175 deletions

View File

@ -16,5 +16,6 @@ public class ClientConfigurator {
ClientConfig.playbackServiceCallbacks = new PlaybackServiceCallbacksImpl();
ClientConfig.storageCallbacks = new StorageCallbacksImpl();
ClientConfig.flattrCallbacks = new FlattrCallbacksImpl();
ClientConfig.dbTasksCallbacks = new DBTasksCallbacksImpl();
}
}

View File

@ -0,0 +1,20 @@
package de.danoeh.antennapod.config;
import de.danoeh.antennapod.core.DBTasksCallbacks;
import de.danoeh.antennapod.core.storage.APCleanupAlgorithm;
import de.danoeh.antennapod.core.storage.APDownloadAlgorithm;
import de.danoeh.antennapod.core.storage.AutomaticDownloadAlgorithm;
import de.danoeh.antennapod.core.storage.EpisodeCleanupAlgorithm;
public class DBTasksCallbacksImpl implements DBTasksCallbacks {
@Override
public AutomaticDownloadAlgorithm getAutomaticDownloadAlgorithm() {
return new APDownloadAlgorithm();
}
@Override
public EpisodeCleanupAlgorithm getEpisodeCacheCleanupAlgorithm() {
return new APCleanupAlgorithm();
}
}

View File

@ -22,4 +22,6 @@ public class ClientConfig {
public static FlattrCallbacks flattrCallbacks;
public static StorageCallbacks storageCallbacks;
public static DBTasksCallbacks dbTasksCallbacks;
}

View File

@ -0,0 +1,20 @@
package de.danoeh.antennapod.core;
import de.danoeh.antennapod.core.storage.AutomaticDownloadAlgorithm;
import de.danoeh.antennapod.core.storage.EpisodeCleanupAlgorithm;
/**
* Callbacks for the DBTasks class of the storage module.
*/
public interface DBTasksCallbacks {
/**
* Returns the client's implementation of the AutomaticDownloadAlgorithm interface.
*/
public AutomaticDownloadAlgorithm getAutomaticDownloadAlgorithm();
/**
* Returns the client's implementation of the EpisodeCacheCleanupAlgorithm interface.
*/
public EpisodeCleanupAlgorithm getEpisodeCacheCleanupAlgorithm();
}

View File

@ -0,0 +1,103 @@
package de.danoeh.antennapod.core.storage;
import android.content.Context;
import android.util.Log;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ExecutionException;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.util.QueueAccess;
/**
* Implementation of the EpisodeCleanupAlgorithm interface used by AntennaPod.
*/
public class APCleanupAlgorithm implements EpisodeCleanupAlgorithm<Integer> {
private static final String TAG = "APCleanupAlgorithm";
@Override
public int performCleanup(Context context, Integer episodeNumber) {
List<FeedItem> candidates = new ArrayList<FeedItem>();
List<FeedItem> downloadedItems = DBReader.getDownloadedItems(context);
QueueAccess queue = QueueAccess.IDListAccess(DBReader.getQueueIDList(context));
List<FeedItem> delete;
for (FeedItem item : downloadedItems) {
if (item.hasMedia() && item.getMedia().isDownloaded()
&& !queue.contains(item.getId()) && 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) {
try {
DBWriter.deleteFeedMediaOfItem(context, item.getMedia().getId()).get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
int counter = delete.size();
Log.i(TAG, String.format(
"Auto-delete deleted %d episodes (%d requested)", counter,
episodeNumber));
return counter;
}
@Override
public Integer getDefaultCleanupParameter(Context context) {
return 0;
}
@Override
public Integer getPerformCleanupParameter(Context context, List<FeedItem> items) {
return getPerformAutoCleanupArgs(context, items.size());
}
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;
}
}

View File

@ -0,0 +1,133 @@
package de.danoeh.antennapod.core.storage;
import android.content.Context;
import android.util.Log;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import de.danoeh.antennapod.core.BuildConfig;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.util.NetworkUtils;
import de.danoeh.antennapod.core.util.PowerUtils;
/**
* Implements the automatic download algorithm used by AntennaPod. This class assumes that
* the client uses the APEpisodeCleanupAlgorithm.
*/
public class APDownloadAlgorithm implements AutomaticDownloadAlgorithm {
private static final String TAG = "APDownloadAlgorithm";
private final APCleanupAlgorithm cleanupAlgorithm = new APCleanupAlgorithm();
/**
* Looks for undownloaded episodes in the queue or list of unread items and request a download if
* 1. Network is available
* 2. The device is charging or the user allows auto download on battery
* 3. There is free space in the episode cache
* This method is executed on an internal single thread executor.
*
* @param context Used for accessing the DB.
* @param mediaIds If this list is not empty, the method will only download a candidate for automatic downloading if
* its media ID is in the mediaIds list.
* @return A Runnable that will be submitted to an ExecutorService.
*/
@Override
public Runnable autoDownloadUndownloadedItems(final Context context, final long... mediaIds) {
return new Runnable() {
@Override
public void run() {
// true if we should auto download based on network status
boolean networkShouldAutoDl = NetworkUtils.autodownloadNetworkAvailable(context)
&& UserPreferences.isEnableAutodownload();
// true if we should auto download based on power status
boolean powerShouldAutoDl = PowerUtils.deviceCharging(context)
|| UserPreferences.isEnableAutodownloadOnBattery();
// we should only auto download if both network AND power are happy
if (networkShouldAutoDl && powerShouldAutoDl) {
Log.d(TAG, "Performing auto-dl of undownloaded episodes");
final List<FeedItem> queue = DBReader.getQueue(context);
final List<FeedItem> unreadItems = DBReader
.getUnreadItemsList(context);
int undownloadedEpisodes = DBTasks.getNumberOfUndownloadedEpisodes(queue,
unreadItems);
int downloadedEpisodes = DBReader
.getNumberOfDownloadedEpisodes(context);
int deletedEpisodes = cleanupAlgorithm.performCleanup(context,
APCleanupAlgorithm.getPerformAutoCleanupArgs(context, undownloadedEpisodes));
int episodeSpaceLeft = undownloadedEpisodes;
boolean cacheIsUnlimited = UserPreferences.getEpisodeCacheSize() == UserPreferences
.getEpisodeCacheSizeUnlimited();
if (!cacheIsUnlimited
&& UserPreferences.getEpisodeCacheSize() < downloadedEpisodes
+ undownloadedEpisodes) {
episodeSpaceLeft = UserPreferences.getEpisodeCacheSize()
- (downloadedEpisodes - deletedEpisodes);
}
Arrays.sort(mediaIds); // sort for binary search
final boolean ignoreMediaIds = mediaIds.length == 0;
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);
long mediaId = (item.hasMedia()) ? item.getMedia().getId() : -1;
if ((ignoreMediaIds || Arrays.binarySearch(mediaIds, mediaId) >= 0)
&& item.hasMedia()
&& !item.getMedia().isDownloaded()
&& !item.getMedia().isPlaying()
&& item.getFeed().getPreferences().getAutoDownload()) {
itemsToDownload.add(item);
episodeSpaceLeft--;
undownloadedEpisodes--;
if (episodeSpaceLeft == 0 || undownloadedEpisodes == 0) {
break;
}
}
}
}
if (episodeSpaceLeft > 0 && undownloadedEpisodes > 0) {
for (FeedItem item : unreadItems) {
long mediaId = (item.hasMedia()) ? item.getMedia().getId() : -1;
if ((ignoreMediaIds || Arrays.binarySearch(mediaIds, mediaId) >= 0)
&& item.hasMedia()
&& !item.getMedia().isDownloaded()
&& item.getFeed().getPreferences().getAutoDownload()) {
itemsToDownload.add(item);
episodeSpaceLeft--;
undownloadedEpisodes--;
if (episodeSpaceLeft == 0 || undownloadedEpisodes == 0) {
break;
}
}
}
}
if (BuildConfig.DEBUG)
Log.d(TAG, "Enqueueing " + itemsToDownload.size()
+ " items for download");
try {
DBTasks.downloadFeedItems(false, context,
itemsToDownload.toArray(new FeedItem[itemsToDownload
.size()])
);
} catch (DownloadRequestException e) {
e.printStackTrace();
}
}
}
};
}
}

View File

@ -0,0 +1,136 @@
package de.danoeh.antennapod.core.storage;
import android.content.Context;
import android.util.Log;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ExecutionException;
import de.danoeh.antennapod.core.BuildConfig;
import de.danoeh.antennapod.core.feed.FeedItem;
/**
* Implementation of the EpisodeCleanupAlgorithm interface used by AntennaPodSP apps.
*/
public class APSPCleanupAlgorithm implements EpisodeCleanupAlgorithm<Integer> {
private static final String TAG = "APSPCleanupAlgorithm";
final int numberOfNewAutomaticallyDownloadedEpisodes;
public APSPCleanupAlgorithm(int numberOfNewAutomaticallyDownloadedEpisodes) {
this.numberOfNewAutomaticallyDownloadedEpisodes = numberOfNewAutomaticallyDownloadedEpisodes;
}
/**
* Performs an automatic cleanup. Episodes that have been downloaded first will also be deleted first.
* The episode that is currently playing as well as the n most recent episodes (the exact value is determined
* by AppPreferences.numberOfNewAutomaticallyDownloadedEpisodes) will never be deleted.
*
* @param context
* @param episodeSize The maximum amount of space that should be freed by this method
* @return The number of episodes that have been deleted
*/
@Override
public int performCleanup(Context context, Integer episodeSize) {
if (BuildConfig.DEBUG) Log.d(TAG, String.format("performAutoCleanup(%d)", episodeSize));
if (episodeSize <= 0) {
return 0;
}
List<FeedItem> candidates = getAutoCleanupCandidates(context);
List<FeedItem> deleteList = new ArrayList<FeedItem>();
long deletedEpisodesSize = 0;
Collections.sort(candidates, new Comparator<FeedItem>() {
@Override
public int compare(FeedItem lhs, FeedItem rhs) {
File lFile = new File(lhs.getMedia().getFile_url());
File rFile = new File(rhs.getMedia().getFile_url());
if (!lFile.exists() || !rFile.exists()) {
return 0;
}
if (FileUtils.isFileOlder(lFile, rFile)) {
return -1;
} else {
return 1;
}
}
});
// listened episodes will be deleted first
Iterator<FeedItem> it = candidates.iterator();
for (FeedItem i = it.next(); it.hasNext() && deletedEpisodesSize <= episodeSize; i = it.next()) {
if (!i.getMedia().isPlaying() && i.getMedia().getPlaybackCompletionDate() != null) {
it.remove();
deleteList.add(i);
deletedEpisodesSize += i.getMedia().getSize();
}
}
// delete unlistened old episodes if necessary
it = candidates.iterator();
for (FeedItem i = it.next(); it.hasNext() && deletedEpisodesSize <= episodeSize; i = it.next()) {
if (!i.getMedia().isPlaying()) {
it.remove();
deleteList.add(i);
deletedEpisodesSize += i.getMedia().getSize();
}
}
for (FeedItem item : deleteList) {
try {
DBWriter.deleteFeedMediaOfItem(context, item.getMedia().getId()).get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
if (BuildConfig.DEBUG)
Log.d(TAG, String.format("performAutoCleanup(%d) deleted %d episodes and free'd %d bytes of memory",
episodeSize, deleteList.size(), deletedEpisodesSize));
return deleteList.size();
}
@Override
public Integer getDefaultCleanupParameter(Context context) {
return 0;
}
@Override
public Integer getPerformCleanupParameter(Context context, List<FeedItem> items) {
int episodeSize = 0;
for (FeedItem item : items) {
if (item.hasMedia() && !item.getMedia().isDownloaded()) {
episodeSize += item.getMedia().getSize();
}
}
return episodeSize;
}
/**
* Returns list of FeedItems that have been downloaded, but are not one of the
* [numberOfNewAutomaticallyDownloadedEpisodes] most recent items.
*/
private List<FeedItem> getAutoCleanupCandidates(Context context) {
List<FeedItem> downloaded = new ArrayList<FeedItem>(DBReader.getDownloadedItems(context));
List<FeedItem> recent = new ArrayList<FeedItem>(DBReader.getRecentlyPublishedEpisodes(context,
numberOfNewAutomaticallyDownloadedEpisodes));
for (FeedItem r : recent) {
if (r.hasMedia() && r.getMedia().isDownloaded()) {
for (int i = 0; i < downloaded.size(); i++) {
if (downloaded.get(i).getId() == r.getId()) {
downloaded.remove(i);
break;
}
}
}
}
return downloaded;
}
}

View File

@ -0,0 +1,66 @@
package de.danoeh.antennapod.core.storage;
import android.content.Context;
import android.util.Log;
import java.util.Iterator;
import java.util.List;
import de.danoeh.antennapod.core.BuildConfig;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.util.NetworkUtils;
/**
* Implements the automatic download algorithm used by AntennaPodSP apps.
*/
public class APSPDownloadAlgorithm implements AutomaticDownloadAlgorithm {
private static final String TAG = "APSPDownloadAlgorithm";
private final int numberOfNewAutomaticallyDownloadedEpisodes;
public APSPDownloadAlgorithm(int numberOfNewAutomaticallyDownloadedEpisodes) {
this.numberOfNewAutomaticallyDownloadedEpisodes = numberOfNewAutomaticallyDownloadedEpisodes;
}
/**
* Downloads the most recent episodes automatically. The exact number of
* episodes that will be downloaded can be set in the AppPreferences.
*
* @param context Used for accessing the DB.
* @return A Runnable that will be submitted to an ExecutorService.
*/
@Override
public Runnable autoDownloadUndownloadedItems(final Context context, final long... mediaIds) {
return new Runnable() {
@Override
public void run() {
if (BuildConfig.DEBUG)
Log.d(TAG, "Performing auto-dl of undownloaded episodes");
if (NetworkUtils.autodownloadNetworkAvailable(context)
&& UserPreferences.isEnableAutodownload()) {
List<FeedItem> itemsToDownload = DBReader.getRecentlyPublishedEpisodes(context,
numberOfNewAutomaticallyDownloadedEpisodes);
Iterator<FeedItem> it = itemsToDownload.iterator();
for (FeedItem item = it.next(); it.hasNext(); item = it.next()) {
if (item.getMedia().isDownloaded()) {
it.remove();
}
}
if (BuildConfig.DEBUG)
Log.d(TAG, "Enqueueing " + itemsToDownload.size()
+ " items for automatic download");
if (!itemsToDownload.isEmpty()) {
try {
DBTasks.downloadFeedItems(false, context,
itemsToDownload.toArray(new FeedItem[itemsToDownload
.size()]));
} catch (DownloadRequestException e) {
e.printStackTrace();
}
}
}
}
};
}
}

View File

@ -0,0 +1,20 @@
package de.danoeh.antennapod.core.storage;
import android.content.Context;
public interface AutomaticDownloadAlgorithm {
/**
* Looks for undownloaded episodes and request a download if
* 1. Network is available
* 2. The device is charging or the user allows auto download on battery
* 3. There is free space in the episode cache
* This method is executed on an internal single thread executor.
*
* @param context Used for accessing the DB.
* @param mediaIds If this list is not empty, the method will only download a candidate for automatic downloading if
* its media ID is in the mediaIds list.
* @return A Runnable that will be submitted to an ExecutorService.
*/
public Runnable autoDownloadUndownloadedItems(Context context, long... mediaIds);
}

View File

@ -8,7 +8,6 @@ import android.util.Log;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
@ -35,8 +34,6 @@ import de.danoeh.antennapod.core.service.GpodnetSyncService;
import de.danoeh.antennapod.core.service.download.DownloadStatus;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.util.DownloadError;
import de.danoeh.antennapod.core.util.NetworkUtils;
import de.danoeh.antennapod.core.util.PowerUtils;
import de.danoeh.antennapod.core.util.QueueAccess;
import de.danoeh.antennapod.core.util.comparator.FeedItemPubdateComparator;
import de.danoeh.antennapod.core.util.exception.MediaFileNotFoundException;
@ -387,8 +384,8 @@ public final class DBTasks {
downloadFeedItems(true, context, items);
}
private static void downloadFeedItems(boolean performAutoCleanup,
final Context context, final FeedItem... items)
static void downloadFeedItems(boolean performAutoCleanup,
final Context context, final FeedItem... items)
throws DownloadRequestException {
final DownloadRequester requester = DownloadRequester.getInstance();
@ -397,8 +394,10 @@ public final class DBTasks {
@Override
public void run() {
performAutoCleanup(context,
getPerformAutoCleanupArgs(context, items.length));
ClientConfig.dbTasksCallbacks.getEpisodeCacheCleanupAlgorithm()
.performCleanup(context,
ClientConfig.dbTasksCallbacks.getEpisodeCacheCleanupAlgorithm()
.getPerformCleanupParameter(context, Arrays.asList(items)));
}
}.start();
@ -428,7 +427,7 @@ public final class DBTasks {
}
}
private static int getNumberOfUndownloadedEpisodes(
static int getNumberOfUndownloadedEpisodes(
final List<FeedItem> queue, final List<FeedItem> unreadItems) {
int counter = 0;
for (FeedItem item : queue) {
@ -460,117 +459,9 @@ public final class DBTasks {
* @return A Future that can be used for waiting for the methods completion.
*/
public static Future<?> autodownloadUndownloadedItems(final Context context, final long... mediaIds) {
return autodownloadExec.submit(new Runnable() {
@Override
public void run() {
return autodownloadExec.submit(ClientConfig.dbTasksCallbacks.getAutomaticDownloadAlgorithm()
.autoDownloadUndownloadedItems(context, mediaIds));
// true if we should auto download based on network status
boolean networkShouldAutoDl = NetworkUtils.autodownloadNetworkAvailable(context)
&& UserPreferences.isEnableAutodownload();
// true if we should auto download based on power status
boolean powerShouldAutoDl = PowerUtils.deviceCharging(context)
|| UserPreferences.isEnableAutodownloadOnBattery();
// we should only auto download if both network AND power are happy
if (networkShouldAutoDl && powerShouldAutoDl) {
Log.d(TAG, "Performing auto-dl of undownloaded episodes");
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);
}
Arrays.sort(mediaIds); // sort for binary search
final boolean ignoreMediaIds = mediaIds.length == 0;
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);
long mediaId = (item.hasMedia()) ? item.getMedia().getId() : -1;
if ((ignoreMediaIds || Arrays.binarySearch(mediaIds, mediaId) >= 0)
&& item.hasMedia()
&& !item.getMedia().isDownloaded()
&& !item.getMedia().isPlaying()
&& item.getFeed().getPreferences().getAutoDownload()) {
itemsToDownload.add(item);
episodeSpaceLeft--;
undownloadedEpisodes--;
if (episodeSpaceLeft == 0 || undownloadedEpisodes == 0) {
break;
}
}
}
}
if (episodeSpaceLeft > 0 && undownloadedEpisodes > 0) {
for (FeedItem item : unreadItems) {
long mediaId = (item.hasMedia()) ? item.getMedia().getId() : -1;
if ((ignoreMediaIds || Arrays.binarySearch(mediaIds, mediaId) >= 0)
&& item.hasMedia()
&& !item.getMedia().isDownloaded()
&& item.getFeed().getPreferences().getAutoDownload()) {
itemsToDownload.add(item);
episodeSpaceLeft--;
undownloadedEpisodes--;
if (episodeSpaceLeft == 0 || undownloadedEpisodes == 0) {
break;
}
}
}
}
if (BuildConfig.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;
}
/**
@ -582,63 +473,8 @@ public final class DBTasks {
* @param context Used for accessing the DB.
*/
public static void performAutoCleanup(final Context context) {
performAutoCleanup(context, getPerformAutoCleanupArgs(context, 0));
}
private static int performAutoCleanup(final Context context,
final int episodeNumber) {
List<FeedItem> candidates = new ArrayList<FeedItem>();
List<FeedItem> downloadedItems = DBReader.getDownloadedItems(context);
QueueAccess queue = QueueAccess.IDListAccess(DBReader.getQueueIDList(context));
List<FeedItem> delete;
for (FeedItem item : downloadedItems) {
if (item.hasMedia() && item.getMedia().isDownloaded()
&& !queue.contains(item.getId()) && 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) {
try {
DBWriter.deleteFeedMediaOfItem(context, item.getMedia().getId()).get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
int counter = delete.size();
if (BuildConfig.DEBUG)
Log.d(TAG, String.format(
"Auto-delete deleted %d episodes (%d requested)", counter,
episodeNumber));
return counter;
ClientConfig.dbTasksCallbacks.getEpisodeCacheCleanupAlgorithm().performCleanup(context,
ClientConfig.dbTasksCallbacks.getEpisodeCacheCleanupAlgorithm().getDefaultCleanupParameter(context));
}
/**

View File

@ -0,0 +1,36 @@
package de.danoeh.antennapod.core.storage;
import android.content.Context;
import java.util.List;
import de.danoeh.antennapod.core.feed.FeedItem;
public interface EpisodeCleanupAlgorithm<T> {
/**
* Deletes downloaded episodes that are no longer needed. What episodes are deleted and how many
* of them depends on the implementation.
*
* @param context Can be used for accessing the database
* @param parameter An additional parameter. This parameter is either returned by getDefaultCleanupParameter
* or getPerformCleanupParameter.
* @return The number of episodes that were deleted.
*/
public int performCleanup(Context context, T parameter);
/**
* Returns a parameter for performCleanup. The implementation of this interface should decide how much
* space to free to satisfy the episode cache conditions. If the conditions are already satisfied, this
* method should not have any effects.
*/
public T getDefaultCleanupParameter(Context context);
/**
* Returns a parameter for performCleanup.
*
* @param items A list of FeedItems that are about to be downloaded. The implementation of this interface
* should decide how much space to free to satisfy the episode cache conditions.
*/
public T getPerformCleanupParameter(Context context, List<FeedItem> items);
}