diff options
8 files changed, 140 insertions, 29 deletions
diff --git a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java index ecbec4edc..083ac5202 100644 --- a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java +++ b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java @@ -30,10 +30,15 @@ import android.widget.Toast; import com.afollestad.materialdialogs.MaterialDialog; +import org.apache.commons.lang3.ArrayUtils; + import java.io.File; import java.util.ArrayList; import java.util.Arrays; +import java.util.Calendar; +import java.util.GregorianCalendar; import java.util.List; +import java.util.concurrent.TimeUnit; import de.danoeh.antennapod.CrashReportWriter; import de.danoeh.antennapod.R; @@ -410,6 +415,7 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc public void onResume() { checkItemVisibility(); + setUpdateIntervalText(); setParallelDownloadsText(UserPreferences.getParallelDownloads()); setEpisodeCacheSizeText(UserPreferences.getEpisodeCacheSize()); setDataFolderText(); @@ -502,15 +508,19 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc final Resources res = ui.getActivity().getResources(); ListPreference pref = (ListPreference) ui.findPreference(UserPreferences.PREF_SMART_MARK_AS_PLAYED_SECS); - String[] values = res.getStringArray( - R.array.smart_mark_as_played_values); + String[] values = res.getStringArray(R.array.smart_mark_as_played_values); String[] entries = new String[values.length]; for (int x = 0; x < values.length; x++) { if(x == 0) { entries[x] = res.getString(R.string.pref_smart_mark_as_played_disabled); } else { Integer v = Integer.parseInt(values[x]); - entries[x] = res.getQuantityString(R.plurals.time_seconds_quantified, v, v); + if(v < 60) { + entries[x] = res.getQuantityString(R.plurals.time_seconds_quantified, v, v); + } else { + v /= 60; + entries[x] = res.getQuantityString(R.plurals.time_minutes_quantified, v, v); + } } } pref.setEntries(entries); @@ -548,6 +558,32 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc } } + private void setUpdateIntervalText() { + Context context = ui.getActivity().getApplicationContext(); + String val; + long interval = UserPreferences.getUpdateInterval(); + if(interval > 0) { + int hours = (int) TimeUnit.MILLISECONDS.toHours(interval); + String hoursStr = context.getResources().getQuantityString(R.plurals.time_hours_quantified, hours, hours); + val = String.format(context.getString(R.string.pref_autoUpdateIntervallOrTime_every), hoursStr); + } else { + int[] timeOfDay = UserPreferences.getUpdateTimeOfDay(); + if(timeOfDay.length == 2) { + Calendar cal = new GregorianCalendar(); + cal.set(Calendar.HOUR_OF_DAY, timeOfDay[0]); + cal.set(Calendar.MINUTE, timeOfDay[1]); + String timeOfDayStr = DateFormat.getTimeFormat(context).format(cal.getTime()); + val = String.format(context.getString(R.string.pref_autoUpdateIntervallOrTime_at), + timeOfDayStr); + } else { + val = context.getString(R.string.pref_smart_mark_as_played_disabled); + } + } + String summary = context.getString(R.string.pref_autoUpdateIntervallOrTime_sum) + "\n" + + String.format(context.getString(R.string.pref_current_value), val); + ui.findPreference(UserPreferences.PREF_UPDATE_INTERVAL).setSummary(summary); + } + private void setParallelDownloadsText(int downloads) { final Resources res = ui.getActivity().getResources(); @@ -767,10 +803,17 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc builder.setTitle(context.getString(R.string.pref_autoUpdateIntervallOrTime_Interval)); final String[] values = context.getResources().getStringArray(R.array.update_intervall_values); final String[] entries = getUpdateIntervalEntries(values); - builder.setSingleChoiceItems(entries, -1, (dialog1, which) -> { + long currInterval = UserPreferences.getUpdateInterval(); + int checkedItem = -1; + if(currInterval > 0) { + String currIntervalStr = String.valueOf(TimeUnit.MILLISECONDS.toHours(currInterval)); + checkedItem = ArrayUtils.indexOf(values, currIntervalStr); + } + builder.setSingleChoiceItems(entries, checkedItem, (dialog1, which) -> { int hours = Integer.valueOf(values[which]); UserPreferences.setUpdateInterval(hours); dialog1.dismiss(); + setUpdateIntervalText(); }); builder.setNegativeButton(context.getString(R.string.cancel_label), null); builder.show(); @@ -789,6 +832,7 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc if (view.getTag() == null) { // onTimeSet() may get called twice! view.setTag("TAGGED"); UserPreferences.setUpdateTimeOfDay(selectedHourOfDay, selectedMinute); + setUpdateIntervalText(); } }, hourOfDay, minute, DateFormat.is24HourFormat(context)); timePickerDialog.setTitle(context.getString(R.string.pref_autoUpdateIntervallOrTime_TimeOfDay)); @@ -798,6 +842,7 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc @Override public void onNeutral(MaterialDialog dialog) { UserPreferences.setUpdateInterval(0); + setUpdateIntervalText(); } }); builder.forceStacking(true); diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItem.java b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItem.java index c54cc1d5b..00b32de5f 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItem.java +++ b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItem.java @@ -11,6 +11,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.Callable; +import java.util.concurrent.TimeUnit; import de.danoeh.antennapod.core.asynctask.ImageResource; import de.danoeh.antennapod.core.storage.DBReader; @@ -75,7 +76,13 @@ public class FeedItem extends FeedComponent implements ShownotesProvider, Flattr private List<Chapter> chapters; private FeedImage image; - private boolean autoDownload = true; + /* + * 0: auto download disabled + * 1: auto download enabled + * > 1: auto download enabled, (approx.) timestamp of the last failed attempt + * where last digit denotes the number of failed attempts + */ + private long autoDownload = 0; /** * Any tags assigned to this item @@ -93,7 +100,7 @@ public class FeedItem extends FeedComponent implements ShownotesProvider, Flattr * */ public FeedItem(long id, String title, String link, Date pubDate, String paymentLink, long feedId, FlattrStatus flattrStatus, boolean hasChapters, FeedImage image, int state, - String itemIdentifier, boolean autoDownload) { + String itemIdentifier, long autoDownload) { this.id = id; this.title = title; this.link = link; @@ -162,7 +169,7 @@ public class FeedItem extends FeedComponent implements ShownotesProvider, Flattr FlattrStatus flattrStatus = new FlattrStatus(cursor.getLong(indexFlattrStatus)); int state = cursor.getInt(indexRead); String itemIdentifier = cursor.getString(indexItemIdentifier); - boolean autoDownload = cursor.getInt(indexAutoDownload) > 0; + long autoDownload = cursor.getLong(indexAutoDownload); FeedItem item = new FeedItem(id, title, link, pubDate, paymentLink, feedId, flattrStatus, hasChapters, null, state, itemIdentifier, autoDownload); @@ -449,18 +456,37 @@ public class FeedItem extends FeedComponent implements ShownotesProvider, Flattr } public void setAutoDownload(boolean autoDownload) { - this.autoDownload = autoDownload; + this.autoDownload = autoDownload ? 1 : 0; } public boolean getAutoDownload() { - return this.autoDownload; + return this.autoDownload > 0; + } + + public int getFailedAutoDownloadAttempts() { + if (autoDownload <= 1) { + return 0; + } + int failedAttempts = (int)(autoDownload % 10); + if (failedAttempts == 0) { + failedAttempts = 10; + } + return failedAttempts; } public boolean isAutoDownloadable() { - return this.hasMedia() && - false == this.getMedia().isPlaying() && - false == this.getMedia().isDownloaded() && - this.getAutoDownload(); + if (media == null || media.isPlaying() || media.isDownloaded() || autoDownload == 0) { + return false; + } + if (autoDownload == 1) { + return true; + } + int failedAttempts = getFailedAutoDownloadAttempts(); + double magicValue = 1.767; // 1.767^(10[=#maxNumAttempts]-1) = 168 hours / 7 days + int millisecondsInHour = 3600000; + long waitingTime = (long) (Math.pow(magicValue, failedAttempts - 1) * millisecondsInHour); + long grace = TimeUnit.MINUTES.toMillis(5); + return System.currentTimeMillis() > (autoDownload + waitingTime - grace); } /** diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java index 29c44207d..c3afff276 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java @@ -26,6 +26,7 @@ import org.xml.sax.SAXException; import java.io.File; import java.io.IOException; +import java.net.HttpURLConnection; import java.util.ArrayList; import java.util.Collections; import java.util.Date; @@ -196,11 +197,19 @@ public class DownloadService extends Service { saveDownloadStatus(status); handleFailedDownload(status, downloader.getDownloadRequest()); - // to make lists reload the failed item, we fake an item update if(type == FeedMedia.FEEDFILETYPE_FEEDMEDIA) { long id = status.getFeedfileId(); FeedMedia media = DBReader.getFeedMedia(id); - EventBus.getDefault().post(FeedItemEvent.updated(media.getItem())); + if(media == null || media.getItem() == null) { + return; + } + FeedItem item = media.getItem(); + if (status.getReason() == DownloadError.ERROR_HTTP_DATA_ERROR && + Integer.valueOf(status.getReasonDetailed()) == HttpURLConnection.HTTP_NOT_FOUND) { + DBWriter.saveFeedItemAutoDownloadFailed(item).get(); + } + // to make lists reload the failed item, we fake an item update + EventBus.getDefault().post(FeedItemEvent.updated(item)); } } } else { diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java index f74064cfc..d1d6bd750 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java @@ -977,13 +977,35 @@ public class DBWriter { * Sets the 'auto_download'-attribute of specific FeedItem. * * @param feedItem FeedItem. + * @param autoDownload true enables auto download, false disables it */ public static Future<?> setFeedItemAutoDownload(final FeedItem feedItem, final boolean autoDownload) { - Log.d(TAG, "FeedItem[id=" + feedItem.getId() + "] SET auto_download " + autoDownload); return dbExec.submit(() -> { final PodDBAdapter adapter = PodDBAdapter.getInstance(); adapter.open(); + adapter.setFeedItemAutoDownload(feedItem, autoDownload ? 1 : 0); + adapter.close(); + EventDistributor.getInstance().sendUnreadItemsUpdateBroadcast(); + }); + } + + public static Future<?> saveFeedItemAutoDownloadFailed(final FeedItem feedItem) { + return dbExec.submit(() -> { + int failedAttempts = feedItem.getFailedAutoDownloadAttempts() + 1; + Log.d(TAG, "failedAttempts: " + failedAttempts); + long autoDownload; + if(!feedItem.getAutoDownload() || failedAttempts >= 10) { + autoDownload = 0; // giving up, disable auto download + feedItem.setAutoDownload(false); + } else { + long now = System.currentTimeMillis(); + autoDownload = (now / 10) * 10 + failedAttempts; + Log.d(TAG, "now: " + now); + Log.d(TAG, "autoDownload: " + autoDownload); + } + final PodDBAdapter adapter = PodDBAdapter.getInstance(); + adapter.open(); adapter.setFeedItemAutoDownload(feedItem, autoDownload); adapter.close(); EventDistributor.getInstance().sendUnreadItemsUpdateBroadcast(); @@ -992,7 +1014,8 @@ public class DBWriter { /** * Sets the 'auto_download'-attribute of specific FeedItem. - * @param feed This feed's episodes will be processed. + * + * @param feed This feed's episodes will be processed. * @param autoDownload If true, auto download will be enabled for the feed's episodes. Else, */ public static Future<?> setFeedsItemsAutoDownload(final Feed feed, 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 629b73668..915ca14c7 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 @@ -794,7 +794,7 @@ public class PodDBAdapter { return status.getId(); } - public void setFeedItemAutoDownload(FeedItem feedItem, boolean autoDownload) { + public void setFeedItemAutoDownload(FeedItem feedItem, long autoDownload) { ContentValues values = new ContentValues(); values.put(KEY_AUTO_DOWNLOAD, autoDownload); db.update(TABLE_NAME_FEED_ITEMS, values, KEY_ID + "=?", diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSITunes.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSITunes.java index 6406295c1..9b9849c49 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSITunes.java +++ b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSITunes.java @@ -1,5 +1,7 @@ package de.danoeh.antennapod.core.syndication.namespace; +import android.text.TextUtils; + import org.xml.sax.Attributes; import java.util.concurrent.TimeUnit; @@ -36,8 +38,10 @@ public class NSITunes extends Namespace { } else { // this is the feed image // prefer to all other images - image.setOwner(state.getFeed()); - state.getFeed().setImage(image); + if(!TextUtils.isEmpty(image.getDownload_url())) { + image.setOwner(state.getFeed()); + state.getFeed().setImage(image); + } } } diff --git a/core/src/main/res/values/arrays.xml b/core/src/main/res/values/arrays.xml index 341a7e520..6e885c0bb 100644 --- a/core/src/main/res/values/arrays.xml +++ b/core/src/main/res/values/arrays.xml @@ -11,8 +11,9 @@ <item>0</item> <item>15</item> <item>30</item> - <item>45</item> <item>60</item> + <item>120</item> + <item>300</item> </string-array> diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml index 69acdcc4a..0371d99bc 100644 --- a/core/src/main/res/values/strings.xml +++ b/core/src/main/res/values/strings.xml @@ -75,7 +75,7 @@ <string name="length_prefix">Length:\u0020</string> <string name="size_prefix">Size:\u0020</string> <string name="processing_label">Processing</string> - <string name="loading_label">Loading...</string> + <string name="loading_label">Loading…</string> <string name="save_username_password_label">Save username and password</string> <string name="close_label">Close</string> <string name="retry_label">Retry</string> @@ -87,7 +87,7 @@ <string name="feed_auto_download_global">Global</string> <string name="feed_auto_download_always">Always</string> <string name="feed_auto_download_never">Never</string> - <string name="send_label">Send...</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 finishing</string> @@ -112,7 +112,7 @@ <string name="mark_all_seen_label">Mark all as seen</string> <string name="show_info_label">Show information</string> <string name="remove_feed_label">Remove Podcast</string> - <string name="share_label">Share...</string> + <string name="share_label">Share…</string> <string name="share_link_label">Share Link</string> <string name="share_link_with_position_label">Share Link with Position</string> <string name="share_feed_url_label">Share Feed URL</string> @@ -299,6 +299,8 @@ <string name="pref_autoUpdateIntervallOrTime_Disable">Disable</string> <string name="pref_autoUpdateIntervallOrTime_Interval">Set Interval</string> <string name="pref_autoUpdateIntervallOrTime_TimeOfDay">Set Time of Day</string> + <string name="pref_autoUpdateIntervallOrTime_every">every %1$s</string> + <string name="pref_autoUpdateIntervallOrTime_at">at %1$s</string> <string name="pref_downloadMediaOnWifiOnly_sum">Download media files only over WiFi</string> <string name="pref_followQueue_title">Continuous Playback</string> <string name="pref_downloadMediaOnWifiOnly_title">WiFi media download</string> @@ -374,6 +376,7 @@ <string name="experimental_pref">Experimental</string> <string name="pref_sonic_title">Sonic media player</string> <string name="pref_sonic_message">Use built-in sonic media player as a replacement for Prestissimo</string> + <string name="pref_current_value">Current value: %1$s</string> <!-- Auto-Flattr dialog --> <string name="auto_flattr_enable">Enable automatic flattring</string> @@ -402,11 +405,11 @@ <string name="opml_import_error_dir_empty">The import directory is empty.</string> <string name="select_all_label">Select all</string> <string name="deselect_all_label">Deselect all</string> - <string name="select_options_label">Select ...</string> + <string name="select_options_label">Select…</string> <string name="choose_file_from_filesystem">From local filesystem</string> <string name="choose_file_from_external_application">Use external application</string> <string name="opml_export_label">OPML export</string> - <string name="exporting_label">Exporting...</string> + <string name="exporting_label">Exporting…</string> <string name="export_error_label">Export error</string> <string name="opml_export_success_title">OPML Export successful.</string> <string name="opml_export_success_sum">The .opml file was written to:\u0020</string> @@ -494,7 +497,7 @@ <!-- Online feed view --> <string name="subscribe_label">Subscribe</string> <string name="subscribed_label">Subscribed</string> - <string name="downloading_label">Downloading...</string> + <string name="downloading_label">Downloading…</string> <!-- Content descriptions for image buttons --> <string name="show_chapters_label">Show chapters</string> @@ -529,7 +532,7 @@ <string name="sp_apps_importing_feeds_msg">Importing subscriptions from single-purpose apps…</string> <string name="search_itunes_label">Search iTunes</string> - <string name="select_label"><b>Select ...</b></string> + <string name="select_label"><b>Select…</b></string> <string name="all_label">All</string> <string name="selected_all_label">Selected all Episodes</string> <string name="none_label">None</string> @@ -542,7 +545,7 @@ <string name="selected_downloaded_label">Selected downloaded Episodes</string> <string name="not_downloaded_label">Not downloaded</string> <string name="selected_not_downloaded_label">Selected not downloaded Episodes</string> - <string name="sort_title"><b>Sort by ...</b></string> + <string name="sort_title"><b>Sort by…</b></string> <string name="sort_title_a_z">Title (A \u2192 Z)</string> <string name="sort_title_z_a">Title (Z \u2192 A)</string> <string name="sort_date_new_old">Date (New \u2192 Old)</string> |