Merge pull request #4693 from spacecowboy/cleanup-space-needed

Added new cleanup option: when not favorited
This commit is contained in:
ByteHamster 2021-02-07 12:50:01 +01:00 committed by GitHub
commit ded779d0c9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 212 additions and 3 deletions

View File

@ -15,6 +15,7 @@ import de.danoeh.antennapod.core.storage.APCleanupAlgorithm;
import de.danoeh.antennapod.core.storage.APNullCleanupAlgorithm; import de.danoeh.antennapod.core.storage.APNullCleanupAlgorithm;
import de.danoeh.antennapod.core.storage.APQueueCleanupAlgorithm; import de.danoeh.antennapod.core.storage.APQueueCleanupAlgorithm;
import de.danoeh.antennapod.core.storage.EpisodeCleanupAlgorithm; 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.EpisodesFragment;
import de.danoeh.antennapod.fragment.QueueFragment; import de.danoeh.antennapod.fragment.QueueFragment;
import de.danoeh.antennapod.fragment.SubscriptionFragment; import de.danoeh.antennapod.fragment.SubscriptionFragment;
@ -371,6 +372,17 @@ public class PreferencesTest {
.until(() -> enableAutodownloadOnBattery == UserPreferences.isEnableAutodownloadOnBattery()); .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 @Test
public void testEpisodeCleanupQueueOnly() { public void testEpisodeCleanupQueueOnly() {
clickPreference(R.string.network_pref); clickPreference(R.string.network_pref);

View File

@ -174,7 +174,9 @@ public class AutoDownloadPreferencesFragment extends PreferenceFragmentCompat {
String[] entries = new String[values.length]; String[] entries = new String[values.length];
for (int x = 0; x < values.length; x++) { for (int x = 0; x < values.length; x++) {
int v = Integer.parseInt(values[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); entries[x] = res.getString(R.string.episode_cleanup_queue_removal);
} else if (v == UserPreferences.EPISODE_CLEANUP_NULL){ } else if (v == UserPreferences.EPISODE_CLEANUP_NULL){
entries[x] = res.getString(R.string.episode_cleanup_never); entries[x] = res.getString(R.string.episode_cleanup_never);

View File

@ -36,6 +36,7 @@ import de.danoeh.antennapod.core.feed.MediaType;
import de.danoeh.antennapod.core.feed.SubscriptionsFilter; import de.danoeh.antennapod.core.feed.SubscriptionsFilter;
import de.danoeh.antennapod.core.service.download.ProxyConfig; import de.danoeh.antennapod.core.service.download.ProxyConfig;
import de.danoeh.antennapod.core.storage.APCleanupAlgorithm; 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.APNullCleanupAlgorithm;
import de.danoeh.antennapod.core.storage.APQueueCleanupAlgorithm; import de.danoeh.antennapod.core.storage.APQueueCleanupAlgorithm;
import de.danoeh.antennapod.core.storage.EpisodeCleanupAlgorithm; 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 String PREF_CAST_ENABLED = "prefCast"; //Used for enabling Chromecast support
public static final int EPISODE_CLEANUP_QUEUE = -1; public static final int EPISODE_CLEANUP_QUEUE = -1;
public static final int EPISODE_CLEANUP_NULL = -2; 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; public static final int EPISODE_CLEANUP_DEFAULT = 0;
// Constants // Constants
@ -882,7 +884,9 @@ public class UserPreferences {
return new APNullCleanupAlgorithm(); return new APNullCleanupAlgorithm();
} }
int cleanupValue = getEpisodeCleanupValue(); 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(); return new APQueueCleanupAlgorithm();
} else if (cleanupValue == EPISODE_CLEANUP_NULL) { } else if (cleanupValue == EPISODE_CLEANUP_NULL) {
return new APNullCleanupAlgorithm(); return new APNullCleanupAlgorithm();

View File

@ -0,0 +1,99 @@
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;
import de.danoeh.antennapod.core.preferences.UserPreferences;
/**
* 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() {
int cacheSize = UserPreferences.getEpisodeCacheSize();
if (cacheSize != UserPreferences.getEpisodeCacheSizeUnlimited()) {
int downloadedEpisodes = DBReader.getNumberOfDownloadedEpisodes();
if (downloadedEpisodes > cacheSize) {
return downloadedEpisodes - cacheSize;
}
}
return 0;
}
}

View File

@ -96,6 +96,7 @@
</string-array> </string-array>
<string-array name="episode_cleanup_entries"> <string-array name="episode_cleanup_entries">
<item>@string/episode_cleanup_except_favorite_removal</item>
<item>@string/episode_cleanup_queue_removal</item> <item>@string/episode_cleanup_queue_removal</item>
<item>0</item> <item>0</item>
<item>1</item> <item>1</item>
@ -133,6 +134,7 @@
</string-array> </string-array>
<string-array name="episode_cleanup_values"> <string-array name="episode_cleanup_values">
<item>-3</item>
<item>-1</item> <item>-1</item>
<item>0</item> <item>0</item>
<item>12</item> <item>12</item>

View File

@ -116,6 +116,7 @@
<string name="feed_auto_download_never">Never</string> <string name="feed_auto_download_never">Never</string>
<string name="send_label">Send&#8230;</string> <string name="send_label">Send&#8230;</string>
<string name="episode_cleanup_never">Never</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_queue_removal">When not in queue</string>
<string name="episode_cleanup_after_listening">After finishing</string> <string name="episode_cleanup_after_listening">After finishing</string>
<plurals name="episode_cleanup_hours_after_listening"> <plurals name="episode_cleanup_hours_after_listening">
@ -387,7 +388,7 @@
<string name="preference_search_clear_history">Clear history</string> <string name="preference_search_clear_history">Clear history</string>
<string name="media_player">Media player</string> <string name="media_player">Media player</string>
<string name="pref_episode_cleanup_title">Episode Cleanup</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_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_unpauseOnHeadsetReconnect_sum">Resume playback when the headphones are reconnected</string>
<string name="pref_unpauseOnBluetoothReconnect_sum">Resume playback when bluetooth reconnects</string> <string name="pref_unpauseOnBluetoothReconnect_sum">Resume playback when bluetooth reconnects</string>

View File

@ -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
}
}