summaryrefslogtreecommitdiff
path: root/core/src
diff options
context:
space:
mode:
Diffstat (limited to 'core/src')
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java69
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java7
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceMediaPlayer.java41
-rw-r--r--core/src/play/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceFlavorHelper.java9
-rw-r--r--core/src/play/java/de/danoeh/antennapod/core/service/playback/RemotePSMP.java61
5 files changed, 124 insertions, 63 deletions
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 2567cd6f5..0871758d0 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
@@ -13,6 +13,7 @@ import org.antennapod.audio.MediaPlayer;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@@ -754,8 +755,8 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
@Override
- protected void endPlayback(final boolean wasSkipped) {
- executor.submit(() -> {
+ protected Future<?> endPlayback(final boolean wasSkipped, final boolean shouldContinue, final boolean toStoppedState) {
+ return executor.submit(() -> {
playerLock.lock();
releaseWifiLockIfNecessary();
@@ -776,35 +777,46 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
mediaPlayer.reset();
}
audioManager.abandonAudioFocus(audioFocusChangeListener);
- // Load next episode if previous episode was in the queue and if there
- // is an episode in the queue left.
- // Start playback immediately if continuous playback is enabled
- final Playable currentMedia = media;
- Playable nextMedia = callback.getNextInQueue(currentMedia);
- boolean playNextEpisode = isPlaying &&
- nextMedia != null &&
- UserPreferences.isFollowQueue();
+ final Playable currentMedia = media;
+ Playable nextMedia = null;
+
+ if (shouldContinue) {
+ // Load next episode if previous episode was in the queue and if there
+ // is an episode in the queue left.
+ // Start playback immediately if continuous playback is enabled
+ nextMedia = callback.getNextInQueue(currentMedia);
+
+ boolean playNextEpisode = isPlaying &&
+ nextMedia != null &&
+ UserPreferences.isFollowQueue();
+
+ if (playNextEpisode) {
+ Log.d(TAG, "Playback of next episode will start immediately.");
+ } else if (nextMedia == null){
+ Log.d(TAG, "No more episodes available to play");
+ } else {
+ Log.d(TAG, "Loading next episode, but not playing automatically.");
+ }
- if (playNextEpisode) {
- Log.d(TAG, "Playback of next episode will start immediately.");
- } else if (nextMedia == null){
- Log.d(TAG, "No more episodes available to play");
- } else {
- Log.d(TAG, "Loading next episode, but not playing automatically.");
+ if (nextMedia != null) {
+ callback.onPlaybackEnded(nextMedia.getMediaType(), !playNextEpisode);
+ // setting media to null signals to playMediaObject() that we're taking care of post-playback processing
+ media = null;
+ playMediaObject(nextMedia, false, !nextMedia.localFileAvailable(), playNextEpisode, playNextEpisode);
+ }
}
+ if (shouldContinue || toStoppedState) {
+ if (nextMedia == null) {
+ callback.onPlaybackEnded(null, true);
+ stop();
+ }
+ final boolean hasNext = nextMedia != null;
- if (nextMedia != null) {
- callback.onPlaybackEnded(nextMedia.getMediaType(), !playNextEpisode);
- // setting media to null signals to playMediaObject() that we're taking care of post-playback processing
- media = null;
- playMediaObject(nextMedia, false, !nextMedia.localFileAvailable(), playNextEpisode, playNextEpisode);
- } else {
- callback.onPlaybackEnded(null, true);
- stop();
+ executor.submit(() -> callback.onPostPlayback(currentMedia, !wasSkipped, hasNext));
+ } else if (isPlaying) {
+ callback.onPlaybackPause(currentMedia, currentMedia.getPosition());
}
-
- executor.submit(() -> callback.onPostPlayback(currentMedia, !wasSkipped, nextMedia != null));
playerLock.unlock();
});
}
@@ -815,8 +827,7 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
* This method will only take care of changing the PlayerStatus of this object! Other tasks like
* abandoning audio focus have to be done with other methods.
*/
- @Override
- public void stop() {
+ private void stop() {
executor.submit(() -> {
playerLock.lock();
releaseWifiLockIfNecessary();
@@ -869,7 +880,7 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
mp -> genericOnCompletion();
private void genericOnCompletion() {
- endPlayback(false);
+ endPlayback(false, true, true);
}
private final MediaPlayer.OnBufferingUpdateListener audioBufferingUpdateListener =
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 71d0abf07..04b5b676d 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
@@ -786,6 +786,11 @@ public class PlaybackService extends MediaBrowserServiceCompat {
if (!(playable instanceof FeedMedia)) {
Log.d(TAG, "Not doing post-playback processing: media not of type FeedMedia");
+ if (ended) {
+ playable.onPlaybackCompleted(getApplicationContext());
+ } else {
+ playable.onPlaybackPause(getApplicationContext());
+ }
return;
}
FeedMedia media = (FeedMedia) playable;
@@ -1587,7 +1592,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
@Override
public void onStop() {
Log.d(TAG, "onStop()");
- mediaPlayer.stop();
+ mediaPlayer.stopPlayback(true);
}
@Override
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceMediaPlayer.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceMediaPlayer.java
index 5940a463a..aec059ca0 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceMediaPlayer.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceMediaPlayer.java
@@ -8,6 +8,8 @@ import android.util.Log;
import android.util.Pair;
import android.view.SurfaceHolder;
+import java.util.concurrent.Future;
+
import de.danoeh.antennapod.core.feed.MediaType;
import de.danoeh.antennapod.core.util.playback.Playable;
@@ -226,18 +228,43 @@ public abstract class PlaybackServiceMediaPlayer {
protected abstract void setPlayable(Playable playable);
public void skip() {
- endPlayback(true);
+ endPlayback(true, true, true);
}
- protected abstract void endPlayback(boolean wasSkipped);
+ /**
+ * 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)
+ */
+ public Future<?> stopPlayback(boolean toStoppedState) {
+ return endPlayback(true, false, toStoppedState);
+ }
/**
- * Moves the PSMP into STOPPED state. This call is only valid if the player is currently in
- * INDETERMINATE state, for example after a call to endPlayback.
- * This method will only take care of changing the PlayerStatus of this object! Other tasks like
- * abandoning audio focus have to be done with other methods.
+ * Internal method that handles end of playback.
+ *
+ * Currently, it has 4 use cases:
+ * <ul>
+ * <li>Media playback has completed: call with (false, true, true)</li>
+ * <li>User asks to skip to next episode: call with (true, true, true)</li>
+ * <li>Stopping the media player: call with (true, false, true)</li>
+ * <li>We want to change the media player implementation: call with (true, false, false)</li>
+ * </ul>
+ *
+ * @param wasSkipped If true, we assume the current media's playback has ended, for
+ * purposes of post playback processing.
+ * @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.
*/
- public abstract void stop();
+ protected abstract Future<?> endPlayback(boolean wasSkipped, boolean shouldContinue, boolean toStoppedState);
/**
* @return {@code true} if the WifiLock feature should be used, {@code false} otherwise.
diff --git a/core/src/play/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceFlavorHelper.java b/core/src/play/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceFlavorHelper.java
index a00e86e73..c7428947b 100644
--- a/core/src/play/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceFlavorHelper.java
+++ b/core/src/play/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceFlavorHelper.java
@@ -15,6 +15,8 @@ import android.widget.Toast;
import com.google.android.gms.cast.ApplicationMetadata;
import com.google.android.libraries.cast.companionlibrary.cast.BaseCastManager;
+import java.util.concurrent.ExecutionException;
+
import de.danoeh.antennapod.core.cast.CastConsumer;
import de.danoeh.antennapod.core.cast.CastManager;
import de.danoeh.antennapod.core.cast.DefaultCastConsumer;
@@ -182,8 +184,11 @@ public class PlaybackServiceFlavorHelper {
boolean wasLaunched) {
PlaybackServiceMediaPlayer mediaPlayer = callback.getMediaPlayer();
if (mediaPlayer != null) {
- //TODO change implementation to new protocol
-// mediaPlayer.endPlayback(true, true);
+ try {
+ mediaPlayer.stopPlayback(false).get();
+ } catch (InterruptedException | ExecutionException e) {
+ Log.e(TAG, "There was a problem stopping playback while switching media players", e);
+ }
mediaPlayer.shutdownQuietly();
}
mediaPlayer = newPlayer;
diff --git a/core/src/play/java/de/danoeh/antennapod/core/service/playback/RemotePSMP.java b/core/src/play/java/de/danoeh/antennapod/core/service/playback/RemotePSMP.java
index ee6cb653c..e594f14ec 100644
--- a/core/src/play/java/de/danoeh/antennapod/core/service/playback/RemotePSMP.java
+++ b/core/src/play/java/de/danoeh/antennapod/core/service/playback/RemotePSMP.java
@@ -15,6 +15,8 @@ import com.google.android.libraries.cast.companionlibrary.cast.exceptions.CastEx
import com.google.android.libraries.cast.companionlibrary.cast.exceptions.NoConnectionException;
import com.google.android.libraries.cast.companionlibrary.cast.exceptions.TransientNetworkDisconnectionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.FutureTask;
import java.util.concurrent.atomic.AtomicBoolean;
import de.danoeh.antennapod.core.R;
@@ -121,7 +123,7 @@ public class RemotePSMP extends PlaybackServiceMediaPlayer {
}
if (playbackEnded) {
// This is an unconventional thing to occur...
- endPlayback(true);
+ endPlayback(true, true, true);
}
}
@@ -259,13 +261,13 @@ public class RemotePSMP extends PlaybackServiceMediaPlayer {
if (mediaChanged && currentMedia != null) {
media = currentMedia;
}
- endPlayback(false);
+ endPlayback(false, true, true);
return;
case MediaStatus.IDLE_REASON_ERROR:
Log.w(TAG, "Got an error status from the Chromecast. Skipping, if possible, to the next episode...");
callback.onMediaPlayerInfo(CAST_ERROR_PRIORITY_HIGH,
R.string.cast_failed_media_error_skipping);
- endPlayback(true);
+ endPlayback(true, true, true);
return;
}
break;
@@ -597,7 +599,7 @@ public class RemotePSMP extends PlaybackServiceMediaPlayer {
}
@Override
- protected void endPlayback(boolean wasSkipped) {
+ protected Future<?> endPlayback(boolean wasSkipped, boolean shouldContinue, boolean toStoppedState) {
Log.d(TAG, "endPlayback() called");
boolean isPlaying = playerStatus == PlayerStatus.PLAYING;
if (playerStatus != PlayerStatus.INDETERMINATE) {
@@ -611,32 +613,43 @@ public class RemotePSMP extends PlaybackServiceMediaPlayer {
}
}
final Playable currentMedia = media;
- Playable nextMedia = callback.getNextInQueue(currentMedia);
+ Playable nextMedia = null;
+ if (shouldContinue) {
+ nextMedia = callback.getNextInQueue(currentMedia);
+
+ boolean playNextEpisode = isPlaying && nextMedia != null && UserPreferences.isFollowQueue();
+ if (playNextEpisode) {
+ Log.d(TAG, "Playback of next episode will start immediately.");
+ } else if (nextMedia == null){
+ Log.d(TAG, "No more episodes available to play");
+ } else {
+ Log.d(TAG, "Loading next episode, but not playing automatically.");
+ }
- boolean playNextEpisode = isPlaying && nextMedia != null && UserPreferences.isFollowQueue();
- if (playNextEpisode) {
- Log.d(TAG, "Playback of next episode will start immediately.");
- } else if (nextMedia == null){
- Log.d(TAG, "No more episodes available to play");
- } else {
- Log.d(TAG, "Loading next episode, but not playing automatically.");
+ if (nextMedia != null) {
+ callback.onPlaybackEnded(nextMedia.getMediaType(), !playNextEpisode);
+ // setting media to null signals to playMediaObject() that we're taking care of post-playback processing
+ media = null;
+ playMediaObject(nextMedia, false, true /*TODO for now we always stream*/, playNextEpisode, playNextEpisode);
+ }
}
-
- if (nextMedia != null) {
- callback.onPlaybackEnded(nextMedia.getMediaType(), !playNextEpisode);
- // setting media to null signals to playMediaObject() that we're taking care of post-playback processing
- media = null;
- playMediaObject(nextMedia, false, true /*TODO for now we always stream*/, playNextEpisode, playNextEpisode);
- } else {
- callback.onPlaybackEnded(null, true);
- stop();
+ if (shouldContinue || toStoppedState) {
+ if (nextMedia != null) {
+ callback.onPlaybackEnded(null, true);
+ stop();
+ }
+ callback.onPostPlayback(currentMedia, !wasSkipped, nextMedia != null);
+ } else if (isPlaying) {
+ callback.onPlaybackPause(currentMedia,
+ currentMedia != null ? currentMedia.getPosition() : INVALID_TIME);
}
- callback.onPostPlayback(currentMedia, !wasSkipped, nextMedia != null);
+ FutureTask<?> future = new FutureTask<>(() -> {}, null);
+ future.run();
+ return future;
}
- @Override
- public void stop() {
+ private void stop() {
if (playerStatus == PlayerStatus.INDETERMINATE) {
setPlayerStatus(PlayerStatus.STOPPED, null);
} else {