Align navigation implementation across UI components

This change also paves the way for splitting out functionality into a utility class.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=221070262
This commit is contained in:
olly 2018-11-12 04:24:51 -08:00 committed by Oliver Woodman
parent 7508219416
commit eb6859e436
4 changed files with 242 additions and 159 deletions

View File

@ -212,7 +212,7 @@ public final class MediaSessionConnector {
* @param player The player connected to the media session. * @param player The player connected to the media session.
* @return The bitmask of the supported media actions. * @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. * Called when the timeline of the player has changed.
* *
@ -586,7 +586,7 @@ public final class MediaSessionConnector {
public final void invalidateMediaSessionPlaybackState() { public final void invalidateMediaSessionPlaybackState() {
PlaybackStateCompat.Builder builder = new PlaybackStateCompat.Builder(); PlaybackStateCompat.Builder builder = new PlaybackStateCompat.Builder();
if (player == null) { 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()); mediaSession.setPlaybackState(builder.build());
return; return;
} }
@ -622,7 +622,7 @@ public final class MediaSessionConnector {
Bundle extras = new Bundle(); Bundle extras = new Bundle();
extras.putFloat(EXTRAS_PITCH, player.getPlaybackParameters().pitch); extras.putFloat(EXTRAS_PITCH, player.getPlaybackParameters().pitch);
builder builder
.setActions(buildPlaybackActions()) .setActions(buildPlaybackActions(player))
.setActiveQueueItemId(activeQueueItemId) .setActiveQueueItemId(activeQueueItemId)
.setBufferedPosition(player.getBufferedPosition()) .setBufferedPosition(player.getBufferedPosition())
.setState( .setState(
@ -657,21 +657,32 @@ public final class MediaSessionConnector {
commandReceivers.remove(commandReceiver); commandReceivers.remove(commandReceiver);
} }
private long buildPlaybackActions() { private long buildPlaybackActions(Player player) {
long actions = 0; boolean enableSeeking = false;
if (player != null && !player.getCurrentTimeline().isEmpty()) { 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; long playbackActions = BASE_PLAYBACK_ACTIONS;
if (player.isCurrentWindowSeekable()) { if (enableSeeking) {
playbackActions |= PlaybackStateCompat.ACTION_SEEK_TO; playbackActions |= PlaybackStateCompat.ACTION_SEEK_TO;
if (fastForwardMs > 0) { }
if (enableFastForward) {
playbackActions |= PlaybackStateCompat.ACTION_FAST_FORWARD; playbackActions |= PlaybackStateCompat.ACTION_FAST_FORWARD;
} }
if (rewindMs > 0) { if (enableRewind) {
playbackActions |= PlaybackStateCompat.ACTION_REWIND; playbackActions |= PlaybackStateCompat.ACTION_REWIND;
} }
} playbackActions &= enabledPlaybackActions;
actions |= (playbackActions & enabledPlaybackActions);
} long actions = playbackActions;
if (playbackPreparer != null) { if (playbackPreparer != null) {
actions |= (PlaybackPreparer.ACTIONS & playbackPreparer.getSupportedPrepareActions()); actions |= (PlaybackPreparer.ACTIONS & playbackPreparer.getSupportedPrepareActions());
} }
@ -679,7 +690,7 @@ public final class MediaSessionConnector {
actions |= actions |=
(QueueNavigator.ACTIONS & queueNavigator.getSupportedQueueNavigatorActions(player)); (QueueNavigator.ACTIONS & queueNavigator.getSupportedQueueNavigatorActions(player));
} }
if (ratingCallback != null) { if (ratingCallback != null && enableSetRating) {
actions |= PlaybackStateCompat.ACTION_SET_RATING; actions |= PlaybackStateCompat.ACTION_SET_RATING;
} }
return actions; return actions;
@ -699,39 +710,46 @@ public final class MediaSessionConnector {
} }
private boolean canDispatchPlaybackAction(long action) { private boolean canDispatchPlaybackAction(long action) {
return (enabledPlaybackActions & action) != 0; return player != null && (enabledPlaybackActions & action) != 0;
} }
private boolean canDispatchToPlaybackPreparer(long action) { private boolean canDispatchToPlaybackPreparer(long action) {
return playbackPreparer != null return player != null
&& (playbackPreparer.getSupportedPrepareActions() & PlaybackPreparer.ACTIONS & action) != 0; && playbackPreparer != null
&& (playbackPreparer.getSupportedPrepareActions() & action) != 0;
} }
private boolean canDispatchToQueueNavigator(long action) { private boolean canDispatchToQueueNavigator(long action) {
return queueNavigator != null return player != null
&& (queueNavigator.getSupportedQueueNavigatorActions(player) && queueNavigator != null
& QueueNavigator.ACTIONS && (queueNavigator.getSupportedQueueNavigatorActions(player) & action) != 0;
& action)
!= 0;
} }
private void rewind() { private boolean canDispatchSetRating() {
if (rewindMs > 0) { return player != null && ratingCallback != null;
seekTo(player.getCurrentPosition() - rewindMs); }
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() { private void fastForward(Player player) {
if (fastForwardMs > 0) { if (player.isCurrentWindowSeekable() && fastForwardMs > 0) {
seekTo(player.getCurrentPosition() + fastForwardMs); seekTo(player, player.getCurrentPosition() + fastForwardMs);
} }
} }
private void seekTo(long positionMs) { private void seekTo(Player player, long positionMs) {
seekTo(player.getCurrentWindowIndex(), 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(); long durationMs = player.getDuration();
if (durationMs != C.TIME_UNSET) { if (durationMs != C.TIME_UNSET) {
positionMs = Math.min(positionMs, durationMs); positionMs = Math.min(positionMs, durationMs);
@ -930,21 +948,21 @@ public final class MediaSessionConnector {
@Override @Override
public void onSeekTo(long positionMs) { public void onSeekTo(long positionMs) {
if (canDispatchPlaybackAction(PlaybackStateCompat.ACTION_SEEK_TO)) { if (canDispatchPlaybackAction(PlaybackStateCompat.ACTION_SEEK_TO)) {
seekTo(positionMs); seekTo(player, positionMs);
} }
} }
@Override @Override
public void onFastForward() { public void onFastForward() {
if (canDispatchPlaybackAction(PlaybackStateCompat.ACTION_FAST_FORWARD)) { if (canDispatchPlaybackAction(PlaybackStateCompat.ACTION_FAST_FORWARD)) {
fastForward(); fastForward(player);
} }
} }
@Override @Override
public void onRewind() { public void onRewind() {
if (canDispatchPlaybackAction(PlaybackStateCompat.ACTION_REWIND)) { if (canDispatchPlaybackAction(PlaybackStateCompat.ACTION_REWIND)) {
rewind(); rewind(player);
} }
} }
@ -1008,7 +1026,7 @@ public final class MediaSessionConnector {
@Override @Override
public void onCustomAction(@NonNull String action, @Nullable Bundle extras) { 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); customActionMap.get(action).onCustomAction(player, controlDispatcher, action, extras);
invalidateMediaSessionPlaybackState(); invalidateMediaSessionPlaybackState();
} }
@ -1016,12 +1034,14 @@ public final class MediaSessionConnector {
@Override @Override
public void onCommand(String command, Bundle extras, ResultReceiver cb) { public void onCommand(String command, Bundle extras, ResultReceiver cb) {
if (player != null) {
for (int i = 0; i < commandReceivers.size(); i++) { for (int i = 0; i < commandReceivers.size(); i++) {
if (commandReceivers.get(i).onCommand(player, controlDispatcher, command, extras, cb)) { if (commandReceivers.get(i).onCommand(player, controlDispatcher, command, extras, cb)) {
return; return;
} }
} }
} }
}
@Override @Override
public void onPrepare() { public void onPrepare() {
@ -1088,35 +1108,35 @@ public final class MediaSessionConnector {
@Override @Override
public void onSetRating(RatingCompat rating) { public void onSetRating(RatingCompat rating) {
if (ratingCallback != null) { if (canDispatchSetRating()) {
ratingCallback.onSetRating(player, rating); ratingCallback.onSetRating(player, rating);
} }
} }
@Override @Override
public void onSetRating(RatingCompat rating, Bundle extras) { public void onSetRating(RatingCompat rating, Bundle extras) {
if (ratingCallback != null) { if (canDispatchSetRating()) {
ratingCallback.onSetRating(player, rating, extras); ratingCallback.onSetRating(player, rating, extras);
} }
} }
@Override @Override
public void onAddQueueItem(MediaDescriptionCompat description) { public void onAddQueueItem(MediaDescriptionCompat description) {
if (queueEditor != null) { if (canDispatchQueueEdit()) {
queueEditor.onAddQueueItem(player, description); queueEditor.onAddQueueItem(player, description);
} }
} }
@Override @Override
public void onAddQueueItem(MediaDescriptionCompat description, int index) { public void onAddQueueItem(MediaDescriptionCompat description, int index) {
if (queueEditor != null) { if (canDispatchQueueEdit()) {
queueEditor.onAddQueueItem(player, description, index); queueEditor.onAddQueueItem(player, description, index);
} }
} }
@Override @Override
public void onRemoveQueueItem(MediaDescriptionCompat description) { public void onRemoveQueueItem(MediaDescriptionCompat description) {
if (queueEditor != null) { if (canDispatchQueueEdit()) {
queueEditor.onRemoveQueueItem(player, description); queueEditor.onRemoveQueueItem(player, description);
} }
} }

View File

@ -84,21 +84,25 @@ public abstract class TimelineQueueNavigator implements MediaSessionConnector.Qu
@Override @Override
public long getSupportedQueueNavigatorActions(Player player) { public long getSupportedQueueNavigatorActions(Player player) {
if (player == null) { boolean enableSkipTo = false;
return 0; boolean enablePrevious = false;
} boolean enableNext = false;
Timeline timeline = player.getCurrentTimeline(); Timeline timeline = player.getCurrentTimeline();
if (timeline.isEmpty() || player.isPlayingAd()) { if (!timeline.isEmpty() && !player.isPlayingAd()) {
return 0; timeline.getWindow(player.getCurrentWindowIndex(), window);
enableSkipTo = timeline.getWindowCount() > 1;
enablePrevious = window.isSeekable || !window.isDynamic || player.hasPrevious();
enableNext = window.isDynamic || player.hasNext();
} }
long actions = 0; long actions = 0;
if (timeline.getWindowCount() > 1) { if (enableSkipTo) {
actions |= PlaybackStateCompat.ACTION_SKIP_TO_QUEUE_ITEM; actions |= PlaybackStateCompat.ACTION_SKIP_TO_QUEUE_ITEM;
} }
if (window.isSeekable || !window.isDynamic || player.hasPrevious()) { if (enablePrevious) {
actions |= PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS; actions |= PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS;
} }
if (window.isDynamic || player.hasNext()) { if (enableNext) {
actions |= PlaybackStateCompat.ACTION_SKIP_TO_NEXT; actions |= PlaybackStateCompat.ACTION_SKIP_TO_NEXT;
} }
return actions; return actions;

View File

@ -224,10 +224,10 @@ public class PlayerControlView extends FrameLayout {
private final String repeatOneButtonContentDescription; private final String repeatOneButtonContentDescription;
private final String repeatAllButtonContentDescription; private final String repeatAllButtonContentDescription;
private Player player; @Nullable private Player player;
private com.google.android.exoplayer2.ControlDispatcher controlDispatcher; private com.google.android.exoplayer2.ControlDispatcher controlDispatcher;
private VisibilityListener visibilityListener; private VisibilityListener visibilityListener;
private @Nullable PlaybackPreparer playbackPreparer; @Nullable private PlaybackPreparer playbackPreparer;
private boolean isAttachedToWindow; private boolean isAttachedToWindow;
private boolean showMultiWindowTimeBar; 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 * Returns the {@link Player} currently being controlled by this view, or null if no player is
* set. * set.
*/ */
@Nullable
public Player getPlayer() { public Player getPlayer() {
return player; return player;
} }
@ -411,8 +412,8 @@ public class PlayerControlView extends FrameLayout {
* *
* @param extraAdGroupTimesMs The millisecond timestamps of the extra ad markers to show, or * @param extraAdGroupTimesMs The millisecond timestamps of the extra ad markers to show, or
* {@code null} to show no extra ad markers. * {@code null} to show no extra ad markers.
* @param extraPlayedAdGroups Whether each ad has been played, or {@code null} to show no extra ad * @param extraPlayedAdGroups Whether each ad has been played. Must be the same length as {@code
* markers. * extraAdGroupTimesMs}, or {@code null} if {@code extraAdGroupTimesMs} is {@code null}.
*/ */
public void setExtraAdGroupMarkers( public void setExtraAdGroupMarkers(
@Nullable long[] extraAdGroupTimesMs, @Nullable boolean[] extraPlayedAdGroups) { @Nullable long[] extraAdGroupTimesMs, @Nullable boolean[] extraPlayedAdGroups) {
@ -420,6 +421,7 @@ public class PlayerControlView extends FrameLayout {
this.extraAdGroupTimesMs = new long[0]; this.extraAdGroupTimesMs = new long[0];
this.extraPlayedAdGroups = new boolean[0]; this.extraPlayedAdGroups = new boolean[0];
} else { } else {
extraPlayedAdGroups = Assertions.checkNotNull(extraPlayedAdGroups);
Assertions.checkArgument(extraAdGroupTimesMs.length == extraPlayedAdGroups.length); Assertions.checkArgument(extraAdGroupTimesMs.length == extraPlayedAdGroups.length);
this.extraAdGroupTimesMs = extraAdGroupTimesMs; this.extraAdGroupTimesMs = extraAdGroupTimesMs;
this.extraPlayedAdGroups = extraPlayedAdGroups; this.extraPlayedAdGroups = extraPlayedAdGroups;
@ -659,24 +661,30 @@ public class PlayerControlView extends FrameLayout {
if (!isVisible() || !isAttachedToWindow) { if (!isVisible() || !isAttachedToWindow) {
return; return;
} }
Timeline timeline = player != null ? player.getCurrentTimeline() : null; boolean enableSeeking = false;
boolean haveNonEmptyTimeline = timeline != null && !timeline.isEmpty();
boolean isSeekable = false;
boolean enablePrevious = false; boolean enablePrevious = false;
boolean enableRewind = false;
boolean enableFastForward = false;
boolean enableNext = false; boolean enableNext = false;
if (haveNonEmptyTimeline && !player.isPlayingAd()) { if (player != null) {
int windowIndex = player.getCurrentWindowIndex(); Timeline timeline = player.getCurrentTimeline();
timeline.getWindow(windowIndex, window); if (!timeline.isEmpty() && !player.isPlayingAd()) {
isSeekable = window.isSeekable; timeline.getWindow(player.getCurrentWindowIndex(), window);
boolean isSeekable = window.isSeekable;
enableSeeking = isSeekable;
enablePrevious = isSeekable || !window.isDynamic || player.hasPrevious(); enablePrevious = isSeekable || !window.isDynamic || player.hasPrevious();
enableRewind = isSeekable && rewindMs > 0;
enableFastForward = isSeekable && fastForwardMs > 0;
enableNext = window.isDynamic || player.hasNext(); enableNext = window.isDynamic || player.hasNext();
} }
}
setButtonEnabled(enablePrevious, previousButton); setButtonEnabled(enablePrevious, previousButton);
setButtonEnabled(enableRewind, rewindButton);
setButtonEnabled(enableFastForward, fastForwardButton);
setButtonEnabled(enableNext, nextButton); setButtonEnabled(enableNext, nextButton);
setButtonEnabled(fastForwardMs > 0 && isSeekable, fastForwardButton);
setButtonEnabled(rewindMs > 0 && isSeekable, rewindButton);
if (timeBar != null) { if (timeBar != null) {
timeBar.setEnabled(isSeekable); timeBar.setEnabled(enableSeeking);
} }
} }
@ -862,7 +870,7 @@ public class PlayerControlView extends FrameLayout {
view.setVisibility(VISIBLE); view.setVisibility(VISIBLE);
} }
private void previous() { private void previous(Player player) {
Timeline timeline = player.getCurrentTimeline(); Timeline timeline = player.getCurrentTimeline();
if (timeline.isEmpty() || player.isPlayingAd()) { if (timeline.isEmpty() || player.isPlayingAd()) {
return; return;
@ -873,13 +881,13 @@ public class PlayerControlView extends FrameLayout {
if (previousWindowIndex != C.INDEX_UNSET if (previousWindowIndex != C.INDEX_UNSET
&& (player.getCurrentPosition() <= MAX_POSITION_FOR_SEEK_TO_PREVIOUS && (player.getCurrentPosition() <= MAX_POSITION_FOR_SEEK_TO_PREVIOUS
|| (window.isDynamic && !window.isSeekable))) { || (window.isDynamic && !window.isSeekable))) {
seekTo(previousWindowIndex, C.TIME_UNSET); seekTo(player, previousWindowIndex, C.TIME_UNSET);
} else { } else {
seekTo(0); seekTo(player, 0);
} }
} }
private void next() { private void next(Player player) {
Timeline timeline = player.getCurrentTimeline(); Timeline timeline = player.getCurrentTimeline();
if (timeline.isEmpty() || player.isPlayingAd()) { if (timeline.isEmpty() || player.isPlayingAd()) {
return; return;
@ -887,29 +895,29 @@ public class PlayerControlView extends FrameLayout {
int windowIndex = player.getCurrentWindowIndex(); int windowIndex = player.getCurrentWindowIndex();
int nextWindowIndex = player.getNextWindowIndex(); int nextWindowIndex = player.getNextWindowIndex();
if (nextWindowIndex != C.INDEX_UNSET) { if (nextWindowIndex != C.INDEX_UNSET) {
seekTo(nextWindowIndex, C.TIME_UNSET); seekTo(player, nextWindowIndex, C.TIME_UNSET);
} else if (timeline.getWindow(windowIndex, window).isDynamic) { } else if (timeline.getWindow(windowIndex, window).isDynamic) {
seekTo(windowIndex, C.TIME_UNSET); seekTo(player, windowIndex, C.TIME_UNSET);
} }
} }
private void rewind() { private void rewind(Player player) {
if (rewindMs > 0) { if (player.isCurrentWindowSeekable() && rewindMs > 0) {
seekTo(player.getCurrentPosition() - rewindMs); seekTo(player, player.getCurrentPosition() - rewindMs);
} }
} }
private void fastForward() { private void fastForward(Player player) {
if (fastForwardMs > 0) { if (player.isCurrentWindowSeekable() && fastForwardMs > 0) {
seekTo(player.getCurrentPosition() + fastForwardMs); seekTo(player, player.getCurrentPosition() + fastForwardMs);
} }
} }
private void seekTo(long positionMs) { private void seekTo(Player player, long positionMs) {
seekTo(player.getCurrentWindowIndex(), 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(); long durationMs = player.getDuration();
if (durationMs != C.TIME_UNSET) { if (durationMs != C.TIME_UNSET) {
positionMs = Math.min(positionMs, durationMs); positionMs = Math.min(positionMs, durationMs);
@ -918,7 +926,7 @@ public class PlayerControlView extends FrameLayout {
return controlDispatcher.dispatchSeekTo(player, windowIndex, positionMs); return controlDispatcher.dispatchSeekTo(player, windowIndex, positionMs);
} }
private void seekToTimeBarPosition(long positionMs) { private void seekToTimeBarPosition(Player player, long positionMs) {
int windowIndex; int windowIndex;
Timeline timeline = player.getCurrentTimeline(); Timeline timeline = player.getCurrentTimeline();
if (multiWindowTimeBar && !timeline.isEmpty()) { if (multiWindowTimeBar && !timeline.isEmpty()) {
@ -939,7 +947,7 @@ public class PlayerControlView extends FrameLayout {
} else { } else {
windowIndex = player.getCurrentWindowIndex(); windowIndex = player.getCurrentWindowIndex();
} }
boolean dispatched = seekTo(windowIndex, positionMs); boolean dispatched = seekTo(player, windowIndex, positionMs);
if (!dispatched) { if (!dispatched) {
// The seek wasn't dispatched then the progress bar scrubber will be in the wrong position. // The seek wasn't dispatched then the progress bar scrubber will be in the wrong position.
// Trigger a progress update to snap it back. // Trigger a progress update to snap it back.
@ -1001,9 +1009,9 @@ public class PlayerControlView extends FrameLayout {
} }
if (event.getAction() == KeyEvent.ACTION_DOWN) { if (event.getAction() == KeyEvent.ACTION_DOWN) {
if (keyCode == KeyEvent.KEYCODE_MEDIA_FAST_FORWARD) { if (keyCode == KeyEvent.KEYCODE_MEDIA_FAST_FORWARD) {
fastForward(); fastForward(player);
} else if (keyCode == KeyEvent.KEYCODE_MEDIA_REWIND) { } else if (keyCode == KeyEvent.KEYCODE_MEDIA_REWIND) {
rewind(); rewind(player);
} else if (event.getRepeatCount() == 0) { } else if (event.getRepeatCount() == 0) {
switch (keyCode) { switch (keyCode) {
case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
@ -1016,10 +1024,10 @@ public class PlayerControlView extends FrameLayout {
controlDispatcher.dispatchSetPlayWhenReady(player, false); controlDispatcher.dispatchSetPlayWhenReady(player, false);
break; break;
case KeyEvent.KEYCODE_MEDIA_NEXT: case KeyEvent.KEYCODE_MEDIA_NEXT:
next(); next(player);
break; break;
case KeyEvent.KEYCODE_MEDIA_PREVIOUS: case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
previous(); previous(player);
break; break;
default: default:
break; break;
@ -1086,7 +1094,7 @@ public class PlayerControlView extends FrameLayout {
public void onScrubStop(TimeBar timeBar, long position, boolean canceled) { public void onScrubStop(TimeBar timeBar, long position, boolean canceled) {
scrubbing = false; scrubbing = false;
if (!canceled && player != null) { if (!canceled && player != null) {
seekToTimeBarPosition(position); seekToTimeBarPosition(player, position);
} }
} }
@ -1124,15 +1132,18 @@ public class PlayerControlView extends FrameLayout {
@Override @Override
public void onClick(View view) { public void onClick(View view) {
if (player != null) { Player player = PlayerControlView.this.player;
if (player == null) {
return;
}
if (nextButton == view) { if (nextButton == view) {
next(); next(player);
} else if (previousButton == view) { } else if (previousButton == view) {
previous(); previous(player);
} else if (fastForwardButton == view) { } else if (fastForwardButton == view) {
fastForward(); fastForward(player);
} else if (rewindButton == view) { } else if (rewindButton == view) {
rewind(); rewind(player);
} else if (playButton == view) { } else if (playButton == view) {
if (player.getPlaybackState() == Player.STATE_IDLE) { if (player.getPlaybackState() == Player.STATE_IDLE) {
if (playbackPreparer != null) { if (playbackPreparer != null) {
@ -1152,5 +1163,4 @@ public class PlayerControlView extends FrameLayout {
} }
} }
} }
}
} }

View File

@ -299,8 +299,9 @@ public class PlayerNotificationManager {
private final Map<String, NotificationCompat.Action> playbackActions; private final Map<String, NotificationCompat.Action> playbackActions;
private final Map<String, NotificationCompat.Action> customActions; private final Map<String, NotificationCompat.Action> customActions;
private final int instanceId; private final int instanceId;
private final Timeline.Window window;
private @Nullable Player player; @Nullable private Player player;
private ControlDispatcher controlDispatcher; private ControlDispatcher controlDispatcher;
private boolean isNotificationStarted; private boolean isNotificationStarted;
private int currentNotificationTag; private int currentNotificationTag;
@ -485,7 +486,8 @@ public class PlayerNotificationManager {
this.mediaDescriptionAdapter = mediaDescriptionAdapter; this.mediaDescriptionAdapter = mediaDescriptionAdapter;
this.notificationListener = notificationListener; this.notificationListener = notificationListener;
this.customActionReceiver = customActionReceiver; this.customActionReceiver = customActionReceiver;
this.controlDispatcher = new DefaultControlDispatcher(); controlDispatcher = new DefaultControlDispatcher();
window = new Timeline.Window();
instanceId = instanceIdCounter++; instanceId = instanceIdCounter++;
mainHandler = new Handler(Looper.getMainLooper()); mainHandler = new Handler(Looper.getMainLooper());
notificationManager = NotificationManagerCompat.from(context); notificationManager = NotificationManagerCompat.from(context);
@ -968,16 +970,26 @@ public class PlayerNotificationManager {
* action name is ignored. * action name is ignored.
*/ */
protected List<String> getActions(Player player) { protected List<String> 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<String> stringActions = new ArrayList<>(); List<String> stringActions = new ArrayList<>();
if (!isPlayingAd) { if (useNavigationActions && enablePrevious) {
if (useNavigationActions) {
stringActions.add(ACTION_PREVIOUS); stringActions.add(ACTION_PREVIOUS);
} }
if (rewindMs > 0) { if (enableRewind) {
stringActions.add(ACTION_REWIND); stringActions.add(ACTION_REWIND);
} }
}
if (usePlayPauseActions) { if (usePlayPauseActions) {
if (player.getPlayWhenReady()) { if (player.getPlayWhenReady()) {
stringActions.add(ACTION_PAUSE); stringActions.add(ACTION_PAUSE);
@ -985,14 +997,12 @@ public class PlayerNotificationManager {
stringActions.add(ACTION_PLAY); stringActions.add(ACTION_PLAY);
} }
} }
if (!isPlayingAd) { if (enableFastForward) {
if (fastForwardMs > 0) {
stringActions.add(ACTION_FAST_FORWARD); stringActions.add(ACTION_FAST_FORWARD);
} }
if (useNavigationActions && player.getNextWindowIndex() != C.INDEX_UNSET) { if (useNavigationActions && enableNext) {
stringActions.add(ACTION_NEXT); stringActions.add(ACTION_NEXT);
} }
}
if (customActionReceiver != null) { if (customActionReceiver != null) {
stringActions.addAll(customActionReceiver.getCustomActions(player)); stringActions.addAll(customActionReceiver.getCustomActions(player));
} }
@ -1011,6 +1021,7 @@ public class PlayerNotificationManager {
* @param actionNames The names of the actions included in the notification. * @param actionNames The names of the actions included in the notification.
* @param player The player for which state to build a notification. * @param player The player for which state to build a notification.
*/ */
@SuppressWarnings("unused")
protected int[] getActionIndicesForCompactView(List<String> actionNames, Player player) { protected int[] getActionIndicesForCompactView(List<String> actionNames, Player player) {
int pauseActionIndex = actionNames.indexOf(ACTION_PAUSE); int pauseActionIndex = actionNames.indexOf(ACTION_PAUSE);
int playActionIndex = actionNames.indexOf(ACTION_PLAY); int playActionIndex = actionNames.indexOf(ACTION_PLAY);
@ -1067,6 +1078,62 @@ public class PlayerNotificationManager {
return actions; 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( private static PendingIntent createBroadcastIntent(
String action, Context context, int instanceId) { String action, Context context, int instanceId) {
Intent intent = new Intent(action).setPackage(context.getPackageName()); Intent intent = new Intent(action).setPackage(context.getPackageName());
@ -1119,13 +1186,6 @@ public class PlayerNotificationManager {
private class NotificationBroadcastReceiver extends BroadcastReceiver { private class NotificationBroadcastReceiver extends BroadcastReceiver {
private final Timeline.Window window;
/** Creates the broadcast receiver. */
public NotificationBroadcastReceiver() {
window = new Timeline.Window();
}
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
Player player = PlayerNotificationManager.this.player; Player player = PlayerNotificationManager.this.player;
@ -1137,25 +1197,14 @@ public class PlayerNotificationManager {
String action = intent.getAction(); String action = intent.getAction();
if (ACTION_PLAY.equals(action) || ACTION_PAUSE.equals(action)) { if (ACTION_PLAY.equals(action) || ACTION_PAUSE.equals(action)) {
controlDispatcher.dispatchSetPlayWhenReady(player, ACTION_PLAY.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)) { } else if (ACTION_PREVIOUS.equals(action)) {
player.getCurrentTimeline().getWindow(player.getCurrentWindowIndex(), window); previous(player);
int previousWindowIndex = player.getPreviousWindowIndex(); } else if (ACTION_REWIND.equals(action)) {
if (previousWindowIndex != C.INDEX_UNSET rewind(player);
&& (player.getCurrentPosition() <= MAX_POSITION_FOR_SEEK_TO_PREVIOUS } else if (ACTION_FAST_FORWARD.equals(action)) {
|| (window.isDynamic && !window.isSeekable))) { fastForward(player);
controlDispatcher.dispatchSeekTo(player, previousWindowIndex, C.TIME_UNSET); } else if (ACTION_NEXT.equals(action)) {
} else { next(player);
controlDispatcher.dispatchSeekTo(player, player.getCurrentWindowIndex(), C.TIME_UNSET);
}
} else if (ACTION_STOP.equals(action)) { } else if (ACTION_STOP.equals(action)) {
controlDispatcher.dispatchStop(player, true); controlDispatcher.dispatchStop(player, true);
stopNotification(); stopNotification();