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.
This commit is contained in:
parent
9924952e2f
commit
08edd151f9
|
@ -15,6 +15,7 @@ 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;
|
||||
import de.danoeh.antennapod.core.storage.ExceptFavoriteCleanupAlgorithm;
|
||||
import de.danoeh.antennapod.fragment.EpisodesFragment;
|
||||
import de.danoeh.antennapod.fragment.QueueFragment;
|
||||
import de.danoeh.antennapod.fragment.SubscriptionFragment;
|
||||
|
@ -371,6 +372,17 @@ public class PreferencesTest {
|
|||
.until(() -> enableAutodownloadOnBattery == UserPreferences.isEnableAutodownloadOnBattery());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEpisodeCleanupFavoriteOnly() {
|
||||
clickPreference(R.string.network_pref);
|
||||
onView(withText(R.string.pref_automatic_download_title)).perform(click());
|
||||
onView(withText(R.string.pref_episode_cleanup_title)).perform(click());
|
||||
onView(isRoot()).perform(waitForView(withText(R.string.episode_cleanup_except_favorite_removal), 1000));
|
||||
onView(withText(R.string.episode_cleanup_except_favorite_removal)).perform(click());
|
||||
Awaitility.await().atMost(1000, MILLISECONDS)
|
||||
.until(() -> UserPreferences.getEpisodeCleanupAlgorithm() instanceof ExceptFavoriteCleanupAlgorithm);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEpisodeCleanupQueueOnly() {
|
||||
clickPreference(R.string.network_pref);
|
||||
|
|
|
@ -174,7 +174,9 @@ public class AutoDownloadPreferencesFragment extends PreferenceFragmentCompat {
|
|||
String[] entries = new String[values.length];
|
||||
for (int x = 0; x < values.length; x++) {
|
||||
int v = Integer.parseInt(values[x]);
|
||||
if (v == UserPreferences.EPISODE_CLEANUP_QUEUE) {
|
||||
if (v == UserPreferences.EPISODE_CLEANUP_EXCEPT_FAVORITE) {
|
||||
entries[x] = res.getString(R.string.episode_cleanup_except_favorite_removal);
|
||||
} else if (v == UserPreferences.EPISODE_CLEANUP_QUEUE) {
|
||||
entries[x] = res.getString(R.string.episode_cleanup_queue_removal);
|
||||
} else if (v == UserPreferences.EPISODE_CLEANUP_NULL){
|
||||
entries[x] = res.getString(R.string.episode_cleanup_never);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
|
|
|
@ -116,6 +116,7 @@
|
|||
<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_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>
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue