mirror of
https://github.com/AntennaPod/AntennaPod.git
synced 2025-01-27 09:01:22 +01:00
Made auto-cleanup and auto-download methods changeable
This commit is contained in:
parent
066cab8da5
commit
3c473c490b
@ -16,5 +16,6 @@ public class ClientConfigurator {
|
||||
ClientConfig.playbackServiceCallbacks = new PlaybackServiceCallbacksImpl();
|
||||
ClientConfig.storageCallbacks = new StorageCallbacksImpl();
|
||||
ClientConfig.flattrCallbacks = new FlattrCallbacksImpl();
|
||||
ClientConfig.dbTasksCallbacks = new DBTasksCallbacksImpl();
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -22,4 +22,6 @@ public class ClientConfig {
|
||||
public static FlattrCallbacks flattrCallbacks;
|
||||
|
||||
public static StorageCallbacks storageCallbacks;
|
||||
|
||||
public static DBTasksCallbacks dbTasksCallbacks;
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user