summaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
authorJonas Kalderstam <jonas@cowboyprogrammer.org>2020-11-16 11:29:11 +0100
committerJonas Kalderstam <jonas@cowboyprogrammer.org>2021-02-05 11:15:22 +0100
commit08edd151f9bc01873128fa97c948841b65b6455c (patch)
treedd44dccccd873ecf2e5a4da49ef6d050fe72fddf /core
parent9924952e2f33cca36f44463426d63411d56edfe5 (diff)
downloadAntennaPod-08edd151f9bc01873128fa97c948841b65b6455c.zip
Added new cleanup option: when not favorited
This is another way of solving #2077. The root issue is that queued episodes are never auto-deleted currently which means that if you automatically add episodes to the queue you will eventually end up with AntennaPod refusing to auto download more episodes because the cache is full and it can't make space. This option will only refuse to delete favorited items. Otherwise it will simply delete the oldest episodes.
Diffstat (limited to 'core')
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java6
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/ExceptFavoriteCleanupAlgorithm.java93
-rw-r--r--core/src/main/res/values/arrays.xml2
-rw-r--r--core/src/main/res/values/strings.xml3
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/storage/ExceptFavoriteCleanupAlgorithmTest.java89
5 files changed, 191 insertions, 2 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 3d8d1b6bc..807348896 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
@@ -36,6 +36,7 @@ import de.danoeh.antennapod.core.feed.MediaType;
import de.danoeh.antennapod.core.feed.SubscriptionsFilter;
import de.danoeh.antennapod.core.service.download.ProxyConfig;
import de.danoeh.antennapod.core.storage.APCleanupAlgorithm;
+import de.danoeh.antennapod.core.storage.ExceptFavoriteCleanupAlgorithm;
import de.danoeh.antennapod.core.storage.APNullCleanupAlgorithm;
import de.danoeh.antennapod.core.storage.APQueueCleanupAlgorithm;
import de.danoeh.antennapod.core.storage.EpisodeCleanupAlgorithm;
@@ -137,6 +138,7 @@ public class UserPreferences {
public static final String PREF_CAST_ENABLED = "prefCast"; //Used for enabling Chromecast support
public static final int EPISODE_CLEANUP_QUEUE = -1;
public static final int EPISODE_CLEANUP_NULL = -2;
+ public static final int EPISODE_CLEANUP_EXCEPT_FAVORITE = -3;
public static final int EPISODE_CLEANUP_DEFAULT = 0;
// Constants
@@ -882,7 +884,9 @@ public class UserPreferences {
return new APNullCleanupAlgorithm();
}
int cleanupValue = getEpisodeCleanupValue();
- if (cleanupValue == EPISODE_CLEANUP_QUEUE) {
+ if (cleanupValue == EPISODE_CLEANUP_EXCEPT_FAVORITE) {
+ return new ExceptFavoriteCleanupAlgorithm();
+ } else if (cleanupValue == EPISODE_CLEANUP_QUEUE) {
return new APQueueCleanupAlgorithm();
} else if (cleanupValue == EPISODE_CLEANUP_NULL) {
return new APNullCleanupAlgorithm();
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/ExceptFavoriteCleanupAlgorithm.java b/core/src/main/java/de/danoeh/antennapod/core/storage/ExceptFavoriteCleanupAlgorithm.java
new file mode 100644
index 000000000..0a495115f
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/ExceptFavoriteCleanupAlgorithm.java
@@ -0,0 +1,93 @@
+package de.danoeh.antennapod.core.storage;
+
+import android.content.Context;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+import java.util.concurrent.ExecutionException;
+
+import de.danoeh.antennapod.core.feed.FeedItem;
+
+/**
+ * A cleanup algorithm that removes any item that isn't a favorite but only if space is needed.
+ */
+public class ExceptFavoriteCleanupAlgorithm extends EpisodeCleanupAlgorithm {
+
+ private static final String TAG = "ExceptFavCleanupAlgo";
+
+ /**
+ * The maximum number of episodes that could be cleaned up.
+ *
+ * @return the number of episodes that *could* be cleaned up, if needed
+ */
+ public int getReclaimableItems() {
+ return getCandidates().size();
+ }
+
+ @Override
+ public int performCleanup(Context context, int numberOfEpisodesToDelete) {
+ List<FeedItem> candidates = getCandidates();
+ List<FeedItem> delete;
+
+ // 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 && r != null) {
+ return l.compareTo(r);
+ } else {
+ // No date - compare by id which should be always incremented
+ return Long.compare(lhs.getId(), rhs.getId());
+ }
+ });
+
+ 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(Locale.US,
+ "Auto-delete deleted %d episodes (%d requested)", counter,
+ numberOfEpisodesToDelete));
+
+ return counter;
+ }
+
+ @NonNull
+ private List<FeedItem> getCandidates() {
+ List<FeedItem> candidates = new ArrayList<>();
+ List<FeedItem> downloadedItems = DBReader.getDownloadedItems();
+ for (FeedItem item : downloadedItems) {
+ if (item.hasMedia()
+ && item.getMedia().isDownloaded()
+ && !item.isTagged(FeedItem.TAG_FAVORITE)) {
+ candidates.add(item);
+ }
+ }
+ return candidates;
+ }
+
+ @Override
+ public int getDefaultCleanupParameter() {
+ return getNumEpisodesToCleanup(0);
+ }
+}
diff --git a/core/src/main/res/values/arrays.xml b/core/src/main/res/values/arrays.xml
index 6492c1aa2..6b3a10f46 100644
--- a/core/src/main/res/values/arrays.xml
+++ b/core/src/main/res/values/arrays.xml
@@ -96,6 +96,7 @@
</string-array>
<string-array name="episode_cleanup_entries">
+ <item>@string/episode_cleanup_except_favorite_removal</item>
<item>@string/episode_cleanup_queue_removal</item>
<item>0</item>
<item>1</item>
@@ -133,6 +134,7 @@
</string-array>
<string-array name="episode_cleanup_values">
+ <item>-3</item>
<item>-1</item>
<item>0</item>
<item>12</item>
diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml
index 0cd8bcd54..91ec75782 100644
--- a/core/src/main/res/values/strings.xml
+++ b/core/src/main/res/values/strings.xml
@@ -116,6 +116,7 @@
<string name="feed_auto_download_never">Never</string>
<string name="send_label">Send&#8230;</string>
<string name="episode_cleanup_never">Never</string>
+ <string name="episode_cleanup_except_favorite_removal">When not favorited</string>
<string name="episode_cleanup_queue_removal">When not in queue</string>
<string name="episode_cleanup_after_listening">After finishing</string>
<plurals name="episode_cleanup_hours_after_listening">
@@ -387,7 +388,7 @@
<string name="preference_search_clear_history">Clear history</string>
<string name="media_player">Media player</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 Auto Download needs space for new episodes</string>
+ <string name="pref_episode_cleanup_summary">Episodes that should be eligible for removal if Auto Download needs space for new episodes</string>
<string name="pref_pauseOnDisconnect_sum">Pause playback when headphones or bluetooth are disconnected</string>
<string name="pref_unpauseOnHeadsetReconnect_sum">Resume playback when the headphones are reconnected</string>
<string name="pref_unpauseOnBluetoothReconnect_sum">Resume playback when bluetooth reconnects</string>
diff --git a/core/src/test/java/de/danoeh/antennapod/core/storage/ExceptFavoriteCleanupAlgorithmTest.java b/core/src/test/java/de/danoeh/antennapod/core/storage/ExceptFavoriteCleanupAlgorithmTest.java
new file mode 100644
index 000000000..8c02391ca
--- /dev/null
+++ b/core/src/test/java/de/danoeh/antennapod/core/storage/ExceptFavoriteCleanupAlgorithmTest.java
@@ -0,0 +1,89 @@
+package de.danoeh.antennapod.core.storage;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests that the APFavoriteCleanupAlgorithm is working correctly.
+ */
+@RunWith(RobolectricTestRunner.class)
+public class ExceptFavoriteCleanupAlgorithmTest extends DbCleanupTests {
+ private final int numberOfItems = EPISODE_CACHE_SIZE * 2;
+
+ public ExceptFavoriteCleanupAlgorithmTest() {
+ setCleanupAlgorithm(UserPreferences.EPISODE_CLEANUP_EXCEPT_FAVORITE);
+ }
+
+ @Test
+ public void testPerformAutoCleanupHandleUnplayed() throws IOException {
+ Feed feed = new Feed("url", null, "title");
+ List<FeedItem> items = new ArrayList<>();
+ feed.setItems(items);
+ List<File> files = new ArrayList<>();
+ populateItems(numberOfItems, feed, items, files, FeedItem.UNPLAYED, false, false);
+
+ DBTasks.performAutoCleanup(context);
+ for (int i = 0; i < files.size(); i++) {
+ if (i < EPISODE_CACHE_SIZE) {
+ assertTrue("Only enough items should be deleted", files.get(i).exists());
+ } else {
+ assertFalse("Expected episode to be deleted", files.get(i).exists());
+ }
+ }
+ }
+
+ @Test
+ public void testPerformAutoCleanupDeletesQueued() throws IOException {
+ Feed feed = new Feed("url", null, "title");
+ List<FeedItem> items = new ArrayList<>();
+ feed.setItems(items);
+ List<File> files = new ArrayList<>();
+ populateItems(numberOfItems, feed, items, files, FeedItem.UNPLAYED, true, false);
+
+ DBTasks.performAutoCleanup(context);
+ for (int i = 0; i < files.size(); i++) {
+ if (i < EPISODE_CACHE_SIZE) {
+ assertTrue("Only enough items should be deleted", files.get(i).exists());
+ } else {
+ assertFalse("Queued episodes should be deleted", files.get(i).exists());
+ }
+ }
+ }
+
+ @Test
+ public void testPerformAutoCleanupSavesFavorited() throws IOException {
+ Feed feed = new Feed("url", null, "title");
+ List<FeedItem> items = new ArrayList<>();
+ feed.setItems(items);
+ List<File> files = new ArrayList<>();
+ populateItems(numberOfItems, feed, items, files, FeedItem.UNPLAYED, false, true);
+
+ DBTasks.performAutoCleanup(context);
+ for (int i = 0; i < files.size(); i++) {
+ assertTrue("Favorite episodes should should not be deleted", files.get(i).exists());
+ }
+ }
+
+ @Override
+ public void testPerformAutoCleanupShouldNotDeleteBecauseInQueue() throws IOException {
+ // Yes it should
+ }
+
+ @Override
+ public void testPerformAutoCleanupShouldNotDeleteBecauseInQueue_withFeedsWithNoMedia() throws IOException {
+ // Yes it should
+ }
+}