diff options
author | ByteHamster <ByteHamster@users.noreply.github.com> | 2022-11-06 10:54:18 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-11-06 10:54:18 +0100 |
commit | be8c8cef4d974f7ecaee858591182065651dca93 (patch) | |
tree | 9be7a7363dfc3a168efb5647c5d23121cdd27173 /core | |
parent | 546c8841db829a6ac57e8508a91d5734fb4d1ddf (diff) | |
parent | 11292b598cc6867e32f4f313dfd6eff1197efdd3 (diff) | |
download | AntennaPod-be8c8cef4d974f7ecaee858591182065651dca93.zip |
Merge pull request #6176 from ByteHamster/decouple
Decouple some classes
Diffstat (limited to 'core')
31 files changed, 436 insertions, 360 deletions
diff --git a/core/src/main/AndroidManifest.xml b/core/src/main/AndroidManifest.xml index 3a5266ec1..3d7b3b473 100644 --- a/core/src/main/AndroidManifest.xml +++ b/core/src/main/AndroidManifest.xml @@ -28,6 +28,7 @@ <intent-filter> <action android:name="android.media.browse.MediaBrowserService"/> + <action android:name="de.danoeh.antennapod.intents.PLAYBACK_SERVICE" /> </intent-filter> </service> diff --git a/core/src/main/java/de/danoeh/antennapod/core/ClientConfig.java b/core/src/main/java/de/danoeh/antennapod/core/ClientConfig.java index 86326911d..9dab98939 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/ClientConfig.java +++ b/core/src/main/java/de/danoeh/antennapod/core/ClientConfig.java @@ -1,26 +1,10 @@ package de.danoeh.antennapod.core; -import android.content.Context; - -import de.danoeh.antennapod.net.ssl.SslProviderInstaller; - -import de.danoeh.antennapod.core.preferences.PlaybackPreferences; -import de.danoeh.antennapod.core.preferences.SleepTimerPreferences; -import de.danoeh.antennapod.core.preferences.UsageStatistics; -import de.danoeh.antennapod.core.preferences.UserPreferences; -import de.danoeh.antennapod.core.service.download.AntennapodHttpClient; -import de.danoeh.antennapod.core.util.NetworkUtils; -import de.danoeh.antennapod.core.util.gui.NotificationUtils; -import de.danoeh.antennapod.storage.database.PodDBAdapter; - -import java.io.File; - /** * Stores callbacks for core classes like Services, DB classes etc. and other configuration variables. * Apps using the core module of AntennaPod should register implementations of all interfaces here. */ public class ClientConfig { - /** * Should be used when setting User-Agent header for HTTP-requests. */ @@ -29,23 +13,4 @@ public class ClientConfig { public static ApplicationCallbacks applicationCallbacks; public static DownloadServiceCallbacks downloadServiceCallbacks; - - private static boolean initialized = false; - - public static synchronized void initialize(Context context) { - if (initialized) { - return; - } - PodDBAdapter.init(context); - UserPreferences.init(context); - UsageStatistics.init(context); - PlaybackPreferences.init(context); - SslProviderInstaller.install(context); - NetworkUtils.init(context); - AntennapodHttpClient.setCacheDirectory(new File(context.getCacheDir(), "okhttp")); - AntennapodHttpClient.setProxyConfig(UserPreferences.getProxyConfig()); - SleepTimerPreferences.init(context); - NotificationUtils.createChannels(context); - initialized = true; - } } diff --git a/core/src/main/java/de/danoeh/antennapod/core/ClientConfigurator.java b/core/src/main/java/de/danoeh/antennapod/core/ClientConfigurator.java new file mode 100644 index 000000000..b70a3a61a --- /dev/null +++ b/core/src/main/java/de/danoeh/antennapod/core/ClientConfigurator.java @@ -0,0 +1,43 @@ +package de.danoeh.antennapod.core; + +import android.content.Context; +import de.danoeh.antennapod.core.preferences.PlaybackPreferences; +import de.danoeh.antennapod.core.preferences.SleepTimerPreferences; +import de.danoeh.antennapod.core.preferences.UsageStatistics; +import de.danoeh.antennapod.core.preferences.UserPreferences; +import de.danoeh.antennapod.core.service.download.AntennapodHttpClient; +import de.danoeh.antennapod.core.service.download.DownloadServiceInterface; +import de.danoeh.antennapod.core.service.download.DownloadServiceInterfaceImpl; +import de.danoeh.antennapod.core.sync.SyncService; +import de.danoeh.antennapod.core.sync.queue.SynchronizationQueueSink; +import de.danoeh.antennapod.core.util.NetworkUtils; +import de.danoeh.antennapod.core.util.download.NetworkConnectionChangeHandler; +import de.danoeh.antennapod.core.util.gui.NotificationUtils; +import de.danoeh.antennapod.net.ssl.SslProviderInstaller; +import de.danoeh.antennapod.storage.database.PodDBAdapter; + +import java.io.File; + +public class ClientConfigurator { + private static boolean initialized = false; + + public static synchronized void initialize(Context context) { + if (initialized) { + return; + } + PodDBAdapter.init(context); + UserPreferences.init(context); + UsageStatistics.init(context); + PlaybackPreferences.init(context); + SslProviderInstaller.install(context); + NetworkUtils.init(context); + NetworkConnectionChangeHandler.init(context); + DownloadServiceInterface.setImpl(new DownloadServiceInterfaceImpl()); + SynchronizationQueueSink.setServiceStarterImpl(() -> SyncService.sync(context)); + AntennapodHttpClient.setCacheDirectory(new File(context.getCacheDir(), "okhttp")); + AntennapodHttpClient.setProxyConfig(UserPreferences.getProxyConfig()); + SleepTimerPreferences.init(context); + NotificationUtils.createChannels(context); + initialized = true; + } +} diff --git a/core/src/main/java/de/danoeh/antennapod/core/backup/OpmlBackupAgent.java b/core/src/main/java/de/danoeh/antennapod/core/backup/OpmlBackupAgent.java index 37849c296..d026e1a7b 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/backup/OpmlBackupAgent.java +++ b/core/src/main/java/de/danoeh/antennapod/core/backup/OpmlBackupAgent.java @@ -9,8 +9,8 @@ import android.os.ParcelFileDescriptor; import android.util.Log; import de.danoeh.antennapod.core.service.download.DownloadRequest; -import de.danoeh.antennapod.core.service.download.DownloadService; import de.danoeh.antennapod.core.service.download.DownloadRequestCreator; +import de.danoeh.antennapod.core.service.download.DownloadServiceInterface; import org.apache.commons.io.IOUtils; import org.xmlpull.v1.XmlPullParserException; @@ -145,7 +145,7 @@ public class OpmlBackupAgent extends BackupAgentHelper { for (OpmlElement opmlElem : opmlElements) { Feed feed = new Feed(opmlElem.getXmlUrl(), null, opmlElem.getText()); DownloadRequest request = DownloadRequestCreator.create(feed).build(); - DownloadService.download(mContext, false, request); + DownloadServiceInterface.get().download(mContext, false, request); } } catch (XmlPullParserException e) { Log.e(TAG, "Error while parsing the OPML file", e); 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 b0321bff6..160b6b774 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 @@ -2,9 +2,12 @@ package de.danoeh.antennapod.core.preferences; import android.content.Context; import android.content.SharedPreferences; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.preference.PreferenceManager; import android.util.Log; +import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.event.PlayerStatusEvent; import de.danoeh.antennapod.model.feed.FeedMedia; import de.danoeh.antennapod.model.playback.MediaType; @@ -187,4 +190,48 @@ public class PlaybackPreferences implements SharedPreferences.OnSharedPreference } return playerStatusAsInt; } + + /** + * Restores a playable object from a sharedPreferences file. This method might load data from the database, + * depending on the type of playable that was restored. + * + * @return The restored Playable object + */ + @Nullable + public static Playable createInstanceFromPreferences(@NonNull Context context) { + long currentlyPlayingMedia = PlaybackPreferences.getCurrentlyPlayingMediaType(); + if (currentlyPlayingMedia != PlaybackPreferences.NO_MEDIA_PLAYING) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext()); + return createInstanceFromPreferences((int) currentlyPlayingMedia, prefs); + } + return null; + } + + /** + * Restores a playable object from a sharedPreferences file. This method might load data from the database, + * depending on the type of playable that was restored. + * + * @param type An integer that represents the type of the Playable object + * that is restored. + * @param pref The SharedPreferences file from which the Playable object + * is restored + * @return The restored Playable object + */ + private static Playable createInstanceFromPreferences(int type, SharedPreferences pref) { + if (type == FeedMedia.PLAYABLE_TYPE_FEEDMEDIA) { + return createFeedMediaInstance(pref); + } else { + Log.e(TAG, "Could not restore Playable object from preferences"); + return null; + } + } + + private static Playable createFeedMediaInstance(SharedPreferences pref) { + Playable result = null; + long mediaId = pref.getLong(FeedMedia.PREF_MEDIA_ID, -1); + if (mediaId != -1) { + result = DBReader.getFeedMedia(mediaId); + } + return result; + } } diff --git a/core/src/main/java/de/danoeh/antennapod/core/receiver/FeedUpdateReceiver.java b/core/src/main/java/de/danoeh/antennapod/core/receiver/FeedUpdateReceiver.java index b667b154c..9ce89ebe2 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/receiver/FeedUpdateReceiver.java +++ b/core/src/main/java/de/danoeh/antennapod/core/receiver/FeedUpdateReceiver.java @@ -5,7 +5,7 @@ import android.content.Context; import android.content.Intent; import android.util.Log; -import de.danoeh.antennapod.core.ClientConfig; +import de.danoeh.antennapod.core.ClientConfigurator; import de.danoeh.antennapod.core.util.download.AutoUpdateManager; /** @@ -18,7 +18,7 @@ public class FeedUpdateReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Log.d(TAG, "Received intent"); - ClientConfig.initialize(context); + ClientConfigurator.initialize(context); AutoUpdateManager.runOnce(context); } 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 9a96379bc..e91e24ec6 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 @@ -9,8 +9,7 @@ import androidx.core.content.ContextCompat; import android.util.Log; import android.view.KeyEvent; -import de.danoeh.antennapod.core.ClientConfig; -import de.danoeh.antennapod.core.service.playback.PlaybackService; +import de.danoeh.antennapod.core.ClientConfigurator; /** * Receives media button events. @@ -22,6 +21,7 @@ public class MediaButtonReceiver extends BroadcastReceiver { public static final String EXTRA_HARDWAREBUTTON = "de.danoeh.antennapod.core.service.extra.MediaButtonReceiver.HARDWAREBUTTON"; public static final String NOTIFY_BUTTON_RECEIVER = "de.danoeh.antennapod.NOTIFY_BUTTON_RECEIVER"; + public static final String PLAYBACK_SERVICE_INTENT = "de.danoeh.antennapod.intents.PLAYBACK_SERVICE"; @Override public void onReceive(Context context, Intent intent) { @@ -31,8 +31,9 @@ public class MediaButtonReceiver extends BroadcastReceiver { } KeyEvent event = (KeyEvent) intent.getExtras().get(Intent.EXTRA_KEY_EVENT); if (event != null && event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) { - ClientConfig.initialize(context); - Intent serviceIntent = new Intent(context, PlaybackService.class); + ClientConfigurator.initialize(context); + Intent serviceIntent = new Intent(PLAYBACK_SERVICE_INTENT); + serviceIntent.setPackage(context.getPackageName()); serviceIntent.putExtra(EXTRA_KEYCODE, event.getKeyCode()); serviceIntent.putExtra(EXTRA_SOURCE, event.getSource()); serviceIntent.putExtra(EXTRA_HARDWAREBUTTON, event.getEventTime() > 0 || event.getDownTime() > 0); 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 6265fc30d..ce3fe0e3f 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 @@ -7,7 +7,7 @@ import android.util.Log; import androidx.work.Worker; import androidx.work.WorkerParameters; -import de.danoeh.antennapod.core.ClientConfig; +import de.danoeh.antennapod.core.ClientConfigurator; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.storage.DBTasks; import de.danoeh.antennapod.core.util.NetworkUtils; @@ -28,7 +28,7 @@ public class FeedUpdateWorker extends Worker { public Result doWork() { final boolean isRunOnce = getInputData().getBoolean(PARAM_RUN_ONCE, false); Log.d(TAG, "doWork() : isRunOnce = " + isRunOnce); - ClientConfig.initialize(getApplicationContext()); + ClientConfigurator.initialize(getApplicationContext()); if (NetworkUtils.networkAvailable() && NetworkUtils.isFeedRefreshAllowed()) { DBTasks.refreshAllFeeds(getApplicationContext(), false); 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 8c26a6d74..12bbf83d0 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 @@ -17,8 +17,6 @@ import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.core.app.ServiceCompat; -import androidx.core.content.ContextCompat; -import de.danoeh.antennapod.core.BuildConfig; import de.danoeh.antennapod.core.R; import de.danoeh.antennapod.core.feed.LocalFeedUpdater; import de.danoeh.antennapod.core.storage.EpisodeCleanupAlgorithmFactory; @@ -141,58 +139,6 @@ public class DownloadService extends Service { } } - public static void download(Context context, boolean cleanupMedia, DownloadRequest... requests) { - ArrayList<DownloadRequest> requestsToSend = new ArrayList<>(); - for (DownloadRequest request : requests) { - if (!isDownloadingFile(request.getSource())) { - requestsToSend.add(request); - } - } - if (requestsToSend.isEmpty()) { - return; - } else if (requestsToSend.size() > 100) { - if (BuildConfig.DEBUG) { - throw new IllegalArgumentException("Android silently drops intent payloads that are too large"); - } else { - Log.d(TAG, "Too many download requests. Dropping some to avoid Android dropping all."); - requestsToSend = new ArrayList<>(requestsToSend.subList(0, 100)); - } - } - - Intent launchIntent = new Intent(context, DownloadService.class); - launchIntent.putParcelableArrayListExtra(DownloadService.EXTRA_REQUESTS, requestsToSend); - if (cleanupMedia) { - launchIntent.putExtra(DownloadService.EXTRA_CLEANUP_MEDIA, true); - } - ContextCompat.startForegroundService(context, launchIntent); - } - - public static void refreshAllFeeds(Context context, boolean initiatedByUser) { - Intent launchIntent = new Intent(context, DownloadService.class); - launchIntent.putExtra(DownloadService.EXTRA_REFRESH_ALL, true); - launchIntent.putExtra(DownloadService.EXTRA_INITIATED_BY_USER, initiatedByUser); - ContextCompat.startForegroundService(context, launchIntent); - } - - public static void cancel(Context context, String url) { - if (!isRunning) { - return; - } - Intent cancelIntent = new Intent(DownloadService.ACTION_CANCEL_DOWNLOAD); - cancelIntent.putExtra(DownloadService.EXTRA_DOWNLOAD_URL, url); - cancelIntent.setPackage(context.getPackageName()); - context.sendBroadcast(cancelIntent); - } - - public static void cancelAll(Context context) { - if (!isRunning) { - return; - } - Intent cancelIntent = new Intent(DownloadService.ACTION_CANCEL_ALL_DOWNLOADS); - cancelIntent.setPackage(context.getPackageName()); - context.sendBroadcast(cancelIntent); - } - public static boolean isDownloadingFeeds() { if (!isRunning) { return false; @@ -373,7 +319,7 @@ public class DownloadService extends Service { Log.d(TAG, "Requested invalid range, restarting download from the beginning"); FileUtils.deleteQuietly(new File(downloader.getDownloadRequest().getDestination())); - download(this, false, downloader.getDownloadRequest()); + DownloadServiceInterface.get().download(this, false, downloader.getDownloadRequest()); } else { Log.e(TAG, "Download failed"); saveDownloadStatus(status); diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadServiceInterface.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadServiceInterface.java new file mode 100644 index 000000000..aa4b16490 --- /dev/null +++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadServiceInterface.java @@ -0,0 +1,23 @@ +package de.danoeh.antennapod.core.service.download; + +import android.content.Context; + +public abstract class DownloadServiceInterface { + private static DownloadServiceInterface impl; + + public static DownloadServiceInterface get() { + return impl; + } + + public static void setImpl(DownloadServiceInterface impl) { + DownloadServiceInterface.impl = impl; + } + + public abstract void download(Context context, boolean cleanupMedia, DownloadRequest... requests); + + public abstract void refreshAllFeeds(Context context, boolean initiatedByUser); + + public abstract void cancel(Context context, String url); + + public abstract void cancelAll(Context context); +} diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadServiceInterfaceImpl.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadServiceInterfaceImpl.java new file mode 100644 index 000000000..f8d34174e --- /dev/null +++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadServiceInterfaceImpl.java @@ -0,0 +1,67 @@ +package de.danoeh.antennapod.core.service.download; + +import android.content.Context; +import android.content.Intent; +import androidx.core.content.ContextCompat; +import com.google.android.exoplayer2.util.Log; +import de.danoeh.antennapod.core.BuildConfig; + +import java.util.ArrayList; + +import static de.danoeh.antennapod.core.service.download.DownloadService.isDownloadingFile; + +public class DownloadServiceInterfaceImpl extends DownloadServiceInterface { + private static final String TAG = "DownloadServiceInterface"; + + public void download(Context context, boolean cleanupMedia, DownloadRequest... requests) { + ArrayList<DownloadRequest> requestsToSend = new ArrayList<>(); + for (DownloadRequest request : requests) { + if (!isDownloadingFile(request.getSource())) { + requestsToSend.add(request); + } + } + if (requestsToSend.isEmpty()) { + return; + } else if (requestsToSend.size() > 100) { + if (BuildConfig.DEBUG) { + throw new IllegalArgumentException("Android silently drops intent payloads that are too large"); + } else { + Log.d(TAG, "Too many download requests. Dropping some to avoid Android dropping all."); + requestsToSend = new ArrayList<>(requestsToSend.subList(0, 100)); + } + } + + Intent launchIntent = new Intent(context, DownloadService.class); + launchIntent.putParcelableArrayListExtra(DownloadService.EXTRA_REQUESTS, requestsToSend); + if (cleanupMedia) { + launchIntent.putExtra(DownloadService.EXTRA_CLEANUP_MEDIA, true); + } + ContextCompat.startForegroundService(context, launchIntent); + } + + public void refreshAllFeeds(Context context, boolean initiatedByUser) { + Intent launchIntent = new Intent(context, DownloadService.class); + launchIntent.putExtra(DownloadService.EXTRA_REFRESH_ALL, true); + launchIntent.putExtra(DownloadService.EXTRA_INITIATED_BY_USER, initiatedByUser); + ContextCompat.startForegroundService(context, launchIntent); + } + + public void cancel(Context context, String url) { + if (!DownloadService.isRunning) { + return; + } + Intent cancelIntent = new Intent(DownloadService.ACTION_CANCEL_DOWNLOAD); + cancelIntent.putExtra(DownloadService.EXTRA_DOWNLOAD_URL, url); + cancelIntent.setPackage(context.getPackageName()); + context.sendBroadcast(cancelIntent); + } + + public void cancelAll(Context context) { + if (!DownloadService.isRunning) { + return; + } + Intent cancelIntent = new Intent(DownloadService.ACTION_CANCEL_ALL_DOWNLOADS); + cancelIntent.setPackage(context.getPackageName()); + context.sendBroadcast(cancelIntent); + } +} 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 c1dc5ee2d..a6d90dc7d 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 @@ -46,6 +46,7 @@ import androidx.media.MediaBrowserServiceCompat; import androidx.preference.PreferenceManager; import de.danoeh.antennapod.core.service.QuickSettingsTileService; +import de.danoeh.antennapod.core.util.playback.PlayableUtils; import de.danoeh.antennapod.event.playback.BufferUpdateEvent; import de.danoeh.antennapod.event.playback.PlaybackServiceEvent; import de.danoeh.antennapod.event.PlayerErrorEvent; @@ -82,7 +83,6 @@ import de.danoeh.antennapod.core.util.FeedItemUtil; import de.danoeh.antennapod.core.util.IntentUtils; import de.danoeh.antennapod.core.util.NetworkUtils; import de.danoeh.antennapod.core.util.gui.NotificationUtils; -import de.danoeh.antennapod.core.util.playback.PlayableUtils; import de.danoeh.antennapod.core.util.playback.PlaybackServiceStarter; import de.danoeh.antennapod.core.widget.WidgetUpdater; import de.danoeh.antennapod.model.feed.Feed; @@ -109,54 +109,22 @@ public class PlaybackService extends MediaBrowserServiceCompat { */ private static final String TAG = "PlaybackService"; - public static final String EXTRA_PLAYABLE = "PlaybackService.PlayableExtra"; - public static final String EXTRA_ALLOW_STREAM_THIS_TIME = "extra.de.danoeh.antennapod.core.service.allowStream"; - public static final String EXTRA_ALLOW_STREAM_ALWAYS = "extra.de.danoeh.antennapod.core.service.allowStreamAlways"; - public static final String ACTION_PLAYER_STATUS_CHANGED = "action.de.danoeh.antennapod.core.service.playerStatusChanged"; private static final String AVRCP_ACTION_PLAYER_STATUS_CHANGED = "com.android.music.playstatechanged"; private static final String AVRCP_ACTION_META_CHANGED = "com.android.music.metachanged"; - public static final String ACTION_PLAYER_NOTIFICATION = "action.de.danoeh.antennapod.core.service.playerNotification"; - public static final String EXTRA_NOTIFICATION_CODE = "extra.de.danoeh.antennapod.core.service.notificationCode"; - public static final String EXTRA_NOTIFICATION_TYPE = "extra.de.danoeh.antennapod.core.service.notificationType"; - - /** - * If the PlaybackService receives this action, it will stop playback and - * try to shutdown. - */ - public static final String ACTION_SHUTDOWN_PLAYBACK_SERVICE = "action.de.danoeh.antennapod.core.service.actionShutdownPlaybackService"; - /** * Custom action used by Android Wear, Android Auto */ private static final String CUSTOM_ACTION_FAST_FORWARD = "action.de.danoeh.antennapod.core.service.fastForward"; private static final String CUSTOM_ACTION_REWIND = "action.de.danoeh.antennapod.core.service.rewind"; - - /** - * Used in NOTIFICATION_TYPE_RELOAD. - */ - public static final int EXTRA_CODE_AUDIO = 1; - public static final int EXTRA_CODE_VIDEO = 2; - public static final int EXTRA_CODE_CAST = 3; - - /** - * Receivers of this intent should update their information about the curently playing media - */ - public static final int NOTIFICATION_TYPE_RELOAD = 3; - /** * Set a max number of episodes to load for Android Auto, otherwise there could be performance issues */ public static final int MAX_ANDROID_AUTO_EPISODES_PER_FEED = 100; /** - * No more episodes are going to be played. - */ - public static final int NOTIFICATION_TYPE_PLAYBACK_END = 7; - - /** * Is true if service is running. */ public static boolean isRunning = false; @@ -243,7 +211,7 @@ public class PlaybackService extends MediaBrowserServiceCompat { registerReceiver(autoStateUpdated, new IntentFilter("com.google.android.gms.car.media.STATUS")); registerReceiver(headsetDisconnected, new IntentFilter(Intent.ACTION_HEADSET_PLUG)); - registerReceiver(shutdownReceiver, new IntentFilter(ACTION_SHUTDOWN_PLAYBACK_SERVICE)); + registerReceiver(shutdownReceiver, new IntentFilter(PlaybackServiceInterface.ACTION_SHUTDOWN_PLAYBACK_SERVICE)); registerReceiver(bluetoothStateUpdated, new IntentFilter(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED)); registerReceiver(audioBecomingNoisy, new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY)); EventBus.getDefault().register(this); @@ -478,7 +446,7 @@ public class PlaybackService extends MediaBrowserServiceCompat { final int keycode = intent.getIntExtra(MediaButtonReceiver.EXTRA_KEYCODE, -1); final boolean hardwareButton = intent.getBooleanExtra(MediaButtonReceiver.EXTRA_HARDWAREBUTTON, false); - Playable playable = intent.getParcelableExtra(EXTRA_PLAYABLE); + Playable playable = intent.getParcelableExtra(PlaybackServiceInterface.EXTRA_PLAYABLE); if (keycode == -1 && playable == null) { Log.e(TAG, "PlaybackService was started with no arguments"); stateManager.stopService(); @@ -505,9 +473,11 @@ public class PlaybackService extends MediaBrowserServiceCompat { } } else { stateManager.validStartCommandWasReceived(); - boolean allowStreamThisTime = intent.getBooleanExtra(EXTRA_ALLOW_STREAM_THIS_TIME, false); - boolean allowStreamAlways = intent.getBooleanExtra(EXTRA_ALLOW_STREAM_ALWAYS, false); - sendNotificationBroadcast(NOTIFICATION_TYPE_RELOAD, 0); + boolean allowStreamThisTime = intent.getBooleanExtra( + PlaybackServiceInterface.EXTRA_ALLOW_STREAM_THIS_TIME, false); + boolean allowStreamAlways = intent.getBooleanExtra( + PlaybackServiceInterface.EXTRA_ALLOW_STREAM_ALWAYS, false); + sendNotificationBroadcast(PlaybackServiceInterface.NOTIFICATION_TYPE_RELOAD, 0); if (allowStreamAlways) { UserPreferences.setAllowMobileStreaming(true); } @@ -561,8 +531,8 @@ public class PlaybackService extends MediaBrowserServiceCompat { private void displayStreamingNotAllowedNotification(Intent originalIntent) { Intent intentAllowThisTime = new Intent(originalIntent); - intentAllowThisTime.setAction(EXTRA_ALLOW_STREAM_THIS_TIME); - intentAllowThisTime.putExtra(EXTRA_ALLOW_STREAM_THIS_TIME, true); + intentAllowThisTime.setAction(PlaybackServiceInterface.EXTRA_ALLOW_STREAM_THIS_TIME); + intentAllowThisTime.putExtra(PlaybackServiceInterface.EXTRA_ALLOW_STREAM_THIS_TIME, true); PendingIntent pendingIntentAllowThisTime; if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { pendingIntentAllowThisTime = PendingIntent.getForegroundService(this, @@ -575,8 +545,8 @@ public class PlaybackService extends MediaBrowserServiceCompat { } Intent intentAlwaysAllow = new Intent(intentAllowThisTime); - intentAlwaysAllow.setAction(EXTRA_ALLOW_STREAM_ALWAYS); - intentAlwaysAllow.putExtra(EXTRA_ALLOW_STREAM_ALWAYS, true); + intentAlwaysAllow.setAction(PlaybackServiceInterface.EXTRA_ALLOW_STREAM_ALWAYS); + intentAlwaysAllow.putExtra(PlaybackServiceInterface.EXTRA_ALLOW_STREAM_ALWAYS, true); PendingIntent pendingIntentAlwaysAllow; if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { pendingIntentAlwaysAllow = PendingIntent.getForegroundService(this, @@ -702,7 +672,7 @@ public class PlaybackService extends MediaBrowserServiceCompat { } private void startPlayingFromPreferences() { - Observable.fromCallable(() -> PlayableUtils.createInstanceFromPreferences(getApplicationContext())) + Observable.fromCallable(() -> PlaybackPreferences.createInstanceFromPreferences(getApplicationContext())) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( @@ -766,7 +736,7 @@ public class PlaybackService extends MediaBrowserServiceCompat { @Override public void onChapterLoaded(Playable media) { - sendNotificationBroadcast(NOTIFICATION_TYPE_RELOAD, 0); + sendNotificationBroadcast(PlaybackServiceInterface.NOTIFICATION_TYPE_RELOAD, 0); } }; @@ -848,7 +818,7 @@ public class PlaybackService extends MediaBrowserServiceCompat { public void onMediaChanged(boolean reloadUI) { Log.d(TAG, "reloadUI callback reached"); if (reloadUI) { - sendNotificationBroadcast(NOTIFICATION_TYPE_RELOAD, 0); + sendNotificationBroadcast(PlaybackServiceInterface.NOTIFICATION_TYPE_RELOAD, 0); } updateNotificationAndMediaSession(getPlayable()); } @@ -1011,11 +981,12 @@ public class PlaybackService extends MediaBrowserServiceCompat { } } if (mediaType == null) { - sendNotificationBroadcast(NOTIFICATION_TYPE_PLAYBACK_END, 0); + sendNotificationBroadcast(PlaybackServiceInterface.NOTIFICATION_TYPE_PLAYBACK_END, 0); } else { - sendNotificationBroadcast(NOTIFICATION_TYPE_RELOAD, - isCasting ? EXTRA_CODE_CAST : - (mediaType == MediaType.VIDEO) ? EXTRA_CODE_VIDEO : EXTRA_CODE_AUDIO); + sendNotificationBroadcast(PlaybackServiceInterface.NOTIFICATION_TYPE_RELOAD, + isCasting ? PlaybackServiceInterface.EXTRA_CODE_CAST : + (mediaType == MediaType.VIDEO) ? PlaybackServiceInterface.EXTRA_CODE_VIDEO : + PlaybackServiceInterface.EXTRA_CODE_AUDIO); } } @@ -1114,9 +1085,9 @@ public class PlaybackService extends MediaBrowserServiceCompat { } private void sendNotificationBroadcast(int type, int code) { - Intent intent = new Intent(ACTION_PLAYER_NOTIFICATION); - intent.putExtra(EXTRA_NOTIFICATION_TYPE, type); - intent.putExtra(EXTRA_NOTIFICATION_CODE, code); + Intent intent = new Intent(PlaybackServiceInterface.ACTION_PLAYER_NOTIFICATION); + intent.putExtra(PlaybackServiceInterface.EXTRA_NOTIFICATION_TYPE, type); + intent.putExtra(PlaybackServiceInterface.EXTRA_NOTIFICATION_CODE, code); intent.setPackage(getPackageName()); sendBroadcast(intent); } @@ -1521,7 +1492,7 @@ public class PlaybackService extends MediaBrowserServiceCompat { @Override public void onReceive(Context context, Intent intent) { - if (TextUtils.equals(intent.getAction(), ACTION_SHUTDOWN_PLAYBACK_SERVICE)) { + if (TextUtils.equals(intent.getAction(), PlaybackServiceInterface.ACTION_SHUTDOWN_PLAYBACK_SERVICE)) { EventBus.getDefault().post(new PlaybackServiceEvent(PlaybackServiceEvent.Action.SERVICE_SHUT_DOWN)); stateManager.stopService(); } diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceInterface.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceInterface.java new file mode 100644 index 000000000..18ead601d --- /dev/null +++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceInterface.java @@ -0,0 +1,20 @@ +package de.danoeh.antennapod.core.service.playback; + +public abstract class PlaybackServiceInterface { + public static final String EXTRA_PLAYABLE = "PlaybackService.PlayableExtra"; + public static final String EXTRA_ALLOW_STREAM_THIS_TIME = "extra.de.danoeh.antennapod.core.service.allowStream"; + public static final String EXTRA_ALLOW_STREAM_ALWAYS = "extra.de.danoeh.antennapod.core.service.allowStreamAlways"; + + public static final String ACTION_PLAYER_NOTIFICATION + = "action.de.danoeh.antennapod.core.service.playerNotification"; + public static final String EXTRA_NOTIFICATION_CODE = "extra.de.danoeh.antennapod.core.service.notificationCode"; + public static final String EXTRA_NOTIFICATION_TYPE = "extra.de.danoeh.antennapod.core.service.notificationType"; + public static final int NOTIFICATION_TYPE_PLAYBACK_END = 7; + public static final int NOTIFICATION_TYPE_RELOAD = 3; + public static final int EXTRA_CODE_AUDIO = 1; // Used in NOTIFICATION_TYPE_RELOAD + public static final int EXTRA_CODE_VIDEO = 2; + public static final int EXTRA_CODE_CAST = 3; + + public static final String ACTION_SHUTDOWN_PLAYBACK_SERVICE + = "action.de.danoeh.antennapod.core.service.actionShutdownPlaybackService"; +} diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/AutomaticDownloadAlgorithm.java b/core/src/main/java/de/danoeh/antennapod/core/storage/AutomaticDownloadAlgorithm.java index c410376c2..4d8a0c25e 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/AutomaticDownloadAlgorithm.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/AutomaticDownloadAlgorithm.java @@ -9,11 +9,11 @@ import java.util.List; import de.danoeh.antennapod.core.service.download.DownloadRequest; import de.danoeh.antennapod.core.service.download.DownloadRequestCreator; -import de.danoeh.antennapod.core.service.download.DownloadService; +import de.danoeh.antennapod.core.service.download.DownloadServiceInterface; +import de.danoeh.antennapod.core.util.PlaybackStatus; import de.danoeh.antennapod.model.feed.FeedItem; import de.danoeh.antennapod.model.feed.FeedPreferences; import de.danoeh.antennapod.core.preferences.UserPreferences; -import de.danoeh.antennapod.core.util.FeedItemUtil; import de.danoeh.antennapod.core.util.NetworkUtils; import de.danoeh.antennapod.core.util.PowerUtils; @@ -68,7 +68,8 @@ public class AutomaticDownloadAlgorithm { Iterator<FeedItem> it = candidates.iterator(); while (it.hasNext()) { FeedItem item = it.next(); - if (!item.isAutoDownloadable(System.currentTimeMillis()) || FeedItemUtil.isPlaying(item.getMedia()) + if (!item.isAutoDownloadable(System.currentTimeMillis()) + || PlaybackStatus.isPlaying(item.getMedia()) || item.getFeed().isLocalFeed()) { it.remove(); } @@ -99,7 +100,7 @@ public class AutomaticDownloadAlgorithm { request.withInitiatedByUser(false); requests.add(request.build()); } - DownloadService.download(context, false, requests.toArray(new DownloadRequest[0])); + DownloadServiceInterface.get().download(context, false, requests.toArray(new DownloadRequest[0])); } } }; diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java index e9fe3af1a..13a18b0d7 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java @@ -12,7 +12,7 @@ import androidx.annotation.VisibleForTesting; import de.danoeh.antennapod.core.service.download.DownloadRequest; import de.danoeh.antennapod.core.service.download.DownloadRequestCreator; -import de.danoeh.antennapod.core.service.download.DownloadService; +import de.danoeh.antennapod.core.service.download.DownloadServiceInterface; import de.danoeh.antennapod.storage.database.PodDBAdapter; import de.danoeh.antennapod.storage.database.mapper.FeedCursorMapper; import org.greenrobot.eventbus.EventBus; @@ -35,7 +35,6 @@ import de.danoeh.antennapod.event.FeedListUpdateEvent; import de.danoeh.antennapod.event.MessageEvent; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.model.download.DownloadStatus; -import de.danoeh.antennapod.core.sync.SyncService; import de.danoeh.antennapod.core.sync.queue.SynchronizationQueueSink; import de.danoeh.antennapod.model.download.DownloadError; import de.danoeh.antennapod.core.util.LongList; @@ -114,12 +113,12 @@ public final class DBTasks { * @param initiatedByUser a boolean indicating if the refresh was triggered by user action. */ public static void refreshAllFeeds(final Context context, boolean initiatedByUser) { - DownloadService.refreshAllFeeds(context, initiatedByUser); + DownloadServiceInterface.get().refreshAllFeeds(context, initiatedByUser); SharedPreferences prefs = context.getSharedPreferences(PREF_NAME, MODE_PRIVATE); prefs.edit().putLong(PREF_LAST_REFRESH, System.currentTimeMillis()).apply(); - SyncService.sync(context); + SynchronizationQueueSink.syncNow(); // Note: automatic download of episodes will be done but not here. // Instead it is done after all feeds have been refreshed (asynchronously), // in DownloadService.onDestroy() @@ -146,7 +145,7 @@ public final class DBTasks { DownloadRequest.Builder builder = DownloadRequestCreator.create(nextFeed); builder.loadAllPages(loadAllPages); - DownloadService.download(context, false, builder.build()); + DownloadServiceInterface.get().download(context, false, builder.build()); } else { Log.e(TAG, "loadNextPageOfFeed: Feed was either not paged or contained no nextPageLink"); } @@ -165,7 +164,7 @@ public final class DBTasks { builder.withInitiatedByUser(initiatedByUser); builder.setForce(true); builder.loadAllPages(loadAllPages); - DownloadService.download(context, false, builder.build()); + DownloadServiceInterface.get().download(context, false, builder.build()); } /** 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 ff8c990db..fbcb86586 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 @@ -8,7 +8,8 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.app.NotificationManagerCompat; -import de.danoeh.antennapod.core.service.download.DownloadService; +import de.danoeh.antennapod.core.service.download.DownloadServiceInterface; +import de.danoeh.antennapod.core.service.playback.PlaybackServiceInterface; import de.danoeh.antennapod.storage.database.PodDBAdapter; import org.greenrobot.eventbus.EventBus; @@ -36,13 +37,11 @@ import de.danoeh.antennapod.core.feed.FeedEvent; import de.danoeh.antennapod.core.preferences.PlaybackPreferences; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.model.download.DownloadStatus; -import de.danoeh.antennapod.core.service.playback.PlaybackService; import de.danoeh.antennapod.core.sync.queue.SynchronizationQueueSink; import de.danoeh.antennapod.core.util.FeedItemPermutors; import de.danoeh.antennapod.core.util.IntentUtils; import de.danoeh.antennapod.core.util.LongList; import de.danoeh.antennapod.core.util.Permutor; -import de.danoeh.antennapod.core.util.playback.PlayableUtils; import de.danoeh.antennapod.model.feed.Feed; import de.danoeh.antennapod.model.feed.FeedItem; import de.danoeh.antennapod.model.feed.FeedMedia; @@ -129,7 +128,7 @@ public class DBWriter { if (media.getId() == PlaybackPreferences.getCurrentlyPlayingFeedMediaId()) { PlaybackPreferences.writeNoMediaPlaying(); - IntentUtils.sendLocalBroadcast(context, PlaybackService.ACTION_SHUTDOWN_PLAYBACK_SERVICE); + IntentUtils.sendLocalBroadcast(context, PlaybackServiceInterface.ACTION_SHUTDOWN_PLAYBACK_SERVICE); NotificationManagerCompat nm = NotificationManagerCompat.from(context); nm.cancel(R.id.notification_playing); @@ -202,12 +201,12 @@ public class DBWriter { if (item.getMedia().getId() == PlaybackPreferences.getCurrentlyPlayingFeedMediaId()) { // Applies to both downloaded and streamed media PlaybackPreferences.writeNoMediaPlaying(); - IntentUtils.sendLocalBroadcast(context, PlaybackService.ACTION_SHUTDOWN_PLAYBACK_SERVICE); + IntentUtils.sendLocalBroadcast(context, PlaybackServiceInterface.ACTION_SHUTDOWN_PLAYBACK_SERVICE); } if (item.getMedia().isDownloaded()) { deleteFeedMediaSynchronous(context, item.getMedia()); } - DownloadService.cancel(context, item.getMedia().getDownload_url()); + DownloadServiceInterface.get().cancel(context, item.getMedia().getDownload_url()); } } @@ -391,7 +390,7 @@ public class DBWriter { List<FeedItem> updatedItems = new ArrayList<>(); ItemEnqueuePositionCalculator positionCalculator = new ItemEnqueuePositionCalculator(UserPreferences.getEnqueueLocation()); - Playable currentlyPlaying = PlayableUtils.createInstanceFromPreferences(context); + Playable currentlyPlaying = PlaybackPreferences.createInstanceFromPreferences(context); int insertPosition = positionCalculator.calcPosition(queue, currentlyPlaying); for (long itemId : itemIds) { if (!itemListContains(queue, itemId)) { diff --git a/core/src/main/java/de/danoeh/antennapod/core/sync/SyncService.java b/core/src/main/java/de/danoeh/antennapod/core/sync/SyncService.java index 0640f7577..c304d67d2 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/sync/SyncService.java +++ b/core/src/main/java/de/danoeh/antennapod/core/sync/SyncService.java @@ -23,6 +23,7 @@ import androidx.work.WorkerParameters; import de.danoeh.antennapod.core.service.download.DownloadRequest; import de.danoeh.antennapod.core.service.download.DownloadService; import de.danoeh.antennapod.core.service.download.DownloadRequestCreator; +import de.danoeh.antennapod.core.service.download.DownloadServiceInterface; import org.apache.commons.lang3.StringUtils; import org.greenrobot.eventbus.EventBus; @@ -152,7 +153,7 @@ public class SyncService extends Worker { if (!URLChecker.containsUrl(localSubscriptions, downloadUrl) && !queuedRemovedFeeds.contains(downloadUrl)) { Feed feed = new Feed(downloadUrl, null); DownloadRequest.Builder builder = DownloadRequestCreator.create(feed); - DownloadService.download(getApplicationContext(), false, builder.build()); + DownloadServiceInterface.get().download(getApplicationContext(), false, builder.build()); } } diff --git a/core/src/main/java/de/danoeh/antennapod/core/sync/queue/SynchronizationQueueSink.java b/core/src/main/java/de/danoeh/antennapod/core/sync/queue/SynchronizationQueueSink.java index 445faf60f..55c415153 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/sync/queue/SynchronizationQueueSink.java +++ b/core/src/main/java/de/danoeh/antennapod/core/sync/queue/SynchronizationQueueSink.java @@ -3,12 +3,21 @@ package de.danoeh.antennapod.core.sync.queue; import android.content.Context; import de.danoeh.antennapod.core.sync.LockingAsyncExecutor; -import de.danoeh.antennapod.core.sync.SyncService; import de.danoeh.antennapod.core.sync.SynchronizationSettings; import de.danoeh.antennapod.model.feed.FeedMedia; import de.danoeh.antennapod.net.sync.model.EpisodeAction; public class SynchronizationQueueSink { + // To avoid a dependency loop of every class to SyncService, and from SyncService back to every class. + private static Runnable serviceStarterImpl = () -> { }; + + public static void setServiceStarterImpl(Runnable serviceStarter) { + serviceStarterImpl = serviceStarter; + } + + public static void syncNow() { + serviceStarterImpl.run(); + } public static void clearQueue(Context context) { LockingAsyncExecutor.executeLockedAsync(new SynchronizationQueueStorage(context)::clearQueue); @@ -20,7 +29,7 @@ public class SynchronizationQueueSink { } LockingAsyncExecutor.executeLockedAsync(() -> { new SynchronizationQueueStorage(context).enqueueFeedAdded(downloadUrl); - SyncService.sync(context); + syncNow(); }); } @@ -30,7 +39,7 @@ public class SynchronizationQueueSink { } LockingAsyncExecutor.executeLockedAsync(() -> { new SynchronizationQueueStorage(context).enqueueFeedRemoved(downloadUrl); - SyncService.sync(context); + syncNow(); }); } @@ -40,7 +49,7 @@ public class SynchronizationQueueSink { } LockingAsyncExecutor.executeLockedAsync(() -> { new SynchronizationQueueStorage(context).enqueueEpisodeAction(action); - SyncService.sync(context); + syncNow(); }); } diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/FeedItemUtil.java b/core/src/main/java/de/danoeh/antennapod/core/util/FeedItemUtil.java index 82c132dc1..93d354096 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/FeedItemUtil.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/FeedItemUtil.java @@ -4,8 +4,6 @@ import androidx.annotation.NonNull; import de.danoeh.antennapod.model.feed.FeedMedia; import de.danoeh.antennapod.core.preferences.UserPreferences; -import de.danoeh.antennapod.core.preferences.PlaybackPreferences; -import de.danoeh.antennapod.core.service.playback.PlaybackService; import org.apache.commons.lang3.StringUtils; import java.util.ArrayList; @@ -75,19 +73,4 @@ public class FeedItemUtil { int smartMarkAsPlayedSecs = UserPreferences.getSmartMarkAsPlayedSecs(); return media.getDuration() > 0 && media.getPosition() >= media.getDuration() - smartMarkAsPlayedSecs * 1000; } - - /** - * Reads playback preferences to determine whether this FeedMedia object is - * currently being played and the current player status is playing. - */ - public static boolean isCurrentlyPlaying(FeedMedia media) { - return isPlaying(media) && PlaybackService.isRunning - && ((PlaybackPreferences.getCurrentPlayerStatus() == PlaybackPreferences.PLAYER_STATUS_PLAYING)); - } - - public static boolean isPlaying(FeedMedia media) { - return PlaybackPreferences.getCurrentlyPlayingMediaType() == FeedMedia.PLAYABLE_TYPE_FEEDMEDIA - && media != null - && PlaybackPreferences.getCurrentlyPlayingFeedMediaId() == media.getId(); - } } 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 85be3c787..c5524d7b5 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 @@ -7,37 +7,18 @@ import android.net.NetworkCapabilities; import android.net.NetworkInfo; import android.net.wifi.WifiManager; import android.os.Build; -import android.text.TextUtils; -import android.util.Log; - -import java.io.File; -import java.io.IOException; import java.util.Arrays; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; -import de.danoeh.antennapod.core.service.download.DownloadService; -import de.danoeh.antennapod.core.storage.DBTasks; -import de.danoeh.antennapod.model.feed.FeedMedia; import de.danoeh.antennapod.core.preferences.UserPreferences; -import de.danoeh.antennapod.core.service.download.AntennapodHttpClient; -import de.danoeh.antennapod.core.storage.DBWriter; -import io.reactivex.Single; -import io.reactivex.SingleOnSubscribe; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.schedulers.Schedulers; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; public class NetworkUtils { private static final String REGEX_PATTERN_IP_ADDRESS = "([0-9]{1,3}[\\.]){3}[0-9]{1,3}"; private NetworkUtils(){} - private static final String TAG = NetworkUtils.class.getSimpleName(); - private static Context context; public static void init(Context context) { @@ -160,73 +141,4 @@ public class NetworkUtils { } return false; } - - public static Single<Long> getFeedMediaSizeObservable(FeedMedia media) { - return Single.create((SingleOnSubscribe<Long>) emitter -> { - if (!NetworkUtils.isEpisodeHeadDownloadAllowed()) { - emitter.onSuccess(0L); - return; - } - long size = Integer.MIN_VALUE; - if (media.isDownloaded()) { - File mediaFile = new File(media.getLocalMediaUrl()); - if (mediaFile.exists()) { - size = mediaFile.length(); - } - } else if (!media.checkedOnSizeButUnknown()) { - // only query the network if we haven't already checked - - String url = media.getDownload_url(); - if(TextUtils.isEmpty(url)) { - emitter.onSuccess(0L); - return; - } - - OkHttpClient client = AntennapodHttpClient.getHttpClient(); - Request.Builder httpReq = new Request.Builder() - .url(url) - .header("Accept-Encoding", "identity") - .head(); - try { - Response response = client.newCall(httpReq.build()).execute(); - if (response.isSuccessful()) { - String contentLength = response.header("Content-Length"); - try { - size = Integer.parseInt(contentLength); - } catch (NumberFormatException e) { - Log.e(TAG, Log.getStackTraceString(e)); - } - } - } catch (IOException e) { - emitter.onSuccess(0L); - Log.e(TAG, Log.getStackTraceString(e)); - return; // better luck next time - } - } - Log.d(TAG, "new size: " + size); - if (size <= 0) { - // they didn't tell us the size, but we don't want to keep querying on it - media.setCheckedOnSizeButUnknown(); - } else { - media.setSize(size); - } - emitter.onSuccess(size); - DBWriter.setFeedMedia(media); - }) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()); - } - - public static void networkChangedDetected() { - if (NetworkUtils.isAutoDownloadAllowed()) { - Log.d(TAG, "auto-dl network available, starting auto-download"); - DBTasks.autodownloadUndownloadedItems(context); - } else { // if new network is Wi-Fi, finish ongoing downloads, - // otherwise cancel all downloads - if (NetworkUtils.isNetworkRestricted()) { - Log.i(TAG, "Device is no longer connected to Wi-Fi. Cancelling ongoing downloads"); - DownloadService.cancelAll(context); - } - } - } } diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/PlaybackStatus.java b/core/src/main/java/de/danoeh/antennapod/core/util/PlaybackStatus.java new file mode 100644 index 000000000..3c07d325b --- /dev/null +++ b/core/src/main/java/de/danoeh/antennapod/core/util/PlaybackStatus.java @@ -0,0 +1,22 @@ +package de.danoeh.antennapod.core.util; + +import de.danoeh.antennapod.core.preferences.PlaybackPreferences; +import de.danoeh.antennapod.core.service.playback.PlaybackService; +import de.danoeh.antennapod.model.feed.FeedMedia; + +public abstract class PlaybackStatus { + /** + * Reads playback preferences to determine whether this FeedMedia object is + * currently being played and the current player status is playing. + */ + public static boolean isCurrentlyPlaying(FeedMedia media) { + return isPlaying(media) && PlaybackService.isRunning + && ((PlaybackPreferences.getCurrentPlayerStatus() == PlaybackPreferences.PLAYER_STATUS_PLAYING)); + } + + public static boolean isPlaying(FeedMedia media) { + return PlaybackPreferences.getCurrentlyPlayingMediaType() == FeedMedia.PLAYABLE_TYPE_FEEDMEDIA + && media != null + && PlaybackPreferences.getCurrentlyPlayingFeedMediaId() == media.getId(); + } +} diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/download/ConnectionStateMonitor.java b/core/src/main/java/de/danoeh/antennapod/core/util/download/ConnectionStateMonitor.java index 5c543bf5a..e88752e4a 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/download/ConnectionStateMonitor.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/download/ConnectionStateMonitor.java @@ -8,7 +8,6 @@ import android.os.Build; import android.util.Log; import androidx.annotation.RequiresApi; -import de.danoeh.antennapod.core.util.NetworkUtils; @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) public class ConnectionStateMonitor @@ -27,7 +26,7 @@ public class ConnectionStateMonitor @Override public void onNetworkActive() { Log.d(TAG, "ConnectionStateMonitor::onNetworkActive network connection changed"); - NetworkUtils.networkChangedDetected(); + NetworkConnectionChangeHandler.networkChangedDetected(); } public void enable(Context context) { diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/download/MediaSizeLoader.java b/core/src/main/java/de/danoeh/antennapod/core/util/download/MediaSizeLoader.java new file mode 100644 index 000000000..67ec35327 --- /dev/null +++ b/core/src/main/java/de/danoeh/antennapod/core/util/download/MediaSizeLoader.java @@ -0,0 +1,78 @@ +package de.danoeh.antennapod.core.util.download; + +import android.text.TextUtils; +import de.danoeh.antennapod.core.service.download.AntennapodHttpClient; +import de.danoeh.antennapod.core.storage.DBWriter; +import de.danoeh.antennapod.core.util.NetworkUtils; +import de.danoeh.antennapod.model.feed.FeedMedia; +import io.reactivex.Single; +import io.reactivex.SingleOnSubscribe; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.schedulers.Schedulers; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import android.util.Log; +import okhttp3.Response; + +import java.io.File; +import java.io.IOException; + +public abstract class MediaSizeLoader { + private static final String TAG = "MediaSizeLoader"; + + public static Single<Long> getFeedMediaSizeObservable(FeedMedia media) { + return Single.create((SingleOnSubscribe<Long>) emitter -> { + if (!NetworkUtils.isEpisodeHeadDownloadAllowed()) { + emitter.onSuccess(0L); + return; + } + long size = Integer.MIN_VALUE; + if (media.isDownloaded()) { + File mediaFile = new File(media.getLocalMediaUrl()); + if (mediaFile.exists()) { + size = mediaFile.length(); + } + } else if (!media.checkedOnSizeButUnknown()) { + // only query the network if we haven't already checked + + String url = media.getDownload_url(); + if (TextUtils.isEmpty(url)) { + emitter.onSuccess(0L); + return; + } + + OkHttpClient client = AntennapodHttpClient.getHttpClient(); + Request.Builder httpReq = new Request.Builder() + .url(url) + .header("Accept-Encoding", "identity") + .head(); + try { + Response response = client.newCall(httpReq.build()).execute(); + if (response.isSuccessful()) { + String contentLength = response.header("Content-Length"); + try { + size = Integer.parseInt(contentLength); + } catch (NumberFormatException e) { + Log.e(TAG, Log.getStackTraceString(e)); + } + } + } catch (IOException e) { + emitter.onSuccess(0L); + Log.e(TAG, Log.getStackTraceString(e)); + return; // better luck next time + } + } + Log.d(TAG, "new size: " + size); + if (size <= 0) { + // they didn't tell us the size, but we don't want to keep querying on it + media.setCheckedOnSizeButUnknown(); + } else { + media.setSize(size); + } + emitter.onSuccess(size); + DBWriter.setFeedMedia(media); + }) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()); + } +} diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/download/NetworkConnectionChangeHandler.java b/core/src/main/java/de/danoeh/antennapod/core/util/download/NetworkConnectionChangeHandler.java new file mode 100644 index 000000000..0eca498c9 --- /dev/null +++ b/core/src/main/java/de/danoeh/antennapod/core/util/download/NetworkConnectionChangeHandler.java @@ -0,0 +1,29 @@ +package de.danoeh.antennapod.core.util.download; + +import android.content.Context; +import com.google.android.exoplayer2.util.Log; +import de.danoeh.antennapod.core.service.download.DownloadServiceInterface; +import de.danoeh.antennapod.core.storage.DBTasks; +import de.danoeh.antennapod.core.util.NetworkUtils; + +public abstract class NetworkConnectionChangeHandler { + private static final String TAG = "NetworkConnectionChangeHandler"; + private static Context context; + + public static void init(Context context) { + NetworkConnectionChangeHandler.context = context; + } + + public static void networkChangedDetected() { + if (NetworkUtils.isAutoDownloadAllowed()) { + Log.d(TAG, "auto-dl network available, starting auto-download"); + DBTasks.autodownloadUndownloadedItems(context); + } else { // if new network is Wi-Fi, finish ongoing downloads, + // otherwise cancel all downloads + if (NetworkUtils.isNetworkRestricted()) { + Log.i(TAG, "Device is no longer connected to Wi-Fi. Cancelling ongoing downloads"); + DownloadServiceInterface.get().cancelAll(context); + } + } + } +} diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlayableUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlayableUtils.java index 56b46903d..90751cbdb 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlayableUtils.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlayableUtils.java @@ -1,79 +1,14 @@ package de.danoeh.antennapod.core.util.playback; -import android.content.Context; -import android.content.SharedPreferences; -import android.util.Log; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.preference.PreferenceManager; - +import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.model.feed.FeedItem; import de.danoeh.antennapod.model.feed.FeedMedia; -import de.danoeh.antennapod.core.preferences.PlaybackPreferences; -import de.danoeh.antennapod.core.storage.DBReader; -import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.model.playback.Playable; /** * Provides utility methods for Playable objects. */ public abstract class PlayableUtils { - - private static final String TAG = "PlayableUtils"; - - /** - * Restores a playable object from a sharedPreferences file. This method might load data from the database, - * depending on the type of playable that was restored. - * - * @return The restored Playable object - */ - @Nullable - public static Playable createInstanceFromPreferences(@NonNull Context context) { - long currentlyPlayingMedia = PlaybackPreferences.getCurrentlyPlayingMediaType(); - if (currentlyPlayingMedia != PlaybackPreferences.NO_MEDIA_PLAYING) { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext()); - return PlayableUtils.createInstanceFromPreferences((int) currentlyPlayingMedia, prefs); - } - return null; - } - - /** - * Restores a playable object from a sharedPreferences file. This method might load data from the database, - * depending on the type of playable that was restored. - * - * @param type An integer that represents the type of the Playable object - * that is restored. - * @param pref The SharedPreferences file from which the Playable object - * is restored - * @return The restored Playable object - */ - private static Playable createInstanceFromPreferences(int type, SharedPreferences pref) { - Playable result; - // ADD new Playable types here: - switch (type) { - case FeedMedia.PLAYABLE_TYPE_FEEDMEDIA: - result = createFeedMediaInstance(pref); - break; - default: - result = null; - break; - } - if (result == null) { - Log.e(TAG, "Could not restore Playable object from preferences"); - } - return result; - } - - private static Playable createFeedMediaInstance(SharedPreferences pref) { - Playable result = null; - long mediaId = pref.getLong(FeedMedia.PREF_MEDIA_ID, -1); - if (mediaId != -1) { - result = DBReader.getFeedMedia(mediaId); - } - return result; - } - /** * Saves the current position of this object. * 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 80795a7cd..a08694e66 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java @@ -12,6 +12,7 @@ import android.util.Log; import android.util.Pair; import android.view.SurfaceHolder; import androidx.annotation.NonNull; +import de.danoeh.antennapod.core.service.playback.PlaybackServiceInterface; import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.event.playback.PlaybackPositionEvent; import de.danoeh.antennapod.model.feed.FeedMedia; @@ -84,10 +85,9 @@ public abstract class PlaybackController { initialized = true; activity.registerReceiver(statusUpdate, new IntentFilter( - PlaybackService.ACTION_PLAYER_STATUS_CHANGED)); - + PlaybackService.ACTION_PLAYER_STATUS_CHANGED)); activity.registerReceiver(notificationReceiver, new IntentFilter( - PlaybackService.ACTION_PLAYER_NOTIFICATION)); + PlaybackServiceInterface.ACTION_PLAYER_NOTIFICATION)); if (!released) { bindToService(); @@ -202,14 +202,14 @@ public abstract class PlaybackController { @Override public void onReceive(Context context, Intent intent) { - int type = intent.getIntExtra(PlaybackService.EXTRA_NOTIFICATION_TYPE, -1); - int code = intent.getIntExtra(PlaybackService.EXTRA_NOTIFICATION_CODE, -1); + int type = intent.getIntExtra(PlaybackServiceInterface.EXTRA_NOTIFICATION_TYPE, -1); + int code = intent.getIntExtra(PlaybackServiceInterface.EXTRA_NOTIFICATION_CODE, -1); if (code == -1 || type == -1) { Log.d(TAG, "Bad arguments. Won't handle intent"); return; } switch (type) { - case PlaybackService.NOTIFICATION_TYPE_RELOAD: + case PlaybackServiceInterface.NOTIFICATION_TYPE_RELOAD: if (playbackService == null && PlaybackService.isRunning) { bindToService(); return; @@ -217,7 +217,7 @@ public abstract class PlaybackController { mediaInfoLoaded = false; queryService(); break; - case PlaybackService.NOTIFICATION_TYPE_PLAYBACK_END: + case PlaybackServiceInterface.NOTIFICATION_TYPE_PLAYBACK_END: onPlaybackEnd(); break; } @@ -341,7 +341,7 @@ public abstract class PlaybackController { public Playable getMedia() { if (media == null) { - media = PlayableUtils.createInstanceFromPreferences(activity); + media = PlaybackPreferences.createInstanceFromPreferences(activity); } return media; } 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 62d981b55..3b20e3d25 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 @@ -6,6 +6,7 @@ import android.os.Parcelable; import androidx.core.content.ContextCompat; import de.danoeh.antennapod.core.service.playback.PlaybackService; +import de.danoeh.antennapod.core.service.playback.PlaybackServiceInterface; import de.danoeh.antennapod.model.playback.Playable; public class PlaybackServiceStarter { @@ -34,8 +35,8 @@ public class PlaybackServiceStarter { public Intent getIntent() { Intent launchIntent = new Intent(context, PlaybackService.class); - launchIntent.putExtra(PlaybackService.EXTRA_PLAYABLE, (Parcelable) media); - launchIntent.putExtra(PlaybackService.EXTRA_ALLOW_STREAM_THIS_TIME, shouldStreamThisTime); + launchIntent.putExtra(PlaybackServiceInterface.EXTRA_PLAYABLE, (Parcelable) media); + launchIntent.putExtra(PlaybackServiceInterface.EXTRA_ALLOW_STREAM_THIS_TIME, shouldStreamThisTime); return launchIntent; } diff --git a/core/src/main/java/de/danoeh/antennapod/core/widget/WidgetUpdaterWorker.java b/core/src/main/java/de/danoeh/antennapod/core/widget/WidgetUpdaterWorker.java index 5bf924ad9..11a573e1f 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/widget/WidgetUpdaterWorker.java +++ b/core/src/main/java/de/danoeh/antennapod/core/widget/WidgetUpdaterWorker.java @@ -9,7 +9,7 @@ import androidx.work.WorkManager; import androidx.work.Worker; import androidx.work.WorkerParameters; import de.danoeh.antennapod.core.feed.util.PlaybackSpeedUtils; -import de.danoeh.antennapod.core.util.playback.PlayableUtils; +import de.danoeh.antennapod.core.preferences.PlaybackPreferences; import de.danoeh.antennapod.model.playback.Playable; import de.danoeh.antennapod.playback.base.PlayerStatus; @@ -43,7 +43,7 @@ public class WidgetUpdaterWorker extends Worker { * Loads the current media from the database and updates the widget in a background job. */ private void updateWidget() { - final Playable media = PlayableUtils.createInstanceFromPreferences(getApplicationContext()); + final Playable media = PlaybackPreferences.createInstanceFromPreferences(getApplicationContext()); if (media != null) { WidgetUpdater.updateWidget(getApplicationContext(), new WidgetUpdater.WidgetState(media, PlayerStatus.STOPPED, diff --git a/core/src/test/java/de/danoeh/antennapod/core/feed/LocalFeedUpdaterTest.java b/core/src/test/java/de/danoeh/antennapod/core/feed/LocalFeedUpdaterTest.java index 4cf746ba4..095bed687 100644 --- a/core/src/test/java/de/danoeh/antennapod/core/feed/LocalFeedUpdaterTest.java +++ b/core/src/test/java/de/danoeh/antennapod/core/feed/LocalFeedUpdaterTest.java @@ -10,6 +10,8 @@ import androidx.annotation.NonNull; import androidx.test.platform.app.InstrumentationRegistry; import de.danoeh.antennapod.core.preferences.PlaybackPreferences; +import de.danoeh.antennapod.core.service.download.DownloadServiceInterface; +import de.danoeh.antennapod.core.service.download.DownloadServiceInterfaceStub; import de.danoeh.antennapod.core.util.FastDocumentFile; import de.danoeh.antennapod.model.feed.Feed; import de.danoeh.antennapod.model.feed.FeedItem; @@ -75,6 +77,7 @@ public class LocalFeedUpdaterTest { Application app = (Application) context; ClientConfig.applicationCallbacks = mock(ApplicationCallbacks.class); when(ClientConfig.applicationCallbacks.getApplicationInstance()).thenReturn(app); + DownloadServiceInterface.setImpl(new DownloadServiceInterfaceStub()); // Initialize database PodDBAdapter.init(context); diff --git a/core/src/test/java/de/danoeh/antennapod/core/service/download/DownloadServiceInterfaceStub.java b/core/src/test/java/de/danoeh/antennapod/core/service/download/DownloadServiceInterfaceStub.java new file mode 100644 index 000000000..952936f70 --- /dev/null +++ b/core/src/test/java/de/danoeh/antennapod/core/service/download/DownloadServiceInterfaceStub.java @@ -0,0 +1,18 @@ +package de.danoeh.antennapod.core.service.download; + +import android.content.Context; + +public class DownloadServiceInterfaceStub extends DownloadServiceInterface { + + public void download(Context context, boolean cleanupMedia, DownloadRequest... requests) { + } + + public void refreshAllFeeds(Context context, boolean initiatedByUser) { + } + + public void cancel(Context context, String url) { + } + + public void cancelAll(Context context) { + } +} diff --git a/core/src/test/java/de/danoeh/antennapod/core/storage/DbWriterTest.java b/core/src/test/java/de/danoeh/antennapod/core/storage/DbWriterTest.java index 533b517d5..ba8877da6 100644 --- a/core/src/test/java/de/danoeh/antennapod/core/storage/DbWriterTest.java +++ b/core/src/test/java/de/danoeh/antennapod/core/storage/DbWriterTest.java @@ -10,6 +10,8 @@ import androidx.core.util.Consumer; import androidx.preference.PreferenceManager; import androidx.test.platform.app.InstrumentationRegistry; +import de.danoeh.antennapod.core.service.download.DownloadServiceInterface; +import de.danoeh.antennapod.core.service.download.DownloadServiceInterfaceStub; import de.danoeh.antennapod.storage.database.PodDBAdapter; import org.awaitility.Awaitility; import org.junit.After; @@ -65,6 +67,7 @@ public class DbWriterTest { Application app = (Application) context; ClientConfig.applicationCallbacks = mock(ApplicationCallbacks.class); when(ClientConfig.applicationCallbacks.getApplicationInstance()).thenReturn(app); + DownloadServiceInterface.setImpl(new DownloadServiceInterfaceStub()); // create new database PodDBAdapter.init(context); |