Commit 0dcdbf0a by kimvde Committed by Ian Baker

Add Player onAvailableCommandsChanged callback

PiperOrigin-RevId: 361122259
parent 3de81c6d
...@@ -105,6 +105,7 @@ public final class CastPlayer extends BasePlayer { ...@@ -105,6 +105,7 @@ public final class CastPlayer extends BasePlayer {
private CastTimeline currentTimeline; private CastTimeline currentTimeline;
private TrackGroupArray currentTrackGroups; private TrackGroupArray currentTrackGroups;
private TrackSelectionArray currentTrackSelection; private TrackSelectionArray currentTrackSelection;
private Commands availableCommands;
@Player.State private int playbackState; @Player.State private int playbackState;
private int currentWindowIndex; private int currentWindowIndex;
private long lastReportedPositionMs; private long lastReportedPositionMs;
...@@ -147,6 +148,7 @@ public final class CastPlayer extends BasePlayer { ...@@ -147,6 +148,7 @@ public final class CastPlayer extends BasePlayer {
currentTimeline = CastTimeline.EMPTY_CAST_TIMELINE; currentTimeline = CastTimeline.EMPTY_CAST_TIMELINE;
currentTrackGroups = TrackGroupArray.EMPTY; currentTrackGroups = TrackGroupArray.EMPTY;
currentTrackSelection = EMPTY_TRACK_SELECTION_ARRAY; currentTrackSelection = EMPTY_TRACK_SELECTION_ARRAY;
availableCommands = Commands.EMPTY;
pendingSeekWindowIndex = C.INDEX_UNSET; pendingSeekWindowIndex = C.INDEX_UNSET;
pendingSeekPositionMs = C.TIME_UNSET; pendingSeekPositionMs = C.TIME_UNSET;
...@@ -370,6 +372,11 @@ public final class CastPlayer extends BasePlayer { ...@@ -370,6 +372,11 @@ public final class CastPlayer extends BasePlayer {
} }
@Override @Override
public boolean isCommandAvailable(@Command int command) {
return availableCommands.contains(command);
}
@Override
public void prepare() { public void prepare() {
// Do nothing. // Do nothing.
} }
...@@ -452,6 +459,7 @@ public final class CastPlayer extends BasePlayer { ...@@ -452,6 +459,7 @@ public final class CastPlayer extends BasePlayer {
listeners.queueEvent( listeners.queueEvent(
Player.EVENT_POSITION_DISCONTINUITY, Player.EVENT_POSITION_DISCONTINUITY,
listener -> listener.onPositionDiscontinuity(DISCONTINUITY_REASON_SEEK)); listener -> listener.onPositionDiscontinuity(DISCONTINUITY_REASON_SEEK));
updateAvailableCommandsAndNotifyIfChanged();
} else if (pendingSeekCount == 0) { } else if (pendingSeekCount == 0) {
listeners.queueEvent(/* eventFlag= */ C.INDEX_UNSET, EventListener::onSeekProcessed); listeners.queueEvent(/* eventFlag= */ C.INDEX_UNSET, EventListener::onSeekProcessed);
} }
...@@ -645,6 +653,7 @@ public final class CastPlayer extends BasePlayer { ...@@ -645,6 +653,7 @@ public final class CastPlayer extends BasePlayer {
Player.EVENT_TRACKS_CHANGED, Player.EVENT_TRACKS_CHANGED,
listener -> listener.onTracksChanged(currentTrackGroups, currentTrackSelection)); listener -> listener.onTracksChanged(currentTrackGroups, currentTrackSelection));
} }
updateAvailableCommandsAndNotifyIfChanged();
listeners.flushEvents(); listeners.flushEvents();
} }
...@@ -693,6 +702,7 @@ public final class CastPlayer extends BasePlayer { ...@@ -693,6 +702,7 @@ public final class CastPlayer extends BasePlayer {
timeline, /* manifest= */ null, Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE); timeline, /* manifest= */ null, Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE);
listener.onTimelineChanged(timeline, Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE); listener.onTimelineChanged(timeline, Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE);
}); });
updateAvailableCommandsAndNotifyIfChanged();
} }
} }
...@@ -762,6 +772,16 @@ public final class CastPlayer extends BasePlayer { ...@@ -762,6 +772,16 @@ public final class CastPlayer extends BasePlayer {
return false; return false;
} }
private void updateAvailableCommandsAndNotifyIfChanged() {
Commands previousAvailableCommands = availableCommands;
availableCommands = getAvailableCommands();
if (!availableCommands.equals(previousAvailableCommands)) {
listeners.queueEvent(
Player.EVENT_AVAILABLE_COMMANDS_CHANGED,
listener -> listener.onAvailableCommandsChanged(availableCommands));
}
}
@Nullable @Nullable
private PendingResult<MediaChannelResult> setMediaItemsInternal( private PendingResult<MediaChannelResult> setMediaItemsInternal(
MediaQueueItem[] mediaQueueItems, MediaQueueItem[] mediaQueueItems,
...@@ -819,6 +839,7 @@ public final class CastPlayer extends BasePlayer { ...@@ -819,6 +839,7 @@ public final class CastPlayer extends BasePlayer {
this.repeatMode.value = repeatMode; this.repeatMode.value = repeatMode;
listeners.queueEvent( listeners.queueEvent(
Player.EVENT_REPEAT_MODE_CHANGED, listener -> listener.onRepeatModeChanged(repeatMode)); Player.EVENT_REPEAT_MODE_CHANGED, listener -> listener.onRepeatModeChanged(repeatMode));
updateAvailableCommandsAndNotifyIfChanged();
} }
} }
...@@ -1003,6 +1024,7 @@ public final class CastPlayer extends BasePlayer { ...@@ -1003,6 +1024,7 @@ public final class CastPlayer extends BasePlayer {
@Override @Override
public void onQueueStatusUpdated() { public void onQueueStatusUpdated() {
updateTimelineAndNotifyIfChanged(); updateTimelineAndNotifyIfChanged();
listeners.flushEvents();
} }
@Override @Override
......
...@@ -72,14 +72,6 @@ public abstract class BasePlayer implements Player { ...@@ -72,14 +72,6 @@ public abstract class BasePlayer implements Player {
} }
@Override @Override
public boolean isCommandAvailable(@Command int command) {
if (command == COMMAND_SEEK_TO_NEXT_MEDIA_ITEM) {
return hasNext();
}
throw new IllegalArgumentException();
}
@Override
public final void play() { public final void play() {
setPlayWhenReady(true); setPlayWhenReady(true);
} }
...@@ -262,4 +254,8 @@ public abstract class BasePlayer implements Player { ...@@ -262,4 +254,8 @@ public abstract class BasePlayer implements Player {
@RepeatMode int repeatMode = getRepeatMode(); @RepeatMode int repeatMode = getRepeatMode();
return repeatMode == REPEAT_MODE_ONE ? REPEAT_MODE_OFF : repeatMode; return repeatMode == REPEAT_MODE_ONE ? REPEAT_MODE_OFF : repeatMode;
} }
protected Commands getAvailableCommands() {
return new Commands.Builder().addIf(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM, hasNext()).build();
}
} }
...@@ -15,8 +15,11 @@ ...@@ -15,8 +15,11 @@
*/ */
package com.google.android.exoplayer2; package com.google.android.exoplayer2;
import static com.google.android.exoplayer2.util.Assertions.checkState;
import android.content.Context; import android.content.Context;
import android.os.Looper; import android.os.Looper;
import android.util.SparseBooleanArray;
import android.view.Surface; import android.view.Surface;
import android.view.SurfaceHolder; import android.view.SurfaceHolder;
import android.view.SurfaceView; import android.view.SurfaceView;
...@@ -482,6 +485,17 @@ public interface Player { ...@@ -482,6 +485,17 @@ public interface Player {
default void onLoadingChanged(boolean isLoading) {} default void onLoadingChanged(boolean isLoading) {}
/** /**
* Called when the value returned from {@link #isCommandAvailable(int)} changes for at least one
* {@link Command}.
*
* <p>{@link #onEvents(Player, Events)} will also be called to report this event along with
* other events that happen in the same {@link Looper} message queue iteration.
*
* @param availableCommands The available {@link Commands}.
*/
default void onAvailableCommandsChanged(Commands availableCommands) {}
/**
* @deprecated Use {@link #onPlaybackStateChanged(int)} and {@link * @deprecated Use {@link #onPlaybackStateChanged(int)} and {@link
* #onPlayWhenReadyChanged(boolean, int)} instead. * #onPlayWhenReadyChanged(boolean, int)} instead.
*/ */
...@@ -693,6 +707,105 @@ public interface Player { ...@@ -693,6 +707,105 @@ public interface Player {
} }
/** /**
* A set of {@link Command commands}.
*
* <p>Instances are immutable.
*/
final class Commands {
/** A builder for {@link Commands} instances. */
public static final class Builder {
private final SparseBooleanArray commandsArray;
private boolean buildCalled;
/** Creates a builder. */
public Builder() {
commandsArray = new SparseBooleanArray();
}
/** Creates a builder with the values of the provided {@link Commands}. */
private Builder(Commands commands) {
this.commandsArray = commands.commandsArray.clone();
}
/**
* Adds a {@link Command}.
*
* @param command A {@link Command}.
* @return This builder.
* @throws IllegalStateException If {@link #build()} has already been called.
*/
public Builder add(@Command int command) {
checkState(!buildCalled);
commandsArray.append(command, /* value= */ true);
return this;
}
/**
* Adds a {@link Command} if the provided condition is true. Does nothing otherwise.
*
* @param command A {@link Command}.
* @param condition A condition.
* @return This builder.
* @throws IllegalStateException If {@link #build()} has already been called.
*/
public Builder addIf(@Command int command, boolean condition) {
checkState(!buildCalled);
if (condition) {
commandsArray.append(command, /* value= */ true);
}
return this;
}
/** Builds a {@link Commands} instance. */
public Commands build() {
checkState(!buildCalled);
buildCalled = true;
return new Commands(commandsArray);
}
}
/** An empty set of commands. */
public static final Commands EMPTY = new Commands.Builder().build();
// A SparseBooleanArray is used instead of a Set to avoid auto-boxing the Command values.
private final SparseBooleanArray commandsArray;
private Commands(SparseBooleanArray commandsArray) {
this.commandsArray = commandsArray;
}
/** Returns a {@link Commands.Builder} initialized with the values of this instance. */
public Builder buildUpon() {
return new Builder(this);
}
/** Returns whether the set of commands contains the specified {@link Command}. */
public boolean contains(@Command int command) {
return commandsArray.get(command);
}
@Override
public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Commands)) {
return false;
}
Commands commands = (Commands) obj;
return this.commandsArray.equals(commands.commandsArray);
}
@Override
public int hashCode() {
return commandsArray.hashCode();
}
}
/**
* Playback state. One of {@link #STATE_IDLE}, {@link #STATE_BUFFERING}, {@link #STATE_READY} or * Playback state. One of {@link #STATE_IDLE}, {@link #STATE_BUFFERING}, {@link #STATE_READY} or
* {@link #STATE_ENDED}. * {@link #STATE_ENDED}.
*/ */
...@@ -881,7 +994,8 @@ public interface Player { ...@@ -881,7 +994,8 @@ public interface Player {
EVENT_SHUFFLE_MODE_ENABLED_CHANGED, EVENT_SHUFFLE_MODE_ENABLED_CHANGED,
EVENT_PLAYER_ERROR, EVENT_PLAYER_ERROR,
EVENT_POSITION_DISCONTINUITY, EVENT_POSITION_DISCONTINUITY,
EVENT_PLAYBACK_PARAMETERS_CHANGED EVENT_PLAYBACK_PARAMETERS_CHANGED,
EVENT_AVAILABLE_COMMANDS_CHANGED
}) })
@interface EventFlags {} @interface EventFlags {}
/** {@link #getCurrentTimeline()} changed. */ /** {@link #getCurrentTimeline()} changed. */
...@@ -912,6 +1026,8 @@ public interface Player { ...@@ -912,6 +1026,8 @@ public interface Player {
int EVENT_POSITION_DISCONTINUITY = 12; int EVENT_POSITION_DISCONTINUITY = 12;
/** {@link #getPlaybackParameters()} changed. */ /** {@link #getPlaybackParameters()} changed. */
int EVENT_PLAYBACK_PARAMETERS_CHANGED = 13; int EVENT_PLAYBACK_PARAMETERS_CHANGED = 13;
/** {@link #isCommandAvailable(int)} changed for at least one {@link Command}. */
int EVENT_AVAILABLE_COMMANDS_CHANGED = 14;
/** /**
* Commands that can be executed on a {@code Player}. One of {@link * Commands that can be executed on a {@code Player}. One of {@link
...@@ -1105,6 +1221,7 @@ public interface Player { ...@@ -1105,6 +1221,7 @@ public interface Player {
* *
* @param command A {@link Command}. * @param command A {@link Command}.
* @return Whether the {@link Command} is available. * @return Whether the {@link Command} is available.
* @see EventListener#onAvailableCommandsChanged(Commands)
*/ */
boolean isCommandAvailable(@Command int command); boolean isCommandAvailable(@Command int command);
......
...@@ -91,6 +91,7 @@ import java.util.List; ...@@ -91,6 +91,7 @@ import java.util.List;
private SeekParameters seekParameters; private SeekParameters seekParameters;
private ShuffleOrder shuffleOrder; private ShuffleOrder shuffleOrder;
private boolean pauseAtEndOfMediaItems; private boolean pauseAtEndOfMediaItems;
private Commands availableCommands;
// Playback information when there is no pending seek/set source operation. // Playback information when there is no pending seek/set source operation.
private PlaybackInfo playbackInfo; private PlaybackInfo playbackInfo;
...@@ -174,6 +175,7 @@ import java.util.List; ...@@ -174,6 +175,7 @@ import java.util.List;
new ExoTrackSelection[renderers.length], new ExoTrackSelection[renderers.length],
/* info= */ null); /* info= */ null);
period = new Timeline.Period(); period = new Timeline.Period();
availableCommands = Commands.EMPTY;
maskingWindowIndex = C.INDEX_UNSET; maskingWindowIndex = C.INDEX_UNSET;
playbackInfoUpdateHandler = clock.createHandler(applicationLooper, /* callback= */ null); playbackInfoUpdateHandler = clock.createHandler(applicationLooper, /* callback= */ null);
playbackInfoUpdateListener = playbackInfoUpdateListener =
...@@ -284,6 +286,11 @@ import java.util.List; ...@@ -284,6 +286,11 @@ import java.util.List;
} }
@Override @Override
public boolean isCommandAvailable(@Command int command) {
return availableCommands.contains(command);
}
@Override
@State @State
public int getPlaybackState() { public int getPlaybackState() {
return playbackInfo.playbackState; return playbackInfo.playbackState;
...@@ -573,8 +580,10 @@ import java.util.List; ...@@ -573,8 +580,10 @@ import java.util.List;
if (this.repeatMode != repeatMode) { if (this.repeatMode != repeatMode) {
this.repeatMode = repeatMode; this.repeatMode = repeatMode;
internalPlayer.setRepeatMode(repeatMode); internalPlayer.setRepeatMode(repeatMode);
listeners.sendEvent( listeners.queueEvent(
Player.EVENT_REPEAT_MODE_CHANGED, listener -> listener.onRepeatModeChanged(repeatMode)); Player.EVENT_REPEAT_MODE_CHANGED, listener -> listener.onRepeatModeChanged(repeatMode));
updateAvailableCommands();
listeners.flushEvents();
} }
} }
...@@ -588,9 +597,11 @@ import java.util.List; ...@@ -588,9 +597,11 @@ import java.util.List;
if (this.shuffleModeEnabled != shuffleModeEnabled) { if (this.shuffleModeEnabled != shuffleModeEnabled) {
this.shuffleModeEnabled = shuffleModeEnabled; this.shuffleModeEnabled = shuffleModeEnabled;
internalPlayer.setShuffleModeEnabled(shuffleModeEnabled); internalPlayer.setShuffleModeEnabled(shuffleModeEnabled);
listeners.sendEvent( listeners.queueEvent(
Player.EVENT_SHUFFLE_MODE_ENABLED_CHANGED, Player.EVENT_SHUFFLE_MODE_ENABLED_CHANGED,
listener -> listener.onShuffleModeEnabledChanged(shuffleModeEnabled)); listener -> listener.onShuffleModeEnabledChanged(shuffleModeEnabled));
updateAvailableCommands();
listeners.flushEvents();
} }
} }
...@@ -1110,6 +1121,7 @@ import java.util.List; ...@@ -1110,6 +1121,7 @@ import java.util.List;
listener -> listener ->
listener.onExperimentalSleepingForOffloadChanged(newPlaybackInfo.sleepingForOffload)); listener.onExperimentalSleepingForOffloadChanged(newPlaybackInfo.sleepingForOffload));
} }
updateAvailableCommands();
listeners.flushEvents(); listeners.flushEvents();
} }
...@@ -1159,6 +1171,16 @@ import java.util.List; ...@@ -1159,6 +1171,16 @@ import java.util.List;
return new Pair<>(/* isTransitioning */ false, /* mediaItemTransitionReason */ C.INDEX_UNSET); return new Pair<>(/* isTransitioning */ false, /* mediaItemTransitionReason */ C.INDEX_UNSET);
} }
private void updateAvailableCommands() {
Commands previousAvailableCommands = availableCommands;
availableCommands = getAvailableCommands();
if (!availableCommands.equals(previousAvailableCommands)) {
listeners.queueEvent(
Player.EVENT_AVAILABLE_COMMANDS_CHANGED,
listener -> listener.onAvailableCommandsChanged(availableCommands));
}
}
private void setMediaSourcesInternal( private void setMediaSourcesInternal(
List<MediaSource> mediaSources, List<MediaSource> mediaSources,
int startWindowIndex, int startWindowIndex,
......
...@@ -1258,6 +1258,12 @@ public class SimpleExoPlayer extends BasePlayer ...@@ -1258,6 +1258,12 @@ public class SimpleExoPlayer extends BasePlayer
} }
@Override @Override
public boolean isCommandAvailable(@Command int command) {
verifyApplicationThread();
return player.isCommandAvailable(command);
}
@Override
public void prepare() { public void prepare() {
verifyApplicationThread(); verifyApplicationThread();
boolean playWhenReady = getPlayWhenReady(); boolean playWhenReady = getPlayWhenReady();
......
...@@ -8079,6 +8079,143 @@ public final class ExoPlayerTest { ...@@ -8079,6 +8079,143 @@ public final class ExoPlayerTest {
} }
@Test @Test
public void seekTo_otherWindow_notifiesAvailableCommandsChanged() {
Player.Commands commandsWithHasNext =
new Player.Commands.Builder().add(Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM).build();
Player.EventListener mockListener = mock(Player.EventListener.class);
ExoPlayer player = new TestExoPlayerBuilder(context).build();
player.addListener(mockListener);
player.addMediaSources(
ImmutableList.of(new FakeMediaSource(), new FakeMediaSource(), new FakeMediaSource()));
verify(mockListener).onAvailableCommandsChanged(commandsWithHasNext);
verify(mockListener).onAvailableCommandsChanged(any());
player.seekTo(/* windowIndex= */ 1, /* positionMs= */ 0);
verify(mockListener).onAvailableCommandsChanged(any());
player.seekTo(/* windowIndex= */ 2, /* positionMs= */ 0);
verify(mockListener).onAvailableCommandsChanged(Player.Commands.EMPTY);
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
player.seekTo(/* windowIndex= */ 1, /* positionMs= */ 0);
verify(mockListener, times(2)).onAvailableCommandsChanged(commandsWithHasNext);
verify(mockListener, times(3)).onAvailableCommandsChanged(any());
player.seekTo(/* windowIndex= */ 0, /* positionMs= */ 0);
verify(mockListener, times(3)).onAvailableCommandsChanged(any());
}
@Test
public void automaticWindowTransition_notifiesAvailableCommandsChanged() throws Exception {
Player.Commands commandsWithHasNext =
new Player.Commands.Builder().add(Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM).build();
Player.EventListener mockListener = mock(Player.EventListener.class);
ExoPlayer player = new TestExoPlayerBuilder(context).build();
player.addListener(mockListener);
player.addMediaSources(
ImmutableList.of(new FakeMediaSource(), new FakeMediaSource(), new FakeMediaSource()));
verify(mockListener).onAvailableCommandsChanged(commandsWithHasNext);
verify(mockListener).onAvailableCommandsChanged(any());
player.prepare();
player.play();
runUntilPlaybackState(player, Player.STATE_ENDED);
verify(mockListener).onAvailableCommandsChanged(Player.Commands.EMPTY);
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
}
@Test
public void addMediaItems_whenLastPlaying_notifiesAvailableCommandsChanged() throws Exception {
Player.Commands commandsWithHasNext =
new Player.Commands.Builder().add(Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM).build();
Player.EventListener mockListener = mock(Player.EventListener.class);
ExoPlayer player = new TestExoPlayerBuilder(context).build();
player.addListener(mockListener);
player.addMediaSource(new FakeMediaSource());
verify(mockListener, never()).onAvailableCommandsChanged(any());
player.addMediaSource(new FakeMediaSource());
verify(mockListener).onAvailableCommandsChanged(commandsWithHasNext);
verify(mockListener).onAvailableCommandsChanged(any());
player.addMediaSource(new FakeMediaSource());
verify(mockListener).onAvailableCommandsChanged(any());
}
@Test
public void removeMediaItems_followingCurrent_notifiesAvailableCommandsChanged()
throws Exception {
Player.Commands commandsWithHasNext =
new Player.Commands.Builder().add(Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM).build();
Player.EventListener mockListener = mock(Player.EventListener.class);
ExoPlayer player = new TestExoPlayerBuilder(context).build();
player.addListener(mockListener);
player.addMediaSources(
ImmutableList.of(new FakeMediaSource(), new FakeMediaSource(), new FakeMediaSource()));
verify(mockListener).onAvailableCommandsChanged(commandsWithHasNext);
verify(mockListener).onAvailableCommandsChanged(any());
player.removeMediaItem(/* index= */ 2);
verify(mockListener).onAvailableCommandsChanged(any());
player.removeMediaItem(/* index= */ 1);
verify(mockListener).onAvailableCommandsChanged(Player.Commands.EMPTY);
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
}
@Test
public void setRepeatMode_all_notifiesAvailableCommandsChanged() throws Exception {
Player.Commands commandsWithHasNext =
new Player.Commands.Builder().add(Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM).build();
Player.EventListener mockListener = mock(Player.EventListener.class);
ExoPlayer player = new TestExoPlayerBuilder(context).build();
player.addListener(mockListener);
player.addMediaSource(new FakeMediaSource());
verify(mockListener, never()).onAvailableCommandsChanged(any());
player.setRepeatMode(Player.REPEAT_MODE_ALL);
verify(mockListener).onAvailableCommandsChanged(commandsWithHasNext);
verify(mockListener).onAvailableCommandsChanged(any());
}
@Test
public void setRepeatMode_one_doesNotNotifyAvailableCommandsChanged() throws Exception {
Player.EventListener mockListener = mock(Player.EventListener.class);
ExoPlayer player = new TestExoPlayerBuilder(context).build();
player.addListener(mockListener);
player.addMediaSource(new FakeMediaSource());
player.setRepeatMode(Player.REPEAT_MODE_ONE);
verify(mockListener, never()).onAvailableCommandsChanged(any());
}
@Test
public void setShuffleModeEnabled_notifiesAvailableCommandsChanged() throws Exception {
Player.Commands commandsWithHasNext =
new Player.Commands.Builder().add(Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM).build();
Player.EventListener mockListener = mock(Player.EventListener.class);
ExoPlayer player = new TestExoPlayerBuilder(context).build();
player.addListener(mockListener);
MediaSource mediaSource =
new ConcatenatingMediaSource(
false,
new FakeShuffleOrder(/* length= */ 2),
new FakeMediaSource(),
new FakeMediaSource());
player.addMediaSource(mediaSource);
verify(mockListener).onAvailableCommandsChanged(commandsWithHasNext);
player.setShuffleModeEnabled(true);
verify(mockListener).onAvailableCommandsChanged(Player.Commands.EMPTY);
}
@Test
public void public void
mediaSourceMaybeThrowSourceInfoRefreshError_isNotThrownUntilPlaybackReachedFailingItem() mediaSourceMaybeThrowSourceInfoRefreshError_isNotThrownUntilPlaybackReachedFailingItem()
throws Exception { throws Exception {
......
...@@ -238,6 +238,11 @@ public abstract class StubExoPlayer extends BasePlayer implements ExoPlayer { ...@@ -238,6 +238,11 @@ public abstract class StubExoPlayer extends BasePlayer implements ExoPlayer {
} }
@Override @Override
public boolean isCommandAvailable(int command) {
throw new UnsupportedOperationException();
}
@Override
public void setPlayWhenReady(boolean playWhenReady) { public void setPlayWhenReady(boolean playWhenReady) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
......
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