summaryrefslogtreecommitdiff
path: root/core/src/main/java/de
diff options
context:
space:
mode:
authorByteHamster <ByteHamster@users.noreply.github.com>2021-11-28 22:19:14 +0100
committerGitHub <noreply@github.com>2021-11-28 22:19:14 +0100
commitf0100e61ac633516082ea112363132c99f7c0b7a (patch)
treef7598c0cee85780b409ab895a8041d1607eec312 /core/src/main/java/de
parentaf2835c59dcb0473aba7a48b38f5abe28dca34d3 (diff)
downloadAntennaPod-f0100e61ac633516082ea112363132c99f7c0b7a.zip
Chromecast rework (#5518)
Diffstat (limited to 'core/src/main/java/de')
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/ClientConfig.java50
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/preferences/PlaybackPreferences.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/ExoPlayerWrapper.java1
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java18
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java176
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceMediaPlayer.java380
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceNotificationBuilder.java25
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackVolumeUpdater.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlayerStatus.java33
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/RewindAfterPauseUtils.java47
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/widget/WidgetUpdater.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/widget/WidgetUpdaterJobService.java2
13 files changed, 125 insertions, 617 deletions
diff --git a/core/src/main/java/de/danoeh/antennapod/core/ClientConfig.java b/core/src/main/java/de/danoeh/antennapod/core/ClientConfig.java
new file mode 100644
index 000000000..ac67fb042
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/ClientConfig.java
@@ -0,0 +1,50 @@
+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.storage.PodDBAdapter;
+import de.danoeh.antennapod.core.util.NetworkUtils;
+import de.danoeh.antennapod.core.util.gui.NotificationUtils;
+
+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.
+ */
+ public static String USER_AGENT;
+
+ 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"));
+ SleepTimerPreferences.init(context);
+ NotificationUtils.createChannels(context);
+ initialized = true;
+ }
+}
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 8d80ef32b..f0c61403f 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
@@ -8,8 +8,8 @@ import android.util.Log;
import de.danoeh.antennapod.event.PlayerStatusEvent;
import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.model.playback.MediaType;
-import de.danoeh.antennapod.core.service.playback.PlayerStatus;
import de.danoeh.antennapod.model.playback.Playable;
+import de.danoeh.antennapod.playback.base.PlayerStatus;
import org.greenrobot.eventbus.EventBus;
import static de.danoeh.antennapod.model.feed.FeedPreferences.SPEED_USE_GLOBAL;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/ExoPlayerWrapper.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/ExoPlayerWrapper.java
index 79363e872..7ce06a9fb 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/ExoPlayerWrapper.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/ExoPlayerWrapper.java
@@ -40,6 +40,7 @@ import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
import de.danoeh.antennapod.core.service.download.HttpDownloader;
import de.danoeh.antennapod.core.util.NetworkUtils;
import de.danoeh.antennapod.core.util.playback.IPlayer;
+import de.danoeh.antennapod.playback.base.PlaybackServiceMediaPlayer;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
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 5648024de..34fc7d699 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
@@ -17,8 +17,9 @@ import androidx.media.AudioManagerCompat;
import de.danoeh.antennapod.event.PlayerErrorEvent;
import de.danoeh.antennapod.event.playback.BufferUpdateEvent;
import de.danoeh.antennapod.event.playback.SpeedChangedEvent;
-import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.util.playback.MediaPlayerError;
+import de.danoeh.antennapod.playback.base.PlaybackServiceMediaPlayer;
+import de.danoeh.antennapod.playback.base.PlayerStatus;
import org.antennapod.audio.MediaPlayer;
import java.io.File;
@@ -39,7 +40,7 @@ import de.danoeh.antennapod.model.playback.MediaType;
import de.danoeh.antennapod.model.feed.VolumeAdaptionSetting;
import de.danoeh.antennapod.core.feed.util.PlaybackSpeedUtils;
import de.danoeh.antennapod.core.preferences.UserPreferences;
-import de.danoeh.antennapod.core.util.RewindAfterPauseUtils;
+import de.danoeh.antennapod.playback.base.RewindAfterPauseUtils;
import de.danoeh.antennapod.core.util.playback.AudioPlayer;
import de.danoeh.antennapod.core.util.playback.IPlayer;
import de.danoeh.antennapod.model.playback.Playable;
@@ -148,7 +149,7 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
}
public LocalPSMP(@NonNull Context context,
- @NonNull PSMPCallback callback) {
+ @NonNull PlaybackServiceMediaPlayer.PSMPCallback callback) {
super(context, callback);
this.audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
this.playerLock = new PlayerLock();
@@ -265,9 +266,7 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
LocalPSMP.this.startWhenPrepared.set(startWhenPrepared);
setPlayerStatus(PlayerStatus.INITIALIZING, media);
try {
- if (media instanceof FeedMedia && ((FeedMedia) media).getItem() == null) {
- ((FeedMedia) media).setItem(DBReader.getFeedItem(((FeedMedia) media).getItemId()));
- }
+ callback.ensureMediaInfoLoaded(media);
callback.onMediaChanged(false);
setPlaybackParams(PlaybackSpeedUtils.getCurrentPlaybackSpeed(media), UserPreferences.isSkipSilence());
if (stream) {
@@ -1098,7 +1097,7 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
EventBus.getDefault().post(BufferUpdateEvent.ended());
return true;
default:
- return callback.onMediaPlayerInfo(what, 0);
+ return true;
}
}
@@ -1148,4 +1147,9 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
executor.submit(r);
}
}
+
+ @Override
+ public boolean isCasting() {
+ return false;
+ }
}
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 60ccb5c9e..949c0ff9d 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
@@ -37,6 +37,7 @@ import android.widget.Toast;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
@@ -47,6 +48,10 @@ import de.danoeh.antennapod.event.playback.BufferUpdateEvent;
import de.danoeh.antennapod.event.playback.PlaybackServiceEvent;
import de.danoeh.antennapod.event.PlayerErrorEvent;
import de.danoeh.antennapod.event.playback.SleepTimerUpdatedEvent;
+import de.danoeh.antennapod.playback.base.PlaybackServiceMediaPlayer;
+import de.danoeh.antennapod.playback.base.PlayerStatus;
+import de.danoeh.antennapod.playback.cast.CastPsmp;
+import de.danoeh.antennapod.playback.cast.CastStateListener;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
@@ -103,24 +108,10 @@ public class PlaybackService extends MediaBrowserServiceCompat {
*/
private static final String TAG = "PlaybackService";
- /**
- * Parcelable of type Playable.
- */
public static final String EXTRA_PLAYABLE = "PlaybackService.PlayableExtra";
- /**
- * True if cast session should disconnect.
- */
- public static final String EXTRA_CAST_DISCONNECT = "extra.de.danoeh.antennapod.core.service.castDisconnect";
- /**
- * True if media should be streamed.
- */
public static final String EXTRA_SHOULD_STREAM = "extra.de.danoeh.antennapod.core.service.shouldStream";
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";
- /**
- * True if playback should be started immediately after media has been
- * prepared.
- */
public static final String EXTRA_START_WHEN_PREPARED = "extra.de.danoeh.antennapod.core.service.startWhenPrepared";
public static final String EXTRA_PREPARE_IMMEDIATELY = "extra.de.danoeh.antennapod.core.service.prepareImmediately";
@@ -200,10 +191,10 @@ public class PlaybackService extends MediaBrowserServiceCompat {
private PlaybackServiceMediaPlayer mediaPlayer;
private PlaybackServiceTaskManager taskManager;
- private PlaybackServiceFlavorHelper flavorHelper;
private PlaybackServiceStateManager stateManager;
private Disposable positionEventTimer;
private PlaybackServiceNotificationBuilder notificationBuilder;
+ private CastStateListener castStateListener;
private String autoSkippedFeedMediaId = null;
@@ -280,7 +271,6 @@ public class PlaybackService extends MediaBrowserServiceCompat {
EventBus.getDefault().register(this);
taskManager = new PlaybackServiceTaskManager(this, taskManagerCallback);
- flavorHelper = new PlaybackServiceFlavorHelper(PlaybackService.this, flavorHelperCallback);
PreferenceManager.getDefaultSharedPreferences(this)
.registerOnSharedPreferenceChangeListener(prefListener);
@@ -305,12 +295,36 @@ public class PlaybackService extends MediaBrowserServiceCompat {
npe.printStackTrace();
}
- flavorHelper.initializeMediaPlayer(PlaybackService.this);
+ recreateMediaPlayer();
mediaSession.setActive(true);
-
+ castStateListener = new CastStateListener(this) {
+ @Override
+ public void onSessionStartedOrEnded() {
+ recreateMediaPlayer();
+ }
+ };
EventBus.getDefault().post(new PlaybackServiceEvent(PlaybackServiceEvent.Action.SERVICE_STARTED));
}
+ void recreateMediaPlayer() {
+ Playable media = null;
+ boolean wasPlaying = false;
+ if (mediaPlayer != null) {
+ media = mediaPlayer.getPlayable();
+ wasPlaying = mediaPlayer.getPlayerStatus() == PlayerStatus.PLAYING;
+ mediaPlayer.pause(true, false);
+ mediaPlayer.shutdown();
+ }
+ mediaPlayer = CastPsmp.getInstanceIfConnected(this, mediaPlayerCallback);
+ if (mediaPlayer == null) {
+ mediaPlayer = new LocalPSMP(this, mediaPlayerCallback); // Cast not supported or not connected
+ }
+ if (media != null) {
+ mediaPlayer.playMediaObject(media, !media.localFileAvailable(), wasPlaying, true);
+ }
+ isCasting = mediaPlayer.isCasting();
+ }
+
@Override
public void onDestroy() {
super.onDestroy();
@@ -324,6 +338,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
stateManager.stopForeground(!UserPreferences.isPersistNotify());
isRunning = false;
currentMediaType = MediaType.UNKNOWN;
+ castStateListener.destroy();
cancelPositionObserver();
PreferenceManager.getDefaultSharedPreferences(this).unregisterOnSharedPreferenceChangeListener(prefListener);
@@ -337,8 +352,6 @@ public class PlaybackService extends MediaBrowserServiceCompat {
unregisterReceiver(audioBecomingNoisy);
unregisterReceiver(skipCurrentEpisodeReceiver);
unregisterReceiver(pausePlayCurrentEpisodeReceiver);
- flavorHelper.removeCastConsumer();
- flavorHelper.unregisterWifiBroadcastReceiver();
mediaPlayer.shutdown();
taskManager.shutdown();
}
@@ -483,9 +496,8 @@ public class PlaybackService extends MediaBrowserServiceCompat {
final int keycode = intent.getIntExtra(MediaButtonReceiver.EXTRA_KEYCODE, -1);
final boolean hardwareButton = intent.getBooleanExtra(MediaButtonReceiver.EXTRA_HARDWAREBUTTON, false);
- final boolean castDisconnect = intent.getBooleanExtra(EXTRA_CAST_DISCONNECT, false);
Playable playable = intent.getParcelableExtra(EXTRA_PLAYABLE);
- if (keycode == -1 && playable == null && !castDisconnect) {
+ if (keycode == -1 && playable == null) {
Log.e(TAG, "PlaybackService was started with no arguments");
stateManager.stopService();
return Service.START_NOT_STICKY;
@@ -509,7 +521,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
stateManager.stopService();
return Service.START_NOT_STICKY;
}
- } else if (!flavorHelper.castDisconnect(castDisconnect) && playable != null) {
+ } else {
stateManager.validStartCommandWasReceived();
boolean stream = intent.getBooleanExtra(EXTRA_SHOULD_STREAM, true);
boolean allowStreamThisTime = intent.getBooleanExtra(EXTRA_ALLOW_STREAM_THIS_TIME, false);
@@ -553,9 +565,6 @@ public class PlaybackService extends MediaBrowserServiceCompat {
stateManager.stopService();
});
return Service.START_NOT_STICKY;
- } else {
- Log.d(TAG, "Did not handle intent to PlaybackService: " + intent);
- Log.d(TAG, "Extras: " + intent.getExtras());
}
}
@@ -781,8 +790,6 @@ public class PlaybackService extends MediaBrowserServiceCompat {
saveCurrentPosition(true, null, PlaybackServiceMediaPlayer.INVALID_TIME);
}
-
-
@Override
public WidgetUpdater.WidgetState requestWidgetState() {
return new WidgetUpdater.WidgetState(getPlayable(), getStatus(),
@@ -873,11 +880,6 @@ public class PlaybackService extends MediaBrowserServiceCompat {
}
@Override
- public boolean onMediaPlayerInfo(int code, @StringRes int resourceId) {
- return flavorHelper.onMediaPlayerInfo(PlaybackService.this, code, resourceId);
- }
-
- @Override
public void onPostPlayback(@NonNull Playable media, boolean ended, boolean skipped,
boolean playingNext) {
PlaybackService.this.onPostPlayback(media, ended, skipped, playingNext);
@@ -916,10 +918,24 @@ public class PlaybackService extends MediaBrowserServiceCompat {
return PlaybackService.this.getNextInQueue(currentMedia);
}
+ @Nullable
+ @Override
+ public Playable findMedia(@NonNull String url) {
+ FeedItem item = DBReader.getFeedItemByGuidOrEpisodeUrl(null, url);
+ return item != null ? item.getMedia() : null;
+ }
+
@Override
public void onPlaybackEnded(MediaType mediaType, boolean stopPlaying) {
PlaybackService.this.onPlaybackEnded(mediaType, stopPlaying);
}
+
+ @Override
+ public void ensureMediaInfoLoaded(@NonNull Playable media) {
+ if (media instanceof FeedMedia && ((FeedMedia) media).getItem() == null) {
+ ((FeedMedia) media).setItem(DBReader.getFeedItem(((FeedMedia) media).getItemId()));
+ }
+ }
};
@Subscribe(threadMode = ThreadMode.MAIN)
@@ -1248,15 +1264,15 @@ public class PlaybackService extends MediaBrowserServiceCompat {
// This would give the PIP of videos a play button
capabilities = capabilities | PlaybackStateCompat.ACTION_PLAY;
if (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_WATCH) {
- flavorHelper.sessionStateAddActionForWear(sessionState,
+ WearMediaSession.sessionStateAddActionForWear(sessionState,
CUSTOM_ACTION_REWIND,
getString(R.string.rewind_label),
android.R.drawable.ic_media_rew);
- flavorHelper.sessionStateAddActionForWear(sessionState,
+ WearMediaSession.sessionStateAddActionForWear(sessionState,
CUSTOM_ACTION_FAST_FORWARD,
getString(R.string.fast_forward_label),
android.R.drawable.ic_media_ff);
- flavorHelper.mediaSessionSetExtraForWear(mediaSession);
+ WearMediaSession.mediaSessionSetExtraForWear(mediaSession);
}
}
@@ -1338,7 +1354,6 @@ public class PlaybackService extends MediaBrowserServiceCompat {
notificationBuilder.setPlayable(playable);
notificationBuilder.setMediaSessionToken(mediaSession.getSessionToken());
notificationBuilder.setPlayerStatus(playerStatus);
- notificationBuilder.setCasting(isCasting);
notificationBuilder.updatePosition(getCurrentPosition(), getCurrentPlaybackSpeed());
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
@@ -1901,93 +1916,6 @@ public class PlaybackService extends MediaBrowserServiceCompat {
(sharedPreferences, key) -> {
if (UserPreferences.PREF_LOCKSCREEN_BACKGROUND.equals(key)) {
updateNotificationAndMediaSession(getPlayable());
- } else {
- flavorHelper.onSharedPreference(key);
}
};
-
- interface FlavorHelperCallback {
- PlaybackServiceMediaPlayer.PSMPCallback getMediaPlayerCallback();
-
- void setMediaPlayer(PlaybackServiceMediaPlayer mediaPlayer);
-
- PlaybackServiceMediaPlayer getMediaPlayer();
-
- void setIsCasting(boolean isCasting);
-
- void sendNotificationBroadcast(int type, int code);
-
- void saveCurrentPosition(boolean fromMediaPlayer, Playable playable, int position);
-
- void setupNotification(boolean connected, PlaybackServiceMediaPlayer.PSMPInfo info);
-
- MediaSessionCompat getMediaSession();
-
- Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter);
-
- void unregisterReceiver(BroadcastReceiver receiver);
- }
-
- private final FlavorHelperCallback flavorHelperCallback = new FlavorHelperCallback() {
- @Override
- public PlaybackServiceMediaPlayer.PSMPCallback getMediaPlayerCallback() {
- return PlaybackService.this.mediaPlayerCallback;
- }
-
- @Override
- public void setMediaPlayer(PlaybackServiceMediaPlayer mediaPlayer) {
- PlaybackService.this.mediaPlayer = mediaPlayer;
- }
-
- @Override
- public PlaybackServiceMediaPlayer getMediaPlayer() {
- return PlaybackService.this.mediaPlayer;
- }
-
- @Override
- public void setIsCasting(boolean isCasting) {
- PlaybackService.isCasting = isCasting;
- stateManager.validStartCommandWasReceived();
- }
-
- @Override
- public void sendNotificationBroadcast(int type, int code) {
- PlaybackService.this.sendNotificationBroadcast(type, code);
- }
-
- @Override
- public void saveCurrentPosition(boolean fromMediaPlayer, Playable playable, int position) {
- PlaybackService.this.saveCurrentPosition(fromMediaPlayer, playable, position);
- }
-
- @Override
- public void setupNotification(boolean connected, PlaybackServiceMediaPlayer.PSMPInfo info) {
- if (connected) {
- PlaybackService.this.updateNotificationAndMediaSession(info.playable);
- } else {
- PlayerStatus status = info.playerStatus;
- if (status == PlayerStatus.PLAYING || status == PlayerStatus.SEEKING
- || status == PlayerStatus.PREPARING || UserPreferences.isPersistNotify()) {
- PlaybackService.this.updateNotificationAndMediaSession(info.playable);
- } else if (!UserPreferences.isPersistNotify()) {
- stateManager.stopForeground(true);
- }
- }
- }
-
- @Override
- public MediaSessionCompat getMediaSession() {
- return PlaybackService.this.mediaSession;
- }
-
- @Override
- public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
- return PlaybackService.this.registerReceiver(receiver, filter);
- }
-
- @Override
- public void unregisterReceiver(BroadcastReceiver receiver) {
- PlaybackService.this.unregisterReceiver(receiver);
- }
- };
}
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
deleted file mode 100644
index 623ad58bb..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceMediaPlayer.java
+++ /dev/null
@@ -1,380 +0,0 @@
-package de.danoeh.antennapod.core.service.playback;
-
-import android.content.Context;
-import android.media.AudioManager;
-import android.net.wifi.WifiManager;
-import androidx.annotation.NonNull;
-import androidx.annotation.StringRes;
-import android.util.Log;
-import android.util.Pair;
-import android.view.SurfaceHolder;
-
-import java.util.List;
-import java.util.concurrent.Future;
-
-import de.danoeh.antennapod.model.playback.MediaType;
-import de.danoeh.antennapod.model.playback.Playable;
-
-
-/*
- * An inconvenience of an implementation like this is that some members and methods that once were
- * private are now protected, allowing for access from classes of the same package, namely
- * PlaybackService. A workaround would be to move this to a dedicated package.
- */
-/**
- * Abstract class that allows for different implementations of the PlaybackServiceMediaPlayer for local
- * and remote (cast devices) playback.
- */
-public abstract class PlaybackServiceMediaPlayer {
- private static final String TAG = "PlaybackSvcMediaPlayer";
-
- /**
- * Return value of some PSMP methods if the method call failed.
- */
- static final int INVALID_TIME = -1;
-
- private volatile PlayerStatus oldPlayerStatus;
- volatile PlayerStatus playerStatus;
-
- /**
- * A wifi-lock that is acquired if the media file is being streamed.
- */
- private WifiManager.WifiLock wifiLock;
-
- final PSMPCallback callback;
- final Context context;
-
- PlaybackServiceMediaPlayer(@NonNull Context context,
- @NonNull PSMPCallback callback){
- this.context = context;
- this.callback = callback;
-
- playerStatus = PlayerStatus.STOPPED;
- }
-
- /**
- * Starts or prepares playback of the specified Playable object. If another Playable object is already being played, the currently playing
- * episode will be stopped and replaced with the new Playable object. If the Playable object is already being played, the method will
- * not do anything.
- * Whether playback starts immediately depends on the given parameters. See below for more details.
- * <p/>
- * States:
- * During execution of the method, the object will be in the INITIALIZING state. The end state depends on the given parameters.
- * <p/>
- * If 'prepareImmediately' is set to true, the method will go into PREPARING state and after that into PREPARED state. If
- * 'startWhenPrepared' is set to true, the method will additionally go into PLAYING state.
- * <p/>
- * If an unexpected error occurs while loading the Playable's metadata or while setting the MediaPlayers data source, the object
- * will enter the ERROR state.
- * <p/>
- * This method is executed on an internal executor service.
- *
- * @param playable The Playable object that is supposed to be played. This parameter must not be null.
- * @param stream The type of playback. If false, the Playable object MUST provide access to a locally available file via
- * getLocalMediaUrl. If true, the Playable object MUST provide access to a resource that can be streamed by
- * the Android MediaPlayer via getStreamUrl.
- * @param startWhenPrepared Sets the 'startWhenPrepared' flag. This flag determines whether playback will start immediately after the
- * episode has been prepared for playback. Setting this flag to true does NOT mean that the episode will be prepared
- * for playback immediately (see 'prepareImmediately' parameter for more details)
- * @param prepareImmediately Set to true if the method should also prepare the episode for playback.
- */
- public abstract void playMediaObject(@NonNull Playable playable, boolean stream, boolean startWhenPrepared, boolean prepareImmediately);
-
- /**
- * Resumes playback if the PSMP object is in PREPARED or PAUSED state. If the PSMP object is in an invalid state.
- * nothing will happen.
- * <p/>
- * This method is executed on an internal executor service.
- */
- public abstract void resume();
-
- /**
- * Saves the current position and pauses playback. Note that, if audiofocus
- * is abandoned, the lockscreen controls will also disapear.
- * <p/>
- * This method is executed on an internal executor service.
- *
- * @param abandonFocus is true if the service should release audio focus
- * @param reinit is true if service should reinit after pausing if the media
- * file is being streamed
- */
- public abstract void pause(boolean abandonFocus, boolean reinit);
-
- /**
- * Prepared media player for playback if the service is in the INITALIZED
- * state.
- * <p/>
- * This method is executed on an internal executor service.
- */
- public abstract void prepare();
-
- /**
- * Resets the media player and moves it into INITIALIZED state.
- * <p/>
- * This method is executed on an internal executor service.
- */
- public abstract void reinit();
-
- /**
- * Seeks to the specified position. If the PSMP object is in an invalid state, this method will do nothing.
- * Invalid time values (< 0) will be ignored.
- * <p/>
- * This method is executed on an internal executor service.
- */
- public abstract void seekTo(int t);
-
- /**
- * Seek a specific position from the current position
- *
- * @param d offset from current position (positive or negative)
- */
- public abstract void seekDelta(int d);
-
- /**
- * Returns the duration of the current media object or INVALID_TIME if the duration could not be retrieved.
- */
- public abstract int getDuration();
-
- /**
- * Returns the position of the current media object or INVALID_TIME if the position could not be retrieved.
- */
- public abstract int getPosition();
-
- public abstract boolean isStartWhenPrepared();
-
- public abstract void setStartWhenPrepared(boolean startWhenPrepared);
-
- /**
- * Sets the playback parameters.
- * - Speed
- * - SkipSilence (ExoPlayer only)
- * This method is executed on an internal executor service.
- */
- public abstract void setPlaybackParams(final float speed, final boolean skipSilence);
-
- /**
- * Returns the current playback speed. If the playback speed could not be retrieved, 1 is returned.
- */
- public abstract float getPlaybackSpeed();
-
- /**
- * Sets the playback volume.
- * This method is executed on an internal executor service.
- */
- public abstract void setVolume(float volumeLeft, float volumeRight);
-
- /**
- * Returns true if the mediaplayer can mix stereo down to mono
- */
- public abstract boolean canDownmix();
-
- public abstract void setDownmix(boolean enable);
-
- public abstract MediaType getCurrentMediaType();
-
- public abstract boolean isStreaming();
-
- /**
- * Releases internally used resources. This method should only be called when the object is not used anymore.
- */
- public abstract void shutdown();
-
- /**
- * Releases internally used resources. This method should only be called when the object is not used anymore.
- * This method is executed on an internal executor service.
- */
- public abstract void shutdownQuietly();
-
- public abstract void setVideoSurface(SurfaceHolder surface);
-
- public abstract void resetVideoSurface();
-
- /**
- * Return width and height of the currently playing video as a pair.
- *
- * @return Width and height as a Pair or null if the video size could not be determined. The method might still
- * return an invalid non-null value if the getVideoWidth() and getVideoHeight() methods of the media player return
- * invalid values.
- */
- public abstract Pair<Integer, Integer> getVideoSize();
-
- /**
- * Returns a PSMInfo object that contains information about the current state of the PSMP object.
- *
- * @return The PSMPInfo object.
- */
- public final synchronized PSMPInfo getPSMPInfo() {
- return new PSMPInfo(oldPlayerStatus, playerStatus, getPlayable());
- }
-
- /**
- * Returns the current status, if you need the media and the player status together, you should
- * use getPSMPInfo() to make sure they're properly synchronized. Otherwise a race condition
- * could result in nonsensical results (like a status of PLAYING, but a null playable)
- * @return the current player status
- */
- public synchronized PlayerStatus getPlayerStatus() {
- return playerStatus;
- }
-
- /**
- * Returns the current media, if you need the media and the player status together, you should
- * use getPSMPInfo() to make sure they're properly synchronized. Otherwise a race condition
- * could result in nonsensical results (like a status of PLAYING, but a null playable)
- * @return the current media. May be null
- */
- public abstract Playable getPlayable();
-
- protected abstract void setPlayable(Playable playable);
-
- public abstract List<String> getAudioTracks();
-
- public abstract void setAudioTrack(int track);
-
- public abstract int getSelectedAudioTrack();
-
- public void skip() {
- endPlayback(false, true, true, true);
- }
-
- /**
- * Ends playback of current media (if any) and moves into INDETERMINATE state, unless
- * {@param toStoppedState} is set to true, in which case it moves into STOPPED state.
- *
- * @see #endPlayback(boolean, boolean, boolean, boolean)
- */
- public Future<?> stopPlayback(boolean toStoppedState) {
- return endPlayback(false, false, false, toStoppedState);
- }
-
- /**
- * Internal method that handles end of playback.
- *
- * Currently, it has 5 use cases:
- * <ul>
- * <li>Media playback has completed: call with (true, false, true, true)</li>
- * <li>User asks to skip to next episode: call with (false, true, true, true)</li>
- * <li>Skipping to next episode due to playback error: call with (false, false, true, true)</li>
- * <li>Stopping the media player: call with (false, false, false, true)</li>
- * <li>We want to change the media player implementation: call with (false, false, false, false)</li>
- * </ul>
- *
- * @param hasEnded If true, we assume the current media's playback has ended, for
- * purposes of post playback processing.
- * @param wasSkipped Whether the user chose to skip the episode (by pressing the skip
- * button).
- * @param shouldContinue If true, the media player should try to load, and possibly play,
- * the next item, based on the user preferences and whether such item
- * exists.
- * @param toStoppedState If true, the playback state gets set to STOPPED if the media player
- * is not loading/playing after this call, and the UI will reflect that.
- * Only relevant if {@param shouldContinue} is set to false, otherwise
- * this method's behavior defaults as if this parameter was true.
- *
- * @return a Future, just for the purpose of tracking its execution.
- */
- protected abstract Future<?> endPlayback(boolean hasEnded, boolean wasSkipped,
- boolean shouldContinue, boolean toStoppedState);
-
- /**
- * @return {@code true} if the WifiLock feature should be used, {@code false} otherwise.
- */
- protected abstract boolean shouldLockWifi();
-
- final synchronized void acquireWifiLockIfNecessary() {
- if (shouldLockWifi()) {
- if (wifiLock == null) {
- wifiLock = ((WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE))
- .createWifiLock(WifiManager.WIFI_MODE_FULL, TAG);
- wifiLock.setReferenceCounted(false);
- }
- wifiLock.acquire();
- }
- }
-
- final synchronized void releaseWifiLockIfNecessary() {
- if (wifiLock != null && wifiLock.isHeld()) {
- wifiLock.release();
- }
- }
-
- /**
- * Sets the player status of the PSMP object. PlayerStatus and media attributes have to be set at the same time
- * so that getPSMPInfo can't return an invalid state (e.g. status is PLAYING, but media is null).
- * <p/>
- * This method will notify the callback about the change of the player status (even if the new status is the same
- * as the old one).
- * <p/>
- * It will also call {@link PSMPCallback#onPlaybackPause(Playable, int)} or {@link PSMPCallback#onPlaybackStart(Playable, int)}
- * depending on the status change.
- *
- * @param newStatus The new PlayerStatus. This must not be null.
- * @param newMedia The new playable object of the PSMP object. This can be null.
- * @param position The position to be set to the current Playable object in case playback started or paused.
- * Will be ignored if given the value of {@link #INVALID_TIME}.
- */
- final synchronized void setPlayerStatus(@NonNull PlayerStatus newStatus, Playable newMedia, int position) {
- Log.d(TAG, this.getClass().getSimpleName() + ": Setting player status to " + newStatus);
-
- this.oldPlayerStatus = playerStatus;
- this.playerStatus = newStatus;
- setPlayable(newMedia);
-
- if (newMedia != null && newStatus != PlayerStatus.INDETERMINATE) {
- if (oldPlayerStatus == PlayerStatus.PLAYING && newStatus != PlayerStatus.PLAYING) {
- callback.onPlaybackPause(newMedia, position);
- } else if (oldPlayerStatus != PlayerStatus.PLAYING && newStatus == PlayerStatus.PLAYING) {
- callback.onPlaybackStart(newMedia, position);
- }
- }
-
- callback.statusChanged(new PSMPInfo(oldPlayerStatus, playerStatus, getPlayable()));
- }
-
- public boolean isAudioChannelInUse() {
- AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
- return (audioManager.getMode() != AudioManager.MODE_NORMAL || audioManager.isMusicActive());
- }
-
- /**
- * @see #setPlayerStatus(PlayerStatus, Playable, int)
- */
- final void setPlayerStatus(@NonNull PlayerStatus newStatus, Playable newMedia) {
- setPlayerStatus(newStatus, newMedia, INVALID_TIME);
- }
-
- public interface PSMPCallback {
- void statusChanged(PSMPInfo newInfo);
-
- void shouldStop();
-
- void onMediaChanged(boolean reloadUI);
-
- boolean onMediaPlayerInfo(int code, @StringRes int resourceId);
-
- void onPostPlayback(@NonNull Playable media, boolean ended, boolean skipped, boolean playingNext);
-
- void onPlaybackStart(@NonNull Playable playable, int position);
-
- void onPlaybackPause(Playable playable, int position);
-
- Playable getNextInQueue(Playable currentMedia);
-
- void onPlaybackEnded(MediaType mediaType, boolean stopPlaying);
- }
-
- /**
- * Holds information about a PSMP object.
- */
- public static class PSMPInfo {
- public final PlayerStatus oldPlayerStatus;
- public PlayerStatus playerStatus;
- public Playable playable;
-
- PSMPInfo(PlayerStatus oldPlayerStatus, PlayerStatus playerStatus, Playable playable) {
- this.oldPlayerStatus = oldPlayerStatus;
- this.playerStatus = playerStatus;
- this.playable = playable;
- }
- }
-}
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 5aee8c24c..c348f5773 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
@@ -31,17 +31,17 @@ import de.danoeh.antennapod.model.playback.Playable;
import java.util.ArrayList;
import java.util.concurrent.ExecutionException;
+import de.danoeh.antennapod.playback.base.PlayerStatus;
import org.apache.commons.lang3.ArrayUtils;
public class PlaybackServiceNotificationBuilder {
private static final String TAG = "PlaybackSrvNotification";
private static Bitmap defaultIcon = null;
- private Context context;
+ private final Context context;
private Playable playable;
private MediaSessionCompat.Token mediaSessionToken;
private PlayerStatus playerStatus;
- private boolean isCasting;
private Bitmap icon;
private String position;
@@ -140,7 +140,7 @@ public class PlaybackServiceNotificationBuilder {
if (playable != null) {
notification.setContentTitle(playable.getFeedTitle());
notification.setContentText(playable.getEpisodeTitle());
- addActions(notification, mediaSessionToken, playerStatus, isCasting);
+ addActions(notification, mediaSessionToken, playerStatus);
if (icon != null) {
notification.setLargeIcon(icon);
@@ -175,23 +175,10 @@ public class PlaybackServiceNotificationBuilder {
}
private void addActions(NotificationCompat.Builder notification, MediaSessionCompat.Token mediaSessionToken,
- PlayerStatus playerStatus, boolean isCasting) {
+ PlayerStatus playerStatus) {
ArrayList<Integer> compactActionList = new ArrayList<>();
int numActions = 0; // we start and 0 and then increment by 1 for each call to addAction
-
- if (isCasting) {
- Intent stopCastingIntent = new Intent(context, PlaybackService.class);
- stopCastingIntent.putExtra(PlaybackService.EXTRA_CAST_DISCONNECT, true);
- PendingIntent stopCastingPendingIntent = PendingIntent.getService(context,
- numActions, stopCastingIntent, PendingIntent.FLAG_UPDATE_CURRENT
- | (Build.VERSION.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0));
- notification.addAction(R.drawable.ic_notification_cast_off,
- context.getString(R.string.cast_disconnect_label),
- stopCastingPendingIntent);
- numActions++;
- }
-
// always let them rewind
PendingIntent rewindButtonPendingIntent = getPendingIntentForMediaAction(
KeyEvent.KEYCODE_MEDIA_REWIND, numActions);
@@ -270,10 +257,6 @@ public class PlaybackServiceNotificationBuilder {
this.playerStatus = playerStatus;
}
- public void setCasting(boolean casting) {
- isCasting = casting;
- }
-
public PlayerStatus getPlayerStatus() {
return playerStatus;
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackVolumeUpdater.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackVolumeUpdater.java
index edb8bc3a9..43837a473 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackVolumeUpdater.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackVolumeUpdater.java
@@ -4,6 +4,8 @@ import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.model.feed.FeedPreferences;
import de.danoeh.antennapod.model.feed.VolumeAdaptionSetting;
import de.danoeh.antennapod.model.playback.Playable;
+import de.danoeh.antennapod.playback.base.PlaybackServiceMediaPlayer;
+import de.danoeh.antennapod.playback.base.PlayerStatus;
class PlaybackVolumeUpdater {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlayerStatus.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlayerStatus.java
deleted file mode 100644
index 4f2ae34f8..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlayerStatus.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package de.danoeh.antennapod.core.service.playback;
-
-public enum PlayerStatus {
- INDETERMINATE(0), // player is currently changing its state, listeners should wait until the player has left this state.
- ERROR(-1),
- PREPARING(19),
- PAUSED(30),
- PLAYING(40),
- STOPPED(5),
- PREPARED(20),
- SEEKING(29),
- INITIALIZING(9), // playback service is loading the Playable's metadata
- INITIALIZED(10); // playback service was started, data source of media player was set.
-
- private final int statusValue;
- private static final PlayerStatus[] fromOrdinalLookup;
-
- static {
- fromOrdinalLookup = PlayerStatus.values();
- }
-
- PlayerStatus(int val) {
- statusValue = val;
- }
-
- public static PlayerStatus fromOrdinal(int o) {
- return fromOrdinalLookup[o];
- }
-
- public boolean isAtLeast(PlayerStatus other) {
- return other == null || this.statusValue>=other.statusValue;
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/RewindAfterPauseUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/RewindAfterPauseUtils.java
deleted file mode 100644
index 813c6d0f7..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/util/RewindAfterPauseUtils.java
+++ /dev/null
@@ -1,47 +0,0 @@
-package de.danoeh.antennapod.core.util;
-
-import java.util.concurrent.TimeUnit;
-
-/**
- * This class calculates the proper rewind time after the pause and resume.
- * <p>
- * User might loose context if he/she pauses and resumes the media after longer time.
- * Media file should be "rewinded" x seconds after user resumes the playback.
- */
-public class RewindAfterPauseUtils {
- private RewindAfterPauseUtils(){}
-
- public static final long ELAPSED_TIME_FOR_SHORT_REWIND = TimeUnit.MINUTES.toMillis(1);
- public static final long ELAPSED_TIME_FOR_MEDIUM_REWIND = TimeUnit.HOURS.toMillis(1);
- public static final long ELAPSED_TIME_FOR_LONG_REWIND = TimeUnit.DAYS.toMillis(1);
-
- public static final long SHORT_REWIND = TimeUnit.SECONDS.toMillis(3);
- public static final long MEDIUM_REWIND = TimeUnit.SECONDS.toMillis(10);
- public static final long LONG_REWIND = TimeUnit.SECONDS.toMillis(20);
-
- /**
- * @param currentPosition current position in a media file in ms
- * @param lastPlayedTime timestamp when was media paused
- * @return new rewinded position for playback in milliseconds
- */
- public static int calculatePositionWithRewind(int currentPosition, long lastPlayedTime) {
- if (currentPosition > 0 && lastPlayedTime > 0) {
- long elapsedTime = System.currentTimeMillis() - lastPlayedTime;
- long rewindTime = 0;
-
- if (elapsedTime > ELAPSED_TIME_FOR_LONG_REWIND) {
- rewindTime = LONG_REWIND;
- } else if (elapsedTime > ELAPSED_TIME_FOR_MEDIUM_REWIND) {
- rewindTime = MEDIUM_REWIND;
- } else if (elapsedTime > ELAPSED_TIME_FOR_SHORT_REWIND) {
- rewindTime = SHORT_REWIND;
- }
-
- int newPosition = currentPosition - (int) rewindTime;
-
- return Math.max(newPosition, 0);
- } else {
- return currentPosition;
- }
- }
-}
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 b436d80b2..549171c76 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
@@ -22,9 +22,9 @@ import de.danoeh.antennapod.core.feed.util.PlaybackSpeedUtils;
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.service.playback.PlaybackServiceMediaPlayer;
-import de.danoeh.antennapod.core.service.playback.PlayerStatus;
import de.danoeh.antennapod.model.playback.Playable;
+import de.danoeh.antennapod.playback.base.PlaybackServiceMediaPlayer;
+import de.danoeh.antennapod.playback.base.PlayerStatus;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/widget/WidgetUpdater.java b/core/src/main/java/de/danoeh/antennapod/core/widget/WidgetUpdater.java
index 5275e7080..2762fb9fe 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/widget/WidgetUpdater.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/widget/WidgetUpdater.java
@@ -25,11 +25,11 @@ import de.danoeh.antennapod.model.playback.MediaType;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.receiver.MediaButtonReceiver;
import de.danoeh.antennapod.core.receiver.PlayerWidget;
-import de.danoeh.antennapod.core.service.playback.PlayerStatus;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.feed.util.ImageResourceUtils;
import de.danoeh.antennapod.core.util.TimeSpeedConverter;
import de.danoeh.antennapod.model.playback.Playable;
+import de.danoeh.antennapod.playback.base.PlayerStatus;
import de.danoeh.antennapod.ui.appstartintent.MainActivityStarter;
import de.danoeh.antennapod.ui.appstartintent.VideoPlayerActivityStarter;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/widget/WidgetUpdaterJobService.java b/core/src/main/java/de/danoeh/antennapod/core/widget/WidgetUpdaterJobService.java
index b14fb3b0b..325c508c5 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/widget/WidgetUpdaterJobService.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/widget/WidgetUpdaterJobService.java
@@ -6,9 +6,9 @@ import androidx.annotation.NonNull;
import androidx.core.app.SafeJobIntentService;
import de.danoeh.antennapod.core.feed.util.PlaybackSpeedUtils;
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
-import de.danoeh.antennapod.core.service.playback.PlayerStatus;
import de.danoeh.antennapod.model.playback.Playable;
import de.danoeh.antennapod.core.util.playback.PlayableUtils;
+import de.danoeh.antennapod.playback.base.PlayerStatus;
public class WidgetUpdaterJobService extends SafeJobIntentService {
private static final int JOB_ID = -17001;