summaryrefslogtreecommitdiff
path: root/core/src/main/java/de/danoeh
diff options
context:
space:
mode:
authorByteHamster <info@bytehamster.com>2021-01-22 11:07:40 +0100
committerByteHamster <info@bytehamster.com>2021-01-22 11:07:40 +0100
commitcdf59a1c8e99720fd737fcbcc1f5ac1eb5252890 (patch)
tree119819aaec911b7510dc3f3bb1a5a4a6ddf82c1c /core/src/main/java/de/danoeh
parent85b897c7d778ae70d837ae0c2a9e7b4252ea8945 (diff)
parent41d23fa671a3ad7d417f4791c63d0f25634d725f (diff)
downloadAntennaPod-cdf59a1c8e99720fd737fcbcc1f5ac1eb5252890.zip
Merge branch 'develop' into fix_episodes_list_item_loading_b
Diffstat (limited to 'core/src/main/java/de/danoeh')
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/DBTasksCallbacks.java20
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/DownloadServiceCallbacks.java10
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/PlaybackServiceCallbacks.java7
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/asynctask/FeedRemover.java61
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/backup/OpmlBackupAgent.java10
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/FeedItemFilter.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/FeedMedia.java16
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/LocalFeedUpdater.java128
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/util/PlaybackSpeedUtils.java6
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideModule.java1
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/glide/ChapterImageModelLoader.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/preferences/PlaybackPreferences.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java30
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/receiver/PlayerWidget.java6
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/PlayerWidgetJobService.java35
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java26
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadServiceNotification.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/HttpDownloader.java5
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FailedDownloadHandler.java1
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/ExoPlayerWrapper.java20
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java12
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java55
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceNotificationBuilder.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/ssl/BackportCaCerts.java32
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/ssl/BackportTrustManager.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/APDownloadAlgorithm.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java8
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java14
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/FeedSearcher.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java41
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/sync/SyncService.java8
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/sync/gpoddernet/GpodnetService.java132
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/DateUtils.java22
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/DownloadError.java8
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/FileNameGenerator.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/LangUtils.java124
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/ShareUtils.java123
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/URLChecker.java1
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/comparator/FeedItemPubdateComparator.java6
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/gui/NotificationUtils.java81
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/playback/AudioPlayer.java9
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/playback/IPlayer.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java7
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackServiceStarter.java1
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/playback/VideoPlayer.java6
46 files changed, 598 insertions, 500 deletions
diff --git a/core/src/main/java/de/danoeh/antennapod/core/DBTasksCallbacks.java b/core/src/main/java/de/danoeh/antennapod/core/DBTasksCallbacks.java
deleted file mode 100644
index 11a6b2c9f..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/DBTasksCallbacks.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package de.danoeh.antennapod.core;
-
-import de.danoeh.antennapod.core.storage.AutomaticDownloadAlgorithm;
-import de.danoeh.antennapod.core.storage.EpisodeCleanupAlgorithm;
-
-/**
- * Callbacks for the DBTasks class of the storage module.
- */
-public interface DBTasksCallbacks {
-
- /**
- * Returns the client's implementation of the AutomaticDownloadAlgorithm interface.
- */
- AutomaticDownloadAlgorithm getAutomaticDownloadAlgorithm();
-
- /**
- * Returns the client's implementation of the EpisodeCacheCleanupAlgorithm interface.
- */
- EpisodeCleanupAlgorithm getEpisodeCacheCleanupAlgorithm();
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/DownloadServiceCallbacks.java b/core/src/main/java/de/danoeh/antennapod/core/DownloadServiceCallbacks.java
index ad3fb8d42..ae9b47629 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/DownloadServiceCallbacks.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/DownloadServiceCallbacks.java
@@ -37,7 +37,7 @@ public interface DownloadServiceCallbacks {
* <p/>
* The PendingIntent takes users to an activity where they can look at all successful and failed downloads.
*
- * @return A non-null PendingIntent for the notification or null if shouldCreateReport()==false
+ * @return A non-null PendingIntent for the notification
*/
PendingIntent getReportNotificationContentIntent(Context context);
@@ -47,14 +47,8 @@ public interface DownloadServiceCallbacks {
* <p/>
* The PendingIntent takes users to an activity where they can look at their episode queue.
*
- * @return A non-null PendingIntent for the notification or null if shouldCreateReport()==false
+ * @return A non-null PendingIntent for the notification
*/
PendingIntent getAutoDownloadReportNotificationContentIntent(Context context);
-
- /**
- * Returns true if the DownloadService should create a report that shows the number of failed
- * downloads when the service shuts down.
- */
- boolean shouldCreateReport();
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/PlaybackServiceCallbacks.java b/core/src/main/java/de/danoeh/antennapod/core/PlaybackServiceCallbacks.java
index 194ee65ae..3dcaac4dc 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/PlaybackServiceCallbacks.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/PlaybackServiceCallbacks.java
@@ -19,11 +19,4 @@ public interface PlaybackServiceCallbacks {
* @return A non-null activity intent.
*/
Intent getPlayerActivityIntent(Context context, MediaType mediaType, boolean remotePlayback);
-
- /**
- * Returns true if the PlaybackService should load new episodes from the queue when playback ends
- * and false if the PlaybackService should ignore the queue and load no more episodes when playback
- * finishes.
- */
- boolean useQueue();
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/asynctask/FeedRemover.java b/core/src/main/java/de/danoeh/antennapod/core/asynctask/FeedRemover.java
deleted file mode 100644
index 4504b2e7f..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/asynctask/FeedRemover.java
+++ /dev/null
@@ -1,61 +0,0 @@
-package de.danoeh.antennapod.core.asynctask;
-
-import android.app.ProgressDialog;
-import android.content.Context;
-import android.os.AsyncTask;
-
-import java.util.concurrent.ExecutionException;
-
-import de.danoeh.antennapod.core.R;
-import de.danoeh.antennapod.core.feed.Feed;
-import de.danoeh.antennapod.core.service.playback.PlaybackService;
-import de.danoeh.antennapod.core.storage.DBWriter;
-import de.danoeh.antennapod.core.util.IntentUtils;
-
-/** Removes a feed in the background. */
-public class FeedRemover extends AsyncTask<Void, Void, Void> {
- private final Context context;
- private ProgressDialog dialog;
- private final Feed feed;
- public boolean skipOnCompletion = false;
-
- public FeedRemover(Context context, Feed feed) {
- super();
- this.context = context;
- this.feed = feed;
- }
-
- @Override
- protected Void doInBackground(Void... params) {
- try {
- DBWriter.deleteFeed(context, feed.getId()).get();
- } catch (InterruptedException | ExecutionException e) {
- e.printStackTrace();
- }
- return null;
- }
-
- @Override
- protected void onPostExecute(Void result) {
- if(dialog != null && dialog.isShowing()) {
- dialog.dismiss();
- }
- if(skipOnCompletion) {
- IntentUtils.sendLocalBroadcast(context, PlaybackService.ACTION_SKIP_CURRENT_EPISODE);
- }
- }
-
- @Override
- protected void onPreExecute() {
- dialog = new ProgressDialog(context);
- dialog.setMessage(context.getString(R.string.feed_remover_msg));
- dialog.setIndeterminate(true);
- dialog.setCancelable(false);
- dialog.show();
- }
-
- public void executeAsync() {
- executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
- }
-
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/backup/OpmlBackupAgent.java b/core/src/main/java/de/danoeh/antennapod/core/backup/OpmlBackupAgent.java
index 4c11d0489..c05e2e9f1 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/backup/OpmlBackupAgent.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/backup/OpmlBackupAgent.java
@@ -20,6 +20,7 @@ import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import java.math.BigInteger;
+import java.nio.charset.Charset;
import java.security.DigestInputStream;
import java.security.DigestOutputStream;
import java.security.MessageDigest;
@@ -34,7 +35,6 @@ import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DownloadRequestException;
import de.danoeh.antennapod.core.storage.DownloadRequester;
-import de.danoeh.antennapod.core.util.LangUtils;
public class OpmlBackupAgent extends BackupAgentHelper {
private static final String OPML_BACKUP_KEY = "opml";
@@ -73,9 +73,9 @@ public class OpmlBackupAgent extends BackupAgentHelper {
try {
digester = MessageDigest.getInstance("MD5");
writer = new OutputStreamWriter(new DigestOutputStream(byteStream, digester),
- LangUtils.UTF_8);
+ Charset.forName("UTF-8"));
} catch (NoSuchAlgorithmException e) {
- writer = new OutputStreamWriter(byteStream, LangUtils.UTF_8);
+ writer = new OutputStreamWriter(byteStream, Charset.forName("UTF-8"));
}
try {
@@ -138,9 +138,9 @@ public class OpmlBackupAgent extends BackupAgentHelper {
try {
digester = MessageDigest.getInstance("MD5");
reader = new InputStreamReader(new DigestInputStream(data, digester),
- LangUtils.UTF_8);
+ Charset.forName("UTF-8"));
} catch (NoSuchAlgorithmException e) {
- reader = new InputStreamReader(data, LangUtils.UTF_8);
+ reader = new InputStreamReader(data, Charset.forName("UTF-8"));
}
try {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItemFilter.java b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItemFilter.java
index b9d79715a..16ab5a171 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItemFilter.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItemFilter.java
@@ -1,10 +1,8 @@
package de.danoeh.antennapod.core.feed;
import android.text.TextUtils;
-import android.util.Log;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
import de.danoeh.antennapod.core.storage.DBReader;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedMedia.java b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedMedia.java
index 88945b930..4857e899d 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedMedia.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedMedia.java
@@ -5,6 +5,7 @@ import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.database.Cursor;
import android.media.MediaMetadataRetriever;
+import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.Nullable;
@@ -165,13 +166,20 @@ public class FeedMedia extends FeedFile implements Playable {
*/
public MediaBrowserCompat.MediaItem getMediaItem() {
Playable p = this;
- MediaDescriptionCompat description = new MediaDescriptionCompat.Builder()
+ MediaDescriptionCompat.Builder builder = new MediaDescriptionCompat.Builder()
.setMediaId(String.valueOf(id))
.setTitle(p.getEpisodeTitle())
.setDescription(p.getFeedTitle())
- .setSubtitle(p.getFeedTitle())
- .build();
- return new MediaBrowserCompat.MediaItem(description, MediaBrowserCompat.MediaItem.FLAG_PLAYABLE);
+ .setSubtitle(p.getFeedTitle());
+ if (item != null) {
+ // getImageLocation() also loads embedded images, which we can not send to external devices
+ if (item.getImageUrl() != null) {
+ builder.setIconUri(Uri.parse(item.getImageUrl()));
+ } else if (item.getFeed() != null && item.getFeed().getImageLocation() != null) {
+ builder.setIconUri(Uri.parse(item.getFeed().getImageLocation()));
+ }
+ }
+ return new MediaBrowserCompat.MediaItem(builder.build(), MediaBrowserCompat.MediaItem.FLAG_PLAYABLE);
}
/**
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/LocalFeedUpdater.java b/core/src/main/java/de/danoeh/antennapod/core/feed/LocalFeedUpdater.java
index 971808eb4..d0e15d591 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/LocalFeedUpdater.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/LocalFeedUpdater.java
@@ -6,14 +6,13 @@ import android.media.MediaMetadataRetriever;
import android.net.Uri;
import android.text.TextUtils;
+import androidx.annotation.NonNull;
import androidx.documentfile.provider.DocumentFile;
-import org.apache.commons.lang3.StringUtils;
-
+import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
@@ -22,7 +21,6 @@ import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.UUID;
-import java.util.concurrent.ExecutionException;
import de.danoeh.antennapod.core.R;
import de.danoeh.antennapod.core.service.download.DownloadStatus;
@@ -34,17 +32,31 @@ import de.danoeh.antennapod.core.util.DownloadError;
public class LocalFeedUpdater {
+ static final String[] PREFERRED_FEED_IMAGE_FILENAMES = { "folder.jpg", "Folder.jpg", "folder.png", "Folder.png" };
+
public static void updateFeed(Feed feed, Context context) {
+ try {
+ tryUpdateFeed(feed, context);
+
+ if (mustReportDownloadSuccessful(feed)) {
+ reportSuccess(feed);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ reportError(feed, e.getMessage());
+ }
+ }
+
+ private static void tryUpdateFeed(Feed feed, Context context) throws IOException {
String uriString = feed.getDownload_url().replace(Feed.PREFIX_LOCAL_FOLDER, "");
DocumentFile documentFolder = DocumentFile.fromTreeUri(context, Uri.parse(uriString));
if (documentFolder == null) {
- reportError(feed, "Unable to retrieve document tree."
+ throw new IOException("Unable to retrieve document tree. "
+ "Try re-connecting the folder on the podcast info page.");
- return;
}
if (!documentFolder.exists() || !documentFolder.canRead()) {
- reportError(feed, "Cannot read local directory. Try re-connecting the folder on the podcast info page.");
- return;
+ throw new IOException("Cannot read local directory. "
+ + "Try re-connecting the folder on the podcast info page.");
}
if (feed.getItems() == null) {
@@ -85,37 +97,43 @@ public class LocalFeedUpdater {
}
}
- List<String> iconLocations = Arrays.asList("folder.jpg", "Folder.jpg", "folder.png", "Folder.png");
- for (String iconLocation : iconLocations) {
- DocumentFile image = documentFolder.findFile(iconLocation);
- if (image != null) {
- feed.setImageUrl(image.getUri().toString());
- break;
- }
- }
- if (StringUtils.isBlank(feed.getImageUrl())) {
- // set default feed image
- feed.setImageUrl(getDefaultIconUrl(context));
- }
- if (feed.getPreferences().getAutoDownload()) {
- feed.getPreferences().setAutoDownload(false);
- feed.getPreferences().setAutoDeleteAction(FeedPreferences.AutoDeleteAction.NO);
- try {
- DBWriter.setFeedPreferences(feed.getPreferences()).get();
- } catch (ExecutionException | InterruptedException e) {
- e.printStackTrace();
- }
- }
+ feed.setImageUrl(getImageUrl(context, documentFolder));
+
+ feed.getPreferences().setAutoDownload(false);
+ feed.getPreferences().setAutoDeleteAction(FeedPreferences.AutoDeleteAction.NO);
+ feed.setDescription(context.getString(R.string.local_feed_description));
+ feed.setAuthor(context.getString(R.string.local_folder));
// update items, delete items without existing file;
// only delete items if the folder contains at least one element to avoid accidentally
// deleting played state or position in case the folder is temporarily unavailable.
boolean removeUnlistedItems = (newItems.size() >= 1);
DBTasks.updateFeed(context, feed, removeUnlistedItems);
+ }
- if (mustReportDownloadSuccessful(feed)) {
- reportSuccess(feed);
+ /**
+ * Returns the image URL for the local feed.
+ */
+ @NonNull
+ static String getImageUrl(@NonNull Context context, @NonNull DocumentFile documentFolder) {
+ // look for special file names
+ for (String iconLocation : PREFERRED_FEED_IMAGE_FILENAMES) {
+ DocumentFile image = documentFolder.findFile(iconLocation);
+ if (image != null) {
+ return image.getUri().toString();
+ }
}
+
+ // use the first image in the folder if existing
+ for (DocumentFile file : documentFolder.listFiles()) {
+ String mime = file.getType();
+ if (mime != null && (mime.startsWith("image/jpeg") || mime.startsWith("image/png"))) {
+ return file.getUri().toString();
+ }
+ }
+
+ // use default icon as fallback
+ return getDefaultIconUrl(context);
}
/**
@@ -139,46 +157,50 @@ public class LocalFeedUpdater {
}
private static FeedItem createFeedItem(Feed feed, DocumentFile file, Context context) {
- String uuid = UUID.randomUUID().toString();
+ FeedItem item = new FeedItem(0, file.getName(), UUID.randomUUID().toString(),
+ file.getName(), new Date(file.lastModified()), FeedItem.UNPLAYED, feed);
+ item.setAutoDownload(false);
+
+ long size = file.length();
+ FeedMedia media = new FeedMedia(0, item, 0, 0, size, file.getType(),
+ file.getUri().toString(), file.getUri().toString(), false, null, 0, 0);
+ item.setMedia(media);
+
+ try {
+ loadMetadata(item, file, context);
+ } catch (Exception e) {
+ item.setDescription(e.getMessage());
+ }
+
+ return item;
+ }
+ private static void loadMetadata(FeedItem item, DocumentFile file, Context context) {
MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever();
mediaMetadataRetriever.setDataSource(context, file.getUri());
- String dateStr = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DATE);
- Date date = null;
+ String dateStr = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DATE);
if (!TextUtils.isEmpty(dateStr)) {
try {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd'T'HHmmss", Locale.getDefault());
- date = simpleDateFormat.parse(dateStr);
+ item.setPubDate(simpleDateFormat.parse(dateStr));
} catch (ParseException parseException) {
- date = DateUtils.parse(dateStr);
- if (date == null) {
- date = new Date(file.lastModified());
+ Date date = DateUtils.parse(dateStr);
+ if (date != null) {
+ item.setPubDate(date);
}
}
- } else {
- date = new Date(file.lastModified());
}
- FeedItem item = new FeedItem(0, file.getName(), uuid, file.getName(), date,
- FeedItem.UNPLAYED, feed);
- item.setAutoDownload(false);
-
- String durationStr = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
String title = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE);
if (!TextUtils.isEmpty(title)) {
item.setTitle(title);
}
- //add the media to the item
- long duration = Long.parseLong(durationStr);
- long size = file.length();
- FeedMedia media = new FeedMedia(0, item, (int) duration, 0, size, file.getType(),
- file.getUri().toString(), file.getUri().toString(), false, null, 0, 0);
- media.setHasEmbeddedPicture(mediaMetadataRetriever.getEmbeddedPicture() != null);
- item.setMedia(media);
+ String durationStr = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
+ item.getMedia().setDuration((int) Long.parseLong(durationStr));
- return item;
+ item.getMedia().setHasEmbeddedPicture(mediaMetadataRetriever.getEmbeddedPicture() != null);
}
private static void reportError(Feed feed, String reasonDetailed) {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/util/PlaybackSpeedUtils.java b/core/src/main/java/de/danoeh/antennapod/core/feed/util/PlaybackSpeedUtils.java
index 0d5ecbb71..d6740994d 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/util/PlaybackSpeedUtils.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/util/PlaybackSpeedUtils.java
@@ -1,5 +1,6 @@
package de.danoeh.antennapod.core.feed.util;
+import android.util.Log;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
@@ -14,6 +15,7 @@ import static de.danoeh.antennapod.core.feed.FeedPreferences.SPEED_USE_GLOBAL;
* Utility class to use the appropriate playback speed based on {@link PlaybackPreferences}
*/
public final class PlaybackSpeedUtils {
+ private static final String TAG = "PlaybackSpeedUtils";
private PlaybackSpeedUtils() {
}
@@ -33,8 +35,10 @@ public final class PlaybackSpeedUtils {
FeedItem item = ((FeedMedia) media).getItem();
if (item != null) {
Feed feed = item.getFeed();
- if (feed != null) {
+ if (feed != null && feed.getPreferences() != null) {
playbackSpeed = feed.getPreferences().getFeedPlaybackSpeed();
+ } else {
+ Log.d(TAG, "Can not get feed specific playback speed: " + feed);
}
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideModule.java b/core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideModule.java
index ab4247cef..b3adc567e 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideModule.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideModule.java
@@ -10,7 +10,6 @@ import com.bumptech.glide.annotation.GlideModule;
import com.bumptech.glide.load.DecodeFormat;
import com.bumptech.glide.load.engine.cache.InternalCacheDiskCacheFactory;
import com.bumptech.glide.load.model.StringLoader;
-import com.bumptech.glide.load.model.UriLoader;
import com.bumptech.glide.module.AppGlideModule;
import de.danoeh.antennapod.core.util.EmbeddedChapterImage;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/glide/ChapterImageModelLoader.java b/core/src/main/java/de/danoeh/antennapod/core/glide/ChapterImageModelLoader.java
index 35a9d987b..519d625e2 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/glide/ChapterImageModelLoader.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/glide/ChapterImageModelLoader.java
@@ -10,17 +10,13 @@ import com.bumptech.glide.load.model.ModelLoader;
import com.bumptech.glide.load.model.ModelLoaderFactory;
import com.bumptech.glide.load.model.MultiModelLoaderFactory;
import com.bumptech.glide.signature.ObjectKey;
-import de.danoeh.antennapod.core.ClientConfig;
-import de.danoeh.antennapod.core.feed.Chapter;
import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
import de.danoeh.antennapod.core.util.EmbeddedChapterImage;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
-import java.net.URL;
import java.nio.ByteBuffer;
-import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.apache.commons.io.IOUtils;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/preferences/PlaybackPreferences.java b/core/src/main/java/de/danoeh/antennapod/core/preferences/PlaybackPreferences.java
index 08ea27434..95b828e28 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/preferences/PlaybackPreferences.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/preferences/PlaybackPreferences.java
@@ -100,7 +100,7 @@ public class PlaybackPreferences implements SharedPreferences.OnSharedPreference
}
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
- if (key.equals(PREF_CURRENT_PLAYER_STATUS)) {
+ if (PREF_CURRENT_PLAYER_STATUS.equals(key)) {
EventBus.getDefault().post(new PlayerStatusEvent());
}
}
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 56dd95fe6..ed9c519a6 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
@@ -301,10 +301,30 @@ public class UserPreferences {
* @return {@code true} if download reports are shown, {@code false} otherwise
*/
public static boolean showDownloadReport() {
+ if (Build.VERSION.SDK_INT >= 26) {
+ return true; // System handles notification preferences
+ }
+ return prefs.getBoolean(PREF_SHOW_DOWNLOAD_REPORT, true);
+ }
+
+ /**
+ * Used for migration of the preference to system notification channels.
+ */
+ public static boolean getShowDownloadReportRaw() {
return prefs.getBoolean(PREF_SHOW_DOWNLOAD_REPORT, true);
}
public static boolean showAutoDownloadReport() {
+ if (Build.VERSION.SDK_INT >= 26) {
+ return true; // System handles notification preferences
+ }
+ return prefs.getBoolean(PREF_SHOW_AUTO_DOWNLOAD_REPORT, false);
+ }
+
+ /**
+ * Used for migration of the preference to system notification channels.
+ */
+ public static boolean getShowAutoDownloadReportRaw() {
return prefs.getBoolean(PREF_SHOW_AUTO_DOWNLOAD_REPORT, false);
}
@@ -728,6 +748,16 @@ public class UserPreferences {
}
public static boolean gpodnetNotificationsEnabled() {
+ if (Build.VERSION.SDK_INT >= 26) {
+ return true; // System handles notification preferences
+ }
+ return prefs.getBoolean(PREF_GPODNET_NOTIFICATIONS, true);
+ }
+
+ /**
+ * Used for migration of the preference to system notification channels.
+ */
+ public static boolean getGpodnetNotificationsEnabledRaw() {
return prefs.getBoolean(PREF_GPODNET_NOTIFICATIONS, true);
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/receiver/PlayerWidget.java b/core/src/main/java/de/danoeh/antennapod/core/receiver/PlayerWidget.java
index 2e592bdf5..9e9663205 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/receiver/PlayerWidget.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/receiver/PlayerWidget.java
@@ -16,6 +16,9 @@ public class PlayerWidget extends AppWidgetProvider {
public static final String PREFS_NAME = "PlayerWidgetPrefs";
private static final String KEY_ENABLED = "WidgetEnabled";
public static final String KEY_WIDGET_COLOR = "widget_color";
+ public static final String KEY_WIDGET_SKIP = "widget_skip";
+ public static final String KEY_WIDGET_FAST_FORWARD = "widget_fast_forward";
+ public static final String KEY_WIDGET_REWIND = "widget_rewind";
public static final int DEFAULT_COLOR = 0x00262C31;
@Override
@@ -52,6 +55,9 @@ public class PlayerWidget extends AppWidgetProvider {
for (int appWidgetId : appWidgetIds) {
SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
prefs.edit().remove(KEY_WIDGET_COLOR + appWidgetId).apply();
+ prefs.edit().remove(KEY_WIDGET_REWIND + appWidgetId).apply();
+ prefs.edit().remove(KEY_WIDGET_FAST_FORWARD + appWidgetId).apply();
+ prefs.edit().remove(KEY_WIDGET_SKIP + appWidgetId).apply();
}
super.onDeleted(context, appWidgetIds);
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/PlayerWidgetJobService.java b/core/src/main/java/de/danoeh/antennapod/core/service/PlayerWidgetJobService.java
index 74735a264..5af05b6d2 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/PlayerWidgetJobService.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/PlayerWidgetJobService.java
@@ -102,9 +102,10 @@ public class PlayerWidgetJobService extends SafeJobIntentService {
ComponentName playerWidget = new ComponentName(this, PlayerWidget.class);
AppWidgetManager manager = AppWidgetManager.getInstance(this);
int[] widgetIds = manager.getAppWidgetIds(playerWidget);
- RemoteViews views = new RemoteViews(getPackageName(), R.layout.player_widget);
final PendingIntent startMediaPlayer = PendingIntent.getActivity(this, R.id.pending_intent_player_activity,
PlaybackService.getPlayerActivityIntent(this), PendingIntent.FLAG_UPDATE_CURRENT);
+ RemoteViews views;
+ views = new RemoteViews(getPackageName(), R.layout.player_widget);
boolean nothingPlaying = false;
Playable media;
@@ -119,6 +120,7 @@ public class PlayerWidgetJobService extends SafeJobIntentService {
if (media != null) {
views.setOnClickPendingIntent(R.id.layout_left, startMediaPlayer);
+ views.setOnClickPendingIntent(R.id.imgvCover, startMediaPlayer);
try {
Bitmap icon;
@@ -155,11 +157,18 @@ public class PlayerWidgetJobService extends SafeJobIntentService {
if (status == PlayerStatus.PLAYING) {
views.setImageViewResource(R.id.butPlay, R.drawable.ic_av_pause_white_48dp);
views.setContentDescription(R.id.butPlay, getString(R.string.pause_label));
+ views.setImageViewResource(R.id.butPlayExtended, R.drawable.ic_av_pause_white_48dp);
+ views.setContentDescription(R.id.butPlayExtended, getString(R.string.pause_label));
} else {
views.setImageViewResource(R.id.butPlay, R.drawable.ic_av_play_white_48dp);
views.setContentDescription(R.id.butPlay, getString(R.string.play_label));
+ views.setImageViewResource(R.id.butPlayExtended, R.drawable.ic_av_play_white_48dp);
+ views.setContentDescription(R.id.butPlayExtended, getString(R.string.play_label));
}
- views.setOnClickPendingIntent(R.id.butPlay, createMediaButtonIntent());
+ views.setOnClickPendingIntent(R.id.butPlay,
+ createMediaButtonIntent(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE));
+ views.setOnClickPendingIntent(R.id.butPlayExtended,
+ createMediaButtonIntent(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE));
} else {
nothingPlaying = true;
}
@@ -168,16 +177,20 @@ public class PlayerWidgetJobService extends SafeJobIntentService {
// start the app if they click anything
views.setOnClickPendingIntent(R.id.layout_left, startMediaPlayer);
views.setOnClickPendingIntent(R.id.butPlay, startMediaPlayer);
+ views.setOnClickPendingIntent(R.id.butPlayExtended,
+ createMediaButtonIntent(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE));
views.setViewVisibility(R.id.txtvProgress, View.GONE);
views.setViewVisibility(R.id.txtvTitle, View.GONE);
views.setViewVisibility(R.id.txtNoPlaying, View.VISIBLE);
views.setImageViewResource(R.id.imgvCover, R.mipmap.ic_launcher_round);
views.setImageViewResource(R.id.butPlay, R.drawable.ic_av_play_white_48dp);
+ views.setImageViewResource(R.id.butPlayExtended, R.drawable.ic_av_play_white_48dp);
}
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
for (int id : widgetIds) {
Bundle options = manager.getAppWidgetOptions(id);
+ SharedPreferences prefs = getSharedPreferences(PlayerWidget.PREFS_NAME, Context.MODE_PRIVATE);
int minWidth = options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH);
int columns = getCellsForSize(minWidth);
if (columns < 3) {
@@ -185,8 +198,18 @@ public class PlayerWidgetJobService extends SafeJobIntentService {
} else {
views.setViewVisibility(R.id.layout_center, View.VISIBLE);
}
+ boolean showRewind = prefs.getBoolean(PlayerWidget.KEY_WIDGET_REWIND + id, false);
+ boolean showFastForward = prefs.getBoolean(PlayerWidget.KEY_WIDGET_FAST_FORWARD + id, false);
+ boolean showSkip = prefs.getBoolean(PlayerWidget.KEY_WIDGET_SKIP + id, false);
+
+ if (showRewind || showSkip || showFastForward) {
+ views.setInt(R.id.extendedButtonsContainer, "setVisibility", View.VISIBLE);
+ views.setInt(R.id.butPlay, "setVisibility", View.GONE);
+ views.setInt(R.id.butRew, "setVisibility", showRewind ? View.VISIBLE : View.GONE);
+ views.setInt(R.id.butFastForward, "setVisibility", showFastForward ? View.VISIBLE : View.GONE);
+ views.setInt(R.id.butSkip, "setVisibility", showSkip ? View.VISIBLE : View.GONE);
+ }
- SharedPreferences prefs = getSharedPreferences(PlayerWidget.PREFS_NAME, Context.MODE_PRIVATE);
int backgroundColor = prefs.getInt(PlayerWidget.KEY_WIDGET_COLOR + id, PlayerWidget.DEFAULT_COLOR);
views.setInt(R.id.widgetLayout, "setBackgroundColor", backgroundColor);
@@ -200,13 +223,13 @@ public class PlayerWidgetJobService extends SafeJobIntentService {
/**
* Creates an intent which fakes a mediabutton press
*/
- private PendingIntent createMediaButtonIntent() {
- KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
+ private PendingIntent createMediaButtonIntent(int eventCode) {
+ KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, eventCode);
Intent startingIntent = new Intent(getBaseContext(), MediaButtonReceiver.class);
startingIntent.setAction(MediaButtonReceiver.NOTIFY_BUTTON_RECEIVER);
startingIntent.putExtra(Intent.EXTRA_KEY_EVENT, event);
- return PendingIntent.getBroadcast(this, 0, startingIntent, 0);
+ return PendingIntent.getBroadcast(this, eventCode, startingIntent, 0);
}
private String getProgressString(int position, int duration, float speed) {
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 de106a01e..6dbc4a82f 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
@@ -25,7 +25,6 @@ import org.greenrobot.eventbus.EventBus;
import java.io.File;
import java.io.IOException;
-import java.net.HttpURLConnection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -39,7 +38,6 @@ import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
-import de.danoeh.antennapod.core.ClientConfig;
import de.danoeh.antennapod.core.event.DownloadEvent;
import de.danoeh.antennapod.core.event.FeedItemEvent;
import de.danoeh.antennapod.core.feed.Feed;
@@ -168,6 +166,7 @@ public class DownloadService extends Service {
startForeground(R.id.notification_downloading, notification);
syncExecutor.execute(() -> onDownloadQueued(intent));
} else if (numberOfDownloads.get() == 0) {
+ stopForeground(true);
stopSelf();
} else {
Log.d(TAG, "onStartCommand: Unknown intent");
@@ -205,8 +204,7 @@ public class DownloadService extends Service {
isRunning = false;
boolean showAutoDownloadReport = UserPreferences.showAutoDownloadReport();
- if (ClientConfig.downloadServiceCallbacks.shouldCreateReport()
- && (UserPreferences.showDownloadReport() || showAutoDownloadReport)) {
+ if (UserPreferences.showDownloadReport() || showAutoDownloadReport) {
notificationManager.updateReport(reportQueue, showAutoDownloadReport);
reportQueue.clear();
}
@@ -326,18 +324,11 @@ public class DownloadService extends Service {
if (item == null) {
return;
}
- boolean httpNotFound = status.getReason() == DownloadError.ERROR_HTTP_DATA_ERROR
- && String.valueOf(HttpURLConnection.HTTP_NOT_FOUND).equals(status.getReasonDetailed());
- boolean forbidden = status.getReason() == DownloadError.ERROR_FORBIDDEN
- && String.valueOf(HttpURLConnection.HTTP_FORBIDDEN).equals(status.getReasonDetailed());
- boolean notEnoughSpace = status.getReason() == DownloadError.ERROR_NOT_ENOUGH_SPACE;
- boolean wrongFileType = status.getReason() == DownloadError.ERROR_FILE_TYPE;
- boolean httpGone = status.getReason() == DownloadError.ERROR_HTTP_DATA_ERROR
- && String.valueOf(HttpURLConnection.HTTP_GONE).equals(status.getReasonDetailed());
- boolean httpBadReq = status.getReason() == DownloadError.ERROR_HTTP_DATA_ERROR
- && String.valueOf(HttpURLConnection.HTTP_BAD_REQUEST).equals(status.getReasonDetailed());
-
- if (httpNotFound || forbidden || notEnoughSpace || wrongFileType || httpGone || httpBadReq ) {
+ boolean unknownHost = status.getReason() == DownloadError.ERROR_UNKNOWN_HOST;
+ boolean unsupportedType = status.getReason() == DownloadError.ERROR_UNSUPPORTED_TYPE;
+ boolean wrongSize = status.getReason() == DownloadError.ERROR_IO_WRONG_SIZE;
+
+ if (! (unknownHost || unsupportedType || wrongSize)) {
try {
DBWriter.saveFeedItemAutoDownloadFailed(item).get();
} catch (ExecutionException | InterruptedException e) {
@@ -429,7 +420,7 @@ public class DownloadService extends Service {
+ ", cleanupMedia=" + cleanupMedia);
if (cleanupMedia) {
- ClientConfig.dbTasksCallbacks.getEpisodeCacheCleanupAlgorithm()
+ UserPreferences.getEpisodeCleanupAlgorithm()
.makeRoomForEpisodes(getApplicationContext(), requests.size());
}
@@ -553,6 +544,7 @@ public class DownloadService extends Service {
if (numberOfDownloads.get() <= 0 && DownloadRequester.getInstance().hasNoDownloads()) {
Log.d(TAG, "Number of downloads is " + numberOfDownloads.get() + ", attempting shutdown");
+ stopForeground(true);
stopSelf();
if (notificationUpdater != null) {
notificationUpdater.run();
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadServiceNotification.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadServiceNotification.java
index 0715d50dd..fb6009c02 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadServiceNotification.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadServiceNotification.java
@@ -148,7 +148,7 @@ public class DownloadServiceNotification {
id = R.id.notification_auto_download_report;
content = createAutoDownloadNotificationContent(reportQueue);
} else {
- channelId = NotificationUtils.CHANNEL_ID_ERROR;
+ channelId = NotificationUtils.CHANNEL_ID_DOWNLOAD_ERROR;
titleId = R.string.download_report_title;
iconId = R.drawable.ic_notification_sync_error;
intent = ClientConfig.downloadServiceCallbacks.getReportNotificationContentIntent(context);
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 65b7ed7d1..b553a9d1f 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
@@ -4,7 +4,6 @@ import androidx.annotation.NonNull;
import android.text.TextUtils;
import android.util.Log;
-import de.danoeh.antennapod.core.service.BasicAuthorizationInterceptor;
import okhttp3.CacheControl;
import org.apache.commons.io.IOUtils;
@@ -21,14 +20,12 @@ 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.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 okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Protocol;
import okhttp3.Request;
@@ -226,7 +223,7 @@ public class HttpDownloader extends Downloader {
// written file. This check cannot be made if compression was used
if (!isGzip && request.getSize() != DownloadStatus.SIZE_UNKNOWN &&
request.getSoFar() != request.getSize()) {
- onFail(DownloadError.ERROR_IO_ERROR, "Download completed but size: " +
+ onFail(DownloadError.ERROR_IO_WRONG_SIZE, "Download completed but size: " +
request.getSoFar() + " does not equal expected size " + request.getSize());
return;
} else if (request.getSize() > 0 && request.getSoFar() == 0) {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FailedDownloadHandler.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FailedDownloadHandler.java
index 041d26bd4..386e5e6f7 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FailedDownloadHandler.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FailedDownloadHandler.java
@@ -3,7 +3,6 @@ package de.danoeh.antennapod.core.service.download.handler;
import android.util.Log;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.service.download.DownloadRequest;
-import de.danoeh.antennapod.core.service.download.DownloadStatus;
import de.danoeh.antennapod.core.storage.DBWriter;
/**
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/ExoPlayerWrapper.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/ExoPlayerWrapper.java
index 71bbf2efd..9a8248984 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/ExoPlayerWrapper.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/ExoPlayerWrapper.java
@@ -2,6 +2,7 @@ package de.danoeh.antennapod.core.service.playback;
import android.content.Context;
import android.net.Uri;
+import android.text.TextUtils;
import android.util.Log;
import android.view.SurfaceHolder;
import com.google.android.exoplayer2.C;
@@ -28,8 +29,10 @@ import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
import com.google.android.exoplayer2.upstream.DefaultHttpDataSource;
import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory;
+
import de.danoeh.antennapod.core.ClientConfig;
import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.service.download.HttpDownloader;
import de.danoeh.antennapod.core.util.playback.IPlayer;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
@@ -184,14 +187,22 @@ public class ExoPlayerWrapper implements IPlayer {
exoPlayer.setAudioAttributes(b.build());
}
- @Override
- public void setDataSource(String s) throws IllegalArgumentException, IllegalStateException {
+ public void setDataSource(String s, String user, String password)
+ throws IllegalArgumentException, IllegalStateException {
Log.d(TAG, "setDataSource: " + s);
DefaultHttpDataSourceFactory httpDataSourceFactory = new DefaultHttpDataSourceFactory(
ClientConfig.USER_AGENT, null,
DefaultHttpDataSource.DEFAULT_CONNECT_TIMEOUT_MILLIS,
DefaultHttpDataSource.DEFAULT_READ_TIMEOUT_MILLIS,
true);
+
+ if (!TextUtils.isEmpty(user) && !TextUtils.isEmpty(password)) {
+ httpDataSourceFactory.getDefaultRequestProperties().set("Authorization",
+ HttpDownloader.encodeCredentials(
+ user,
+ password,
+ "ISO-8859-1"));
+ }
DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(context, null, httpDataSourceFactory);
DefaultExtractorsFactory extractorsFactory = new DefaultExtractorsFactory();
extractorsFactory.setConstantBitrateSeekingEnabled(true);
@@ -200,6 +211,11 @@ public class ExoPlayerWrapper implements IPlayer {
}
@Override
+ public void setDataSource(String s) throws IllegalArgumentException, IllegalStateException {
+ setDataSource(s, null, null);
+ }
+
+ @Override
public void setDisplay(SurfaceHolder sh) {
exoPlayer.setVideoSurfaceHolder(sh);
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java
index ae5d62872..98280f54d 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java
@@ -1,7 +1,6 @@
package de.danoeh.antennapod.core.service.playback;
import android.content.Context;
-import android.media.AudioAttributes;
import android.media.AudioManager;
import android.os.PowerManager;
import androidx.annotation.NonNull;
@@ -261,7 +260,16 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
callback.onMediaChanged(false);
setPlaybackParams(PlaybackSpeedUtils.getCurrentPlaybackSpeed(media), UserPreferences.isSkipSilence());
if (stream) {
- mediaPlayer.setDataSource(media.getStreamUrl());
+ if (playable instanceof FeedMedia) {
+ FeedMedia feedMedia = (FeedMedia) playable;
+ FeedPreferences preferences = feedMedia.getItem().getFeed().getPreferences();
+ mediaPlayer.setDataSource(
+ media.getStreamUrl(),
+ preferences.getUsername(),
+ preferences.getPassword());
+ } else {
+ mediaPlayer.setDataSource(media.getStreamUrl());
+ }
} else if (media.getLocalMediaUrl() != null && new File(media.getLocalMediaUrl()).canRead()) {
mediaPlayer.setDataSource(media.getLocalMediaUrl());
} else {
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 60075dda6..c1500d78b 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
@@ -7,6 +7,7 @@ import android.app.UiModeManager;
import android.bluetooth.BluetoothA2dp;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -51,7 +52,6 @@ import java.util.concurrent.TimeUnit;
import de.danoeh.antennapod.core.ClientConfig;
import de.danoeh.antennapod.core.R;
-import de.danoeh.antennapod.core.event.FeedItemEvent;
import de.danoeh.antennapod.core.event.MessageEvent;
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
import de.danoeh.antennapod.core.event.ServiceEvent;
@@ -149,7 +149,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
public static final String ACTION_PAUSE_PLAY_CURRENT_EPISODE = "action.de.danoeh.antennapod.core.service.pausePlayCurrentEpisode";
/**
- * Custom action used by Android Wear
+ * Custom action used by Android Wear, Android Auto
*/
private static final String CUSTOM_ACTION_FAST_FORWARD = "action.de.danoeh.antennapod.core.service.fastForward";
private static final String CUSTOM_ACTION_REWIND = "action.de.danoeh.antennapod.core.service.rewind";
@@ -370,9 +370,26 @@ public class PlaybackService extends MediaBrowserServiceCompat {
}
private MediaBrowserCompat.MediaItem createBrowsableMediaItemForRoot() {
+ Uri uri = new Uri.Builder()
+ .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
+ .authority(getResources().getResourcePackageName(R.drawable.ic_playlist_black))
+ .appendPath(getResources().getResourceTypeName(R.drawable.ic_playlist_black))
+ .appendPath(getResources().getResourceEntryName(R.drawable.ic_playlist_black))
+ .build();
+
+ String subtitle = "";
+ try {
+ int count = taskManager.getQueue().size();
+ subtitle = getResources().getQuantityString(R.plurals.num_episodes, count, count);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+
MediaDescriptionCompat description = new MediaDescriptionCompat.Builder()
+ .setIconUri(uri)
.setMediaId(getResources().getString(R.string.queue_label))
.setTitle(getResources().getString(R.string.queue_label))
+ .setSubtitle(subtitle)
.build();
return new MediaBrowserCompat.MediaItem(description,
MediaBrowserCompat.MediaItem.FLAG_BROWSABLE);
@@ -517,11 +534,12 @@ public class PlaybackService extends MediaBrowserServiceCompat {
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
playableLoaded -> {
- mediaPlayer.playMediaObject(playable, stream, startWhenPrepared,
+ mediaPlayer.playMediaObject(playableLoaded, stream, startWhenPrepared,
prepareImmediately);
- addPlayableToQueue(playable);
+ addPlayableToQueue(playableLoaded);
}, error -> {
Log.d(TAG, "Playable was not found. Stopping service.");
+ error.printStackTrace();
stateManager.stopService();
});
return Service.START_NOT_STICKY;
@@ -729,6 +747,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
addPlayableToQueue(playable);
}, error -> {
Log.d(TAG, "Playable was not loaded from preferences. Stopping service.");
+ error.printStackTrace();
stateManager.stopService();
});
}
@@ -971,10 +990,6 @@ public class PlaybackService extends MediaBrowserServiceCompat {
Log.d(TAG, "getNextInQueue(), but playable not an instance of FeedMedia, so not proceeding");
return null;
}
- if (!ClientConfig.playbackServiceCallbacks.useQueue()) {
- Log.d(TAG, "getNextInQueue(), but queue not in use by this app");
- return null;
- }
Log.d(TAG, "getNextInQueue()");
FeedMedia media = (FeedMedia) currentMedia;
try {
@@ -1022,6 +1037,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
*/
private void onPlaybackEnded(MediaType mediaType, boolean stopPlaying) {
Log.d(TAG, "Playback ended");
+ PlaybackPreferences.clearCurrentlyPlayingTemporaryPlaybackSpeed();
if (stopPlaying) {
taskManager.cancelPositionSaver();
cancelPositionObserver();
@@ -1060,7 +1076,6 @@ public class PlaybackService extends MediaBrowserServiceCompat {
*/
private void onPostPlayback(final Playable playable, boolean ended, boolean skipped,
boolean playingNext) {
- PlaybackPreferences.clearCurrentlyPlayingTemporaryPlaybackSpeed();
if (playable == null) {
Log.e(TAG, "Cannot do post-playback processing: media was null");
return;
@@ -1206,6 +1221,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
sessionState.setState(state, getCurrentPosition(), getCurrentPlaybackSpeed());
long capabilities = PlaybackStateCompat.ACTION_PLAY_PAUSE
| PlaybackStateCompat.ACTION_REWIND
+ | PlaybackStateCompat.ACTION_PAUSE
| PlaybackStateCompat.ACTION_FAST_FORWARD
| PlaybackStateCompat.ACTION_SKIP_TO_NEXT
| PlaybackStateCompat.ACTION_SEEK_TO;
@@ -1236,17 +1252,24 @@ public class PlaybackService extends MediaBrowserServiceCompat {
CUSTOM_ACTION_FAST_FORWARD,
getString(R.string.fast_forward_label), R.drawable.ic_notification_fast_forward)
.build());
+ } else {
+ // This would give the PIP of videos a play button
+ capabilities = capabilities | PlaybackStateCompat.ACTION_PLAY;
+ if (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_WATCH) {
+ flavorHelper.sessionStateAddActionForWear(sessionState,
+ CUSTOM_ACTION_REWIND,
+ getString(R.string.rewind_label),
+ android.R.drawable.ic_media_rew);
+ flavorHelper.sessionStateAddActionForWear(sessionState,
+ CUSTOM_ACTION_FAST_FORWARD,
+ getString(R.string.fast_forward_label),
+ android.R.drawable.ic_media_ff);
+ flavorHelper.mediaSessionSetExtraForWear(mediaSession);
+ }
}
sessionState.setActions(capabilities);
- flavorHelper.sessionStateAddActionForWear(sessionState,
- CUSTOM_ACTION_REWIND, getString(R.string.rewind_label), android.R.drawable.ic_media_rew);
- flavorHelper.sessionStateAddActionForWear(sessionState,
- CUSTOM_ACTION_FAST_FORWARD, getString(R.string.fast_forward_label), android.R.drawable.ic_media_ff);
-
- flavorHelper.mediaSessionSetExtraForWear(mediaSession);
-
mediaSession.setPlaybackState(sessionState.build());
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceNotificationBuilder.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceNotificationBuilder.java
index 632ac07ea..9d249620d 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceNotificationBuilder.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceNotificationBuilder.java
@@ -69,7 +69,7 @@ public class PlaybackServiceNotificationBuilder {
}
public void loadIcon() {
- int iconSize = context.getResources().getDimensionPixelSize(android.R.dimen.notification_large_icon_width);
+ int iconSize = (int) (128 * context.getResources().getDisplayMetrics().density);
try {
icon = Glide.with(context)
.asBitmap()
diff --git a/core/src/main/java/de/danoeh/antennapod/core/ssl/BackportCaCerts.java b/core/src/main/java/de/danoeh/antennapod/core/ssl/BackportCaCerts.java
index 720d6a9d9..78c105e38 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/ssl/BackportCaCerts.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/ssl/BackportCaCerts.java
@@ -70,4 +70,36 @@ public class BackportCaCerts {
+ "0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7ILaZRfyHB\n"
+ "NVOFBkpdn627G190\n"
+ "-----END CERTIFICATE-----";
+
+ public static final String LETSENCRYPT_ISRG = "-----BEGIN CERTIFICATE-----\n"
+ + "MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw\n"
+ + "TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\n"
+ + "cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4\n"
+ + "WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu\n"
+ + "ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY\n"
+ + "MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc\n"
+ + "h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+\n"
+ + "0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U\n"
+ + "A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW\n"
+ + "T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH\n"
+ + "B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC\n"
+ + "B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv\n"
+ + "KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn\n"
+ + "OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn\n"
+ + "jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw\n"
+ + "qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI\n"
+ + "rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV\n"
+ + "HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq\n"
+ + "hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL\n"
+ + "ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ\n"
+ + "3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK\n"
+ + "NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5\n"
+ + "ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur\n"
+ + "TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC\n"
+ + "jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc\n"
+ + "oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq\n"
+ + "4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA\n"
+ + "mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d\n"
+ + "emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=\n"
+ + "-----END CERTIFICATE-----";
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/ssl/BackportTrustManager.java b/core/src/main/java/de/danoeh/antennapod/core/ssl/BackportTrustManager.java
index b8fe950b2..81d2a0709 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/ssl/BackportTrustManager.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/ssl/BackportTrustManager.java
@@ -45,6 +45,8 @@ public class BackportTrustManager {
new ByteArrayInputStream(BackportCaCerts.COMODO.getBytes(Charset.forName("UTF-8")))));
keystore.setCertificateEntry("SECTIGO_USER_TRUST_CA", cf.generateCertificate(
new ByteArrayInputStream(BackportCaCerts.SECTIGO_USER_TRUST.getBytes(Charset.forName("UTF-8")))));
+ keystore.setCertificateEntry("LETSENCRYPT_ISRG_CA", cf.generateCertificate(
+ new ByteArrayInputStream(BackportCaCerts.LETSENCRYPT_ISRG.getBytes(Charset.forName("UTF-8")))));
List<X509TrustManager> managers = new ArrayList<>();
managers.add(getSystemTrustManager(keystore));
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/APDownloadAlgorithm.java b/core/src/main/java/de/danoeh/antennapod/core/storage/APDownloadAlgorithm.java
index 7ec4db5dd..061d6cf3f 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/APDownloadAlgorithm.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/APDownloadAlgorithm.java
@@ -65,7 +65,7 @@ public class APDownloadAlgorithm implements AutomaticDownloadAlgorithm {
Iterator<FeedItem> it = candidates.iterator();
while (it.hasNext()) {
FeedItem item = it.next();
- if (!item.isAutoDownloadable()) {
+ if (!item.isAutoDownloadable() || item.getFeed().isLocalFeed()) {
it.remove();
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java
index 2ba817b94..58f838a75 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java
@@ -7,10 +7,10 @@ import androidx.collection.ArrayMap;
import android.text.TextUtils;
import android.util.Log;
+import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
-import java.util.Date;
import java.util.List;
import java.util.Map;
@@ -799,7 +799,7 @@ public final class DBReader {
feedTotalTime += media.getDuration() / 1000;
if (media.isDownloaded()) {
- totalDownloadSize = totalDownloadSize + media.getSize();
+ totalDownloadSize += new File(media.getFile_url()).length();
episodesDownloadCount++;
}
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 c059e696a..ec39e7144 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
@@ -6,7 +6,6 @@ import android.database.Cursor;
import android.os.Looper;
import android.text.TextUtils;
import android.util.Log;
-import androidx.annotation.VisibleForTesting;
import de.danoeh.antennapod.core.ClientConfig;
import de.danoeh.antennapod.core.R;
import de.danoeh.antennapod.core.event.FeedItemEvent;
@@ -290,7 +289,7 @@ public final class DBTasks {
*/
public static Future<?> autodownloadUndownloadedItems(final Context context) {
Log.d(TAG, "autodownloadUndownloadedItems");
- return autodownloadExec.submit(ClientConfig.dbTasksCallbacks.getAutomaticDownloadAlgorithm()
+ return autodownloadExec.submit(ClientConfig.automaticDownloadAlgorithm
.autoDownloadUndownloadedItems(context));
}
@@ -304,7 +303,7 @@ public final class DBTasks {
* @param context Used for accessing the DB.
*/
public static void performAutoCleanup(final Context context) {
- ClientConfig.dbTasksCallbacks.getEpisodeCacheCleanupAlgorithm().performCleanup(context);
+ UserPreferences.getEpisodeCleanupAlgorithm().performCleanup(context);
}
/**
@@ -445,7 +444,8 @@ public final class DBTasks {
// as the most recent item
// (if the most recent date is null then we can assume there are no items
// and this is the first, hence 'new')
- if (priorMostRecentDate == null
+ // New items that do not have a pubDate set are always marked as new
+ if (item.getPubDate() == null || priorMostRecentDate == null
|| priorMostRecentDate.before(item.getPubDate())
|| priorMostRecentDate.equals(item.getPubDate())) {
Log.d(TAG, "Marking item published on " + item.getPubDate()
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 9e6041df3..84cc4b6a8 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
@@ -21,8 +21,8 @@ import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
-import de.danoeh.antennapod.core.ClientConfig;
import de.danoeh.antennapod.core.R;
import de.danoeh.antennapod.core.event.DownloadLogEvent;
import de.danoeh.antennapod.core.event.FavoritesEvent;
@@ -74,6 +74,18 @@ public class DBWriter {
}
/**
+ * Wait until all threads are finished to avoid the "Illegal connection pointer" error of
+ * Robolectric. Call this method only for unit tests.
+ */
+ public static void tearDownTests() {
+ try {
+ dbExec.awaitTermination(1, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ // ignore error
+ }
+ }
+
+ /**
* Deletes a downloaded FeedMedia file from the storage device.
*
* @param context A context that is used for opening a database connection.
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/FeedSearcher.java b/core/src/main/java/de/danoeh/antennapod/core/storage/FeedSearcher.java
index 2f48cfc07..ea62065fc 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/FeedSearcher.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/FeedSearcher.java
@@ -3,10 +3,8 @@ package de.danoeh.antennapod.core.storage;
import android.content.Context;
import androidx.annotation.NonNull;
import de.danoeh.antennapod.core.feed.Feed;
-import de.danoeh.antennapod.core.feed.FeedComponent;
import de.danoeh.antennapod.core.feed.FeedItem;
-import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutionException;
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 a02cce504..7eae8b9f0 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
@@ -1,6 +1,5 @@
package de.danoeh.antennapod.core.storage;
-import android.annotation.SuppressLint;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
@@ -317,46 +316,44 @@ public class PodDBAdapter {
+ JOIN_FEED_ITEM_AND_MEDIA;
private static Context context;
+ private static PodDBAdapter instance;
- private static volatile SQLiteDatabase db;
+ private final SQLiteDatabase db;
+ private final PodDBHelper dbHelper;
public static void init(Context context) {
PodDBAdapter.context = context.getApplicationContext();
}
- // Bill Pugh Singleton Implementation
- private static class SingletonHolder {
- private static final PodDBHelper dbHelper = new PodDBHelper(PodDBAdapter.context, DATABASE_NAME, null);
- private static final PodDBAdapter dbAdapter = new PodDBAdapter();
- }
-
public static PodDBAdapter getInstance() {
- return SingletonHolder.dbAdapter;
+ if (instance == null) {
+ instance = new PodDBAdapter();
+ }
+ return instance;
}
private PodDBAdapter() {
+ dbHelper = new PodDBHelper(PodDBAdapter.context, DATABASE_NAME, null);
+ db = openDb();
}
- public synchronized PodDBAdapter open() {
- if (db == null || !db.isOpen() || db.isReadOnly()) {
- db = openDb();
- }
- return this;
- }
-
- @SuppressLint("NewApi")
private SQLiteDatabase openDb() {
SQLiteDatabase newDb;
try {
- newDb = SingletonHolder.dbHelper.getWritableDatabase();
+ newDb = dbHelper.getWritableDatabase();
newDb.disableWriteAheadLogging();
} catch (SQLException ex) {
Log.e(TAG, Log.getStackTraceString(ex));
- newDb = SingletonHolder.dbHelper.getReadableDatabase();
+ newDb = dbHelper.getReadableDatabase();
}
return newDb;
}
+ public synchronized PodDBAdapter open() {
+ // do nothing
+ return this;
+ }
+
public synchronized void close() {
// do nothing
}
@@ -372,8 +369,8 @@ public class PodDBAdapter {
* <a href="https://github.com/robolectric/robolectric/issues/1890">robolectric/robolectric#1890</a>.</p>
*/
public static void tearDownTests() {
- db = null;
- SingletonHolder.dbHelper.close();
+ getInstance().dbHelper.close();
+ instance = null;
}
public static boolean deleteDatabase() {
@@ -381,7 +378,7 @@ public class PodDBAdapter {
adapter.open();
try {
for (String tableName : ALL_TABLES) {
- db.delete(tableName, "1", null);
+ adapter.db.delete(tableName, "1", null);
}
return true;
} finally {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/sync/SyncService.java b/core/src/main/java/de/danoeh/antennapod/core/sync/SyncService.java
index 1f5d9b75f..7563ab715 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/sync/SyncService.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/sync/SyncService.java
@@ -80,7 +80,7 @@ public class SyncService extends Worker {
if (!GpodnetPreferences.loggedIn()) {
return Result.success();
}
- syncServiceImpl = new GpodnetService(AntennapodHttpClient.getHttpClient(), GpodnetService.DEFAULT_BASE_HOST);
+ syncServiceImpl = new GpodnetService(AntennapodHttpClient.getHttpClient(), GpodnetPreferences.getHostname());
SharedPreferences.Editor prefs = getApplicationContext().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
.edit();
prefs.putLong(PREF_LAST_SYNC_ATTEMPT_TIMESTAMP, System.currentTimeMillis()).apply();
@@ -485,7 +485,11 @@ public class SyncService extends Worker {
}
private void updateErrorNotification(SyncServiceException exception) {
- Log.d(TAG, "Posting error notification");
+ if (!UserPreferences.gpodnetNotificationsEnabled()) {
+ Log.d(TAG, "Skipping sync error notification because of user setting");
+ return;
+ }
+ Log.d(TAG, "Posting sync error notification");
final String description = getApplicationContext().getString(R.string.gpodnetsync_error_descr)
+ exception.getMessage();
diff --git a/core/src/main/java/de/danoeh/antennapod/core/sync/gpoddernet/GpodnetService.java b/core/src/main/java/de/danoeh/antennapod/core/sync/gpoddernet/GpodnetService.java
index 62c8ce5f3..75ac42b31 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/sync/gpoddernet/GpodnetService.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/sync/gpoddernet/GpodnetService.java
@@ -2,6 +2,7 @@ package de.danoeh.antennapod.core.sync.gpoddernet;
import android.util.Log;
import androidx.annotation.NonNull;
+import de.danoeh.antennapod.core.BuildConfig;
import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
import de.danoeh.antennapod.core.sync.gpoddernet.model.GpodnetDevice;
import de.danoeh.antennapod.core.sync.model.EpisodeAction;
@@ -36,6 +37,7 @@ import java.net.URL;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
@@ -47,6 +49,7 @@ public class GpodnetService implements ISyncService {
public static final String TAG = "GpodnetService";
public static final String DEFAULT_BASE_HOST = "gpodder.net";
private static final String BASE_SCHEME = "https";
+ private static final int PORT = 443;
private static final int UPLOAD_BULK_SIZE = 30;
private static final MediaType TEXT = MediaType.parse("plain/text; charset=utf-8");
private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
@@ -71,7 +74,8 @@ public class GpodnetService implements ISyncService {
public List<GpodnetTag> getTopTags(int count) throws GpodnetServiceException {
URL url;
try {
- url = new URI(BASE_SCHEME, baseHost, String.format(Locale.US, "/api/2/tags/%d.json", count), null).toURL();
+ url = new URI(BASE_SCHEME, null, baseHost, PORT,
+ String.format(Locale.US, "/api/2/tags/%d.json", count), null, null).toURL();
} catch (MalformedURLException | URISyntaxException e) {
e.printStackTrace();
throw new GpodnetServiceException(e);
@@ -104,8 +108,8 @@ public class GpodnetService implements ISyncService {
public List<GpodnetPodcast> getPodcastsForTag(@NonNull GpodnetTag tag, int count)
throws GpodnetServiceException {
try {
- URL url = new URI(BASE_SCHEME, baseHost, String.format(Locale.US,
- "/api/2/tag/%s/%d.json", tag.getTag(), count), null).toURL();
+ URL url = new URI(BASE_SCHEME, null, baseHost, PORT,
+ String.format(Locale.US, "/api/2/tag/%s/%d.json", tag.getTag(), count), null, null).toURL();
Request.Builder request = new Request.Builder().url(url);
String response = executeRequest(request);
@@ -130,7 +134,8 @@ public class GpodnetService implements ISyncService {
}
try {
- URL url = new URI(BASE_SCHEME, baseHost, String.format(Locale.US, "/toplist/%d.json", count), null).toURL();
+ URL url = new URI(BASE_SCHEME, null, baseHost, PORT,
+ String.format(Locale.US, "/toplist/%d.json", count), null, null).toURL();
Request.Builder request = new Request.Builder().url(url);
String response = executeRequest(request);
@@ -161,8 +166,8 @@ public class GpodnetService implements ISyncService {
}
try {
- URL url = new URI(BASE_SCHEME, baseHost,
- String.format(Locale.US, "/suggestions/%d.json", count), null).toURL();
+ URL url = new URI(BASE_SCHEME, null, baseHost, PORT,
+ String.format(Locale.US, "/suggestions/%d.json", count), null, null).toURL();
Request.Builder request = new Request.Builder().url(url);
String response = executeRequest(request);
@@ -187,7 +192,7 @@ public class GpodnetService implements ISyncService {
.format(Locale.US, "q=%s&scale_logo=%d", query, scaledLogoSize) : String
.format("q=%s", query);
try {
- URL url = new URI(BASE_SCHEME, null, baseHost, -1, "/search.json",
+ URL url = new URI(BASE_SCHEME, null, baseHost, PORT, "/search.json",
parameters, null).toURL();
Request.Builder request = new Request.Builder().url(url);
String response = executeRequest(request);
@@ -214,7 +219,8 @@ public class GpodnetService implements ISyncService {
public List<GpodnetDevice> getDevices() throws GpodnetServiceException {
requireLoggedIn();
try {
- URL url = new URI(BASE_SCHEME, baseHost, String.format("/api/2/devices/%s.json", username), null).toURL();
+ URL url = new URI(BASE_SCHEME, null, baseHost, PORT,
+ String.format("/api/2/devices/%s.json", username), null, null).toURL();
Request.Builder request = new Request.Builder().url(url);
String response = executeRequest(request);
JSONArray devicesArray = new JSONArray(response);
@@ -226,6 +232,45 @@ public class GpodnetService implements ISyncService {
}
/**
+ * Returns synchronization status of devices.
+ * <p/>
+ * This method requires authentication.
+ *
+ * @throws GpodnetServiceAuthenticationException If there is an authentication error.
+ */
+ public List<List<String>> getSynchronizedDevices() throws GpodnetServiceException {
+ requireLoggedIn();
+ try {
+ URL url = new URI(BASE_SCHEME, null, baseHost, PORT,
+ String.format("/api/2/sync-devices/%s.json", username), null, null).toURL();
+ Request.Builder request = new Request.Builder().url(url);
+ String response = executeRequest(request);
+ JSONObject syncStatus = new JSONObject(response);
+ List<List<String>> result = new ArrayList<>();
+
+ JSONArray synchronizedDevices = syncStatus.getJSONArray("synchronized");
+ for (int i = 0; i < synchronizedDevices.length(); i++) {
+ JSONArray groupDevices = synchronizedDevices.getJSONArray(i);
+ List<String> group = new ArrayList<>();
+ for (int j = 0; j < groupDevices.length(); j++) {
+ group.add(groupDevices.getString(j));
+ }
+ result.add(group);
+ }
+
+ JSONArray notSynchronizedDevices = syncStatus.getJSONArray("not-synchronized");
+ for (int i = 0; i < notSynchronizedDevices.length(); i++) {
+ result.add(Collections.singletonList(notSynchronizedDevices.getString(i)));
+ }
+
+ return result;
+ } catch (JSONException | MalformedURLException | URISyntaxException e) {
+ e.printStackTrace();
+ throw new GpodnetServiceException(e);
+ }
+ }
+
+ /**
* Configures the device of a given user.
* <p/>
* This method requires authentication.
@@ -237,8 +282,8 @@ public class GpodnetService implements ISyncService {
throws GpodnetServiceException {
requireLoggedIn();
try {
- URL url = new URI(BASE_SCHEME, baseHost, String.format(
- "/api/2/devices/%s/%s.json", username, deviceId), null).toURL();
+ URL url = new URI(BASE_SCHEME, null, baseHost, PORT,
+ String.format("/api/2/devices/%s/%s.json", username, deviceId), null, null).toURL();
String content;
if (caption != null || type != null) {
JSONObject jsonContent = new JSONObject();
@@ -262,6 +307,39 @@ public class GpodnetService implements ISyncService {
}
/**
+ * Links devices for synchronization.
+ * <p/>
+ * This method requires authentication.
+ *
+ * @throws GpodnetServiceAuthenticationException If there is an authentication error.
+ */
+ public void linkDevices(@NonNull List<String> deviceIds) throws GpodnetServiceException {
+ requireLoggedIn();
+ try {
+ final URL url = new URI(BASE_SCHEME, null, baseHost, PORT,
+ String.format("/api/2/sync-devices/%s.json", username), null, null).toURL();
+ JSONObject jsonContent = new JSONObject();
+ JSONArray group = new JSONArray();
+ for (String deviceId : deviceIds) {
+ group.put(deviceId);
+ }
+
+ JSONArray synchronizedGroups = new JSONArray();
+ synchronizedGroups.put(group);
+ jsonContent.put("synchronize", synchronizedGroups);
+ jsonContent.put("stop-synchronize", new JSONArray());
+
+ Log.d("aaaa", jsonContent.toString());
+ RequestBody body = RequestBody.create(JSON, jsonContent.toString());
+ Request.Builder request = new Request.Builder().post(body).url(url);
+ executeRequest(request);
+ } catch (JSONException | MalformedURLException | URISyntaxException e) {
+ e.printStackTrace();
+ throw new GpodnetServiceException(e);
+ }
+ }
+
+ /**
* Returns the subscriptions of a specific device.
* <p/>
* This method requires authentication.
@@ -273,8 +351,8 @@ public class GpodnetService implements ISyncService {
public String getSubscriptionsOfDevice(@NonNull String deviceId) throws GpodnetServiceException {
requireLoggedIn();
try {
- URL url = new URI(BASE_SCHEME, baseHost, String.format(
- "/subscriptions/%s/%s.opml", username, deviceId), null).toURL();
+ URL url = new URI(BASE_SCHEME, null, baseHost, PORT,
+ String.format("/subscriptions/%s/%s.opml", username, deviceId), null, null).toURL();
Request.Builder request = new Request.Builder().url(url);
return executeRequest(request);
} catch (MalformedURLException | URISyntaxException e) {
@@ -295,7 +373,8 @@ public class GpodnetService implements ISyncService {
public String getSubscriptionsOfUser() throws GpodnetServiceException {
requireLoggedIn();
try {
- URL url = new URI(BASE_SCHEME, baseHost, String.format("/subscriptions/%s.opml", username), null).toURL();
+ URL url = new URI(BASE_SCHEME, null, baseHost, PORT,
+ String.format("/subscriptions/%s.opml", username), null, null).toURL();
Request.Builder request = new Request.Builder().url(url);
return executeRequest(request);
} catch (MalformedURLException | URISyntaxException e) {
@@ -319,8 +398,8 @@ public class GpodnetService implements ISyncService {
throws GpodnetServiceException {
requireLoggedIn();
try {
- URL url = new URI(BASE_SCHEME, baseHost, String.format(
- "/subscriptions/%s/%s.txt", username, deviceId), null).toURL();
+ URL url = new URI(BASE_SCHEME, null, baseHost, PORT,
+ String.format("/subscriptions/%s/%s.txt", username, deviceId), null, null).toURL();
StringBuilder builder = new StringBuilder();
for (String s : subscriptions) {
builder.append(s);
@@ -353,8 +432,8 @@ public class GpodnetService implements ISyncService {
@NonNull Collection<String> removed) throws GpodnetServiceException {
requireLoggedIn();
try {
- URL url = new URI(BASE_SCHEME, baseHost, String.format(
- "/api/2/subscriptions/%s/%s.json", username, deviceId), null).toURL();
+ URL url = new URI(BASE_SCHEME, null, baseHost, PORT,
+ String.format("/api/2/subscriptions/%s/%s.json", username, deviceId), null, null).toURL();
final JSONObject requestObject = new JSONObject();
requestObject.put("add", new JSONArray(added));
@@ -389,8 +468,7 @@ public class GpodnetService implements ISyncService {
String params = String.format(Locale.US, "since=%d", timestamp);
String path = String.format("/api/2/subscriptions/%s/%s.json", username, deviceId);
try {
- URL url = new URI(BASE_SCHEME, null, baseHost, -1, path, params,
- null).toURL();
+ URL url = new URI(BASE_SCHEME, null, baseHost, PORT, path, params, null).toURL();
Request.Builder request = new Request.Builder().url(url);
String response = executeRequest(request);
@@ -432,8 +510,8 @@ public class GpodnetService implements ISyncService {
throws SyncServiceException {
try {
Log.d(TAG, "Uploading partial actions " + from + " to " + to + " of " + episodeActions.size());
- URL url = new URI(BASE_SCHEME, baseHost, String.format(
- "/api/2/episodes/%s.json", username), null).toURL();
+ URL url = new URI(BASE_SCHEME, null, baseHost, PORT,
+ String.format("/api/2/episodes/%s.json", username), null, null).toURL();
final JSONArray list = new JSONArray();
for (int i = from; i < to; i++) {
@@ -471,7 +549,7 @@ public class GpodnetService implements ISyncService {
String params = String.format(Locale.US, "since=%d", timestamp);
String path = String.format("/api/2/episodes/%s.json", username);
try {
- URL url = new URI(BASE_SCHEME, null, baseHost, -1, path, params, null).toURL();
+ URL url = new URI(BASE_SCHEME, null, baseHost, PORT, path, params, null).toURL();
Request.Builder request = new Request.Builder().url(url);
String response = executeRequest(request);
@@ -497,7 +575,8 @@ public class GpodnetService implements ISyncService {
public void authenticate(@NonNull String username, @NonNull String password) throws GpodnetServiceException {
URL url;
try {
- url = new URI(BASE_SCHEME, baseHost, String.format("/api/2/auth/%s/login.json", username), null).toURL();
+ url = new URI(BASE_SCHEME, null, baseHost, PORT,
+ String.format("/api/2/auth/%s/login.json", username), null, null).toURL();
} catch (MalformedURLException | URISyntaxException e) {
e.printStackTrace();
throw new GpodnetServiceException(e);
@@ -567,6 +646,13 @@ public class GpodnetService implements ISyncService {
if (responseCode == HttpURLConnection.HTTP_UNAUTHORIZED) {
throw new GpodnetServiceAuthenticationException("Wrong username or password");
} else {
+ if (BuildConfig.DEBUG) {
+ try {
+ Log.d(TAG, response.body().string());
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
throw new GpodnetServiceBadStatusCodeException("Bad response code: " + responseCode, responseCode);
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/DateUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/DateUtils.java
index e15ab2fdc..196583bcd 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/DateUtils.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/DateUtils.java
@@ -22,14 +22,7 @@ public class DateUtils {
private DateUtils(){}
private static final String TAG = "DateUtils";
-
private static final TimeZone defaultTimezone = TimeZone.getTimeZone("GMT");
- private static final SimpleDateFormat dateFormatParser = new SimpleDateFormat("", Locale.US);
-
- static {
- dateFormatParser.setLenient(false);
- dateFormatParser.setTimeZone(defaultTimezone);
- }
public static Date parse(final String input) {
if (input == null) {
@@ -37,9 +30,12 @@ public class DateUtils {
}
String date = input.trim().replace('/', '-').replaceAll("( ){2,}+", " ");
+ // remove colon from timezone to avoid differences between Android and Java SimpleDateFormat
+ date = date.replaceAll("([+-]\\d\\d):(\\d\\d)$", "$1$2");
+
// CEST is widely used but not in the "ISO 8601 Time zone" list. Let's hack around.
- date = date.replaceAll("CEST$", "+02:00");
- date = date.replaceAll("CET$", "+01:00");
+ date = date.replaceAll("CEST$", "+0200");
+ date = date.replaceAll("CET$", "+0100");
// some generators use "Sept" for September
date = date.replaceAll("\\bSept\\b", "Sep");
@@ -99,12 +95,16 @@ public class DateUtils {
"EEE d MMM yyyy HH:mm:ss 'GMT'Z (z)"
};
+ SimpleDateFormat parser = new SimpleDateFormat("", Locale.US);
+ parser.setLenient(false);
+ parser.setTimeZone(defaultTimezone);
+
ParsePosition pos = new ParsePosition(0);
for (String pattern : patterns) {
- dateFormatParser.applyPattern(pattern);
+ parser.applyPattern(pattern);
pos.setIndex(0);
try {
- Date result = dateFormatParser.parse(date, pos);
+ Date result = parser.parse(date, pos);
if (result != null && pos.getIndex() == date.length()) {
return result;
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/DownloadError.java b/core/src/main/java/de/danoeh/antennapod/core/util/DownloadError.java
index 0c9989b43..babf3a846 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/DownloadError.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/DownloadError.java
@@ -19,11 +19,11 @@ public enum DownloadError {
ERROR_NOT_ENOUGH_SPACE(10, R.string.download_error_insufficient_space),
ERROR_UNKNOWN_HOST(11, R.string.download_error_unknown_host),
ERROR_REQUEST_ERROR(12, R.string.download_error_request_error),
- ERROR_DB_ACCESS_ERROR(13, R.string.download_error_db_access),
- ERROR_UNAUTHORIZED(14, R.string.download_error_unauthorized),
+ ERROR_DB_ACCESS_ERROR(13, R.string.download_error_db_access),
+ ERROR_UNAUTHORIZED(14, R.string.download_error_unauthorized),
ERROR_FILE_TYPE(15, R.string.download_error_file_type_type),
- ERROR_FORBIDDEN(16, R.string.download_error_forbidden);
-
+ ERROR_FORBIDDEN(16, R.string.download_error_forbidden),
+ ERROR_IO_WRONG_SIZE(17, R.string.download_error_forbidden);
private final int code;
private final int resId;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/FileNameGenerator.java b/core/src/main/java/de/danoeh/antennapod/core/util/FileNameGenerator.java
index 2a387b7b0..69c23efc2 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/FileNameGenerator.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/FileNameGenerator.java
@@ -13,7 +13,7 @@ import java.security.NoSuchAlgorithmException;
/** Generates valid filenames for a given string. */
public class FileNameGenerator {
@VisibleForTesting
- public static final int MAX_FILENAME_LENGTH = 255; // Limited by ext4
+ public static final int MAX_FILENAME_LENGTH = 242; // limited by CircleCI
private static final int MD5_HEX_LENGTH = 32;
private static final char[] validChars =
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/LangUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/LangUtils.java
deleted file mode 100644
index 20af6415e..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/util/LangUtils.java
+++ /dev/null
@@ -1,124 +0,0 @@
-package de.danoeh.antennapod.core.util;
-
-import androidx.collection.ArrayMap;
-
-import java.nio.charset.Charset;
-
-public class LangUtils {
-
- private LangUtils(){}
-
- public static final Charset UTF_8 = Charset.forName("UTF-8");
-
- private static final ArrayMap<String, String> languages;
- static {
- languages = new ArrayMap<>();
- languages.put("af", "Afrikaans");
- languages.put("sq", "Albanian");
- languages.put("sq", "Albanian");
- languages.put("eu", "Basque");
- languages.put("be", "Belarusian");
- languages.put("bg", "Bulgarian");
- languages.put("ca", "Catalan");
- languages.put("Chinese (Simplified)", "zh-cn");
- languages.put("Chinese (Traditional)", "zh-tw");
- languages.put("hr", "Croatian");
- languages.put("cs", "Czech");
- languages.put("da", "Danish");
- languages.put("nl", "Dutch");
- languages.put("nl-be", "Dutch (Belgium)");
- languages.put("nl-nl", "Dutch (Netherlands)");
- languages.put("en", "English");
- languages.put("en-au", "English (Australia)");
- languages.put("en-bz", "English (Belize)");
- languages.put("en-ca", "English (Canada)");
- languages.put("en-ie", "English (Ireland)");
- languages.put("en-jm", "English (Jamaica)");
- languages.put("en-nz", "English (New Zealand)");
- languages.put("en-ph", "English (Phillipines)");
- languages.put("en-za", "English (South Africa)");
- languages.put("en-tt", "English (Trinidad)");
- languages.put("en-gb", "English (United Kingdom)");
- languages.put("en-us", "English (United States)");
- languages.put("en-zw", "English (Zimbabwe)");
- languages.put("et", "Estonian");
- languages.put("fo", "Faeroese");
- languages.put("fi", "Finnish");
- languages.put("fr", "French");
- languages.put("fr-be", "French (Belgium)");
- languages.put("fr-ca", "French (Canada)");
- languages.put("fr-fr", "French (France)");
- languages.put("fr-lu", "French (Luxembourg)");
- languages.put("fr-mc", "French (Monaco)");
- languages.put("fr-ch", "French (Switzerland)");
- languages.put("gl", "Galician");
- languages.put("gd", "Gaelic");
- languages.put("de", "German");
- languages.put("de-at", "German (Austria)");
- languages.put("de-de", "German (Germany)");
- languages.put("de-li", "German (Liechtenstein)");
- languages.put("de-lu", "German (Luxembourg)");
- languages.put("de-ch", "German (Switzerland)");
- languages.put("el", "Greek");
- languages.put("haw", "Hawaiian");
- languages.put("hu", "Hungarian");
- languages.put("is", "Icelandic");
- languages.put("in", "Indonesian");
- languages.put("ga", "Irish");
- languages.put("it", "Italian");
- languages.put("it-it", "Italian (Italy)");
- languages.put("it-ch", "Italian (Switzerland)");
- languages.put("ja", "Japanese");
- languages.put("ko", "Korean");
- languages.put("mk", "Macedonian");
- languages.put("no", "Norwegian");
- languages.put("pl", "Polish");
- languages.put("pt", "Portugese");
- languages.put("pt-br", "Portugese (Brazil)");
- languages.put("pt-pt", "Portugese (Portugal");
- languages.put("ro", "Romanian");
- languages.put("ro-mo", "Romanian (Moldova)");
- languages.put("ro-ro", "Romanian (Romania");
- languages.put("ru", "Russian");
- languages.put("ru-mo", "Russian (Moldova)");
- languages.put("ru-ru", "Russian (Russia)");
- languages.put("sr", "Serbian");
- languages.put("sk", "Slovak");
- languages.put("sl", "Slovenian");
- languages.put("es", "Spanish");
- languages.put("es-ar", "Spanish (Argentinia)");
- languages.put("es=bo", "Spanish (Bolivia)");
- languages.put("es-cl", "Spanish (Chile)");
- languages.put("es-co", "Spanish (Colombia)");
- languages.put("es-cr", "Spanish (Costa Rica)");
- languages.put("es-do", "Spanish (Dominican Republic)");
- languages.put("es-ec", "Spanish (Ecuador)");
- languages.put("es-sv", "Spanish (El Salvador)");
- languages.put("es-gt", "Spanish (Guatemala)");
- languages.put("es-hn", "Spanish (Honduras)");
- languages.put("es-mx", "Spanish (Mexico)");
- languages.put("es-ni", "Spanish (Nicaragua)");
- languages.put("es-pa", "Spanish (Panama)");
- languages.put("es-py", "Spanish (Paraguay)");
- languages.put("es-pe", "Spanish (Peru)");
- languages.put("es-pr", "Spanish (Puerto Rico)");
- languages.put("es-es", "Spanish (Spain)");
- languages.put("es-uy", "Spanish (Uruguay)");
- languages.put("es-ve", "Spanish (Venezuela)");
- languages.put("sv", "Swedish");
- languages.put("sv-fi", "Swedish (Finland)");
- languages.put("sv-se", "Swedish (Sweden)");
- languages.put("tr", "Turkish");
- languages.put("uk", "Ukranian");
- }
-
- /** Finds language string for key or returns the language key if it can't be found. */
- public static String getLanguageString(String key) {
- String language = languages.get(key);
- if (language != null) {
- return language;
- } else {
- return key;
- }
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/ShareUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/ShareUtils.java
index 3e9e8327e..2622d81aa 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/ShareUtils.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/ShareUtils.java
@@ -19,74 +19,77 @@ import de.danoeh.antennapod.core.feed.FeedMedia;
/** Utility methods for sharing data */
public class ShareUtils {
- private static final String TAG = "ShareUtils";
-
- private ShareUtils() {}
-
- public static void shareLink(Context context, String text) {
- Intent i = new Intent(Intent.ACTION_SEND);
- i.setType("text/plain");
- i.putExtra(Intent.EXTRA_TEXT, text);
- context.startActivity(Intent.createChooser(i, context.getString(R.string.share_url_label)));
- }
+ private static final String TAG = "ShareUtils";
- public static void shareFeedlink(Context context, Feed feed) {
- shareLink(context, feed.getTitle() + ": " + feed.getLink());
- }
-
- public static void shareFeedDownloadLink(Context context, Feed feed) {
- shareLink(context, feed.getTitle() + ": " + feed.getDownload_url());
- }
+ private ShareUtils() {
+ }
- public static void shareFeedItemLink(Context context, FeedItem item) {
- shareFeedItemLink(context, item, false);
- }
+ public static void shareLink(Context context, String text) {
+ Intent i = new Intent(Intent.ACTION_SEND);
+ i.setType("text/plain");
+ i.putExtra(Intent.EXTRA_TEXT, text);
+ context.startActivity(Intent.createChooser(i, context.getString(R.string.share_url_label)));
+ }
- public static void shareFeedItemDownloadLink(Context context, FeedItem item) {
- shareFeedItemDownloadLink(context, item, false);
- }
+ public static void shareFeedlink(Context context, Feed feed) {
+ shareLink(context, feed.getTitle() + ": " + feed.getLink());
+ }
- private static String getItemShareText(FeedItem item) {
- return item.getFeed().getTitle() + ": " + item.getTitle();
- }
+ public static void shareFeedDownloadLink(Context context, Feed feed) {
+ shareLink(context, feed.getTitle() + ": " + feed.getDownload_url());
+ }
+
+ public static void shareFeedItemLink(Context context, FeedItem item) {
+ shareFeedItemLink(context, item, false);
+ }
+
+ public static void shareFeedItemDownloadLink(Context context, FeedItem item) {
+ shareFeedItemDownloadLink(context, item, false);
+ }
+
+ private static String getItemShareText(FeedItem item) {
+ return item.getFeed().getTitle() + ": " + item.getTitle();
+ }
public static boolean hasLinkToShare(FeedItem item) {
- return FeedItemUtil.getLinkWithFallback(item) != null;
+ return FeedItemUtil.getLinkWithFallback(item) != null;
}
- public static void shareFeedItemLink(Context context, FeedItem item, boolean withPosition) {
- String text = getItemShareText(item) + " " + FeedItemUtil.getLinkWithFallback(item);
- if(withPosition) {
- int pos = item.getMedia().getPosition();
- text += " [" + Converter.getDurationStringLong(pos) + "]";
- }
- shareLink(context, text);
- }
+ public static void shareFeedItemLink(Context context, FeedItem item, boolean withPosition) {
+ String text = getItemShareText(item) + " " + FeedItemUtil.getLinkWithFallback(item);
+ if (withPosition) {
+ int pos = item.getMedia().getPosition();
+ text += " [" + Converter.getDurationStringLong(pos) + "]";
+ }
+ shareLink(context, text);
+ }
- public static void shareFeedItemDownloadLink(Context context, FeedItem item, boolean withPosition) {
- String text = getItemShareText(item) + " " + item.getMedia().getDownload_url();
- if(withPosition) {
- int pos = item.getMedia().getPosition();
- text += " [" + Converter.getDurationStringLong(pos) + "]";
- }
- shareLink(context, text);
- }
+ public static void shareFeedItemDownloadLink(Context context, FeedItem item, boolean withPosition) {
+ String text = getItemShareText(item) + " " + item.getMedia().getDownload_url();
+ if (withPosition) {
+ int pos = item.getMedia().getPosition();
+ text += "#t=" + pos / 1000;
+ text += " [" + Converter.getDurationStringLong(pos) + "]";
+ }
+ shareLink(context, text);
+ }
- public static void shareFeedItemFile(Context context, FeedMedia media) {
- Intent i = new Intent(Intent.ACTION_SEND);
- i.setType(media.getMime_type());
- Uri fileUri = FileProvider.getUriForFile(context, context.getString(R.string.provider_authority),
- new File(media.getLocalMediaUrl()));
- i.putExtra(Intent.EXTRA_STREAM, fileUri);
- i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
- List<ResolveInfo> resInfoList = context.getPackageManager().queryIntentActivities(i, PackageManager.MATCH_DEFAULT_ONLY);
- for (ResolveInfo resolveInfo : resInfoList) {
- String packageName = resolveInfo.activityInfo.packageName;
- context.grantUriPermission(packageName, fileUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
- }
- }
- context.startActivity(Intent.createChooser(i, context.getString(R.string.share_file_label)));
- Log.e(TAG, "shareFeedItemFile called");
- }
+ public static void shareFeedItemFile(Context context, FeedMedia media) {
+ Intent i = new Intent(Intent.ACTION_SEND);
+ i.setType(media.getMime_type());
+ Uri fileUri = FileProvider.getUriForFile(context, context.getString(R.string.provider_authority),
+ new File(media.getLocalMediaUrl()));
+ i.putExtra(Intent.EXTRA_STREAM, fileUri);
+ i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
+ List<ResolveInfo> resInfoList = context.getPackageManager()
+ .queryIntentActivities(i, PackageManager.MATCH_DEFAULT_ONLY);
+ for (ResolveInfo resolveInfo : resInfoList) {
+ String packageName = resolveInfo.activityInfo.packageName;
+ context.grantUriPermission(packageName, fileUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ }
+ }
+ context.startActivity(Intent.createChooser(i, context.getString(R.string.share_file_label)));
+ Log.e(TAG, "shareFeedItemFile called");
+ }
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/URLChecker.java b/core/src/main/java/de/danoeh/antennapod/core/util/URLChecker.java
index ac7f4848c..cb7db1709 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/URLChecker.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/URLChecker.java
@@ -9,7 +9,6 @@ import de.danoeh.antennapod.core.BuildConfig;
import okhttp3.HttpUrl;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
/**
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/comparator/FeedItemPubdateComparator.java b/core/src/main/java/de/danoeh/antennapod/core/util/comparator/FeedItemPubdateComparator.java
index ad81a1d17..766986bed 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/comparator/FeedItemPubdateComparator.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/comparator/FeedItemPubdateComparator.java
@@ -14,8 +14,12 @@ public class FeedItemPubdateComparator implements Comparator<FeedItem> {
*/
@Override
public int compare(FeedItem lhs, FeedItem rhs) {
- if (rhs.getPubDate() == null || lhs.getPubDate() == null) {
+ if (rhs.getPubDate() == null && lhs.getPubDate() == null) {
return 0;
+ } else if (rhs.getPubDate() == null) {
+ return 1;
+ } else if (lhs.getPubDate() == null) {
+ return -1;
}
return rhs.getPubDate().compareTo(lhs.getPubDate());
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/gui/NotificationUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/gui/NotificationUtils.java
index ddbe68938..3101eac34 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/gui/NotificationUtils.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/gui/NotificationUtils.java
@@ -2,28 +2,36 @@ package de.danoeh.antennapod.core.util.gui;
import android.app.NotificationChannel;
+import android.app.NotificationChannelGroup;
import android.app.NotificationManager;
import android.content.Context;
import android.os.Build;
import androidx.annotation.RequiresApi;
import de.danoeh.antennapod.core.R;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
public class NotificationUtils {
public static final String CHANNEL_ID_USER_ACTION = "user_action";
public static final String CHANNEL_ID_DOWNLOADING = "downloading";
public static final String CHANNEL_ID_PLAYING = "playing";
- public static final String CHANNEL_ID_ERROR = "error";
+ public static final String CHANNEL_ID_DOWNLOAD_ERROR = "error";
public static final String CHANNEL_ID_SYNC_ERROR = "sync_error";
public static final String CHANNEL_ID_AUTO_DOWNLOAD = "auto_download";
+ public static final String GROUP_ID_ERRORS = "group_errors";
+ public static final String GROUP_ID_NEWS = "group_news";
+
public static void createChannels(Context context) {
- if (android.os.Build.VERSION.SDK_INT < 26) {
+ if (Build.VERSION.SDK_INT < 26) {
return;
}
NotificationManager mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
if (mNotificationManager != null) {
+ mNotificationManager.createNotificationChannelGroup(createGroupErrors(context));
+ mNotificationManager.createNotificationChannelGroup(createGroupNews(context));
+
mNotificationManager.createNotificationChannel(createChannelUserAction(context));
mNotificationManager.createNotificationChannel(createChannelDownloading(context));
mNotificationManager.createNotificationChannel(createChannelPlaying(context));
@@ -35,36 +43,43 @@ public class NotificationUtils {
@RequiresApi(api = Build.VERSION_CODES.O)
private static NotificationChannel createChannelUserAction(Context c) {
- NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID_USER_ACTION,
+ NotificationChannel notificationChannel = new NotificationChannel(CHANNEL_ID_USER_ACTION,
c.getString(R.string.notification_channel_user_action), NotificationManager.IMPORTANCE_HIGH);
- mChannel.setDescription(c.getString(R.string.notification_channel_user_action_description));
- return mChannel;
+ notificationChannel.setDescription(c.getString(R.string.notification_channel_user_action_description));
+ notificationChannel.setGroup(GROUP_ID_ERRORS);
+ return notificationChannel;
}
@RequiresApi(api = Build.VERSION_CODES.O)
private static NotificationChannel createChannelDownloading(Context c) {
- NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID_DOWNLOADING,
+ NotificationChannel notificationChannel = new NotificationChannel(CHANNEL_ID_DOWNLOADING,
c.getString(R.string.notification_channel_downloading), NotificationManager.IMPORTANCE_LOW);
- mChannel.setDescription(c.getString(R.string.notification_channel_downloading_description));
- mChannel.setShowBadge(false);
- return mChannel;
+ notificationChannel.setDescription(c.getString(R.string.notification_channel_downloading_description));
+ notificationChannel.setShowBadge(false);
+ return notificationChannel;
}
@RequiresApi(api = Build.VERSION_CODES.O)
private static NotificationChannel createChannelPlaying(Context c) {
- NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID_PLAYING,
+ NotificationChannel notificationChannel = new NotificationChannel(CHANNEL_ID_PLAYING,
c.getString(R.string.notification_channel_playing), NotificationManager.IMPORTANCE_LOW);
- mChannel.setDescription(c.getString(R.string.notification_channel_playing_description));
- mChannel.setShowBadge(false);
- return mChannel;
+ notificationChannel.setDescription(c.getString(R.string.notification_channel_playing_description));
+ notificationChannel.setShowBadge(false);
+ return notificationChannel;
}
@RequiresApi(api = Build.VERSION_CODES.O)
private static NotificationChannel createChannelError(Context c) {
- NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID_ERROR,
- c.getString(R.string.notification_channel_error), NotificationManager.IMPORTANCE_HIGH);
- mChannel.setDescription(c.getString(R.string.notification_channel_error_description));
- return mChannel;
+ NotificationChannel notificationChannel = new NotificationChannel(CHANNEL_ID_DOWNLOAD_ERROR,
+ c.getString(R.string.notification_channel_download_error), NotificationManager.IMPORTANCE_HIGH);
+ notificationChannel.setDescription(c.getString(R.string.notification_channel_download_error_description));
+ notificationChannel.setGroup(GROUP_ID_ERRORS);
+
+ if (!UserPreferences.getShowDownloadReportRaw()) {
+ // Migration from app managed setting: disable notification
+ notificationChannel.setImportance(NotificationManager.IMPORTANCE_NONE);
+ }
+ return notificationChannel;
}
@RequiresApi(api = Build.VERSION_CODES.O)
@@ -72,14 +87,38 @@ public class NotificationUtils {
NotificationChannel notificationChannel = new NotificationChannel(CHANNEL_ID_SYNC_ERROR,
c.getString(R.string.notification_channel_sync_error), NotificationManager.IMPORTANCE_HIGH);
notificationChannel.setDescription(c.getString(R.string.notification_channel_sync_error_description));
+ notificationChannel.setGroup(GROUP_ID_ERRORS);
+
+ if (!UserPreferences.getGpodnetNotificationsEnabledRaw()) {
+ // Migration from app managed setting: disable notification
+ notificationChannel.setImportance(NotificationManager.IMPORTANCE_NONE);
+ }
return notificationChannel;
}
@RequiresApi(api = Build.VERSION_CODES.O)
private static NotificationChannel createChannelAutoDownload(Context c) {
- NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID_AUTO_DOWNLOAD,
- c.getString(R.string.notification_channel_auto_download), NotificationManager.IMPORTANCE_DEFAULT);
- mChannel.setDescription(c.getString(R.string.notification_channel_episode_auto_download));
- return mChannel;
+ NotificationChannel notificationChannel = new NotificationChannel(CHANNEL_ID_AUTO_DOWNLOAD,
+ c.getString(R.string.notification_channel_auto_download), NotificationManager.IMPORTANCE_NONE);
+ notificationChannel.setDescription(c.getString(R.string.notification_channel_episode_auto_download));
+ notificationChannel.setGroup(GROUP_ID_NEWS);
+
+ if (UserPreferences.getShowAutoDownloadReportRaw()) {
+ // Migration from app managed setting: enable notification
+ notificationChannel.setImportance(NotificationManager.IMPORTANCE_DEFAULT);
+ }
+ return notificationChannel;
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.O)
+ private static NotificationChannelGroup createGroupErrors(Context c) {
+ return new NotificationChannelGroup(GROUP_ID_ERRORS,
+ c.getString(R.string.notification_group_errors));
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.O)
+ private static NotificationChannelGroup createGroupNews(Context c) {
+ return new NotificationChannelGroup(GROUP_ID_NEWS,
+ c.getString(R.string.notification_group_news));
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/playback/AudioPlayer.java b/core/src/main/java/de/danoeh/antennapod/core/util/playback/AudioPlayer.java
index fecb14d25..c948d98a3 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/playback/AudioPlayer.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/playback/AudioPlayer.java
@@ -1,7 +1,6 @@
package de.danoeh.antennapod.core.util.playback;
import android.content.Context;
-import android.content.SharedPreferences;
import androidx.preference.PreferenceManager;
import android.util.Log;
import android.view.SurfaceHolder;
@@ -11,6 +10,7 @@ import org.antennapod.audio.MediaPlayer;
import de.danoeh.antennapod.core.preferences.UserPreferences;
+import java.io.IOException;
import java.util.Collections;
import java.util.List;
@@ -21,7 +21,7 @@ public class AudioPlayer extends MediaPlayer implements IPlayer {
super(context, true, ClientConfig.USER_AGENT);
PreferenceManager.getDefaultSharedPreferences(context)
.registerOnSharedPreferenceChangeListener((sharedPreferences, key) -> {
- if (key.equals(UserPreferences.PREF_MEDIA_PLAYER)) {
+ if (UserPreferences.PREF_MEDIA_PLAYER.equals(key)) {
checkMpi();
}
});
@@ -65,4 +65,9 @@ public class AudioPlayer extends MediaPlayer implements IPlayer {
public int getSelectedAudioTrack() {
return -1;
}
+
+ @Override
+ public void setDataSource(String streamUrl, String username, String password) throws IOException {
+ setDataSource(streamUrl);
+ }
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/playback/IPlayer.java b/core/src/main/java/de/danoeh/antennapod/core/util/playback/IPlayer.java
index 363004709..a511916fa 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/playback/IPlayer.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/playback/IPlayer.java
@@ -35,6 +35,8 @@ public interface IPlayer {
void setDataSource(String path) throws IllegalStateException, IOException,
IllegalArgumentException, SecurityException;
+ void setDataSource(String streamUrl, String username, String password) throws IOException;
+
void setDisplay(SurfaceHolder sh);
void setPlaybackParams(float speed, boolean skipSilence);
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java b/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java
index 425a07f4a..e1b4c967c 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java
@@ -564,6 +564,13 @@ public class PlaybackController {
}
}
+ public void extendSleepTimer(long extendTime) {
+ long timeLeft = getSleepTimerTimeLeft();
+ if (playbackService != null && timeLeft != INVALID_TIME) {
+ setSleepTimer(timeLeft + extendTime);
+ }
+ }
+
public void setSleepTimer(long time) {
if (playbackService != null) {
playbackService.setSleepTimer(time);
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackServiceStarter.java b/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackServiceStarter.java
index b12967264..107399e60 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackServiceStarter.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackServiceStarter.java
@@ -2,7 +2,6 @@ package de.danoeh.antennapod.core.util.playback;
import android.content.Context;
import android.content.Intent;
-import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/playback/VideoPlayer.java b/core/src/main/java/de/danoeh/antennapod/core/util/playback/VideoPlayer.java
index d18801870..6728c027d 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/playback/VideoPlayer.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/playback/VideoPlayer.java
@@ -3,6 +3,7 @@ package de.danoeh.antennapod.core.util.playback;
import android.media.MediaPlayer;
import android.util.Log;
+import java.io.IOException;
import java.util.Collections;
import java.util.List;
@@ -52,4 +53,9 @@ public class VideoPlayer extends MediaPlayer implements IPlayer {
public int getSelectedAudioTrack() {
return -1;
}
+
+ @Override
+ public void setDataSource(String streamUrl, String username, String password) throws IOException {
+ setDataSource(streamUrl);
+ }
}