diff options
author | Max Bechtold <max.bechtold@andrena.de> | 2019-10-17 12:36:45 +0200 |
---|---|---|
committer | Max Bechtold <max.bechtold@andrena.de> | 2019-10-17 13:13:19 +0200 |
commit | 87cca61dcd8337facf695f03f1330c55bfa85168 (patch) | |
tree | 630888c57de93856f33184ec475f01716812deff /core/src/main/java | |
parent | cc9c8bb63a27deb35f0e234d252e22a73ef6c117 (diff) | |
parent | 2ffdc275b8dd127f15506556d1b7ba9fb9108b47 (diff) | |
download | AntennaPod-87cca61dcd8337facf695f03f1330c55bfa85168.zip |
Merge remote-tracking branch 'origin/develop' into feat/simple-adjust-volume-per-feed
Diffstat (limited to 'core/src/main/java')
67 files changed, 681 insertions, 505 deletions
diff --git a/core/src/main/java/android/support/v4/app/SafeJobIntentService.java b/core/src/main/java/androidx/core/app/SafeJobIntentService.java index c07c409ee..aedc9418b 100644 --- a/core/src/main/java/android/support/v4/app/SafeJobIntentService.java +++ b/core/src/main/java/androidx/core/app/SafeJobIntentService.java @@ -1,4 +1,4 @@ -package android.support.v4.app; +package androidx.core.app; import android.app.job.JobParameters; import android.app.job.JobServiceEngine; @@ -6,7 +6,7 @@ import android.app.job.JobWorkItem; import android.content.Intent; import android.os.Build; import android.os.IBinder; -import android.support.annotation.RequiresApi; +import androidx.annotation.RequiresApi; import android.util.Log; 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/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/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/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 2a535b2c4..61a09565f 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 744e9b924..ecd34acff 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; 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 218570632..b1c2ff15a 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; @@ -21,7 +21,6 @@ import de.danoeh.antennapod.core.preferences.PlaybackPreferences; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.service.playback.PlaybackService; 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.PodDBAdapter; import de.danoeh.antennapod.core.util.ChapterUtils; 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 eaafab322..cb5a59a3b 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; @@ -14,6 +14,8 @@ import de.danoeh.antennapod.core.storage.PodDBAdapter; */ public class FeedPreferences { + public static final float SPEED_USE_GLOBAL = -1; + @NonNull private FeedFilter filter; private long feedID; @@ -31,12 +33,13 @@ public class FeedPreferences { private String username; private String password; + private float feedPlaybackSpeed; public FeedPreferences(long feedID, boolean autoDownload, AutoDeleteAction auto_delete_action, VolumeReductionSetting volumeReductionSetting, String username, String password) { - this(feedID, autoDownload, true, auto_delete_action, volumeReductionSetting, username, password, new FeedFilter()); + this(feedID, autoDownload, true, auto_delete_action, volumeReductionSetting, username, password, new FeedFilter(), SPEED_USE_GLOBAL); } - private FeedPreferences(long feedID, boolean autoDownload, boolean keepUpdated, AutoDeleteAction auto_delete_action, VolumeReductionSetting volumeReductionSetting, String username, String password, @NonNull FeedFilter filter) { + private FeedPreferences(long feedID, boolean autoDownload, boolean keepUpdated, AutoDeleteAction auto_delete_action, VolumeReductionSetting volumeReductionSetting, String username, String password, @NonNull FeedFilter filter, float feedPlaybackSpeed) { this.feedID = feedID; this.autoDownload = autoDownload; this.keepUpdated = keepUpdated; @@ -45,6 +48,7 @@ public class FeedPreferences { this.username = username; this.password = password; this.filter = filter; + this.feedPlaybackSpeed = feedPlaybackSpeed; } public static FeedPreferences fromCursor(Cursor cursor) { @@ -57,6 +61,7 @@ public class FeedPreferences { int indexPassword = cursor.getColumnIndex(PodDBAdapter.KEY_PASSWORD); int indexIncludeFilter = cursor.getColumnIndex(PodDBAdapter.KEY_INCLUDE_FILTER); int indexExcludeFilter = cursor.getColumnIndex(PodDBAdapter.KEY_EXCLUDE_FILTER); + int indexFeedPlaybackSpeed = cursor.getColumnIndex(PodDBAdapter.KEY_FEED_PLAYBACK_SPEED); long feedId = cursor.getLong(indexId); boolean autoDownload = cursor.getInt(indexAutoDownload) > 0; @@ -69,7 +74,8 @@ public class FeedPreferences { String password = cursor.getString(indexPassword); String includeFilter = cursor.getString(indexIncludeFilter); String excludeFilter = cursor.getString(indexExcludeFilter); - return new FeedPreferences(feedId, autoDownload, autoRefresh, autoDeleteAction, volumeReductionSetting, username, password, new FeedFilter(includeFilter, excludeFilter)); + float feedPlaybackSpeed = cursor.getFloat(indexFeedPlaybackSpeed); + return new FeedPreferences(feedId, autoDownload, autoRefresh, autoDeleteAction, volumeReductionSetting, username, password, new FeedFilter(includeFilter, excludeFilter), feedPlaybackSpeed); } /** @@ -190,4 +196,12 @@ public class FeedPreferences { public void setPassword(String password) { this.password = password; } + + public float getFeedPlaybackSpeed() { + return feedPlaybackSpeed; + } + + public void setFeedPlaybackSpeed(float playbackSpeed) { + feedPlaybackSpeed = playbackSpeed; + } } 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 3dd87cc0b..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; 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 f2c0c8fe3..192674f0d 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 @@ -11,6 +11,8 @@ import de.danoeh.antennapod.core.feed.MediaType; import de.danoeh.antennapod.core.service.playback.PlayerStatus; import de.danoeh.antennapod.core.util.playback.Playable; +import static de.danoeh.antennapod.core.feed.FeedPreferences.SPEED_USE_GLOBAL; + /** * Provides access to preferences set by the playback service. A private * instance of this class must first be instantiated via createInstance() or @@ -55,6 +57,13 @@ public class PlaybackPreferences implements SharedPreferences.OnSharedPreference private static final String PREF_CURRENT_PLAYER_STATUS = "de.danoeh.antennapod.preferences.currentPlayerStatus"; /** + * A temporary playback speed which overrides the per-feed playback speed for the currently playing + * media. Considered unset if set to SPEED_USE_GLOBAL; + */ + private static final String PREF_CURRENTLY_PLAYING_TEMPORARY_PLAYBACK_SPEED = "de.danoeh.antennapod.preferences.temporaryPlaybackSpeed"; + + + /** * Value of PREF_CURRENTLY_PLAYING_MEDIA if no media is playing. */ public static final long NO_MEDIA_PLAYING = -1; @@ -112,6 +121,10 @@ public class PlaybackPreferences implements SharedPreferences.OnSharedPreference return prefs.getInt(PREF_CURRENT_PLAYER_STATUS, PLAYER_STATUS_OTHER); } + public static float getCurrentlyPlayingTemporaryPlaybackSpeed() { + return prefs.getFloat(PREF_CURRENTLY_PLAYING_TEMPORARY_PLAYBACK_SPEED, SPEED_USE_GLOBAL); + } + public static void writeNoMediaPlaying() { SharedPreferences.Editor editor = prefs.edit(); editor.putLong(PREF_CURRENTLY_PLAYING_MEDIA, NO_MEDIA_PLAYING); @@ -154,6 +167,18 @@ public class PlaybackPreferences implements SharedPreferences.OnSharedPreference editor.apply(); } + public static void setCurrentlyPlayingTemporaryPlaybackSpeed(float speed) { + SharedPreferences.Editor editor = prefs.edit(); + editor.putFloat(PREF_CURRENTLY_PLAYING_TEMPORARY_PLAYBACK_SPEED, speed); + editor.apply(); + } + + public static void clearCurrentlyPlayingTemporaryPlaybackSpeed() { + SharedPreferences.Editor editor = prefs.edit(); + editor.remove(PREF_CURRENTLY_PLAYING_TEMPORARY_PLAYBACK_SPEED); + editor.apply(); + } + private static int getCurrentPlayerStatusAsInt(PlayerStatus playerStatus) { int playerStatusAsInt; switch (playerStatus) { diff --git a/core/src/main/java/de/danoeh/antennapod/core/preferences/PlaybackSpeedHelper.java b/core/src/main/java/de/danoeh/antennapod/core/preferences/PlaybackSpeedHelper.java new file mode 100644 index 000000000..d48e41c3b --- /dev/null +++ b/core/src/main/java/de/danoeh/antennapod/core/preferences/PlaybackSpeedHelper.java @@ -0,0 +1,41 @@ +package de.danoeh.antennapod.core.preferences; + +import de.danoeh.antennapod.core.feed.Feed; +import de.danoeh.antennapod.core.feed.FeedItem; +import de.danoeh.antennapod.core.feed.FeedMedia; +import de.danoeh.antennapod.core.feed.MediaType; +import de.danoeh.antennapod.core.util.playback.Playable; + +import static de.danoeh.antennapod.core.feed.FeedPreferences.SPEED_USE_GLOBAL; + +public class PlaybackSpeedHelper { + + /** + * Returns the currently configured playback speed for the specified media. + */ + public static float getCurrentPlaybackSpeed(Playable media) { + float playbackSpeed = SPEED_USE_GLOBAL; + MediaType mediaType = null; + + if (media != null) { + mediaType = media.getMediaType(); + playbackSpeed = PlaybackPreferences.getCurrentlyPlayingTemporaryPlaybackSpeed(); + + if (playbackSpeed == SPEED_USE_GLOBAL && media instanceof FeedMedia) { + FeedItem item = ((FeedMedia) media).getItem(); + if (item != null) { + Feed feed = item.getFeed(); + if (feed != null) { + playbackSpeed = feed.getPreferences().getFeedPlaybackSpeed(); + } + } + } + } + + if (playbackSpeed == SPEED_USE_GLOBAL) { + playbackSpeed = UserPreferences.getPlaybackSpeed(mediaType); + } + + return playbackSpeed; + } +} 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 787e32ccc..ba02a9b8c 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 @@ -4,9 +4,9 @@ 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; @@ -23,6 +23,7 @@ import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; +import de.danoeh.antennapod.core.feed.MediaType; import de.danoeh.antennapod.core.R; import de.danoeh.antennapod.core.service.download.ProxyConfig; import de.danoeh.antennapod.core.storage.APCleanupAlgorithm; @@ -71,7 +72,7 @@ public class UserPreferences { private static final String PREF_HARDWARE_FOWARD_BUTTON_SKIPS = "prefHardwareForwardButtonSkips"; private static final String PREF_HARDWARE_PREVIOUS_BUTTON_RESTARTS = "prefHardwarePreviousButtonRestarts"; public static final String PREF_FOLLOW_QUEUE = "prefFollowQueue"; - private static final String PREF_SKIP_KEEPS_EPISODE = "prefSkipKeepsEpisode"; + public static final String PREF_SKIP_KEEPS_EPISODE = "prefSkipKeepsEpisode"; private static final String PREF_FAVORITE_KEEPS_EPISODE = "prefFavoriteKeepsEpisode"; private static final String PREF_AUTO_DELETE = "prefAutoDelete"; public static final String PREF_SMART_MARK_AS_PLAYED_SECS = "prefSmartMarkAsPlayedSecs"; @@ -321,7 +322,15 @@ public class UserPreferences { return prefs.getBoolean(PREF_DELETE_REMOVES_FROM_QUEUE, false); } - public static float getPlaybackSpeed() { + public static float getPlaybackSpeed(MediaType mediaType) { + if (mediaType == MediaType.VIDEO) { + return getVideoPlaybackSpeed(); + } else { + return getAudioPlaybackSpeed(); + } + } + + private static float getAudioPlaybackSpeed() { try { return Float.parseFloat(prefs.getString(PREF_PLAYBACK_SPEED, "1.00")); } catch (NumberFormatException e) { @@ -331,7 +340,7 @@ public class UserPreferences { } } - public static float getVideoPlaybackSpeed() { + private static float getVideoPlaybackSpeed() { try { return Float.parseFloat(prefs.getString(PREF_VIDEO_PLAYBACK_SPEED, "1.00")); } catch (NumberFormatException e) { @@ -706,7 +715,7 @@ public class UserPreferences { String[] selectedSpeeds = null; // If this preference hasn't been set yet, return the default options if (valueFromPrefs == null) { - selectedSpeeds = new String[] { "1.00", "1.25", "1.50", "1.75", "2.00" }; + selectedSpeeds = new String[] { "0.75", "1.00", "1.25", "1.50", "1.75", "2.00" }; } else { try { JSONArray jsonArray = new JSONArray(valueFromPrefs); 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 87c18227b..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,7 +1,7 @@ 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; 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 abb1d0c0b..a34a1e2c3 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; @@ -23,9 +23,8 @@ import com.bumptech.glide.request.RequestOptions; import java.util.concurrent.TimeUnit; import de.danoeh.antennapod.core.R; -import de.danoeh.antennapod.core.feed.MediaType; import de.danoeh.antennapod.core.glide.ApGlideSettings; -import de.danoeh.antennapod.core.preferences.UserPreferences; +import de.danoeh.antennapod.core.preferences.PlaybackSpeedHelper; import de.danoeh.antennapod.core.receiver.MediaButtonReceiver; import de.danoeh.antennapod.core.receiver.PlayerWidget; import de.danoeh.antennapod.core.service.playback.PlaybackService; @@ -148,9 +147,7 @@ public class PlayerWidgetJobService extends SafeJobIntentService { progressString = getProgressString(playbackService.getCurrentPosition(), playbackService.getDuration(), playbackService.getCurrentPlaybackSpeed()); } else { - float speed = media.getMediaType() == MediaType.VIDEO ? - UserPreferences.getVideoPlaybackSpeed() : UserPreferences.getPlaybackSpeed(); - progressString = getProgressString(media.getPosition(), media.getDuration(), speed); + progressString = getProgressString(media.getPosition(), media.getDuration(), PlaybackSpeedHelper.getCurrentPlaybackSpeed(media)); } if (progressString != null) { 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 04a6d5882..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; 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 e9b4c5d1e..b6e5de254 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,10 +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.annotation.Nullable; -import android.support.annotation.VisibleForTesting; -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; 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 d88eb63f4..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; 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/LocalPSMP.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java index e5dd292f0..8ac2721b6 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; @@ -29,6 +29,7 @@ import java.util.concurrent.locks.ReentrantLock; import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.feed.FeedPreferences; import de.danoeh.antennapod.core.feed.MediaType; +import de.danoeh.antennapod.core.preferences.PlaybackSpeedHelper; import de.danoeh.antennapod.core.feed.VolumeReductionSetting; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.util.RewindAfterPauseUtils; @@ -246,6 +247,7 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer { try { media.loadMetadata(); callback.onMediaChanged(false); + setPlaybackParams(PlaybackSpeedHelper.getCurrentPlaybackSpeed(media), UserPreferences.isSkipSilence()); if (stream) { mediaPlayer.setDataSource(media.getStreamUrl()); } else if (media.getLocalMediaUrl() != null && new File(media.getLocalMediaUrl()).canRead()) { @@ -308,11 +310,8 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer { Log.d(TAG, "Audiofocus successfully requested"); Log.d(TAG, "Resuming/Starting playback"); acquireWifiLockIfNecessary(); - if (media.getMediaType() == MediaType.VIDEO) { - setPlaybackParams(UserPreferences.getVideoPlaybackSpeed(), UserPreferences.isSkipSilence()); - } else { - setPlaybackParams(UserPreferences.getPlaybackSpeed(), UserPreferences.isSkipSilence()); - } + + setPlaybackParams(PlaybackSpeedHelper.getCurrentPlaybackSpeed(media), UserPreferences.isSkipSilence()); float leftVolume = UserPreferences.getLeftVolume(); float rightVolume = UserPreferences.getRightVolume(); 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 d444875ad..eb38a02d9 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 @@ -21,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; @@ -225,6 +225,7 @@ public class PlaybackService extends MediaBrowserServiceCompat { private PlaybackServiceFlavorHelper flavorHelper; private PlaybackServiceStateManager stateManager; private Disposable positionEventTimer; + private PlaybackServiceNotificationBuilder notificationBuilder; /** * Used for Lollipop notifications, Android Wear, and Android Auto. @@ -280,7 +281,7 @@ public class PlaybackService extends MediaBrowserServiceCompat { isRunning = true; stateManager = new PlaybackServiceStateManager(this); - PlaybackServiceNotificationBuilder notificationBuilder = new PlaybackServiceNotificationBuilder(this); + notificationBuilder = new PlaybackServiceNotificationBuilder(this); stateManager.startForeground(NOTIFICATION_ID, notificationBuilder.build()); registerReceiver(autoStateUpdated, new IntentFilter("com.google.android.gms.car.media.STATUS")); @@ -454,20 +455,9 @@ public class PlaybackService extends MediaBrowserServiceCompat { @Override public int onStartCommand(Intent intent, int flags, int startId) { super.onStartCommand(intent, flags, startId); - Log.d(TAG, "OnStartCommand called"); - if (!stateManager.isInForeground()) { - PlaybackServiceNotificationBuilder notificationBuilder = new PlaybackServiceNotificationBuilder(this); - if (mediaPlayer != null && getPlayable() != null) { - notificationBuilder.addMetadata(getPlayable(), mediaSession.getSessionToken(), getStatus(), isCasting); - if (notificationBuilder.isIconCached(getPlayable())) { - notificationBuilder.loadIcon(getPlayable()); - } - } - stateManager.startForeground(NOTIFICATION_ID, notificationBuilder.build()); - } - + stateManager.startForeground(NOTIFICATION_ID, notificationBuilder.build()); NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this); notificationManager.cancel(NOTIFICATION_ID_STREAMING); @@ -847,6 +837,9 @@ public class PlaybackService extends MediaBrowserServiceCompat { @Override public void onPlaybackStart(@NonNull Playable playable, int position) { + if (taskManager.isSleepTimerActive()) { + taskManager.restartSleepTimer(); + } taskManager.startWidgetUpdater(); if (position != PlaybackServiceMediaPlayer.INVALID_TIME) { playable.setPosition(position); @@ -858,6 +851,7 @@ public class PlaybackService extends MediaBrowserServiceCompat { @Override public void onPlaybackPause(Playable playable, int position) { taskManager.cancelPositionSaver(); + cancelPositionObserver(); saveCurrentPosition(position == PlaybackServiceMediaPlayer.INVALID_TIME || playable == null, playable, position); taskManager.cancelWidgetUpdater(); @@ -934,6 +928,7 @@ public class PlaybackService extends MediaBrowserServiceCompat { Log.d(TAG, "Playback ended"); if (stopPlaying) { taskManager.cancelPositionSaver(); + cancelPositionObserver(); PlaybackPreferences.writeNoMediaPlaying(); if (!isCasting) { stateManager.stopForeground(true); @@ -969,6 +964,7 @@ public class PlaybackService extends MediaBrowserServiceCompat { */ private void onPostPlayback(final Playable playable, boolean ended, boolean skipped, boolean playingNext) { + PlaybackPreferences.clearCurrentlyPlayingTemporaryPlaybackSpeed(); if (playable == null) { Log.e(TAG, "Cannot do post-playback processing: media was null"); return; @@ -986,7 +982,7 @@ public class PlaybackService extends MediaBrowserServiceCompat { } FeedMedia media = (FeedMedia) playable; FeedItem item = media.getItem(); - boolean smartMarkAsPlayed = playingNext && media.hasAlmostEnded(); + boolean smartMarkAsPlayed = media.hasAlmostEnded(); if (!ended && smartMarkAsPlayed) { Log.d(TAG, "smart mark as played"); } @@ -1204,59 +1200,51 @@ public class PlaybackService extends MediaBrowserServiceCompat { } private synchronized void setupNotification(final Playable playable) { + Log.d(TAG, "setupNotification"); if (notificationSetupThread != null) { notificationSetupThread.interrupt(); } - if (playable == null) { - Log.d(TAG, "setupNotification: playable is null" + Log.getStackTraceString(new Exception())); + if (playable == null || mediaPlayer == null) { + Log.d(TAG, "setupNotification: playable=" + playable); + Log.d(TAG, "setupNotification: mediaPlayer=" + mediaPlayer); if (!stateManager.hasReceivedValidStartCommand()) { stateManager.stopService(); } return; } - Runnable notificationSetupTask = new Runnable() { - @Override - public void run() { - Log.d(TAG, "Starting background work"); - if (mediaPlayer == null) { - Log.d(TAG, "notificationSetupTask: mediaPlayer is null"); - if (!stateManager.hasReceivedValidStartCommand()) { - stateManager.stopService(); - } - return; - } - PlayerStatus playerStatus = mediaPlayer.getPlayerStatus(); - PlaybackServiceNotificationBuilder notificationBuilder = - new PlaybackServiceNotificationBuilder(PlaybackService.this); - notificationBuilder.addMetadata(playable, mediaSession.getSessionToken(), playerStatus, isCasting); - - if (!notificationBuilder.isIconCached(playable)) { - // To make sure that the notification is shown instantly - notificationBuilder.loadDefaultIcon(); - stateManager.startForeground(NOTIFICATION_ID, notificationBuilder.build()); - } - notificationBuilder.loadIcon(playable); - - if (!Thread.currentThread().isInterrupted() && stateManager.hasReceivedValidStartCommand()) { - Notification notification = notificationBuilder.build(); - - if (playerStatus == PlayerStatus.PLAYING || - playerStatus == PlayerStatus.PREPARING || - playerStatus == PlayerStatus.SEEKING || - isCasting) { - stateManager.startForeground(NOTIFICATION_ID, notification); - } else { - stateManager.stopForeground(false); - NotificationManager mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); - mNotificationManager.notify(NOTIFICATION_ID, notification); - } - Log.d(TAG, "Notification set up"); + PlayerStatus playerStatus = mediaPlayer.getPlayerStatus(); + notificationBuilder.setMetadata(playable, mediaSession.getSessionToken(), playerStatus, isCasting); + notificationBuilder.updatePosition(getCurrentPosition(), getCurrentPlaybackSpeed()); + + Log.d(TAG, "setupNotification: startForeground" + playerStatus); + NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this); + notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build()); + startForegroundIfPlaying(playerStatus); + + if (!notificationBuilder.isIconCached()) { + notificationSetupThread = new Thread(() -> { + Log.d(TAG, "Loading notification icon"); + notificationBuilder.loadIcon(); + if (!Thread.currentThread().isInterrupted()) { + notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build()); } + }); + notificationSetupThread.start(); + } + } + + private void startForegroundIfPlaying(@NonNull PlayerStatus status) { + if (stateManager.hasReceivedValidStartCommand()) { + if (isCasting || status == PlayerStatus.PLAYING || status == PlayerStatus.PREPARING + || status == PlayerStatus.SEEKING) { + stateManager.startForeground(NOTIFICATION_ID, notificationBuilder.build()); + } else { + stateManager.stopForeground(false); + NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this); + notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build()); } - }; - notificationSetupThread = new Thread(notificationSetupTask); - notificationSetupThread.start(); + } } /** @@ -1618,8 +1606,15 @@ public class PlaybackService extends MediaBrowserServiceCompat { 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()))); + .subscribe(number -> { + EventBus.getDefault().post(new PlaybackPositionEvent(getCurrentPosition(), getDuration())); + if (Build.VERSION.SDK_INT < 29) { + notificationBuilder.updatePosition(getCurrentPosition(), getCurrentPlaybackSpeed()); + NotificationManager notificationManager = (NotificationManager) + getSystemService(NOTIFICATION_SERVICE); + notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build()); + } + }); } private void cancelPositionObserver() { 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 1a13fe5a7..b277a6bc2 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,82 +1,77 @@ package de.danoeh.antennapod.core.service.playback; +import android.annotation.TargetApi; +import android.app.Notification; 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 android.support.v4.media.session.MediaSessionCompat; import android.util.Log; import android.view.KeyEvent; +import androidx.annotation.NonNull; +import androidx.core.app.NotificationCompat; +import androidx.core.content.ContextCompat; import com.bumptech.glide.Glide; import com.bumptech.glide.request.RequestOptions; -import de.danoeh.antennapod.core.ClientConfig; 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.util.Converter; import de.danoeh.antennapod.core.util.IntList; +import de.danoeh.antennapod.core.util.TimeSpeedConverter; import de.danoeh.antennapod.core.util.gui.NotificationUtils; import de.danoeh.antennapod.core.util.playback.Playable; -public class PlaybackServiceNotificationBuilder extends NotificationCompat.Builder { +public class PlaybackServiceNotificationBuilder { private static final String TAG = "PlaybackSrvNotification"; private static Bitmap defaultIcon = null; private Context context; + private Playable playable; + private MediaSessionCompat.Token mediaSessionToken; + private PlayerStatus playerStatus; + private boolean isCasting; + private Bitmap icon; + private String position; public PlaybackServiceNotificationBuilder(@NonNull Context context) { - super(context, NotificationUtils.CHANNEL_ID_PLAYING); this.context = context; + } - final int smallIcon = ClientConfig.playbackServiceCallbacks.getNotificationIconResource(context); + public void setMetadata(Playable playable, MediaSessionCompat.Token mediaSessionToken, + PlayerStatus playerStatus, boolean isCasting) { - final PendingIntent pIntent = PendingIntent.getActivity(context, 0, - PlaybackService.getPlayerActivityIntent(context), - PendingIntent.FLAG_UPDATE_CURRENT); + if (playable != this.playable) { + clearCache(); + } + this.playable = playable; + this.mediaSessionToken = mediaSessionToken; + this.playerStatus = playerStatus; + this.isCasting = isCasting; + } - setContentTitle(context.getString(R.string.app_name)); - setContentText("Service is running"); // Just in case the notification is not updated (should not occur) - setOngoing(false); - setContentIntent(pIntent); - setWhen(0); // we don't need the time - setSmallIcon(smallIcon); - setPriority(NotificationCompat.PRIORITY_MIN); + private void clearCache() { + this.icon = null; + this.position = null; } - public void addMetadata(Playable playable, MediaSessionCompat.Token mediaSessionToken, PlayerStatus playerStatus, boolean isCasting) { - Log.v(TAG, "notificationSetupTask: playerStatus=" + playerStatus); - setContentTitle(playable.getFeedTitle()); - setContentText(playable.getEpisodeTitle()); - setPriority(UserPreferences.getNotifyPriority()); - addActions(mediaSessionToken, playerStatus, isCasting); - setVisibility(NotificationCompat.VISIBILITY_PUBLIC); - setColor(NotificationCompat.COLOR_DEFAULT); + public void updatePosition(int position,float speed) { + TimeSpeedConverter converter = new TimeSpeedConverter(speed); + this.position = Converter.getDurationStringLong(converter.convert(position)); } - public boolean isIconCached(Playable playable) { - int iconSize = context.getResources().getDimensionPixelSize(android.R.dimen.notification_large_icon_width); - try { - Bitmap icon = Glide.with(context) - .asBitmap() - .load(playable.getImageLocation()) - .apply(RequestOptions.diskCacheStrategyOf(ApGlideSettings.AP_DISK_CACHE_STRATEGY)) - .apply(new RequestOptions() - .centerCrop() - .onlyRetrieveFromCache(true)) - .submit(iconSize, iconSize) - .get(); - return icon != null; - } catch (Throwable tr) { - return false; - } + public boolean isIconCached() { + return icon != null; } - public void loadIcon(Playable playable) { - Bitmap icon = null; + public void loadIcon() { int iconSize = context.getResources().getDimensionPixelSize(android.R.dimen.notification_large_icon_width); try { icon = Glide.with(context) @@ -89,23 +84,77 @@ public class PlaybackServiceNotificationBuilder extends NotificationCompat.Build } catch (Throwable tr) { Log.e(TAG, "Error loading the media icon for the notification", tr); } + } - if (icon == null) { - loadDefaultIcon(); + private Bitmap getDefaultIcon() { + if (defaultIcon == null) { + defaultIcon = getBitmap(context, R.drawable.notification_default_large_icon); + } + return 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 { - setLargeIcon(icon); + return null; } } - public void loadDefaultIcon() { - if (defaultIcon == null) { - defaultIcon = BitmapFactory.decodeResource(context.getResources(), - ClientConfig.playbackServiceCallbacks.getNotificationIconResource(context)); + public Notification build() { + NotificationCompat.Builder notification = new NotificationCompat.Builder(context, + NotificationUtils.CHANNEL_ID_PLAYING); + + if (playable != null) { + notification.setContentTitle(playable.getFeedTitle()); + notification.setContentText(playable.getEpisodeTitle()); + addActions(notification, mediaSessionToken, playerStatus, isCasting); + + if (icon != null) { + notification.setLargeIcon(icon); + } else { + notification.setLargeIcon(getDefaultIcon()); + } + + if (Build.VERSION.SDK_INT < 29) { + notification.setSubText(position); + } + } else { + notification.setContentTitle(context.getString(R.string.app_name)); + notification.setContentText("Service is running"); } - setLargeIcon(defaultIcon); + + notification.setContentIntent(getPlayerActivityPendingIntent()); + notification.setWhen(0); + notification.setSmallIcon(R.drawable.ic_antenna); + notification.setOngoing(false); + notification.setOnlyAlertOnce(true); + notification.setPriority(UserPreferences.getNotifyPriority()); + notification.setVisibility(NotificationCompat.VISIBILITY_PUBLIC); + notification.setColor(NotificationCompat.COLOR_DEFAULT); + return notification.build(); + } + + private PendingIntent getPlayerActivityPendingIntent() { + return PendingIntent.getActivity(context, 0, PlaybackService.getPlayerActivityIntent(context), + PendingIntent.FLAG_UPDATE_CURRENT); } - private void addActions(MediaSessionCompat.Token mediaSessionToken, PlayerStatus playerStatus, boolean isCasting) { + private void addActions(NotificationCompat.Builder notification, MediaSessionCompat.Token mediaSessionToken, + PlayerStatus playerStatus, boolean isCasting) { IntList compactActionList = new IntList(); int numActions = 0; // we start and 0 and then increment by 1 for each call to addAction @@ -115,7 +164,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_notification_cast_off, + notification.addAction(R.drawable.ic_notification_cast_off, context.getString(R.string.cast_disconnect_label), stopCastingPendingIntent); numActions++; @@ -124,7 +173,8 @@ public class PlaybackServiceNotificationBuilder extends NotificationCompat.Build // always let them rewind PendingIntent rewindButtonPendingIntent = getPendingIntentForMediaAction( KeyEvent.KEYCODE_MEDIA_REWIND, numActions); - addAction(R.drawable.ic_notification_fast_rewind, context.getString(R.string.rewind_label), rewindButtonPendingIntent); + notification.addAction(R.drawable.ic_notification_fast_rewind, context.getString(R.string.rewind_label), + rewindButtonPendingIntent); if (UserPreferences.showRewindOnCompactNotification()) { compactActionList.add(numActions); } @@ -133,14 +183,14 @@ public class PlaybackServiceNotificationBuilder extends NotificationCompat.Build if (playerStatus == PlayerStatus.PLAYING) { PendingIntent pauseButtonPendingIntent = getPendingIntentForMediaAction( KeyEvent.KEYCODE_MEDIA_PAUSE, numActions); - addAction(R.drawable.ic_notification_pause, //pause action + notification.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(R.drawable.ic_notification_play, //play action + notification.addAction(R.drawable.ic_notification_play, //play action context.getString(R.string.play_label), playButtonPendingIntent); compactActionList.add(numActions++); @@ -149,7 +199,8 @@ 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(R.drawable.ic_notification_fast_forward, context.getString(R.string.fast_forward_label), ffButtonPendingIntent); + notification.addAction(R.drawable.ic_notification_fast_forward, context.getString(R.string.fast_forward_label), + ffButtonPendingIntent); if (UserPreferences.showFastForwardOnCompactNotification()) { compactActionList.add(numActions); } @@ -158,7 +209,7 @@ public class PlaybackServiceNotificationBuilder extends NotificationCompat.Build if (UserPreferences.isFollowQueue()) { PendingIntent skipButtonPendingIntent = getPendingIntentForMediaAction( KeyEvent.KEYCODE_MEDIA_NEXT, numActions); - addAction(R.drawable.ic_notification_skip, + notification.addAction(R.drawable.ic_notification_skip, context.getString(R.string.skip_episode_label), skipButtonPendingIntent); if (UserPreferences.showSkipOnCompactNotification()) { @@ -169,7 +220,7 @@ public class PlaybackServiceNotificationBuilder extends NotificationCompat.Build PendingIntent stopButtonPendingIntent = getPendingIntentForMediaAction( KeyEvent.KEYCODE_MEDIA_STOP, numActions); - setStyle(new android.support.v4.media.app.NotificationCompat.MediaStyle() + notification.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 1a13f9e9f..5647590b1 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,7 +4,7 @@ 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; @@ -249,6 +249,16 @@ public class PlaybackServiceTaskManager { } /** + * Restarts the sleep timer. If the sleep timer is not active, nothing will happen. + */ + public synchronized void restartSleepTimer() { + if (isSleepTimerActive()) { + Log.d(TAG, "Restarting sleep timer"); + sleepTimer.restart(); + } + } + + /** * Returns the current sleep timer time or 0 if the sleep timer is not active. */ public synchronized long getSleepTimerTimeLeft() { @@ -428,13 +438,15 @@ public class PlaybackServiceTaskManager { return timeLeft; } - public void onShake() { + public void restart() { postCallback(() -> { setSleepTimer(waitingTime, shakeToReset, vibrate); callback.onSleepTimerReset(); }); - shakeListener.pause(); - shakeListener = null; + if (shakeListener != null) { + shakeListener.pause(); + shakeListener = null; + } } } diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/ShakeListener.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/ShakeListener.java index c0b1b3bc0..b0b6e164d 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/ShakeListener.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/ShakeListener.java @@ -52,7 +52,7 @@ class ShakeListener implements SensorEventListener double gForce = Math.sqrt(gX*gX + gY*gY + gZ*gZ); if (gForce > 2.25) { Log.d(TAG, "Detected shake " + gForce); - mSleepTimer.onShake(); + mSleepTimer.restart(); } } 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/DBUpgrader.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DBUpgrader.java index d9aec8649..148252902 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/DBUpgrader.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DBUpgrader.java @@ -8,6 +8,8 @@ import android.util.Log; import de.danoeh.antennapod.core.feed.FeedItem; +import static de.danoeh.antennapod.core.feed.FeedPreferences.SPEED_USE_GLOBAL; + class DBUpgrader { /** * Upgrades the given database to a new schema version @@ -288,7 +290,10 @@ class DBUpgrader { db.execSQL("DROP TABLE " + PodDBAdapter.TABLE_NAME_FEED_IMAGES); } - if (oldVersion < 1070296) { + if (oldVersion < 1070400) { + db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS + + " ADD COLUMN " + PodDBAdapter.KEY_FEED_PLAYBACK_SPEED + " REAL DEFAULT " + SPEED_USE_GLOBAL); + db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS + " ADD COLUMN " + PodDBAdapter.KEY_FEED_VOLUME_REDUCTION + " INTEGER DEFAULT 0"); } 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..e0e15c1ff 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,12 +2,10 @@ 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 android.util.Log; +import androidx.annotation.NonNull; + import org.greenrobot.eventbus.EventBus; import java.io.File; @@ -89,7 +87,8 @@ public class DBWriter { }); } - private static boolean deleteFeedMediaSynchronous(@NonNull Context context, @NonNull FeedMedia media) { + private static boolean deleteFeedMediaSynchronous( + @NonNull Context context, @NonNull FeedMedia media) { Log.i(TAG, String.format("Requested to delete FeedMedia [id=%d, title=%s, downloaded=%s", media.getId(), media.getEpisodeTitle(), String.valueOf(media.isDownloaded()))); if (media.isDownloaded()) { @@ -114,7 +113,7 @@ public class DBWriter { } // Gpodder: queue delete action for synchronization - if(GpodnetPreferences.loggedIn()) { + if (GpodnetPreferences.loggedIn()) { FeedItem item = media.getItem(); GpodnetEpisodeAction action = new GpodnetEpisodeAction.Builder(item, GpodnetEpisodeAction.Action.DELETE) .currentDeviceId() @@ -162,7 +161,7 @@ public class DBWriter { adapter.open(); if (removed.size() > 0) { adapter.setQueue(queue); - for(FeedItem item : removed) { + for (FeedItem item : removed) { EventBus.getDefault().post(QueueEvent.irreversibleRemoved(item)); } } @@ -187,7 +186,6 @@ public class DBWriter { /** * Deletes the entire playback history. - * */ public static Future<?> clearPlaybackHistory() { return dbExec.submit(() -> { @@ -218,7 +216,7 @@ public class DBWriter { * its playback completion date is set to a non-null value. This method will set the playback completion date to the * current date regardless of the current value. * - * @param media FeedMedia that should be added to the playback history. + * @param media FeedMedia that should be added to the playback history. */ public static Future<?> addItemToPlaybackHistory(final FeedMedia media) { return dbExec.submit(() -> { @@ -237,7 +235,7 @@ public class DBWriter { /** * Adds a Download status object to the download log. * - * @param status The DownloadStatus object. + * @param status The DownloadStatus object. */ public static Future<?> addDownloadStatus(final DownloadStatus status) { return dbExec.submit(() -> { @@ -307,9 +305,9 @@ public class DBWriter { * Appends FeedItem objects to the end of the queue. The 'read'-attribute of all items will be set to true. * If a FeedItem is already in the queue, the FeedItem will not change its position in the queue. * - * @param context A context that is used for opening a database connection. + * @param context A context that is used for opening a database connection. * @param performAutoDownload true if an auto-download process should be started after the operation. - * @param itemIds IDs of the FeedItem objects that should be added to the queue. + * @param itemIds IDs of the FeedItem objects that should be added to the queue. */ public static Future<?> addQueueItem(final Context context, final boolean performAutoDownload, final long... itemIds) { @@ -397,7 +395,6 @@ public class DBWriter { /** * Removes all FeedItem objects from the queue. - * */ public static Future<?> clearQueue() { return dbExec.submit(() -> { @@ -412,7 +409,8 @@ public class DBWriter { /** * Removes a FeedItem object from the queue. - * @param context A context that is used for opening a database connection. + * + * @param context A context that is used for opening a database connection. * @param performAutoDownload true if an auto-download process should be started after the operation. * @param item FeedItem that should be removed. */ @@ -500,14 +498,15 @@ public class DBWriter { /** * Moves the specified item to the top of the queue. - * @param itemId The item to move to the top of the queue + * + * @param itemId The item to move to the top of the queue * @param broadcastUpdate true if this operation should trigger a QueueUpdateBroadcast. This option should be set to */ public static Future<?> moveQueueItemToTop(final long itemId, final boolean broadcastUpdate) { return dbExec.submit(() -> { LongList queueIdList = DBReader.getQueueIDList(); int index = queueIdList.indexOf(itemId); - if (index >=0) { + if (index >= 0) { moveQueueItemHelper(index, 0, broadcastUpdate); } else { Log.e(TAG, "moveQueueItemToTop: item not found"); @@ -517,7 +516,8 @@ public class DBWriter { /** * Moves the specified item to the bottom of the queue. - * @param itemId The item to move to the bottom of the queue + * + * @param itemId The item to move to the bottom of the queue * @param broadcastUpdate true if this operation should trigger a QueueUpdateBroadcast. This option should be set to */ public static Future<?> moveQueueItemToBottom(final long itemId, @@ -527,7 +527,7 @@ public class DBWriter { int index = queueIdList.indexOf(itemId); if (index >= 0) { moveQueueItemHelper(index, queueIdList.size() - 1, - broadcastUpdate); + broadcastUpdate); } else { Log.e(TAG, "moveQueueItemToBottom: item not found"); } @@ -608,7 +608,7 @@ public class DBWriter { adapter.open(); adapter.setFeedItemRead(played, itemIds); adapter.close(); - if(broadcastUpdate) { + if (broadcastUpdate) { EventDistributor.getInstance().sendUnreadItemsUpdateBroadcast(); } }); @@ -617,7 +617,8 @@ public class DBWriter { /** * Sets the 'read'-attribute of a FeedItem to the specified value. - * @param item The FeedItem object + * + * @param item The FeedItem object * @param played New value of the 'read'-attribute one of FeedItem.PLAYED, * FeedItem.NEW, FeedItem.UNPLAYED * @param resetMediaPosition true if this method should also reset the position of the FeedItem's FeedMedia object. @@ -647,7 +648,7 @@ public class DBWriter { /** * Sets the 'read'-attribute of all NEW FeedItems of a specific Feed to UNPLAYED. * - * @param feedId ID of the Feed. + * @param feedId ID of the Feed. */ public static Future<?> removeFeedNewFlag(final long feedId) { return dbExec.submit(() -> { @@ -663,7 +664,7 @@ public class DBWriter { /** * Sets the 'read'-attribute of all FeedItems of a specific Feed to PLAYED. * - * @param feedId ID of the Feed. + * @param feedId ID of the Feed. */ public static Future<?> markFeedRead(final long feedId) { return dbExec.submit(() -> { @@ -735,7 +736,7 @@ public class DBWriter { * Saves a FeedMedia object in the database. This method will save all attributes of the FeedMedia object. The * contents of FeedComponent-attributes (e.g. the FeedMedia's 'item'-attribute) will not be saved. * - * @param media The FeedMedia object. + * @param media The FeedMedia object. */ public static Future<?> setFeedMedia(final FeedMedia media) { return dbExec.submit(() -> { @@ -749,7 +750,7 @@ public class DBWriter { /** * Saves the 'position', 'duration' and 'last played time' attributes of a FeedMedia object * - * @param media The FeedMedia object. + * @param media The FeedMedia object. */ public static Future<?> setFeedMediaPlaybackInformation(final FeedMedia media) { return dbExec.submit(() -> { @@ -764,7 +765,7 @@ public class DBWriter { * Saves a FeedItem object in the database. This method will save all attributes of the FeedItem object including * the content of FeedComponent-attributes. * - * @param item The FeedItem object. + * @param item The FeedItem object. */ public static Future<?> setFeedItem(final FeedItem item) { return dbExec.submit(() -> { @@ -780,7 +781,7 @@ public class DBWriter { * Updates download URL of a feed */ public static Future<?> updateFeedDownloadURL(final String original, final String updated) { - Log.d(TAG, "updateFeedDownloadURL(original: " + original + ", updated: " + updated +")"); + Log.d(TAG, "updateFeedDownloadURL(original: " + original + ", updated: " + updated + ")"); return dbExec.submit(() -> { PodDBAdapter adapter = PodDBAdapter.getInstance(); adapter.open(); @@ -805,11 +806,11 @@ public class DBWriter { } private static boolean itemListContains(List<FeedItem> items, long itemId) { - return indexInItemList(items, itemId) >= 0; + return indexInItemList(items, itemId) >= 0; } private static int indexInItemList(List<FeedItem> items, long itemId) { - for (int i = 0; i < items.size(); i ++) { + for (int i = 0; i < items.size(); i++) { FeedItem item = items.get(i); if (item.getId() == itemId) { return i; @@ -873,7 +874,7 @@ public class DBWriter { /** * Sets the 'auto_download'-attribute of specific FeedItem. * - * @param feedItem FeedItem. + * @param feedItem FeedItem. * @param autoDownload true enables auto download, false disables it */ public static Future<?> setFeedItemAutoDownload(final FeedItem feedItem, @@ -891,7 +892,7 @@ public class DBWriter { return dbExec.submit(() -> { int failedAttempts = feedItem.getFailedAutoDownloadAttempts() + 1; long autoDownload; - if(!feedItem.getAutoDownload() || failedAttempts >= 10) { + if (!feedItem.getAutoDownload() || failedAttempts >= 10) { autoDownload = 0; // giving up, disable auto download feedItem.setAutoDownload(false); } else { @@ -927,7 +928,8 @@ public class DBWriter { /** * Set filter of the feed - * @param feedId The feed's ID + * + * @param feedId The feed's ID * @param filterValues Values that represent properties to filter by */ public static Future<?> setFeedItemsFilter(final long feedId, @@ -942,4 +944,17 @@ public class DBWriter { }); } + + /** + * Reset the statistics in DB + */ + @NonNull + public static Future<?> resetStatistics() { + return dbExec.submit(() -> { + PodDBAdapter adapter = PodDBAdapter.getInstance(); + adapter.open(); + adapter.resetAllMediaPlayedDuration(); + adapter.close(); + }); + } } 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 9c48f31dd..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; 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 04accecd0..fa72cd510 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 @@ -12,10 +12,9 @@ import android.database.SQLException; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase.CursorFactory; import android.database.sqlite.SQLiteOpenHelper; -import android.media.MediaMetadataRetriever; import android.os.Build; -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; @@ -37,6 +36,8 @@ import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.service.download.DownloadStatus; import de.danoeh.antennapod.core.util.LongIntMap; +import static de.danoeh.antennapod.core.feed.FeedPreferences.SPEED_USE_GLOBAL; + // TODO Remove media column from feeditem table /** @@ -106,6 +107,7 @@ public class PodDBAdapter { public static final String KEY_LAST_PLAYED_TIME = "last_played_time"; public static final String KEY_INCLUDE_FILTER = "include_filter"; public static final String KEY_EXCLUDE_FILTER = "exclude_filter"; + public static final String KEY_FEED_PLAYBACK_SPEED = "feed_playback_speed"; // Table names static final String TABLE_NAME_FEEDS = "Feeds"; @@ -139,6 +141,7 @@ public class PodDBAdapter { + KEY_HIDE + " TEXT," + KEY_LAST_UPDATE_FAILED + " INTEGER DEFAULT 0," + KEY_AUTO_DELETE_ACTION + " INTEGER DEFAULT 0," + + KEY_FEED_PLAYBACK_SPEED + " REAL DEFAULT " + SPEED_USE_GLOBAL + "," + KEY_FEED_VOLUME_REDUCTION + " INTEGER DEFAULT 0)"; private static final String CREATE_TABLE_FEED_ITEMS = "CREATE TABLE " @@ -160,7 +163,7 @@ public class PodDBAdapter { + KEY_FEEDITEM + " INTEGER," + KEY_PLAYED_DURATION + " INTEGER," + KEY_HAS_EMBEDDED_PICTURE + " INTEGER," - + KEY_LAST_PLAYED_TIME + " INTEGER)"; + + KEY_LAST_PLAYED_TIME + " INTEGER" + ")"; private static final String CREATE_TABLE_DOWNLOAD_LOG = "CREATE TABLE " + TABLE_NAME_DOWNLOAD_LOG + " (" + TABLE_PRIMARY_KEY + KEY_FEEDFILE @@ -237,7 +240,8 @@ public class PodDBAdapter { TABLE_NAME_FEEDS + "." + KEY_AUTO_DELETE_ACTION, TABLE_NAME_FEEDS + "." + KEY_FEED_VOLUME_REDUCTION, TABLE_NAME_FEEDS + "." + KEY_INCLUDE_FILTER, - TABLE_NAME_FEEDS + "." + KEY_EXCLUDE_FILTER + TABLE_NAME_FEEDS + "." + KEY_EXCLUDE_FILTER, + TABLE_NAME_FEEDS + "." + KEY_FEED_PLAYBACK_SPEED }; /** @@ -403,6 +407,7 @@ public class PodDBAdapter { values.put(KEY_PASSWORD, prefs.getPassword()); values.put(KEY_INCLUDE_FILTER, prefs.getFilter().getIncludeFilter()); values.put(KEY_EXCLUDE_FILTER, prefs.getFilter().getExcludeFilter()); + values.put(KEY_FEED_PLAYBACK_SPEED, prefs.getFeedPlaybackSpeed()); db.update(TABLE_NAME_FEEDS, values, KEY_ID + "=?", new String[]{String.valueOf(prefs.getFeedID())}); } @@ -475,6 +480,20 @@ public class PodDBAdapter { } } + public void resetAllMediaPlayedDuration() { + try { + db.beginTransactionNonExclusive(); + ContentValues values = new ContentValues(); + values.put(KEY_PLAYED_DURATION, 0); + db.update(TABLE_NAME_FEED_MEDIA, values, null, new String[0]); + db.setTransactionSuccessful(); + } catch (SQLException e) { + Log.e(TAG, Log.getStackTraceString(e)); + } finally { + db.endTransaction(); + } + } + /** * Insert all FeedItems of a feed and the feed object itself in a single * transaction @@ -1440,7 +1459,7 @@ public class PodDBAdapter { */ private static class PodDBHelper extends SQLiteOpenHelper { - private static final int VERSION = 1070296; + private static final int VERSION = 1070400; private final Context context; 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/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/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/download/AutoUpdateManager.java b/core/src/main/java/de/danoeh/antennapod/core/util/download/AutoUpdateManager.java index ebeec058d..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,7 +1,7 @@ package de.danoeh.antennapod.core.util.download; import android.content.Context; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import android.util.Log; import androidx.work.Constraints; 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/playback/ExternalMedia.java b/core/src/main/java/de/danoeh/antennapod/core/util/playback/ExternalMedia.java index 9b644c3ba..b55091009 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 @@ -6,245 +6,252 @@ import android.content.SharedPreferences.Editor; import android.media.MediaMetadataRetriever; import android.os.Parcel; import android.os.Parcelable; - -import java.util.List; -import java.util.concurrent.Callable; - import de.danoeh.antennapod.core.feed.Chapter; import de.danoeh.antennapod.core.feed.MediaType; import de.danoeh.antennapod.core.util.ChapterUtils; +import java.util.List; +import java.util.concurrent.Callable; +import org.apache.commons.io.FilenameUtils; /** Represents a media file that is stored on the local storage device. */ public class ExternalMedia implements Playable { + public static final int PLAYABLE_TYPE_EXTERNAL_MEDIA = 2; + public static final String PREF_SOURCE_URL = "ExternalMedia.PrefSourceUrl"; + public static final String PREF_POSITION = "ExternalMedia.PrefPosition"; + public static final String PREF_MEDIA_TYPE = "ExternalMedia.PrefMediaType"; + public static final String PREF_LAST_PLAYED_TIME = "ExternalMedia.PrefLastPlayedTime"; + + private final String source; + private String episodeTitle; + private String feedTitle; + private MediaType mediaType; + private List<Chapter> chapters; + private int duration; + private int position; + private long lastPlayedTime; + + /** + * Creates a new playable for files on the sd card. + * @param source File path of the file + * @param mediaType Type of the file + */ + public ExternalMedia(String source, MediaType mediaType) { + super(); + this.source = source; + this.mediaType = mediaType; + } - public static final int PLAYABLE_TYPE_EXTERNAL_MEDIA = 2; - public static final String PREF_SOURCE_URL = "ExternalMedia.PrefSourceUrl"; - public static final String PREF_POSITION = "ExternalMedia.PrefPosition"; - public static final String PREF_MEDIA_TYPE = "ExternalMedia.PrefMediaType"; - public static final String PREF_LAST_PLAYED_TIME = "ExternalMedia.PrefLastPlayedTime"; - - private final String source; - - private String episodeTitle; - private String feedTitle; - private MediaType mediaType = MediaType.AUDIO; - private List<Chapter> chapters; - private int duration; - private int position; - private long lastPlayedTime; - - public ExternalMedia(String source, MediaType mediaType) { - super(); - this.source = source; - this.mediaType = mediaType; - } - - public ExternalMedia(String source, MediaType mediaType, int position, long lastPlayedTime) { - this(source, mediaType); - this.position = position; - this.lastPlayedTime = lastPlayedTime; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeString(source); - dest.writeString(mediaType.toString()); - dest.writeInt(position); - dest.writeLong(lastPlayedTime); - } - - @Override - public void writeToPreferences(Editor prefEditor) { - prefEditor.putString(PREF_SOURCE_URL, source); - prefEditor.putString(PREF_MEDIA_TYPE, mediaType.toString()); - prefEditor.putInt(PREF_POSITION, position); - prefEditor.putLong(PREF_LAST_PLAYED_TIME, lastPlayedTime); - } - - @Override - public void loadMetadata() throws PlayableException { - MediaMetadataRetriever mmr = new MediaMetadataRetriever(); - try { - mmr.setDataSource(source); - } catch (IllegalArgumentException e) { - e.printStackTrace(); - throw new PlayableException( - "IllegalArgumentException when setting up MediaMetadataReceiver"); - } catch (RuntimeException e) { - // http://code.google.com/p/android/issues/detail?id=39770 - e.printStackTrace(); - throw new PlayableException( - "RuntimeException when setting up MediaMetadataRetriever"); - } - episodeTitle = mmr - .extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE); - feedTitle = mmr - .extractMetadata(MediaMetadataRetriever.METADATA_KEY_ALBUM); + /** + * Creates a new playable for files on the sd card. + * @param source File path of the file + * @param mediaType Type of the file + * @param position Position to start from + * @param lastPlayedTime Timestamp when it was played last + */ + public ExternalMedia(String source, MediaType mediaType, int position, long lastPlayedTime) { + this(source, mediaType); + this.position = position; + this.lastPlayedTime = lastPlayedTime; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(source); + dest.writeString(mediaType.toString()); + dest.writeInt(position); + dest.writeLong(lastPlayedTime); + } + + @Override + public void writeToPreferences(Editor prefEditor) { + prefEditor.putString(PREF_SOURCE_URL, source); + prefEditor.putString(PREF_MEDIA_TYPE, mediaType.toString()); + prefEditor.putInt(PREF_POSITION, position); + prefEditor.putLong(PREF_LAST_PLAYED_TIME, lastPlayedTime); + } + + @Override + public void loadMetadata() throws PlayableException { + MediaMetadataRetriever mmr = new MediaMetadataRetriever(); try { - duration = Integer.parseInt(mmr - .extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)); + mmr.setDataSource(source); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + throw new PlayableException("IllegalArgumentException when setting up MediaMetadataReceiver"); + } catch (RuntimeException e) { + // http://code.google.com/p/android/issues/detail?id=39770 + e.printStackTrace(); + throw new PlayableException("RuntimeException when setting up MediaMetadataRetriever"); + } + episodeTitle = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE); + if (episodeTitle == null) { + episodeTitle = FilenameUtils.getName(source); + } + feedTitle = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ALBUM); + try { + duration = Integer.parseInt(mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)); } catch (NumberFormatException e) { e.printStackTrace(); throw new PlayableException("NumberFormatException when reading duration of media file"); } - ChapterUtils.loadChaptersFromFileUrl(this); - } - - @Override - public void loadChapterMarks() { - - } - - @Override - public String getEpisodeTitle() { - return episodeTitle; - } - - @Override - public Callable<String> loadShownotes() { - return () -> ""; - } - - @Override - public List<Chapter> getChapters() { - return chapters; - } - - @Override - public String getWebsiteLink() { - return null; - } - - @Override - public String getPaymentLink() { - return null; - } - - @Override - public String getFeedTitle() { - return feedTitle; - } - - @Override - public Object getIdentifier() { - return source; - } - - @Override - public int getDuration() { - return duration; - } - - @Override - public int getPosition() { - return position; - } - - @Override - public long getLastPlayedTime() { - return lastPlayedTime; - } - - @Override - public MediaType getMediaType() { - return mediaType; - } - - @Override - public String getLocalMediaUrl() { - return source; - } - - @Override - public String getStreamUrl() { - return null; - } - - @Override - public boolean localFileAvailable() { - return true; - } - - @Override - public boolean streamAvailable() { - return false; - } - - @Override - public void saveCurrentPosition(SharedPreferences pref, int newPosition, long timestamp) { - SharedPreferences.Editor editor = pref.edit(); - editor.putInt(PREF_POSITION, newPosition); - editor.putLong(PREF_LAST_PLAYED_TIME, timestamp); - position = newPosition; - lastPlayedTime = timestamp; - editor.apply(); - } - - @Override - public void setPosition(int newPosition) { - position = newPosition; - } - - @Override - public void setDuration(int newDuration) { - duration = newDuration; - } - - @Override - public void setLastPlayedTime(long lastPlayedTime) { - this.lastPlayedTime = lastPlayedTime; - } - - @Override - public void onPlaybackStart() { - - } - - @Override - public void onPlaybackPause(Context context) { - - } - - @Override - public void onPlaybackCompleted(Context context) { - - } - - @Override - public int getPlayableType() { - return PLAYABLE_TYPE_EXTERNAL_MEDIA; - } - - @Override - public void setChapters(List<Chapter> chapters) { - this.chapters = chapters; - } - - public static final Parcelable.Creator<ExternalMedia> CREATOR = new Parcelable.Creator<ExternalMedia>() { - public ExternalMedia createFromParcel(Parcel in) { - String source = in.readString(); - MediaType type = MediaType.valueOf(in.readString()); - int position = 0; - if (in.dataAvail() > 0) { - position = in.readInt(); - } - long lastPlayedTime = 0; - if (in.dataAvail() > 0) { - lastPlayedTime = in.readLong(); - } - - return new ExternalMedia(source, type, position, lastPlayedTime); - } - - public ExternalMedia[] newArray(int size) { - return new ExternalMedia[size]; - } - }; + ChapterUtils.loadChaptersFromFileUrl(this); + } + + @Override + public void loadChapterMarks() { + + } + + @Override + public String getEpisodeTitle() { + return episodeTitle; + } + + @Override + public Callable<String> loadShownotes() { + return () -> ""; + } + + @Override + public List<Chapter> getChapters() { + return chapters; + } + + @Override + public String getWebsiteLink() { + return null; + } + + @Override + public String getPaymentLink() { + return null; + } + + @Override + public String getFeedTitle() { + return feedTitle; + } + + @Override + public Object getIdentifier() { + return source; + } + + @Override + public int getDuration() { + return duration; + } + + @Override + public int getPosition() { + return position; + } + + @Override + public long getLastPlayedTime() { + return lastPlayedTime; + } + + @Override + public MediaType getMediaType() { + return mediaType; + } + + @Override + public String getLocalMediaUrl() { + return source; + } + + @Override + public String getStreamUrl() { + return null; + } + + @Override + public boolean localFileAvailable() { + return true; + } + + @Override + public boolean streamAvailable() { + return false; + } + + @Override + public void saveCurrentPosition(SharedPreferences pref, int newPosition, long timestamp) { + SharedPreferences.Editor editor = pref.edit(); + editor.putInt(PREF_POSITION, newPosition); + editor.putLong(PREF_LAST_PLAYED_TIME, timestamp); + position = newPosition; + lastPlayedTime = timestamp; + editor.apply(); + } + + @Override + public void setPosition(int newPosition) { + position = newPosition; + } + + @Override + public void setDuration(int newDuration) { + duration = newDuration; + } + + @Override + public void setLastPlayedTime(long lastPlayedTime) { + this.lastPlayedTime = lastPlayedTime; + } + + @Override + public void onPlaybackStart() { + + } + + @Override + public void onPlaybackPause(Context context) { + + } + + @Override + public void onPlaybackCompleted(Context context) { + + } + + @Override + public int getPlayableType() { + return PLAYABLE_TYPE_EXTERNAL_MEDIA; + } + + @Override + public void setChapters(List<Chapter> chapters) { + this.chapters = chapters; + } + + public static final Parcelable.Creator<ExternalMedia> CREATOR = new Parcelable.Creator<ExternalMedia>() { + public ExternalMedia createFromParcel(Parcel in) { + String source = in.readString(); + MediaType type = MediaType.valueOf(in.readString()); + int position = 0; + if (in.dataAvail() > 0) { + position = in.readInt(); + } + long lastPlayedTime = 0; + if (in.dataAvail() > 0) { + lastPlayedTime = in.readLong(); + } + + return new ExternalMedia(source, type, position, lastPlayedTime); + } + + public ExternalMedia[] newArray(int size) { + return new ExternalMedia[size]; + } + }; @Override public String getImageLocation() { 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 1456ebd8d..ba903eeb9 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; @@ -24,12 +24,12 @@ import android.widget.TextView; import java.util.concurrent.ScheduledThreadPoolExecutor; import de.danoeh.antennapod.core.R; -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.FeedMedia; import de.danoeh.antennapod.core.feed.MediaType; import de.danoeh.antennapod.core.preferences.PlaybackPreferences; +import de.danoeh.antennapod.core.preferences.PlaybackSpeedHelper; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.service.playback.PlaybackService; import de.danoeh.antennapod.core.service.playback.PlaybackServiceMediaPlayer; @@ -503,16 +503,6 @@ public class PlaybackController { PlaybackServiceMediaPlayer.PSMPInfo info = playbackService.getPSMPInfo(); status = info.playerStatus; media = info.playable; - /* - if (media == null) { - Log.w(TAG, - "PlaybackService has no media object. Trying to restore last played media."); - Intent serviceIntent = getPlayLastPlayedMediaIntent(); - if (serviceIntent != null) { - ContextCompat.startForegroundService(activity, serviceIntent); - } - } - */ onServiceQueried(); setupGUI(); @@ -715,12 +705,13 @@ public class PlaybackController { if (playbackService != null && canSetPlaybackSpeed()) { return playbackService.getCurrentPlaybackSpeed(); } else { - return -1; + return PlaybackSpeedHelper.getCurrentPlaybackSpeed(getMedia()); } } public boolean canDownmix() { - return playbackService != null && playbackService.canDownmix(); + return (playbackService != null && playbackService.canDownmix()) + || UserPreferences.useSonic(); } public void setDownmix(boolean enable) { 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; |