diff options
author | Jonas Kalderstam <jonas@cowboyprogrammer.org> | 2019-10-05 01:18:14 +0200 |
---|---|---|
committer | Jonas Kalderstam <jonas@cowboyprogrammer.org> | 2019-10-05 01:18:14 +0200 |
commit | 97aa36061186ae589b26e90939a32e83573368c2 (patch) | |
tree | da900ade55ada0976017046c8ba0b7dd49937f9e /core/src/main/java/de | |
parent | de78c0e31eeff0609fe5388204e6e39e8f60579b (diff) | |
parent | eb70b4e0fa48f72eef50b2ca55eb590ae5df5726 (diff) | |
download | AntennaPod-97aa36061186ae589b26e90939a32e83573368c2.zip |
Merge remote-tracking branch 'antennapod/develop' into per_feed_playbackspeed
Diffstat (limited to 'core/src/main/java/de')
80 files changed, 570 insertions, 709 deletions
diff --git a/core/src/main/java/de/danoeh/antennapod/core/dialog/ConfirmationDialog.java b/core/src/main/java/de/danoeh/antennapod/core/dialog/ConfirmationDialog.java index c626a8189..c9fe886fb 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/dialog/ConfirmationDialog.java +++ b/core/src/main/java/de/danoeh/antennapod/core/dialog/ConfirmationDialog.java @@ -2,7 +2,7 @@ package de.danoeh.antennapod.core.dialog; import android.content.Context; import android.content.DialogInterface; -import android.support.v7.app.AlertDialog; +import androidx.appcompat.app.AlertDialog; import android.util.Log; import de.danoeh.antennapod.core.R; diff --git a/core/src/main/java/de/danoeh/antennapod/core/dialog/DownloadRequestErrorDialogCreator.java b/core/src/main/java/de/danoeh/antennapod/core/dialog/DownloadRequestErrorDialogCreator.java index b1beac315..e38abb990 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/dialog/DownloadRequestErrorDialogCreator.java +++ b/core/src/main/java/de/danoeh/antennapod/core/dialog/DownloadRequestErrorDialogCreator.java @@ -1,7 +1,7 @@ package de.danoeh.antennapod.core.dialog; import android.content.Context; -import android.support.v7.app.AlertDialog; +import androidx.appcompat.app.AlertDialog; import de.danoeh.antennapod.core.R; diff --git a/core/src/main/java/de/danoeh/antennapod/core/event/DownloadEvent.java b/core/src/main/java/de/danoeh/antennapod/core/event/DownloadEvent.java index 124fd3e64..24a71ec96 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/event/DownloadEvent.java +++ b/core/src/main/java/de/danoeh/antennapod/core/event/DownloadEvent.java @@ -25,4 +25,8 @@ public class DownloadEvent { "update=" + update + '}'; } + + public boolean hasChangedFeedUpdateStatus(boolean oldStatus) { + return oldStatus != update.feedIds.length > 0; + } } diff --git a/core/src/main/java/de/danoeh/antennapod/core/event/DownloaderUpdate.java b/core/src/main/java/de/danoeh/antennapod/core/event/DownloaderUpdate.java index 7ca6f78de..9acd7728a 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/event/DownloaderUpdate.java +++ b/core/src/main/java/de/danoeh/antennapod/core/event/DownloaderUpdate.java @@ -1,6 +1,6 @@ package de.danoeh.antennapod.core.event; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import java.util.Arrays; import java.util.List; 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 index 9db262857..89ecf271b 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/event/FeedItemEvent.java +++ b/core/src/main/java/de/danoeh/antennapod/core/event/FeedItemEvent.java @@ -1,7 +1,7 @@ package de.danoeh.antennapod.core.event; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; diff --git a/core/src/main/java/de/danoeh/antennapod/core/event/FeedMediaEvent.java b/core/src/main/java/de/danoeh/antennapod/core/event/FeedMediaEvent.java deleted file mode 100644 index 4a591c996..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/event/FeedMediaEvent.java +++ /dev/null @@ -1,23 +0,0 @@ -package de.danoeh.antennapod.core.event; - -import de.danoeh.antennapod.core.feed.FeedMedia; - -public class FeedMediaEvent { - - public enum Action { - UPDATE - } - - private final Action action; - private final FeedMedia media; - - private FeedMediaEvent(Action action, FeedMedia media) { - this.action = action; - this.media = media; - } - - public static FeedMediaEvent update(FeedMedia media) { - return new FeedMediaEvent(Action.UPDATE, media); - } - -} 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 index 9fc488fbc..9fb22b8ea 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/event/MessageEvent.java +++ b/core/src/main/java/de/danoeh/antennapod/core/event/MessageEvent.java @@ -1,6 +1,6 @@ package de.danoeh.antennapod.core.event; -import android.support.annotation.Nullable; +import androidx.annotation.Nullable; public class MessageEvent { 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 new file mode 100644 index 000000000..3327d8a02 --- /dev/null +++ b/core/src/main/java/de/danoeh/antennapod/core/event/PlaybackPositionEvent.java @@ -0,0 +1,19 @@ +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/ProgressEvent.java b/core/src/main/java/de/danoeh/antennapod/core/event/ProgressEvent.java deleted file mode 100644 index 3769d6bb1..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/event/ProgressEvent.java +++ /dev/null @@ -1,36 +0,0 @@ -package de.danoeh.antennapod.core.event; - -import org.apache.commons.lang3.builder.ToStringBuilder; -import org.apache.commons.lang3.builder.ToStringStyle; - -public class ProgressEvent { - - public enum Action { - START, END - } - - public final Action action; - public final String message; - - private ProgressEvent(Action action, String message) { - this.action = action; - this.message = message; - } - - public static ProgressEvent start(String message) { - return new ProgressEvent(Action.START, message); - } - - public static ProgressEvent end() { - return new ProgressEvent(Action.END, null); - } - - @Override - public String toString() { - return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE) - .append("action", action) - .append("message", message) - .toString(); - } - -} 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 index a84e8456f..209dcba4d 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/event/QueueEvent.java +++ b/core/src/main/java/de/danoeh/antennapod/core/event/QueueEvent.java @@ -1,6 +1,6 @@ package de.danoeh.antennapod.core.event; -import android.support.annotation.Nullable; +import androidx.annotation.Nullable; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java b/core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java index 5718c06c2..6a7c97b7b 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java +++ b/core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java @@ -1,7 +1,7 @@ package de.danoeh.antennapod.core.feed; import android.database.Cursor; -import android.support.annotation.Nullable; +import androidx.annotation.Nullable; import android.text.TextUtils; import java.util.ArrayList; diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItem.java b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItem.java index 86d8f5294..0f9ca0445 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItem.java +++ b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItem.java @@ -1,7 +1,7 @@ package de.danoeh.antennapod.core.feed; import android.database.Cursor; -import android.support.annotation.Nullable; +import androidx.annotation.Nullable; import android.text.TextUtils; import org.apache.commons.lang3.builder.ToStringBuilder; @@ -221,6 +221,8 @@ public class FeedItem extends FeedComponent implements ShownotesProvider, ImageR return itemIdentifier; } else if (title != null && !title.isEmpty()) { return title; + } else if (hasMedia() && media.getDownload_url() != null) { + return media.getDownload_url(); } else { return link; } diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedMedia.java b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedMedia.java index 05ca84bac..10cf1f5c8 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedMedia.java +++ b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedMedia.java @@ -7,7 +7,7 @@ import android.database.Cursor; import android.media.MediaMetadataRetriever; import android.os.Parcel; import android.os.Parcelable; -import android.support.annotation.Nullable; +import androidx.annotation.Nullable; import android.support.v4.media.MediaBrowserCompat; import android.support.v4.media.MediaDescriptionCompat; diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedPreferences.java b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedPreferences.java index b0512d844..8fdf2034f 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedPreferences.java +++ b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedPreferences.java @@ -2,7 +2,7 @@ package de.danoeh.antennapod.core.feed; import android.content.Context; import android.database.Cursor; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import android.text.TextUtils; import de.danoeh.antennapod.core.preferences.UserPreferences; 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 552c1b691..b2c809e90 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 @@ -2,7 +2,7 @@ package de.danoeh.antennapod.core.glide; import android.content.Context; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import com.bumptech.glide.Glide; import com.bumptech.glide.GlideBuilder; import com.bumptech.glide.Registry; diff --git a/core/src/main/java/de/danoeh/antennapod/core/glide/ApOkHttpUrlLoader.java b/core/src/main/java/de/danoeh/antennapod/core/glide/ApOkHttpUrlLoader.java index b75e1630c..f02334af5 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/glide/ApOkHttpUrlLoader.java +++ b/core/src/main/java/de/danoeh/antennapod/core/glide/ApOkHttpUrlLoader.java @@ -1,7 +1,7 @@ package de.danoeh.antennapod.core.glide; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import android.text.TextUtils; import android.util.Log; @@ -21,8 +21,12 @@ import de.danoeh.antennapod.core.service.download.AntennapodHttpClient; import de.danoeh.antennapod.core.service.download.HttpDownloader; import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.util.NetworkUtils; -import okhttp3.*; -import okhttp3.internal.http.RealResponseBody; +import okhttp3.Interceptor; +import okhttp3.OkHttpClient; +import okhttp3.Protocol; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.ResponseBody; /** * @see com.bumptech.glide.integration.okhttp3.OkHttpUrlLoader @@ -88,11 +92,9 @@ class ApOkHttpUrlLoader implements ModelLoader<String, InputStream> { @Nullable @Override public LoadData<InputStream> buildLoadData(@NonNull String model, int width, int height, @NonNull Options options) { - Log.d(TAG, "buildLoadData() called with: " + "model = [" + model + "], width = [" - + width + "], height = [" + height + "]"); - if(TextUtils.isEmpty(model)) { + if (TextUtils.isEmpty(model)) { return null; - } else if(model.startsWith("/")) { + } else if (model.startsWith("/")) { return new LoadData<>(new ObjectKey(model), new AudioCoverFetcher(model)); } else { GlideUrl url = new GlideUrl(model); diff --git a/core/src/main/java/de/danoeh/antennapod/core/glide/AudioCoverFetcher.java b/core/src/main/java/de/danoeh/antennapod/core/glide/AudioCoverFetcher.java index 479846655..6a237573b 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/glide/AudioCoverFetcher.java +++ b/core/src/main/java/de/danoeh/antennapod/core/glide/AudioCoverFetcher.java @@ -2,7 +2,7 @@ package de.danoeh.antennapod.core.glide; import android.media.MediaMetadataRetriever; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import com.bumptech.glide.Priority; import com.bumptech.glide.load.DataSource; import com.bumptech.glide.load.data.DataFetcher; diff --git a/core/src/main/java/de/danoeh/antennapod/core/glide/FastBlurTransformation.java b/core/src/main/java/de/danoeh/antennapod/core/glide/FastBlurTransformation.java index a740782d6..cbd22ceb0 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/glide/FastBlurTransformation.java +++ b/core/src/main/java/de/danoeh/antennapod/core/glide/FastBlurTransformation.java @@ -2,7 +2,7 @@ package de.danoeh.antennapod.core.glide; import android.graphics.Bitmap; import android.media.ThumbnailUtils; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import android.util.Log; import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool; diff --git a/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/GpodnetService.java b/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/GpodnetService.java index 2588cfdee..9ee8c0fc1 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/GpodnetService.java +++ b/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/GpodnetService.java @@ -1,6 +1,6 @@ package de.danoeh.antennapod.core.gpoddernet; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import org.json.JSONArray; import org.json.JSONException; diff --git a/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetDevice.java b/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetDevice.java index faf4264e5..e86b74164 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetDevice.java +++ b/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetDevice.java @@ -1,6 +1,6 @@ package de.danoeh.antennapod.core.gpoddernet.model; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; public class GpodnetDevice { diff --git a/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetEpisodeActionGetResponse.java b/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetEpisodeActionGetResponse.java index 1e21efcda..7b28bba49 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetEpisodeActionGetResponse.java +++ b/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetEpisodeActionGetResponse.java @@ -1,7 +1,7 @@ package de.danoeh.antennapod.core.gpoddernet.model; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import java.util.List; diff --git a/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetEpisodeActionPostResponse.java b/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetEpisodeActionPostResponse.java index b6efab016..10ea4cd9b 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetEpisodeActionPostResponse.java +++ b/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetEpisodeActionPostResponse.java @@ -1,6 +1,6 @@ package de.danoeh.antennapod.core.gpoddernet.model; -import android.support.v4.util.ArrayMap; +import androidx.collection.ArrayMap; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; diff --git a/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetPodcast.java b/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetPodcast.java index 680dc1042..2c2d759c9 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetPodcast.java +++ b/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetPodcast.java @@ -1,6 +1,6 @@ package de.danoeh.antennapod.core.gpoddernet.model; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; public class GpodnetPodcast { private final String url; diff --git a/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetSubscriptionChange.java b/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetSubscriptionChange.java index 0f1961bef..56a64053f 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetSubscriptionChange.java +++ b/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetSubscriptionChange.java @@ -1,6 +1,6 @@ package de.danoeh.antennapod.core.gpoddernet.model; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import java.util.List; diff --git a/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetTag.java b/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetTag.java index 40543592e..dec3be7f2 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetTag.java +++ b/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetTag.java @@ -2,7 +2,7 @@ package de.danoeh.antennapod.core.gpoddernet.model; import android.os.Parcel; import android.os.Parcelable; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; public class GpodnetTag implements Parcelable { diff --git a/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetUploadChangesResponse.java b/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetUploadChangesResponse.java index 9f9c3bd74..ba3db8412 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetUploadChangesResponse.java +++ b/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetUploadChangesResponse.java @@ -1,6 +1,6 @@ package de.danoeh.antennapod.core.gpoddernet.model; -import android.support.v4.util.ArrayMap; +import androidx.collection.ArrayMap; import org.json.JSONArray; import org.json.JSONException; 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 a4cc22d8b..f2c0c8fe3 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 @@ -4,7 +4,12 @@ import android.content.Context; import android.content.SharedPreferences; import android.preference.PreferenceManager; +import android.util.Log; import de.danoeh.antennapod.core.feed.EventDistributor; +import de.danoeh.antennapod.core.feed.FeedMedia; +import de.danoeh.antennapod.core.feed.MediaType; +import de.danoeh.antennapod.core.service.playback.PlayerStatus; +import de.danoeh.antennapod.core.util.playback.Playable; /** * Provides access to preferences set by the playback service. A private @@ -19,35 +24,35 @@ public class PlaybackPreferences implements SharedPreferences.OnSharedPreference * Contains the feed id of the currently playing item if it is a FeedMedia * object. */ - public static final String PREF_CURRENTLY_PLAYING_FEED_ID = "de.danoeh.antennapod.preferences.lastPlayedFeedId"; + private static final String PREF_CURRENTLY_PLAYING_FEED_ID = "de.danoeh.antennapod.preferences.lastPlayedFeedId"; /** * Contains the id of the currently playing FeedMedia object or * NO_MEDIA_PLAYING if the currently playing media is no FeedMedia object. */ - public static final String PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID = "de.danoeh.antennapod.preferences.lastPlayedFeedMediaId"; + private static final String PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID = "de.danoeh.antennapod.preferences.lastPlayedFeedMediaId"; /** * Type of the media object that is currently being played. This preference * is set to NO_MEDIA_PLAYING after playback has been completed and is set * as soon as the 'play' button is pressed. */ - public static final String PREF_CURRENTLY_PLAYING_MEDIA = "de.danoeh.antennapod.preferences.currentlyPlayingMedia"; + private static final String PREF_CURRENTLY_PLAYING_MEDIA = "de.danoeh.antennapod.preferences.currentlyPlayingMedia"; /** * True if last played media was streamed. */ - public static final String PREF_CURRENT_EPISODE_IS_STREAM = "de.danoeh.antennapod.preferences.lastIsStream"; + private static final String PREF_CURRENT_EPISODE_IS_STREAM = "de.danoeh.antennapod.preferences.lastIsStream"; /** * True if last played media was a video. */ - public static final String PREF_CURRENT_EPISODE_IS_VIDEO = "de.danoeh.antennapod.preferences.lastIsVideo"; + private static final String PREF_CURRENT_EPISODE_IS_VIDEO = "de.danoeh.antennapod.preferences.lastIsVideo"; /** * The current player status as int. */ - public static final String PREF_CURRENT_PLAYER_STATUS = "de.danoeh.antennapod.preferences.currentPlayerStatus"; + private static final String PREF_CURRENT_PLAYER_STATUS = "de.danoeh.antennapod.preferences.currentPlayerStatus"; /** * Value of PREF_CURRENTLY_PLAYING_MEDIA if no media is playing. @@ -87,10 +92,6 @@ public class PlaybackPreferences implements SharedPreferences.OnSharedPreference } } - public static long getLastPlayedFeedId() { - return prefs.getLong(PREF_CURRENTLY_PLAYING_FEED_ID, -1); - } - public static long getCurrentlyPlayingMedia() { return prefs.getLong(PREF_CURRENTLY_PLAYING_MEDIA, NO_MEDIA_PLAYING); } @@ -119,4 +120,52 @@ public class PlaybackPreferences implements SharedPreferences.OnSharedPreference editor.putInt(PREF_CURRENT_PLAYER_STATUS, PLAYER_STATUS_OTHER); editor.apply(); } + + public static void writeMediaPlaying(Playable playable, PlayerStatus playerStatus, boolean stream) { + Log.d(TAG, "Writing playback preferences"); + SharedPreferences.Editor editor = prefs.edit(); + + if (playable == null) { + writeNoMediaPlaying(); + } else { + editor.putLong(PREF_CURRENTLY_PLAYING_MEDIA, playable.getPlayableType()); + editor.putBoolean(PREF_CURRENT_EPISODE_IS_STREAM, stream); + editor.putBoolean(PREF_CURRENT_EPISODE_IS_VIDEO, playable.getMediaType() == MediaType.VIDEO); + if (playable instanceof FeedMedia) { + FeedMedia fMedia = (FeedMedia) playable; + editor.putLong(PREF_CURRENTLY_PLAYING_FEED_ID, fMedia.getItem().getFeed().getId()); + editor.putLong(PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID, fMedia.getId()); + } else { + editor.putLong(PREF_CURRENTLY_PLAYING_FEED_ID, NO_MEDIA_PLAYING); + editor.putLong(PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID, NO_MEDIA_PLAYING); + } + playable.writeToPreferences(editor); + } + editor.putInt(PREF_CURRENT_PLAYER_STATUS, getCurrentPlayerStatusAsInt(playerStatus)); + + editor.apply(); + } + + public static void writePlayerStatus(PlayerStatus playerStatus) { + Log.d(TAG, "Writing player status playback preferences"); + + SharedPreferences.Editor editor = prefs.edit(); + editor.putInt(PREF_CURRENT_PLAYER_STATUS, getCurrentPlayerStatusAsInt(playerStatus)); + editor.apply(); + } + + private static int getCurrentPlayerStatusAsInt(PlayerStatus playerStatus) { + int playerStatusAsInt; + switch (playerStatus) { + case PLAYING: + playerStatusAsInt = PLAYER_STATUS_PLAYING; + break; + case PAUSED: + playerStatusAsInt = PLAYER_STATUS_PAUSED; + break; + default: + playerStatusAsInt = PLAYER_STATUS_OTHER; + } + return playerStatusAsInt; + } } diff --git a/core/src/main/java/de/danoeh/antennapod/core/preferences/SleepTimerPreferences.java b/core/src/main/java/de/danoeh/antennapod/core/preferences/SleepTimerPreferences.java index 0f3a9fcb3..eb87acb5f 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/preferences/SleepTimerPreferences.java +++ b/core/src/main/java/de/danoeh/antennapod/core/preferences/SleepTimerPreferences.java @@ -2,7 +2,7 @@ package de.danoeh.antennapod.core.preferences; import android.content.Context; import android.content.SharedPreferences; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import android.util.Log; import java.util.concurrent.TimeUnit; 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 80f0fd454..fc4166eb4 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 @@ -2,10 +2,11 @@ package de.danoeh.antennapod.core.preferences; import android.content.Context; import android.content.SharedPreferences; +import android.content.res.Configuration; import android.preference.PreferenceManager; -import android.support.annotation.IntRange; -import android.support.annotation.NonNull; -import android.support.v4.app.NotificationCompat; +import androidx.annotation.IntRange; +import androidx.annotation.NonNull; +import androidx.core.app.NotificationCompat; import android.text.TextUtils; import android.util.Log; @@ -169,7 +170,7 @@ public class UserPreferences { * @return R.style.Theme_AntennaPod_Light or R.style.Theme_AntennaPod_Dark */ public static int getTheme() { - return readThemeValue(prefs.getString(PREF_THEME, "0")); + return readThemeValue(prefs.getString(PREF_THEME, "system")); } public static int getNoTitleTheme() { @@ -645,7 +646,7 @@ public class UserPreferences { .apply(); // when updating with an interval, we assume the user wants // to update *now* and then every 'hours' interval thereafter. - restartUpdateAlarm(); + AutoUpdateManager.restartUpdateAlarm(); } /** @@ -655,7 +656,7 @@ public class UserPreferences { prefs.edit() .putString(PREF_UPDATE_INTERVAL, hourOfDay + ":" + minute) .apply(); - restartUpdateAlarm(); + AutoUpdateManager.restartUpdateAlarm(); } public static void disableAutoUpdate() { @@ -696,14 +697,18 @@ public class UserPreferences { } private static int readThemeValue(String valueFromPrefs) { - switch (Integer.parseInt(valueFromPrefs)) { - case 0: + switch (valueFromPrefs) { + case "0": return R.style.Theme_AntennaPod_Light; - case 1: + case "1": return R.style.Theme_AntennaPod_Dark; - case 2: + case "2": return R.style.Theme_AntennaPod_TrueBlack; default: + int nightMode = context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; + if (nightMode == Configuration.UI_MODE_NIGHT_YES) { + return R.style.Theme_AntennaPod_Dark; + } return R.style.Theme_AntennaPod_Light; } } @@ -901,26 +906,6 @@ public class UserPreferences { return getUpdateTimeOfDay().length == 2; } - public static void restartUpdateAlarm() { - if (isAutoUpdateDisabled()) { - AutoUpdateManager.disableAutoUpdate(); - } else if (isAutoUpdateTimeOfDay()) { - int[] timeOfDay = getUpdateTimeOfDay(); - Log.d(TAG, "timeOfDay: " + Arrays.toString(timeOfDay)); - AutoUpdateManager.restartUpdateTimeOfDayAlarm(timeOfDay[0], timeOfDay[1]); - } else { - long milliseconds = getUpdateInterval(); - AutoUpdateManager.restartUpdateIntervalAlarm(milliseconds); - } - } - - /** - * Reads episode cache size as it is saved in the episode_cache_size_values array. - */ - public static int readEpisodeCacheSize(String valueFromPrefs) { - return readEpisodeCacheSizeInternal(valueFromPrefs); - } - /** * Evaluates whether Cast support (Chromecast, Audio Cast, etc) is enabled on the preferences. */ diff --git a/core/src/main/java/de/danoeh/antennapod/core/receiver/FeedUpdateReceiver.java b/core/src/main/java/de/danoeh/antennapod/core/receiver/FeedUpdateReceiver.java index 126f12247..af0a0d426 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/receiver/FeedUpdateReceiver.java +++ b/core/src/main/java/de/danoeh/antennapod/core/receiver/FeedUpdateReceiver.java @@ -6,8 +6,7 @@ import android.content.Intent; import android.util.Log; import de.danoeh.antennapod.core.ClientConfig; -import de.danoeh.antennapod.core.preferences.UserPreferences; -import de.danoeh.antennapod.core.util.FeedUpdateUtils; +import de.danoeh.antennapod.core.util.download.AutoUpdateManager; /** * Refreshes all feeds when it receives an intent @@ -20,7 +19,8 @@ public class FeedUpdateReceiver extends BroadcastReceiver { public void onReceive(Context context, Intent intent) { Log.d(TAG, "Received intent"); ClientConfig.initialize(context); - FeedUpdateUtils.startAutoUpdate(context, null); + + AutoUpdateManager.runOnce(); } } diff --git a/core/src/main/java/de/danoeh/antennapod/core/receiver/MediaButtonReceiver.java b/core/src/main/java/de/danoeh/antennapod/core/receiver/MediaButtonReceiver.java index b191dbf8b..b683f849c 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/receiver/MediaButtonReceiver.java +++ b/core/src/main/java/de/danoeh/antennapod/core/receiver/MediaButtonReceiver.java @@ -3,7 +3,7 @@ package de.danoeh.antennapod.core.receiver; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.support.v4.content.ContextCompat; +import androidx.core.content.ContextCompat; import android.util.Log; import android.view.KeyEvent; diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/FeedUpdateWorker.java b/core/src/main/java/de/danoeh/antennapod/core/service/FeedUpdateWorker.java index efdb96dc1..4443319e9 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/FeedUpdateWorker.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/FeedUpdateWorker.java @@ -1,18 +1,24 @@ package de.danoeh.antennapod.core.service; import android.content.Context; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; +import android.util.Log; + import androidx.work.Worker; import androidx.work.WorkerParameters; + import de.danoeh.antennapod.core.ClientConfig; import de.danoeh.antennapod.core.preferences.UserPreferences; -import de.danoeh.antennapod.core.util.FeedUpdateUtils; -import org.awaitility.Awaitility; - -import java.util.concurrent.atomic.AtomicBoolean; +import de.danoeh.antennapod.core.storage.DBTasks; +import de.danoeh.antennapod.core.util.NetworkUtils; +import de.danoeh.antennapod.core.util.download.AutoUpdateManager; public class FeedUpdateWorker extends Worker { + private static final String TAG = "FeedUpdateWorker"; + + public static final String PARAM_RUN_ONCE = "runOnce"; + public FeedUpdateWorker(@NonNull Context context, @NonNull WorkerParameters params) { super(context, params); } @@ -20,16 +26,20 @@ public class FeedUpdateWorker extends Worker { @Override @NonNull public Result doWork() { + final boolean isRunOnce = getInputData().getBoolean(PARAM_RUN_ONCE, false); + Log.d(TAG, "doWork() : isRunOnce = " + isRunOnce); ClientConfig.initialize(getApplicationContext()); - AtomicBoolean finished = new AtomicBoolean(false); - FeedUpdateUtils.startAutoUpdate(getApplicationContext(), () -> finished.set(true)); - Awaitility.await().until(finished::get); + if (NetworkUtils.networkAvailable() && NetworkUtils.isFeedRefreshAllowed()) { + DBTasks.refreshAllFeeds(getApplicationContext()); + } else { + Log.d(TAG, "Blocking automatic update: no wifi available / no mobile updates allowed"); + } - if (UserPreferences.isAutoUpdateTimeOfDay()) { + if (!isRunOnce && UserPreferences.isAutoUpdateTimeOfDay()) { // WorkManager does not allow to set specific time for repeated tasks. // We repeatedly schedule a OneTimeWorkRequest instead. - UserPreferences.restartUpdateAlarm(); + AutoUpdateManager.restartUpdateAlarm(); } return Result.success(); diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/GpodnetSyncService.java b/core/src/main/java/de/danoeh/antennapod/core/service/GpodnetSyncService.java index 5584991ca..b254cfc59 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/GpodnetSyncService.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/GpodnetSyncService.java @@ -5,10 +5,10 @@ import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; -import android.support.annotation.NonNull; -import android.support.v4.app.NotificationCompat; -import android.support.v4.app.SafeJobIntentService; -import android.support.v4.util.ArrayMap; +import androidx.annotation.NonNull; +import androidx.core.app.NotificationCompat; +import androidx.core.app.SafeJobIntentService; +import androidx.collection.ArrayMap; import android.util.Log; import android.util.Pair; diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/PlayerWidgetJobService.java b/core/src/main/java/de/danoeh/antennapod/core/service/PlayerWidgetJobService.java index 7938e262d..a3eb92c0c 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/PlayerWidgetJobService.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/PlayerWidgetJobService.java @@ -10,8 +10,8 @@ import android.graphics.Bitmap; import android.os.Build; import android.os.Bundle; import android.os.IBinder; -import android.support.annotation.NonNull; -import android.support.v4.app.SafeJobIntentService; +import androidx.annotation.NonNull; +import androidx.core.app.SafeJobIntentService; import android.util.Log; import android.view.KeyEvent; import android.view.View; @@ -24,6 +24,7 @@ import java.util.concurrent.TimeUnit; import de.danoeh.antennapod.core.R; import de.danoeh.antennapod.core.glide.ApGlideSettings; +import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.receiver.MediaButtonReceiver; import de.danoeh.antennapod.core.receiver.PlayerWidget; import de.danoeh.antennapod.core.service.playback.PlaybackService; @@ -69,9 +70,7 @@ public class PlayerWidgetJobService extends SafeJobIntentService { } synchronized (waitUsingService) { - if (playbackService != null) { - updateViews(); - } + updateViews(); } if (playbackService != null) { @@ -145,9 +144,10 @@ public class PlayerWidgetJobService extends SafeJobIntentService { String progressString; if (playbackService != null) { - progressString = getProgressString(playbackService.getCurrentPosition(), playbackService.getDuration()); + progressString = getProgressString(playbackService.getCurrentPosition(), + playbackService.getDuration(), playbackService.getCurrentPlaybackSpeed()); } else { - progressString = getProgressString(media.getPosition(), media.getDuration()); + progressString = getProgressString(media.getPosition(), media.getDuration(), UserPreferences.getPlaybackSpeed(media)); } if (progressString != null) { @@ -211,9 +211,9 @@ public class PlayerWidgetJobService extends SafeJobIntentService { return PendingIntent.getBroadcast(this, 0, startingIntent, 0); } - private String getProgressString(int position, int duration) { + private String getProgressString(int position, int duration, float speed) { if (position > 0 && duration > 0) { - TimeSpeedConverter converter = new TimeSpeedConverter(playbackService.getCurrentPlaybackSpeed()); + TimeSpeedConverter converter = new TimeSpeedConverter(speed); position = converter.convert(position); duration = converter.convert(duration); return Converter.getDurationStringLong(position) + " / " diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/AntennapodHttpClient.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/AntennapodHttpClient.java index 97007a214..2082b95d1 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/download/AntennapodHttpClient.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/AntennapodHttpClient.java @@ -1,7 +1,7 @@ package de.danoeh.antennapod.core.service.download; import android.os.Build; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import android.text.TextUtils; import android.util.Log; @@ -16,7 +16,9 @@ import java.net.Socket; import java.net.SocketAddress; import java.security.GeneralSecurityException; import java.security.KeyStore; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.concurrent.TimeUnit; import javax.net.ssl.SSLContext; @@ -28,6 +30,8 @@ import javax.net.ssl.X509TrustManager; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.storage.DBWriter; +import okhttp3.CipherSuite; +import okhttp3.ConnectionSpec; import okhttp3.Credentials; import okhttp3.HttpUrl; import okhttp3.JavaNetCookieJar; @@ -138,9 +142,24 @@ public class AntennapodHttpClient { }); } } - if(16 <= Build.VERSION.SDK_INT && Build.VERSION.SDK_INT < 21) { + if (16 <= Build.VERSION.SDK_INT && Build.VERSION.SDK_INT < 21) { builder.sslSocketFactory(new CustomSslSocketFactory(), trustManager()); } + + if (Build.VERSION.SDK_INT < 21) { + // workaround for Android 4.x for certain web sites. + // see: https://github.com/square/okhttp/issues/4053#issuecomment-402579554 + List<CipherSuite> cipherSuites = new ArrayList<>(); + cipherSuites.addAll(ConnectionSpec.MODERN_TLS.cipherSuites()); + cipherSuites.add(CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA); + cipherSuites.add(CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA); + + ConnectionSpec legacyTls = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) + .cipherSuites(cipherSuites.toArray(new CipherSuite[0])) + .build(); + builder.connectionSpecs(Arrays.asList(legacyTls, ConnectionSpec.CLEARTEXT)); + } + return builder; } diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadRequest.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadRequest.java index 48234c387..60591899d 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadRequest.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadRequest.java @@ -3,8 +3,8 @@ package de.danoeh.antennapod.core.service.download; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import de.danoeh.antennapod.core.feed.FeedFile; import de.danoeh.antennapod.core.util.URLChecker; 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 787d465d8..296046031 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 @@ -12,8 +12,10 @@ import android.os.Binder; import android.os.Build; import android.os.Handler; import android.os.IBinder; -import android.support.annotation.NonNull; -import android.support.v4.app.NotificationCompat; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; +import androidx.core.app.NotificationCompat; import android.text.TextUtils; import android.util.Log; import android.util.Pair; @@ -190,10 +192,8 @@ public class DownloadService extends Service { handleFailedDownload(status, downloader.getDownloadRequest()); if (type == FeedMedia.FEEDFILETYPE_FEEDMEDIA) { - long id = status.getFeedfileId(); - FeedMedia media = DBReader.getFeedMedia(id); - FeedItem item; - if (media == null || (item = media.getItem()) == null) { + FeedItem item = getFeedItemFromId(status.getFeedfileId()); + if (item == null) { return; } boolean httpNotFound = status.getReason() == DownloadError.ERROR_HTTP_DATA_ERROR @@ -213,9 +213,8 @@ public class DownloadService extends Service { // if FeedMedia download has been canceled, fake FeedItem update // so that lists reload that it if (status.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA) { - FeedMedia media = DBReader.getFeedMedia(status.getFeedfileId()); - FeedItem item; - if (media == null || (item = media.getItem()) == null) { + FeedItem item = getFeedItemFromId(status.getFeedfileId()); + if (item == null) { return; } EventBus.getDefault().post(FeedItemEvent.updated(item)); @@ -386,6 +385,12 @@ public class DownloadService extends Service { Downloader d = getDownloader(url); if (d != null) { d.cancel(); + DownloadRequester.getInstance().removeDownload(d.getDownloadRequest()); + + FeedItem item = getFeedItemFromId(d.getDownloadRequest().getFeedfileId()); + if (item != null) { + EventBus.getDefault().post(FeedItemEvent.updated(item)); + } } else { Log.e(TAG, "Could not cancel download with url " + url); } @@ -430,12 +435,40 @@ public class DownloadService extends Service { queryDownloads(); } - private Downloader getDownloader(DownloadRequest request) { - if (!URLUtil.isHttpUrl(request.getSource()) && !URLUtil.isHttpsUrl(request.getSource())) { - Log.e(TAG, "Could not find appropriate downloader for " + request.getSource()); - return null; + @VisibleForTesting + public interface DownloaderFactory { + @Nullable + Downloader create(@NonNull DownloadRequest request); + } + + private static class DefaultDownloaderFactory implements DownloaderFactory { + @Nullable + @Override + public Downloader create(@NonNull DownloadRequest request) { + if (!URLUtil.isHttpUrl(request.getSource()) && !URLUtil.isHttpsUrl(request.getSource())) { + Log.e(TAG, "Could not find appropriate downloader for " + request.getSource()); + return null; + } + return new HttpDownloader(request); } - return new HttpDownloader(request); + } + + private static DownloaderFactory downloaderFactory = new DefaultDownloaderFactory(); + + @VisibleForTesting + public static DownloaderFactory getDownloaderFactory() { + return downloaderFactory; + } + + // public scope rather than package private, + // because androidTest put classes in the non-standard de.test.antennapod hierarchy + @VisibleForTesting + public static void setDownloaderFactory(DownloaderFactory downloaderFactory) { + DownloadService.downloaderFactory = downloaderFactory; + } + + private Downloader getDownloader(@NonNull DownloadRequest request) { + return downloaderFactory.create(request); } /** @@ -546,7 +579,7 @@ public class DownloadService extends Service { .setContentText(getText(R.string.authentication_notification_msg)) .setStyle(new NotificationCompat.BigTextStyle().bigText(getText(R.string.authentication_notification_msg) + ": " + resourceTitle)) - .setSmallIcon(R.drawable.ic_stat_authentication) + .setSmallIcon(R.drawable.ic_notification_key) .setAutoCancel(true) .setContentIntent(ClientConfig.downloadServiceCallbacks.getAuthentificationNotificationContentIntent(DownloadService.this, downloadRequest)); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { @@ -578,6 +611,16 @@ public class DownloadService extends Service { syncExecutor.execute(new FailedDownloadHandler(status, request)); } + @Nullable + private FeedItem getFeedItemFromId(long id) { + FeedMedia media = DBReader.getFeedMedia(id); + if (media != null) { + return media.getItem(); + } else { + return null; + } + } + /** * Takes a single Feed, parses the corresponding file and refreshes * information in the manager @@ -654,6 +697,7 @@ public class DownloadService extends Service { Log.e(TAG, "FeedSyncThread was interrupted"); } catch (ExecutionException e) { Log.e(TAG, "ExecutionException in FeedSyncThread: " + e.getMessage()); + e.printStackTrace(); } } @@ -688,6 +732,7 @@ public class DownloadService extends Service { Log.e(TAG, "FeedSyncThread was interrupted"); } catch (ExecutionException e) { Log.e(TAG, "ExecutionException in FeedSyncThread: " + e.getMessage()); + e.printStackTrace(); } } @@ -983,14 +1028,17 @@ public class DownloadService extends Service { final FeedItem item = media.getItem(); try { + DBWriter.setFeedMedia(media).get(); + // we've received the media, we don't want to autodownload it again if (item != null) { item.setAutoDownload(false); + // 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 DBWriter.setFeedItem(item).get(); } - DBWriter.setFeedMedia(media).get(); - if (item != null && UserPreferences.enqueueDownloadedEpisodes() && !DBTasks.isInQueue(DownloadService.this, item.getId())) { DBWriter.addQueueItem(DownloadService.this, item).get(); @@ -1058,7 +1106,13 @@ public class DownloadService extends Service { private final Runnable postDownloaderTask = new Runnable() { @Override public void run() { - List<Downloader> list = Collections.unmodifiableList(downloads); + List<Downloader> runningDownloads = new ArrayList<>(); + for (Downloader downloader : downloads) { + if (!downloader.cancelled) { + runningDownloads.add(downloader); + } + } + List<Downloader> list = Collections.unmodifiableList(runningDownloads); EventBus.getDefault().postSticky(DownloadEvent.refresh(list)); postHandler.postDelayed(postDownloaderTask, 1500); } @@ -1076,7 +1130,10 @@ public class DownloadService extends Service { private static String compileNotificationString(List<Downloader> downloads) { List<String> lines = new ArrayList<>(downloads.size()); for (Downloader downloader : downloads) { - StringBuilder line = new StringBuilder("\u2022 "); + if (downloader.cancelled) { + continue; + } + StringBuilder line = new StringBuilder("• "); DownloadRequest request = downloader.getDownloadRequest(); switch (request.getFeedfileType()) { case Feed.FEEDFILETYPE_FEED: diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadStatus.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadStatus.java index 5debc6d05..675115bc7 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadStatus.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadStatus.java @@ -1,7 +1,7 @@ package de.danoeh.antennapod.core.service.download; import android.database.Cursor; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import java.util.Date; @@ -193,10 +193,6 @@ public class DownloadStatus { this.cancelled = true; } - public void setCompletionDate(Date completionDate) { - this.completionDate = (Date) completionDate.clone(); - } - public void setId(long id) { this.id = id; } diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/Downloader.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/Downloader.java index 38b93eab8..02dc17301 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/download/Downloader.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/Downloader.java @@ -2,7 +2,7 @@ package de.danoeh.antennapod.core.service.download; import android.content.Context; import android.net.wifi.WifiManager; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import java.util.concurrent.Callable; 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 c27cefc10..8abfa3da3 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 @@ -1,6 +1,6 @@ package de.danoeh.antennapod.core.service.download; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import android.text.TextUtils; import android.util.Log; diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/ProxyConfig.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/ProxyConfig.java index c4096c3da..0d59a1b7e 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/download/ProxyConfig.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/ProxyConfig.java @@ -1,6 +1,6 @@ package de.danoeh.antennapod.core.service.download; -import android.support.annotation.Nullable; +import androidx.annotation.Nullable; import java.net.Proxy; 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 f20525f73..c988ef70e 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/ExoPlayerWrapper.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/ExoPlayerWrapper.java @@ -2,6 +2,7 @@ package de.danoeh.antennapod.core.service.playback; import android.content.Context; import android.net.Uri; +import android.util.Log; import android.view.SurfaceHolder; import com.google.android.exoplayer2.C; @@ -22,28 +23,52 @@ import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.trackselection.TrackSelectionArray; 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.util.Util; +import de.danoeh.antennapod.core.preferences.UserPreferences; +import io.reactivex.Observable; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; import org.antennapod.audio.MediaPlayer; import de.danoeh.antennapod.core.util.playback.IPlayer; +import java.util.concurrent.TimeUnit; public class ExoPlayerWrapper implements IPlayer { + private static final String TAG = "ExoPlayerWrapper"; private final Context mContext; + private final Disposable bufferingUpdateDisposable; private SimpleExoPlayer mExoPlayer; private MediaSource mediaSource; private MediaPlayer.OnSeekCompleteListener audioSeekCompleteListener; private MediaPlayer.OnCompletionListener audioCompletionListener; private MediaPlayer.OnErrorListener audioErrorListener; + private MediaPlayer.OnBufferingUpdateListener bufferingUpdateListener; + ExoPlayerWrapper(Context context) { mContext = context; mExoPlayer = createPlayer(); + + bufferingUpdateDisposable = Observable.interval(2, TimeUnit.SECONDS) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(aLong -> { + if (bufferingUpdateListener != null) { + bufferingUpdateListener.onBufferingUpdate(null, mExoPlayer.getBufferedPercentage()); + } + }); } private SimpleExoPlayer createPlayer() { + DefaultLoadControl.Builder loadControl = new DefaultLoadControl.Builder(); + loadControl.setBufferDurationsMs(30000, 120000, + DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_MS, + DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS); + loadControl.setBackBuffer(UserPreferences.getRewindSecs() * 1000 + 500, true); SimpleExoPlayer p = ExoPlayerFactory.newSimpleInstance(mContext, new DefaultRenderersFactory(mContext), - new DefaultTrackSelector(), new DefaultLoadControl()); + new DefaultTrackSelector(), loadControl.createDefaultLoadControl()); p.setSeekParameters(SeekParameters.PREVIOUS_SYNC); p.addListener(new Player.EventListener() { @Override @@ -148,12 +173,14 @@ public class ExoPlayerWrapper implements IPlayer { @Override public void release() { + bufferingUpdateDisposable.dispose(); if (mExoPlayer != null) { mExoPlayer.release(); } audioSeekCompleteListener = null; audioCompletionListener = null; audioErrorListener = null; + bufferingUpdateListener = null; } @Override @@ -180,8 +207,13 @@ public class ExoPlayerWrapper implements IPlayer { @Override public void setDataSource(String s) throws IllegalArgumentException, IllegalStateException { - DataSource.Factory dataSourceFactory = - new DefaultDataSourceFactory(mContext, Util.getUserAgent(mContext, mContext.getPackageName()), null); + Log.d(TAG, "setDataSource: " + s); + DefaultHttpDataSourceFactory httpDataSourceFactory = new DefaultHttpDataSourceFactory( + Util.getUserAgent(mContext, mContext.getPackageName()), null, + DefaultHttpDataSource.DEFAULT_CONNECT_TIMEOUT_MILLIS, + DefaultHttpDataSource.DEFAULT_READ_TIMEOUT_MILLIS, + true); + DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(mContext, null, httpDataSourceFactory); ExtractorMediaSource.Factory f = new ExtractorMediaSource.Factory(dataSourceFactory); mediaSource = f.createMediaSource(Uri.parse(s)); } @@ -247,4 +279,8 @@ public class ExoPlayerWrapper implements IPlayer { } return mExoPlayer.getVideoFormat().height; } + + void setOnBufferingUpdateListener(MediaPlayer.OnBufferingUpdateListener bufferingUpdateListener) { + this.bufferingUpdateListener = bufferingUpdateListener; + } } 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 72f5f183a..c982cb1e9 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 @@ -6,7 +6,7 @@ import android.media.AudioFocusRequest; import android.media.AudioManager; import android.os.Build; import android.os.PowerManager; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import android.telephony.TelephonyManager; import android.util.Log; import android.util.Pair; @@ -585,7 +585,6 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer { } playerLock.unlock(); - Log.d(TAG, "getPosition() -> " + retVal); return retVal; } @@ -1026,6 +1025,7 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer { ExoPlayerWrapper ap = (ExoPlayerWrapper) mp; ap.setOnCompletionListener(audioCompletionListener); ap.setOnSeekCompleteListener(audioSeekCompleteListener); + ap.setOnBufferingUpdateListener(audioBufferingUpdateListener); ap.setOnErrorListener(audioErrorListener); } else { Log.w(TAG, "Unknown media player: " + mp); 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 e479a7711..2eb9c92e4 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 @@ -12,7 +12,6 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.graphics.Bitmap; -import android.graphics.BitmapFactory; import android.media.AudioManager; import android.media.MediaPlayer; import android.net.Uri; @@ -22,12 +21,12 @@ import android.os.Bundle; import android.os.IBinder; import android.os.Vibrator; import android.preference.PreferenceManager; -import android.support.annotation.NonNull; -import android.support.annotation.StringRes; -import android.support.v4.app.NotificationCompat; -import android.support.v4.app.NotificationManagerCompat; +import androidx.annotation.NonNull; +import androidx.annotation.StringRes; +import androidx.core.app.NotificationCompat; +import androidx.core.app.NotificationManagerCompat; import android.support.v4.media.MediaBrowserCompat; -import android.support.v4.media.MediaBrowserServiceCompat; +import androidx.media.MediaBrowserServiceCompat; import android.support.v4.media.MediaDescriptionCompat; import android.support.v4.media.MediaMetadataCompat; import android.support.v4.media.session.MediaSessionCompat; @@ -45,10 +44,12 @@ import com.bumptech.glide.request.target.Target; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.TimeUnit; import de.danoeh.antennapod.core.ClientConfig; 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.feed.Chapter; import de.danoeh.antennapod.core.feed.Feed; @@ -66,7 +67,6 @@ import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.storage.DBTasks; import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.core.storage.FeedSearcher; -import de.danoeh.antennapod.core.util.IntList; import de.danoeh.antennapod.core.util.IntentUtils; import de.danoeh.antennapod.core.util.NetworkUtils; import de.danoeh.antennapod.core.util.QueueAccess; @@ -74,6 +74,9 @@ import de.danoeh.antennapod.core.util.gui.NotificationUtils; import de.danoeh.antennapod.core.util.playback.ExternalMedia; import de.danoeh.antennapod.core.util.playback.Playable; import de.danoeh.antennapod.core.util.playback.PlaybackServiceStarter; +import io.reactivex.Observable; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; import org.greenrobot.eventbus.EventBus; /** @@ -212,6 +215,7 @@ public class PlaybackService extends MediaBrowserServiceCompat { private PlaybackServiceTaskManager taskManager; private PlaybackServiceFlavorHelper flavorHelper; private PlaybackServiceStateManager stateManager; + private Disposable positionEventTimer; /** * Used for Lollipop notifications, Android Wear, and Android Auto. @@ -331,8 +335,8 @@ public class PlaybackService extends MediaBrowserServiceCompat { isRunning = false; currentMediaType = MediaType.UNKNOWN; - PreferenceManager.getDefaultSharedPreferences(this) - .unregisterOnSharedPreferenceChangeListener(prefListener); + cancelPositionObserver(); + PreferenceManager.getDefaultSharedPreferences(this).unregisterOnSharedPreferenceChangeListener(prefListener); if (mediaSession != null) { mediaSession.release(); } @@ -451,7 +455,7 @@ public class PlaybackService extends MediaBrowserServiceCompat { notificationBuilder.loadIcon(getPlayable()); } } - startForeground(NOTIFICATION_ID, notificationBuilder.build()); + stateManager.startForeground(NOTIFICATION_ID, notificationBuilder.build()); } NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this); @@ -566,7 +570,7 @@ public class PlaybackService extends MediaBrowserServiceCompat { case KeyEvent.KEYCODE_HEADSETHOOK: case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: if (status == PlayerStatus.PLAYING) { - mediaPlayer.pause(!UserPreferences.isPersistNotify(), true); + mediaPlayer.pause(!UserPreferences.isPersistNotify(), false); } else if (status == PlayerStatus.PAUSED || status == PlayerStatus.PREPARED) { mediaPlayer.resume(); } else if (status == PlayerStatus.PREPARING) { @@ -590,7 +594,7 @@ public class PlaybackService extends MediaBrowserServiceCompat { return true; case KeyEvent.KEYCODE_MEDIA_PAUSE: if (status == PlayerStatus.PLAYING) { - mediaPlayer.pause(!UserPreferences.isPersistNotify(), true); + mediaPlayer.pause(!UserPreferences.isPersistNotify(), false); } return true; @@ -717,7 +721,8 @@ public class PlaybackService extends MediaBrowserServiceCompat { updateMediaSession(newInfo.playerStatus); switch (newInfo.playerStatus) { case INITIALIZED: - writePlaybackPreferences(); + PlaybackPreferences.writeMediaPlaying(mediaPlayer.getPSMPInfo().playable, + mediaPlayer.getPSMPInfo().playerStatus, mediaPlayer.isStreaming()); break; case PREPARED: @@ -734,7 +739,8 @@ public class PlaybackService extends MediaBrowserServiceCompat { // remove notification on pause stateManager.stopForeground(true); } - writePlayerStatusPlaybackPreferences(); + cancelPositionObserver(); + PlaybackPreferences.writePlayerStatus(mediaPlayer.getPlayerStatus()); break; case STOPPED: @@ -743,8 +749,9 @@ public class PlaybackService extends MediaBrowserServiceCompat { break; case PLAYING: - writePlayerStatusPlaybackPreferences(); + PlaybackPreferences.writePlayerStatus(mediaPlayer.getPlayerStatus()); setupNotification(newInfo); + setupPositionUpdater(); stateManager.validStartCommandWasReceived(); // set sleep timer if auto-enabled if (newInfo.oldPlayerStatus != null && newInfo.oldPlayerStatus != PlayerStatus.SEEKING && @@ -1023,85 +1030,6 @@ public class PlaybackService extends MediaBrowserServiceCompat { EventBus.getDefault().post(new MessageEvent(getString(R.string.sleep_timer_disabled_label))); } - private int getCurrentPlayerStatusAsInt(PlayerStatus playerStatus) { - int playerStatusAsInt; - switch (playerStatus) { - case PLAYING: - playerStatusAsInt = PlaybackPreferences.PLAYER_STATUS_PLAYING; - break; - case PAUSED: - playerStatusAsInt = PlaybackPreferences.PLAYER_STATUS_PAUSED; - break; - default: - playerStatusAsInt = PlaybackPreferences.PLAYER_STATUS_OTHER; - } - return playerStatusAsInt; - } - - private void writePlaybackPreferences() { - Log.d(TAG, "Writing playback preferences"); - - SharedPreferences.Editor editor = PreferenceManager - .getDefaultSharedPreferences(getApplicationContext()).edit(); - PlaybackServiceMediaPlayer.PSMPInfo info = mediaPlayer.getPSMPInfo(); - MediaType mediaType = mediaPlayer.getCurrentMediaType(); - boolean stream = mediaPlayer.isStreaming(); - int playerStatus = getCurrentPlayerStatusAsInt(info.playerStatus); - - if (info.playable != null) { - editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_MEDIA, - info.playable.getPlayableType()); - editor.putBoolean( - PlaybackPreferences.PREF_CURRENT_EPISODE_IS_STREAM, - stream); - editor.putBoolean( - PlaybackPreferences.PREF_CURRENT_EPISODE_IS_VIDEO, - mediaType == MediaType.VIDEO); - if (info.playable instanceof FeedMedia) { - FeedMedia fMedia = (FeedMedia) info.playable; - editor.putLong( - PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEED_ID, - fMedia.getItem().getFeed().getId()); - editor.putLong( - PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID, - fMedia.getId()); - } else { - editor.putLong( - PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEED_ID, - PlaybackPreferences.NO_MEDIA_PLAYING); - editor.putLong( - PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID, - PlaybackPreferences.NO_MEDIA_PLAYING); - } - info.playable.writeToPreferences(editor); - } else { - editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_MEDIA, - PlaybackPreferences.NO_MEDIA_PLAYING); - editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEED_ID, - PlaybackPreferences.NO_MEDIA_PLAYING); - editor.putLong( - PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID, - PlaybackPreferences.NO_MEDIA_PLAYING); - } - editor.putInt( - PlaybackPreferences.PREF_CURRENT_PLAYER_STATUS, playerStatus); - - editor.commit(); - } - - private void writePlayerStatusPlaybackPreferences() { - Log.d(TAG, "Writing player status playback preferences"); - - SharedPreferences.Editor editor = PreferenceManager - .getDefaultSharedPreferences(getApplicationContext()).edit(); - int playerStatus = getCurrentPlayerStatusAsInt(mediaPlayer.getPlayerStatus()); - - editor.putInt( - PlaybackPreferences.PREF_CURRENT_PLAYER_STATUS, playerStatus); - - editor.commit(); - } - private void sendNotificationBroadcast(int type, int code) { Intent intent = new Intent(ACTION_PLAYER_NOTIFICATION); intent.putExtra(EXTRA_NOTIFICATION_TYPE, type); @@ -1656,6 +1584,24 @@ public class PlaybackService extends MediaBrowserServiceCompat { return mediaPlayer.getVideoSize(); } + private void setupPositionUpdater() { + if (positionEventTimer != null) { + positionEventTimer.dispose(); + } + + Log.d(TAG, "Setting up position observer"); + positionEventTimer = Observable.interval(1, TimeUnit.SECONDS) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(aLong -> + EventBus.getDefault().post(new PlaybackPositionEvent(getCurrentPosition(), getDuration()))); + } + + private void cancelPositionObserver() { + if (positionEventTimer != null) { + positionEventTimer.dispose(); + } + } + private final MediaSessionCompat.Callback sessionCallback = new MediaSessionCompat.Callback() { private static final String TAG = "MediaSessionCompat"; 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 b8198fa63..bc009e2bc 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 @@ -2,8 +2,8 @@ package de.danoeh.antennapod.core.service.playback; import android.content.Context; import android.net.wifi.WifiManager; -import android.support.annotation.NonNull; -import android.support.annotation.StringRes; +import androidx.annotation.NonNull; +import androidx.annotation.StringRes; import android.util.Log; import android.util.Pair; import android.view.SurfaceHolder; 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 0e7339ac4..b809ad659 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 @@ -1,13 +1,18 @@ package de.danoeh.antennapod.core.service.playback; +import android.annotation.TargetApi; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; -import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.VectorDrawable; import android.os.Build; -import android.support.annotation.NonNull; -import android.support.v4.app.NotificationCompat; +import androidx.annotation.NonNull; +import androidx.core.app.NotificationCompat; +import androidx.core.content.ContextCompat; import android.support.v4.media.session.MediaSessionCompat; import android.util.Log; import android.view.KeyEvent; @@ -99,12 +104,32 @@ public class PlaybackServiceNotificationBuilder extends NotificationCompat.Build public void loadDefaultIcon() { if (defaultIcon == null) { - defaultIcon = BitmapFactory.decodeResource(context.getResources(), - ClientConfig.playbackServiceCallbacks.getNotificationIconResource(context)); + defaultIcon = getBitmap(context, R.drawable.notification_default_large_icon); } setLargeIcon(defaultIcon); } + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + private static Bitmap getBitmap(VectorDrawable vectorDrawable) { + Bitmap bitmap = Bitmap.createBitmap(vectorDrawable.getIntrinsicWidth(), + vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + vectorDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); + vectorDrawable.draw(canvas); + return bitmap; + } + + private static Bitmap getBitmap(Context context, int drawableId) { + Drawable drawable = ContextCompat.getDrawable(context, drawableId); + if (drawable instanceof BitmapDrawable) { + return ((BitmapDrawable) drawable).getBitmap(); + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && drawable instanceof VectorDrawable) { + return getBitmap((VectorDrawable) drawable); + } else { + return null; + } + } + private void addActions(MediaSessionCompat.Token mediaSessionToken, PlayerStatus playerStatus, boolean isCasting) { IntList compactActionList = new IntList(); @@ -115,7 +140,7 @@ public class PlaybackServiceNotificationBuilder extends NotificationCompat.Build stopCastingIntent.putExtra(PlaybackService.EXTRA_CAST_DISCONNECT, true); PendingIntent stopCastingPendingIntent = PendingIntent.getService(context, numActions, stopCastingIntent, PendingIntent.FLAG_UPDATE_CURRENT); - addAction(R.drawable.ic_media_cast_disconnect, + addAction(R.drawable.ic_notification_cast_off, context.getString(R.string.cast_disconnect_label), stopCastingPendingIntent); numActions++; @@ -124,7 +149,7 @@ public class PlaybackServiceNotificationBuilder extends NotificationCompat.Build // always let them rewind PendingIntent rewindButtonPendingIntent = getPendingIntentForMediaAction( KeyEvent.KEYCODE_MEDIA_REWIND, numActions); - addAction(android.R.drawable.ic_media_rew, context.getString(R.string.rewind_label), rewindButtonPendingIntent); + addAction(R.drawable.ic_notification_fast_rewind, context.getString(R.string.rewind_label), rewindButtonPendingIntent); if (UserPreferences.showRewindOnCompactNotification()) { compactActionList.add(numActions); } @@ -133,14 +158,14 @@ public class PlaybackServiceNotificationBuilder extends NotificationCompat.Build if (playerStatus == PlayerStatus.PLAYING) { PendingIntent pauseButtonPendingIntent = getPendingIntentForMediaAction( KeyEvent.KEYCODE_MEDIA_PAUSE, numActions); - addAction(android.R.drawable.ic_media_pause, //pause action + addAction(R.drawable.ic_notification_pause, //pause action context.getString(R.string.pause_label), pauseButtonPendingIntent); compactActionList.add(numActions++); } else { PendingIntent playButtonPendingIntent = getPendingIntentForMediaAction( KeyEvent.KEYCODE_MEDIA_PLAY, numActions); - addAction(android.R.drawable.ic_media_play, //play action + addAction(R.drawable.ic_notification_play, //play action context.getString(R.string.play_label), playButtonPendingIntent); compactActionList.add(numActions++); @@ -149,7 +174,7 @@ public class PlaybackServiceNotificationBuilder extends NotificationCompat.Build // ff follows play, then we have skip (if it's present) PendingIntent ffButtonPendingIntent = getPendingIntentForMediaAction( KeyEvent.KEYCODE_MEDIA_FAST_FORWARD, numActions); - addAction(android.R.drawable.ic_media_ff, context.getString(R.string.fast_forward_label), ffButtonPendingIntent); + addAction(R.drawable.ic_notification_fast_forward, context.getString(R.string.fast_forward_label), ffButtonPendingIntent); if (UserPreferences.showFastForwardOnCompactNotification()) { compactActionList.add(numActions); } @@ -158,7 +183,7 @@ public class PlaybackServiceNotificationBuilder extends NotificationCompat.Build if (UserPreferences.isFollowQueue()) { PendingIntent skipButtonPendingIntent = getPendingIntentForMediaAction( KeyEvent.KEYCODE_MEDIA_NEXT, numActions); - addAction(android.R.drawable.ic_media_next, + addAction(R.drawable.ic_notification_skip, context.getString(R.string.skip_episode_label), skipButtonPendingIntent); if (UserPreferences.showSkipOnCompactNotification()) { @@ -169,7 +194,7 @@ public class PlaybackServiceNotificationBuilder extends NotificationCompat.Build PendingIntent stopButtonPendingIntent = getPendingIntentForMediaAction( KeyEvent.KEYCODE_MEDIA_STOP, numActions); - setStyle(new android.support.v4.media.app.NotificationCompat.MediaStyle() + setStyle(new androidx.media.app.NotificationCompat.MediaStyle() .setMediaSession(mediaSessionToken) .setShowActionsInCompactView(compactActionList.toArray()) .setShowCancelButton(true) 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 68839023e..1cf271a29 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 @@ -4,9 +4,12 @@ import android.content.Context; import android.os.Handler; import android.os.Looper; import android.os.Vibrator; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import android.util.Log; +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.Subscribe; + import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; @@ -14,13 +17,12 @@ 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.core.feed.FeedItem; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.util.playback.Playable; -import org.greenrobot.eventbus.EventBus; -import org.greenrobot.eventbus.Subscribe; import io.reactivex.Completable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.schedulers.Schedulers; @@ -102,6 +104,34 @@ public class PlaybackServiceTaskManager { } } + @Subscribe + public void onEvent(FeedItemEvent event) { + // Use case: when an item in the queue has been downloaded, + // listening to the event to ensure the downloaded item will be used. + Log.d(TAG, "onEvent(FeedItemEvent " + event + ")"); + + for (FeedItem item : event.items) { + if (isItemInQueue(item.getId())) { + Log.d(TAG, "onEvent(FeedItemEvent) - some item (" + item.getId() + ") in the queue has been updated (usually downloaded). Refresh the queue."); + cancelQueueLoader(); + loadQueue(); + return; + } + } + } + + private boolean isItemInQueue(long itemId) { + List<FeedItem> queue = getQueueIfLoaded(); + if (queue != null) { + for (FeedItem item : queue) { + if (item.getId() == itemId) { + return true; + } + } + } + return false; + } + /** * Returns the queue if it is already loaded or null if it hasn't been loaded yet. * In order to wait until the queue has been loaded, use getQueue() diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/APCleanupAlgorithm.java b/core/src/main/java/de/danoeh/antennapod/core/storage/APCleanupAlgorithm.java index c63ac4416..b88793e87 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/APCleanupAlgorithm.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/APCleanupAlgorithm.java @@ -1,8 +1,8 @@ package de.danoeh.antennapod.core.storage; import android.content.Context; -import android.support.annotation.NonNull; -import android.support.annotation.VisibleForTesting; +import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; import android.util.Log; import java.util.ArrayList; diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/APQueueCleanupAlgorithm.java b/core/src/main/java/de/danoeh/antennapod/core/storage/APQueueCleanupAlgorithm.java index 04b200699..dfe0b5201 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/APQueueCleanupAlgorithm.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/APQueueCleanupAlgorithm.java @@ -1,7 +1,7 @@ package de.danoeh.antennapod.core.storage; import android.content.Context; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import android.util.Log; import java.util.ArrayList; diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java index 70d3ba9dd..948a21efc 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java @@ -1,9 +1,9 @@ package de.danoeh.antennapod.core.storage; import android.database.Cursor; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v4.util.ArrayMap; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.collection.ArrayMap; import android.text.TextUtils; import android.util.Log; 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 e68bff16e..46fa4b99c 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 @@ -3,7 +3,8 @@ package de.danoeh.antennapod.core.storage; import android.content.Context; import android.content.SharedPreferences; import android.database.Cursor; -import android.support.annotation.Nullable; +import android.os.Looper; +import android.text.TextUtils; import android.util.Log; import java.util.ArrayList; @@ -144,58 +145,36 @@ public final class DBTasks { private static final AtomicBoolean isRefreshing = new AtomicBoolean(false); /** - * Refreshes a given list of Feeds in a separate Thread. This method might ignore subsequent calls if it is still + * Refreshes all feeds. + * It must not be from the main thread. + * This method might ignore subsequent calls if it is still * enqueuing Feeds for download from a previous call * * @param context Might be used for accessing the database - * @param feeds List of Feeds that should be refreshed. */ - public static void refreshAllFeeds(final Context context, final List<Feed> feeds) { - refreshAllFeeds(context, feeds, null); - } - - /** - * Refreshes a given list of Feeds in a separate Thread. This method might ignore subsequent calls if it is still - * enqueuing Feeds for download from a previous call - * - * @param context Might be used for accessing the database - * @param feeds List of Feeds that should be refreshed. - * @param callback Called after everything was added enqueued for download. Might be null. - */ - public static void refreshAllFeeds(final Context context, final List<Feed> feeds, @Nullable Runnable callback) { + public static void refreshAllFeeds(final Context context) { if (!isRefreshing.compareAndSet(false, true)) { Log.d(TAG, "Ignoring request to refresh all feeds: Refresh lock is locked"); return; } - new Thread(() -> { - if (feeds != null) { - refreshFeeds(context, feeds); - } else { - refreshFeeds(context, DBReader.getFeedList()); - } - isRefreshing.set(false); + if (Looper.myLooper() == Looper.getMainLooper()) { + throw new IllegalStateException("DBTasks.refreshAllFeeds() must not be called from the main thread."); + } - SharedPreferences prefs = context.getSharedPreferences(PREF_NAME, MODE_PRIVATE); - prefs.edit().putLong(PREF_LAST_REFRESH, System.currentTimeMillis()).apply(); + refreshFeeds(context, DBReader.getFeedList()); + isRefreshing.set(false); - if (ClientConfig.gpodnetCallbacks.gpodnetEnabled()) { - GpodnetSyncService.sendSyncIntent(context); - } - // Note: automatic download of episodes will be done but not here. - // Instead it is done after all feeds have been refreshed (asynchronously), - // in DownloadService.onDestroy() - // See Issue #2577 for the details of the rationale + SharedPreferences prefs = context.getSharedPreferences(PREF_NAME, MODE_PRIVATE); + prefs.edit().putLong(PREF_LAST_REFRESH, System.currentTimeMillis()).apply(); - if (callback != null) { - callback.run(); - } - }).start(); - } - - public static long getLastRefreshAllFeedsTimeMillis(final Context context) { - SharedPreferences prefs = context.getSharedPreferences(DBTasks.PREF_NAME, MODE_PRIVATE); - return prefs.getLong(DBTasks.PREF_LAST_REFRESH, 0); + if (ClientConfig.gpodnetCallbacks.gpodnetEnabled()) { + GpodnetSyncService.sendSyncIntent(context); + } + // Note: automatic download of episodes will be done but not here. + // Instead it is done after all feeds have been refreshed (asynchronously), + // in DownloadService.onDestroy() + // See Issue #2577 for the details of the rationale } /** @@ -462,10 +441,9 @@ public final class DBTasks { /** * Get a FeedItem by its identifying value. */ - private static FeedItem searchFeedItemByIdentifyingValue(Feed feed, - String identifier) { + private static FeedItem searchFeedItemByIdentifyingValue(Feed feed, String identifier) { for (FeedItem item : feed.getItems()) { - if (item.getIdentifyingValue().equals(identifier)) { + if (TextUtils.equals(item.getIdentifyingValue(), identifier)) { return item; } } 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 4f0ee70ef..11d90e57c 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 @@ -2,10 +2,7 @@ package de.danoeh.antennapod.core.storage; import android.app.backup.BackupManager; import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.preference.PreferenceManager; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import android.util.Log; import org.greenrobot.eventbus.EventBus; diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DownloadRequester.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DownloadRequester.java index 892a4675a..71f6845c5 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/DownloadRequester.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DownloadRequester.java @@ -3,8 +3,8 @@ package de.danoeh.antennapod.core.storage; import android.content.Context; import android.content.Intent; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.v4.content.ContextCompat; +import androidx.annotation.NonNull; +import androidx.core.content.ContextCompat; import android.text.TextUtils; import android.util.Log; import android.webkit.URLUtil; @@ -34,7 +34,6 @@ import de.danoeh.antennapod.core.util.URLChecker; public class DownloadRequester { private static final String TAG = "DownloadRequester"; - public static final String IMAGE_DOWNLOADPATH = "images/"; private static final String FEED_DOWNLOADPATH = "cache/"; private static final String MEDIA_DOWNLOADPATH = "media/"; @@ -274,10 +273,6 @@ public class DownloadRequester { return item.getDownload_url() != null && downloads.containsKey(item.getDownload_url()); } - public synchronized DownloadRequest getDownload(String downloadUrl) { - return downloads.get(downloadUrl); - } - /** * Checks if feedfile with the given download url is in the downloads list */ diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/FeedSearcher.java b/core/src/main/java/de/danoeh/antennapod/core/storage/FeedSearcher.java index c6620a90e..0c8d20007 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/FeedSearcher.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/FeedSearcher.java @@ -1,7 +1,7 @@ package de.danoeh.antennapod.core.storage; import android.content.Context; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import java.util.ArrayList; import java.util.Collections; 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 0c93590e2..4de83f548 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 @@ -13,6 +13,8 @@ import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase.CursorFactory; import android.database.sqlite.SQLiteOpenHelper; import android.os.Build; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import android.text.TextUtils; import android.util.Log; @@ -25,8 +27,6 @@ import java.util.Collections; import java.util.List; import java.util.Set; -import de.danoeh.antennapod.core.R; -import de.danoeh.antennapod.core.event.ProgressEvent; import de.danoeh.antennapod.core.feed.Chapter; import de.danoeh.antennapod.core.feed.Feed; import de.danoeh.antennapod.core.feed.FeedItem; @@ -35,7 +35,6 @@ import de.danoeh.antennapod.core.feed.FeedPreferences; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.service.download.DownloadStatus; import de.danoeh.antennapod.core.util.LongIntMap; -import org.greenrobot.eventbus.EventBus; import static de.danoeh.antennapod.core.feed.FeedMedia.LAST_PLAYBACK_SPEED_UNSET; import static de.danoeh.antennapod.core.feed.FeedPreferences.SPEED_USE_GLOBAL; @@ -55,16 +54,10 @@ public class PodDBAdapter { */ private static final int IN_OPERATOR_MAXIMUM = 800; - /** - * Maximum number of entries per search request. - */ - public static final int SEARCH_LIMIT = 30; - // Key-constants public static final String KEY_ID = "id"; public static final String KEY_TITLE = "title"; public static final String KEY_CUSTOM_TITLE = "custom_title"; - public static final String KEY_NAME = "name"; public static final String KEY_LINK = "link"; public static final String KEY_DESCRIPTION = "description"; public static final String KEY_FILE_URL = "file_url"; @@ -1410,13 +1403,6 @@ public class PodDBAdapter { return db.rawQuery(query, null); } - - public static final int IDX_FEEDSTATISTICS_FEED = 0; - public static final int IDX_FEEDSTATISTICS_NUM_ITEMS = 1; - public static final int IDX_FEEDSTATISTICS_NEW_ITEMS = 2; - public static final int IDX_FEEDSTATISTICS_LATEST_EPISODE = 3; - public static final int IDX_FEEDSTATISTICS_IN_PROGRESS_EPISODES = 4; - /** * Select number of items, new items, the date of the latest episode and the number of episodes in progress. The result * is sorted by the title of the feed. @@ -1501,11 +1487,9 @@ public class PodDBAdapter { @Override public void onUpgrade(final SQLiteDatabase db, final int oldVersion, final int newVersion) { - EventBus.getDefault().post(ProgressEvent.start(context.getString(R.string.progress_upgrading_database))); Log.w("DBAdapter", "Upgrading from version " + oldVersion + " to " + newVersion + "."); DBUpgrader.upgrade(db, oldVersion, newVersion); - EventBus.getDefault().post(ProgressEvent.end()); } } } diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/HandlerState.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/HandlerState.java index 1cd05aa26..608cade88 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/HandlerState.java +++ b/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/HandlerState.java @@ -1,6 +1,6 @@ package de.danoeh.antennapod.core.syndication.handler; -import android.support.v4.util.ArrayMap; +import androidx.collection.ArrayMap; import java.util.ArrayList; import java.util.Map; diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/ChapterUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/ChapterUtils.java index e6d4ed6b7..300e0038a 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/ChapterUtils.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/ChapterUtils.java @@ -1,7 +1,7 @@ package de.danoeh.antennapod.core.util; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import android.util.Log; import org.apache.commons.io.IOUtils; diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/DuckType.java b/core/src/main/java/de/danoeh/antennapod/core/util/DuckType.java deleted file mode 100644 index 69dc38895..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/util/DuckType.java +++ /dev/null @@ -1,117 +0,0 @@ -/* Adapted from: http://thinking-in-code.blogspot.com/2008/11/duck-typing-in-java-using-dynamic.html */ - -package de.danoeh.antennapod.core.util; - -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; - -import de.danoeh.antennapod.core.BuildConfig; - -/** - * Allows "duck typing" or dynamic invocation based on method signature rather - * than type hierarchy. In other words, rather than checking whether something - * IS-a duck, check whether it WALKS-like-a duck or QUACKS-like a duck. - * - * To use first use the coerce static method to indicate the object you want to - * do Duck Typing for, then specify an interface to the to method which you want - * to coerce the type to, e.g: - * - * public interface Foo { void aMethod(); } class Bar { ... public void - * aMethod() { ... } ... } Bar bar = ...; Foo foo = - * DuckType.coerce(bar).to(Foo.class); foo.aMethod(); - * - * - */ -public class DuckType { - - private final Object objectToCoerce; - - private DuckType(Object objectToCoerce) { - this.objectToCoerce = objectToCoerce; - } - - private class CoercedProxy implements InvocationHandler { - @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - Method delegateMethod = findMethodBySignature(method); - assert delegateMethod != null; - return delegateMethod.invoke(DuckType.this.objectToCoerce, args); - } - } - - /** - * Specify the duck typed object to coerce. - * - * @param object - * the object to coerce - * @return - */ - public static DuckType coerce(Object object) { - return new DuckType(object); - } - - /** - * Coerce the Duck Typed object to the given interface providing it - * implements all the necessary methods. - * - * @param - * @param iface - * @return an instance of the given interface that wraps the duck typed - * class - * @throws ClassCastException - * if the object being coerced does not implement all the - * methods in the given interface. - */ - @SuppressWarnings({ "rawtypes", "unchecked" }) - public <T> T to(Class iface) { - if (BuildConfig.DEBUG && !iface.isInterface()) throw new AssertionError("cannot coerce object to a class, must be an interface"); - if (isA(iface)) { - return (T) iface.cast(objectToCoerce); - } - if (quacksLikeA(iface)) { - return generateProxy(iface); - } - throw new ClassCastException("Could not coerce object of type " + objectToCoerce.getClass() + " to " + iface); - } - - @SuppressWarnings("rawtypes") - private boolean isA(Class iface) { - return objectToCoerce.getClass().isInstance(iface); - } - - /** - * Determine whether the duck typed object can be used with the given - * interface. - * - * @param Type - * of the interface to check. - * @param iface - * Interface class to check - * @return true if the object will support all the methods in the interface, - * false otherwise. - */ - @SuppressWarnings("rawtypes") - private boolean quacksLikeA(Class iface) { - for (Method method : iface.getMethods()) { - if (findMethodBySignature(method) == null) { - return false; - } - } - return true; - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - private <T> T generateProxy(Class iface) { - return (T) Proxy.newProxyInstance(iface.getClassLoader(), new Class[] { iface }, new CoercedProxy()); - } - - private Method findMethodBySignature(Method method) { - try { - return objectToCoerce.getClass().getMethod(method.getName(), method.getParameterTypes()); - } catch (NoSuchMethodException e) { - return null; - } - } - -}
\ No newline at end of file diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/FeedItemUtil.java b/core/src/main/java/de/danoeh/antennapod/core/util/FeedItemUtil.java index 826c06822..a8206d3bd 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/FeedItemUtil.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/FeedItemUtil.java @@ -7,19 +7,6 @@ import de.danoeh.antennapod.core.feed.FeedItem; public class FeedItemUtil { private FeedItemUtil(){} - public static int indexOfItemWithDownloadUrl(List<FeedItem> items, String downloadUrl) { - if(items == null) { - return -1; - } - for(int i=0; i < items.size(); i++) { - FeedItem item = items.get(i); - if(item.hasMedia() && item.getMedia().getDownload_url().equals(downloadUrl)) { - return i; - } - } - return -1; - } - public static int indexOfItemWithId(List<FeedItem> items, long id) { for(int i=0; i < items.size(); i++) { FeedItem item = items.get(i); @@ -40,17 +27,6 @@ public class FeedItemUtil { return -1; } - public static long[] getIds(FeedItem... items) { - if(items == null || items.length == 0) { - return new long[0]; - } - long[] result = new long[items.length]; - for(int i=0; i < items.length; i++) { - result[i] = items[i].getId(); - } - return result; - } - public static long[] getIds(List<FeedItem> items) { if(items == null || items.size() == 0) { return new long[0]; @@ -62,20 +38,6 @@ public class FeedItemUtil { return result; } - public static boolean containsAnyId(List<FeedItem> items, long[] ids) { - if(items == null || items.size() == 0) { - return false; - } - for(FeedItem item : items) { - for(long id : ids) { - if(item.getId() == id) { - return true; - } - } - } - return false; - } - /** * Get the link for the feed item for the purpose of Share. It fallbacks to * use the feed's link if the named feed item has no link. diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/FeedUpdateUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/FeedUpdateUtils.java deleted file mode 100644 index b425687ae..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/util/FeedUpdateUtils.java +++ /dev/null @@ -1,36 +0,0 @@ -package de.danoeh.antennapod.core.util; - -import android.content.Context; -import android.util.Log; - -import org.awaitility.core.ConditionTimeoutException; - -import java.util.concurrent.TimeUnit; - -import de.danoeh.antennapod.core.storage.DBTasks; - -import static org.awaitility.Awaitility.with; - -public class FeedUpdateUtils { - private static final String TAG = "FeedUpdateUtils"; - - private FeedUpdateUtils() {} - - public static void startAutoUpdate(Context context, Runnable callback) { - // the network check is blocking for possibly a long time: so run the logic - // in a separate thread to prevent the code blocking the callers - final Runnable runnable = () -> { - try { - with().pollInterval(1, TimeUnit.SECONDS) - .await() - .atMost(10, TimeUnit.SECONDS) - .until(() -> NetworkUtils.networkAvailable() && NetworkUtils.isFeedRefreshAllowed()); - DBTasks.refreshAllFeeds(context, null, callback); - } catch (ConditionTimeoutException ignore) { - Log.d(TAG, "Blocking automatic update: no wifi available / no mobile updates allowed"); - } - }; - new Thread(runnable).start(); - } - -} diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/FeedtitleComparator.java b/core/src/main/java/de/danoeh/antennapod/core/util/FeedtitleComparator.java deleted file mode 100644 index 29d095cd2..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/util/FeedtitleComparator.java +++ /dev/null @@ -1,15 +0,0 @@ -package de.danoeh.antennapod.core.util; - -import java.util.Comparator; - -import de.danoeh.antennapod.core.feed.Feed; - -/** Compares the title of two feeds for sorting. */ -class FeedtitleComparator implements Comparator<Feed> { - - @Override - public int compare(Feed lhs, Feed rhs) { - return lhs.getTitle().compareToIgnoreCase(rhs.getTitle()); - } - -} diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/IntentUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/IntentUtils.java index e81ab47ed..656b518bf 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/IntentUtils.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/IntentUtils.java @@ -1,13 +1,20 @@ package de.danoeh.antennapod.core.util; +import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.net.Uri; +import android.util.Log; +import android.widget.Toast; +import de.danoeh.antennapod.core.R; import java.util.List; public class IntentUtils { + private static final String TAG = "IntentUtils"; + private IntentUtils(){} /* @@ -28,4 +35,13 @@ public class IntentUtils { context.sendBroadcast(new Intent(action).setPackage(context.getPackageName())); } + public static void openInBrowser(Context context, String url) { + try { + Intent myIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); + context.startActivity(myIntent); + } catch (ActivityNotFoundException e) { + Toast.makeText(context, R.string.pref_no_browser_found, Toast.LENGTH_LONG).show(); + Log.e(TAG, Log.getStackTraceString(e)); + } + } } diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/LangUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/LangUtils.java index 90e0b0981..20af6415e 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/LangUtils.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/LangUtils.java @@ -1,6 +1,6 @@ package de.danoeh.antennapod.core.util; -import android.support.v4.util.ArrayMap; +import androidx.collection.ArrayMap; import java.nio.charset.Charset; 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 ca48c9bc9..a9e46e42c 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 @@ -5,7 +5,7 @@ import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; -import android.support.v4.net.ConnectivityManagerCompat; +import androidx.core.net.ConnectivityManagerCompat; import android.text.TextUtils; import android.util.Log; diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/Optional.java b/core/src/main/java/de/danoeh/antennapod/core/util/Optional.java index 0fe11ec53..37f12c01c 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/Optional.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/Optional.java @@ -77,7 +77,7 @@ public final class Optional<T> { * @param <T> Type of the non-existent value * @return an empty {@code Optional} */ - public static<T> Optional<T> empty() { + public static <T> Optional<T> empty() { @SuppressWarnings("unchecked") Optional<T> t = (Optional<T>) EMPTY; return t; diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/QueueSorter.java b/core/src/main/java/de/danoeh/antennapod/core/util/QueueSorter.java index 37172d042..0c21ca393 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/QueueSorter.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/QueueSorter.java @@ -164,7 +164,7 @@ public class QueueSorter { Collections.sort(feedItems, itemComparator); if (spread == 0) { spread = feedItems.size(); - } else if (feedItems.size() % spread != 0){ + } else if (spread % feedItems.size() != 0){ spread *= feedItems.size(); } } @@ -180,6 +180,9 @@ public class QueueSorter { Map<Long, List<FeedItem>> spreadItems = new HashMap<>(); for (List<FeedItem> feedItems : feeds) { long thisSpread = spread / feedItems.size(); + if (thisSpread == 0) { + thisSpread = 1; + } // Starting from 0 ensures we front-load, so the queue starts with one episode from // each feed in the queue long itemSpread = 0; 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 5ae00460e..3e9e8327e 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 @@ -6,7 +6,7 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.net.Uri; import android.os.Build; -import android.support.v4.content.FileProvider; +import androidx.core.content.FileProvider; import android.util.Log; import java.io.File; diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/ThemeUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/ThemeUtils.java index 1da7a5c50..eabaaa828 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/ThemeUtils.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/ThemeUtils.java @@ -1,8 +1,8 @@ package de.danoeh.antennapod.core.util; import android.content.Context; -import android.support.annotation.AttrRes; -import android.support.annotation.ColorInt; +import androidx.annotation.AttrRes; +import androidx.annotation.ColorInt; import android.util.TypedValue; public class ThemeUtils { diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/URLChecker.java b/core/src/main/java/de/danoeh/antennapod/core/util/URLChecker.java index ffc6a6e28..7e3870a20 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/URLChecker.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/URLChecker.java @@ -1,7 +1,7 @@ package de.danoeh.antennapod.core.util; import android.net.Uri; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import android.util.Log; import de.danoeh.antennapod.core.BuildConfig; diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/comparator/SearchResultValueComparator.java b/core/src/main/java/de/danoeh/antennapod/core/util/comparator/SearchResultValueComparator.java deleted file mode 100644 index 56a684475..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/util/comparator/SearchResultValueComparator.java +++ /dev/null @@ -1,27 +0,0 @@ -package de.danoeh.antennapod.core.util.comparator; - -import java.util.Comparator; - -import de.danoeh.antennapod.core.feed.FeedItem; -import de.danoeh.antennapod.core.feed.SearchResult; - -public class SearchResultValueComparator implements Comparator<SearchResult> { - - /** - * Compare items based, first, on where they were found (ie. title, chapters, or show notes). - * If they were found in the same section, then compare based on the title, in lexicographic - * order. This is still not ideal since, for example, "#12 Example A" would be considered - * before "#8 Example B" due to the fact that "8" has a larger unicode value than "1" - */ - @Override - public int compare(SearchResult lhs, SearchResult rhs) { - int value = rhs.getValue() - lhs.getValue(); - if (value == 0 && lhs.getComponent() instanceof FeedItem && rhs.getComponent() instanceof FeedItem) { - String lhsTitle = ((FeedItem) lhs.getComponent()).getTitle(); - String rhsTitle = ((FeedItem) rhs.getComponent()).getTitle(); - return lhsTitle.compareTo(rhsTitle); - } - return value; - } - -} diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/download/AutoUpdateManager.java b/core/src/main/java/de/danoeh/antennapod/core/util/download/AutoUpdateManager.java index 412b150fa..e093a7e00 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/download/AutoUpdateManager.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/download/AutoUpdateManager.java @@ -1,21 +1,29 @@ package de.danoeh.antennapod.core.util.download; +import android.content.Context; +import androidx.annotation.NonNull; import android.util.Log; + import androidx.work.Constraints; +import androidx.work.Data; import androidx.work.ExistingPeriodicWorkPolicy; import androidx.work.ExistingWorkPolicy; import androidx.work.NetworkType; import androidx.work.OneTimeWorkRequest; import androidx.work.PeriodicWorkRequest; import androidx.work.WorkManager; -import de.danoeh.antennapod.core.preferences.UserPreferences; -import de.danoeh.antennapod.core.service.FeedUpdateWorker; +import java.util.Arrays; import java.util.Calendar; import java.util.concurrent.TimeUnit; +import de.danoeh.antennapod.core.preferences.UserPreferences; +import de.danoeh.antennapod.core.service.FeedUpdateWorker; +import de.danoeh.antennapod.core.storage.DBTasks; + public class AutoUpdateManager { - private static final String WORK_ID_FEED_UPDATE = FeedUpdateWorker.class.getName(); + private static final String WORK_ID_FEED_UPDATE = "de.danoeh.antennapod.core.service.FeedUpdateWorker"; + private static final String WORK_ID_FEED_UPDATE_ONCE = WORK_ID_FEED_UPDATE + "Once"; private static final String TAG = "AutoUpdateManager"; private AutoUpdateManager() { @@ -23,9 +31,25 @@ public class AutoUpdateManager { } /** + * Start / restart periodic auto feed refresh + */ + public static void restartUpdateAlarm() { + if (UserPreferences.isAutoUpdateDisabled()) { + disableAutoUpdate(); + } else if (UserPreferences.isAutoUpdateTimeOfDay()) { + int[] timeOfDay = UserPreferences.getUpdateTimeOfDay(); + Log.d(TAG, "timeOfDay: " + Arrays.toString(timeOfDay)); + restartUpdateTimeOfDayAlarm(timeOfDay[0], timeOfDay[1]); + } else { + long milliseconds = UserPreferences.getUpdateInterval(); + restartUpdateIntervalAlarm(milliseconds); + } + } + + /** * Sets the interval in which the feeds are refreshed automatically */ - public static void restartUpdateIntervalAlarm(long intervalMillis) { + private static void restartUpdateIntervalAlarm(long intervalMillis) { Log.d(TAG, "Restarting update alarm."); PeriodicWorkRequest workRequest = new PeriodicWorkRequest.Builder(FeedUpdateWorker.class, @@ -40,7 +64,7 @@ public class AutoUpdateManager { /** * Sets time of day the feeds are refreshed automatically */ - public static void restartUpdateTimeOfDayAlarm(int hoursOfDay, int minute) { + private static void restartUpdateTimeOfDayAlarm(int hoursOfDay, int minute) { Log.d(TAG, "Restarting update alarm."); Calendar now = Calendar.getInstance(); @@ -60,6 +84,41 @@ public class AutoUpdateManager { WorkManager.getInstance().enqueueUniqueWork(WORK_ID_FEED_UPDATE, ExistingWorkPolicy.REPLACE, workRequest); } + /** + * Run auto feed refresh once in background, as soon as what OS scheduling allows. + * + * Callers from UI should use {@link #runImmediate(Context)}, as it will guarantee + * the refresh be run immediately. + */ + public static void runOnce() { + Log.d(TAG, "Run auto update once, as soon as OS allows."); + + OneTimeWorkRequest workRequest = new OneTimeWorkRequest.Builder(FeedUpdateWorker.class) + .setConstraints(getConstraints()) + .setInitialDelay(0L, TimeUnit.MILLISECONDS) + .setInputData(new Data.Builder() + .putBoolean(FeedUpdateWorker.PARAM_RUN_ONCE, true) + .build() + ) + .build(); + + WorkManager.getInstance().enqueueUniqueWork(WORK_ID_FEED_UPDATE_ONCE, ExistingWorkPolicy.REPLACE, workRequest); + + } + + /** + /** + * Run auto feed refresh once in background immediately, using its own thread. + * + * Callers where the additional threads is not suitable should use {@link #runOnce()} + */ + public static void runImmediate(@NonNull Context context) { + Log.d(TAG, "Run auto update immediately in background."); + new Thread(() -> { + DBTasks.refreshAllFeeds(context.getApplicationContext()); + }, "ManualRefreshAllFeeds").start(); + } + public static void disableAutoUpdate() { WorkManager.getInstance().cancelUniqueWork(WORK_ID_FEED_UPDATE); } @@ -74,4 +133,5 @@ public class AutoUpdateManager { } return constraints.build(); } + } diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/exception/RxJavaErrorHandlerSetup.java b/core/src/main/java/de/danoeh/antennapod/core/util/exception/RxJavaErrorHandlerSetup.java index 12f0c1c6e..223104d2e 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/exception/RxJavaErrorHandlerSetup.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/exception/RxJavaErrorHandlerSetup.java @@ -1,12 +1,9 @@ package de.danoeh.antennapod.core.util.exception; import android.util.Log; -import de.danoeh.antennapod.core.gpoddernet.GpodnetServiceException; import io.reactivex.exceptions.UndeliverableException; import io.reactivex.plugins.RxJavaPlugins; -import java.io.InterruptedIOException; - public class RxJavaErrorHandlerSetup { private RxJavaErrorHandlerSetup() { @@ -14,21 +11,14 @@ public class RxJavaErrorHandlerSetup { } public static void setupRxJavaErrorHandler() { - RxJavaPlugins.setErrorHandler(originalCause -> { - Throwable e = originalCause; + RxJavaPlugins.setErrorHandler(e -> { if (e instanceof UndeliverableException) { - e = e.getCause(); - } - if (e instanceof GpodnetServiceException) { - e = e.getCause(); - } - if (e instanceof InterruptedException || e instanceof InterruptedIOException) { - // fine, some blocking code was interrupted by a dispose call - Log.d("RxJavaErrorHandler", "Ignored exception: " + Log.getStackTraceString(originalCause)); + // Probably just disposed because the fragment was left + Log.d("RxJavaErrorHandler", "Ignored exception: " + Log.getStackTraceString(e)); return; } Thread.currentThread().getUncaughtExceptionHandler() - .uncaughtException(Thread.currentThread(), originalCause); + .uncaughtException(Thread.currentThread(), e); }); } } diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/gui/NotificationUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/gui/NotificationUtils.java index 52a43aab2..02e98ba84 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/gui/NotificationUtils.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/gui/NotificationUtils.java @@ -5,7 +5,7 @@ import android.app.NotificationChannel; import android.app.NotificationManager; import android.content.Context; import android.os.Build; -import android.support.annotation.RequiresApi; +import androidx.annotation.RequiresApi; import de.danoeh.antennapod.core.R; diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ChapterReader.java b/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ChapterReader.java index d22d08e09..a3f747e09 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ChapterReader.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ChapterReader.java @@ -72,7 +72,7 @@ public class ChapterReader extends ID3Reader { String decodedLink = URLDecoder.decode(link.toString(), "UTF-8"); currentChapter.setLink(decodedLink); Log.d(TAG, "Found link: " + currentChapter.getLink()); - } catch (IllegalArgumentException _iae) { + } catch (IllegalArgumentException iae) { Log.w(TAG, "Bad URL found in ID3 data"); } diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/playback/ExternalMedia.java b/core/src/main/java/de/danoeh/antennapod/core/util/playback/ExternalMedia.java index 645bae5f3..9b644c3ba 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/playback/ExternalMedia.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/playback/ExternalMedia.java @@ -182,7 +182,7 @@ public class ExternalMedia implements Playable { editor.putLong(PREF_LAST_PLAYED_TIME, timestamp); position = newPosition; lastPlayedTime = timestamp; - editor.commit(); + editor.apply(); } @Override diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/playback/Playable.java b/core/src/main/java/de/danoeh/antennapod/core/util/playback/Playable.java index da9b96430..378c47faf 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/playback/Playable.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/playback/Playable.java @@ -4,7 +4,7 @@ import android.content.Context; import android.content.SharedPreferences; import android.os.Parcelable; import android.preference.PreferenceManager; -import android.support.annotation.Nullable; +import androidx.annotation.Nullable; import android.util.Log; import java.util.List; 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 9caa2156d..8328ea577 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 @@ -11,8 +11,8 @@ import android.content.res.TypedArray; import android.media.MediaPlayer; import android.os.Build; import android.os.IBinder; -import android.support.annotation.NonNull; -import android.support.v4.content.ContextCompat; +import androidx.annotation.NonNull; +import androidx.core.content.ContextCompat; import android.text.TextUtils; import android.util.Log; import android.util.Pair; @@ -21,9 +21,7 @@ import android.widget.ImageButton; import android.widget.SeekBar; import android.widget.TextView; -import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledThreadPoolExecutor; -import java.util.concurrent.TimeUnit; import de.danoeh.antennapod.core.R; import de.danoeh.antennapod.core.event.ServiceEvent; @@ -69,9 +67,6 @@ public class PlaybackController { private final ScheduledThreadPoolExecutor schedExecutor; private static final int SCHED_EX_POOLSIZE = 1; - private MediaPositionObserver positionObserver; - private ScheduledFuture<?> positionObserverFuture; - private boolean mediaInfoLoaded = false; private boolean released = false; private boolean initialized = false; @@ -177,7 +172,6 @@ public class PlaybackController { } catch (IllegalArgumentException e) { // ignore } - cancelPositionObserver(); schedExecutor.shutdownNow(); media = null; released = true; @@ -254,29 +248,6 @@ public class PlaybackController { .getIntent()); } - - - private void setupPositionObserver() { - if (positionObserverFuture == null || - positionObserverFuture.isCancelled() || - positionObserverFuture.isDone()) { - - Log.d(TAG, "Setting up position observer"); - positionObserver = new MediaPositionObserver(); - positionObserverFuture = schedExecutor.scheduleWithFixedDelay( - positionObserver, MediaPositionObserver.WAITING_INTERVALL, - MediaPositionObserver.WAITING_INTERVALL, - TimeUnit.MILLISECONDS); - } - } - - private void cancelPositionObserver() { - if (positionObserverFuture != null) { - boolean result = positionObserverFuture.cancel(true); - Log.d(TAG, "PositionObserver cancelled. Result: " + result); - } - } - private final ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if(service instanceof PlaybackService.LocalBinder) { @@ -337,7 +308,6 @@ public class PlaybackController { onBufferUpdate(progress); break; case PlaybackService.NOTIFICATION_TYPE_RELOAD: - cancelPositionObserver(); mediaInfoLoaded = false; queryService(); onReloadNotification(intent.getIntExtra( @@ -447,7 +417,6 @@ public class PlaybackController { case PAUSED: clearStatusMsg(); checkMediaInfoLoaded(); - cancelPositionObserver(); onPositionObserverUpdate(); updatePlayButtonAppearance(playResource, playText); if (!PlaybackService.isCasting() && @@ -463,7 +432,6 @@ public class PlaybackController { onAwaitingVideoSurface(); setScreenOn(true); } - setupPositionObserver(); updatePlayButtonAppearance(pauseResource, pauseText); break; case PREPARING: @@ -581,7 +549,6 @@ public class PlaybackController { */ public void onSeekBarStartTrackingTouch(SeekBar seekBar) { // interrupt position Observer, restart later - cancelPositionObserver(); } /** @@ -590,7 +557,6 @@ public class PlaybackController { public void onSeekBarStopTrackingTouch(SeekBar seekBar, float prog) { if (playbackService != null && media != null) { playbackService.seekTo((int) (prog * media.getDuration())); - setupPositionObserver(); } } @@ -728,6 +694,8 @@ public class PlaybackController { public void setPlaybackSpeed(float speed) { if (playbackService != null) { playbackService.setSpeed(speed); + } else { + onPlaybackSpeedChange(); } } public void setSkipSilence(boolean skipSilence) { @@ -834,19 +802,4 @@ public class PlaybackController { } }, error -> Log.e(TAG, Log.getStackTraceString(error))); } - - /** - * Refreshes the current position of the media file that is playing. - */ - public class MediaPositionObserver implements Runnable { - - static final int WAITING_INTERVALL = 1000; - - @Override - public void run() { - if (playbackService != null && playbackService.getStatus() == PlayerStatus.PLAYING) { - activity.runOnUiThread(PlaybackController.this::onPositionObserverUpdate); - } - } - } } diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackServiceStarter.java b/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackServiceStarter.java index f7d2ee409..01ca97134 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackServiceStarter.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackServiceStarter.java @@ -2,9 +2,8 @@ package de.danoeh.antennapod.core.util.playback; import android.content.Context; import android.content.Intent; -import android.media.MediaPlayer; -import android.support.annotation.NonNull; -import android.support.v4.content.ContextCompat; +import androidx.annotation.NonNull; +import androidx.core.content.ContextCompat; import de.danoeh.antennapod.core.preferences.PlaybackPreferences; import de.danoeh.antennapod.core.service.playback.PlaybackService; diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/playback/Timeline.java b/core/src/main/java/de/danoeh/antennapod/core/util/playback/Timeline.java index 75229b9cf..0fe1f0036 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/playback/Timeline.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/playback/Timeline.java @@ -3,11 +3,10 @@ package de.danoeh.antennapod.core.util.playback; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Color; -import android.support.annotation.ColorInt; -import android.support.annotation.NonNull; +import androidx.annotation.ColorInt; +import androidx.annotation.NonNull; import android.text.TextUtils; import android.util.Log; -import android.util.Pair; import android.util.TypedValue; import org.jsoup.Jsoup; @@ -15,7 +14,6 @@ import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; -import java.util.ArrayList; import java.util.Locale; import java.util.regex.Matcher; import java.util.regex.Pattern; diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/syndication/FeedDiscoverer.java b/core/src/main/java/de/danoeh/antennapod/core/util/syndication/FeedDiscoverer.java index c5ad9cfd6..a474756e9 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/syndication/FeedDiscoverer.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/syndication/FeedDiscoverer.java @@ -1,7 +1,7 @@ package de.danoeh.antennapod.core.util.syndication; import android.net.Uri; -import android.support.v4.util.ArrayMap; +import androidx.collection.ArrayMap; import android.text.TextUtils; import org.jsoup.Jsoup; |