Commit 6c9f9f9d by kimvde Committed by Oliver Woodman

Add available command to seek in current item

PiperOrigin-RevId: 362972550
parent 46aab922
......@@ -15,6 +15,9 @@
*/
package com.google.android.exoplayer2.ext.mediasession;
import static com.google.android.exoplayer2.Player.COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM;
import static com.google.android.exoplayer2.Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM;
import static com.google.android.exoplayer2.Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM;
import static java.lang.Math.min;
import android.os.Bundle;
......@@ -99,12 +102,12 @@ public abstract class TimelineQueueNavigator implements MediaSessionConnector.Qu
timeline.getWindow(player.getCurrentWindowIndex(), window);
enableSkipTo = timeline.getWindowCount() > 1;
enablePrevious =
window.isSeekable
player.isCommandAvailable(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM)
|| !window.isLive()
|| player.isCommandAvailable(Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM);
|| player.isCommandAvailable(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM);
enableNext =
(window.isLive() && window.isDynamic)
|| player.isCommandAvailable(Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM);
|| player.isCommandAvailable(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM);
}
long actions = 0;
......
......@@ -323,6 +323,7 @@ public abstract class BasePlayer implements Player {
protected Commands getAvailableCommands(@Command int[] permanentAvailableCommands) {
return new Commands.Builder()
.addAll(permanentAvailableCommands)
.addIf(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM, isCurrentWindowSeekable() && !isPlayingAd())
.addIf(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM, hasNext() && !isPlayingAd())
.addIf(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM, hasPrevious() && !isPlayingAd())
.build();
......
......@@ -1029,43 +1029,54 @@ public interface Player {
/**
* Commands that can be executed on a {@code Player}. One of {@link #COMMAND_PLAY_PAUSE}, {@link
* #COMMAND_PREPARE_STOP_RELEASE}, {@link #COMMAND_SEEK_TO_NEXT_MEDIA_ITEM}, {@link
* #COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM}, {@link #COMMAND_SET_SPEED_AND_PITCH}, {@link
* #COMMAND_SET_SHUFFLE_MODE}, {@link #COMMAND_SET_REPEAT_MODE}, {@link
* #COMMAND_GET_CURRENT_MEDIA_ITEM}, {@link #COMMAND_GET_MEDIA_ITEMS}, {@link
* #COMMAND_GET_MEDIA_ITEMS_METADATA} or {@link #COMMAND_CHANGE_MEDIA_ITEMS}.
* #COMMAND_PREPARE_STOP_RELEASE}, {@link #COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM}, {@link
* #COMMAND_SEEK_TO_NEXT_MEDIA_ITEM}, {@link #COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM}, {@link
* #COMMAND_SET_SPEED_AND_PITCH}, {@link #COMMAND_SET_SHUFFLE_MODE}, {@link
* #COMMAND_SET_REPEAT_MODE}, {@link #COMMAND_GET_CURRENT_MEDIA_ITEM}, {@link
* #COMMAND_GET_MEDIA_ITEMS}, {@link #COMMAND_GET_MEDIA_ITEMS_METADATA} or {@link
* #COMMAND_CHANGE_MEDIA_ITEMS}.
*/
@Documented
@Retention(RetentionPolicy.SOURCE)
@IntDef({
COMMAND_PLAY_PAUSE,
COMMAND_PREPARE_STOP_RELEASE,
COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM,
COMMAND_SEEK_TO_NEXT_MEDIA_ITEM,
COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM
COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM,
COMMAND_SET_SPEED_AND_PITCH,
COMMAND_SET_SHUFFLE_MODE,
COMMAND_SET_REPEAT_MODE,
COMMAND_GET_CURRENT_MEDIA_ITEM,
COMMAND_GET_MEDIA_ITEMS,
COMMAND_GET_MEDIA_ITEMS_METADATA,
COMMAND_CHANGE_MEDIA_ITEMS
})
@interface Command {}
/** Command to start, pause or resume playback. */
int COMMAND_PLAY_PAUSE = 1;
/** Command to prepare the player, stop playback or release the player. */
int COMMAND_PREPARE_STOP_RELEASE = 2;
/** Command to seek into the current {@link MediaItem}. */
int COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM = 3;
/** Command to seek to the next {@link MediaItem} in the playlist. */
int COMMAND_SEEK_TO_NEXT_MEDIA_ITEM = 3;
int COMMAND_SEEK_TO_NEXT_MEDIA_ITEM = 4;
/** Command to seek to the previous {@link MediaItem} in the playlist. */
int COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM = 4;
int COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM = 5;
/** Command to set the playback speed and pitch. */
int COMMAND_SET_SPEED_AND_PITCH = 5;
int COMMAND_SET_SPEED_AND_PITCH = 6;
/** Command to enable shuffling. */
int COMMAND_SET_SHUFFLE_MODE = 6;
int COMMAND_SET_SHUFFLE_MODE = 7;
/** Command to set the repeat mode. */
int COMMAND_SET_REPEAT_MODE = 7;
int COMMAND_SET_REPEAT_MODE = 8;
/** Command to get the current {@link MediaItem}. */
int COMMAND_GET_CURRENT_MEDIA_ITEM = 8;
int COMMAND_GET_CURRENT_MEDIA_ITEM = 9;
/** Command to get the {@link MediaItem MediaItems} in the playlist. */
int COMMAND_GET_MEDIA_ITEMS = 9;
int COMMAND_GET_MEDIA_ITEMS = 10;
/** Command to get the {@link MediaItem MediaItems} metadata. */
int COMMAND_GET_MEDIA_ITEMS_METADATA = 10;
int COMMAND_GET_MEDIA_ITEMS_METADATA = 11;
/** Command to change the {@link MediaItem MediaItems} in the playlist. */
int COMMAND_CHANGE_MEDIA_ITEMS = 11;
int COMMAND_CHANGE_MEDIA_ITEMS = 12;
/** Returns the component of this player for audio output, or null if audio is not supported. */
@Nullable
......
......@@ -21,6 +21,7 @@ import static com.google.android.exoplayer2.Player.COMMAND_GET_MEDIA_ITEMS;
import static com.google.android.exoplayer2.Player.COMMAND_GET_MEDIA_ITEMS_METADATA;
import static com.google.android.exoplayer2.Player.COMMAND_PLAY_PAUSE;
import static com.google.android.exoplayer2.Player.COMMAND_PREPARE_STOP_RELEASE;
import static com.google.android.exoplayer2.Player.COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM;
import static com.google.android.exoplayer2.Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM;
import static com.google.android.exoplayer2.Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM;
import static com.google.android.exoplayer2.Player.COMMAND_SET_REPEAT_MODE;
......@@ -8087,6 +8088,7 @@ public final class ExoPlayerTest {
assertThat(player.isCommandAvailable(COMMAND_PLAY_PAUSE)).isTrue();
assertThat(player.isCommandAvailable(COMMAND_PREPARE_STOP_RELEASE)).isTrue();
assertThat(player.isCommandAvailable(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM)).isFalse();
assertThat(player.isCommandAvailable(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM)).isTrue();
assertThat(player.isCommandAvailable(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM)).isFalse();
assertThat(player.isCommandAvailable(COMMAND_SET_SPEED_AND_PITCH)).isTrue();
......@@ -8099,7 +8101,7 @@ public final class ExoPlayerTest {
}
@Test
public void isCommandAvailable_whenPlayingAd_isFalseForSeekCommands() throws Exception {
public void isCommandAvailable_duringAd_isFalseForSeekCommands() throws Exception {
AdPlaybackState adPlaybackState =
new AdPlaybackState(/* adsId= */ new Object(), /* adGroupTimesUs...= */ 0)
.withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 1)
......@@ -8123,11 +8125,29 @@ public final class ExoPlayerTest {
player.prepare();
runUntilPlaybackState(player, Player.STATE_READY);
assertThat(player.isCommandAvailable(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM)).isFalse();
assertThat(player.isCommandAvailable(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM)).isFalse();
assertThat(player.isCommandAvailable(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM)).isFalse();
}
@Test
public void isCommandAvailable_duringUnseekableItem_isFalseForSeekInCurrent() throws Exception {
Timeline timelineWithUnseekableWindow =
new FakeTimeline(
new TimelineWindowDefinition(
/* isSeekable= */ false,
/* isDynamic= */ false,
/* durationUs= */ C.msToUs(10_000)));
ExoPlayer player = new TestExoPlayerBuilder(context).build();
player.addMediaSource(new FakeMediaSource(timelineWithUnseekableWindow));
player.prepare();
runUntilPlaybackState(player, Player.STATE_READY);
assertThat(player.isCommandAvailable(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM)).isFalse();
}
@Test
public void seekTo_nextWindow_notifiesAvailableCommandsChanged() {
Player.Commands commandsWithSeekToNext = createCommands(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM);
Player.Commands commandsWithSeekToPrevious =
......@@ -8145,6 +8165,7 @@ public final class ExoPlayerTest {
new FakeMediaSource(),
new FakeMediaSource()));
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToNext);
// Check that there were no other calls to onAvailableCommandsChanged.
verify(mockListener).onAvailableCommandsChanged(any());
player.seekTo(/* windowIndex= */ 1, /* positionMs= */ 0);
......@@ -8178,6 +8199,7 @@ public final class ExoPlayerTest {
new FakeMediaSource(),
new FakeMediaSource()));
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToPrevious);
// Check that there were no other calls to onAvailableCommandsChanged.
verify(mockListener).onAvailableCommandsChanged(any());
player.seekTo(/* windowIndex= */ 2, /* positionMs= */ 0);
......@@ -8207,10 +8229,15 @@ public final class ExoPlayerTest {
@Test
public void automaticWindowTransition_notifiesAvailableCommandsChanged() throws Exception {
Player.Commands commandsWithSeekToNext = createCommands(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM);
Player.Commands commandsWithSeekToPrevious =
createCommands(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM);
Player.Commands commandsWithSeekToNextAndPrevious =
createCommands(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM, COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM);
Player.Commands commandsWithSeekInCurrentAndToNext =
createCommands(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM, COMMAND_SEEK_TO_NEXT_MEDIA_ITEM);
Player.Commands commandsWithSeekInCurrentAndToPrevious =
createCommands(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM, COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM);
Player.Commands commandsWithSeekAnywhere =
createCommands(
COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM,
COMMAND_SEEK_TO_NEXT_MEDIA_ITEM,
COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM);
Player.EventListener mockListener = mock(Player.EventListener.class);
ExoPlayer player = new TestExoPlayerBuilder(context).build();
player.addListener(mockListener);
......@@ -8222,22 +8249,27 @@ public final class ExoPlayerTest {
new FakeMediaSource(),
new FakeMediaSource()));
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToNext);
// Check that there were no other calls to onAvailableCommandsChanged.
verify(mockListener).onAvailableCommandsChanged(any());
player.prepare();
runUntilPlaybackState(player, Player.STATE_READY);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekInCurrentAndToNext);
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
playUntilStartOfWindow(player, /* windowIndex= */ 1);
runUntilPendingCommandsAreFullyHandled(player);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToNextAndPrevious);
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekAnywhere);
verify(mockListener, times(3)).onAvailableCommandsChanged(any());
playUntilStartOfWindow(player, /* windowIndex= */ 2);
runUntilPendingCommandsAreFullyHandled(player);
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
verify(mockListener, times(3)).onAvailableCommandsChanged(any());
player.play();
runUntilPlaybackState(player, Player.STATE_ENDED);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToPrevious);
verify(mockListener, times(3)).onAvailableCommandsChanged(any());
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekInCurrentAndToPrevious);
verify(mockListener, times(4)).onAvailableCommandsChanged(any());
}
@Test
......@@ -8252,6 +8284,7 @@ public final class ExoPlayerTest {
player.addMediaSource(new FakeMediaSource());
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToNext);
// Check that there were no other calls to onAvailableCommandsChanged.
verify(mockListener).onAvailableCommandsChanged(any());
player.addMediaSource(new FakeMediaSource());
......@@ -8271,6 +8304,7 @@ public final class ExoPlayerTest {
player.addMediaSource(/* index= */ 0, new FakeMediaSource());
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToPrevious);
// Check that there were no other calls to onAvailableCommandsChanged.
verify(mockListener).onAvailableCommandsChanged(any());
player.addMediaSource(/* index= */ 0, new FakeMediaSource());
......@@ -8288,6 +8322,7 @@ public final class ExoPlayerTest {
player.addMediaSources(
ImmutableList.of(new FakeMediaSource(), new FakeMediaSource(), new FakeMediaSource()));
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToNext);
// Check that there were no other calls to onAvailableCommandsChanged.
verify(mockListener).onAvailableCommandsChanged(any());
player.removeMediaItem(/* index= */ 2);
......@@ -8314,6 +8349,7 @@ public final class ExoPlayerTest {
player.addMediaSources(
ImmutableList.of(new FakeMediaSource(), new FakeMediaSource(), new FakeMediaSource()));
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToPrevious);
// Check that there were no other calls to onAvailableCommandsChanged.
verify(mockListener).onAvailableCommandsChanged(any());
player.removeMediaItem(/* index= */ 0);
......@@ -8337,6 +8373,7 @@ public final class ExoPlayerTest {
player.addMediaSources(ImmutableList.of(new FakeMediaSource(), new FakeMediaSource()));
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToNext);
// Check that there were no other calls to onAvailableCommandsChanged.
verify(mockListener).onAvailableCommandsChanged(any());
player.removeMediaItem(/* index= */ 0);
......@@ -8357,6 +8394,7 @@ public final class ExoPlayerTest {
player.setRepeatMode(Player.REPEAT_MODE_ALL);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToNextAndPrevious);
// Check that there were no other calls to onAvailableCommandsChanged.
verify(mockListener).onAvailableCommandsChanged(any());
}
......@@ -8388,9 +8426,12 @@ public final class ExoPlayerTest {
player.addMediaSource(mediaSource);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToNext);
// Check that there were no other calls to onAvailableCommandsChanged.
verify(mockListener).onAvailableCommandsChanged(any());
player.setShuffleModeEnabled(true);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToPrevious);
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
}
@Test
......
......@@ -15,6 +15,9 @@
*/
package com.google.android.exoplayer2.ui;
import static com.google.android.exoplayer2.Player.COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM;
import static com.google.android.exoplayer2.Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM;
import static com.google.android.exoplayer2.Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM;
import static com.google.android.exoplayer2.Player.EVENT_IS_PLAYING_CHANGED;
import static com.google.android.exoplayer2.Player.EVENT_PLAYBACK_STATE_CHANGED;
import static com.google.android.exoplayer2.Player.EVENT_PLAY_WHEN_READY_CHANGED;
......@@ -910,18 +913,18 @@ public class PlayerControlView extends FrameLayout {
if (player != null) {
Timeline timeline = player.getCurrentTimeline();
if (!timeline.isEmpty() && !player.isPlayingAd()) {
boolean isSeekable = player.isCommandAvailable(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM);
timeline.getWindow(player.getCurrentWindowIndex(), window);
boolean isSeekable = window.isSeekable;
enableSeeking = isSeekable;
enablePrevious =
isSeekable
|| !window.isLive()
|| player.isCommandAvailable(Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM);
|| player.isCommandAvailable(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM);
enableRewind = isSeekable && controlDispatcher.isRewindEnabled();
enableFastForward = isSeekable && controlDispatcher.isFastForwardEnabled();
enableNext =
(window.isLive() && window.isDynamic)
|| player.isCommandAvailable(Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM);
|| player.isCommandAvailable(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM);
}
}
......
......@@ -15,6 +15,9 @@
*/
package com.google.android.exoplayer2.ui;
import static com.google.android.exoplayer2.Player.COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM;
import static com.google.android.exoplayer2.Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM;
import static com.google.android.exoplayer2.Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM;
import static com.google.android.exoplayer2.Player.EVENT_IS_PLAYING_CHANGED;
import static com.google.android.exoplayer2.Player.EVENT_PLAYBACK_PARAMETERS_CHANGED;
import static com.google.android.exoplayer2.Player.EVENT_PLAYBACK_STATE_CHANGED;
......@@ -1458,17 +1461,17 @@ public class PlayerNotificationManager {
boolean enableNext = false;
Timeline timeline = player.getCurrentTimeline();
if (!timeline.isEmpty() && !player.isPlayingAd()) {
boolean isSeekable = player.isCommandAvailable(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM);
timeline.getWindow(player.getCurrentWindowIndex(), window);
boolean isSeekable = window.isSeekable;
enablePrevious =
isSeekable
|| !window.isLive()
|| player.isCommandAvailable(Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM);
|| player.isCommandAvailable(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM);
enableRewind = isSeekable && controlDispatcher.isRewindEnabled();
enableFastForward = isSeekable && controlDispatcher.isFastForwardEnabled();
enableNext =
(window.isLive() && window.isDynamic)
|| player.isCommandAvailable(Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM);
|| player.isCommandAvailable(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM);
}
List<String> stringActions = new ArrayList<>();
......
......@@ -15,6 +15,9 @@
*/
package com.google.android.exoplayer2.ui;
import static com.google.android.exoplayer2.Player.COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM;
import static com.google.android.exoplayer2.Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM;
import static com.google.android.exoplayer2.Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM;
import static com.google.android.exoplayer2.Player.EVENT_IS_PLAYING_CHANGED;
import static com.google.android.exoplayer2.Player.EVENT_PLAYBACK_PARAMETERS_CHANGED;
import static com.google.android.exoplayer2.Player.EVENT_PLAYBACK_STATE_CHANGED;
......@@ -1145,18 +1148,18 @@ public class StyledPlayerControlView extends FrameLayout {
if (player != null) {
Timeline timeline = player.getCurrentTimeline();
if (!timeline.isEmpty() && !player.isPlayingAd()) {
boolean isSeekable = player.isCommandAvailable(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM);
timeline.getWindow(player.getCurrentWindowIndex(), window);
boolean isSeekable = window.isSeekable;
enableSeeking = isSeekable;
enablePrevious =
isSeekable
|| !window.isLive()
|| player.isCommandAvailable(Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM);
|| player.isCommandAvailable(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM);
enableRewind = isSeekable && controlDispatcher.isRewindEnabled();
enableFastForward = isSeekable && controlDispatcher.isFastForwardEnabled();
enableNext =
(window.isLive() && window.isDynamic)
|| player.isCommandAvailable(Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM);
|| player.isCommandAvailable(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM);
}
}
......
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