summaryrefslogtreecommitdiff
path: root/core/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'core/src/main')
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java11
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/HttpDownloader.java156
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java28
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java39
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/atom/NSAtom.java8
5 files changed, 167 insertions, 75 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 7c01b7fef..d5a1007af 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
@@ -337,7 +337,10 @@ public class UserPreferences {
}
-
+ /*
+ * Returns update interval in milliseconds; value 0 means that auto update is disabled
+ * or feeds are updated at a certain time of day
+ */
public static long getUpdateInterval() {
String updateInterval = prefs.getString(PREF_UPDATE_INTERVAL, "0");
if(!updateInterval.contains(":")) {
@@ -759,12 +762,12 @@ public class UserPreferences {
if (timeOfDay.length == 2) {
restartUpdateTimeOfDayAlarm(timeOfDay[0], timeOfDay[1]);
} else {
- long hours = getUpdateInterval();
- long startTrigger = hours;
+ long milliseconds = getUpdateInterval();
+ long startTrigger = milliseconds;
if (now) {
startTrigger = TimeUnit.SECONDS.toMillis(10);
}
- restartUpdateIntervalAlarm(startTrigger, hours);
+ restartUpdateIntervalAlarm(startTrigger, milliseconds);
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/HttpDownloader.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/HttpDownloader.java
index 869cb8f7a..99456b756 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/HttpDownloader.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/HttpDownloader.java
@@ -2,13 +2,21 @@ package de.danoeh.antennapod.core.service.download;
import android.text.TextUtils;
import android.util.Log;
-
+import com.squareup.okhttp.Interceptor;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Protocol;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.Response;
import com.squareup.okhttp.ResponseBody;
-
+import de.danoeh.antennapod.core.ClientConfig;
+import de.danoeh.antennapod.core.R;
+import de.danoeh.antennapod.core.feed.FeedImage;
+import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.core.util.DateUtils;
+import de.danoeh.antennapod.core.util.DownloadError;
+import de.danoeh.antennapod.core.util.StorageUtils;
+import de.danoeh.antennapod.core.util.URIUtil;
+import okio.ByteString;
import org.apache.commons.io.IOUtils;
import java.io.BufferedInputStream;
@@ -24,16 +32,6 @@ import java.net.UnknownHostException;
import java.util.Collections;
import java.util.Date;
-import de.danoeh.antennapod.core.ClientConfig;
-import de.danoeh.antennapod.core.R;
-import de.danoeh.antennapod.core.feed.FeedImage;
-import de.danoeh.antennapod.core.feed.FeedMedia;
-import de.danoeh.antennapod.core.util.DateUtils;
-import de.danoeh.antennapod.core.util.DownloadError;
-import de.danoeh.antennapod.core.util.StorageUtils;
-import de.danoeh.antennapod.core.util.URIUtil;
-import okio.ByteString;
-
public class HttpDownloader extends Downloader {
private static final String TAG = "HttpDownloader";
@@ -59,7 +57,8 @@ public class HttpDownloader extends Downloader {
}
}
- OkHttpClient httpClient = AntennapodHttpClient.getHttpClient();
+ OkHttpClient httpClient = AntennapodHttpClient.newHttpClient();
+ httpClient.interceptors().add(new BasicAuthorizationInterceptor(request));
RandomAccessFile out = null;
InputStream connection;
ResponseBody responseBody = null;
@@ -68,16 +67,16 @@ public class HttpDownloader extends Downloader {
final URI uri = URIUtil.getURIFromRequestUrl(request.getSource());
Request.Builder httpReq = new Request.Builder().url(uri.toURL())
.header("User-Agent", ClientConfig.USER_AGENT);
- if(request.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
+ if (request.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
// set header explicitly so that okhttp doesn't do transparent gzip
Log.d(TAG, "addHeader(\"Accept-Encoding\", \"identity\")");
httpReq.addHeader("Accept-Encoding", "identity");
}
- if(!TextUtils.isEmpty(request.getLastModified())) {
+ if (!TextUtils.isEmpty(request.getLastModified())) {
String lastModified = request.getLastModified();
Date lastModifiedDate = DateUtils.parse(lastModified);
- if(lastModifiedDate != null) {
+ if (lastModifiedDate != null) {
long threeDaysAgo = System.currentTimeMillis() - 1000 * 60 * 60 * 24 * 3;
if (lastModifiedDate.getTime() > threeDaysAgo) {
Log.d(TAG, "addHeader(\"If-Modified-Since\", \"" + lastModified + "\")");
@@ -89,19 +88,6 @@ public class HttpDownloader extends Downloader {
}
}
- // add authentication information
- String userInfo = uri.getUserInfo();
- if (userInfo != null) {
- String[] parts = userInfo.split(":");
- if (parts.length == 2) {
- String credentials = encodeCredentials(parts[0], parts[1], "ISO-8859-1");
- httpReq.header("Authorization", credentials);
- }
- } else if (!TextUtils.isEmpty(request.getUsername()) && request.getPassword() != null) {
- String credentials = encodeCredentials(request.getUsername(), request.getPassword(),
- "ISO-8859-1");
- httpReq.header("Authorization", credentials);
- }
// add range header if necessary
if (fileExists) {
@@ -111,15 +97,15 @@ public class HttpDownloader extends Downloader {
}
Response response;
+
try {
response = httpClient.newCall(httpReq.build()).execute();
- } catch(IOException e) {
+ } catch (IOException e) {
Log.e(TAG, e.toString());
- if(e.getMessage().contains("PROTOCOL_ERROR")) {
+ if (e.getMessage().contains("PROTOCOL_ERROR")) {
httpClient.setProtocols(Collections.singletonList(Protocol.HTTP_1_1));
response = httpClient.newCall(httpReq.build()).execute();
- }
- else {
+ } else {
throw e;
}
}
@@ -127,34 +113,13 @@ public class HttpDownloader extends Downloader {
responseBody = response.body();
String contentEncodingHeader = response.header("Content-Encoding");
boolean isGzip = false;
- if(!TextUtils.isEmpty(contentEncodingHeader)) {
+ if (!TextUtils.isEmpty(contentEncodingHeader)) {
isGzip = TextUtils.equals(contentEncodingHeader.toLowerCase(), "gzip");
}
Log.d(TAG, "Response code is " + response.code());
- if(!response.isSuccessful() && response.code() == HttpURLConnection.HTTP_UNAUTHORIZED) {
- Log.d(TAG, "Authorization failed, re-trying with UTF-8 encoding");
- if (userInfo != null) {
- String[] parts = userInfo.split(":");
- if (parts.length == 2) {
- String credentials = encodeCredentials(parts[0], parts[1], "UTF-8");
- httpReq.header("Authorization", credentials);
- }
- } else if (!TextUtils.isEmpty(request.getUsername()) && request.getPassword() != null) {
- String credentials = encodeCredentials(request.getUsername(), request.getPassword(),
- "UTF-8");
- httpReq.header("Authorization", credentials);
- }
- response = httpClient.newCall(httpReq.build()).execute();
- responseBody = response.body();
- contentEncodingHeader = response.header("Content-Encoding");
- if(!TextUtils.isEmpty(contentEncodingHeader)) {
- isGzip = TextUtils.equals(contentEncodingHeader.toLowerCase(), "gzip");
- }
- }
-
- if(!response.isSuccessful() && response.code() == HttpURLConnection.HTTP_NOT_MODIFIED) {
+ if (!response.isSuccessful() && response.code() == HttpURLConnection.HTTP_NOT_MODIFIED) {
Log.d(TAG, "Feed '" + request.getSource() + "' not modified since last update, Download canceled");
onCancelled();
return;
@@ -166,7 +131,7 @@ public class HttpDownloader extends Downloader {
if (response.code() == HttpURLConnection.HTTP_UNAUTHORIZED) {
error = DownloadError.ERROR_UNAUTHORIZED;
details = String.valueOf(response.code());
- } else if(response.code() == HttpURLConnection.HTTP_FORBIDDEN) {
+ } else if (response.code() == HttpURLConnection.HTTP_FORBIDDEN) {
error = DownloadError.ERROR_FORBIDDEN;
details = String.valueOf(response.code());
} else {
@@ -184,18 +149,19 @@ public class HttpDownloader extends Downloader {
// fail with a file type error when the content type is text and
// the reported content length is less than 100kb (or no length is given)
- if(request.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
+ if (request.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
int contentLength = -1;
String contentLen = response.header("Content-Length");
- if(contentLen != null) {
+ if (contentLen != null) {
try {
contentLength = Integer.parseInt(contentLen);
- } catch(NumberFormatException e) {}
+ } catch (NumberFormatException e) {
+ }
}
Log.d(TAG, "content length: " + contentLength);
String contentType = response.header("Content-Type");
Log.d(TAG, "content type: " + contentType);
- if(contentType != null && contentType.startsWith("text/") &&
+ if (contentType != null && contentType.startsWith("text/") &&
contentLength < 100 * 1024) {
onFail(DownloadError.ERROR_FILE_TYPE, null);
return;
@@ -244,10 +210,10 @@ public class HttpDownloader extends Downloader {
while (!cancelled && (count = connection.read(buffer)) != -1) {
out.write(buffer, 0, count);
request.setSoFar(request.getSoFar() + count);
- int progressPercent = (int)(100.0 * request.getSoFar() / request.getSize());
+ int progressPercent = (int) (100.0 * request.getSoFar() / request.getSize());
request.setProgressPercent(progressPercent);
}
- } catch(IOException e) {
+ } catch (IOException e) {
Log.e(TAG, Log.getStackTraceString(e));
}
if (cancelled) {
@@ -260,12 +226,12 @@ public class HttpDownloader extends Downloader {
onFail(DownloadError.ERROR_IO_ERROR, "Download completed but size: " +
request.getSoFar() + " does not equal expected size " + request.getSize());
return;
- } else if(request.getSize() > 0 && request.getSoFar() == 0){
+ } else if (request.getSize() > 0 && request.getSoFar() == 0) {
onFail(DownloadError.ERROR_IO_ERROR, "Download completed, but nothing was read");
return;
}
String lastModified = response.header("Last-Modified");
- if(lastModified != null) {
+ if (lastModified != null) {
request.setLastModified(lastModified);
} else {
request.setLastModified(response.header("ETag"));
@@ -325,7 +291,7 @@ public class HttpDownloader extends Downloader {
if (dest.exists()) {
boolean rc = dest.delete();
Log.d(TAG, "Deleted file " + dest.getName() + "; Result: "
- + rc);
+ + rc);
} else {
Log.d(TAG, "cleanup() didn't delete file: does not exist.");
}
@@ -343,4 +309,62 @@ public class HttpDownloader extends Downloader {
}
}
+ private class BasicAuthorizationInterceptor implements Interceptor {
+
+ private DownloadRequest downloadRequest;
+
+ public BasicAuthorizationInterceptor(DownloadRequest downloadRequest) {
+ this.downloadRequest = downloadRequest;
+ }
+
+ @Override
+ public Response intercept(Chain chain) throws IOException {
+ Request request = chain.request();
+ String userInfo = URIUtil.getURIFromRequestUrl(downloadRequest.getSource()).getUserInfo();
+
+ Response response = chain.proceed(request);
+
+ if (response.code() != HttpURLConnection.HTTP_UNAUTHORIZED) {
+ return response;
+ }
+
+ Request.Builder newRequest = request.newBuilder();
+
+ Log.d(TAG, "Authorization failed, re-trying with ISO-8859-1 encoded credentials");
+ if (userInfo != null) {
+ String[] parts = userInfo.split(":");
+ if (parts.length == 2) {
+ String credentials = encodeCredentials(parts[0], parts[1], "ISO-8859-1");
+ newRequest.header("Authorization", credentials);
+ }
+ } else if (!TextUtils.isEmpty(downloadRequest.getUsername()) && downloadRequest.getPassword() != null) {
+ String credentials = encodeCredentials(downloadRequest.getUsername(), downloadRequest.getPassword(),
+ "ISO-8859-1");
+ newRequest.header("Authorization", credentials);
+ }
+
+ response = chain.proceed(newRequest.build());
+
+ if (response.code() != HttpURLConnection.HTTP_UNAUTHORIZED) {
+ return response;
+ }
+
+ Log.d(TAG, "Authorization failed, re-trying with UTF-8 encoded credentials");
+ if (userInfo != null) {
+ String[] parts = userInfo.split(":");
+ if (parts.length == 2) {
+ String credentials = encodeCredentials(parts[0], parts[1], "UTF-8");
+ newRequest.header("Authorization", credentials);
+ }
+ } else if (!TextUtils.isEmpty(downloadRequest.getUsername()) && downloadRequest.getPassword() != null) {
+ String credentials = encodeCredentials(downloadRequest.getUsername(), downloadRequest.getPassword(),
+ "UTF-8");
+ newRequest.header("Authorization", credentials);
+ }
+
+ return chain.proceed(newRequest.build());
+ }
+
+ }
+
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java
index 15656de3b..e67dc9d0a 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java
@@ -1004,13 +1004,37 @@ public class PlaybackService extends MediaBrowserServiceCompat {
state = PlaybackStateCompat.STATE_NONE;
}
sessionState.setState(state, mediaPlayer.getPosition(), mediaPlayer.getPlaybackSpeed());
- sessionState.setActions(PlaybackStateCompat.ACTION_PLAY_PAUSE
+ long capabilities = PlaybackStateCompat.ACTION_PLAY_PAUSE
| PlaybackStateCompat.ACTION_REWIND
| PlaybackStateCompat.ACTION_FAST_FORWARD
- | PlaybackStateCompat.ACTION_SKIP_TO_NEXT);
+ | PlaybackStateCompat.ACTION_SKIP_TO_NEXT;
+
+ if (useSkipToPreviousForRewindInLockscreen()) {
+ // Workaround to fool Android so that Lockscreen will expose a skip-to-previous button,
+ // which will be used for rewind.
+ // The workaround is used for pre Lollipop (Androidv5) devices.
+ // For Androidv5+, lockscreen widges are really notifications (compact),
+ // with an independent codepath
+ //
+ // @see #sessionCallback in the backing callback, skipToPrevious implementation
+ // is actually the same as rewind. So no new inconsistency is created.
+ // @see #setupNotification() for the method to create Androidv5+ lockscreen UI
+ // with notification (compact)
+ capabilities = capabilities | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS;
+ }
+
+ sessionState.setActions(capabilities);
mediaSession.setPlaybackState(sessionState.build());
}
+ private static boolean useSkipToPreviousForRewindInLockscreen() {
+ // showRewindOnCompactNotification() corresponds to the "Set Lockscreen Buttons"
+ // Settings in UI.
+ // Hence, from user perspective, he/she is setting the buttons for Lockscreen
+ return ( UserPreferences.showRewindOnCompactNotification() &&
+ (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) );
+ }
+
/**
* Used by updateMediaSessionMetadata to load notification data in another thread.
*/
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java
index d17b815ff..4d442aac2 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java
@@ -2,6 +2,7 @@ package de.danoeh.antennapod.core.storage;
import android.content.Context;
import android.content.Intent;
+import android.content.SharedPreferences;
import android.database.Cursor;
import android.util.Log;
@@ -16,7 +17,7 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
-import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import de.danoeh.antennapod.core.ClientConfig;
@@ -27,21 +28,29 @@ import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.FeedPreferences;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.GpodnetSyncService;
import de.danoeh.antennapod.core.service.download.DownloadStatus;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
+import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.DownloadError;
import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.core.util.comparator.FeedItemPubdateComparator;
import de.danoeh.antennapod.core.util.exception.MediaFileNotFoundException;
import de.danoeh.antennapod.core.util.flattr.FlattrUtils;
+import static android.content.Context.MODE_PRIVATE;
+import static android.provider.Contacts.SettingsColumns.KEY;
+
/**
* Provides methods for doing common tasks that use DBReader and DBWriter.
*/
public final class DBTasks {
private static final String TAG = "DBTasks";
+ public static final String PREF_NAME = "dbtasks";
+ private static final String PREF_LAST_REFRESH = "last_refresh";
+
/**
* Executor service used by the autodownloadUndownloadedEpisodes method.
*/
@@ -162,6 +171,9 @@ public final class DBTasks {
}
isRefreshing.set(false);
+ SharedPreferences prefs = context.getSharedPreferences(PREF_NAME, MODE_PRIVATE);
+ prefs.edit().putLong(PREF_LAST_REFRESH, System.currentTimeMillis()).apply();
+
if (FlattrUtils.hasToken()) {
Log.d(TAG, "Flattring all pending things.");
new FlattrClickWorker(context).executeAsync(); // flattr pending things
@@ -313,6 +325,31 @@ public final class DBTasks {
DownloadRequester.getInstance().downloadFeed(context, f, loadAllPages, force);
}
+ /*
+ * Checks if the app should refresh all feeds, i.e. if the last auto refresh failed.
+ *
+ * The feeds are only refreshed if an update interval or time of day is set and the last
+ * (successful) refresh was before the last interval or more than a day ago, respectively.
+ */
+ public static void checkShouldRefreshFeeds(Context context) {
+ long interval = 0;
+ if(UserPreferences.getUpdateInterval() > 0) {
+ interval = UserPreferences.getUpdateInterval();
+ } else if(UserPreferences.getUpdateTimeOfDay().length > 0){
+ interval = TimeUnit.DAYS.toMillis(1);
+ }
+ if(interval == 0) { // auto refresh is disabled
+ return;
+ }
+ SharedPreferences prefs = context.getSharedPreferences(PREF_NAME, MODE_PRIVATE);
+ long lastRefresh = prefs.getLong(PREF_LAST_REFRESH, 0);
+ Log.d(TAG, "last refresh: " + Converter.getDurationStringLocalized(context,
+ System.currentTimeMillis() - lastRefresh) + " ago");
+ if(lastRefresh <= System.currentTimeMillis() - interval) {
+ DBTasks.refreshAllFeeds(context, null);
+ }
+ }
+
/**
* Notifies the database about a missing FeedMedia file. This method will correct the FeedMedia object's values in the
* DB and send a FeedUpdateBroadcast.
diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/atom/NSAtom.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/atom/NSAtom.java
index 52b05aa98..ec7ebe1ac 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/atom/NSAtom.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/atom/NSAtom.java
@@ -30,6 +30,7 @@ public class NSAtom extends Namespace {
private static final String AUTHOR = "author";
private static final String AUTHOR_NAME = "name";
private static final String CONTENT = "content";
+ private static final String SUMMARY = "summary";
private static final String IMAGE_LOGO = "logo";
private static final String IMAGE_ICON = "icon";
private static final String SUBTITLE = "subtitle";
@@ -59,8 +60,8 @@ public class NSAtom extends Namespace {
/**
* Regexp to test whether an Element is a Text Element.
*/
- private static final String isText = TITLE + "|" + CONTENT + "|" + "|"
- + SUBTITLE;
+ private static final String isText = TITLE + "|" + CONTENT + "|"
+ + SUBTITLE + "|" + SUMMARY;
public static final String isFeed = FEED + "|" + NSRSS20.CHANNEL;
public static final String isFeedItem = ENTRY + "|" + NSRSS20.ITEM;
@@ -191,6 +192,9 @@ public class NSAtom extends Namespace {
} else if (CONTENT.equals(top) && ENTRY.equals(second) && textElement != null &&
state.getCurrentItem() != null) {
state.getCurrentItem().setDescription(textElement.getProcessedContent());
+ } else if (SUMMARY.equals(top) && ENTRY.equals(second) && textElement != null &&
+ state.getCurrentItem() != null && state.getCurrentItem().getDescription() == null) {
+ state.getCurrentItem().setDescription(textElement.getProcessedContent());
} else if (UPDATED.equals(top) && ENTRY.equals(second) && state.getCurrentItem() != null &&
state.getCurrentItem().getPubDate() == null) {
state.getCurrentItem().setPubDate(DateUtils.parse(content));