Merge pull request #2688 from orionlee/auto_download_12_hour_option_2685
Episode Cleanup (for automatic download) - support 12 hours after finishing
This commit is contained in:
commit
91987460db
|
@ -32,7 +32,7 @@ android {
|
||||||
// Version code schema:
|
// Version code schema:
|
||||||
// "1.2.3-SNAPSHOT" -> 1020300
|
// "1.2.3-SNAPSHOT" -> 1020300
|
||||||
// "1.2.3-RC4" -> 1020304
|
// "1.2.3-RC4" -> 1020304
|
||||||
versionCode 1070195
|
versionCode 1070196
|
||||||
versionName "1.7.1"
|
versionName "1.7.1"
|
||||||
testApplicationId "de.test.antennapod"
|
testApplicationId "de.test.antennapod"
|
||||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||||
|
|
|
@ -7,18 +7,9 @@ import android.support.test.espresso.contrib.RecyclerViewActions;
|
||||||
import android.support.test.rule.ActivityTestRule;
|
import android.support.test.rule.ActivityTestRule;
|
||||||
import android.support.test.runner.AndroidJUnit4;
|
import android.support.test.runner.AndroidJUnit4;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import com.robotium.solo.Solo;
|
import com.robotium.solo.Solo;
|
||||||
import com.robotium.solo.Timeout;
|
import com.robotium.solo.Timeout;
|
||||||
import de.danoeh.antennapod.R;
|
|
||||||
import de.danoeh.antennapod.activity.PreferenceActivity;
|
|
||||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
|
||||||
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.fragment.EpisodesFragment;
|
|
||||||
import de.danoeh.antennapod.fragment.QueueFragment;
|
|
||||||
import de.danoeh.antennapod.fragment.SubscriptionFragment;
|
|
||||||
|
|
||||||
import org.hamcrest.Matcher;
|
import org.hamcrest.Matcher;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
|
@ -30,6 +21,17 @@ import org.junit.runner.RunWith;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import de.danoeh.antennapod.R;
|
||||||
|
import de.danoeh.antennapod.activity.PreferenceActivity;
|
||||||
|
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||||
|
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.fragment.EpisodesFragment;
|
||||||
|
import de.danoeh.antennapod.fragment.QueueFragment;
|
||||||
|
import de.danoeh.antennapod.fragment.SubscriptionFragment;
|
||||||
|
|
||||||
import static android.support.test.InstrumentationRegistry.getInstrumentation;
|
import static android.support.test.InstrumentationRegistry.getInstrumentation;
|
||||||
import static android.support.test.espresso.Espresso.onView;
|
import static android.support.test.espresso.Espresso.onView;
|
||||||
import static android.support.test.espresso.action.ViewActions.click;
|
import static android.support.test.espresso.action.ViewActions.click;
|
||||||
|
@ -397,7 +399,7 @@ public class PreferencesTest {
|
||||||
EpisodeCleanupAlgorithm alg = UserPreferences.getEpisodeCleanupAlgorithm();
|
EpisodeCleanupAlgorithm alg = UserPreferences.getEpisodeCleanupAlgorithm();
|
||||||
if (alg instanceof APCleanupAlgorithm) {
|
if (alg instanceof APCleanupAlgorithm) {
|
||||||
APCleanupAlgorithm cleanupAlg = (APCleanupAlgorithm)alg;
|
APCleanupAlgorithm cleanupAlg = (APCleanupAlgorithm)alg;
|
||||||
return cleanupAlg.getNumberOfDaysAfterPlayback() == 0;
|
return cleanupAlg.getNumberOfHoursAfterPlayback() == 0;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
@ -416,7 +418,7 @@ public class PreferencesTest {
|
||||||
EpisodeCleanupAlgorithm alg = UserPreferences.getEpisodeCleanupAlgorithm();
|
EpisodeCleanupAlgorithm alg = UserPreferences.getEpisodeCleanupAlgorithm();
|
||||||
if (alg instanceof APCleanupAlgorithm) {
|
if (alg instanceof APCleanupAlgorithm) {
|
||||||
APCleanupAlgorithm cleanupAlg = (APCleanupAlgorithm)alg;
|
APCleanupAlgorithm cleanupAlg = (APCleanupAlgorithm)alg;
|
||||||
return cleanupAlg.getNumberOfDaysAfterPlayback() == 5;
|
return cleanupAlg.getNumberOfHoursAfterPlayback() == 120; // 5 days
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
|
|
@ -846,8 +846,11 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
|
||||||
entries[x] = res.getString(R.string.episode_cleanup_never);
|
entries[x] = res.getString(R.string.episode_cleanup_never);
|
||||||
} else if (v == 0) {
|
} else if (v == 0) {
|
||||||
entries[x] = res.getString(R.string.episode_cleanup_after_listening);
|
entries[x] = res.getString(R.string.episode_cleanup_after_listening);
|
||||||
|
} else if (v > 0 && v < 24) {
|
||||||
|
entries[x] = res.getQuantityString(R.plurals.episode_cleanup_hours_after_listening, v, v);
|
||||||
} else {
|
} else {
|
||||||
entries[x] = res.getQuantityString(R.plurals.episode_cleanup_days_after_listening, v, v);
|
int numDays = (int)(v / 24); // assume underlying value will be NOT fraction of days, e.g., 36 (hours)
|
||||||
|
entries[x] = res.getQuantityString(R.plurals.episode_cleanup_days_after_listening, numDays, numDays);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pref.setEntries(entries);
|
pref.setEntries(entries);
|
||||||
|
|
|
@ -58,11 +58,19 @@ class UpdateManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void onUpgrade(final int oldVersionCode, final int newVersionCode) {
|
private static void onUpgrade(final int oldVersionCode, final int newVersionCode) {
|
||||||
if(oldVersionCode < 1050004) {
|
if (oldVersionCode < 1050004) {
|
||||||
if(MediaPlayer.isPrestoLibraryInstalled(context) && Build.VERSION.SDK_INT >= 16) {
|
if(MediaPlayer.isPrestoLibraryInstalled(context) && Build.VERSION.SDK_INT >= 16) {
|
||||||
UserPreferences.enableSonic();
|
UserPreferences.enableSonic();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (oldVersionCode < 1070196) {
|
||||||
|
// migrate episode cleanup value (unit changed from days to hours)
|
||||||
|
int oldValueInDays = UserPreferences.getEpisodeCleanupValue();
|
||||||
|
if (oldValueInDays > 0) {
|
||||||
|
UserPreferences.setEpisodeCleanupValue(oldValueInDays * 24);
|
||||||
|
} // else 0 or special negative values, no change needed
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -691,7 +691,7 @@ public class UserPreferences {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static EpisodeCleanupAlgorithm getEpisodeCleanupAlgorithm() {
|
public static EpisodeCleanupAlgorithm getEpisodeCleanupAlgorithm() {
|
||||||
int cleanupValue = Integer.parseInt(prefs.getString(PREF_EPISODE_CLEANUP, "-1"));
|
int cleanupValue = getEpisodeCleanupValue();
|
||||||
if (cleanupValue == EPISODE_CLEANUP_QUEUE) {
|
if (cleanupValue == EPISODE_CLEANUP_QUEUE) {
|
||||||
return new APQueueCleanupAlgorithm();
|
return new APQueueCleanupAlgorithm();
|
||||||
} else if (cleanupValue == EPISODE_CLEANUP_NULL) {
|
} else if (cleanupValue == EPISODE_CLEANUP_NULL) {
|
||||||
|
@ -701,6 +701,16 @@ public class UserPreferences {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int getEpisodeCleanupValue() {
|
||||||
|
return Integer.parseInt(prefs.getString(PREF_EPISODE_CLEANUP, "-1"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setEpisodeCleanupValue(int episodeCleanupValue) {
|
||||||
|
prefs.edit()
|
||||||
|
.putString(PREF_EPISODE_CLEANUP, Integer.toString(episodeCleanupValue))
|
||||||
|
.apply();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the folder where the app stores all of its data. This method will
|
* 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.
|
* return the standard data folder if none has been set by the user.
|
||||||
|
|
|
@ -2,6 +2,7 @@ package de.danoeh.antennapod.core.storage;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.VisibleForTesting;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -20,11 +21,12 @@ import de.danoeh.antennapod.core.feed.FeedMedia;
|
||||||
public class APCleanupAlgorithm extends EpisodeCleanupAlgorithm {
|
public class APCleanupAlgorithm extends EpisodeCleanupAlgorithm {
|
||||||
|
|
||||||
private static final String TAG = "APCleanupAlgorithm";
|
private static final String TAG = "APCleanupAlgorithm";
|
||||||
/** the number of days after playback to wait before an item is eligible to be cleaned up */
|
/** the number of days after playback to wait before an item is eligible to be cleaned up.
|
||||||
private final int numberOfDaysAfterPlayback;
|
Fractional for number of hours, e.g., 0.5 = 12 hours, 0.0416 = 1 hour. */
|
||||||
|
private final int numberOfHoursAfterPlayback;
|
||||||
|
|
||||||
public APCleanupAlgorithm(int numberOfDaysAfterPlayback) {
|
public APCleanupAlgorithm(int numberOfHoursAfterPlayback) {
|
||||||
this.numberOfDaysAfterPlayback = numberOfDaysAfterPlayback;
|
this.numberOfHoursAfterPlayback = numberOfHoursAfterPlayback;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -77,13 +79,17 @@ public class APCleanupAlgorithm extends EpisodeCleanupAlgorithm {
|
||||||
return counter;
|
return counter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
Date calcMostRecentDateForDeletion(@NonNull Date currentDate) {
|
||||||
|
return minusHours(currentDate, numberOfHoursAfterPlayback);
|
||||||
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private List<FeedItem> getCandidates() {
|
private List<FeedItem> getCandidates() {
|
||||||
List<FeedItem> candidates = new ArrayList<>();
|
List<FeedItem> candidates = new ArrayList<>();
|
||||||
List<FeedItem> downloadedItems = DBReader.getDownloadedItems();
|
List<FeedItem> downloadedItems = DBReader.getDownloadedItems();
|
||||||
Calendar cal = Calendar.getInstance();
|
|
||||||
cal.add(Calendar.DAY_OF_MONTH, -1 * numberOfDaysAfterPlayback);
|
Date mostRecentDateForDeletion = calcMostRecentDateForDeletion(new Date());
|
||||||
Date mostRecentDateForDeletion = cal.getTime();
|
|
||||||
for (FeedItem item : downloadedItems) {
|
for (FeedItem item : downloadedItems) {
|
||||||
if (item.hasMedia()
|
if (item.hasMedia()
|
||||||
&& item.getMedia().isDownloaded()
|
&& item.getMedia().isDownloaded()
|
||||||
|
@ -108,5 +114,16 @@ public class APCleanupAlgorithm extends EpisodeCleanupAlgorithm {
|
||||||
return getNumEpisodesToCleanup(0);
|
return getNumEpisodesToCleanup(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getNumberOfDaysAfterPlayback() { return numberOfDaysAfterPlayback; }
|
@VisibleForTesting
|
||||||
|
public int getNumberOfHoursAfterPlayback() { return numberOfHoursAfterPlayback; }
|
||||||
|
|
||||||
|
private static Date minusHours(Date baseDate, int numberOfHours) {
|
||||||
|
Calendar cal = Calendar.getInstance();
|
||||||
|
cal.setTime(baseDate);
|
||||||
|
|
||||||
|
cal.add(Calendar.HOUR_OF_DAY, -1 * numberOfHours);
|
||||||
|
|
||||||
|
return cal.getTime();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -130,6 +130,7 @@ public final class Converter {
|
||||||
return String.format(Locale.getDefault(), "%.1f ", hours) + context.getString(R.string.time_hours);
|
return String.format(Locale.getDefault(), "%.1f ", hours) + context.getString(R.string.time_hours);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts the volume as read as the progress from a SeekBar scaled to 100 and as saved in
|
* Converts the volume as read as the progress from a SeekBar scaled to 100 and as saved in
|
||||||
* UserPreferences to the format taken by setVolume methods.
|
* UserPreferences to the format taken by setVolume methods.
|
||||||
|
|
|
@ -68,10 +68,11 @@
|
||||||
<string-array name="episode_cleanup_values">
|
<string-array name="episode_cleanup_values">
|
||||||
<item>-1</item>
|
<item>-1</item>
|
||||||
<item>0</item>
|
<item>0</item>
|
||||||
<item>1</item>
|
<item>12</item>
|
||||||
<item>3</item>
|
<item>24</item>
|
||||||
<item>5</item>
|
<item>72</item>
|
||||||
<item>7</item>
|
<item>120</item>
|
||||||
|
<item>168</item>
|
||||||
<item>-2</item>
|
<item>-2</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
|
|
|
@ -105,6 +105,10 @@
|
||||||
<string name="episode_cleanup_never">Never</string>
|
<string name="episode_cleanup_never">Never</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">
|
||||||
|
<item quantity="one">1 hour after finishing</item>
|
||||||
|
<item quantity="other">%d hours after finishing</item>
|
||||||
|
</plurals>
|
||||||
<plurals name="episode_cleanup_days_after_listening">
|
<plurals name="episode_cleanup_days_after_listening">
|
||||||
<item quantity="one">1 day after finishing</item>
|
<item quantity="one">1 day after finishing</item>
|
||||||
<item quantity="other">%d days after finishing</item>
|
<item quantity="other">%d days after finishing</item>
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
package de.danoeh.antennapod.core.storage;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
public class APCleanupAlgorithmTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCalcMostRecentDateForDeletion() throws Exception {
|
||||||
|
APCleanupAlgorithm algo = new APCleanupAlgorithm(24);
|
||||||
|
Date curDateForTest = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").parse("2018-11-13T14:08:56-0800");
|
||||||
|
Date resExpected = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").parse("2018-11-12T14:08:56-0800");
|
||||||
|
Date resActual = algo.calcMostRecentDateForDeletion(curDateForTest);
|
||||||
|
assertEquals("cutoff for retaining most recent 1 day", resExpected, resActual);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue