Commit eb6859e4 by olly Committed by Oliver Woodman

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
parent 75082194
......@@ -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 boolean canDispatchSetRating() {
return player != null && ratingCallback != null;
}
private void rewind() {
if (rewindMs > 0) {
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() {
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);
}
}
......
......@@ -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;
......
......@@ -299,8 +299,9 @@ public class PlayerNotificationManager {
private final Map<String, NotificationCompat.Action> playbackActions;
private final Map<String, NotificationCompat.Action> 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<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<>();
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<String> 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();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment