summaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/build.gradle2
-rw-r--r--core/src/debug/res/drawable-nodpi/ic_launcher_foreground_no_finish.pngbin0 -> 70874 bytes
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/event/DiscoveryDefaultUpdateEvent.java6
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/event/FavoritesEvent.java41
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/event/FeedItemEvent.java54
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/event/FeedListUpdateEvent.java28
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/event/MessageEvent.java21
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/event/PlaybackHistoryEvent.java16
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/event/PlaybackPositionEvent.java19
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/event/PlayerErrorEvent.java13
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/event/PlayerStatusEvent.java6
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/event/QueueEvent.java71
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/event/ServiceEvent.java14
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/event/SyncServiceEvent.java13
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/event/UnreadItemsUpdateEvent.java6
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/event/settings/SkipIntroEndingChangedEvent.java25
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/event/settings/SpeedPresetChangedEvent.java19
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/event/settings/VolumeAdaptionChangedEvent.java21
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/LocalFeedUpdater.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideModule.java3
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/glide/NoHttpStringLoader.java39
-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.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java16
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/HttpDownloader.java25
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/NewEpisodesNotification.java7
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/handler/MediaDownloadedHandler.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/ExoPlayerWrapper.java31
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java80
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java222
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceMediaPlayer.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceNotificationBuilder.java12
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceTaskManager.java48
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/AutomaticDownloadAlgorithm.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java6
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DBUpgrader.java12
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java33
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java28
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/mapper/FeedItemCursorMapper.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/mapper/FeedPreferencesCursorMapper.java6
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/sync/SyncService.java11
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/FeedItemPermutors.java14
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/NetworkUtils.java85
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/ShareUtils.java24
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java44
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/widget/WidgetUpdater.java21
-rw-r--r--core/src/main/res/drawable/ic_download_black.xml9
-rw-r--r--core/src/main/res/values/strings.xml15
-rw-r--r--core/src/play/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceFlavorHelper.java2
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/feed/FeedFilterTest.java31
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/feed/FeedItemTest.java37
51 files changed, 464 insertions, 794 deletions
diff --git a/core/build.gradle b/core/build.gradle
index 59e2973ae..ce76fec70 100644
--- a/core/build.gradle
+++ b/core/build.gradle
@@ -20,6 +20,7 @@ android {
}
dependencies {
+ implementation project(':event')
implementation project(':model')
implementation project(':net:ssl')
implementation project(':net:sync:gpoddernet')
@@ -56,6 +57,7 @@ dependencies {
implementation "com.google.android.exoplayer:exoplayer-core:$exoPlayerVersion"
implementation "com.google.android.exoplayer:exoplayer-ui:$exoPlayerVersion"
+ implementation "com.google.android.exoplayer:extension-okhttp:$exoPlayerVersion"
implementation "com.github.AntennaPod:AntennaPod-AudioPlayer:$audioPlayerVersion"
// Non-free dependencies:
diff --git a/core/src/debug/res/drawable-nodpi/ic_launcher_foreground_no_finish.png b/core/src/debug/res/drawable-nodpi/ic_launcher_foreground_no_finish.png
new file mode 100644
index 000000000..825421990
--- /dev/null
+++ b/core/src/debug/res/drawable-nodpi/ic_launcher_foreground_no_finish.png
Binary files differ
diff --git a/core/src/main/java/de/danoeh/antennapod/core/event/DiscoveryDefaultUpdateEvent.java b/core/src/main/java/de/danoeh/antennapod/core/event/DiscoveryDefaultUpdateEvent.java
deleted file mode 100644
index f7757935a..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/event/DiscoveryDefaultUpdateEvent.java
+++ /dev/null
@@ -1,6 +0,0 @@
-package de.danoeh.antennapod.core.event;
-
-public class DiscoveryDefaultUpdateEvent {
- public DiscoveryDefaultUpdateEvent() {
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/event/FavoritesEvent.java b/core/src/main/java/de/danoeh/antennapod/core/event/FavoritesEvent.java
deleted file mode 100644
index cbfcc37e6..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/event/FavoritesEvent.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package de.danoeh.antennapod.core.event;
-
-import androidx.annotation.NonNull;
-
-import org.apache.commons.lang3.builder.ToStringBuilder;
-import org.apache.commons.lang3.builder.ToStringStyle;
-
-import de.danoeh.antennapod.model.feed.FeedItem;
-
-public class FavoritesEvent {
-
- public enum Action {
- ADDED, REMOVED
- }
-
- private final Action action;
- private final FeedItem item;
-
- private FavoritesEvent(Action action, FeedItem item) {
- this.action = action;
- this.item = item;
- }
-
- public static FavoritesEvent added(FeedItem item) {
- return new FavoritesEvent(Action.ADDED, item);
- }
-
- public static FavoritesEvent removed(FeedItem item) {
- return new FavoritesEvent(Action.REMOVED, item);
- }
-
- @NonNull
- @Override
- public String toString() {
- return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
- .append("action", action)
- .append("item", item)
- .toString();
- }
-
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/event/FeedItemEvent.java b/core/src/main/java/de/danoeh/antennapod/core/event/FeedItemEvent.java
deleted file mode 100644
index 99cb01714..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/event/FeedItemEvent.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package de.danoeh.antennapod.core.event;
-
-
-import androidx.annotation.NonNull;
-
-import org.apache.commons.lang3.builder.ToStringBuilder;
-import org.apache.commons.lang3.builder.ToStringStyle;
-
-import java.util.Arrays;
-import java.util.List;
-
-import de.danoeh.antennapod.model.feed.FeedItem;
-
-public class FeedItemEvent {
-
- public enum Action {
- UPDATE, DELETE_MEDIA
- }
-
- @NonNull
- private final Action action;
- @NonNull public final List<FeedItem> items;
-
- private FeedItemEvent(@NonNull Action action, @NonNull List<FeedItem> items) {
- this.action = action;
- this.items = items;
- }
-
- public static FeedItemEvent deletedMedia(List<FeedItem> items) {
- return new FeedItemEvent(Action.DELETE_MEDIA, items);
- }
-
- public static FeedItemEvent deletedMedia(FeedItem... items) {
- return deletedMedia(Arrays.asList(items));
- }
-
- public static FeedItemEvent updated(List<FeedItem> items) {
- return new FeedItemEvent(Action.UPDATE, items);
- }
-
- public static FeedItemEvent updated(FeedItem... items) {
- return updated(Arrays.asList(items));
- }
-
- @NonNull
- @Override
- public String toString() {
- return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
- .append("action", action)
- .append("items", items)
- .toString();
- }
-
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/event/FeedListUpdateEvent.java b/core/src/main/java/de/danoeh/antennapod/core/event/FeedListUpdateEvent.java
deleted file mode 100644
index 4ed8e33ec..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/event/FeedListUpdateEvent.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package de.danoeh.antennapod.core.event;
-
-import de.danoeh.antennapod.model.feed.Feed;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class FeedListUpdateEvent {
- private final List<Long> feeds = new ArrayList<>();
-
- public FeedListUpdateEvent(List<Feed> feeds) {
- for (Feed feed : feeds) {
- this.feeds.add(feed.getId());
- }
- }
-
- public FeedListUpdateEvent(Feed feed) {
- feeds.add(feed.getId());
- }
-
- public FeedListUpdateEvent(long feedId) {
- feeds.add(feedId);
- }
-
- public boolean contains(Feed feed) {
- return feeds.contains(feed.getId());
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/event/MessageEvent.java b/core/src/main/java/de/danoeh/antennapod/core/event/MessageEvent.java
deleted file mode 100644
index 9fb22b8ea..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/event/MessageEvent.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package de.danoeh.antennapod.core.event;
-
-import androidx.annotation.Nullable;
-
-public class MessageEvent {
-
- public final String message;
-
- @Nullable
- public final Runnable action;
-
- public MessageEvent(String message) {
- this(message, null);
- }
-
- public MessageEvent(String message, Runnable action) {
- this.message = message;
- this.action = action;
- }
-
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/event/PlaybackHistoryEvent.java b/core/src/main/java/de/danoeh/antennapod/core/event/PlaybackHistoryEvent.java
deleted file mode 100644
index cd3f27bf5..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/event/PlaybackHistoryEvent.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package de.danoeh.antennapod.core.event;
-
-public class PlaybackHistoryEvent {
-
- private PlaybackHistoryEvent() {
- }
-
- public static PlaybackHistoryEvent listUpdated() {
- return new PlaybackHistoryEvent();
- }
-
- @Override
- public String toString() {
- return "PlaybackHistoryEvent";
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/event/PlaybackPositionEvent.java b/core/src/main/java/de/danoeh/antennapod/core/event/PlaybackPositionEvent.java
deleted file mode 100644
index 3327d8a02..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/event/PlaybackPositionEvent.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package de.danoeh.antennapod.core.event;
-
-public class PlaybackPositionEvent {
- private final int position;
- private final int duration;
-
- public PlaybackPositionEvent(int position, int duration) {
- this.position = position;
- this.duration = duration;
- }
-
- public int getPosition() {
- return position;
- }
-
- public int getDuration() {
- return duration;
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/event/PlayerErrorEvent.java b/core/src/main/java/de/danoeh/antennapod/core/event/PlayerErrorEvent.java
deleted file mode 100644
index 2fb27e958..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/event/PlayerErrorEvent.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package de.danoeh.antennapod.core.event;
-
-public class PlayerErrorEvent {
- private final String message;
-
- public PlayerErrorEvent(String message) {
- this.message = message;
- }
-
- public String getMessage() {
- return message;
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/event/PlayerStatusEvent.java b/core/src/main/java/de/danoeh/antennapod/core/event/PlayerStatusEvent.java
deleted file mode 100644
index fe7f17968..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/event/PlayerStatusEvent.java
+++ /dev/null
@@ -1,6 +0,0 @@
-package de.danoeh.antennapod.core.event;
-
-public class PlayerStatusEvent {
- public PlayerStatusEvent() {
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/event/QueueEvent.java b/core/src/main/java/de/danoeh/antennapod/core/event/QueueEvent.java
deleted file mode 100644
index c866939bd..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/event/QueueEvent.java
+++ /dev/null
@@ -1,71 +0,0 @@
-package de.danoeh.antennapod.core.event;
-
-import androidx.annotation.Nullable;
-
-import org.apache.commons.lang3.builder.ToStringBuilder;
-import org.apache.commons.lang3.builder.ToStringStyle;
-
-import java.util.List;
-
-import de.danoeh.antennapod.model.feed.FeedItem;
-
-public class QueueEvent {
-
- public enum Action {
- ADDED, ADDED_ITEMS, SET_QUEUE, REMOVED, IRREVERSIBLE_REMOVED, CLEARED, DELETED_MEDIA, SORTED, MOVED
- }
-
- public final Action action;
- public final FeedItem item;
- public final int position;
- public final List<FeedItem> items;
-
-
- private QueueEvent(Action action,
- @Nullable FeedItem item,
- @Nullable List<FeedItem> items,
- int position) {
- this.action = action;
- this.item = item;
- this.items = items;
- this.position = position;
- }
-
- public static QueueEvent added(FeedItem item, int position) {
- return new QueueEvent(Action.ADDED, item, null, position);
- }
-
- public static QueueEvent setQueue(List<FeedItem> queue) {
- return new QueueEvent(Action.SET_QUEUE, null, queue, -1);
- }
-
- public static QueueEvent removed(FeedItem item) {
- return new QueueEvent(Action.REMOVED, item, null, -1);
- }
-
- public static QueueEvent irreversibleRemoved(FeedItem item) {
- return new QueueEvent(Action.IRREVERSIBLE_REMOVED, item, null, -1);
- }
-
- public static QueueEvent cleared() {
- return new QueueEvent(Action.CLEARED, null, null, -1);
- }
-
- public static QueueEvent sorted(List<FeedItem> sortedQueue) {
- return new QueueEvent(Action.SORTED, null, sortedQueue, -1);
- }
-
- public static QueueEvent moved(FeedItem item, int newPosition) {
- return new QueueEvent(Action.MOVED, item, null, newPosition);
- }
-
- @Override
- public String toString() {
- return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
- .append("action", action)
- .append("item", item)
- .append("items", items)
- .append("position", position)
- .toString();
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/event/ServiceEvent.java b/core/src/main/java/de/danoeh/antennapod/core/event/ServiceEvent.java
deleted file mode 100644
index 2230ee84f..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/event/ServiceEvent.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package de.danoeh.antennapod.core.event;
-
-public class ServiceEvent {
- public enum Action {
- SERVICE_STARTED,
- SERVICE_SHUT_DOWN
- }
-
- public final Action action;
-
- public ServiceEvent(Action action) {
- this.action = action;
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/event/SyncServiceEvent.java b/core/src/main/java/de/danoeh/antennapod/core/event/SyncServiceEvent.java
deleted file mode 100644
index 7aa5f6bf1..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/event/SyncServiceEvent.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package de.danoeh.antennapod.core.event;
-
-public class SyncServiceEvent {
- private final int messageResId;
-
- public SyncServiceEvent(int messageResId) {
- this.messageResId = messageResId;
- }
-
- public int getMessageResId() {
- return messageResId;
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/event/UnreadItemsUpdateEvent.java b/core/src/main/java/de/danoeh/antennapod/core/event/UnreadItemsUpdateEvent.java
deleted file mode 100644
index c3efbfe8b..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/event/UnreadItemsUpdateEvent.java
+++ /dev/null
@@ -1,6 +0,0 @@
-package de.danoeh.antennapod.core.event;
-
-public class UnreadItemsUpdateEvent {
- public UnreadItemsUpdateEvent() {
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/event/settings/SkipIntroEndingChangedEvent.java b/core/src/main/java/de/danoeh/antennapod/core/event/settings/SkipIntroEndingChangedEvent.java
deleted file mode 100644
index 583f7b13f..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/event/settings/SkipIntroEndingChangedEvent.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package de.danoeh.antennapod.core.event.settings;
-
-public class SkipIntroEndingChangedEvent {
- private final int skipIntro;
- private final int skipEnding;
- private final long feedId;
-
- public SkipIntroEndingChangedEvent(int skipIntro, int skipEnding, long feedId) {
- this.skipIntro= skipIntro;
- this.skipEnding = skipEnding;
- this.feedId = feedId;
- }
-
- public int getSkipIntro() {
- return skipIntro;
- }
-
- public int getSkipEnding() {
- return skipEnding;
- }
-
- public long getFeedId() {
- return feedId;
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/event/settings/SpeedPresetChangedEvent.java b/core/src/main/java/de/danoeh/antennapod/core/event/settings/SpeedPresetChangedEvent.java
deleted file mode 100644
index 0ac7e1316..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/event/settings/SpeedPresetChangedEvent.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package de.danoeh.antennapod.core.event.settings;
-
-public class SpeedPresetChangedEvent {
- private final float speed;
- private final long feedId;
-
- public SpeedPresetChangedEvent(float speed, long feedId) {
- this.speed = speed;
- this.feedId = feedId;
- }
-
- public float getSpeed() {
- return speed;
- }
-
- public long getFeedId() {
- return feedId;
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/event/settings/VolumeAdaptionChangedEvent.java b/core/src/main/java/de/danoeh/antennapod/core/event/settings/VolumeAdaptionChangedEvent.java
deleted file mode 100644
index 3905ce68f..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/event/settings/VolumeAdaptionChangedEvent.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package de.danoeh.antennapod.core.event.settings;
-
-import de.danoeh.antennapod.model.feed.VolumeAdaptionSetting;
-
-public class VolumeAdaptionChangedEvent {
- private final VolumeAdaptionSetting volumeAdaptionSetting;
- private final long feedId;
-
- public VolumeAdaptionChangedEvent(VolumeAdaptionSetting volumeAdaptionSetting, long feedId) {
- this.volumeAdaptionSetting = volumeAdaptionSetting;
- this.feedId = feedId;
- }
-
- public VolumeAdaptionSetting getVolumeAdaptionSetting() {
- return volumeAdaptionSetting;
- }
-
- public long getFeedId() {
- return feedId;
- }
-}
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 82583b7b5..5d685c24f 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
@@ -178,7 +178,7 @@ public class LocalFeedUpdater {
private static FeedItem createFeedItem(Feed feed, DocumentFile file, Context context) {
FeedItem item = new FeedItem(0, file.getName(), UUID.randomUUID().toString(),
file.getName(), new Date(file.lastModified()), FeedItem.UNPLAYED, feed);
- item.setAutoDownload(false);
+ item.disableAutoDownload();
long size = file.length();
FeedMedia media = new FeedMedia(0, item, 0, 0, size, file.getType(),
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 defe6c9f8..9b06d2138 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
@@ -11,7 +11,6 @@ import com.bumptech.glide.Registry;
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.module.AppGlideModule;
import de.danoeh.antennapod.model.feed.EmbeddedChapterImage;
@@ -43,7 +42,7 @@ public class ApGlideModule extends AppGlideModule {
public void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) {
registry.replace(String.class, InputStream.class, new MetadataRetrieverLoader.Factory(context));
registry.append(String.class, InputStream.class, new ApOkHttpUrlLoader.Factory());
- registry.append(String.class, InputStream.class, new StringLoader.StreamFactory());
+ registry.append(String.class, InputStream.class, new NoHttpStringLoader.StreamFactory());
registry.append(EmbeddedChapterImage.class, ByteBuffer.class, new ChapterImageModelLoader.Factory());
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/glide/NoHttpStringLoader.java b/core/src/main/java/de/danoeh/antennapod/core/glide/NoHttpStringLoader.java
new file mode 100644
index 000000000..9cda3b1aa
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/glide/NoHttpStringLoader.java
@@ -0,0 +1,39 @@
+package de.danoeh.antennapod.core.glide;
+
+import android.net.Uri;
+import androidx.annotation.NonNull;
+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.load.model.StringLoader;
+
+import java.io.InputStream;
+
+/**
+ * StringLoader that does not handle http/https urls. Used to avoid fallback to StringLoader when
+ * AntennaPod blocks mobile image loading.
+ */
+public final class NoHttpStringLoader extends StringLoader<InputStream> {
+
+ public static class StreamFactory implements ModelLoaderFactory<String, InputStream> {
+ @NonNull
+ @Override
+ public ModelLoader<String, InputStream> build(@NonNull MultiModelLoaderFactory multiFactory) {
+ return new NoHttpStringLoader(multiFactory.build(Uri.class, InputStream.class));
+ }
+
+ @Override
+ public void teardown() {
+ // Do nothing.
+ }
+ }
+
+ public NoHttpStringLoader(ModelLoader<Uri, InputStream> uriLoader) {
+ super(uriLoader);
+ }
+
+ @Override
+ public boolean handles(@NonNull String model) {
+ return !model.startsWith("http") && super.handles(model);
+ }
+}
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 9c73ed9ae..8d80ef32b 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
@@ -5,7 +5,7 @@ import android.content.SharedPreferences;
import androidx.preference.PreferenceManager;
import android.util.Log;
-import de.danoeh.antennapod.core.event.PlayerStatusEvent;
+import de.danoeh.antennapod.event.PlayerStatusEvent;
import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.model.playback.MediaType;
import de.danoeh.antennapod.core.service.playback.PlayerStatus;
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 9bfad9959..c61dafd1b 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
@@ -85,7 +85,7 @@ public class UserPreferences {
private static final String PREF_AUTO_DELETE = "prefAutoDelete";
public static final String PREF_SMART_MARK_AS_PLAYED_SECS = "prefSmartMarkAsPlayedSecs";
private static final String PREF_PLAYBACK_SPEED_ARRAY = "prefPlaybackSpeedArray";
- private static final String PREF_PAUSE_PLAYBACK_FOR_FOCUS_LOSS = "prefPauseForFocusLoss";
+ public static final String PREF_PAUSE_PLAYBACK_FOR_FOCUS_LOSS = "prefPauseForFocusLoss";
private static final String PREF_RESUME_AFTER_CALL = "prefResumeAfterCall";
public static final String PREF_VIDEO_BEHAVIOR = "prefVideoBehavior";
private static final String PREF_TIME_RESPECTS_SPEED = "prefPlaybackTimeRespectsSpeed";
@@ -467,7 +467,7 @@ public class UserPreferences {
}
public static boolean shouldPauseForFocusLoss() {
- return prefs.getBoolean(PREF_PAUSE_PLAYBACK_FOR_FOCUS_LOSS, false);
+ return prefs.getBoolean(PREF_PAUSE_PLAYBACK_FOR_FOCUS_LOSS, true);
}
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 2a1aef6cc..2da0c2db4 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
@@ -39,7 +39,7 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import de.danoeh.antennapod.core.event.DownloadEvent;
-import de.danoeh.antennapod.core.event.FeedItemEvent;
+import de.danoeh.antennapod.event.FeedItemEvent;
import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.model.feed.FeedMedia;
@@ -321,18 +321,8 @@ public class DownloadService extends Service {
if (item == null) {
return;
}
- 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) {
- Log.d(TAG, "Ignoring exception while setting item download status");
- e.printStackTrace();
- }
- }
+ item.increaseFailedAutoDownloadAttempts(System.currentTimeMillis());
+ DBWriter.setFeedItem(item);
// to make lists reload the failed item, we fake an item update
EventBus.getDefault().post(FeedItemEvent.updated(item));
}
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 781110f82..cbfb2cede 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,6 +4,7 @@ import androidx.annotation.NonNull;
import android.text.TextUtils;
import android.util.Log;
+import de.danoeh.antennapod.core.util.NetworkUtils;
import okhttp3.CacheControl;
import org.apache.commons.io.IOUtils;
@@ -19,8 +20,6 @@ import java.net.URI;
import java.net.UnknownHostException;
import java.util.Collections;
import java.util.Date;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
import de.danoeh.antennapod.core.R;
import de.danoeh.antennapod.model.feed.FeedMedia;
@@ -39,7 +38,6 @@ public class HttpDownloader extends Downloader {
private static final String TAG = "HttpDownloader";
private static final int BUFFER_SIZE = 8 * 1024;
- private static final String REGEX_PATTERN_IP_ADDRESS = "([0-9]{1,3}[\\.]){3}[0-9]{1,3}";
public HttpDownloader(@NonNull DownloadRequest request) {
super(request);
@@ -259,21 +257,14 @@ public class HttpDownloader extends Downloader {
onFail(DownloadError.ERROR_UNKNOWN_HOST, e.getMessage());
} catch (IOException e) {
e.printStackTrace();
+ if (NetworkUtils.wasDownloadBlocked(e)) {
+ onFail(DownloadError.ERROR_IO_BLOCKED, e.getMessage());
+ return;
+ }
String message = e.getMessage();
- if (message != null) {
- // Try to parse message for a more detailed error message
- Pattern pattern = Pattern.compile(REGEX_PATTERN_IP_ADDRESS);
- Matcher matcher = pattern.matcher(message);
- if (matcher.find()) {
- String ip = matcher.group();
- if (ip.startsWith("127.") || ip.startsWith("0.")) {
- onFail(DownloadError.ERROR_IO_BLOCKED, e.getMessage());
- return;
- }
- } else if (message.contains("Trust anchor for certification path not found")) {
- onFail(DownloadError.ERROR_CERTIFICATE, e.getMessage());
- return;
- }
+ if (message != null && message.contains("Trust anchor for certification path not found")) {
+ onFail(DownloadError.ERROR_CERTIFICATE, e.getMessage());
+ return;
}
onFail(DownloadError.ERROR_IO_ERROR, e.getMessage());
} catch (NullPointerException e) {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/NewEpisodesNotification.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/NewEpisodesNotification.java
index 63e005927..f7ed049cd 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/NewEpisodesNotification.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/NewEpisodesNotification.java
@@ -8,6 +8,7 @@ import android.content.Intent;
import android.content.res.Resources;
import android.graphics.Bitmap;
+import android.os.Build;
import android.util.Log;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
@@ -68,7 +69,8 @@ public class NewEpisodesNotification {
intent.setComponent(new ComponentName(context, "de.danoeh.antennapod.activity.MainActivity"));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
intent.putExtra("fragment_feed_id", feed.getId());
- PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
+ PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent,
+ (Build.VERSION.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0));
Notification notification = new NotificationCompat.Builder(
context, NotificationUtils.CHANNEL_ID_EPISODE_NOTIFICATIONS)
@@ -93,7 +95,8 @@ public class NewEpisodesNotification {
intent.setComponent(new ComponentName(context, "de.danoeh.antennapod.activity.MainActivity"));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
intent.putExtra("fragment_tag", "EpisodesFragment");
- PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
+ PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent,
+ (Build.VERSION.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0));
Notification notificationGroupSummary = new NotificationCompat.Builder(
context, NotificationUtils.CHANNEL_ID_EPISODE_NOTIFICATIONS)
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/MediaDownloadedHandler.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/MediaDownloadedHandler.java
index 6bbd704e2..541e17cf6 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/MediaDownloadedHandler.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/MediaDownloadedHandler.java
@@ -11,7 +11,7 @@ import org.greenrobot.eventbus.EventBus;
import java.io.File;
import java.util.concurrent.ExecutionException;
-import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
+import de.danoeh.antennapod.event.UnreadItemsUpdateEvent;
import de.danoeh.antennapod.core.service.download.DownloadRequest;
import de.danoeh.antennapod.core.service.download.DownloadStatus;
import de.danoeh.antennapod.core.storage.DBReader;
@@ -83,7 +83,7 @@ public class MediaDownloadedHandler implements Runnable {
// we've received the media, we don't want to autodownload it again
if (item != null) {
- item.setAutoDownload(false);
+ item.disableAutoDownload();
// setFeedItem() signals (via EventBus) that the item has been updated,
// so we do it after the enclosing media has been updated above,
// to ensure subscribers will get the updated FeedMedia as well
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 30e76787d..79363e872 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
@@ -17,6 +17,7 @@ import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.SeekParameters;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.audio.AudioAttributes;
+import com.google.android.exoplayer2.ext.okhttp.OkHttpDataSourceFactory;
import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory;
import com.google.android.exoplayer2.extractor.mp3.Mp3Extractor;
import com.google.android.exoplayer2.source.MediaSource;
@@ -30,12 +31,14 @@ import com.google.android.exoplayer2.ui.DefaultTrackNameProvider;
import com.google.android.exoplayer2.ui.TrackNameProvider;
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 com.google.android.exoplayer2.upstream.HttpDataSource;
import de.danoeh.antennapod.core.ClientConfig;
+import de.danoeh.antennapod.core.R;
import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
import de.danoeh.antennapod.core.service.download.HttpDownloader;
+import de.danoeh.antennapod.core.util.NetworkUtils;
import de.danoeh.antennapod.core.util.playback.IPlayer;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
@@ -102,9 +105,15 @@ public class ExoPlayerWrapper implements IPlayer {
@Override
public void onPlayerError(@NonNull ExoPlaybackException error) {
if (audioErrorListener != null) {
- Throwable cause = error.getCause();
- audioErrorListener.accept(cause != null
- ? cause.getLocalizedMessage() : error.getLocalizedMessage());
+ if (NetworkUtils.wasDownloadBlocked(error)) {
+ audioErrorListener.accept(context.getString(R.string.download_error_blocked));
+ } else {
+ Throwable cause = error.getCause();
+ if (cause instanceof HttpDataSource.HttpDataSourceException) {
+ cause = cause.getCause();
+ }
+ audioErrorListener.accept(cause != null ? cause.getMessage() : error.getMessage());
+ }
}
}
@@ -194,18 +203,12 @@ public class ExoPlayerWrapper implements IPlayer {
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);
+ OkHttpDataSourceFactory httpDataSourceFactory = new OkHttpDataSourceFactory(
+ AntennapodHttpClient.getHttpClient(), ClientConfig.USER_AGENT);
if (!TextUtils.isEmpty(user) && !TextUtils.isEmpty(password)) {
httpDataSourceFactory.getDefaultRequestProperties().set("Authorization",
- HttpDownloader.encodeCredentials(
- user,
- password,
- "ISO-8859-1"));
+ HttpDownloader.encodeCredentials(user, password, "ISO-8859-1"));
}
DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(context, null, httpDataSourceFactory);
DefaultExtractorsFactory extractorsFactory = new DefaultExtractorsFactory();
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 90b3b6ae2..5648024de 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
@@ -14,7 +14,9 @@ import android.view.SurfaceHolder;
import androidx.media.AudioAttributesCompat;
import androidx.media.AudioFocusRequestCompat;
import androidx.media.AudioManagerCompat;
-import de.danoeh.antennapod.core.event.PlayerErrorEvent;
+import de.danoeh.antennapod.event.PlayerErrorEvent;
+import de.danoeh.antennapod.event.playback.BufferUpdateEvent;
+import de.danoeh.antennapod.event.playback.SpeedChangedEvent;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.util.playback.MediaPlayerError;
import org.antennapod.audio.MediaPlayer;
@@ -71,6 +73,7 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
private final PlayerLock playerLock;
private final PlayerExecutor executor;
private boolean useCallerThread = true;
+ private boolean isShutDown = false;
private CountDownLatch seekLatch;
@@ -616,7 +619,7 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
private void setSpeedSyncAndSkipSilence(float speed, boolean skipSilence) {
playerLock.lock();
Log.d(TAG, "Playback speed was set to " + speed);
- callback.playbackSpeedChanged(speed);
+ EventBus.getDefault().post(new SpeedChangedEvent(speed));
mediaPlayer.setPlaybackParams(speed, skipSilence);
playerLock.unlock();
}
@@ -717,32 +720,22 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
*/
@Override
public void shutdown() {
- executor.shutdown();
if (mediaPlayer != null) {
try {
- removeMediaPlayerErrorListener();
+ clearMediaPlayerListeners();
if (mediaPlayer.isPlaying()) {
mediaPlayer.stop();
}
} catch (Exception ignore) { }
mediaPlayer.release();
+ mediaPlayer = null;
}
+ isShutDown = true;
+ executor.shutdown();
+ abandonAudioFocus();
releaseWifiLockIfNecessary();
}
- private void removeMediaPlayerErrorListener() {
- if (mediaPlayer instanceof VideoPlayer) {
- VideoPlayer vp = (VideoPlayer) mediaPlayer;
- vp.setOnErrorListener((mp, what, extra) -> true);
- } else if (mediaPlayer instanceof AudioPlayer) {
- AudioPlayer ap = (AudioPlayer) mediaPlayer;
- ap.setOnErrorListener((mediaPlayer, i, i1) -> true);
- } else if (mediaPlayer instanceof ExoPlayerWrapper) {
- ExoPlayerWrapper ap = (ExoPlayerWrapper) mediaPlayer;
- ap.setOnErrorListener(message -> { });
- }
- }
-
/**
* Releases internally used resources. This method should only be called when the object is not used anymore.
* This method is executed on an internal executor service.
@@ -862,10 +855,14 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
@Override
public void onAudioFocusChange(final int focusChange) {
+ if (isShutDown) {
+ return;
+ }
if (!PlaybackService.isRunning) {
abandonAudioFocus();
Log.d(TAG, "onAudioFocusChange: PlaybackService is no longer running");
if (focusChange == AudioManager.AUDIOFOCUS_GAIN && pausedBecauseOfTransientAudiofocusLoss) {
+ pausedBecauseOfTransientAudiofocusLoss = false;
new PlaybackServiceStarter(context, getPlayable())
.startWhenPrepared(true)
.streamIfLastWasStream()
@@ -1009,9 +1006,9 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
return stream;
}
- private IPlayer setMediaPlayerListeners(IPlayer mp) {
+ private void setMediaPlayerListeners(IPlayer mp) {
if (mp == null || media == null) {
- return mp;
+ return;
}
if (mp instanceof VideoPlayer) {
if (media.getMediaType() != MediaType.VIDEO) {
@@ -1043,7 +1040,31 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
} else {
Log.w(TAG, "Unknown media player: " + mp);
}
- return mp;
+ }
+
+ private void clearMediaPlayerListeners() {
+ if (mediaPlayer instanceof VideoPlayer) {
+ VideoPlayer vp = (VideoPlayer) mediaPlayer;
+ vp.setOnCompletionListener(x -> { });
+ vp.setOnSeekCompleteListener(x -> { });
+ vp.setOnErrorListener((mediaPlayer, i, i1) -> false);
+ vp.setOnBufferingUpdateListener((mediaPlayer, i) -> { });
+ vp.setOnInfoListener((mediaPlayer, i, i1) -> false);
+ } else if (mediaPlayer instanceof AudioPlayer) {
+ AudioPlayer ap = (AudioPlayer) mediaPlayer;
+ ap.setOnCompletionListener(x -> { });
+ ap.setOnSeekCompleteListener(x -> { });
+ ap.setOnErrorListener((x, y, z) -> false);
+ ap.setOnBufferingUpdateListener((arg0, percent) -> { });
+ ap.setOnInfoListener((arg0, what, extra) -> false);
+ } else if (mediaPlayer instanceof ExoPlayerWrapper) {
+ ExoPlayerWrapper ap = (ExoPlayerWrapper) mediaPlayer;
+ ap.setOnCompletionListener(x -> { });
+ ap.setOnSeekCompleteListener(x -> { });
+ ap.setOnBufferingUpdateListener((arg0, percent) -> { });
+ ap.setOnErrorListener(x -> { });
+ ap.setOnInfoListener((arg0, what, extra) -> false);
+ }
}
private final MediaPlayer.OnCompletionListener audioCompletionListener =
@@ -1057,14 +1078,10 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
}
private final MediaPlayer.OnBufferingUpdateListener audioBufferingUpdateListener =
- (mp, percent) -> genericOnBufferingUpdate(percent);
+ (mp, percent) -> EventBus.getDefault().post(BufferUpdateEvent.progressUpdate(0.01f * percent));
private final android.media.MediaPlayer.OnBufferingUpdateListener videoBufferingUpdateListener =
- (mp, percent) -> genericOnBufferingUpdate(percent);
-
- private void genericOnBufferingUpdate(int percent) {
- callback.onBufferingUpdate(percent);
- }
+ (mp, percent) -> EventBus.getDefault().post(BufferUpdateEvent.progressUpdate(0.01f * percent));
private final MediaPlayer.OnInfoListener audioInfoListener =
(mp, what, extra) -> genericInfoListener(what);
@@ -1073,7 +1090,16 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
(mp, what, extra) -> genericInfoListener(what);
private boolean genericInfoListener(int what) {
- return callback.onMediaPlayerInfo(what, 0);
+ switch (what) {
+ case android.media.MediaPlayer.MEDIA_INFO_BUFFERING_START:
+ EventBus.getDefault().post(BufferUpdateEvent.started());
+ return true;
+ case android.media.MediaPlayer.MEDIA_INFO_BUFFERING_END:
+ EventBus.getDefault().post(BufferUpdateEvent.ended());
+ return true;
+ default:
+ return callback.onMediaPlayerInfo(what, 0);
+ }
}
private final MediaPlayer.OnErrorListener audioErrorListener =
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 49a4fb021..415b9f1ed 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
@@ -16,7 +16,6 @@ import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.media.AudioManager;
-import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
@@ -36,6 +35,7 @@ import android.view.SurfaceHolder;
import android.webkit.URLUtil;
import android.widget.Toast;
+import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
import androidx.core.app.NotificationCompat;
@@ -43,7 +43,10 @@ import androidx.core.app.NotificationManagerCompat;
import androidx.media.MediaBrowserServiceCompat;
import androidx.preference.PreferenceManager;
-import de.danoeh.antennapod.core.event.PlayerErrorEvent;
+import de.danoeh.antennapod.event.playback.BufferUpdateEvent;
+import de.danoeh.antennapod.event.playback.PlaybackServiceEvent;
+import de.danoeh.antennapod.event.PlayerErrorEvent;
+import de.danoeh.antennapod.event.playback.SleepTimerUpdatedEvent;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
@@ -54,12 +57,11 @@ import java.util.List;
import java.util.concurrent.TimeUnit;
import de.danoeh.antennapod.core.R;
-import de.danoeh.antennapod.core.event.MessageEvent;
-import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
-import de.danoeh.antennapod.core.event.ServiceEvent;
-import de.danoeh.antennapod.core.event.settings.SkipIntroEndingChangedEvent;
-import de.danoeh.antennapod.core.event.settings.SpeedPresetChangedEvent;
-import de.danoeh.antennapod.core.event.settings.VolumeAdaptionChangedEvent;
+import de.danoeh.antennapod.event.MessageEvent;
+import de.danoeh.antennapod.event.playback.PlaybackPositionEvent;
+import de.danoeh.antennapod.event.settings.SkipIntroEndingChangedEvent;
+import de.danoeh.antennapod.event.settings.SpeedPresetChangedEvent;
+import de.danoeh.antennapod.event.settings.VolumeAdaptionChangedEvent;
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
import de.danoeh.antennapod.core.preferences.SleepTimerPreferences;
import de.danoeh.antennapod.core.preferences.UserPreferences;
@@ -162,18 +164,10 @@ public class PlaybackService extends MediaBrowserServiceCompat {
public static final int EXTRA_CODE_VIDEO = 2;
public static final int EXTRA_CODE_CAST = 3;
- public static final int NOTIFICATION_TYPE_BUFFER_UPDATE = 2;
-
/**
* Receivers of this intent should update their information about the curently playing media
*/
public static final int NOTIFICATION_TYPE_RELOAD = 3;
- /**
- * The state of the sleeptimer changed.
- */
- public static final int NOTIFICATION_TYPE_SLEEPTIMER_UPDATE = 4;
- public static final int NOTIFICATION_TYPE_BUFFER_START = 5;
- public static final int NOTIFICATION_TYPE_BUFFER_END = 6;
/**
* Set a max number of episodes to load for Android Auto, otherwise there could be performance issues
@@ -186,11 +180,6 @@ public class PlaybackService extends MediaBrowserServiceCompat {
public static final int NOTIFICATION_TYPE_PLAYBACK_END = 7;
/**
- * Playback speed has changed
- */
- public static final int NOTIFICATION_TYPE_PLAYBACK_SPEED_CHANGE = 8;
-
- /**
* Returned by getPositionSafe() or getDurationSafe() if the playbackService
* is in an invalid state.
*/
@@ -298,7 +287,8 @@ public class PlaybackService extends MediaBrowserServiceCompat {
ComponentName eventReceiver = new ComponentName(getApplicationContext(), MediaButtonReceiver.class);
Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
mediaButtonIntent.setComponent(eventReceiver);
- PendingIntent buttonReceiverIntent = PendingIntent.getBroadcast(this, 0, mediaButtonIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+ PendingIntent buttonReceiverIntent = PendingIntent.getBroadcast(this, 0, mediaButtonIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT | (Build.VERSION.SDK_INT >= 31 ? PendingIntent.FLAG_MUTABLE : 0));
mediaSession = new MediaSessionCompat(getApplicationContext(), TAG, eventReceiver, buttonReceiverIntent);
setSessionToken(mediaSession.getSessionToken());
@@ -318,7 +308,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
flavorHelper.initializeMediaPlayer(PlaybackService.this);
mediaSession.setActive(true);
- EventBus.getDefault().post(new ServiceEvent(ServiceEvent.Action.SERVICE_STARTED));
+ EventBus.getDefault().post(new PlaybackServiceEvent(PlaybackServiceEvent.Action.SERVICE_STARTED));
}
@Override
@@ -378,30 +368,22 @@ public class PlaybackService extends MediaBrowserServiceCompat {
.subscribe(queueItems -> mediaSession.setQueue(queueItems), Throwable::printStackTrace);
}
- private MediaBrowserCompat.MediaItem createBrowsableMediaItemForRoot() {
+ private MediaBrowserCompat.MediaItem createBrowsableMediaItem(
+ @StringRes int title, @DrawableRes int icon, int numEpisodes) {
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))
+ .authority(getResources().getResourcePackageName(icon))
+ .appendPath(getResources().getResourceTypeName(icon))
+ .appendPath(getResources().getResourceEntryName(icon))
.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)
+ .setMediaId(getResources().getString(title))
+ .setTitle(getResources().getString(title))
+ .setSubtitle(getResources().getQuantityString(R.plurals.num_episodes, numEpisodes, numEpisodes))
.build();
- return new MediaBrowserCompat.MediaItem(description,
- MediaBrowserCompat.MediaItem.FLAG_BROWSABLE);
+ return new MediaBrowserCompat.MediaItem(description, MediaBrowserCompat.MediaItem.FLAG_BROWSABLE);
}
private MediaBrowserCompat.MediaItem createBrowsableMediaItemForFeed(Feed feed) {
@@ -433,46 +415,47 @@ public class PlaybackService extends MediaBrowserServiceCompat {
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
- .subscribe(() -> { }, Throwable::printStackTrace);
+ .subscribe(
+ () -> {
+ }, e -> {
+ e.printStackTrace();
+ result.sendResult(null);
+ });
}
- private List<MediaBrowserCompat.MediaItem> loadChildrenSynchronous(@NonNull String parentId) {
+ private List<MediaBrowserCompat.MediaItem> loadChildrenSynchronous(@NonNull String parentId)
+ throws InterruptedException {
List<MediaBrowserCompat.MediaItem> mediaItems = new ArrayList<>();
if (parentId.equals(getResources().getString(R.string.app_name))) {
- // Root List
- try {
- if (!(taskManager.getQueue().isEmpty())) {
- mediaItems.add(createBrowsableMediaItemForRoot());
- }
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
+ mediaItems.add(createBrowsableMediaItem(R.string.queue_label, R.drawable.ic_playlist_black,
+ taskManager.getQueue().size()));
+ mediaItems.add(createBrowsableMediaItem(R.string.downloads_label, R.drawable.ic_download_black,
+ DBReader.getDownloadedItems().size()));
List<Feed> feeds = DBReader.getFeedList();
for (Feed feed : feeds) {
mediaItems.add(createBrowsableMediaItemForFeed(feed));
}
- } else if (parentId.equals(getResources().getString(R.string.queue_label))) {
- // Child List
- try {
- for (FeedItem feedItem : taskManager.getQueue()) {
- FeedMedia media = feedItem.getMedia();
- if (media != null) {
- mediaItems.add(media.getMediaItem());
- }
- }
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
+ return mediaItems;
+ }
+
+ List<FeedItem> feedItems;
+ if (parentId.equals(getResources().getString(R.string.queue_label))) {
+ feedItems = taskManager.getQueue();
+ } else if (parentId.equals(getResources().getString(R.string.downloads_label))) {
+ feedItems = DBReader.getDownloadedItems();
} else if (parentId.startsWith("FeedId:")) {
long feedId = Long.parseLong(parentId.split(":")[1]);
- List<FeedItem> feedItems = DBReader.getFeedItemList(DBReader.getFeed(feedId));
- int count = 0;
- for (FeedItem feedItem : feedItems) {
- if (feedItem.getMedia() != null && feedItem.getMedia().getMediaItem() != null) {
- mediaItems.add(feedItem.getMedia().getMediaItem());
- if (++count >= MAX_ANDROID_AUTO_EPISODES_PER_FEED) {
- break;
- }
+ feedItems = DBReader.getFeedItemList(DBReader.getFeed(feedId));
+ } else {
+ Log.e(TAG, "Parent ID not found: " + parentId);
+ return null;
+ }
+ int count = 0;
+ for (FeedItem feedItem : feedItems) {
+ if (feedItem.getMedia() != null && feedItem.getMedia().getMediaItem() != null) {
+ mediaItems.add(feedItem.getMedia().getMediaItem());
+ if (++count >= MAX_ANDROID_AUTO_EPISODES_PER_FEED) {
+ break;
}
}
}
@@ -610,10 +593,12 @@ public class PlaybackService extends MediaBrowserServiceCompat {
PendingIntent pendingIntentAllowThisTime;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
pendingIntentAllowThisTime = PendingIntent.getForegroundService(this,
- R.id.pending_intent_allow_stream_this_time, intentAllowThisTime, PendingIntent.FLAG_UPDATE_CURRENT);
+ R.id.pending_intent_allow_stream_this_time, intentAllowThisTime,
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
} else {
pendingIntentAllowThisTime = PendingIntent.getService(this,
- R.id.pending_intent_allow_stream_this_time, intentAllowThisTime, PendingIntent.FLAG_UPDATE_CURRENT);
+ R.id.pending_intent_allow_stream_this_time, intentAllowThisTime, PendingIntent.FLAG_UPDATE_CURRENT
+ | (Build.VERSION.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0));
}
Intent intentAlwaysAllow = new Intent(intentAllowThisTime);
@@ -622,10 +607,12 @@ public class PlaybackService extends MediaBrowserServiceCompat {
PendingIntent pendingIntentAlwaysAllow;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
pendingIntentAlwaysAllow = PendingIntent.getForegroundService(this,
- R.id.pending_intent_allow_stream_always, intentAlwaysAllow, PendingIntent.FLAG_UPDATE_CURRENT);
+ R.id.pending_intent_allow_stream_always, intentAlwaysAllow,
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
} else {
pendingIntentAlwaysAllow = PendingIntent.getService(this,
- R.id.pending_intent_allow_stream_always, intentAlwaysAllow, PendingIntent.FLAG_UPDATE_CURRENT);
+ R.id.pending_intent_allow_stream_always, intentAlwaysAllow, PendingIntent.FLAG_UPDATE_CURRENT
+ | (Build.VERSION.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0));
}
NotificationCompat.Builder builder = new NotificationCompat.Builder(this,
@@ -794,25 +781,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
saveCurrentPosition(true, null, PlaybackServiceMediaPlayer.INVALID_TIME);
}
- @Override
- public void onSleepTimerAlmostExpired(long timeLeft) {
- final float[] multiplicators = {0.1f, 0.2f, 0.3f, 0.3f, 0.3f, 0.4f, 0.4f, 0.4f, 0.6f, 0.8f};
- float multiplicator = multiplicators[Math.max(0, (int) timeLeft / 1000)];
- Log.d(TAG, "onSleepTimerAlmostExpired: " + multiplicator);
- mediaPlayer.setVolume(multiplicator, multiplicator);
- }
- @Override
- public void onSleepTimerExpired() {
- mediaPlayer.pause(true, true);
- mediaPlayer.setVolume(1.0f, 1.0f);
- sendNotificationBroadcast(NOTIFICATION_TYPE_SLEEPTIMER_UPDATE, 0);
- }
-
- @Override
- public void onSleepTimerReset() {
- mediaPlayer.setVolume(1.0f, 1.0f);
- }
@Override
public WidgetUpdater.WidgetState requestWidgetState() {
@@ -896,16 +865,6 @@ public class PlaybackService extends MediaBrowserServiceCompat {
}
@Override
- public void playbackSpeedChanged(float s) {
- sendNotificationBroadcast(NOTIFICATION_TYPE_PLAYBACK_SPEED_CHANGE, 0);
- }
-
- @Override
- public void onBufferingUpdate(int percent) {
- sendNotificationBroadcast(NOTIFICATION_TYPE_BUFFER_UPDATE, percent);
- }
-
- @Override
public void onMediaChanged(boolean reloadUI) {
Log.d(TAG, "reloadUI callback reached");
if (reloadUI) {
@@ -916,26 +875,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
@Override
public boolean onMediaPlayerInfo(int code, @StringRes int resourceId) {
- switch (code) {
- case MediaPlayer.MEDIA_INFO_BUFFERING_START:
- sendNotificationBroadcast(NOTIFICATION_TYPE_BUFFER_START, 0);
- return true;
- case MediaPlayer.MEDIA_INFO_BUFFERING_END:
- sendNotificationBroadcast(NOTIFICATION_TYPE_BUFFER_END, 0);
-
- Playable playable = getPlayable();
- if (getPlayable() instanceof FeedMedia
- && playable.getDuration() <= 0 && mediaPlayer.getDuration() > 0) {
- // Playable is being streamed and does not have a duration specified in the feed
- playable.setDuration(mediaPlayer.getDuration());
- DBWriter.setFeedMedia((FeedMedia) playable);
- updateNotificationAndMediaSession(playable);
- }
-
- return true;
- default:
- return flavorHelper.onMediaPlayerInfo(PlaybackService.this, code, resourceId);
- }
+ return flavorHelper.onMediaPlayerInfo(PlaybackService.this, code, resourceId);
}
@Override
@@ -993,6 +933,37 @@ public class PlaybackService extends MediaBrowserServiceCompat {
stateManager.stopService();
}
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ @SuppressWarnings("unused")
+ public void bufferUpdate(BufferUpdateEvent event) {
+ if (event.hasEnded()) {
+ Playable playable = getPlayable();
+ if (getPlayable() instanceof FeedMedia
+ && playable.getDuration() <= 0 && mediaPlayer.getDuration() > 0) {
+ // Playable is being streamed and does not have a duration specified in the feed
+ playable.setDuration(mediaPlayer.getDuration());
+ DBWriter.setFeedMedia((FeedMedia) playable);
+ updateNotificationAndMediaSession(playable);
+ }
+ }
+ }
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ @SuppressWarnings("unused")
+ public void sleepTimerUpdate(SleepTimerUpdatedEvent event) {
+ if (event.isOver()) {
+ mediaPlayer.pause(true, true);
+ mediaPlayer.setVolume(1.0f, 1.0f);
+ } else if (event.getTimeLeft() < PlaybackServiceTaskManager.SleepTimer.NOTIFICATION_THRESHOLD) {
+ final float[] multiplicators = {0.1f, 0.2f, 0.3f, 0.3f, 0.3f, 0.4f, 0.4f, 0.4f, 0.6f, 0.8f};
+ float multiplicator = multiplicators[Math.max(0, (int) event.getTimeLeft() / 1000)];
+ Log.d(TAG, "onSleepTimerAlmostExpired: " + multiplicator);
+ mediaPlayer.setVolume(multiplicator, multiplicator);
+ } else if (event.isCancelled()) {
+ mediaPlayer.setVolume(1.0f, 1.0f);
+ }
+ }
+
private Playable getNextInQueue(final Playable currentMedia) {
if (!(currentMedia instanceof FeedMedia)) {
Log.d(TAG, "getNextInQueue(), but playable not an instance of FeedMedia, so not proceeding");
@@ -1158,12 +1129,10 @@ public class PlaybackService extends MediaBrowserServiceCompat {
public void setSleepTimer(long waitingTime) {
Log.d(TAG, "Setting sleep timer to " + waitingTime + " milliseconds");
taskManager.setSleepTimer(waitingTime);
- sendNotificationBroadcast(NOTIFICATION_TYPE_SLEEPTIMER_UPDATE, 0);
}
public void disableSleepTimer() {
taskManager.disableSleepTimer();
- sendNotificationBroadcast(NOTIFICATION_TYPE_SLEEPTIMER_UPDATE, 0);
}
private void sendNotificationBroadcast(int type, int code) {
@@ -1332,7 +1301,8 @@ public class PlaybackService extends MediaBrowserServiceCompat {
if (stateManager.hasReceivedValidStartCommand()) {
mediaSession.setSessionActivity(PendingIntent.getActivity(this, R.id.pending_intent_player_activity,
- PlaybackService.getPlayerActivityIntent(this), PendingIntent.FLAG_UPDATE_CURRENT));
+ PlaybackService.getPlayerActivityIntent(this), PendingIntent.FLAG_UPDATE_CURRENT
+ | (Build.VERSION.SDK_INT >= 31 ? PendingIntent.FLAG_MUTABLE : 0)));
try {
mediaSession.setMetadata(builder.build());
} catch (OutOfMemoryError e) {
@@ -1578,7 +1548,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
@Override
public void onReceive(Context context, Intent intent) {
if (TextUtils.equals(intent.getAction(), ACTION_SHUTDOWN_PLAYBACK_SERVICE)) {
- EventBus.getDefault().post(new ServiceEvent(ServiceEvent.Action.SERVICE_SHUT_DOWN));
+ EventBus.getDefault().post(new PlaybackServiceEvent(PlaybackServiceEvent.Action.SERVICE_SHUT_DOWN));
stateManager.stopService();
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceMediaPlayer.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceMediaPlayer.java
index 2aeb84cb0..623ad58bb 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceMediaPlayer.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceMediaPlayer.java
@@ -348,10 +348,6 @@ public abstract class PlaybackServiceMediaPlayer {
void shouldStop();
- void playbackSpeedChanged(float s);
-
- void onBufferingUpdate(int percent);
-
void onMediaChanged(boolean reloadUI);
boolean onMediaPlayerInfo(int code, @StringRes int resourceId);
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 e7dea192a..5aee8c24c 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
@@ -170,7 +170,8 @@ public class PlaybackServiceNotificationBuilder {
private PendingIntent getPlayerActivityPendingIntent() {
return PendingIntent.getActivity(context, R.id.pending_intent_player_activity,
- PlaybackService.getPlayerActivityIntent(context), PendingIntent.FLAG_UPDATE_CURRENT);
+ PlaybackService.getPlayerActivityIntent(context), PendingIntent.FLAG_UPDATE_CURRENT
+ | (Build.VERSION.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0));
}
private void addActions(NotificationCompat.Builder notification, MediaSessionCompat.Token mediaSessionToken,
@@ -183,7 +184,8 @@ public class PlaybackServiceNotificationBuilder {
Intent stopCastingIntent = new Intent(context, PlaybackService.class);
stopCastingIntent.putExtra(PlaybackService.EXTRA_CAST_DISCONNECT, true);
PendingIntent stopCastingPendingIntent = PendingIntent.getService(context,
- numActions, stopCastingIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+ numActions, stopCastingIntent, PendingIntent.FLAG_UPDATE_CURRENT
+ | (Build.VERSION.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0));
notification.addAction(R.drawable.ic_notification_cast_off,
context.getString(R.string.cast_disconnect_label),
stopCastingPendingIntent);
@@ -252,9 +254,11 @@ public class PlaybackServiceNotificationBuilder {
intent.putExtra(MediaButtonReceiver.EXTRA_KEYCODE, keycodeValue);
if (Build.VERSION.SDK_INT >= 26) {
- return PendingIntent.getForegroundService(context, requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+ return PendingIntent.getForegroundService(context, requestCode, intent,
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
} else {
- return PendingIntent.getService(context, requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+ return PendingIntent.getService(context, requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT
+ | (Build.VERSION.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0));
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceTaskManager.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceTaskManager.java
index a14605e5b..9ca7b6647 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceTaskManager.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceTaskManager.java
@@ -7,6 +7,7 @@ import android.os.Vibrator;
import androidx.annotation.NonNull;
import android.util.Log;
+import de.danoeh.antennapod.event.playback.SleepTimerUpdatedEvent;
import de.danoeh.antennapod.core.preferences.SleepTimerPreferences;
import de.danoeh.antennapod.core.util.ChapterUtils;
import de.danoeh.antennapod.core.widget.WidgetUpdater;
@@ -22,10 +23,9 @@ import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
-import de.danoeh.antennapod.core.event.FeedItemEvent;
-import de.danoeh.antennapod.core.event.QueueEvent;
+import de.danoeh.antennapod.event.FeedItemEvent;
+import de.danoeh.antennapod.event.QueueEvent;
import de.danoeh.antennapod.model.feed.FeedItem;
-import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.model.playback.Playable;
import io.reactivex.Completable;
@@ -244,6 +244,7 @@ public class PlaybackServiceTaskManager {
}
sleepTimer = new SleepTimer(waitingTime);
sleepTimerFuture = schedExecutor.schedule(sleepTimer, 0, TimeUnit.MILLISECONDS);
+ EventBus.getDefault().post(SleepTimerUpdatedEvent.justEnabled(waitingTime));
}
/**
@@ -349,10 +350,10 @@ public class PlaybackServiceTaskManager {
* Cancels all tasks and shuts down the internal executor service of the PSTM. The object should not be used after
* execution of this method.
*/
- public synchronized void shutdown() {
+ public void shutdown() {
EventBus.getDefault().unregister(this);
cancelAllTasks();
- schedExecutor.shutdown();
+ schedExecutor.shutdownNow();
}
private Runnable useMainThreadIfNecessary(Runnable runnable) {
@@ -377,27 +378,11 @@ public class PlaybackServiceTaskManager {
private final long waitingTime;
private long timeLeft;
private ShakeListener shakeListener;
- private final Handler handler;
public SleepTimer(long waitingTime) {
super();
this.waitingTime = waitingTime;
this.timeLeft = waitingTime;
-
- if (UserPreferences.useExoplayer() && Looper.myLooper() == Looper.getMainLooper()) {
- // Run callbacks in main thread so they can call ExoPlayer methods themselves
- this.handler = new Handler(Looper.getMainLooper());
- } else {
- this.handler = null;
- }
- }
-
- private void postCallback(Runnable r) {
- if (handler == null) {
- r.run();
- } else {
- handler.post(r);
- }
}
@Override
@@ -417,6 +402,7 @@ public class PlaybackServiceTaskManager {
timeLeft -= now - lastTick;
lastTick = now;
+ EventBus.getDefault().post(SleepTimerUpdatedEvent.updated(timeLeft));
if (timeLeft < NOTIFICATION_THRESHOLD) {
Log.d(TAG, "Sleep timer is about to expire");
if (SleepTimerPreferences.vibrate() && !hasVibrated) {
@@ -429,7 +415,6 @@ public class PlaybackServiceTaskManager {
if (shakeListener == null && SleepTimerPreferences.shakeToReset()) {
shakeListener = new ShakeListener(context, this);
}
- postCallback(() -> callback.onSleepTimerAlmostExpired(timeLeft));
}
if (timeLeft <= 0) {
Log.d(TAG, "Sleep timer expired");
@@ -438,11 +423,6 @@ public class PlaybackServiceTaskManager {
shakeListener = null;
}
hasVibrated = false;
- if (!Thread.currentThread().isInterrupted()) {
- postCallback(callback::onSleepTimerExpired);
- } else {
- Log.d(TAG, "Sleep timer interrupted");
- }
}
}
}
@@ -452,10 +432,8 @@ public class PlaybackServiceTaskManager {
}
public void restart() {
- postCallback(() -> {
- setSleepTimer(waitingTime);
- callback.onSleepTimerReset();
- });
+ EventBus.getDefault().post(SleepTimerUpdatedEvent.cancelled());
+ setSleepTimer(waitingTime);
if (shakeListener != null) {
shakeListener.pause();
shakeListener = null;
@@ -467,19 +445,13 @@ public class PlaybackServiceTaskManager {
if (shakeListener != null) {
shakeListener.pause();
}
- postCallback(callback::onSleepTimerReset);
+ EventBus.getDefault().post(SleepTimerUpdatedEvent.cancelled());
}
}
public interface PSTMCallback {
void positionSaverTick();
- void onSleepTimerAlmostExpired(long timeLeft);
-
- void onSleepTimerExpired();
-
- void onSleepTimerReset();
-
WidgetUpdater.WidgetState requestWidgetState();
void onChapterLoaded(Playable media);
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/AutomaticDownloadAlgorithm.java b/core/src/main/java/de/danoeh/antennapod/core/storage/AutomaticDownloadAlgorithm.java
index b5202d79c..cf32eb838 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/AutomaticDownloadAlgorithm.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/AutomaticDownloadAlgorithm.java
@@ -35,7 +35,7 @@ public class AutomaticDownloadAlgorithm {
return () -> {
// true if we should auto download based on network status
- boolean networkShouldAutoDl = NetworkUtils.autodownloadNetworkAvailable()
+ boolean networkShouldAutoDl = NetworkUtils.isAutoDownloadAllowed()
&& UserPreferences.isEnableAutodownload();
// true if we should auto download based on power status
@@ -65,7 +65,7 @@ public class AutomaticDownloadAlgorithm {
Iterator<FeedItem> it = candidates.iterator();
while (it.hasNext()) {
FeedItem item = it.next();
- if (!item.isAutoDownloadable() || FeedItemUtil.isPlaying(item.getMedia())
+ if (!item.isAutoDownloadable(System.currentTimeMillis()) || FeedItemUtil.isPlaying(item.getMedia())
|| item.getFeed().isLocalFeed()) {
it.remove();
}
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 d7267f16a..04722b916 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
@@ -28,9 +28,9 @@ import java.util.concurrent.FutureTask;
import java.util.concurrent.atomic.AtomicBoolean;
import de.danoeh.antennapod.core.R;
-import de.danoeh.antennapod.core.event.FeedItemEvent;
-import de.danoeh.antennapod.core.event.FeedListUpdateEvent;
-import de.danoeh.antennapod.core.event.MessageEvent;
+import de.danoeh.antennapod.event.FeedItemEvent;
+import de.danoeh.antennapod.event.FeedListUpdateEvent;
+import de.danoeh.antennapod.event.MessageEvent;
import de.danoeh.antennapod.core.feed.LocalFeedUpdater;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.DownloadStatus;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DBUpgrader.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DBUpgrader.java
index 46ab7502b..4e0a6aeda 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/DBUpgrader.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DBUpgrader.java
@@ -73,7 +73,7 @@ class DBUpgrader {
}
if (oldVersion <= 9) {
db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
- + " ADD COLUMN " + PodDBAdapter.KEY_AUTO_DOWNLOAD
+ + " ADD COLUMN " + PodDBAdapter.KEY_AUTO_DOWNLOAD_ENABLED
+ " INTEGER DEFAULT 1");
}
if (oldVersion <= 10) {
@@ -121,10 +121,10 @@ class DBUpgrader {
}
if (oldVersion <= 14) {
db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_ITEMS
- + " ADD COLUMN " + PodDBAdapter.KEY_AUTO_DOWNLOAD + " INTEGER");
+ + " ADD COLUMN " + PodDBAdapter.KEY_AUTO_DOWNLOAD_ATTEMPTS + " INTEGER");
db.execSQL("UPDATE " + PodDBAdapter.TABLE_NAME_FEED_ITEMS
- + " SET " + PodDBAdapter.KEY_AUTO_DOWNLOAD + " = "
- + "(SELECT " + PodDBAdapter.KEY_AUTO_DOWNLOAD
+ + " SET " + PodDBAdapter.KEY_AUTO_DOWNLOAD_ATTEMPTS + " = "
+ + "(SELECT " + PodDBAdapter.KEY_AUTO_DOWNLOAD_ENABLED
+ " FROM " + PodDBAdapter.TABLE_NAME_FEEDS
+ " WHERE " + PodDBAdapter.TABLE_NAME_FEEDS + "." + PodDBAdapter.KEY_ID
+ " = " + PodDBAdapter.TABLE_NAME_FEED_ITEMS + "." + PodDBAdapter.KEY_FEED + ")");
@@ -322,6 +322,10 @@ class DBUpgrader {
db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
+ " ADD COLUMN " + PodDBAdapter.KEY_FEED_TAGS + " TEXT;");
}
+ if (oldVersion < 2050000) {
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
+ + " ADD COLUMN " + PodDBAdapter.KEY_MINIMAL_DURATION_FILTER + " INTEGER DEFAULT -1");
+ }
}
}
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 479a7763c..0e996c6c8 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
@@ -23,13 +23,13 @@ import java.util.concurrent.TimeUnit;
import de.danoeh.antennapod.core.R;
import de.danoeh.antennapod.core.event.DownloadLogEvent;
-import de.danoeh.antennapod.core.event.FavoritesEvent;
-import de.danoeh.antennapod.core.event.FeedItemEvent;
-import de.danoeh.antennapod.core.event.FeedListUpdateEvent;
-import de.danoeh.antennapod.core.event.MessageEvent;
-import de.danoeh.antennapod.core.event.PlaybackHistoryEvent;
-import de.danoeh.antennapod.core.event.QueueEvent;
-import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
+import de.danoeh.antennapod.event.FavoritesEvent;
+import de.danoeh.antennapod.event.FeedItemEvent;
+import de.danoeh.antennapod.event.FeedListUpdateEvent;
+import de.danoeh.antennapod.event.MessageEvent;
+import de.danoeh.antennapod.event.playback.PlaybackHistoryEvent;
+import de.danoeh.antennapod.event.QueueEvent;
+import de.danoeh.antennapod.event.UnreadItemsUpdateEvent;
import de.danoeh.antennapod.core.feed.FeedEvent;
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
import de.danoeh.antennapod.core.preferences.UserPreferences;
@@ -950,25 +950,6 @@ public class DBWriter {
});
}
- public static Future<?> saveFeedItemAutoDownloadFailed(final FeedItem feedItem) {
- return dbExec.submit(() -> {
- int failedAttempts = feedItem.getFailedAutoDownloadAttempts() + 1;
- long autoDownload;
- if (!feedItem.getAutoDownload() || failedAttempts >= 10) {
- autoDownload = 0; // giving up, disable auto download
- feedItem.setAutoDownload(false);
- } else {
- long now = System.currentTimeMillis();
- autoDownload = (now / 10) * 10 + failedAttempts;
- }
- final PodDBAdapter adapter = PodDBAdapter.getInstance();
- adapter.open();
- adapter.setFeedItemAutoDownload(feedItem, autoDownload);
- adapter.close();
- EventBus.getDefault().post(new UnreadItemsUpdateEvent());
- });
- }
-
/**
* Set filter of the feed
*
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java b/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java
index 55cfafbbb..719e546b5 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
@@ -53,7 +53,7 @@ public class PodDBAdapter {
private static final String TAG = "PodDBAdapter";
public static final String DATABASE_NAME = "Antennapod.db";
- public static final int VERSION = 2030000;
+ public static final int VERSION = 2050000;
/**
* Maximum number of arguments for IN-operator.
@@ -97,7 +97,8 @@ public class PodDBAdapter {
public static final String KEY_DOWNLOADSTATUS_TITLE = "title";
public static final String KEY_CHAPTER_TYPE = "type";
public static final String KEY_PLAYBACK_COMPLETION_DATE = "playback_completion_date";
- public static final String KEY_AUTO_DOWNLOAD = "auto_download";
+ public static final String KEY_AUTO_DOWNLOAD_ATTEMPTS = "auto_download";
+ public static final String KEY_AUTO_DOWNLOAD_ENABLED = "auto_download"; // Both tables use the same key
public static final String KEY_KEEP_UPDATED = "keep_updated";
public static final String KEY_AUTO_DELETE_ACTION = "auto_delete_action";
public static final String KEY_FEED_VOLUME_ADAPTION = "feed_volume_adaption";
@@ -113,6 +114,7 @@ public class PodDBAdapter {
public static final String KEY_LAST_PLAYED_TIME = "last_played_time";
public static final String KEY_INCLUDE_FILTER = "include_filter";
public static final String KEY_EXCLUDE_FILTER = "exclude_filter";
+ public static final String KEY_MINIMAL_DURATION_FILTER = "minimal_duration_filter";
public static final String KEY_FEED_PLAYBACK_SPEED = "feed_playback_speed";
public static final String KEY_FEED_SKIP_INTRO = "feed_skip_intro";
public static final String KEY_FEED_SKIP_ENDING = "feed_skip_ending";
@@ -140,11 +142,12 @@ public class PodDBAdapter {
+ KEY_DESCRIPTION + " TEXT," + KEY_PAYMENT_LINK + " TEXT,"
+ KEY_LASTUPDATE + " TEXT," + KEY_LANGUAGE + " TEXT," + KEY_AUTHOR
+ " TEXT," + KEY_IMAGE_URL + " TEXT," + KEY_TYPE + " TEXT,"
- + KEY_FEED_IDENTIFIER + " TEXT," + KEY_AUTO_DOWNLOAD + " INTEGER DEFAULT 1,"
+ + KEY_FEED_IDENTIFIER + " TEXT," + KEY_AUTO_DOWNLOAD_ENABLED + " INTEGER DEFAULT 1,"
+ KEY_USERNAME + " TEXT,"
+ KEY_PASSWORD + " TEXT,"
+ KEY_INCLUDE_FILTER + " TEXT DEFAULT '',"
+ KEY_EXCLUDE_FILTER + " TEXT DEFAULT '',"
+ + KEY_MINIMAL_DURATION_FILTER + " INTEGER DEFAULT -1,"
+ KEY_KEEP_UPDATED + " INTEGER DEFAULT 1,"
+ KEY_IS_PAGED + " INTEGER DEFAULT 0,"
+ KEY_NEXT_PAGE_LINK + " TEXT,"
@@ -167,7 +170,7 @@ public class PodDBAdapter {
+ KEY_MEDIA + " INTEGER," + KEY_FEED + " INTEGER,"
+ KEY_HAS_CHAPTERS + " INTEGER," + KEY_ITEM_IDENTIFIER + " TEXT,"
+ KEY_IMAGE_URL + " TEXT,"
- + KEY_AUTO_DOWNLOAD + " INTEGER)";
+ + KEY_AUTO_DOWNLOAD_ATTEMPTS + " INTEGER)";
private static final String CREATE_TABLE_FEED_MEDIA = "CREATE TABLE "
+ TABLE_NAME_FEED_MEDIA + " (" + TABLE_PRIMARY_KEY + KEY_DURATION
@@ -244,7 +247,7 @@ public class PodDBAdapter {
TABLE_NAME_FEEDS + "." + KEY_IMAGE_URL,
TABLE_NAME_FEEDS + "." + KEY_TYPE,
TABLE_NAME_FEEDS + "." + KEY_FEED_IDENTIFIER,
- TABLE_NAME_FEEDS + "." + KEY_AUTO_DOWNLOAD,
+ TABLE_NAME_FEEDS + "." + KEY_AUTO_DOWNLOAD_ENABLED,
TABLE_NAME_FEEDS + "." + KEY_KEEP_UPDATED,
TABLE_NAME_FEEDS + "." + KEY_IS_PAGED,
TABLE_NAME_FEEDS + "." + KEY_NEXT_PAGE_LINK,
@@ -257,6 +260,7 @@ public class PodDBAdapter {
TABLE_NAME_FEEDS + "." + KEY_FEED_VOLUME_ADAPTION,
TABLE_NAME_FEEDS + "." + KEY_INCLUDE_FILTER,
TABLE_NAME_FEEDS + "." + KEY_EXCLUDE_FILTER,
+ TABLE_NAME_FEEDS + "." + KEY_MINIMAL_DURATION_FILTER,
TABLE_NAME_FEEDS + "." + KEY_FEED_PLAYBACK_SPEED,
TABLE_NAME_FEEDS + "." + KEY_FEED_TAGS,
TABLE_NAME_FEEDS + "." + KEY_FEED_SKIP_INTRO,
@@ -292,7 +296,7 @@ public class PodDBAdapter {
+ TABLE_NAME_FEED_ITEMS + "." + KEY_HAS_CHAPTERS + ", "
+ TABLE_NAME_FEED_ITEMS + "." + KEY_ITEM_IDENTIFIER + ", "
+ TABLE_NAME_FEED_ITEMS + "." + KEY_IMAGE_URL + ", "
- + TABLE_NAME_FEED_ITEMS + "." + KEY_AUTO_DOWNLOAD;
+ + TABLE_NAME_FEED_ITEMS + "." + KEY_AUTO_DOWNLOAD_ATTEMPTS;
private static final String KEYS_FEED_MEDIA =
TABLE_NAME_FEED_MEDIA + "." + KEY_ID + " AS " + SELECT_KEY_MEDIA_ID + ", "
@@ -442,7 +446,7 @@ public class PodDBAdapter {
throw new IllegalArgumentException("Feed ID of preference must not be null");
}
ContentValues values = new ContentValues();
- values.put(KEY_AUTO_DOWNLOAD, prefs.getAutoDownload());
+ values.put(KEY_AUTO_DOWNLOAD_ENABLED, prefs.getAutoDownload());
values.put(KEY_KEEP_UPDATED, prefs.getKeepUpdated());
values.put(KEY_AUTO_DELETE_ACTION, prefs.getAutoDeleteAction().ordinal());
values.put(KEY_FEED_VOLUME_ADAPTION, prefs.getVolumeAdaptionSetting().toInteger());
@@ -450,6 +454,7 @@ public class PodDBAdapter {
values.put(KEY_PASSWORD, prefs.getPassword());
values.put(KEY_INCLUDE_FILTER, prefs.getFilter().getIncludeFilter());
values.put(KEY_EXCLUDE_FILTER, prefs.getFilter().getExcludeFilter());
+ values.put(KEY_MINIMAL_DURATION_FILTER, prefs.getFilter().getMinimalDurationFilter());
values.put(KEY_FEED_PLAYBACK_SPEED, prefs.getFeedPlaybackSpeed());
values.put(KEY_FEED_TAGS, prefs.getTagsAsString());
values.put(KEY_FEED_SKIP_INTRO, prefs.getFeedSkipIntro());
@@ -645,7 +650,7 @@ public class PodDBAdapter {
}
values.put(KEY_HAS_CHAPTERS, item.getChapters() != null || item.hasChapters());
values.put(KEY_ITEM_IDENTIFIER, item.getItemIdentifier());
- values.put(KEY_AUTO_DOWNLOAD, item.getAutoDownload());
+ values.put(KEY_AUTO_DOWNLOAD_ATTEMPTS, item.getAutoDownloadAttemptsAndTime());
values.put(KEY_IMAGE_URL, item.getImageUrl());
if (item.getId() == 0) {
@@ -761,13 +766,6 @@ public class PodDBAdapter {
return status.getId();
}
- public void setFeedItemAutoDownload(FeedItem feedItem, long autoDownload) {
- ContentValues values = new ContentValues();
- values.put(KEY_AUTO_DOWNLOAD, autoDownload);
- db.update(TABLE_NAME_FEED_ITEMS, values, KEY_ID + "=?",
- new String[]{String.valueOf(feedItem.getId())});
- }
-
public void setFavorites(List<FeedItem> favorites) {
ContentValues values = new ContentValues();
try {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/mapper/FeedItemCursorMapper.java b/core/src/main/java/de/danoeh/antennapod/core/storage/mapper/FeedItemCursorMapper.java
index 19695ca95..ca0834339 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/mapper/FeedItemCursorMapper.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/mapper/FeedItemCursorMapper.java
@@ -25,7 +25,7 @@ public abstract class FeedItemCursorMapper {
int indexHasChapters = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_HAS_CHAPTERS);
int indexRead = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_READ);
int indexItemIdentifier = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_ITEM_IDENTIFIER);
- int indexAutoDownload = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_AUTO_DOWNLOAD);
+ int indexAutoDownload = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_AUTO_DOWNLOAD_ATTEMPTS);
int indexImageUrl = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_IMAGE_URL);
long id = cursor.getInt(indexId);
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/mapper/FeedPreferencesCursorMapper.java b/core/src/main/java/de/danoeh/antennapod/core/storage/mapper/FeedPreferencesCursorMapper.java
index cab6ea618..f062609b6 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/mapper/FeedPreferencesCursorMapper.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/mapper/FeedPreferencesCursorMapper.java
@@ -21,7 +21,7 @@ public abstract class FeedPreferencesCursorMapper {
@NonNull
public static FeedPreferences convert(@NonNull Cursor cursor) {
int indexId = cursor.getColumnIndex(PodDBAdapter.KEY_ID);
- int indexAutoDownload = cursor.getColumnIndex(PodDBAdapter.KEY_AUTO_DOWNLOAD);
+ int indexAutoDownload = cursor.getColumnIndex(PodDBAdapter.KEY_AUTO_DOWNLOAD_ENABLED);
int indexAutoRefresh = cursor.getColumnIndex(PodDBAdapter.KEY_KEEP_UPDATED);
int indexAutoDeleteAction = cursor.getColumnIndex(PodDBAdapter.KEY_AUTO_DELETE_ACTION);
int indexVolumeAdaption = cursor.getColumnIndex(PodDBAdapter.KEY_FEED_VOLUME_ADAPTION);
@@ -29,6 +29,7 @@ public abstract class FeedPreferencesCursorMapper {
int indexPassword = cursor.getColumnIndex(PodDBAdapter.KEY_PASSWORD);
int indexIncludeFilter = cursor.getColumnIndex(PodDBAdapter.KEY_INCLUDE_FILTER);
int indexExcludeFilter = cursor.getColumnIndex(PodDBAdapter.KEY_EXCLUDE_FILTER);
+ int indexMinimalDurationFilter = cursor.getColumnIndex(PodDBAdapter.KEY_MINIMAL_DURATION_FILTER);
int indexFeedPlaybackSpeed = cursor.getColumnIndex(PodDBAdapter.KEY_FEED_PLAYBACK_SPEED);
int indexAutoSkipIntro = cursor.getColumnIndex(PodDBAdapter.KEY_FEED_SKIP_INTRO);
int indexAutoSkipEnding = cursor.getColumnIndex(PodDBAdapter.KEY_FEED_SKIP_ENDING);
@@ -47,6 +48,7 @@ public abstract class FeedPreferencesCursorMapper {
String password = cursor.getString(indexPassword);
String includeFilter = cursor.getString(indexIncludeFilter);
String excludeFilter = cursor.getString(indexExcludeFilter);
+ int minimalDurationFilter = cursor.getInt(indexMinimalDurationFilter);
float feedPlaybackSpeed = cursor.getFloat(indexFeedPlaybackSpeed);
int feedAutoSkipIntro = cursor.getInt(indexAutoSkipIntro);
int feedAutoSkipEnding = cursor.getInt(indexAutoSkipEnding);
@@ -62,7 +64,7 @@ public abstract class FeedPreferencesCursorMapper {
volumeAdaptionSetting,
username,
password,
- new FeedFilter(includeFilter, excludeFilter),
+ new FeedFilter(includeFilter, excludeFilter, minimalDurationFilter),
feedPlaybackSpeed,
feedAutoSkipIntro,
feedAutoSkipEnding,
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 35b60ca4b..82896382d 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
@@ -5,6 +5,7 @@ import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
+import android.os.Build;
import android.util.Log;
import androidx.annotation.NonNull;
@@ -28,7 +29,7 @@ import java.util.Map;
import java.util.concurrent.TimeUnit;
import de.danoeh.antennapod.core.R;
-import de.danoeh.antennapod.core.event.SyncServiceEvent;
+import de.danoeh.antennapod.event.SyncServiceEvent;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
import de.danoeh.antennapod.core.storage.DBReader;
@@ -302,7 +303,8 @@ public class SyncService extends Worker {
Intent intent = getApplicationContext().getPackageManager().getLaunchIntentForPackage(
getApplicationContext().getPackageName());
PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(),
- R.id.pending_intent_sync_error, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+ R.id.pending_intent_sync_error, intent, PendingIntent.FLAG_UPDATE_CURRENT
+ | (Build.VERSION.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0));
Notification notification = new NotificationCompat.Builder(getApplicationContext(),
NotificationUtils.CHANNEL_ID_SYNC_ERROR)
.setContentTitle(getApplicationContext().getString(R.string.gpodnetsync_error_title))
@@ -344,7 +346,10 @@ public class SyncService extends Worker {
private ISyncService getActiveSyncProvider() {
String selectedSyncProviderKey = SynchronizationSettings.getSelectedSyncProviderKey();
SynchronizationProviderViewData selectedService = SynchronizationProviderViewData
- .valueOf(selectedSyncProviderKey);
+ .fromIdentifier(selectedSyncProviderKey);
+ if (selectedService == null) {
+ return null;
+ }
switch (selectedService) {
case GPODDER_NET:
return new GpodnetService(AntennapodHttpClient.getHttpClient(),
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/FeedItemPermutors.java b/core/src/main/java/de/danoeh/antennapod/core/util/FeedItemPermutors.java
index e5f60d64b..09161ca7b 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/FeedItemPermutors.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/FeedItemPermutors.java
@@ -9,6 +9,7 @@ import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import de.danoeh.antennapod.model.feed.FeedItem;
@@ -77,25 +78,22 @@ public class FeedItemPermutors {
@NonNull
private static Date pubDate(@Nullable FeedItem item) {
- return (item != null && item.getPubDate() != null) ?
- item.getPubDate() : new Date(0);
+ return (item != null && item.getPubDate() != null) ? item.getPubDate() : new Date(0);
}
@NonNull
private static String itemTitle(@Nullable FeedItem item) {
- return (item != null && item.getTitle() != null) ?
- item.getTitle() : "";
+ return (item != null && item.getTitle() != null) ? item.getTitle().toLowerCase(Locale.getDefault()) : "";
}
private static int duration(@Nullable FeedItem item) {
- return (item != null && item.getMedia() != null) ?
- item.getMedia().getDuration() : 0;
+ return (item != null && item.getMedia() != null) ? item.getMedia().getDuration() : 0;
}
@NonNull
private static String feedTitle(@Nullable FeedItem item) {
- return (item != null && item.getFeed() != null && item.getFeed().getTitle() != null) ?
- item.getFeed().getTitle() : "";
+ return (item != null && item.getFeed() != null && item.getFeed().getTitle() != null)
+ ? item.getFeed().getTitle().toLowerCase(Locale.getDefault()) : "";
}
/**
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/NetworkUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/NetworkUtils.java
index 12f1e98f9..4aeed734e 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/NetworkUtils.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/NetworkUtils.java
@@ -16,6 +16,8 @@ import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.core.preferences.UserPreferences;
@@ -30,6 +32,8 @@ import okhttp3.Request;
import okhttp3.Response;
public class NetworkUtils {
+ private static final String REGEX_PATTERN_IP_ADDRESS = "([0-9]{1,3}[\\.]){3}[0-9]{1,3}";
+
private NetworkUtils(){}
private static final String TAG = NetworkUtils.class.getSimpleName();
@@ -40,56 +44,23 @@ public class NetworkUtils {
NetworkUtils.context = context;
}
- /**
- * Returns true if the device is connected to Wi-Fi and the Wi-Fi filter for
- * automatic downloads is disabled or the device is connected to a Wi-Fi
- * network that is on the 'selected networks' list of the Wi-Fi filter for
- * automatic downloads and false otherwise.
- * */
- public static boolean autodownloadNetworkAvailable() {
- ConnectivityManager cm = (ConnectivityManager) context
- .getSystemService(Context.CONNECTIVITY_SERVICE);
+ public static boolean isAutoDownloadAllowed() {
+ ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = cm.getActiveNetworkInfo();
- if (networkInfo != null) {
- if (networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
- Log.d(TAG, "Device is connected to Wi-Fi");
- if (networkInfo.isConnected()) {
- if (!UserPreferences.isEnableAutodownloadWifiFilter()) {
- Log.d(TAG, "Auto-dl filter is disabled");
- return true;
- } else {
- WifiManager wm = (WifiManager) context.getApplicationContext()
- .getSystemService(Context.WIFI_SERVICE);
- WifiInfo wifiInfo = wm.getConnectionInfo();
- List<String> selectedNetworks = Arrays
- .asList(UserPreferences
- .getAutodownloadSelectedNetworks());
- if (selectedNetworks.contains(Integer.toString(wifiInfo
- .getNetworkId()))) {
- Log.d(TAG, "Current network is on the selected networks list");
- return true;
- }
- }
- }
- } else if (networkInfo.getType() == ConnectivityManager.TYPE_ETHERNET) {
- Log.d(TAG, "Device is connected to Ethernet");
- if (networkInfo.isConnected()) {
- return true;
- }
+ if (networkInfo == null) {
+ return false;
+ }
+ if (networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
+ if (UserPreferences.isEnableAutodownloadWifiFilter()) {
+ return isInAllowedWifiNetwork();
} else {
- if (!UserPreferences.isAllowMobileAutoDownload()) {
- Log.d(TAG, "Auto Download not enabled on Mobile");
- return false;
- }
- if (networkInfo.isRoaming()) {
- Log.d(TAG, "Roaming on foreign network");
- return false;
- }
- return true;
+ return !isNetworkMetered();
}
+ } else if (networkInfo.getType() == ConnectivityManager.TYPE_ETHERNET) {
+ return true;
+ } else {
+ return UserPreferences.isAllowMobileAutoDownload() || !NetworkUtils.isNetworkRestricted();
}
- Log.d(TAG, "Network for auto-dl is not available");
- return false;
}
public static boolean networkAvailable() {
@@ -157,6 +128,12 @@ public class NetworkUtils {
}
}
+ private static boolean isInAllowedWifiNetwork() {
+ WifiManager wm = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
+ List<String> selectedNetworks = Arrays.asList(UserPreferences.getAutodownloadSelectedNetworks());
+ return selectedNetworks.contains(Integer.toString(wm.getConnectionInfo().getNetworkId()));
+ }
+
/**
* Returns the SSID of the wifi connection, or <code>null</code> if there is no wifi.
*/
@@ -169,6 +146,22 @@ public class NetworkUtils {
return null;
}
+ public static boolean wasDownloadBlocked(Throwable throwable) {
+ String message = throwable.getMessage();
+ if (message != null) {
+ Pattern pattern = Pattern.compile(REGEX_PATTERN_IP_ADDRESS);
+ Matcher matcher = pattern.matcher(message);
+ if (matcher.find()) {
+ String ip = matcher.group();
+ return ip.startsWith("127.") || ip.startsWith("0.");
+ }
+ }
+ if (throwable.getCause() != null) {
+ return wasDownloadBlocked(throwable.getCause());
+ }
+ return false;
+ }
+
public static Single<Long> getFeedMediaSizeObservable(FeedMedia media) {
return Single.create((SingleOnSubscribe<Long>) emitter -> {
if (!NetworkUtils.isEpisodeHeadDownloadAllowed()) {
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 c1c48f70d..a5aed5da9 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
@@ -5,7 +5,6 @@ import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
-import android.os.Build;
import androidx.core.content.FileProvider;
import android.util.Log;
@@ -75,21 +74,20 @@ public class ShareUtils {
}
public static void shareFeedItemFile(Context context, FeedMedia media) {
- Intent i = new Intent(Intent.ACTION_SEND);
- i.setType(media.getMime_type());
+ Intent intent = new Intent(Intent.ACTION_SEND);
+ intent.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);
- }
+ intent.putExtra(Intent.EXTRA_STREAM, fileUri);
+ intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ Intent chooserIntent = Intent.createChooser(intent, context.getString(R.string.share_file_label));
+ List<ResolveInfo> resInfoList = context.getPackageManager()
+ .queryIntentActivities(intent, 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)));
+ context.startActivity(chooserIntent);
Log.e(TAG, "shareFeedItemFile called");
}
}
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 ec74b2fe3..b436d80b2 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
@@ -12,7 +12,11 @@ import android.util.Log;
import android.util.Pair;
import android.view.SurfaceHolder;
import androidx.annotation.NonNull;
-import de.danoeh.antennapod.core.event.ServiceEvent;
+import de.danoeh.antennapod.core.storage.DBWriter;
+import de.danoeh.antennapod.event.playback.PlaybackPositionEvent;
+import de.danoeh.antennapod.model.feed.FeedMedia;
+import de.danoeh.antennapod.event.playback.PlaybackServiceEvent;
+import de.danoeh.antennapod.event.playback.SpeedChangedEvent;
import de.danoeh.antennapod.model.playback.MediaType;
import de.danoeh.antennapod.core.feed.util.PlaybackSpeedUtils;
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
@@ -68,8 +72,8 @@ public abstract class PlaybackController {
}
@Subscribe(threadMode = ThreadMode.MAIN)
- public void onEventMainThread(ServiceEvent event) {
- if (event.action == ServiceEvent.Action.SERVICE_STARTED) {
+ public void onEventMainThread(PlaybackServiceEvent event) {
+ if (event.action == PlaybackServiceEvent.Action.SERVICE_STARTED) {
init();
}
}
@@ -206,10 +210,6 @@ public abstract class PlaybackController {
return;
}
switch (type) {
- case PlaybackService.NOTIFICATION_TYPE_BUFFER_UPDATE:
- float progress = ((float) code) / 100;
- onBufferUpdate(progress);
- break;
case PlaybackService.NOTIFICATION_TYPE_RELOAD:
if (playbackService == null && PlaybackService.isRunning) {
bindToService();
@@ -220,21 +220,9 @@ public abstract class PlaybackController {
onReloadNotification(intent.getIntExtra(
PlaybackService.EXTRA_NOTIFICATION_CODE, -1));
break;
- case PlaybackService.NOTIFICATION_TYPE_SLEEPTIMER_UPDATE:
- onSleepTimerUpdate();
- break;
- case PlaybackService.NOTIFICATION_TYPE_BUFFER_START:
- onBufferStart();
- break;
- case PlaybackService.NOTIFICATION_TYPE_BUFFER_END:
- onBufferEnd();
- break;
case PlaybackService.NOTIFICATION_TYPE_PLAYBACK_END:
onPlaybackEnd();
break;
- case PlaybackService.NOTIFICATION_TYPE_PLAYBACK_SPEED_CHANGE:
- onPlaybackSpeedChange();
- break;
}
}
@@ -242,22 +230,11 @@ public abstract class PlaybackController {
public void onPositionObserverUpdate() {}
-
- public void onPlaybackSpeedChange() {}
-
/**
* Called when the currently displayed information should be refreshed.
*/
public void onReloadNotification(int code) {}
- public void onBufferStart() {}
-
- public void onBufferEnd() {}
-
- public void onBufferUpdate(float progress) {}
-
- public void onSleepTimerUpdate() {}
-
public void onPlaybackEnd() {}
/**
@@ -446,6 +423,11 @@ public abstract class PlaybackController {
public void seekTo(int time) {
if (playbackService != null) {
playbackService.seekTo(time);
+ } else if (getMedia() instanceof FeedMedia) {
+ FeedMedia media = (FeedMedia) getMedia();
+ media.setPosition(time);
+ DBWriter.setFeedItem(media.getItem());
+ EventBus.getDefault().post(new PlaybackPositionEvent(time, getMedia().getDuration()));
}
}
@@ -470,7 +452,7 @@ public abstract class PlaybackController {
if (playbackService != null) {
playbackService.setSpeed(speed);
} else {
- onPlaybackSpeedChange();
+ EventBus.getDefault().post(new SpeedChangedEvent(speed));
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/widget/WidgetUpdater.java b/core/src/main/java/de/danoeh/antennapod/core/widget/WidgetUpdater.java
index cecd4b3b6..ed3f1800d 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/widget/WidgetUpdater.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/widget/WidgetUpdater.java
@@ -7,6 +7,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
+import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
@@ -19,6 +20,7 @@ import com.bumptech.glide.request.RequestOptions;
import java.util.concurrent.TimeUnit;
import de.danoeh.antennapod.core.R;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.model.playback.MediaType;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.receiver.MediaButtonReceiver;
@@ -212,18 +214,21 @@ public abstract class WidgetUpdater {
startingIntent.setAction(MediaButtonReceiver.NOTIFY_BUTTON_RECEIVER);
startingIntent.putExtra(Intent.EXTRA_KEY_EVENT, event);
- return PendingIntent.getBroadcast(context, eventCode, startingIntent, 0);
+ return PendingIntent.getBroadcast(context, eventCode, startingIntent,
+ (Build.VERSION.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0));
}
private static String getProgressString(int position, int duration, float speed) {
- if (position >= 0 && duration > 0) {
- TimeSpeedConverter converter = new TimeSpeedConverter(speed);
- position = converter.convert(position);
- duration = converter.convert(duration);
- return Converter.getDurationStringLong(position) + " / "
- + Converter.getDurationStringLong(duration);
- } else {
+ if (position < 0 || duration <= 0) {
return null;
}
+ TimeSpeedConverter converter = new TimeSpeedConverter(speed);
+ if (UserPreferences.shouldShowRemainingTime()) {
+ return Converter.getDurationStringLong(converter.convert(position)) + " / -"
+ + Converter.getDurationStringLong(converter.convert(Math.max(0, duration - position)));
+ } else {
+ return Converter.getDurationStringLong(converter.convert(position)) + " / "
+ + Converter.getDurationStringLong(converter.convert(duration));
+ }
}
}
diff --git a/core/src/main/res/drawable/ic_download_black.xml b/core/src/main/res/drawable/ic_download_black.xml
new file mode 100644
index 000000000..eba137a59
--- /dev/null
+++ b/core/src/main/res/drawable/ic_download_black.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="#000000"
+ android:pathData="M18,15v3H6v-3H4v3c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2v-3H18zM17,11l-1.41,-1.41L13,12.17V4h-2v8.17L8.41,9.59L7,11l5,5L17,11z"/>
+</vector>
diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml
index 390fe7d95..4333929c4 100644
--- a/core/src/main/res/values/strings.xml
+++ b/core/src/main/res/values/strings.xml
@@ -45,7 +45,6 @@
<!-- Statistics fragment -->
<string name="total_time_listened_to_podcasts">Total time of episodes played:</string>
- <string name="statistics_details_dialog">%1$d out of %2$d episodes started.\n\nPlayed %3$s out of %4$s.</string>
<string name="statistics_mode">Statistics mode</string>
<string name="statistics_mode_normal">Calculate duration that was actually played. Playing twice is counted twice, while marking as played is not counted</string>
<string name="statistics_mode_count_all">Sum up all episodes marked as played</string>
@@ -264,8 +263,8 @@
<string name="download_error_forbidden">The podcast host\'s server refuses to respond.</string>
<string name="download_canceled_msg">Download canceled</string>
<string name="download_error_wrong_size">The server connection was lost before completing the download</string>
- <string name="download_error_blocked">The download was blocked by another app on your device.</string>
- <string name="download_error_certificate">Unable to establish a secure connection. This can mean that another app on your device blocked the download, or that something is wrong with the server certificates.</string>
+ <string name="download_error_blocked">The download was blocked by another app on your device (like a VPN or ad blocker).</string>
+ <string name="download_error_certificate">Unable to establish a secure connection. This can mean that another app on your device (like a VPN or an ad blocker) blocked the download, or that something is wrong with the server certificates.</string>
<string name="download_report_title">Downloads completed with error(s)</string>
<string name="auto_download_report_title">Auto-downloads completed</string>
<string name="download_error_io_error">IO Error</string>
@@ -702,13 +701,17 @@
<string name="episode_filters_description">List of terms used to decide if an episode should be included or excluded when auto downloading</string>
<string name="episode_filters_include">Include</string>
<string name="episode_filters_exclude">Exclude</string>
+ <string name="episode_filters_duration">Minimal Duration (in minutes)</string>
<string name="episode_filters_hint">Single words \n\"Multiple Words\"</string>
<string name="keep_updated">Keep Updated</string>
<string name="keep_updated_summary">Include this podcast when (auto-)refreshing all podcasts</string>
<string name="auto_download_disabled_globally">Auto download is disabled in the main AntennaPod settings</string>
- <string name="statistics_listened_for">Listened for:</string>
+ <string name="statistics_time_played">Time played:</string>
+ <string name="statistics_total_duration">Total duration (estimate):</string>
+ <string name="statistics_duration_played_episodes">Duration of played episodes:</string>
<string name="statistics_episodes_on_device">Episodes on the device:</string>
<string name="statistics_space_used">Space used:</string>
+ <string name="statistics_episodes_started_total">Episodes started/total:</string>
<string name="statistics_view_all">View for all podcasts ยป</string>
<!-- AntennaPodSP -->
@@ -847,4 +850,8 @@
<string name="on_demand_config_setting_changed">Setting updated successfully.</string>
<string name="on_demand_config_stream_text">Looks like you stream a lot. Do you want episode lists to show stream buttons?</string>
<string name="on_demand_config_download_text">Looks like you download a lot. Do you want episode lists to show download buttons?</string>
+
+ <string name="shortcut_subscription_label">Subscription shortcut</string>
+ <string name="shortcut_select_subscription">Select subscription</string>
+ <string name="add_shortcut">Add Shortcut</string>
</resources>
diff --git a/core/src/play/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceFlavorHelper.java b/core/src/play/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceFlavorHelper.java
index 38e84017f..41fd01441 100644
--- a/core/src/play/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceFlavorHelper.java
+++ b/core/src/play/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceFlavorHelper.java
@@ -26,7 +26,7 @@ import java.util.concurrent.TimeoutException;
import de.danoeh.antennapod.core.cast.CastConsumer;
import de.danoeh.antennapod.core.cast.CastManager;
import de.danoeh.antennapod.core.cast.DefaultCastConsumer;
-import de.danoeh.antennapod.core.event.MessageEvent;
+import de.danoeh.antennapod.event.MessageEvent;
import de.danoeh.antennapod.model.playback.MediaType;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.util.NetworkUtils;
diff --git a/core/src/test/java/de/danoeh/antennapod/core/feed/FeedFilterTest.java b/core/src/test/java/de/danoeh/antennapod/core/feed/FeedFilterTest.java
index 4ad578727..3840f6387 100644
--- a/core/src/test/java/de/danoeh/antennapod/core/feed/FeedFilterTest.java
+++ b/core/src/test/java/de/danoeh/antennapod/core/feed/FeedFilterTest.java
@@ -1,7 +1,10 @@
package de.danoeh.antennapod.core.feed;
+import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.model.feed.FeedFilter;
import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedMedia;
+
import org.junit.Test;
import static org.junit.Assert.assertEquals;
@@ -125,4 +128,32 @@ public class FeedFilterTest {
assertFalse(filter.shouldAutoDownload(doNotDownload2));
}
+ @Test
+ public void testMinimalDurationFilter() {
+ FeedItem download = new FeedItem();
+ download.setTitle("Hello friend!");
+ FeedMedia downloadMedia = FeedMediaMother.anyFeedMedia();
+ downloadMedia.setDuration(Converter.durationStringShortToMs("05:00", false));
+ download.setMedia(downloadMedia);
+ // because duration of the media in unknown
+ FeedItem download2 = new FeedItem();
+ download2.setTitle("Hello friend!");
+ FeedMedia unknownDurationMedia = FeedMediaMother.anyFeedMedia();
+ download2.setMedia(unknownDurationMedia);
+ // because it is not long enough
+ FeedItem doNotDownload = new FeedItem();
+ doNotDownload.setTitle("Hello friend!");
+ FeedMedia doNotDownloadMedia = FeedMediaMother.anyFeedMedia();
+ doNotDownloadMedia.setDuration(Converter.durationStringShortToMs("02:00", false));
+ doNotDownload.setMedia(doNotDownloadMedia);
+
+ int minimalDurationFilter = 3 * 60;
+ FeedFilter filter = new FeedFilter("", "", minimalDurationFilter);
+
+ assertTrue(filter.hasMinimalDurationFilter());
+ assertTrue(filter.shouldAutoDownload(download));
+ assertFalse(filter.shouldAutoDownload(doNotDownload));
+ assertTrue(filter.shouldAutoDownload(download2));
+ }
+
}
diff --git a/core/src/test/java/de/danoeh/antennapod/core/feed/FeedItemTest.java b/core/src/test/java/de/danoeh/antennapod/core/feed/FeedItemTest.java
index c4860d818..a08d0897d 100644
--- a/core/src/test/java/de/danoeh/antennapod/core/feed/FeedItemTest.java
+++ b/core/src/test/java/de/danoeh/antennapod/core/feed/FeedItemTest.java
@@ -1,6 +1,7 @@
package de.danoeh.antennapod.core.feed;
import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedMedia;
import org.junit.Before;
import org.junit.Test;
@@ -10,11 +11,13 @@ import java.util.Date;
import static de.danoeh.antennapod.core.feed.FeedItemMother.anyFeedItemWithImage;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
public class FeedItemTest {
private static final String TEXT_LONG = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.";
private static final String TEXT_SHORT = "Lorem ipsum";
+ private static final long ONE_HOUR = 1000L * 3600L;
private FeedItem original;
private FeedItem changedFeedItem;
@@ -136,4 +139,36 @@ public class FeedItemTest {
item.setDescriptionIfLonger(contentEncoded);
assertEquals(TEXT_LONG, item.getDescription());
}
-} \ No newline at end of file
+
+ @Test
+ public void testAutoDownloadBackoff() {
+ FeedItem item = new FeedItem();
+ item.setMedia(new FeedMedia(item, "https://example.com/file.mp3", 0, "audio/mpeg"));
+
+ long now = ONE_HOUR; // In reality, this is System.currentTimeMillis()
+ assertTrue(item.isAutoDownloadable(now));
+ item.increaseFailedAutoDownloadAttempts(now);
+ assertFalse(item.isAutoDownloadable(now));
+
+ now += ONE_HOUR;
+ assertTrue(item.isAutoDownloadable(now));
+ item.increaseFailedAutoDownloadAttempts(now);
+ assertFalse(item.isAutoDownloadable(now));
+
+ now += ONE_HOUR;
+ assertFalse(item.isAutoDownloadable(now)); // Should backoff, so more than 1 hour needed
+
+ now += ONE_HOUR;
+ assertTrue(item.isAutoDownloadable(now)); // Now it's enough
+ item.increaseFailedAutoDownloadAttempts(now);
+ item.increaseFailedAutoDownloadAttempts(now);
+ item.increaseFailedAutoDownloadAttempts(now);
+
+ now += 1000L * ONE_HOUR;
+ assertFalse(item.isAutoDownloadable(now)); // Should have given up
+ item.increaseFailedAutoDownloadAttempts(now);
+
+ now += 1000L * ONE_HOUR;
+ assertFalse(item.isAutoDownloadable(now)); // Still given up
+ }
+}