summaryrefslogtreecommitdiff
path: root/core/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'core/src/main')
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java21
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/APCleanupAlgorithm.java64
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/APDownloadAlgorithm.java7
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/APNullCleanupAlgorithm.java24
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/APQueueCleanupAlgorithm.java81
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java7
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/EpisodeCleanupAlgorithm.java51
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java15
-rw-r--r--core/src/main/res/values/arrays.xml21
-rw-r--r--core/src/main/res/values/strings.xml9
10 files changed, 244 insertions, 56 deletions
diff --git a/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java b/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java
index 59f8e29e1..4561c9bad 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java
@@ -27,6 +27,10 @@ import java.util.concurrent.TimeUnit;
import de.danoeh.antennapod.core.ClientConfig;
import de.danoeh.antennapod.core.R;
import de.danoeh.antennapod.core.receiver.FeedUpdateReceiver;
+import de.danoeh.antennapod.core.storage.APCleanupAlgorithm;
+import de.danoeh.antennapod.core.storage.APNullCleanupAlgorithm;
+import de.danoeh.antennapod.core.storage.APQueueCleanupAlgorithm;
+import de.danoeh.antennapod.core.storage.EpisodeCleanupAlgorithm;
/**
* Provides access to preferences set by the user in the settings screen. A
@@ -67,6 +71,7 @@ public class UserPreferences {
// Network
public static final String PREF_UPDATE_INTERVAL = "prefAutoUpdateIntervall";
public static final String PREF_MOBILE_UPDATE = "prefMobileUpdate";
+ public static final String PREF_EPISODE_CLEANUP = "prefEpisodeCleanup";
public static final String PREF_PARALLEL_DOWNLOADS = "prefParallelDownloads";
public static final String PREF_EPISODE_CACHE_SIZE = "prefEpisodeCacheSize";
public static final String PREF_ENABLE_AUTODL = "prefEnableAutoDl";
@@ -93,6 +98,9 @@ public class UserPreferences {
// Experimental
public static final String PREF_SONIC = "prefSonic";
public static final String PREF_NORMALIZER = "prefNormalizer";
+ public static final int EPISODE_CLEANUP_QUEUE = -1;
+ public static final int EPISODE_CLEANUP_NULL = -2;
+ public static final int EPISODE_CLEANUP_DEFAULT = 0;
// Constants
private static int EPISODE_CACHE_SIZE_UNLIMITED = -1;
@@ -487,6 +495,18 @@ public class UserPreferences {
.apply();
}
+
+ public static EpisodeCleanupAlgorithm getEpisodeCleanupAlgorithm() {
+ int cleanupValue = Integer.valueOf(prefs.getString(PREF_EPISODE_CLEANUP, "-1"));
+ if (cleanupValue == EPISODE_CLEANUP_QUEUE) {
+ return new APQueueCleanupAlgorithm();
+ } else if (cleanupValue == EPISODE_CLEANUP_NULL) {
+ return new APNullCleanupAlgorithm();
+ } else {
+ return new APCleanupAlgorithm(cleanupValue);
+ }
+ }
+
/**
* Return the folder where the app stores all of its data. This method will
* return the standard data folder if none has been set by the user.
@@ -646,5 +666,4 @@ public class UserPreferences {
public static int readEpisodeCacheSize(String valueFromPrefs) {
return readEpisodeCacheSizeInternal(valueFromPrefs);
}
-
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/APCleanupAlgorithm.java b/core/src/main/java/de/danoeh/antennapod/core/storage/APCleanupAlgorithm.java
index 70b3aa90a..0dc54fb6e 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/APCleanupAlgorithm.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/APCleanupAlgorithm.java
@@ -4,34 +4,53 @@ import android.content.Context;
import android.util.Log;
import java.util.ArrayList;
+import java.util.Calendar;
import java.util.Collections;
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.feed.FeedMedia;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.util.LongList;
/**
* Implementation of the EpisodeCleanupAlgorithm interface used by AntennaPod.
*/
-public class APCleanupAlgorithm implements EpisodeCleanupAlgorithm<Integer> {
+public class APCleanupAlgorithm extends EpisodeCleanupAlgorithm {
private static final String TAG = "APCleanupAlgorithm";
+ /** the number of days after playback to wait before an item is eligible to be cleaned up */
+ private final int numberOfDaysAfterPlayback;
+
+ public APCleanupAlgorithm(int numberOfDaysAfterPlayback) {
+ this.numberOfDaysAfterPlayback = numberOfDaysAfterPlayback;
+ }
@Override
- public int performCleanup(Context context, Integer episodeNumber) {
+ public int performCleanup(Context context, int numberOfEpisodesToDelete) {
List<FeedItem> candidates = new ArrayList<>();
List<FeedItem> downloadedItems = DBReader.getDownloadedItems();
- LongList queue = DBReader.getQueueIDList();
List<FeedItem> delete;
+ Calendar cal = Calendar.getInstance();
+ cal.add(Calendar.DAY_OF_MONTH, -1 * numberOfDaysAfterPlayback);
+ Date mostRecentDateForDeletion = cal.getTime();
for (FeedItem item : downloadedItems) {
- if (item.hasMedia() && item.getMedia().isDownloaded()
- && !queue.contains(item.getId()) && item.isPlayed()) {
- candidates.add(item);
+ if (item.hasMedia()
+ && item.getMedia().isDownloaded()
+ && !item.isTagged(FeedItem.TAG_QUEUE)
+ && item.isPlayed()
+ && !item.isTagged(FeedItem.TAG_FAVORITE)) {
+ FeedMedia media = item.getMedia();
+ // make sure this candidate was played at least the proper amount of days prior
+ // to now
+ if (media != null
+ && media.getPlaybackCompletionDate() != null
+ && media.getPlaybackCompletionDate().before(mostRecentDateForDeletion)) {
+ candidates.add(item);
+ }
}
-
}
Collections.sort(candidates, (lhs, rhs) -> {
@@ -47,8 +66,8 @@ public class APCleanupAlgorithm implements EpisodeCleanupAlgorithm<Integer> {
return l.compareTo(r);
});
- if (candidates.size() > episodeNumber) {
- delete = candidates.subList(0, episodeNumber);
+ if (candidates.size() > numberOfEpisodesToDelete) {
+ delete = candidates.subList(0, numberOfEpisodesToDelete);
} else {
delete = candidates;
}
@@ -66,34 +85,15 @@ public class APCleanupAlgorithm implements EpisodeCleanupAlgorithm<Integer> {
Log.i(TAG, String.format(
"Auto-delete deleted %d episodes (%d requested)", counter,
- episodeNumber));
+ numberOfEpisodesToDelete));
return counter;
}
@Override
- public Integer getDefaultCleanupParameter() {
- return getPerformAutoCleanupArgs(0);
+ public int getDefaultCleanupParameter() {
+ return getNumEpisodesToCleanup(0);
}
- @Override
- public Integer getPerformCleanupParameter(List<FeedItem> items) {
- return getPerformAutoCleanupArgs(items.size());
- }
-
- static int getPerformAutoCleanupArgs(final int episodeNumber) {
- if (episodeNumber >= 0
- && UserPreferences.getEpisodeCacheSize() != UserPreferences
- .getEpisodeCacheSizeUnlimited()) {
- int downloadedEpisodes = DBReader
- .getNumberOfDownloadedEpisodes();
- if (downloadedEpisodes + episodeNumber >= UserPreferences
- .getEpisodeCacheSize()) {
-
- return downloadedEpisodes + episodeNumber
- - UserPreferences.getEpisodeCacheSize();
- }
- }
- return 0;
- }
+ public int getNumberOfDaysAfterPlayback() { return numberOfDaysAfterPlayback; }
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/APDownloadAlgorithm.java b/core/src/main/java/de/danoeh/antennapod/core/storage/APDownloadAlgorithm.java
index f2c56ee79..9e21a55f2 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/APDownloadAlgorithm.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/APDownloadAlgorithm.java
@@ -19,8 +19,6 @@ import de.danoeh.antennapod.core.util.PowerUtils;
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 new items and request a download if
* 1. Network is available
@@ -72,8 +70,8 @@ public class APDownloadAlgorithm implements AutomaticDownloadAlgorithm {
int autoDownloadableEpisodes = candidates.size();
int downloadedEpisodes = DBReader.getNumberOfDownloadedEpisodes();
- int deletedEpisodes = cleanupAlgorithm.performCleanup(context,
- APCleanupAlgorithm.getPerformAutoCleanupArgs(autoDownloadableEpisodes));
+ int deletedEpisodes = UserPreferences.getEpisodeCleanupAlgorithm()
+ .makeRoomForEpisodes(context, autoDownloadableEpisodes);
boolean cacheIsUnlimited = UserPreferences.getEpisodeCacheSize() == UserPreferences
.getEpisodeCacheSizeUnlimited();
int episodeCacheSize = UserPreferences.getEpisodeCacheSize();
@@ -101,5 +99,4 @@ public class APDownloadAlgorithm implements AutomaticDownloadAlgorithm {
}
};
}
-
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/APNullCleanupAlgorithm.java b/core/src/main/java/de/danoeh/antennapod/core/storage/APNullCleanupAlgorithm.java
new file mode 100644
index 000000000..132b61853
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/APNullCleanupAlgorithm.java
@@ -0,0 +1,24 @@
+package de.danoeh.antennapod.core.storage;
+
+import android.content.Context;
+import android.util.Log;
+
+/**
+ * A cleanup algorithm that never removes anything
+ */
+public class APNullCleanupAlgorithm extends EpisodeCleanupAlgorithm {
+
+ private static final String TAG = "APNullCleanupAlgorithm";
+
+ @Override
+ public int performCleanup(Context context, int parameter) {
+ // never clean anything up
+ Log.i(TAG, "performCleanup: Not removing anything");
+ return 0;
+ }
+
+ @Override
+ public int getDefaultCleanupParameter() {
+ return 0;
+ }
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/APQueueCleanupAlgorithm.java b/core/src/main/java/de/danoeh/antennapod/core/storage/APQueueCleanupAlgorithm.java
new file mode 100644
index 000000000..234d6162c
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/APQueueCleanupAlgorithm.java
@@ -0,0 +1,81 @@
+package de.danoeh.antennapod.core.storage;
+
+import android.content.Context;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collections;
+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.feed.FeedMedia;
+import de.danoeh.antennapod.core.util.LongList;
+
+/**
+ * A cleanup algorithm that removes any item that isn't in the queue and isn't a favorite
+ * but only if space is needed.
+ */
+public class APQueueCleanupAlgorithm extends EpisodeCleanupAlgorithm {
+
+ private static final String TAG = "APQueueCleanupAlgorithm";
+
+ @Override
+ public int performCleanup(Context context, int numberOfEpisodesToDelete) {
+ List<FeedItem> candidates = new ArrayList<>();
+ List<FeedItem> downloadedItems = DBReader.getDownloadedItems();
+ List<FeedItem> delete;
+ for (FeedItem item : downloadedItems) {
+ if (item.hasMedia()
+ && item.getMedia().isDownloaded()
+ && !item.isTagged(FeedItem.TAG_QUEUE)
+ && !item.isTagged(FeedItem.TAG_FAVORITE)) {
+ candidates.add(item);
+ }
+ }
+
+ // in the absence of better data, we'll sort by item publication date
+ Collections.sort(candidates, (lhs, rhs) -> {
+ Date l = lhs.getPubDate();
+ Date r = rhs.getPubDate();
+
+ if (l == null) {
+ l = new Date();
+ }
+ if (r == null) {
+ r = new Date();
+ }
+ return l.compareTo(r);
+ });
+
+ if (candidates.size() > numberOfEpisodesToDelete) {
+ delete = candidates.subList(0, numberOfEpisodesToDelete);
+ } 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,
+ numberOfEpisodesToDelete));
+
+ return counter;
+ }
+
+ @Override
+ public int getDefaultCleanupParameter() {
+ return getNumEpisodesToCleanup(0);
+ }
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java
index 3f9cece77..f54e13471 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java
@@ -332,9 +332,7 @@ public final class DBTasks {
@Override
public void run() {
ClientConfig.dbTasksCallbacks.getEpisodeCacheCleanupAlgorithm()
- .performCleanup(context,
- ClientConfig.dbTasksCallbacks.getEpisodeCacheCleanupAlgorithm()
- .getPerformCleanupParameter(Arrays.asList(items)));
+ .makeRoomForEpisodes(context, items.length);
}
}.start();
@@ -390,8 +388,7 @@ public final class DBTasks {
* @param context Used for accessing the DB.
*/
public static void performAutoCleanup(final Context context) {
- ClientConfig.dbTasksCallbacks.getEpisodeCacheCleanupAlgorithm().performCleanup(context,
- ClientConfig.dbTasksCallbacks.getEpisodeCacheCleanupAlgorithm().getDefaultCleanupParameter());
+ ClientConfig.dbTasksCallbacks.getEpisodeCacheCleanupAlgorithm().performCleanup(context);
}
/**
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/EpisodeCleanupAlgorithm.java b/core/src/main/java/de/danoeh/antennapod/core/storage/EpisodeCleanupAlgorithm.java
index 91f221f39..0f402745c 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/EpisodeCleanupAlgorithm.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/EpisodeCleanupAlgorithm.java
@@ -2,35 +2,60 @@ package de.danoeh.antennapod.core.storage;
import android.content.Context;
-import java.util.List;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
-import de.danoeh.antennapod.core.feed.FeedItem;
-
-public interface EpisodeCleanupAlgorithm<T> {
+public abstract class EpisodeCleanupAlgorithm {
/**
* 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.
+ * @param context Can be used for accessing the database
+ * @param numToRemove 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);
+ public abstract int performCleanup(Context context, int numToRemove);
+
+ public int performCleanup(Context context) {
+ return performCleanup(context, getDefaultCleanupParameter());
+ }
/**
* 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();
+ public abstract int getDefaultCleanupParameter();
/**
- * Returns a parameter for performCleanup.
+ * Cleans up just enough episodes to make room for the requested number
*
- * @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.
+ * @param context Can be used for accessing the database
+ * @param amountOfRoomNeeded the number of episodes we need space for
+ * @return The number of epiosdes that were deleted
+ */
+ public int makeRoomForEpisodes(Context context, int amountOfRoomNeeded) {
+ return performCleanup(context, getNumEpisodesToCleanup(amountOfRoomNeeded));
+ }
+
+ /**
+ * @param amountOfRoomNeeded the number of episodes we want to download
+ * @return the number of episodes to delete in order to make room
*/
- public T getPerformCleanupParameter(List<FeedItem> items);
+ protected int getNumEpisodesToCleanup(final int amountOfRoomNeeded) {
+ if (amountOfRoomNeeded >= 0
+ && UserPreferences.getEpisodeCacheSize() != UserPreferences
+ .getEpisodeCacheSizeUnlimited()) {
+ int downloadedEpisodes = DBReader
+ .getNumberOfDownloadedEpisodes();
+ if (downloadedEpisodes + amountOfRoomNeeded >= UserPreferences
+ .getEpisodeCacheSize()) {
+
+ return downloadedEpisodes + amountOfRoomNeeded
+ - UserPreferences.getEpisodeCacheSize();
+ }
+ }
+ return 0;
+ }
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java b/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java
index 9e20693a3..d55d4c231 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java
@@ -790,6 +790,21 @@ public class PodDBAdapter {
db.execSQL(sql);
}
+ public void setFavorites(List<FeedItem> favorites) {
+ ContentValues values = new ContentValues();
+ db.beginTransaction();
+ db.delete(TABLE_NAME_FAVORITES, null, null);
+ for (int i = 0; i < favorites.size(); i++) {
+ FeedItem item = favorites.get(i);
+ values.put(KEY_ID, i);
+ values.put(KEY_FEEDITEM, item.getId());
+ values.put(KEY_FEED, item.getFeed().getId());
+ db.insertWithOnConflict(TABLE_NAME_FAVORITES, null, values, SQLiteDatabase.CONFLICT_REPLACE);
+ }
+ db.setTransactionSuccessful();
+ db.endTransaction();
+ }
+
/**
* Adds the item to favorites
*/
diff --git a/core/src/main/res/values/arrays.xml b/core/src/main/res/values/arrays.xml
index b2f928617..341a7e520 100644
--- a/core/src/main/res/values/arrays.xml
+++ b/core/src/main/res/values/arrays.xml
@@ -44,6 +44,7 @@
<item>100</item>
<item>@string/pref_episode_cache_unlimited</item>
</string-array>
+
<string-array name="episode_cache_size_values">
<item>5</item>
<item>10</item>
@@ -53,6 +54,26 @@
<item>-1</item>
</string-array>
+ <string-array name="episode_cleanup_entries">
+ <item>@string/episode_cleanup_queue_removal</item>
+ <item>0</item>
+ <item>1</item>
+ <item>3</item>
+ <item>5</item>
+ <item>7</item>
+ <item>@string/episode_cleanup_never</item>
+ </string-array>
+
+ <string-array name="episode_cleanup_values">
+ <item>-1</item>
+ <item>0</item>
+ <item>1</item>
+ <item>3</item>
+ <item>5</item>
+ <item>7</item>
+ <item>-2</item>
+ </string-array>
+
<string-array name="playback_speed_values">
<item>0.5</item>
<item>0.6</item>
diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml
index 34f3791c4..168477463 100644
--- a/core/src/main/res/values/strings.xml
+++ b/core/src/main/res/values/strings.xml
@@ -87,6 +87,13 @@
<string name="feed_auto_download_always">Always</string>
<string name="feed_auto_download_never">Never</string>
<string name="send_label">Send...</string>
+ <string name="episode_cleanup_never">Never</string>
+ <string name="episode_cleanup_queue_removal">When not in queue</string>
+ <string name="episode_cleanup_after_listening">After listening</string>
+ <plurals name="episode_cleanup_days_after_listening">
+ <item quantity="one">1 day after listening</item>
+ <item quantity="other">%d days after listening</item>
+ </plurals>
<!-- 'Add Feed' Activity labels -->
<string name="feedurl_label">Feed URL</string>
@@ -267,6 +274,8 @@
<string name="queue_label">Queue</string>
<string name="services_label">Services</string>
<string name="flattr_label">Flattr</string>
+ <string name="pref_episode_cleanup_title">Episode Cleanup</string>
+ <string name="pref_episode_cleanup_summary">Episodes that aren\'t in the queue and aren\'t favorites should be eligible for removal if space is needed</string>
<string name="pref_pauseOnHeadsetDisconnect_sum">Pause playback when the headphones are disconnected</string>
<string name="pref_unpauseOnHeadsetReconnect_sum">Resume playback when the headphones are reconnected</string>
<string name="pref_followQueue_sum">Jump to next queue item when playback completes</string>