diff --git a/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/MediaSessionConnector.java b/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/MediaSessionConnector.java index 0b5fdbc624..0cb590b65e 100644 --- a/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/MediaSessionConnector.java +++ b/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/MediaSessionConnector.java @@ -212,7 +212,7 @@ public final class MediaSessionConnector { * @param player The player connected to the media session. * @return The bitmask of the supported media actions. */ - long getSupportedQueueNavigatorActions(@Nullable Player player); + long getSupportedQueueNavigatorActions(Player player); /** * Called when the timeline of the player has changed. * @@ -586,7 +586,7 @@ public final class MediaSessionConnector { public final void invalidateMediaSessionPlaybackState() { PlaybackStateCompat.Builder builder = new PlaybackStateCompat.Builder(); if (player == null) { - builder.setActions(buildPlaybackActions()).setState(PlaybackStateCompat.STATE_NONE, 0, 0, 0); + builder.setActions(/* capabilities= */ 0).setState(PlaybackStateCompat.STATE_NONE, 0, 0, 0); mediaSession.setPlaybackState(builder.build()); return; } @@ -622,7 +622,7 @@ public final class MediaSessionConnector { Bundle extras = new Bundle(); extras.putFloat(EXTRAS_PITCH, player.getPlaybackParameters().pitch); builder - .setActions(buildPlaybackActions()) + .setActions(buildPlaybackActions(player)) .setActiveQueueItemId(activeQueueItemId) .setBufferedPosition(player.getBufferedPosition()) .setState( @@ -657,21 +657,32 @@ public final class MediaSessionConnector { commandReceivers.remove(commandReceiver); } - private long buildPlaybackActions() { - long actions = 0; - if (player != null && !player.getCurrentTimeline().isEmpty()) { - long playbackActions = BASE_PLAYBACK_ACTIONS; - if (player.isCurrentWindowSeekable()) { - playbackActions |= PlaybackStateCompat.ACTION_SEEK_TO; - if (fastForwardMs > 0) { - playbackActions |= PlaybackStateCompat.ACTION_FAST_FORWARD; - } - if (rewindMs > 0) { - playbackActions |= PlaybackStateCompat.ACTION_REWIND; - } - } - actions |= (playbackActions & enabledPlaybackActions); + private long buildPlaybackActions(Player player) { + boolean enableSeeking = false; + boolean enableRewind = false; + boolean enableFastForward = false; + boolean enableSetRating = false; + Timeline timeline = player.getCurrentTimeline(); + if (!timeline.isEmpty() && !player.isPlayingAd()) { + enableSeeking = player.isCurrentWindowSeekable(); + enableRewind = enableSeeking && rewindMs > 0; + enableFastForward = enableSeeking && fastForwardMs > 0; + enableSetRating = true; } + + long playbackActions = BASE_PLAYBACK_ACTIONS; + if (enableSeeking) { + playbackActions |= PlaybackStateCompat.ACTION_SEEK_TO; + } + if (enableFastForward) { + playbackActions |= PlaybackStateCompat.ACTION_FAST_FORWARD; + } + if (enableRewind) { + playbackActions |= PlaybackStateCompat.ACTION_REWIND; + } + playbackActions &= enabledPlaybackActions; + + long actions = playbackActions; if (playbackPreparer != null) { actions |= (PlaybackPreparer.ACTIONS & playbackPreparer.getSupportedPrepareActions()); } @@ -679,7 +690,7 @@ public final class MediaSessionConnector { actions |= (QueueNavigator.ACTIONS & queueNavigator.getSupportedQueueNavigatorActions(player)); } - if (ratingCallback != null) { + if (ratingCallback != null && enableSetRating) { actions |= PlaybackStateCompat.ACTION_SET_RATING; } return actions; @@ -699,39 +710,46 @@ public final class MediaSessionConnector { } private boolean canDispatchPlaybackAction(long action) { - return (enabledPlaybackActions & action) != 0; + return player != null && (enabledPlaybackActions & action) != 0; } private boolean canDispatchToPlaybackPreparer(long action) { - return playbackPreparer != null - && (playbackPreparer.getSupportedPrepareActions() & PlaybackPreparer.ACTIONS & action) != 0; + return player != null + && playbackPreparer != null + && (playbackPreparer.getSupportedPrepareActions() & action) != 0; } private boolean canDispatchToQueueNavigator(long action) { - return queueNavigator != null - && (queueNavigator.getSupportedQueueNavigatorActions(player) - & QueueNavigator.ACTIONS - & action) - != 0; + return player != null + && queueNavigator != null + && (queueNavigator.getSupportedQueueNavigatorActions(player) & action) != 0; } - private void rewind() { - if (rewindMs > 0) { - seekTo(player.getCurrentPosition() - rewindMs); + private boolean canDispatchSetRating() { + return player != null && ratingCallback != null; + } + + private boolean canDispatchQueueEdit() { + return player != null && queueEditor != null; + } + + private void rewind(Player player) { + if (player.isCurrentWindowSeekable() && rewindMs > 0) { + seekTo(player, player.getCurrentPosition() - rewindMs); } } - private void fastForward() { - if (fastForwardMs > 0) { - seekTo(player.getCurrentPosition() + fastForwardMs); + private void fastForward(Player player) { + if (player.isCurrentWindowSeekable() && fastForwardMs > 0) { + seekTo(player, player.getCurrentPosition() + fastForwardMs); } } - private void seekTo(long positionMs) { - seekTo(player.getCurrentWindowIndex(), positionMs); + private void seekTo(Player player, long positionMs) { + seekTo(player, player.getCurrentWindowIndex(), positionMs); } - private void seekTo(int windowIndex, long positionMs) { + private void seekTo(Player player, int windowIndex, long positionMs) { long durationMs = player.getDuration(); if (durationMs != C.TIME_UNSET) { positionMs = Math.min(positionMs, durationMs); @@ -930,21 +948,21 @@ public final class MediaSessionConnector { @Override public void onSeekTo(long positionMs) { if (canDispatchPlaybackAction(PlaybackStateCompat.ACTION_SEEK_TO)) { - seekTo(positionMs); + seekTo(player, positionMs); } } @Override public void onFastForward() { if (canDispatchPlaybackAction(PlaybackStateCompat.ACTION_FAST_FORWARD)) { - fastForward(); + fastForward(player); } } @Override public void onRewind() { if (canDispatchPlaybackAction(PlaybackStateCompat.ACTION_REWIND)) { - rewind(); + rewind(player); } } @@ -1008,7 +1026,7 @@ public final class MediaSessionConnector { @Override public void onCustomAction(@NonNull String action, @Nullable Bundle extras) { - if (customActionMap.containsKey(action)) { + if (player != null && customActionMap.containsKey(action)) { customActionMap.get(action).onCustomAction(player, controlDispatcher, action, extras); invalidateMediaSessionPlaybackState(); } @@ -1016,9 +1034,11 @@ public final class MediaSessionConnector { @Override public void onCommand(String command, Bundle extras, ResultReceiver cb) { - for (int i = 0; i < commandReceivers.size(); i++) { - if (commandReceivers.get(i).onCommand(player, controlDispatcher, command, extras, cb)) { - return; + if (player != null) { + for (int i = 0; i < commandReceivers.size(); i++) { + if (commandReceivers.get(i).onCommand(player, controlDispatcher, command, extras, cb)) { + return; + } } } } @@ -1088,35 +1108,35 @@ public final class MediaSessionConnector { @Override public void onSetRating(RatingCompat rating) { - if (ratingCallback != null) { + if (canDispatchSetRating()) { ratingCallback.onSetRating(player, rating); } } @Override public void onSetRating(RatingCompat rating, Bundle extras) { - if (ratingCallback != null) { + if (canDispatchSetRating()) { ratingCallback.onSetRating(player, rating, extras); } } @Override public void onAddQueueItem(MediaDescriptionCompat description) { - if (queueEditor != null) { + if (canDispatchQueueEdit()) { queueEditor.onAddQueueItem(player, description); } } @Override public void onAddQueueItem(MediaDescriptionCompat description, int index) { - if (queueEditor != null) { + if (canDispatchQueueEdit()) { queueEditor.onAddQueueItem(player, description, index); } } @Override public void onRemoveQueueItem(MediaDescriptionCompat description) { - if (queueEditor != null) { + if (canDispatchQueueEdit()) { queueEditor.onRemoveQueueItem(player, description); } } diff --git a/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/TimelineQueueNavigator.java b/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/TimelineQueueNavigator.java index 6eb1b4d4ca..5d2b37618f 100644 --- a/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/TimelineQueueNavigator.java +++ b/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/TimelineQueueNavigator.java @@ -84,21 +84,25 @@ public abstract class TimelineQueueNavigator implements MediaSessionConnector.Qu @Override public long getSupportedQueueNavigatorActions(Player player) { - if (player == null) { - return 0; - } + boolean enableSkipTo = false; + boolean enablePrevious = false; + boolean enableNext = false; Timeline timeline = player.getCurrentTimeline(); - if (timeline.isEmpty() || player.isPlayingAd()) { - return 0; + if (!timeline.isEmpty() && !player.isPlayingAd()) { + timeline.getWindow(player.getCurrentWindowIndex(), window); + enableSkipTo = timeline.getWindowCount() > 1; + enablePrevious = window.isSeekable || !window.isDynamic || player.hasPrevious(); + enableNext = window.isDynamic || player.hasNext(); } + long actions = 0; - if (timeline.getWindowCount() > 1) { + if (enableSkipTo) { actions |= PlaybackStateCompat.ACTION_SKIP_TO_QUEUE_ITEM; } - if (window.isSeekable || !window.isDynamic || player.hasPrevious()) { + if (enablePrevious) { actions |= PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS; } - if (window.isDynamic || player.hasNext()) { + if (enableNext) { actions |= PlaybackStateCompat.ACTION_SKIP_TO_NEXT; } return actions; diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerControlView.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerControlView.java index b0de163ad2..7ac813211a 100644 --- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerControlView.java +++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerControlView.java @@ -224,10 +224,10 @@ public class PlayerControlView extends FrameLayout { private final String repeatOneButtonContentDescription; private final String repeatAllButtonContentDescription; - private Player player; + @Nullable private Player player; private com.google.android.exoplayer2.ControlDispatcher controlDispatcher; private VisibilityListener visibilityListener; - private @Nullable PlaybackPreparer playbackPreparer; + @Nullable private PlaybackPreparer playbackPreparer; private boolean isAttachedToWindow; private boolean showMultiWindowTimeBar; @@ -363,6 +363,7 @@ public class PlayerControlView extends FrameLayout { * Returns the {@link Player} currently being controlled by this view, or null if no player is * set. */ + @Nullable public Player getPlayer() { return player; } @@ -411,8 +412,8 @@ public class PlayerControlView extends FrameLayout { * * @param extraAdGroupTimesMs The millisecond timestamps of the extra ad markers to show, or * {@code null} to show no extra ad markers. - * @param extraPlayedAdGroups Whether each ad has been played, or {@code null} to show no extra ad - * markers. + * @param extraPlayedAdGroups Whether each ad has been played. Must be the same length as {@code + * extraAdGroupTimesMs}, or {@code null} if {@code extraAdGroupTimesMs} is {@code null}. */ public void setExtraAdGroupMarkers( @Nullable long[] extraAdGroupTimesMs, @Nullable boolean[] extraPlayedAdGroups) { @@ -420,6 +421,7 @@ public class PlayerControlView extends FrameLayout { this.extraAdGroupTimesMs = new long[0]; this.extraPlayedAdGroups = new boolean[0]; } else { + extraPlayedAdGroups = Assertions.checkNotNull(extraPlayedAdGroups); Assertions.checkArgument(extraAdGroupTimesMs.length == extraPlayedAdGroups.length); this.extraAdGroupTimesMs = extraAdGroupTimesMs; this.extraPlayedAdGroups = extraPlayedAdGroups; @@ -659,24 +661,30 @@ public class PlayerControlView extends FrameLayout { if (!isVisible() || !isAttachedToWindow) { return; } - Timeline timeline = player != null ? player.getCurrentTimeline() : null; - boolean haveNonEmptyTimeline = timeline != null && !timeline.isEmpty(); - boolean isSeekable = false; + boolean enableSeeking = false; boolean enablePrevious = false; + boolean enableRewind = false; + boolean enableFastForward = false; boolean enableNext = false; - if (haveNonEmptyTimeline && !player.isPlayingAd()) { - int windowIndex = player.getCurrentWindowIndex(); - timeline.getWindow(windowIndex, window); - isSeekable = window.isSeekable; - enablePrevious = isSeekable || !window.isDynamic || player.hasPrevious(); - enableNext = window.isDynamic || player.hasNext(); + if (player != null) { + Timeline timeline = player.getCurrentTimeline(); + if (!timeline.isEmpty() && !player.isPlayingAd()) { + timeline.getWindow(player.getCurrentWindowIndex(), window); + boolean isSeekable = window.isSeekable; + enableSeeking = isSeekable; + enablePrevious = isSeekable || !window.isDynamic || player.hasPrevious(); + enableRewind = isSeekable && rewindMs > 0; + enableFastForward = isSeekable && fastForwardMs > 0; + enableNext = window.isDynamic || player.hasNext(); + } } + setButtonEnabled(enablePrevious, previousButton); + setButtonEnabled(enableRewind, rewindButton); + setButtonEnabled(enableFastForward, fastForwardButton); setButtonEnabled(enableNext, nextButton); - setButtonEnabled(fastForwardMs > 0 && isSeekable, fastForwardButton); - setButtonEnabled(rewindMs > 0 && isSeekable, rewindButton); if (timeBar != null) { - timeBar.setEnabled(isSeekable); + timeBar.setEnabled(enableSeeking); } } @@ -862,7 +870,7 @@ public class PlayerControlView extends FrameLayout { view.setVisibility(VISIBLE); } - private void previous() { + private void previous(Player player) { Timeline timeline = player.getCurrentTimeline(); if (timeline.isEmpty() || player.isPlayingAd()) { return; @@ -873,13 +881,13 @@ public class PlayerControlView extends FrameLayout { if (previousWindowIndex != C.INDEX_UNSET && (player.getCurrentPosition() <= MAX_POSITION_FOR_SEEK_TO_PREVIOUS || (window.isDynamic && !window.isSeekable))) { - seekTo(previousWindowIndex, C.TIME_UNSET); + seekTo(player, previousWindowIndex, C.TIME_UNSET); } else { - seekTo(0); + seekTo(player, 0); } } - private void next() { + private void next(Player player) { Timeline timeline = player.getCurrentTimeline(); if (timeline.isEmpty() || player.isPlayingAd()) { return; @@ -887,29 +895,29 @@ public class PlayerControlView extends FrameLayout { int windowIndex = player.getCurrentWindowIndex(); int nextWindowIndex = player.getNextWindowIndex(); if (nextWindowIndex != C.INDEX_UNSET) { - seekTo(nextWindowIndex, C.TIME_UNSET); + seekTo(player, nextWindowIndex, C.TIME_UNSET); } else if (timeline.getWindow(windowIndex, window).isDynamic) { - seekTo(windowIndex, C.TIME_UNSET); + seekTo(player, windowIndex, C.TIME_UNSET); } } - private void rewind() { - if (rewindMs > 0) { - seekTo(player.getCurrentPosition() - rewindMs); + private void rewind(Player player) { + if (player.isCurrentWindowSeekable() && rewindMs > 0) { + seekTo(player, player.getCurrentPosition() - rewindMs); } } - private void fastForward() { - if (fastForwardMs > 0) { - seekTo(player.getCurrentPosition() + fastForwardMs); + private void fastForward(Player player) { + if (player.isCurrentWindowSeekable() && fastForwardMs > 0) { + seekTo(player, player.getCurrentPosition() + fastForwardMs); } } - private void seekTo(long positionMs) { - seekTo(player.getCurrentWindowIndex(), positionMs); + private void seekTo(Player player, long positionMs) { + seekTo(player, player.getCurrentWindowIndex(), positionMs); } - private boolean seekTo(int windowIndex, long positionMs) { + private boolean seekTo(Player player, int windowIndex, long positionMs) { long durationMs = player.getDuration(); if (durationMs != C.TIME_UNSET) { positionMs = Math.min(positionMs, durationMs); @@ -918,7 +926,7 @@ public class PlayerControlView extends FrameLayout { return controlDispatcher.dispatchSeekTo(player, windowIndex, positionMs); } - private void seekToTimeBarPosition(long positionMs) { + private void seekToTimeBarPosition(Player player, long positionMs) { int windowIndex; Timeline timeline = player.getCurrentTimeline(); if (multiWindowTimeBar && !timeline.isEmpty()) { @@ -939,7 +947,7 @@ public class PlayerControlView extends FrameLayout { } else { windowIndex = player.getCurrentWindowIndex(); } - boolean dispatched = seekTo(windowIndex, positionMs); + boolean dispatched = seekTo(player, windowIndex, positionMs); if (!dispatched) { // The seek wasn't dispatched then the progress bar scrubber will be in the wrong position. // Trigger a progress update to snap it back. @@ -1001,9 +1009,9 @@ public class PlayerControlView extends FrameLayout { } if (event.getAction() == KeyEvent.ACTION_DOWN) { if (keyCode == KeyEvent.KEYCODE_MEDIA_FAST_FORWARD) { - fastForward(); + fastForward(player); } else if (keyCode == KeyEvent.KEYCODE_MEDIA_REWIND) { - rewind(); + rewind(player); } else if (event.getRepeatCount() == 0) { switch (keyCode) { case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: @@ -1016,10 +1024,10 @@ public class PlayerControlView extends FrameLayout { controlDispatcher.dispatchSetPlayWhenReady(player, false); break; case KeyEvent.KEYCODE_MEDIA_NEXT: - next(); + next(player); break; case KeyEvent.KEYCODE_MEDIA_PREVIOUS: - previous(); + previous(player); break; default: break; @@ -1086,7 +1094,7 @@ public class PlayerControlView extends FrameLayout { public void onScrubStop(TimeBar timeBar, long position, boolean canceled) { scrubbing = false; if (!canceled && player != null) { - seekToTimeBarPosition(position); + seekToTimeBarPosition(player, position); } } @@ -1124,32 +1132,34 @@ public class PlayerControlView extends FrameLayout { @Override public void onClick(View view) { - if (player != null) { - if (nextButton == view) { - next(); - } else if (previousButton == view) { - previous(); - } else if (fastForwardButton == view) { - fastForward(); - } else if (rewindButton == view) { - rewind(); - } else if (playButton == view) { - if (player.getPlaybackState() == Player.STATE_IDLE) { - if (playbackPreparer != null) { - playbackPreparer.preparePlayback(); - } - } else if (player.getPlaybackState() == Player.STATE_ENDED) { - controlDispatcher.dispatchSeekTo(player, player.getCurrentWindowIndex(), C.TIME_UNSET); + Player player = PlayerControlView.this.player; + if (player == null) { + return; + } + if (nextButton == view) { + next(player); + } else if (previousButton == view) { + previous(player); + } else if (fastForwardButton == view) { + fastForward(player); + } else if (rewindButton == view) { + rewind(player); + } else if (playButton == view) { + if (player.getPlaybackState() == Player.STATE_IDLE) { + if (playbackPreparer != null) { + playbackPreparer.preparePlayback(); } - controlDispatcher.dispatchSetPlayWhenReady(player, true); - } else if (pauseButton == view) { - controlDispatcher.dispatchSetPlayWhenReady(player, false); - } else if (repeatToggleButton == view) { - controlDispatcher.dispatchSetRepeatMode( - player, RepeatModeUtil.getNextRepeatMode(player.getRepeatMode(), repeatToggleModes)); - } else if (shuffleButton == view) { - controlDispatcher.dispatchSetShuffleModeEnabled(player, !player.getShuffleModeEnabled()); + } else if (player.getPlaybackState() == Player.STATE_ENDED) { + controlDispatcher.dispatchSeekTo(player, player.getCurrentWindowIndex(), C.TIME_UNSET); } + controlDispatcher.dispatchSetPlayWhenReady(player, true); + } else if (pauseButton == view) { + controlDispatcher.dispatchSetPlayWhenReady(player, false); + } else if (repeatToggleButton == view) { + controlDispatcher.dispatchSetRepeatMode( + player, RepeatModeUtil.getNextRepeatMode(player.getRepeatMode(), repeatToggleModes)); + } else if (shuffleButton == view) { + controlDispatcher.dispatchSetShuffleModeEnabled(player, !player.getShuffleModeEnabled()); } } } diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerNotificationManager.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerNotificationManager.java index a125f31932..fff59b09cd 100644 --- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerNotificationManager.java +++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerNotificationManager.java @@ -299,8 +299,9 @@ public class PlayerNotificationManager { private final Map playbackActions; private final Map customActions; private final int instanceId; + private final Timeline.Window window; - private @Nullable Player player; + @Nullable private Player player; private ControlDispatcher controlDispatcher; private boolean isNotificationStarted; private int currentNotificationTag; @@ -485,7 +486,8 @@ public class PlayerNotificationManager { this.mediaDescriptionAdapter = mediaDescriptionAdapter; this.notificationListener = notificationListener; this.customActionReceiver = customActionReceiver; - this.controlDispatcher = new DefaultControlDispatcher(); + controlDispatcher = new DefaultControlDispatcher(); + window = new Timeline.Window(); instanceId = instanceIdCounter++; mainHandler = new Handler(Looper.getMainLooper()); notificationManager = NotificationManagerCompat.from(context); @@ -968,15 +970,25 @@ public class PlayerNotificationManager { * action name is ignored. */ protected List getActions(Player player) { - boolean isPlayingAd = player.isPlayingAd(); + boolean enablePrevious = false; + boolean enableRewind = false; + boolean enableFastForward = false; + boolean enableNext = false; + Timeline timeline = player.getCurrentTimeline(); + if (!timeline.isEmpty() && !player.isPlayingAd()) { + timeline.getWindow(player.getCurrentWindowIndex(), window); + enablePrevious = window.isSeekable || !window.isDynamic || player.hasPrevious(); + enableRewind = rewindMs > 0; + enableFastForward = fastForwardMs > 0; + enableNext = window.isDynamic || player.hasNext(); + } + List stringActions = new ArrayList<>(); - if (!isPlayingAd) { - if (useNavigationActions) { - stringActions.add(ACTION_PREVIOUS); - } - if (rewindMs > 0) { - stringActions.add(ACTION_REWIND); - } + if (useNavigationActions && enablePrevious) { + stringActions.add(ACTION_PREVIOUS); + } + if (enableRewind) { + stringActions.add(ACTION_REWIND); } if (usePlayPauseActions) { if (player.getPlayWhenReady()) { @@ -985,13 +997,11 @@ public class PlayerNotificationManager { stringActions.add(ACTION_PLAY); } } - if (!isPlayingAd) { - if (fastForwardMs > 0) { - stringActions.add(ACTION_FAST_FORWARD); - } - if (useNavigationActions && player.getNextWindowIndex() != C.INDEX_UNSET) { - stringActions.add(ACTION_NEXT); - } + if (enableFastForward) { + stringActions.add(ACTION_FAST_FORWARD); + } + if (useNavigationActions && enableNext) { + stringActions.add(ACTION_NEXT); } if (customActionReceiver != null) { stringActions.addAll(customActionReceiver.getCustomActions(player)); @@ -1011,6 +1021,7 @@ public class PlayerNotificationManager { * @param actionNames The names of the actions included in the notification. * @param player The player for which state to build a notification. */ + @SuppressWarnings("unused") protected int[] getActionIndicesForCompactView(List actionNames, Player player) { int pauseActionIndex = actionNames.indexOf(ACTION_PAUSE); int playActionIndex = actionNames.indexOf(ACTION_PLAY); @@ -1067,6 +1078,62 @@ public class PlayerNotificationManager { return actions; } + private void previous(Player player) { + Timeline timeline = player.getCurrentTimeline(); + if (timeline.isEmpty() || player.isPlayingAd()) { + return; + } + int windowIndex = player.getCurrentWindowIndex(); + timeline.getWindow(windowIndex, window); + int previousWindowIndex = player.getPreviousWindowIndex(); + if (previousWindowIndex != C.INDEX_UNSET + && (player.getCurrentPosition() <= MAX_POSITION_FOR_SEEK_TO_PREVIOUS + || (window.isDynamic && !window.isSeekable))) { + seekTo(player, previousWindowIndex, C.TIME_UNSET); + } else { + seekTo(player, 0); + } + } + + private void next(Player player) { + Timeline timeline = player.getCurrentTimeline(); + if (timeline.isEmpty() || player.isPlayingAd()) { + return; + } + int windowIndex = player.getCurrentWindowIndex(); + int nextWindowIndex = player.getNextWindowIndex(); + if (nextWindowIndex != C.INDEX_UNSET) { + seekTo(player, nextWindowIndex, C.TIME_UNSET); + } else if (timeline.getWindow(windowIndex, window).isDynamic) { + seekTo(player, windowIndex, C.TIME_UNSET); + } + } + + private void rewind(Player player) { + if (player.isCurrentWindowSeekable() && rewindMs > 0) { + seekTo(player, Math.max(player.getCurrentPosition() - rewindMs, 0)); + } + } + + private void fastForward(Player player) { + if (player.isCurrentWindowSeekable() && fastForwardMs > 0) { + seekTo(player, player.getCurrentPosition() + fastForwardMs); + } + } + + private void seekTo(Player player, long positionMs) { + seekTo(player, player.getCurrentWindowIndex(), positionMs); + } + + private void seekTo(Player player, int windowIndex, long positionMs) { + long duration = player.getDuration(); + if (duration != C.TIME_UNSET) { + positionMs = Math.min(positionMs, duration); + } + positionMs = Math.max(positionMs, 0); + controlDispatcher.dispatchSeekTo(player, windowIndex, positionMs); + } + private static PendingIntent createBroadcastIntent( String action, Context context, int instanceId) { Intent intent = new Intent(action).setPackage(context.getPackageName()); @@ -1119,13 +1186,6 @@ public class PlayerNotificationManager { private class NotificationBroadcastReceiver extends BroadcastReceiver { - private final Timeline.Window window; - - /** Creates the broadcast receiver. */ - public NotificationBroadcastReceiver() { - window = new Timeline.Window(); - } - @Override public void onReceive(Context context, Intent intent) { Player player = PlayerNotificationManager.this.player; @@ -1137,25 +1197,14 @@ public class PlayerNotificationManager { String action = intent.getAction(); if (ACTION_PLAY.equals(action) || ACTION_PAUSE.equals(action)) { controlDispatcher.dispatchSetPlayWhenReady(player, ACTION_PLAY.equals(action)); - } else if (ACTION_FAST_FORWARD.equals(action) || ACTION_REWIND.equals(action)) { - long increment = ACTION_FAST_FORWARD.equals(action) ? fastForwardMs : -rewindMs; - controlDispatcher.dispatchSeekTo( - player, player.getCurrentWindowIndex(), player.getCurrentPosition() + increment); - } else if (ACTION_NEXT.equals(action)) { - int nextWindowIndex = player.getNextWindowIndex(); - if (nextWindowIndex != C.INDEX_UNSET) { - controlDispatcher.dispatchSeekTo(player, nextWindowIndex, C.TIME_UNSET); - } } else if (ACTION_PREVIOUS.equals(action)) { - player.getCurrentTimeline().getWindow(player.getCurrentWindowIndex(), window); - int previousWindowIndex = player.getPreviousWindowIndex(); - if (previousWindowIndex != C.INDEX_UNSET - && (player.getCurrentPosition() <= MAX_POSITION_FOR_SEEK_TO_PREVIOUS - || (window.isDynamic && !window.isSeekable))) { - controlDispatcher.dispatchSeekTo(player, previousWindowIndex, C.TIME_UNSET); - } else { - controlDispatcher.dispatchSeekTo(player, player.getCurrentWindowIndex(), C.TIME_UNSET); - } + previous(player); + } else if (ACTION_REWIND.equals(action)) { + rewind(player); + } else if (ACTION_FAST_FORWARD.equals(action)) { + fastForward(player); + } else if (ACTION_NEXT.equals(action)) { + next(player); } else if (ACTION_STOP.equals(action)) { controlDispatcher.dispatchStop(player, true); stopNotification();