Commit e598a17b by tonihei Committed by Ian Baker

Add support for most setters in SimpleBasePlayer

This adds the forwarding logic for most setters in SimpleExoPlayer
in the same style as the existing logic for setPlayWhenReady.

This change doesn't implement the setters for modifying media items,
seeking and releasing yet as they require additional handling that
goes beyond the repeated implementation pattern in this change.

PiperOrigin-RevId: 492124399
parent 1ac72de5
...@@ -21,6 +21,7 @@ import static com.google.android.exoplayer2.util.Util.castNonNull; ...@@ -21,6 +21,7 @@ import static com.google.android.exoplayer2.util.Util.castNonNull;
import static com.google.android.exoplayer2.util.Util.usToMs; import static com.google.android.exoplayer2.util.Util.usToMs;
import static java.lang.Math.max; import static java.lang.Math.max;
import android.graphics.Rect;
import android.os.Looper; import android.os.Looper;
import android.os.SystemClock; import android.os.SystemClock;
import android.util.Pair; import android.util.Pair;
...@@ -2039,6 +2040,7 @@ public abstract class SimpleBasePlayer extends BasePlayer { ...@@ -2039,6 +2040,7 @@ public abstract class SimpleBasePlayer extends BasePlayer {
@Override @Override
public final void setPlayWhenReady(boolean playWhenReady) { public final void setPlayWhenReady(boolean playWhenReady) {
verifyApplicationThreadAndInitState(); verifyApplicationThreadAndInitState();
// Use a local copy to ensure the lambda below uses the current state value.
State state = this.state; State state = this.state;
if (!state.availableCommands.contains(Player.COMMAND_PLAY_PAUSE)) { if (!state.availableCommands.contains(Player.COMMAND_PLAY_PAUSE)) {
return; return;
...@@ -2091,8 +2093,20 @@ public abstract class SimpleBasePlayer extends BasePlayer { ...@@ -2091,8 +2093,20 @@ public abstract class SimpleBasePlayer extends BasePlayer {
@Override @Override
public final void prepare() { public final void prepare() {
// TODO: implement. verifyApplicationThreadAndInitState();
throw new IllegalStateException(); // Use a local copy to ensure the lambda below uses the current state value.
State state = this.state;
if (!state.availableCommands.contains(Player.COMMAND_PREPARE)) {
return;
}
updateStateForPendingOperation(
/* pendingOperation= */ handlePrepare(),
/* placeholderStateSupplier= */ () ->
state
.buildUpon()
.setPlayerError(null)
.setPlaybackState(state.timeline.isEmpty() ? STATE_ENDED : STATE_BUFFERING)
.build());
} }
@Override @Override
...@@ -2117,8 +2131,15 @@ public abstract class SimpleBasePlayer extends BasePlayer { ...@@ -2117,8 +2131,15 @@ public abstract class SimpleBasePlayer extends BasePlayer {
@Override @Override
public final void setRepeatMode(@Player.RepeatMode int repeatMode) { public final void setRepeatMode(@Player.RepeatMode int repeatMode) {
// TODO: implement. verifyApplicationThreadAndInitState();
throw new IllegalStateException(); // Use a local copy to ensure the lambda below uses the current state value.
State state = this.state;
if (!state.availableCommands.contains(Player.COMMAND_SET_REPEAT_MODE)) {
return;
}
updateStateForPendingOperation(
/* pendingOperation= */ handleSetRepeatMode(repeatMode),
/* placeholderStateSupplier= */ () -> state.buildUpon().setRepeatMode(repeatMode).build());
} }
@Override @Override
...@@ -2130,8 +2151,16 @@ public abstract class SimpleBasePlayer extends BasePlayer { ...@@ -2130,8 +2151,16 @@ public abstract class SimpleBasePlayer extends BasePlayer {
@Override @Override
public final void setShuffleModeEnabled(boolean shuffleModeEnabled) { public final void setShuffleModeEnabled(boolean shuffleModeEnabled) {
// TODO: implement. verifyApplicationThreadAndInitState();
throw new IllegalStateException(); // Use a local copy to ensure the lambda below uses the current state value.
State state = this.state;
if (!state.availableCommands.contains(Player.COMMAND_SET_SHUFFLE_MODE)) {
return;
}
updateStateForPendingOperation(
/* pendingOperation= */ handleSetShuffleModeEnabled(shuffleModeEnabled),
/* placeholderStateSupplier= */ () ->
state.buildUpon().setShuffleModeEnabled(shuffleModeEnabled).build());
} }
@Override @Override
...@@ -2153,6 +2182,12 @@ public abstract class SimpleBasePlayer extends BasePlayer { ...@@ -2153,6 +2182,12 @@ public abstract class SimpleBasePlayer extends BasePlayer {
} }
@Override @Override
protected final void repeatCurrentMediaItem() {
// TODO: implement.
throw new IllegalStateException();
}
@Override
public final long getSeekBackIncrement() { public final long getSeekBackIncrement() {
verifyApplicationThreadAndInitState(); verifyApplicationThreadAndInitState();
return state.seekBackIncrementMs; return state.seekBackIncrementMs;
...@@ -2172,8 +2207,16 @@ public abstract class SimpleBasePlayer extends BasePlayer { ...@@ -2172,8 +2207,16 @@ public abstract class SimpleBasePlayer extends BasePlayer {
@Override @Override
public final void setPlaybackParameters(PlaybackParameters playbackParameters) { public final void setPlaybackParameters(PlaybackParameters playbackParameters) {
// TODO: implement. verifyApplicationThreadAndInitState();
throw new IllegalStateException(); // Use a local copy to ensure the lambda below uses the current state value.
State state = this.state;
if (!state.availableCommands.contains(Player.COMMAND_SET_SPEED_AND_PITCH)) {
return;
}
updateStateForPendingOperation(
/* pendingOperation= */ handleSetPlaybackParameters(playbackParameters),
/* placeholderStateSupplier= */ () ->
state.buildUpon().setPlaybackParameters(playbackParameters).build());
} }
@Override @Override
...@@ -2184,14 +2227,30 @@ public abstract class SimpleBasePlayer extends BasePlayer { ...@@ -2184,14 +2227,30 @@ public abstract class SimpleBasePlayer extends BasePlayer {
@Override @Override
public final void stop() { public final void stop() {
// TODO: implement. verifyApplicationThreadAndInitState();
throw new IllegalStateException(); // Use a local copy to ensure the lambda below uses the current state value.
State state = this.state;
if (!state.availableCommands.contains(Player.COMMAND_STOP)) {
return;
}
updateStateForPendingOperation(
/* pendingOperation= */ handleStop(),
/* placeholderStateSupplier= */ () ->
state
.buildUpon()
.setPlaybackState(Player.STATE_IDLE)
.setTotalBufferedDurationMs(PositionSupplier.ZERO)
.setContentBufferedPositionMs(state.contentPositionMsSupplier)
.setAdBufferedPositionMs(state.adPositionMsSupplier)
.build());
} }
@Override @Override
public final void stop(boolean reset) { public final void stop(boolean reset) {
// TODO: implement. stop();
throw new IllegalStateException(); if (reset) {
clearMediaItems();
}
} }
@Override @Override
...@@ -2214,8 +2273,16 @@ public abstract class SimpleBasePlayer extends BasePlayer { ...@@ -2214,8 +2273,16 @@ public abstract class SimpleBasePlayer extends BasePlayer {
@Override @Override
public final void setTrackSelectionParameters(TrackSelectionParameters parameters) { public final void setTrackSelectionParameters(TrackSelectionParameters parameters) {
// TODO: implement. verifyApplicationThreadAndInitState();
throw new IllegalStateException(); // Use a local copy to ensure the lambda below uses the current state value.
State state = this.state;
if (!state.availableCommands.contains(Player.COMMAND_SET_TRACK_SELECTION_PARAMETERS)) {
return;
}
updateStateForPendingOperation(
/* pendingOperation= */ handleSetTrackSelectionParameters(parameters),
/* placeholderStateSupplier= */ () ->
state.buildUpon().setTrackSelectionParameters(parameters).build());
} }
@Override @Override
...@@ -2232,8 +2299,16 @@ public abstract class SimpleBasePlayer extends BasePlayer { ...@@ -2232,8 +2299,16 @@ public abstract class SimpleBasePlayer extends BasePlayer {
@Override @Override
public final void setPlaylistMetadata(MediaMetadata mediaMetadata) { public final void setPlaylistMetadata(MediaMetadata mediaMetadata) {
// TODO: implement. verifyApplicationThreadAndInitState();
throw new IllegalStateException(); // Use a local copy to ensure the lambda below uses the current state value.
State state = this.state;
if (!state.availableCommands.contains(Player.COMMAND_SET_MEDIA_ITEMS_METADATA)) {
return;
}
updateStateForPendingOperation(
/* pendingOperation= */ handleSetPlaylistMetadata(mediaMetadata),
/* placeholderStateSupplier= */ () ->
state.buildUpon().setPlaylistMetadata(mediaMetadata).build());
} }
@Override @Override
...@@ -2325,8 +2400,15 @@ public abstract class SimpleBasePlayer extends BasePlayer { ...@@ -2325,8 +2400,15 @@ public abstract class SimpleBasePlayer extends BasePlayer {
@Override @Override
public final void setVolume(float volume) { public final void setVolume(float volume) {
// TODO: implement. verifyApplicationThreadAndInitState();
throw new IllegalStateException(); // Use a local copy to ensure the lambda below uses the current state value.
State state = this.state;
if (!state.availableCommands.contains(Player.COMMAND_SET_VOLUME)) {
return;
}
updateStateForPendingOperation(
/* pendingOperation= */ handleSetVolume(volume),
/* placeholderStateSupplier= */ () -> state.buildUpon().setVolume(volume).build());
} }
@Override @Override
...@@ -2336,57 +2418,121 @@ public abstract class SimpleBasePlayer extends BasePlayer { ...@@ -2336,57 +2418,121 @@ public abstract class SimpleBasePlayer extends BasePlayer {
} }
@Override @Override
public final void clearVideoSurface() { public final void setVideoSurface(@Nullable Surface surface) {
// TODO: implement. verifyApplicationThreadAndInitState();
throw new IllegalStateException(); // Use a local copy to ensure the lambda below uses the current state value.
State state = this.state;
if (!state.availableCommands.contains(Player.COMMAND_SET_VIDEO_SURFACE)) {
return;
}
if (surface == null) {
clearVideoSurface();
return;
}
updateStateForPendingOperation(
/* pendingOperation= */ handleSetVideoOutput(surface),
/* placeholderStateSupplier= */ () ->
state.buildUpon().setSurfaceSize(Size.UNKNOWN).build());
} }
@Override @Override
public final void clearVideoSurface(@Nullable Surface surface) { public final void setVideoSurfaceHolder(@Nullable SurfaceHolder surfaceHolder) {
// TODO: implement. verifyApplicationThreadAndInitState();
throw new IllegalStateException(); // Use a local copy to ensure the lambda below uses the current state value.
State state = this.state;
if (!state.availableCommands.contains(Player.COMMAND_SET_VIDEO_SURFACE)) {
return;
}
if (surfaceHolder == null) {
clearVideoSurface();
return;
}
updateStateForPendingOperation(
/* pendingOperation= */ handleSetVideoOutput(surfaceHolder),
/* placeholderStateSupplier= */ () ->
state.buildUpon().setSurfaceSize(getSurfaceHolderSize(surfaceHolder)).build());
} }
@Override @Override
public final void setVideoSurface(@Nullable Surface surface) { public final void setVideoSurfaceView(@Nullable SurfaceView surfaceView) {
// TODO: implement. verifyApplicationThreadAndInitState();
throw new IllegalStateException(); // Use a local copy to ensure the lambda below uses the current state value.
State state = this.state;
if (!state.availableCommands.contains(Player.COMMAND_SET_VIDEO_SURFACE)) {
return;
}
if (surfaceView == null) {
clearVideoSurface();
return;
}
updateStateForPendingOperation(
/* pendingOperation= */ handleSetVideoOutput(surfaceView),
/* placeholderStateSupplier= */ () ->
state
.buildUpon()
.setSurfaceSize(getSurfaceHolderSize(surfaceView.getHolder()))
.build());
} }
@Override @Override
public final void setVideoSurfaceHolder(@Nullable SurfaceHolder surfaceHolder) { public final void setVideoTextureView(@Nullable TextureView textureView) {
// TODO: implement. verifyApplicationThreadAndInitState();
throw new IllegalStateException(); // Use a local copy to ensure the lambda below uses the current state value.
State state = this.state;
if (!state.availableCommands.contains(Player.COMMAND_SET_VIDEO_SURFACE)) {
return;
}
if (textureView == null) {
clearVideoSurface();
return;
}
Size surfaceSize;
if (textureView.isAvailable()) {
surfaceSize = new Size(textureView.getWidth(), textureView.getHeight());
} else {
surfaceSize = Size.ZERO;
}
updateStateForPendingOperation(
/* pendingOperation= */ handleSetVideoOutput(textureView),
/* placeholderStateSupplier= */ () ->
state.buildUpon().setSurfaceSize(surfaceSize).build());
} }
@Override @Override
public final void clearVideoSurfaceHolder(@Nullable SurfaceHolder surfaceHolder) { public final void clearVideoSurface() {
// TODO: implement. clearVideoOutput(/* videoOutput= */ null);
throw new IllegalStateException();
} }
@Override @Override
public final void setVideoSurfaceView(@Nullable SurfaceView surfaceView) { public final void clearVideoSurface(@Nullable Surface surface) {
// TODO: implement. clearVideoOutput(surface);
throw new IllegalStateException();
} }
@Override @Override
public final void clearVideoSurfaceView(@Nullable SurfaceView surfaceView) { public final void clearVideoSurfaceHolder(@Nullable SurfaceHolder surfaceHolder) {
// TODO: implement. clearVideoOutput(surfaceHolder);
throw new IllegalStateException();
} }
@Override @Override
public final void setVideoTextureView(@Nullable TextureView textureView) { public final void clearVideoSurfaceView(@Nullable SurfaceView surfaceView) {
// TODO: implement. clearVideoOutput(surfaceView);
throw new IllegalStateException();
} }
@Override @Override
public final void clearVideoTextureView(@Nullable TextureView textureView) { public final void clearVideoTextureView(@Nullable TextureView textureView) {
// TODO: implement. clearVideoOutput(textureView);
throw new IllegalStateException(); }
private void clearVideoOutput(@Nullable Object videoOutput) {
verifyApplicationThreadAndInitState();
// Use a local copy to ensure the lambda below uses the current state value.
State state = this.state;
if (!state.availableCommands.contains(Player.COMMAND_SET_VIDEO_SURFACE)) {
return;
}
updateStateForPendingOperation(
/* pendingOperation= */ handleClearVideoOutput(videoOutput),
/* placeholderStateSupplier= */ () -> state.buildUpon().setSurfaceSize(Size.ZERO).build());
} }
@Override @Override
...@@ -2427,26 +2573,56 @@ public abstract class SimpleBasePlayer extends BasePlayer { ...@@ -2427,26 +2573,56 @@ public abstract class SimpleBasePlayer extends BasePlayer {
@Override @Override
public final void setDeviceVolume(int volume) { public final void setDeviceVolume(int volume) {
// TODO: implement. verifyApplicationThreadAndInitState();
throw new IllegalStateException(); // Use a local copy to ensure the lambda below uses the current state value.
State state = this.state;
if (!state.availableCommands.contains(Player.COMMAND_SET_DEVICE_VOLUME)) {
return;
}
updateStateForPendingOperation(
/* pendingOperation= */ handleSetDeviceVolume(volume),
/* placeholderStateSupplier= */ () -> state.buildUpon().setDeviceVolume(volume).build());
} }
@Override @Override
public final void increaseDeviceVolume() { public final void increaseDeviceVolume() {
// TODO: implement. verifyApplicationThreadAndInitState();
throw new IllegalStateException(); // Use a local copy to ensure the lambda below uses the current state value.
State state = this.state;
if (!state.availableCommands.contains(Player.COMMAND_ADJUST_DEVICE_VOLUME)) {
return;
}
updateStateForPendingOperation(
/* pendingOperation= */ handleIncreaseDeviceVolume(),
/* placeholderStateSupplier= */ () ->
state.buildUpon().setDeviceVolume(state.deviceVolume + 1).build());
} }
@Override @Override
public final void decreaseDeviceVolume() { public final void decreaseDeviceVolume() {
// TODO: implement. verifyApplicationThreadAndInitState();
throw new IllegalStateException(); // Use a local copy to ensure the lambda below uses the current state value.
State state = this.state;
if (!state.availableCommands.contains(Player.COMMAND_ADJUST_DEVICE_VOLUME)) {
return;
}
updateStateForPendingOperation(
/* pendingOperation= */ handleDecreaseDeviceVolume(),
/* placeholderStateSupplier= */ () ->
state.buildUpon().setDeviceVolume(max(0, state.deviceVolume - 1)).build());
} }
@Override @Override
public final void setDeviceMuted(boolean muted) { public final void setDeviceMuted(boolean muted) {
// TODO: implement. verifyApplicationThreadAndInitState();
throw new IllegalStateException(); // Use a local copy to ensure the lambda below uses the current state value.
State state = this.state;
if (!state.availableCommands.contains(Player.COMMAND_ADJUST_DEVICE_VOLUME)) {
return;
}
updateStateForPendingOperation(
/* pendingOperation= */ handleSetDeviceMuted(muted),
/* placeholderStateSupplier= */ () -> state.buildUpon().setIsDeviceMuted(muted).build());
} }
/** /**
...@@ -2500,22 +2676,217 @@ public abstract class SimpleBasePlayer extends BasePlayer { ...@@ -2500,22 +2676,217 @@ public abstract class SimpleBasePlayer extends BasePlayer {
} }
/** /**
* Handles calls to set {@link State#playWhenReady}. * Handles calls to {@link Player#setPlayWhenReady}, {@link Player#play} and {@link Player#pause}.
* *
* <p>Will only be called if {@link Player.Command#COMMAND_PLAY_PAUSE} is available. * <p>Will only be called if {@link Player#COMMAND_PLAY_PAUSE} is available.
* *
* @param playWhenReady The requested {@link State#playWhenReady} * @param playWhenReady The requested {@link State#playWhenReady}
* @return A {@link ListenableFuture} indicating the completion of all immediate {@link State} * @return A {@link ListenableFuture} indicating the completion of all immediate {@link State}
* changes caused by this call. * changes caused by this call.
* @see Player#setPlayWhenReady(boolean)
* @see Player#play()
* @see Player#pause()
*/ */
@ForOverride @ForOverride
protected ListenableFuture<?> handleSetPlayWhenReady(boolean playWhenReady) { protected ListenableFuture<?> handleSetPlayWhenReady(boolean playWhenReady) {
throw new IllegalStateException(); throw new IllegalStateException();
} }
/**
* Handles calls to {@link Player#prepare}.
*
* <p>Will only be called if {@link Player#COMMAND_PREPARE} is available.
*
* @return A {@link ListenableFuture} indicating the completion of all immediate {@link State}
* changes caused by this call.
*/
@ForOverride
protected ListenableFuture<?> handlePrepare() {
throw new IllegalStateException();
}
/**
* Handles calls to {@link Player#stop}.
*
* <p>Will only be called if {@link Player#COMMAND_STOP} is available.
*
* @return A {@link ListenableFuture} indicating the completion of all immediate {@link State}
* changes caused by this call.
*/
@ForOverride
protected ListenableFuture<?> handleStop() {
throw new IllegalStateException();
}
/**
* Handles calls to {@link Player#setRepeatMode}.
*
* <p>Will only be called if {@link Player#COMMAND_SET_REPEAT_MODE} is available.
*
* @param repeatMode The requested {@link RepeatMode}.
* @return A {@link ListenableFuture} indicating the completion of all immediate {@link State}
* changes caused by this call.
*/
@ForOverride
protected ListenableFuture<?> handleSetRepeatMode(@RepeatMode int repeatMode) {
throw new IllegalStateException();
}
/**
* Handles calls to {@link Player#setShuffleModeEnabled}.
*
* <p>Will only be called if {@link Player#COMMAND_SET_SHUFFLE_MODE} is available.
*
* @param shuffleModeEnabled Whether shuffle mode was requested to be enabled.
* @return A {@link ListenableFuture} indicating the completion of all immediate {@link State}
* changes caused by this call.
*/
@ForOverride
protected ListenableFuture<?> handleSetShuffleModeEnabled(boolean shuffleModeEnabled) {
throw new IllegalStateException();
}
/**
* Handles calls to {@link Player#setPlaybackParameters} or {@link Player#setPlaybackSpeed}.
*
* <p>Will only be called if {@link Player#COMMAND_SET_SPEED_AND_PITCH} is available.
*
* @param playbackParameters The requested {@link PlaybackParameters}.
* @return A {@link ListenableFuture} indicating the completion of all immediate {@link State}
* changes caused by this call.
*/
@ForOverride
protected ListenableFuture<?> handleSetPlaybackParameters(PlaybackParameters playbackParameters) {
throw new IllegalStateException();
}
/**
* Handles calls to {@link Player#setTrackSelectionParameters}.
*
* <p>Will only be called if {@link Player#COMMAND_SET_TRACK_SELECTION_PARAMETERS} is available.
*
* @param trackSelectionParameters The requested {@link TrackSelectionParameters}.
* @return A {@link ListenableFuture} indicating the completion of all immediate {@link State}
* changes caused by this call.
*/
@ForOverride
protected ListenableFuture<?> handleSetTrackSelectionParameters(
TrackSelectionParameters trackSelectionParameters) {
throw new IllegalStateException();
}
/**
* Handles calls to {@link Player#setPlaylistMetadata}.
*
* <p>Will only be called if {@link Player#COMMAND_SET_MEDIA_ITEMS_METADATA} is available.
*
* @param playlistMetadata The requested {@linkplain MediaMetadata playlist metadata}.
* @return A {@link ListenableFuture} indicating the completion of all immediate {@link State}
* changes caused by this call.
*/
@ForOverride
protected ListenableFuture<?> handleSetPlaylistMetadata(MediaMetadata playlistMetadata) {
throw new IllegalStateException();
}
/**
* Handles calls to {@link Player#setVolume}.
*
* <p>Will only be called if {@link Player#COMMAND_SET_VOLUME} is available.
*
* @param volume The requested audio volume, with 0 being silence and 1 being unity gain (signal
* unchanged).
* @return A {@link ListenableFuture} indicating the completion of all immediate {@link State}
* changes caused by this call.
*/
@ForOverride
protected ListenableFuture<?> handleSetVolume(@FloatRange(from = 0, to = 1.0) float volume) {
throw new IllegalStateException();
}
/**
* Handles calls to {@link Player#setDeviceVolume}.
*
* <p>Will only be called if {@link Player#COMMAND_SET_DEVICE_VOLUME} is available.
*
* @param deviceVolume The requested device volume.
* @return A {@link ListenableFuture} indicating the completion of all immediate {@link State}
* changes caused by this call.
*/
@ForOverride
protected ListenableFuture<?> handleSetDeviceVolume(@IntRange(from = 0) int deviceVolume) {
throw new IllegalStateException();
}
/**
* Handles calls to {@link Player#increaseDeviceVolume()}.
*
* <p>Will only be called if {@link Player#COMMAND_ADJUST_DEVICE_VOLUME} is available.
*
* @return A {@link ListenableFuture} indicating the completion of all immediate {@link State}
* changes caused by this call.
*/
@ForOverride
protected ListenableFuture<?> handleIncreaseDeviceVolume() {
throw new IllegalStateException();
}
/**
* Handles calls to {@link Player#decreaseDeviceVolume()}.
*
* <p>Will only be called if {@link Player#COMMAND_ADJUST_DEVICE_VOLUME} is available.
*
* @return A {@link ListenableFuture} indicating the completion of all immediate {@link State}
* changes caused by this call.
*/
@ForOverride
protected ListenableFuture<?> handleDecreaseDeviceVolume() {
throw new IllegalStateException();
}
/**
* Handles calls to {@link Player#setDeviceMuted}.
*
* <p>Will only be called if {@link Player#COMMAND_ADJUST_DEVICE_VOLUME} is available.
*
* @param muted Whether the device was requested to be muted.
* @return A {@link ListenableFuture} indicating the completion of all immediate {@link State}
* changes caused by this call.
*/
@ForOverride
protected ListenableFuture<?> handleSetDeviceMuted(boolean muted) {
throw new IllegalStateException();
}
/**
* Handles calls to set the video output.
*
* <p>Will only be called if {@link Player#COMMAND_SET_VIDEO_SURFACE} is available.
*
* @param videoOutput The requested video output. This is either a {@link Surface}, {@link
* SurfaceHolder}, {@link TextureView} or {@link SurfaceView}.
* @return A {@link ListenableFuture} indicating the completion of all immediate {@link State}
* changes caused by this call.
*/
@ForOverride
protected ListenableFuture<?> handleSetVideoOutput(Object videoOutput) {
throw new IllegalStateException();
}
/**
* Handles calls to clear the video output.
*
* <p>Will only be called if {@link Player#COMMAND_SET_VIDEO_SURFACE} is available.
*
* @param videoOutput The video output to clear. If null any current output should be cleared. If
* non-null, the output should only be cleared if it matches the provided argument. This is
* either a {@link Surface}, {@link SurfaceHolder}, {@link TextureView} or {@link
* SurfaceView}.
* @return A {@link ListenableFuture} indicating the completion of all immediate {@link State}
* changes caused by this call.
*/
@ForOverride
protected ListenableFuture<?> handleClearVideoOutput(@Nullable Object videoOutput) {
throw new IllegalStateException();
}
@SuppressWarnings("deprecation") // Calling deprecated listener methods. @SuppressWarnings("deprecation") // Calling deprecated listener methods.
@RequiresNonNull("state") @RequiresNonNull("state")
private void updateStateAndInformListeners(State newState) { private void updateStateAndInformListeners(State newState) {
...@@ -2974,4 +3345,12 @@ public abstract class SimpleBasePlayer extends BasePlayer { ...@@ -2974,4 +3345,12 @@ public abstract class SimpleBasePlayer extends BasePlayer {
} }
return C.INDEX_UNSET; return C.INDEX_UNSET;
} }
private static Size getSurfaceHolderSize(SurfaceHolder surfaceHolder) {
if (!surfaceHolder.getSurface().isValid()) {
return Size.ZERO;
}
Rect surfaceFrame = surfaceHolder.getSurfaceFrame();
return new Size(surfaceFrame.width(), surfaceFrame.height());
}
} }
...@@ -28,6 +28,9 @@ public final class Size { ...@@ -28,6 +28,9 @@ public final class Size {
public static final Size UNKNOWN = public static final Size UNKNOWN =
new Size(/* width= */ C.LENGTH_UNSET, /* height= */ C.LENGTH_UNSET); new Size(/* width= */ C.LENGTH_UNSET, /* height= */ C.LENGTH_UNSET);
/* A static instance to represent a size of zero height and width. */
public static final Size ZERO = new Size(/* width= */ 0, /* height= */ 0);
private final int width; private final int width;
private final int height; private final int height;
......
...@@ -27,8 +27,12 @@ import static org.mockito.Mockito.never; ...@@ -27,8 +27,12 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.graphics.SurfaceTexture;
import android.os.Looper; import android.os.Looper;
import android.os.SystemClock; import android.os.SystemClock;
import android.view.Surface;
import androidx.annotation.Nullable;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.Player.Commands; import com.google.android.exoplayer2.Player.Commands;
import com.google.android.exoplayer2.Player.Listener; import com.google.android.exoplayer2.Player.Listener;
...@@ -1833,17 +1837,18 @@ public class SimpleBasePlayerTest { ...@@ -1833,17 +1837,18 @@ public class SimpleBasePlayerTest {
.setPlayWhenReady( .setPlayWhenReady(
/* playWhenReady= */ true, Player.PLAY_WHEN_READY_CHANGE_REASON_REMOTE) /* playWhenReady= */ true, Player.PLAY_WHEN_READY_CHANGE_REASON_REMOTE)
.build(); .build();
AtomicBoolean stateUpdated = new AtomicBoolean();
SimpleBasePlayer player = SimpleBasePlayer player =
new SimpleBasePlayer(Looper.myLooper()) { new SimpleBasePlayer(Looper.myLooper()) {
private State playerState = state;
@Override @Override
protected State getState() { protected State getState() {
return stateUpdated.get() ? updatedState : state; return playerState;
} }
@Override @Override
protected ListenableFuture<?> handleSetPlayWhenReady(boolean playWhenReady) { protected ListenableFuture<?> handleSetPlayWhenReady(boolean playWhenReady) {
stateUpdated.set(true); playerState = updatedState;
return Futures.immediateVoidFuture(); return Futures.immediateVoidFuture();
} }
}; };
...@@ -1941,6 +1946,1506 @@ public class SimpleBasePlayerTest { ...@@ -1941,6 +1946,1506 @@ public class SimpleBasePlayerTest {
assertThat(callForwarded.get()).isFalse(); assertThat(callForwarded.get()).isFalse();
} }
@SuppressWarnings("deprecation") // Verifying deprecated listener call.
@Test
public void prepare_immediateHandling_updatesStateAndInformsListeners() {
State state =
new State.Builder()
.setAvailableCommands(new Commands.Builder().addAllCommands().build())
.setPlaybackState(Player.STATE_IDLE)
.setPlaylist(
ImmutableList.of(
new SimpleBasePlayer.MediaItemData.Builder(/* uid= */ new Object()).build()))
.build();
State updatedState = state.buildUpon().setPlaybackState(Player.STATE_READY).build();
SimpleBasePlayer player =
new SimpleBasePlayer(Looper.myLooper()) {
private State playerState = state;
@Override
protected State getState() {
return playerState;
}
@Override
protected ListenableFuture<?> handlePrepare() {
playerState = updatedState;
return Futures.immediateVoidFuture();
}
};
Listener listener = mock(Listener.class);
player.addListener(listener);
player.prepare();
assertThat(player.getPlaybackState()).isEqualTo(Player.STATE_READY);
verify(listener).onPlaybackStateChanged(Player.STATE_READY);
verify(listener)
.onPlayerStateChanged(/* playWhenReady= */ false, /* playbackState= */ Player.STATE_READY);
verifyNoMoreInteractions(listener);
}
@SuppressWarnings("deprecation") // Verifying deprecated listener call.
@Test
public void prepare_asyncHandling_usesPlaceholderStateAndInformsListeners() {
State state =
new State.Builder()
.setAvailableCommands(new Commands.Builder().addAllCommands().build())
.setPlaybackState(Player.STATE_IDLE)
.setPlaylist(
ImmutableList.of(
new SimpleBasePlayer.MediaItemData.Builder(/* uid= */ new Object()).build()))
.build();
State updatedState = state.buildUpon().setPlaybackState(Player.STATE_READY).build();
SettableFuture<?> future = SettableFuture.create();
SimpleBasePlayer player =
new SimpleBasePlayer(Looper.myLooper()) {
@Override
protected State getState() {
return future.isDone() ? updatedState : state;
}
@Override
protected ListenableFuture<?> handlePrepare() {
return future;
}
};
Listener listener = mock(Listener.class);
player.addListener(listener);
player.prepare();
// Verify placeholder state and listener calls.
assertThat(player.getPlaybackState()).isEqualTo(Player.STATE_BUFFERING);
verify(listener).onPlaybackStateChanged(Player.STATE_BUFFERING);
verify(listener)
.onPlayerStateChanged(
/* playWhenReady= */ false, /* playbackState= */ Player.STATE_BUFFERING);
verifyNoMoreInteractions(listener);
future.set(null);
// Verify actual state update.
assertThat(player.getPlaybackState()).isEqualTo(Player.STATE_READY);
verify(listener).onPlaybackStateChanged(Player.STATE_READY);
verify(listener)
.onPlayerStateChanged(/* playWhenReady= */ false, /* playbackState= */ Player.STATE_READY);
verifyNoMoreInteractions(listener);
}
@Test
public void prepare_withoutAvailableCommand_isNotForwarded() {
State state =
new State.Builder()
.setAvailableCommands(
new Commands.Builder().addAllCommands().remove(Player.COMMAND_PREPARE).build())
.build();
AtomicBoolean callForwarded = new AtomicBoolean();
SimpleBasePlayer player =
new SimpleBasePlayer(Looper.myLooper()) {
@Override
protected State getState() {
return state;
}
@Override
protected ListenableFuture<?> handlePrepare() {
callForwarded.set(true);
return Futures.immediateVoidFuture();
}
};
player.prepare();
assertThat(callForwarded.get()).isFalse();
}
@SuppressWarnings("deprecation") // Verifying deprecated listener call.
@Test
public void stop_immediateHandling_updatesStateAndInformsListeners() {
State state =
new State.Builder()
.setAvailableCommands(new Commands.Builder().addAllCommands().build())
.setPlaybackState(Player.STATE_READY)
.setPlaylist(
ImmutableList.of(
new SimpleBasePlayer.MediaItemData.Builder(/* uid= */ new Object()).build()))
.build();
State updatedState = state.buildUpon().setPlaybackState(Player.STATE_IDLE).build();
SimpleBasePlayer player =
new SimpleBasePlayer(Looper.myLooper()) {
private State playerState = state;
@Override
protected State getState() {
return playerState;
}
@Override
protected ListenableFuture<?> handleStop() {
playerState = updatedState;
return Futures.immediateVoidFuture();
}
};
Listener listener = mock(Listener.class);
player.addListener(listener);
player.stop();
assertThat(player.getPlaybackState()).isEqualTo(Player.STATE_IDLE);
verify(listener).onPlaybackStateChanged(Player.STATE_IDLE);
verify(listener)
.onPlayerStateChanged(/* playWhenReady= */ false, /* playbackState= */ Player.STATE_IDLE);
verifyNoMoreInteractions(listener);
}
@SuppressWarnings("deprecation") // Verifying deprecated listener call.
@Test
public void stop_asyncHandling_usesPlaceholderStateAndInformsListeners() {
State state =
new State.Builder()
.setAvailableCommands(new Commands.Builder().addAllCommands().build())
.setPlaybackState(Player.STATE_READY)
.setPlaylist(
ImmutableList.of(
new SimpleBasePlayer.MediaItemData.Builder(/* uid= */ new Object()).build()))
.build();
// Additionally set the repeat mode to see a difference between the placeholder and new state.
State updatedState =
state
.buildUpon()
.setPlaybackState(Player.STATE_IDLE)
.setRepeatMode(Player.REPEAT_MODE_ALL)
.build();
SettableFuture<?> future = SettableFuture.create();
SimpleBasePlayer player =
new SimpleBasePlayer(Looper.myLooper()) {
@Override
protected State getState() {
return future.isDone() ? updatedState : state;
}
@Override
protected ListenableFuture<?> handleStop() {
return future;
}
};
Listener listener = mock(Listener.class);
player.addListener(listener);
player.stop();
// Verify placeholder state and listener calls.
assertThat(player.getPlaybackState()).isEqualTo(Player.STATE_IDLE);
assertThat(player.getRepeatMode()).isEqualTo(Player.REPEAT_MODE_OFF);
verify(listener).onPlaybackStateChanged(Player.STATE_IDLE);
verify(listener)
.onPlayerStateChanged(/* playWhenReady= */ false, /* playbackState= */ Player.STATE_IDLE);
verifyNoMoreInteractions(listener);
future.set(null);
// Verify actual state update.
assertThat(player.getRepeatMode()).isEqualTo(Player.REPEAT_MODE_ALL);
verify(listener).onRepeatModeChanged(Player.REPEAT_MODE_ALL);
verifyNoMoreInteractions(listener);
}
@Test
public void stop_withoutAvailableCommand_isNotForwarded() {
State state =
new State.Builder()
.setAvailableCommands(
new Commands.Builder().addAllCommands().remove(Player.COMMAND_STOP).build())
.build();
AtomicBoolean callForwarded = new AtomicBoolean();
SimpleBasePlayer player =
new SimpleBasePlayer(Looper.myLooper()) {
@Override
protected State getState() {
return state;
}
@Override
protected ListenableFuture<?> handleStop() {
callForwarded.set(true);
return Futures.immediateVoidFuture();
}
};
player.stop();
assertThat(callForwarded.get()).isFalse();
}
@Test
public void setRepeatMode_immediateHandling_updatesStateAndInformsListeners() {
State state =
new State.Builder()
.setAvailableCommands(new Commands.Builder().addAllCommands().build())
.build();
// Set a different one to the one requested to ensure the updated state is used.
State updatedState = state.buildUpon().setRepeatMode(Player.REPEAT_MODE_ALL).build();
SimpleBasePlayer player =
new SimpleBasePlayer(Looper.myLooper()) {
private State playerState = state;
@Override
protected State getState() {
return playerState;
}
@Override
protected ListenableFuture<?> handleSetRepeatMode(@Player.RepeatMode int repeatMode) {
playerState = updatedState;
return Futures.immediateVoidFuture();
}
};
Listener listener = mock(Listener.class);
player.addListener(listener);
player.setRepeatMode(Player.REPEAT_MODE_ONE);
assertThat(player.getRepeatMode()).isEqualTo(Player.REPEAT_MODE_ALL);
verify(listener).onRepeatModeChanged(Player.REPEAT_MODE_ALL);
verifyNoMoreInteractions(listener);
}
@Test
public void setRepeatMode_asyncHandling_usesPlaceholderStateAndInformsListeners() {
State state =
new State.Builder()
.setAvailableCommands(new Commands.Builder().addAllCommands().build())
.build();
// Set a new repeat mode to see a difference between the placeholder and new state.
State updatedState = state.buildUpon().setRepeatMode(Player.REPEAT_MODE_ALL).build();
SettableFuture<?> future = SettableFuture.create();
SimpleBasePlayer player =
new SimpleBasePlayer(Looper.myLooper()) {
@Override
protected State getState() {
return future.isDone() ? updatedState : state;
}
@Override
protected ListenableFuture<?> handleSetRepeatMode(@Player.RepeatMode int repeatMode) {
return future;
}
};
Listener listener = mock(Listener.class);
player.addListener(listener);
player.setRepeatMode(Player.REPEAT_MODE_ONE);
// Verify placeholder state and listener calls.
assertThat(player.getRepeatMode()).isEqualTo(Player.REPEAT_MODE_ONE);
verify(listener).onRepeatModeChanged(Player.REPEAT_MODE_ONE);
verifyNoMoreInteractions(listener);
future.set(null);
// Verify actual state update.
assertThat(player.getRepeatMode()).isEqualTo(Player.REPEAT_MODE_ALL);
verify(listener).onRepeatModeChanged(Player.REPEAT_MODE_ALL);
verifyNoMoreInteractions(listener);
}
@Test
public void setRepeatMode_withoutAvailableCommand_isNotForwarded() {
State state =
new State.Builder()
.setAvailableCommands(
new Commands.Builder()
.addAllCommands()
.remove(Player.COMMAND_SET_REPEAT_MODE)
.build())
.build();
AtomicBoolean callForwarded = new AtomicBoolean();
SimpleBasePlayer player =
new SimpleBasePlayer(Looper.myLooper()) {
@Override
protected State getState() {
return state;
}
@Override
protected ListenableFuture<?> handleSetRepeatMode(@Player.RepeatMode int repeatMode) {
callForwarded.set(true);
return Futures.immediateVoidFuture();
}
};
player.setRepeatMode(Player.REPEAT_MODE_ONE);
assertThat(callForwarded.get()).isFalse();
}
@Test
public void setShuffleModeEnabled_immediateHandling_updatesStateAndInformsListeners() {
State state =
new State.Builder()
.setAvailableCommands(new Commands.Builder().addAllCommands().build())
.build();
// Also change the repeat mode to ensure the updated state is used.
State updatedState =
state.buildUpon().setShuffleModeEnabled(true).setRepeatMode(Player.REPEAT_MODE_ALL).build();
SimpleBasePlayer player =
new SimpleBasePlayer(Looper.myLooper()) {
private State playerState = state;
@Override
protected State getState() {
return playerState;
}
@Override
protected ListenableFuture<?> handleSetShuffleModeEnabled(boolean shuffleModeEnabled) {
playerState = updatedState;
return Futures.immediateVoidFuture();
}
};
Listener listener = mock(Listener.class);
player.addListener(listener);
player.setShuffleModeEnabled(true);
assertThat(player.getShuffleModeEnabled()).isTrue();
assertThat(player.getRepeatMode()).isEqualTo(Player.REPEAT_MODE_ALL);
verify(listener).onShuffleModeEnabledChanged(true);
verify(listener).onRepeatModeChanged(Player.REPEAT_MODE_ALL);
verifyNoMoreInteractions(listener);
}
@Test
public void setShuffleModeEnabled_asyncHandling_usesPlaceholderStateAndInformsListeners() {
State state =
new State.Builder()
.setAvailableCommands(new Commands.Builder().addAllCommands().build())
.build();
SettableFuture<?> future = SettableFuture.create();
SimpleBasePlayer player =
new SimpleBasePlayer(Looper.myLooper()) {
@Override
protected State getState() {
// Always return the same state to revert the shuffle mode change. This allows to see a
// difference between the placeholder and new state.
return state;
}
@Override
protected ListenableFuture<?> handleSetShuffleModeEnabled(boolean shuffleModeEnabled) {
return future;
}
};
Listener listener = mock(Listener.class);
player.addListener(listener);
player.setShuffleModeEnabled(true);
// Verify placeholder state and listener calls.
assertThat(player.getShuffleModeEnabled()).isTrue();
verify(listener).onShuffleModeEnabledChanged(true);
verifyNoMoreInteractions(listener);
future.set(null);
// Verify actual state update.
assertThat(player.getShuffleModeEnabled()).isFalse();
verify(listener).onShuffleModeEnabledChanged(false);
verifyNoMoreInteractions(listener);
}
@Test
public void setShuffleModeEnabled_withoutAvailableCommand_isNotForwarded() {
State state =
new State.Builder()
.setAvailableCommands(
new Commands.Builder()
.addAllCommands()
.remove(Player.COMMAND_SET_SHUFFLE_MODE)
.build())
.build();
AtomicBoolean callForwarded = new AtomicBoolean();
SimpleBasePlayer player =
new SimpleBasePlayer(Looper.myLooper()) {
@Override
protected State getState() {
return state;
}
@Override
protected ListenableFuture<?> handleSetShuffleModeEnabled(boolean shuffleModeEnabled) {
callForwarded.set(true);
return Futures.immediateVoidFuture();
}
};
player.setShuffleModeEnabled(true);
assertThat(callForwarded.get()).isFalse();
}
@Test
public void setPlaybackParameters_immediateHandling_updatesStateAndInformsListeners() {
State state =
new State.Builder()
.setAvailableCommands(new Commands.Builder().addAllCommands().build())
.build();
// Set a different one to the one requested to ensure the updated state is used.
State updatedState =
state.buildUpon().setPlaybackParameters(new PlaybackParameters(/* speed= */ 3f)).build();
SimpleBasePlayer player =
new SimpleBasePlayer(Looper.myLooper()) {
private State playerState = state;
@Override
protected State getState() {
return playerState;
}
@Override
protected ListenableFuture<?> handleSetPlaybackParameters(
PlaybackParameters playbackParameters) {
playerState = updatedState;
return Futures.immediateVoidFuture();
}
};
Listener listener = mock(Listener.class);
player.addListener(listener);
player.setPlaybackParameters(new PlaybackParameters(/* speed= */ 2f));
assertThat(player.getPlaybackParameters()).isEqualTo(new PlaybackParameters(/* speed= */ 3f));
verify(listener).onPlaybackParametersChanged(new PlaybackParameters(/* speed= */ 3f));
verifyNoMoreInteractions(listener);
}
@Test
public void setPlaybackParameters_asyncHandling_usesPlaceholderStateAndInformsListeners() {
State state =
new State.Builder()
.setAvailableCommands(new Commands.Builder().addAllCommands().build())
.build();
// Set a new repeat mode to see a difference between the placeholder and new state.
State updatedState =
state.buildUpon().setPlaybackParameters(new PlaybackParameters(/* speed= */ 3f)).build();
SettableFuture<?> future = SettableFuture.create();
SimpleBasePlayer player =
new SimpleBasePlayer(Looper.myLooper()) {
@Override
protected State getState() {
return future.isDone() ? updatedState : state;
}
@Override
protected ListenableFuture<?> handleSetPlaybackParameters(
PlaybackParameters playbackParameters) {
return future;
}
};
Listener listener = mock(Listener.class);
player.addListener(listener);
player.setPlaybackParameters(new PlaybackParameters(/* speed= */ 2f));
// Verify placeholder state and listener calls.
assertThat(player.getPlaybackParameters()).isEqualTo(new PlaybackParameters(/* speed= */ 2f));
verify(listener).onPlaybackParametersChanged(new PlaybackParameters(/* speed= */ 2f));
verifyNoMoreInteractions(listener);
future.set(null);
// Verify actual state update.
assertThat(player.getPlaybackParameters()).isEqualTo(new PlaybackParameters(/* speed= */ 3f));
verify(listener).onPlaybackParametersChanged(new PlaybackParameters(/* speed= */ 3f));
verifyNoMoreInteractions(listener);
}
@Test
public void setPlaybackParameters_withoutAvailableCommand_isNotForwarded() {
State state =
new State.Builder()
.setAvailableCommands(
new Commands.Builder()
.addAllCommands()
.remove(Player.COMMAND_SET_SPEED_AND_PITCH)
.build())
.build();
AtomicBoolean callForwarded = new AtomicBoolean();
SimpleBasePlayer player =
new SimpleBasePlayer(Looper.myLooper()) {
@Override
protected State getState() {
return state;
}
@Override
protected ListenableFuture<?> handleSetPlaybackParameters(
PlaybackParameters playbackParameters) {
callForwarded.set(true);
return Futures.immediateVoidFuture();
}
};
player.setPlaybackParameters(new PlaybackParameters(/* speed= */ 2f));
assertThat(callForwarded.get()).isFalse();
}
@Test
public void setTrackSelectionParameters_immediateHandling_updatesStateAndInformsListeners() {
State state =
new State.Builder()
.setAvailableCommands(new Commands.Builder().addAllCommands().build())
.build();
// Set a different one to the one requested to ensure the updated state is used.
TrackSelectionParameters updatedParameters =
new TrackSelectionParameters.Builder(ApplicationProvider.getApplicationContext())
.setMaxVideoBitrate(3000)
.build();
State updatedState = state.buildUpon().setTrackSelectionParameters(updatedParameters).build();
SimpleBasePlayer player =
new SimpleBasePlayer(Looper.myLooper()) {
private State playerState = state;
@Override
protected State getState() {
return playerState;
}
@Override
protected ListenableFuture<?> handleSetTrackSelectionParameters(
TrackSelectionParameters trackSelectionParameters) {
playerState = updatedState;
return Futures.immediateVoidFuture();
}
};
Listener listener = mock(Listener.class);
player.addListener(listener);
player.setTrackSelectionParameters(
new TrackSelectionParameters.Builder(ApplicationProvider.getApplicationContext())
.setMaxVideoBitrate(1000)
.build());
assertThat(player.getTrackSelectionParameters()).isEqualTo(updatedParameters);
verify(listener).onTrackSelectionParametersChanged(updatedParameters);
verifyNoMoreInteractions(listener);
}
@Test
public void setTrackSelectionParameters_asyncHandling_usesPlaceholderStateAndInformsListeners() {
State state =
new State.Builder()
.setAvailableCommands(new Commands.Builder().addAllCommands().build())
.build();
// Set new parameters to see a difference between the placeholder and new state.
TrackSelectionParameters updatedParameters =
new TrackSelectionParameters.Builder(ApplicationProvider.getApplicationContext())
.setMaxVideoBitrate(3000)
.build();
State updatedState = state.buildUpon().setTrackSelectionParameters(updatedParameters).build();
SettableFuture<?> future = SettableFuture.create();
SimpleBasePlayer player =
new SimpleBasePlayer(Looper.myLooper()) {
@Override
protected State getState() {
return future.isDone() ? updatedState : state;
}
@Override
protected ListenableFuture<?> handleSetTrackSelectionParameters(
TrackSelectionParameters trackSelectionParameters) {
return future;
}
};
Listener listener = mock(Listener.class);
player.addListener(listener);
TrackSelectionParameters requestedParameters =
new TrackSelectionParameters.Builder(ApplicationProvider.getApplicationContext())
.setMaxVideoBitrate(3000)
.build();
player.setTrackSelectionParameters(requestedParameters);
// Verify placeholder state and listener calls.
assertThat(player.getTrackSelectionParameters()).isEqualTo(requestedParameters);
verify(listener).onTrackSelectionParametersChanged(requestedParameters);
verifyNoMoreInteractions(listener);
future.set(null);
// Verify actual state update.
assertThat(player.getTrackSelectionParameters()).isEqualTo(updatedParameters);
verify(listener).onTrackSelectionParametersChanged(updatedParameters);
verifyNoMoreInteractions(listener);
}
@Test
public void setTrackSelectionParameters_withoutAvailableCommand_isNotForwarded() {
State state =
new State.Builder()
.setAvailableCommands(
new Commands.Builder()
.addAllCommands()
.remove(Player.COMMAND_SET_TRACK_SELECTION_PARAMETERS)
.build())
.build();
AtomicBoolean callForwarded = new AtomicBoolean();
SimpleBasePlayer player =
new SimpleBasePlayer(Looper.myLooper()) {
@Override
protected State getState() {
return state;
}
@Override
protected ListenableFuture<?> handleSetTrackSelectionParameters(
TrackSelectionParameters trackSelectionParameters) {
callForwarded.set(true);
return Futures.immediateVoidFuture();
}
};
player.setTrackSelectionParameters(
new TrackSelectionParameters.Builder(ApplicationProvider.getApplicationContext())
.setMaxVideoBitrate(1000)
.build());
assertThat(callForwarded.get()).isFalse();
}
@Test
public void setPlaylistMetadata_immediateHandling_updatesStateAndInformsListeners() {
State state =
new State.Builder()
.setAvailableCommands(new Commands.Builder().addAllCommands().build())
.build();
// Set a different one to the one requested to ensure the updated state is used.
MediaMetadata updatedMetadata = new MediaMetadata.Builder().setArtist("artist").build();
State updatedState = state.buildUpon().setPlaylistMetadata(updatedMetadata).build();
SimpleBasePlayer player =
new SimpleBasePlayer(Looper.myLooper()) {
private State playerState = state;
@Override
protected State getState() {
return playerState;
}
@Override
protected ListenableFuture<?> handleSetPlaylistMetadata(MediaMetadata playlistMetadata) {
playerState = updatedState;
return Futures.immediateVoidFuture();
}
};
Listener listener = mock(Listener.class);
player.addListener(listener);
player.setPlaylistMetadata(new MediaMetadata.Builder().setTitle("title").build());
assertThat(player.getPlaylistMetadata()).isEqualTo(updatedMetadata);
verify(listener).onPlaylistMetadataChanged(updatedMetadata);
verifyNoMoreInteractions(listener);
}
@Test
public void setPlaylistMetadata_asyncHandling_usesPlaceholderStateAndInformsListeners() {
State state =
new State.Builder()
.setAvailableCommands(new Commands.Builder().addAllCommands().build())
.build();
// Set new metadata to see a difference between the placeholder and new state.
MediaMetadata updatedMetadata = new MediaMetadata.Builder().setArtist("artist").build();
State updatedState = state.buildUpon().setPlaylistMetadata(updatedMetadata).build();
SettableFuture<?> future = SettableFuture.create();
SimpleBasePlayer player =
new SimpleBasePlayer(Looper.myLooper()) {
@Override
protected State getState() {
return future.isDone() ? updatedState : state;
}
@Override
protected ListenableFuture<?> handleSetPlaylistMetadata(MediaMetadata playlistMetadata) {
return future;
}
};
Listener listener = mock(Listener.class);
player.addListener(listener);
MediaMetadata requestedMetadata = new MediaMetadata.Builder().setTitle("title").build();
player.setPlaylistMetadata(requestedMetadata);
// Verify placeholder state and listener calls.
assertThat(player.getPlaylistMetadata()).isEqualTo(requestedMetadata);
verify(listener).onPlaylistMetadataChanged(requestedMetadata);
verifyNoMoreInteractions(listener);
future.set(null);
// Verify actual state update.
assertThat(player.getPlaylistMetadata()).isEqualTo(updatedMetadata);
verify(listener).onPlaylistMetadataChanged(updatedMetadata);
verifyNoMoreInteractions(listener);
}
@Test
public void setPlaylistMetadata_withoutAvailableCommand_isNotForwarded() {
State state =
new State.Builder()
.setAvailableCommands(
new Commands.Builder()
.addAllCommands()
.remove(Player.COMMAND_SET_MEDIA_ITEMS_METADATA)
.build())
.build();
AtomicBoolean callForwarded = new AtomicBoolean();
SimpleBasePlayer player =
new SimpleBasePlayer(Looper.myLooper()) {
@Override
protected State getState() {
return state;
}
@Override
protected ListenableFuture<?> handleSetPlaylistMetadata(MediaMetadata playlistMetadata) {
callForwarded.set(true);
return Futures.immediateVoidFuture();
}
};
player.setPlaylistMetadata(new MediaMetadata.Builder().setTitle("title").build());
assertThat(callForwarded.get()).isFalse();
}
@Test
public void setVolume_immediateHandling_updatesStateAndInformsListeners() {
State state =
new State.Builder()
.setAvailableCommands(new Commands.Builder().addAllCommands().build())
.build();
// Set a different one to the one requested to ensure the updated state is used.
State updatedState = state.buildUpon().setVolume(.8f).build();
SimpleBasePlayer player =
new SimpleBasePlayer(Looper.myLooper()) {
private State playerState = state;
@Override
protected State getState() {
return playerState;
}
@Override
protected ListenableFuture<?> handleSetVolume(float volume) {
playerState = updatedState;
return Futures.immediateVoidFuture();
}
};
Listener listener = mock(Listener.class);
player.addListener(listener);
player.setVolume(.5f);
assertThat(player.getVolume()).isEqualTo(.8f);
verify(listener).onVolumeChanged(.8f);
verifyNoMoreInteractions(listener);
}
@Test
public void setVolume_asyncHandling_usesPlaceholderStateAndInformsListeners() {
State state =
new State.Builder()
.setAvailableCommands(new Commands.Builder().addAllCommands().build())
.build();
// Set a new volume to see a difference between the placeholder and new state.
State updatedState = state.buildUpon().setVolume(.8f).build();
SettableFuture<?> future = SettableFuture.create();
SimpleBasePlayer player =
new SimpleBasePlayer(Looper.myLooper()) {
@Override
protected State getState() {
return future.isDone() ? updatedState : state;
}
@Override
protected ListenableFuture<?> handleSetVolume(float volume) {
return future;
}
};
Listener listener = mock(Listener.class);
player.addListener(listener);
player.setVolume(.5f);
// Verify placeholder state and listener calls.
assertThat(player.getVolume()).isEqualTo(.5f);
verify(listener).onVolumeChanged(.5f);
verifyNoMoreInteractions(listener);
future.set(null);
// Verify actual state update.
assertThat(player.getVolume()).isEqualTo(.8f);
verify(listener).onVolumeChanged(.8f);
verifyNoMoreInteractions(listener);
}
@Test
public void setVolume_withoutAvailableCommand_isNotForwarded() {
State state =
new State.Builder()
.setAvailableCommands(
new Commands.Builder().addAllCommands().remove(Player.COMMAND_SET_VOLUME).build())
.build();
AtomicBoolean callForwarded = new AtomicBoolean();
SimpleBasePlayer player =
new SimpleBasePlayer(Looper.myLooper()) {
@Override
protected State getState() {
return state;
}
@Override
protected ListenableFuture<?> handleSetVolume(float volume) {
callForwarded.set(true);
return Futures.immediateVoidFuture();
}
};
player.setVolume(.5f);
assertThat(callForwarded.get()).isFalse();
}
@Test
public void setDeviceVolume_immediateHandling_updatesStateAndInformsListeners() {
State state =
new State.Builder()
.setAvailableCommands(new Commands.Builder().addAllCommands().build())
.build();
// Set a different one to the one requested to ensure the updated state is used.
State updatedState = state.buildUpon().setDeviceVolume(6).build();
SimpleBasePlayer player =
new SimpleBasePlayer(Looper.myLooper()) {
private State playerState = state;
@Override
protected State getState() {
return playerState;
}
@Override
protected ListenableFuture<?> handleSetDeviceVolume(int volume) {
playerState = updatedState;
return Futures.immediateVoidFuture();
}
};
Listener listener = mock(Listener.class);
player.addListener(listener);
player.setDeviceVolume(3);
assertThat(player.getDeviceVolume()).isEqualTo(6);
verify(listener).onDeviceVolumeChanged(6, /* muted= */ false);
verifyNoMoreInteractions(listener);
}
@Test
public void setDeviceVolume_asyncHandling_usesPlaceholderStateAndInformsListeners() {
State state =
new State.Builder()
.setAvailableCommands(new Commands.Builder().addAllCommands().build())
.build();
// Set a new volume to see a difference between the placeholder and new state.
State updatedState = state.buildUpon().setDeviceVolume(6).build();
SettableFuture<?> future = SettableFuture.create();
SimpleBasePlayer player =
new SimpleBasePlayer(Looper.myLooper()) {
@Override
protected State getState() {
return future.isDone() ? updatedState : state;
}
@Override
protected ListenableFuture<?> handleSetDeviceVolume(int volume) {
return future;
}
};
Listener listener = mock(Listener.class);
player.addListener(listener);
player.setDeviceVolume(3);
// Verify placeholder state and listener calls.
assertThat(player.getDeviceVolume()).isEqualTo(3);
verify(listener).onDeviceVolumeChanged(3, /* muted= */ false);
verifyNoMoreInteractions(listener);
future.set(null);
// Verify actual state update.
assertThat(player.getDeviceVolume()).isEqualTo(6);
verify(listener).onDeviceVolumeChanged(6, /* muted= */ false);
verifyNoMoreInteractions(listener);
}
@Test
public void setDeviceVolume_withoutAvailableCommand_isNotForwarded() {
State state =
new State.Builder()
.setAvailableCommands(
new Commands.Builder()
.addAllCommands()
.remove(Player.COMMAND_SET_DEVICE_VOLUME)
.build())
.build();
AtomicBoolean callForwarded = new AtomicBoolean();
SimpleBasePlayer player =
new SimpleBasePlayer(Looper.myLooper()) {
@Override
protected State getState() {
return state;
}
@Override
protected ListenableFuture<?> handleSetDeviceVolume(int volume) {
callForwarded.set(true);
return Futures.immediateVoidFuture();
}
};
player.setDeviceVolume(3);
assertThat(callForwarded.get()).isFalse();
}
@Test
public void increaseDeviceVolume_immediateHandling_updatesStateAndInformsListeners() {
State state =
new State.Builder()
.setAvailableCommands(new Commands.Builder().addAllCommands().build())
.setDeviceVolume(3)
.build();
// Set a different one to the one requested to ensure the updated state is used.
State updatedState = state.buildUpon().setDeviceVolume(6).build();
SimpleBasePlayer player =
new SimpleBasePlayer(Looper.myLooper()) {
private State playerState = state;
@Override
protected State getState() {
return playerState;
}
@Override
protected ListenableFuture<?> handleIncreaseDeviceVolume() {
playerState = updatedState;
return Futures.immediateVoidFuture();
}
};
Listener listener = mock(Listener.class);
player.addListener(listener);
player.increaseDeviceVolume();
assertThat(player.getDeviceVolume()).isEqualTo(6);
verify(listener).onDeviceVolumeChanged(6, /* muted= */ false);
verifyNoMoreInteractions(listener);
}
@Test
public void increaseDeviceVolume_asyncHandling_usesPlaceholderStateAndInformsListeners() {
State state =
new State.Builder()
.setAvailableCommands(new Commands.Builder().addAllCommands().build())
.setDeviceVolume(3)
.build();
// Set a new volume to see a difference between the placeholder and new state.
State updatedState = state.buildUpon().setDeviceVolume(6).build();
SettableFuture<?> future = SettableFuture.create();
SimpleBasePlayer player =
new SimpleBasePlayer(Looper.myLooper()) {
@Override
protected State getState() {
return future.isDone() ? updatedState : state;
}
@Override
protected ListenableFuture<?> handleIncreaseDeviceVolume() {
return future;
}
};
Listener listener = mock(Listener.class);
player.addListener(listener);
player.increaseDeviceVolume();
// Verify placeholder state and listener calls.
assertThat(player.getDeviceVolume()).isEqualTo(4);
verify(listener).onDeviceVolumeChanged(4, /* muted= */ false);
verifyNoMoreInteractions(listener);
future.set(null);
// Verify actual state update.
assertThat(player.getDeviceVolume()).isEqualTo(6);
verify(listener).onDeviceVolumeChanged(6, /* muted= */ false);
verifyNoMoreInteractions(listener);
}
@Test
public void increaseDeviceVolume_withoutAvailableCommand_isNotForwarded() {
State state =
new State.Builder()
.setAvailableCommands(
new Commands.Builder()
.addAllCommands()
.remove(Player.COMMAND_ADJUST_DEVICE_VOLUME)
.build())
.build();
AtomicBoolean callForwarded = new AtomicBoolean();
SimpleBasePlayer player =
new SimpleBasePlayer(Looper.myLooper()) {
@Override
protected State getState() {
return state;
}
@Override
protected ListenableFuture<?> handleIncreaseDeviceVolume() {
callForwarded.set(true);
return Futures.immediateVoidFuture();
}
};
player.increaseDeviceVolume();
assertThat(callForwarded.get()).isFalse();
}
@Test
public void decreaseDeviceVolume_immediateHandling_updatesStateAndInformsListeners() {
State state =
new State.Builder()
.setAvailableCommands(new Commands.Builder().addAllCommands().build())
.setDeviceVolume(3)
.build();
// Set a different one to the one requested to ensure the updated state is used.
State updatedState = state.buildUpon().setDeviceVolume(1).build();
SimpleBasePlayer player =
new SimpleBasePlayer(Looper.myLooper()) {
private State playerState = state;
@Override
protected State getState() {
return playerState;
}
@Override
protected ListenableFuture<?> handleDecreaseDeviceVolume() {
playerState = updatedState;
return Futures.immediateVoidFuture();
}
};
Listener listener = mock(Listener.class);
player.addListener(listener);
player.decreaseDeviceVolume();
assertThat(player.getDeviceVolume()).isEqualTo(1);
verify(listener).onDeviceVolumeChanged(1, /* muted= */ false);
verifyNoMoreInteractions(listener);
}
@Test
public void decreaseDeviceVolume_asyncHandling_usesPlaceholderStateAndInformsListeners() {
State state =
new State.Builder()
.setAvailableCommands(new Commands.Builder().addAllCommands().build())
.setDeviceVolume(3)
.build();
// Set a new volume to see a difference between the placeholder and new state.
State updatedState = state.buildUpon().setDeviceVolume(1).build();
SettableFuture<?> future = SettableFuture.create();
SimpleBasePlayer player =
new SimpleBasePlayer(Looper.myLooper()) {
@Override
protected State getState() {
return future.isDone() ? updatedState : state;
}
@Override
protected ListenableFuture<?> handleDecreaseDeviceVolume() {
return future;
}
};
Listener listener = mock(Listener.class);
player.addListener(listener);
player.decreaseDeviceVolume();
// Verify placeholder state and listener calls.
assertThat(player.getDeviceVolume()).isEqualTo(2);
verify(listener).onDeviceVolumeChanged(2, /* muted= */ false);
verifyNoMoreInteractions(listener);
future.set(null);
// Verify actual state update.
assertThat(player.getDeviceVolume()).isEqualTo(1);
verify(listener).onDeviceVolumeChanged(1, /* muted= */ false);
verifyNoMoreInteractions(listener);
}
@Test
public void decreaseDeviceVolume_withoutAvailableCommand_isNotForwarded() {
State state =
new State.Builder()
.setAvailableCommands(
new Commands.Builder()
.addAllCommands()
.remove(Player.COMMAND_ADJUST_DEVICE_VOLUME)
.build())
.build();
AtomicBoolean callForwarded = new AtomicBoolean();
SimpleBasePlayer player =
new SimpleBasePlayer(Looper.myLooper()) {
@Override
protected State getState() {
return state;
}
@Override
protected ListenableFuture<?> handleDecreaseDeviceVolume() {
callForwarded.set(true);
return Futures.immediateVoidFuture();
}
};
player.decreaseDeviceVolume();
assertThat(callForwarded.get()).isFalse();
}
@Test
public void setDeviceMuted_immediateHandling_updatesStateAndInformsListeners() {
State state =
new State.Builder()
.setAvailableCommands(new Commands.Builder().addAllCommands().build())
.build();
// Also change the volume to ensure the updated state is used.
State updatedState = state.buildUpon().setIsDeviceMuted(true).setDeviceVolume(6).build();
SimpleBasePlayer player =
new SimpleBasePlayer(Looper.myLooper()) {
private State playerState = state;
@Override
protected State getState() {
return playerState;
}
@Override
protected ListenableFuture<?> handleSetDeviceMuted(boolean muted) {
playerState = updatedState;
return Futures.immediateVoidFuture();
}
};
Listener listener = mock(Listener.class);
player.addListener(listener);
player.setDeviceMuted(true);
assertThat(player.isDeviceMuted()).isTrue();
assertThat(player.getDeviceVolume()).isEqualTo(6);
verify(listener).onDeviceVolumeChanged(6, /* muted= */ true);
verifyNoMoreInteractions(listener);
}
@Test
public void setDeviceMuted_asyncHandling_usesPlaceholderStateAndInformsListeners() {
State state =
new State.Builder()
.setAvailableCommands(new Commands.Builder().addAllCommands().build())
.build();
SettableFuture<?> future = SettableFuture.create();
SimpleBasePlayer player =
new SimpleBasePlayer(Looper.myLooper()) {
@Override
protected State getState() {
// Always return the same state to revert the muted change. This allows to see a
// difference between the placeholder and new state.
return state;
}
@Override
protected ListenableFuture<?> handleSetDeviceMuted(boolean muted) {
return future;
}
};
Listener listener = mock(Listener.class);
player.addListener(listener);
player.setDeviceMuted(true);
// Verify placeholder state and listener calls.
assertThat(player.isDeviceMuted()).isTrue();
verify(listener).onDeviceVolumeChanged(0, /* muted= */ true);
verifyNoMoreInteractions(listener);
future.set(null);
// Verify actual state update.
assertThat(player.isDeviceMuted()).isFalse();
verify(listener).onDeviceVolumeChanged(0, /* muted= */ false);
verifyNoMoreInteractions(listener);
}
@Test
public void setDeviceMuted_withoutAvailableCommand_isNotForwarded() {
State state =
new State.Builder()
.setAvailableCommands(
new Commands.Builder()
.addAllCommands()
.remove(Player.COMMAND_ADJUST_DEVICE_VOLUME)
.build())
.build();
AtomicBoolean callForwarded = new AtomicBoolean();
SimpleBasePlayer player =
new SimpleBasePlayer(Looper.myLooper()) {
@Override
protected State getState() {
return state;
}
@Override
protected ListenableFuture<?> handleSetDeviceMuted(boolean muted) {
callForwarded.set(true);
return Futures.immediateVoidFuture();
}
};
player.setDeviceMuted(true);
assertThat(callForwarded.get()).isFalse();
}
@Test
public void setVideoSurface_immediateHandling_updatesStateAndInformsListeners() {
State state =
new State.Builder()
.setAvailableCommands(new Commands.Builder().addAllCommands().build())
.setSurfaceSize(Size.ZERO)
.build();
Size updatedSize = new Size(/* width= */ 300, /* height= */ 200);
State updatedState = state.buildUpon().setSurfaceSize(updatedSize).build();
SimpleBasePlayer player =
new SimpleBasePlayer(Looper.myLooper()) {
private State playerState = state;
@Override
protected State getState() {
return playerState;
}
@Override
protected ListenableFuture<?> handleSetVideoOutput(Object videoOutput) {
playerState = updatedState;
return Futures.immediateVoidFuture();
}
};
Listener listener = mock(Listener.class);
player.addListener(listener);
player.setVideoSurface(new Surface(new SurfaceTexture(0)));
assertThat(player.getSurfaceSize()).isEqualTo(updatedSize);
verify(listener).onSurfaceSizeChanged(updatedSize.getWidth(), updatedSize.getHeight());
verifyNoMoreInteractions(listener);
}
@Test
public void setVideoSurface_asyncHandling_usesPlaceholderStateAndInformsListeners() {
State state =
new State.Builder()
.setAvailableCommands(new Commands.Builder().addAllCommands().build())
.setSurfaceSize(Size.ZERO)
.build();
SettableFuture<?> future = SettableFuture.create();
Size updatedSize = new Size(/* width= */ 300, /* height= */ 200);
State updatedState = state.buildUpon().setSurfaceSize(updatedSize).build();
SimpleBasePlayer player =
new SimpleBasePlayer(Looper.myLooper()) {
@Override
protected State getState() {
return future.isDone() ? updatedState : state;
}
@Override
protected ListenableFuture<?> handleSetVideoOutput(Object videoOutput) {
return future;
}
};
Listener listener = mock(Listener.class);
player.addListener(listener);
player.setVideoSurface(new Surface(new SurfaceTexture(0)));
// Verify placeholder state and listener calls.
assertThat(player.getSurfaceSize()).isEqualTo(Size.UNKNOWN);
verify(listener)
.onSurfaceSizeChanged(/* width= */ C.LENGTH_UNSET, /* height= */ C.LENGTH_UNSET);
verifyNoMoreInteractions(listener);
future.set(null);
// Verify actual state update.
assertThat(player.getSurfaceSize()).isEqualTo(updatedSize);
verify(listener).onSurfaceSizeChanged(updatedSize.getWidth(), updatedSize.getHeight());
verifyNoMoreInteractions(listener);
}
@Test
public void setVideoSurface_withoutAvailableCommand_isNotForwarded() {
State state =
new State.Builder()
.setAvailableCommands(
new Commands.Builder()
.addAllCommands()
.remove(Player.COMMAND_SET_VIDEO_SURFACE)
.build())
.build();
AtomicBoolean callForwarded = new AtomicBoolean();
SimpleBasePlayer player =
new SimpleBasePlayer(Looper.myLooper()) {
@Override
protected State getState() {
return state;
}
@Override
protected ListenableFuture<?> handleSetVideoOutput(Object videoOutput) {
callForwarded.set(true);
return Futures.immediateVoidFuture();
}
};
player.setVideoSurface(new Surface(new SurfaceTexture(0)));
assertThat(callForwarded.get()).isFalse();
}
@Test
public void clearVideoSurface_immediateHandling_updatesStateAndInformsListeners() {
State state =
new State.Builder()
.setAvailableCommands(new Commands.Builder().addAllCommands().build())
.setSurfaceSize(new Size(/* width= */ 300, /* height= */ 200))
.build();
// Change something else in addition to ensure we actually use the updated state.
State updatedState =
state.buildUpon().setSurfaceSize(Size.ZERO).setRepeatMode(Player.REPEAT_MODE_ONE).build();
SimpleBasePlayer player =
new SimpleBasePlayer(Looper.myLooper()) {
private State playerState = state;
@Override
protected State getState() {
return playerState;
}
@Override
protected ListenableFuture<?> handleClearVideoOutput(@Nullable Object videoOutput) {
playerState = updatedState;
return Futures.immediateVoidFuture();
}
};
Listener listener = mock(Listener.class);
player.addListener(listener);
player.clearVideoSurface();
assertThat(player.getSurfaceSize()).isEqualTo(Size.ZERO);
assertThat(player.getRepeatMode()).isEqualTo(Player.REPEAT_MODE_ONE);
verify(listener).onSurfaceSizeChanged(/* width= */ 0, /* height= */ 0);
verify(listener).onRepeatModeChanged(Player.REPEAT_MODE_ONE);
verifyNoMoreInteractions(listener);
}
@Test
public void clearVideoSurface_asyncHandling_usesPlaceholderStateAndInformsListeners() {
State state =
new State.Builder()
.setAvailableCommands(new Commands.Builder().addAllCommands().build())
.setSurfaceSize(new Size(/* width= */ 300, /* height= */ 200))
.build();
// Change something else in addition to ensure we actually use the updated state.
State updatedState =
state.buildUpon().setSurfaceSize(Size.ZERO).setRepeatMode(Player.REPEAT_MODE_ONE).build();
SettableFuture<?> future = SettableFuture.create();
SimpleBasePlayer player =
new SimpleBasePlayer(Looper.myLooper()) {
@Override
protected State getState() {
return future.isDone() ? updatedState : state;
}
@Override
protected ListenableFuture<?> handleClearVideoOutput(@Nullable Object videoOutput) {
return future;
}
};
Listener listener = mock(Listener.class);
player.addListener(listener);
player.clearVideoSurface();
// Verify placeholder state and listener calls.
assertThat(player.getSurfaceSize()).isEqualTo(Size.ZERO);
verify(listener).onSurfaceSizeChanged(/* width= */ 0, /* height= */ 0);
verifyNoMoreInteractions(listener);
future.set(null);
// Verify actual state update.
assertThat(player.getSurfaceSize()).isEqualTo(Size.ZERO);
assertThat(player.getRepeatMode()).isEqualTo(Player.REPEAT_MODE_ONE);
verify(listener).onRepeatModeChanged(Player.REPEAT_MODE_ONE);
verifyNoMoreInteractions(listener);
}
@Test
public void clearVideoSurface_withoutAvailableCommand_isNotForwarded() {
State state =
new State.Builder()
.setAvailableCommands(
new Commands.Builder()
.addAllCommands()
.remove(Player.COMMAND_SET_VIDEO_SURFACE)
.build())
.build();
AtomicBoolean callForwarded = new AtomicBoolean();
SimpleBasePlayer player =
new SimpleBasePlayer(Looper.myLooper()) {
@Override
protected State getState() {
return state;
}
@Override
protected ListenableFuture<?> handleClearVideoOutput(@Nullable Object videoOutput) {
callForwarded.set(true);
return Futures.immediateVoidFuture();
}
};
player.clearVideoSurface();
assertThat(callForwarded.get()).isFalse();
}
private static Object[] getAnyArguments(Method method) { private static Object[] getAnyArguments(Method method) {
Object[] arguments = new Object[method.getParameterCount()]; Object[] arguments = new Object[method.getParameterCount()];
Class<?>[] argumentTypes = method.getParameterTypes(); Class<?>[] argumentTypes = method.getParameterTypes();
......
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