add next, previous, fastForward and rewind to ControlDispatcher

Issue: #6926, #6934
PiperOrigin-RevId: 293315532
This commit is contained in:
bachinger 2020-02-05 08:49:06 +00:00 committed by kim-vde
parent 0f6bf1525d
commit e1c48515eb
8 changed files with 270 additions and 292 deletions

View File

@ -39,6 +39,9 @@
* Testing * Testing
* Upgrade Truth dependency from 0.44 to 1.0. * Upgrade Truth dependency from 0.44 to 1.0.
* Upgrade to JUnit 4.13-rc-2. * Upgrade to JUnit 4.13-rc-2.
* UI
* move logic of prev, next, fast forward and rewind to ControlDispatcher
[#6926](https://github.com/google/ExoPlayer/issues/6926)).
### 2.11.2 (TBD) ### ### 2.11.2 (TBD) ###

View File

@ -127,11 +127,6 @@ public final class MediaSessionConnector {
/** The default playback actions. */ /** The default playback actions. */
@PlaybackActions public static final long DEFAULT_PLAYBACK_ACTIONS = ALL_PLAYBACK_ACTIONS; @PlaybackActions public static final long DEFAULT_PLAYBACK_ACTIONS = ALL_PLAYBACK_ACTIONS;
/** The default fast forward increment, in milliseconds. */
public static final int DEFAULT_FAST_FORWARD_MS = 15000;
/** The default rewind increment, in milliseconds. */
public static final int DEFAULT_REWIND_MS = 5000;
/** /**
* The name of the {@link PlaybackStateCompat} float extra with the value of {@link * The name of the {@link PlaybackStateCompat} float extra with the value of {@link
* PlaybackParameters#speed}. * PlaybackParameters#speed}.
@ -440,8 +435,6 @@ public final class MediaSessionConnector {
@Nullable private MediaButtonEventHandler mediaButtonEventHandler; @Nullable private MediaButtonEventHandler mediaButtonEventHandler;
private long enabledPlaybackActions; private long enabledPlaybackActions;
private int rewindMs;
private int fastForwardMs;
/** /**
* Creates an instance. * Creates an instance.
@ -461,8 +454,6 @@ public final class MediaSessionConnector {
new DefaultMediaMetadataProvider( new DefaultMediaMetadataProvider(
mediaSession.getController(), /* metadataExtrasPrefix= */ null); mediaSession.getController(), /* metadataExtrasPrefix= */ null);
enabledPlaybackActions = DEFAULT_PLAYBACK_ACTIONS; enabledPlaybackActions = DEFAULT_PLAYBACK_ACTIONS;
rewindMs = DEFAULT_REWIND_MS;
fastForwardMs = DEFAULT_FAST_FORWARD_MS;
mediaSession.setFlags(BASE_MEDIA_SESSION_FLAGS); mediaSession.setFlags(BASE_MEDIA_SESSION_FLAGS);
mediaSession.setCallback(componentListener, new Handler(looper)); mediaSession.setCallback(componentListener, new Handler(looper));
} }
@ -504,13 +495,12 @@ public final class MediaSessionConnector {
/** /**
* Sets the {@link ControlDispatcher}. * Sets the {@link ControlDispatcher}.
* *
* @param controlDispatcher The {@link ControlDispatcher}, or null to use {@link * @param controlDispatcher The {@link ControlDispatcher}.
* DefaultControlDispatcher}.
*/ */
public void setControlDispatcher(@Nullable ControlDispatcher controlDispatcher) { public void setControlDispatcher(ControlDispatcher controlDispatcher) {
if (this.controlDispatcher != controlDispatcher) { if (this.controlDispatcher != controlDispatcher) {
this.controlDispatcher = this.controlDispatcher = controlDispatcher;
controlDispatcher == null ? new DefaultControlDispatcher() : controlDispatcher; invalidateMediaSessionPlaybackState();
} }
} }
@ -551,27 +541,27 @@ public final class MediaSessionConnector {
} }
/** /**
* Sets the rewind increment in milliseconds. * @deprecated Use {@link #setControlDispatcher(ControlDispatcher)} with {@link
* * DefaultControlDispatcher#DefaultControlDispatcher(long, long)} instead.
* @param rewindMs The rewind increment in milliseconds. A non-positive value will cause the
* rewind button to be disabled.
*/ */
@SuppressWarnings("deprecation")
@Deprecated
public void setRewindIncrementMs(int rewindMs) { public void setRewindIncrementMs(int rewindMs) {
if (this.rewindMs != rewindMs) { if (controlDispatcher instanceof DefaultControlDispatcher) {
this.rewindMs = rewindMs; ((DefaultControlDispatcher) controlDispatcher).setRewindIncrementMs(rewindMs);
invalidateMediaSessionPlaybackState(); invalidateMediaSessionPlaybackState();
} }
} }
/** /**
* Sets the fast forward increment in milliseconds. * @deprecated Use {@link #setControlDispatcher(ControlDispatcher)} with {@link
* * DefaultControlDispatcher#DefaultControlDispatcher(long, long)} instead.
* @param fastForwardMs The fast forward increment in milliseconds. A non-positive value will
* cause the fast forward button to be disabled.
*/ */
@SuppressWarnings("deprecation")
@Deprecated
public void setFastForwardIncrementMs(int fastForwardMs) { public void setFastForwardIncrementMs(int fastForwardMs) {
if (this.fastForwardMs != fastForwardMs) { if (controlDispatcher instanceof DefaultControlDispatcher) {
this.fastForwardMs = fastForwardMs; ((DefaultControlDispatcher) controlDispatcher).setFastForwardIncrementMs(fastForwardMs);
invalidateMediaSessionPlaybackState(); invalidateMediaSessionPlaybackState();
} }
} }
@ -875,8 +865,8 @@ public final class MediaSessionConnector {
Timeline timeline = player.getCurrentTimeline(); Timeline timeline = player.getCurrentTimeline();
if (!timeline.isEmpty() && !player.isPlayingAd()) { if (!timeline.isEmpty() && !player.isPlayingAd()) {
enableSeeking = player.isCurrentWindowSeekable(); enableSeeking = player.isCurrentWindowSeekable();
enableRewind = enableSeeking && rewindMs > 0; enableRewind = enableSeeking && controlDispatcher.isRewindEnabled();
enableFastForward = enableSeeking && fastForwardMs > 0; enableFastForward = enableSeeking && controlDispatcher.isFastForwardEnabled();
enableSetRating = ratingCallback != null; enableSetRating = ratingCallback != null;
enableSetCaptioningEnabled = captionCallback != null && captionCallback.hasCaptions(player); enableSetCaptioningEnabled = captionCallback != null && captionCallback.hasCaptions(player);
} }
@ -955,28 +945,6 @@ public final class MediaSessionConnector {
return player != null && mediaButtonEventHandler != null; return player != null && mediaButtonEventHandler != null;
} }
private void rewind(Player player) {
if (player.isCurrentWindowSeekable() && rewindMs > 0) {
seekToOffset(player, /* offsetMs= */ -rewindMs);
}
}
private void fastForward(Player player) {
if (player.isCurrentWindowSeekable() && fastForwardMs > 0) {
seekToOffset(player, /* offsetMs= */ fastForwardMs);
}
}
private void seekToOffset(Player player, long offsetMs) {
long positionMs = player.getCurrentPosition() + offsetMs;
long durationMs = player.getDuration();
if (durationMs != C.TIME_UNSET) {
positionMs = Math.min(positionMs, durationMs);
}
positionMs = Math.max(positionMs, 0);
seekTo(player, player.getCurrentWindowIndex(), positionMs);
}
private void seekTo(Player player, int windowIndex, long positionMs) { private void seekTo(Player player, int windowIndex, long positionMs) {
controlDispatcher.dispatchSeekTo(player, windowIndex, positionMs); controlDispatcher.dispatchSeekTo(player, windowIndex, positionMs);
} }
@ -1203,14 +1171,14 @@ public final class MediaSessionConnector {
@Override @Override
public void onFastForward() { public void onFastForward() {
if (canDispatchPlaybackAction(PlaybackStateCompat.ACTION_FAST_FORWARD)) { if (canDispatchPlaybackAction(PlaybackStateCompat.ACTION_FAST_FORWARD)) {
fastForward(player); controlDispatcher.dispatchFastForward(player);
} }
} }
@Override @Override
public void onRewind() { public void onRewind() {
if (canDispatchPlaybackAction(PlaybackStateCompat.ACTION_REWIND)) { if (canDispatchPlaybackAction(PlaybackStateCompat.ACTION_REWIND)) {
rewind(player); controlDispatcher.dispatchRewind(player);
} }
} }

View File

@ -36,7 +36,6 @@ import java.util.Collections;
*/ */
public abstract class TimelineQueueNavigator implements MediaSessionConnector.QueueNavigator { public abstract class TimelineQueueNavigator implements MediaSessionConnector.QueueNavigator {
public static final long MAX_POSITION_FOR_SEEK_TO_PREVIOUS = 3000;
public static final int DEFAULT_MAX_QUEUE_SIZE = 10; public static final int DEFAULT_MAX_QUEUE_SIZE = 10;
private final MediaSessionCompat mediaSession; private final MediaSessionCompat mediaSession;
@ -136,20 +135,7 @@ public abstract class TimelineQueueNavigator implements MediaSessionConnector.Qu
@Override @Override
public void onSkipToPrevious(Player player, ControlDispatcher controlDispatcher) { public void onSkipToPrevious(Player player, ControlDispatcher controlDispatcher) {
Timeline timeline = player.getCurrentTimeline(); controlDispatcher.dispatchPrevious(player);
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))) {
controlDispatcher.dispatchSeekTo(player, previousWindowIndex, C.TIME_UNSET);
} else {
controlDispatcher.dispatchSeekTo(player, windowIndex, 0);
}
} }
@Override @Override
@ -166,17 +152,7 @@ public abstract class TimelineQueueNavigator implements MediaSessionConnector.Qu
@Override @Override
public void onSkipToNext(Player player, ControlDispatcher controlDispatcher) { public void onSkipToNext(Player player, ControlDispatcher controlDispatcher) {
Timeline timeline = player.getCurrentTimeline(); controlDispatcher.dispatchNext(player);
if (timeline.isEmpty() || player.isPlayingAd()) {
return;
}
int windowIndex = player.getCurrentWindowIndex();
int nextWindowIndex = player.getNextWindowIndex();
if (nextWindowIndex != C.INDEX_UNSET) {
controlDispatcher.dispatchSeekTo(player, nextWindowIndex, C.TIME_UNSET);
} else if (timeline.getWindow(windowIndex, window).isDynamic) {
controlDispatcher.dispatchSeekTo(player, windowIndex, C.TIME_UNSET);
}
} }
// CommandReceiver implementation. // CommandReceiver implementation.

View File

@ -46,6 +46,38 @@ public interface ControlDispatcher {
*/ */
boolean dispatchSeekTo(Player player, int windowIndex, long positionMs); boolean dispatchSeekTo(Player player, int windowIndex, long positionMs);
/**
* Dispatches a {@link Player#previous()} operation.
*
* @param player The {@link Player} to which the operation should be dispatched.
* @return True if the operation was dispatched. False if suppressed.
*/
boolean dispatchPrevious(Player player);
/**
* Dispatches a {@link Player#next()} operation.
*
* @param player The {@link Player} to which the operation should be dispatched.
* @return True if the operation was dispatched. False if suppressed.
*/
boolean dispatchNext(Player player);
/**
* Dispatches a rewind operation.
*
* @param player The {@link Player} to which the operation should be dispatched.
* @return True if the operation was dispatched. False if suppressed.
*/
boolean dispatchRewind(Player player);
/**
* Dispatches a fast forward operation.
*
* @param player The {@link Player} to which the operation should be dispatched.
* @return True if the operation was dispatched. False if suppressed.
*/
boolean dispatchFastForward(Player player);
/** /**
* Dispatches a {@link Player#setRepeatMode(int)} operation. * Dispatches a {@link Player#setRepeatMode(int)} operation.
* *
@ -72,4 +104,10 @@ public interface ControlDispatcher {
* @return True if the operation was dispatched. False if suppressed. * @return True if the operation was dispatched. False if suppressed.
*/ */
boolean dispatchStop(Player player, boolean reset); boolean dispatchStop(Player player, boolean reset);
/** Returns {@code true} if rewind is enabled, {@code false} otherwise. */
boolean isRewindEnabled();
/** Returns {@code true} if fast forward is enabled, {@code false} otherwise. */
boolean isFastForwardEnabled();
} }

View File

@ -15,14 +15,40 @@
*/ */
package com.google.android.exoplayer2; package com.google.android.exoplayer2;
import com.google.android.exoplayer2.Player.RepeatMode; /** Default {@link ControlDispatcher}. */
/**
* Default {@link ControlDispatcher} that dispatches all operations to the player without
* modification.
*/
public class DefaultControlDispatcher implements ControlDispatcher { public class DefaultControlDispatcher implements ControlDispatcher {
/** The default fast forward increment, in milliseconds. */
public static final int DEFAULT_FAST_FORWARD_MS = 15000;
/** The default rewind increment, in milliseconds. */
public static final int DEFAULT_REWIND_MS = 5000;
private static final int MAX_POSITION_FOR_SEEK_TO_PREVIOUS = 3000;
private final Timeline.Window window;
private long rewindIncrementMs;
private long fastForwardIncrementMs;
/** Creates an instance. */
public DefaultControlDispatcher() {
this(DEFAULT_FAST_FORWARD_MS, DEFAULT_REWIND_MS);
}
/**
* Creates an instance with the given increments.
*
* @param fastForwardIncrementMs The fast forward increment in milliseconds. A non-positive value
* disables the fast forward operation.
* @param rewindIncrementMs The rewind increment in milliseconds. A non-positive value disables
* the rewind operation.
*/
public DefaultControlDispatcher(long fastForwardIncrementMs, long rewindIncrementMs) {
this.fastForwardIncrementMs = fastForwardIncrementMs;
this.rewindIncrementMs = rewindIncrementMs;
window = new Timeline.Window();
}
@Override @Override
public boolean dispatchSetPlayWhenReady(Player player, boolean playWhenReady) { public boolean dispatchSetPlayWhenReady(Player player, boolean playWhenReady) {
player.setPlayWhenReady(playWhenReady); player.setPlayWhenReady(playWhenReady);
@ -36,7 +62,58 @@ public class DefaultControlDispatcher implements ControlDispatcher {
} }
@Override @Override
public boolean dispatchSetRepeatMode(Player player, @RepeatMode int repeatMode) { public boolean dispatchPrevious(Player player) {
Timeline timeline = player.getCurrentTimeline();
if (timeline.isEmpty() || player.isPlayingAd()) {
return true;
}
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))) {
player.seekTo(previousWindowIndex, C.TIME_UNSET);
} else {
player.seekTo(windowIndex, /* positionMs= */ 0);
}
return true;
}
@Override
public boolean dispatchNext(Player player) {
Timeline timeline = player.getCurrentTimeline();
if (timeline.isEmpty() || player.isPlayingAd()) {
return true;
}
int windowIndex = player.getCurrentWindowIndex();
int nextWindowIndex = player.getNextWindowIndex();
if (nextWindowIndex != C.INDEX_UNSET) {
player.seekTo(nextWindowIndex, C.TIME_UNSET);
} else if (timeline.getWindow(windowIndex, window).isLive) {
player.seekTo(windowIndex, C.TIME_UNSET);
}
return true;
}
@Override
public boolean dispatchRewind(Player player) {
if (isRewindEnabled() && player.isCurrentWindowSeekable()) {
seekToOffset(player, -rewindIncrementMs);
}
return true;
}
@Override
public boolean dispatchFastForward(Player player) {
if (isFastForwardEnabled() && player.isCurrentWindowSeekable()) {
seekToOffset(player, fastForwardIncrementMs);
}
return true;
}
@Override
public boolean dispatchSetRepeatMode(Player player, @Player.RepeatMode int repeatMode) {
player.setRepeatMode(repeatMode); player.setRepeatMode(repeatMode);
return true; return true;
} }
@ -52,4 +129,44 @@ public class DefaultControlDispatcher implements ControlDispatcher {
player.stop(reset); player.stop(reset);
return true; return true;
} }
@Override
public boolean isRewindEnabled() {
return rewindIncrementMs > 0;
}
@Override
public boolean isFastForwardEnabled() {
return fastForwardIncrementMs > 0;
}
/**
* @deprecated Create a new instance instead and pass the new instance to the UI component. This
* makes sure the UI gets updated and is in sync with the new values.
*/
@Deprecated
public void setRewindIncrementMs(long rewindMs) {
this.rewindIncrementMs = rewindMs;
}
/**
* @deprecated Create a new instance instead and pass the new instance to the UI component. This
* makes sure the UI gets updated and is in sync with the new values.
*/
@Deprecated
public void setFastForwardIncrementMs(long fastForwardMs) {
this.fastForwardIncrementMs = fastForwardMs;
}
// Internal methods.
private static void seekToOffset(Player player, long offsetMs) {
long positionMs = player.getCurrentPosition() + offsetMs;
long durationMs = player.getDuration();
if (durationMs != C.TIME_UNSET) {
positionMs = Math.min(positionMs, durationMs);
}
positionMs = Math.max(positionMs, 0);
player.seekTo(player.getCurrentWindowIndex(), positionMs);
}
} }

View File

@ -33,6 +33,8 @@ import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ControlDispatcher;
import com.google.android.exoplayer2.DefaultControlDispatcher;
import com.google.android.exoplayer2.ExoPlayerLibraryInfo; import com.google.android.exoplayer2.ExoPlayerLibraryInfo;
import com.google.android.exoplayer2.PlaybackPreparer; import com.google.android.exoplayer2.PlaybackPreparer;
import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Player;
@ -67,13 +69,13 @@ import java.util.concurrent.CopyOnWriteArrayList;
* <li><b>{@code rewind_increment}</b> - The duration of the rewind applied when the user taps the * <li><b>{@code rewind_increment}</b> - The duration of the rewind applied when the user taps the
* rewind button, in milliseconds. Use zero to disable the rewind button. * rewind button, in milliseconds. Use zero to disable the rewind button.
* <ul> * <ul>
* <li>Corresponding method: {@link #setRewindIncrementMs(int)} * <li>Corresponding method: {@link #setControlDispatcher(ControlDispatcher)}
* <li>Default: {@link #DEFAULT_REWIND_MS} * <li>Default: {@link DefaultControlDispatcher#DEFAULT_REWIND_MS}
* </ul> * </ul>
* <li><b>{@code fastforward_increment}</b> - Like {@code rewind_increment}, but for fast forward. * <li><b>{@code fastforward_increment}</b> - Like {@code rewind_increment}, but for fast forward.
* <ul> * <ul>
* <li>Corresponding method: {@link #setFastForwardIncrementMs(int)} * <li>Corresponding method: {@link #setControlDispatcher(ControlDispatcher)}
* <li>Default: {@link #DEFAULT_FAST_FORWARD_MS} * <li>Default: {@link DefaultControlDispatcher#DEFAULT_FAST_FORWARD_MS}
* </ul> * </ul>
* <li><b>{@code repeat_toggle_modes}</b> - A flagged enumeration value specifying which repeat * <li><b>{@code repeat_toggle_modes}</b> - A flagged enumeration value specifying which repeat
* mode toggle options are enabled. Valid values are: {@code none}, {@code one}, {@code all}, * mode toggle options are enabled. Valid values are: {@code none}, {@code one}, {@code all},
@ -246,10 +248,6 @@ public class PlayerControlView extends FrameLayout {
void onProgressUpdate(long position, long bufferedPosition); void onProgressUpdate(long position, long bufferedPosition);
} }
/** The default fast forward increment, in milliseconds. */
public static final int DEFAULT_FAST_FORWARD_MS = 15000;
/** The default rewind increment, in milliseconds. */
public static final int DEFAULT_REWIND_MS = 5000;
/** The default show timeout, in milliseconds. */ /** The default show timeout, in milliseconds. */
public static final int DEFAULT_SHOW_TIMEOUT_MS = 5000; public static final int DEFAULT_SHOW_TIMEOUT_MS = 5000;
/** The default repeat toggle modes. */ /** The default repeat toggle modes. */
@ -260,7 +258,6 @@ public class PlayerControlView extends FrameLayout {
/** The maximum number of windows that can be shown in a multi-window time bar. */ /** The maximum number of windows that can be shown in a multi-window time bar. */
public static final int MAX_WINDOWS_FOR_MULTI_WINDOW_TIME_BAR = 100; public static final int MAX_WINDOWS_FOR_MULTI_WINDOW_TIME_BAR = 100;
private static final long MAX_POSITION_FOR_SEEK_TO_PREVIOUS = 3000;
/** The maximum interval between time bar position updates. */ /** The maximum interval between time bar position updates. */
private static final int MAX_UPDATE_INTERVAL_MS = 1000; private static final int MAX_UPDATE_INTERVAL_MS = 1000;
@ -307,8 +304,6 @@ public class PlayerControlView extends FrameLayout {
private boolean showMultiWindowTimeBar; private boolean showMultiWindowTimeBar;
private boolean multiWindowTimeBar; private boolean multiWindowTimeBar;
private boolean scrubbing; private boolean scrubbing;
private int rewindMs;
private int fastForwardMs;
private int showTimeoutMs; private int showTimeoutMs;
private int timeBarMinUpdateIntervalMs; private int timeBarMinUpdateIntervalMs;
private @RepeatModeUtil.RepeatToggleModes int repeatToggleModes; private @RepeatModeUtil.RepeatToggleModes int repeatToggleModes;
@ -344,13 +339,13 @@ public class PlayerControlView extends FrameLayout {
@Nullable AttributeSet playbackAttrs) { @Nullable AttributeSet playbackAttrs) {
super(context, attrs, defStyleAttr); super(context, attrs, defStyleAttr);
int controllerLayoutId = R.layout.exo_player_control_view; int controllerLayoutId = R.layout.exo_player_control_view;
rewindMs = DEFAULT_REWIND_MS;
fastForwardMs = DEFAULT_FAST_FORWARD_MS;
showTimeoutMs = DEFAULT_SHOW_TIMEOUT_MS; showTimeoutMs = DEFAULT_SHOW_TIMEOUT_MS;
repeatToggleModes = DEFAULT_REPEAT_TOGGLE_MODES; repeatToggleModes = DEFAULT_REPEAT_TOGGLE_MODES;
timeBarMinUpdateIntervalMs = DEFAULT_TIME_BAR_MIN_UPDATE_INTERVAL_MS; timeBarMinUpdateIntervalMs = DEFAULT_TIME_BAR_MIN_UPDATE_INTERVAL_MS;
hideAtMs = C.TIME_UNSET; hideAtMs = C.TIME_UNSET;
showShuffleButton = false; showShuffleButton = false;
int rewindMs = DefaultControlDispatcher.DEFAULT_REWIND_MS;
int fastForwardMs = DefaultControlDispatcher.DEFAULT_FAST_FORWARD_MS;
if (playbackAttrs != null) { if (playbackAttrs != null) {
TypedArray a = TypedArray a =
context context
@ -384,7 +379,8 @@ public class PlayerControlView extends FrameLayout {
extraAdGroupTimesMs = new long[0]; extraAdGroupTimesMs = new long[0];
extraPlayedAdGroups = new boolean[0]; extraPlayedAdGroups = new boolean[0];
componentListener = new ComponentListener(); componentListener = new ComponentListener();
controlDispatcher = new com.google.android.exoplayer2.DefaultControlDispatcher(); controlDispatcher =
new com.google.android.exoplayer2.DefaultControlDispatcher(fastForwardMs, rewindMs);
updateProgressAction = this::updateProgress; updateProgressAction = this::updateProgress;
hideAction = this::hide; hideAction = this::hide;
@ -589,37 +585,39 @@ public class PlayerControlView extends FrameLayout {
/** /**
* Sets the {@link com.google.android.exoplayer2.ControlDispatcher}. * Sets the {@link com.google.android.exoplayer2.ControlDispatcher}.
* *
* @param controlDispatcher The {@link com.google.android.exoplayer2.ControlDispatcher}, or null * @param controlDispatcher The {@link com.google.android.exoplayer2.ControlDispatcher}.
* to use {@link com.google.android.exoplayer2.DefaultControlDispatcher}.
*/ */
public void setControlDispatcher( public void setControlDispatcher(ControlDispatcher controlDispatcher) {
@Nullable com.google.android.exoplayer2.ControlDispatcher controlDispatcher) { if (this.controlDispatcher != controlDispatcher) {
this.controlDispatcher = this.controlDispatcher = controlDispatcher;
controlDispatcher == null updateNavigation();
? new com.google.android.exoplayer2.DefaultControlDispatcher() }
: controlDispatcher;
} }
/** /**
* Sets the rewind increment in milliseconds. * @deprecated Use {@link #setControlDispatcher(ControlDispatcher)} with {@link
* * DefaultControlDispatcher#DefaultControlDispatcher(long, long)}.
* @param rewindMs The rewind increment in milliseconds. A non-positive value will cause the
* rewind button to be disabled.
*/ */
@SuppressWarnings("deprecation")
@Deprecated
public void setRewindIncrementMs(int rewindMs) { public void setRewindIncrementMs(int rewindMs) {
this.rewindMs = rewindMs; if (controlDispatcher instanceof DefaultControlDispatcher) {
updateNavigation(); ((DefaultControlDispatcher) controlDispatcher).setRewindIncrementMs(rewindMs);
updateNavigation();
}
} }
/** /**
* Sets the fast forward increment in milliseconds. * @deprecated Use {@link #setControlDispatcher(ControlDispatcher)} with {@link
* * DefaultControlDispatcher#DefaultControlDispatcher(long, long)}.
* @param fastForwardMs The fast forward increment in milliseconds. A non-positive value will
* cause the fast forward button to be disabled.
*/ */
@SuppressWarnings("deprecation")
@Deprecated
public void setFastForwardIncrementMs(int fastForwardMs) { public void setFastForwardIncrementMs(int fastForwardMs) {
this.fastForwardMs = fastForwardMs; if (controlDispatcher instanceof DefaultControlDispatcher) {
updateNavigation(); ((DefaultControlDispatcher) controlDispatcher).setFastForwardIncrementMs(fastForwardMs);
updateNavigation();
}
} }
/** /**
@ -830,8 +828,8 @@ public class PlayerControlView extends FrameLayout {
boolean isSeekable = window.isSeekable; boolean isSeekable = window.isSeekable;
enableSeeking = isSeekable; enableSeeking = isSeekable;
enablePrevious = isSeekable || !window.isDynamic || player.hasPrevious(); enablePrevious = isSeekable || !window.isDynamic || player.hasPrevious();
enableRewind = isSeekable && rewindMs > 0; enableRewind = isSeekable && controlDispatcher.isRewindEnabled();
enableFastForward = isSeekable && fastForwardMs > 0; enableFastForward = isSeekable && controlDispatcher.isFastForwardEnabled();
enableNext = window.isDynamic || player.hasNext(); enableNext = window.isDynamic || player.hasNext();
} }
} }
@ -1042,59 +1040,6 @@ public class PlayerControlView extends FrameLayout {
view.setVisibility(VISIBLE); view.setVisibility(VISIBLE);
} }
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, windowIndex, /* positionMs= */ 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) {
seekToOffset(player, -rewindMs);
}
}
private void fastForward(Player player) {
if (player.isCurrentWindowSeekable() && fastForwardMs > 0) {
seekToOffset(player, fastForwardMs);
}
}
private void seekToOffset(Player player, long offsetMs) {
long positionMs = player.getCurrentPosition() + offsetMs;
long durationMs = player.getDuration();
if (durationMs != C.TIME_UNSET) {
positionMs = Math.min(positionMs, durationMs);
}
positionMs = Math.max(positionMs, 0);
seekTo(player, player.getCurrentWindowIndex(), positionMs);
}
private void seekToTimeBarPosition(Player player, long positionMs) { private void seekToTimeBarPosition(Player player, long positionMs) {
int windowIndex; int windowIndex;
Timeline timeline = player.getCurrentTimeline(); Timeline timeline = player.getCurrentTimeline();
@ -1183,9 +1128,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(player); controlDispatcher.dispatchFastForward(player);
} else if (keyCode == KeyEvent.KEYCODE_MEDIA_REWIND) { } else if (keyCode == KeyEvent.KEYCODE_MEDIA_REWIND) {
rewind(player); controlDispatcher.dispatchRewind(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:
@ -1198,10 +1143,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(player); controlDispatcher.dispatchNext(player);
break; break;
case KeyEvent.KEYCODE_MEDIA_PREVIOUS: case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
previous(player); controlDispatcher.dispatchPrevious(player);
break; break;
default: default:
break; break;
@ -1317,13 +1262,13 @@ public class PlayerControlView extends FrameLayout {
return; return;
} }
if (nextButton == view) { if (nextButton == view) {
next(player); controlDispatcher.dispatchNext(player);
} else if (previousButton == view) { } else if (previousButton == view) {
previous(player); controlDispatcher.dispatchPrevious(player);
} else if (fastForwardButton == view) { } else if (fastForwardButton == view) {
fastForward(player); controlDispatcher.dispatchFastForward(player);
} else if (rewindButton == view) { } else if (rewindButton == view) {
rewind(player); controlDispatcher.dispatchRewind(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) {

View File

@ -94,14 +94,14 @@ import java.util.Map;
* <li><b>{@code rewindIncrementMs}</b> - Sets the rewind increment. If set to zero the rewind * <li><b>{@code rewindIncrementMs}</b> - Sets the rewind increment. If set to zero the rewind
* action is not displayed. * action is not displayed.
* <ul> * <ul>
* <li>Corresponding setter: {@link #setRewindIncrementMs(long)} * <li>Corresponding setter: {@link #setControlDispatcher(ControlDispatcher)}
* <li>Default: {@link #DEFAULT_REWIND_MS} (5000) * <li>Default: {@link DefaultControlDispatcher#DEFAULT_REWIND_MS} (5000)
* </ul> * </ul>
* <li><b>{@code fastForwardIncrementMs}</b> - Sets the fast forward increment. If set to zero the * <li><b>{@code fastForwardIncrementMs}</b> - Sets the fast forward increment. If set to zero the
* fast forward action is not displayed. * fast forward action is not displayed.
* <ul> * <ul>
* <li>Corresponding setter: {@link #setFastForwardIncrementMs(long)} * <li>Corresponding setter: {@link #setControlDispatcher(ControlDispatcher)}
* <li>Default: {@link #DEFAULT_FAST_FORWARD_MS} (15000) * <li>Default: {@link DefaultControlDispatcher#DEFAULT_FAST_FORWARD_MS} (15000)
* </ul> * </ul>
* </ul> * </ul>
* *
@ -354,13 +354,6 @@ public class PlayerNotificationManager {
}) })
public @interface Priority {} public @interface Priority {}
/** The default fast forward increment, in milliseconds. */
public static final int DEFAULT_FAST_FORWARD_MS = 15000;
/** The default rewind increment, in milliseconds. */
public static final int DEFAULT_REWIND_MS = 5000;
private static final long MAX_POSITION_FOR_SEEK_TO_PREVIOUS = 3000;
private static int instanceIdCounter; private static int instanceIdCounter;
private final Context context; private final Context context;
@ -392,8 +385,6 @@ public class PlayerNotificationManager {
private boolean useNavigationActionsInCompactView; private boolean useNavigationActionsInCompactView;
private boolean usePlayPauseActions; private boolean usePlayPauseActions;
private boolean useStopAction; private boolean useStopAction;
private long fastForwardMs;
private long rewindMs;
private int badgeIconType; private int badgeIconType;
private boolean colorized; private boolean colorized;
private int defaults; private int defaults;
@ -634,8 +625,6 @@ public class PlayerNotificationManager {
smallIconResourceId = R.drawable.exo_notification_small_icon; smallIconResourceId = R.drawable.exo_notification_small_icon;
defaults = 0; defaults = 0;
priority = NotificationCompat.PRIORITY_LOW; priority = NotificationCompat.PRIORITY_LOW;
fastForwardMs = DEFAULT_FAST_FORWARD_MS;
rewindMs = DEFAULT_REWIND_MS;
badgeIconType = NotificationCompat.BADGE_ICON_SMALL; badgeIconType = NotificationCompat.BADGE_ICON_SMALL;
visibility = NotificationCompat.VISIBILITY_PUBLIC; visibility = NotificationCompat.VISIBILITY_PUBLIC;
@ -701,12 +690,13 @@ public class PlayerNotificationManager {
/** /**
* Sets the {@link ControlDispatcher}. * Sets the {@link ControlDispatcher}.
* *
* @param controlDispatcher The {@link ControlDispatcher}, or null to use {@link * @param controlDispatcher The {@link ControlDispatcher}.
* DefaultControlDispatcher}.
*/ */
public final void setControlDispatcher(ControlDispatcher controlDispatcher) { public final void setControlDispatcher(ControlDispatcher controlDispatcher) {
this.controlDispatcher = if (this.controlDispatcher != controlDispatcher) {
controlDispatcher != null ? controlDispatcher : new DefaultControlDispatcher(); this.controlDispatcher = controlDispatcher;
invalidate();
}
} }
/** /**
@ -725,31 +715,29 @@ public class PlayerNotificationManager {
} }
/** /**
* Sets the fast forward increment in milliseconds. * @deprecated Use {@link #setControlDispatcher(ControlDispatcher)} with {@link
* * DefaultControlDispatcher#DefaultControlDispatcher(long, long)}.
* @param fastForwardMs The fast forward increment in milliseconds. A value of zero will cause the
* fast forward action to be disabled.
*/ */
@SuppressWarnings("deprecation")
@Deprecated
public final void setFastForwardIncrementMs(long fastForwardMs) { public final void setFastForwardIncrementMs(long fastForwardMs) {
if (this.fastForwardMs == fastForwardMs) { if (controlDispatcher instanceof DefaultControlDispatcher) {
return; ((DefaultControlDispatcher) controlDispatcher).setFastForwardIncrementMs(fastForwardMs);
invalidate();
} }
this.fastForwardMs = fastForwardMs;
invalidate();
} }
/** /**
* Sets the rewind increment in milliseconds. * @deprecated Use {@link #setControlDispatcher(ControlDispatcher)} with {@link
* * DefaultControlDispatcher#DefaultControlDispatcher(long, long)}.
* @param rewindMs The rewind increment in milliseconds. A value of zero will cause the rewind
* action to be disabled.
*/ */
@SuppressWarnings("deprecation")
@Deprecated
public final void setRewindIncrementMs(long rewindMs) { public final void setRewindIncrementMs(long rewindMs) {
if (this.rewindMs == rewindMs) { if (controlDispatcher instanceof DefaultControlDispatcher) {
return; ((DefaultControlDispatcher) controlDispatcher).setRewindIncrementMs(rewindMs);
invalidate();
} }
this.rewindMs = rewindMs;
invalidate();
} }
/** /**
@ -1047,6 +1035,7 @@ public class PlayerNotificationManager {
List<NotificationCompat.Action> actions = new ArrayList<>(actionNames.size()); List<NotificationCompat.Action> actions = new ArrayList<>(actionNames.size());
for (int i = 0; i < actionNames.size(); i++) { for (int i = 0; i < actionNames.size(); i++) {
String actionName = actionNames.get(i); String actionName = actionNames.get(i);
@Nullable
NotificationCompat.Action action = NotificationCompat.Action action =
playbackActions.containsKey(actionName) playbackActions.containsKey(actionName)
? playbackActions.get(actionName) ? playbackActions.get(actionName)
@ -1146,8 +1135,8 @@ public class PlayerNotificationManager {
if (!timeline.isEmpty() && !player.isPlayingAd()) { if (!timeline.isEmpty() && !player.isPlayingAd()) {
timeline.getWindow(player.getCurrentWindowIndex(), window); timeline.getWindow(player.getCurrentWindowIndex(), window);
enablePrevious = window.isSeekable || !window.isDynamic || player.hasPrevious(); enablePrevious = window.isSeekable || !window.isDynamic || player.hasPrevious();
enableRewind = rewindMs > 0; enableRewind = controlDispatcher.isRewindEnabled();
enableFastForward = fastForwardMs > 0; enableFastForward = controlDispatcher.isFastForwardEnabled();
enableNext = window.isDynamic || player.hasNext(); enableNext = window.isDynamic || player.hasNext();
} }
@ -1222,63 +1211,6 @@ public class PlayerNotificationManager {
&& player.getPlayWhenReady(); && player.getPlayWhenReady();
} }
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, windowIndex, /* positionMs= */ 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) {
seekToOffset(player, /* offsetMs= */ -rewindMs);
}
}
private void fastForward(Player player) {
if (player.isCurrentWindowSeekable() && fastForwardMs > 0) {
seekToOffset(player, /* offsetMs= */ fastForwardMs);
}
}
private void seekToOffset(Player player, long offsetMs) {
long positionMs = player.getCurrentPosition() + offsetMs;
long durationMs = player.getDuration();
if (durationMs != C.TIME_UNSET) {
positionMs = Math.min(positionMs, durationMs);
}
positionMs = Math.max(positionMs, 0);
seekTo(player, player.getCurrentWindowIndex(), positionMs);
}
private void seekTo(Player player, int windowIndex, long positionMs) {
controlDispatcher.dispatchSeekTo(player, windowIndex, positionMs);
}
private boolean shouldShowPauseButton(Player player) { private boolean shouldShowPauseButton(Player player) {
return player.getPlaybackState() != Player.STATE_ENDED return player.getPlaybackState() != Player.STATE_ENDED
&& player.getPlaybackState() != Player.STATE_IDLE && player.getPlaybackState() != Player.STATE_IDLE
@ -1438,19 +1370,19 @@ public class PlayerNotificationManager {
playbackPreparer.preparePlayback(); playbackPreparer.preparePlayback();
} }
} else if (player.getPlaybackState() == Player.STATE_ENDED) { } else if (player.getPlaybackState() == Player.STATE_ENDED) {
seekTo(player, player.getCurrentWindowIndex(), C.TIME_UNSET); controlDispatcher.dispatchSeekTo(player, player.getCurrentWindowIndex(), C.TIME_UNSET);
} }
controlDispatcher.dispatchSetPlayWhenReady(player, /* playWhenReady= */ true); controlDispatcher.dispatchSetPlayWhenReady(player, /* playWhenReady= */ true);
} else if (ACTION_PAUSE.equals(action)) { } else if (ACTION_PAUSE.equals(action)) {
controlDispatcher.dispatchSetPlayWhenReady(player, /* playWhenReady= */ false); controlDispatcher.dispatchSetPlayWhenReady(player, /* playWhenReady= */ false);
} else if (ACTION_PREVIOUS.equals(action)) { } else if (ACTION_PREVIOUS.equals(action)) {
previous(player); controlDispatcher.dispatchPrevious(player);
} else if (ACTION_REWIND.equals(action)) { } else if (ACTION_REWIND.equals(action)) {
rewind(player); controlDispatcher.dispatchRewind(player);
} else if (ACTION_FAST_FORWARD.equals(action)) { } else if (ACTION_FAST_FORWARD.equals(action)) {
fastForward(player); controlDispatcher.dispatchFastForward(player);
} else if (ACTION_NEXT.equals(action)) { } else if (ACTION_NEXT.equals(action)) {
next(player); controlDispatcher.dispatchNext(player);
} else if (ACTION_STOP.equals(action)) { } else if (ACTION_STOP.equals(action)) {
controlDispatcher.dispatchStop(player, /* reset= */ true); controlDispatcher.dispatchStop(player, /* reset= */ true);
} else if (ACTION_DISMISS.equals(action)) { } else if (ACTION_DISMISS.equals(action)) {

View File

@ -965,31 +965,30 @@ public class PlayerView extends FrameLayout implements AdsLoader.AdViewProvider
/** /**
* Sets the {@link ControlDispatcher}. * Sets the {@link ControlDispatcher}.
* *
* @param controlDispatcher The {@link ControlDispatcher}, or null to use {@link * @param controlDispatcher The {@link ControlDispatcher}.
* DefaultControlDispatcher}.
*/ */
public void setControlDispatcher(@Nullable ControlDispatcher controlDispatcher) { public void setControlDispatcher(ControlDispatcher controlDispatcher) {
Assertions.checkStateNotNull(controller); Assertions.checkStateNotNull(controller);
controller.setControlDispatcher(controlDispatcher); controller.setControlDispatcher(controlDispatcher);
} }
/** /**
* Sets the rewind increment in milliseconds. * @deprecated Use {@link #setControlDispatcher(ControlDispatcher)} with {@link
* * DefaultControlDispatcher#DefaultControlDispatcher(long, long)}.
* @param rewindMs The rewind increment in milliseconds. A non-positive value will cause the
* rewind button to be disabled.
*/ */
@SuppressWarnings("deprecation")
@Deprecated
public void setRewindIncrementMs(int rewindMs) { public void setRewindIncrementMs(int rewindMs) {
Assertions.checkStateNotNull(controller); Assertions.checkStateNotNull(controller);
controller.setRewindIncrementMs(rewindMs); controller.setRewindIncrementMs(rewindMs);
} }
/** /**
* Sets the fast forward increment in milliseconds. * @deprecated Use {@link #setControlDispatcher(ControlDispatcher)} with {@link
* * DefaultControlDispatcher#DefaultControlDispatcher(long, long)}.
* @param fastForwardMs The fast forward increment in milliseconds. A non-positive value will
* cause the fast forward button to be disabled.
*/ */
@SuppressWarnings("deprecation")
@Deprecated
public void setFastForwardIncrementMs(int fastForwardMs) { public void setFastForwardIncrementMs(int fastForwardMs) {
Assertions.checkStateNotNull(controller); Assertions.checkStateNotNull(controller);
controller.setFastForwardIncrementMs(fastForwardMs); controller.setFastForwardIncrementMs(fastForwardMs);