diff --git a/RELEASENOTES.md b/RELEASENOTES.md
index 739fe7419a..edddbf001f 100644
--- a/RELEASENOTES.md
+++ b/RELEASENOTES.md
@@ -6,6 +6,9 @@
* ExoPlayer:
* Add `ExoPlayer.isReleased()` to check whether `Exoplayer.release()` has
been called.
+ * Add `ExoPlayer.Builder.setMaxSeekToPreviousPositionMs` to configure the
+ maximum position for which `seekToPrevious()` seeks to the previous item
+ ([#1425](https://github.com/androidx/media/issues/1425)).
* Transformer:
* Remove `ExportResult.processedInputs` field. If you use this field for
codec details, then use `DefaultDecoderFactory.listener` instead. In
diff --git a/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java b/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java
index 56309d8de4..ab318c999a 100644
--- a/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java
+++ b/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java
@@ -217,7 +217,7 @@ public final class CastPlayer extends BasePlayer {
* @param seekBackIncrementMs The {@link #seekBack()} increment, in milliseconds.
* @param seekForwardIncrementMs The {@link #seekForward()} increment, in milliseconds.
* @param maxSeekToPreviousPositionMs The maximum position for which {@link #seekToPrevious()}
- * seeks to the previous {@link MediaItem}, in milliseconds.
+ * seeks to the previous {@link MediaItem}, in milliseconds.
* @throws IllegalArgumentException If {@code seekBackIncrementMs} or {@code
* seekForwardIncrementMs} is non-positive, or if {@code maxSeekToPreviousPositionMs} is
* negative.
diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java
index 86cf79948c..9433a68002 100644
--- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java
+++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java
@@ -543,6 +543,7 @@ public interface ExoPlayer extends Player {
*
{@link SeekParameters}: {@link SeekParameters#DEFAULT}
* {@code seekBackIncrementMs}: {@link C#DEFAULT_SEEK_BACK_INCREMENT_MS}
* {@code seekForwardIncrementMs}: {@link C#DEFAULT_SEEK_FORWARD_INCREMENT_MS}
+ * {@code maxSeekToPreviousPositionMs}: {@link C#DEFAULT_MAX_SEEK_TO_PREVIOUS_POSITION_MS}
* {@code releaseTimeoutMs}: {@link #DEFAULT_RELEASE_TIMEOUT_MS}
* {@code detachSurfaceTimeoutMs}: {@link #DEFAULT_DETACH_SURFACE_TIMEOUT_MS}
* {@code pauseAtEndOfMediaItems}: {@code false}
@@ -1137,7 +1138,8 @@ public interface ExoPlayer extends Player {
*/
@CanIgnoreReturnValue
@UnstableApi
- public Builder setMaxSeekToPreviousPosition(long maxSeekToPreviousPositionMs) {
+ public Builder setMaxSeekToPreviousPositionMs(
+ @IntRange(from = 0) long maxSeekToPreviousPositionMs) {
checkArgument(maxSeekToPreviousPositionMs >= 0L);
checkState(!buildCalled);
this.maxSeekToPreviousPositionMs = maxSeekToPreviousPositionMs;
diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java
index 8ce6082f24..3565f0b6a1 100644
--- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java
+++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java
@@ -12990,9 +12990,12 @@ public class ExoPlayerTest {
@Test
public void seekToPrevious_withPreviousWindowAndCloseToStart_seeksToPreviousWindow() {
- ExoPlayer player = parameterizeTestExoPlayerBuilder(new TestExoPlayerBuilder(context)).build();
+ ExoPlayer player =
+ parameterizeTestExoPlayerBuilder(
+ new TestExoPlayerBuilder(context).setMaxSeekToPreviousPositionMs(10_000))
+ .build();
player.addMediaSources(ImmutableList.of(new FakeMediaSource(), new FakeMediaSource()));
- player.seekTo(/* mediaItemIndex= */ 1, C.DEFAULT_MAX_SEEK_TO_PREVIOUS_POSITION_MS);
+ player.seekTo(/* mediaItemIndex= */ 1, 10_000);
player.seekToPrevious();
@@ -13004,9 +13007,12 @@ public class ExoPlayerTest {
@Test
public void seekToPrevious_notCloseToStart_seeksToZero() {
- ExoPlayer player = parameterizeTestExoPlayerBuilder(new TestExoPlayerBuilder(context)).build();
+ ExoPlayer player =
+ parameterizeTestExoPlayerBuilder(
+ new TestExoPlayerBuilder(context).setMaxSeekToPreviousPositionMs(10_000))
+ .build();
player.addMediaSources(ImmutableList.of(new FakeMediaSource(), new FakeMediaSource()));
- player.seekTo(/* mediaItemIndex= */ 1, C.DEFAULT_MAX_SEEK_TO_PREVIOUS_POSITION_MS + 1);
+ player.seekTo(/* mediaItemIndex= */ 1, 10_001);
player.seekToPrevious();
diff --git a/libraries/session/src/main/java/androidx/media3/session/ConnectionState.java b/libraries/session/src/main/java/androidx/media3/session/ConnectionState.java
index d34abaa0a2..f6dd0a9d39 100644
--- a/libraries/session/src/main/java/androidx/media3/session/ConnectionState.java
+++ b/libraries/session/src/main/java/androidx/media3/session/ConnectionState.java
@@ -173,7 +173,9 @@ import java.util.List;
@Nullable Bundle sessionExtras = bundle.getBundle(FIELD_SESSION_EXTRAS);
@Nullable Bundle playerInfoBundle = bundle.getBundle(FIELD_PLAYER_INFO);
PlayerInfo playerInfo =
- playerInfoBundle == null ? PlayerInfo.DEFAULT : PlayerInfo.fromBundle(playerInfoBundle);
+ playerInfoBundle == null
+ ? PlayerInfo.DEFAULT
+ : PlayerInfo.fromBundle(playerInfoBundle, sessionInterfaceVersion);
return new ConnectionState(
libraryVersion,
sessionInterfaceVersion,
diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplLegacy.java b/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplLegacy.java
index f3d3ee810e..775e811442 100644
--- a/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplLegacy.java
+++ b/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplLegacy.java
@@ -2132,6 +2132,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
LegacyConversions.convertToIsDeviceMuted(newLegacyPlayerInfo.playbackInfoCompat);
long seekBackIncrementMs = oldControllerInfo.playerInfo.seekBackIncrementMs;
long seekForwardIncrementMs = oldControllerInfo.playerInfo.seekForwardIncrementMs;
+ long maxSeekToPreviousPositionMs = oldControllerInfo.playerInfo.maxSeekToPreviousPositionMs;
return createControllerInfo(
currentTimeline,
@@ -2161,7 +2162,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
deviceVolume,
deviceMuted,
seekBackIncrementMs,
- seekForwardIncrementMs);
+ seekForwardIncrementMs,
+ maxSeekToPreviousPositionMs);
}
/**
@@ -2330,7 +2332,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
int deviceVolume,
boolean deviceMuted,
long seekBackIncrementMs,
- long seekForwardIncrementMs) {
+ long seekForwardIncrementMs,
+ long maxSeekToPreviousPositionMs) {
@Nullable MediaItem currentMediaItem = currentTimeline.getMediaItemAt(currentMediaItemIndex);
PositionInfo positionInfo =
@@ -2379,7 +2382,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
/* mediaMetadata= */ mediaMetadata,
seekBackIncrementMs,
seekForwardIncrementMs,
- /* maxSeekToPreviousPositionMs= */ 0L,
+ maxSeekToPreviousPositionMs,
/* currentTracks= */ Tracks.EMPTY,
/* parameters= */ TrackSelectionParameters.DEFAULT_WITHOUT_CONTEXT);
diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaControllerStub.java b/libraries/session/src/main/java/androidx/media3/session/MediaControllerStub.java
index 320c4339b4..57c6d99d48 100644
--- a/libraries/session/src/main/java/androidx/media3/session/MediaControllerStub.java
+++ b/libraries/session/src/main/java/androidx/media3/session/MediaControllerStub.java
@@ -24,6 +24,7 @@ import android.os.Handler;
import android.os.RemoteException;
import android.text.TextUtils;
import androidx.annotation.Nullable;
+import androidx.media3.common.C;
import androidx.media3.common.Player.Commands;
import androidx.media3.common.util.BundleCollectionUtil;
import androidx.media3.common.util.Log;
@@ -38,7 +39,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
private static final String TAG = "MediaControllerStub";
/** The version of the IMediaController interface. */
- public static final int VERSION_INT = 5;
+ public static final int VERSION_INT = 6;
private final WeakReference controller;
@@ -112,14 +113,11 @@ import org.checkerframework.checker.nullness.qual.NonNull;
}
List layout;
try {
- @Nullable MediaControllerImplBase controller = this.controller.get();
- @Nullable
- SessionToken connectedToken = controller == null ? null : controller.getConnectedToken();
- if (connectedToken == null) {
+ int sessionInterfaceVersion = getSessionInterfaceVersion();
+ if (sessionInterfaceVersion == C.INDEX_UNSET) {
// Stale event.
return;
}
- int sessionInterfaceVersion = connectedToken.getInterfaceVersion();
layout =
BundleCollectionUtil.fromBundleList(
bundle -> CommandButton.fromBundle(bundle, sessionInterfaceVersion),
@@ -239,7 +237,12 @@ import org.checkerframework.checker.nullness.qual.NonNull;
}
PlayerInfo playerInfo;
try {
- playerInfo = PlayerInfo.fromBundle(playerInfoBundle);
+ int sessionInterfaceVersion = getSessionInterfaceVersion();
+ if (sessionInterfaceVersion == C.INDEX_UNSET) {
+ // Stale event.
+ return;
+ }
+ playerInfo = PlayerInfo.fromBundle(playerInfoBundle, sessionInterfaceVersion);
} catch (RuntimeException e) {
Log.w(TAG, "Ignoring malformed Bundle for PlayerInfo", e);
return;
@@ -375,6 +378,20 @@ import org.checkerframework.checker.nullness.qual.NonNull;
}
}
+ /** Returns session interface version or {@link C#INDEX_UNSET} for stale events. */
+ private int getSessionInterfaceVersion() {
+ @Nullable MediaControllerImplBase controller = this.controller.get();
+ if (controller == null) {
+ return C.INDEX_UNSET;
+ }
+ @Nullable SessionToken connectedToken = controller.getConnectedToken();
+ if (connectedToken == null) {
+ // Stale event.
+ return C.INDEX_UNSET;
+ }
+ return connectedToken.getInterfaceVersion();
+ }
+
/* @FunctionalInterface */
private interface ControllerTask {
diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaSessionStub.java b/libraries/session/src/main/java/androidx/media3/session/MediaSessionStub.java
index 619413e36f..b032a38924 100644
--- a/libraries/session/src/main/java/androidx/media3/session/MediaSessionStub.java
+++ b/libraries/session/src/main/java/androidx/media3/session/MediaSessionStub.java
@@ -118,7 +118,7 @@ import java.util.concurrent.ExecutionException;
private static final String TAG = "MediaSessionStub";
/** The version of the IMediaSession interface. */
- public static final int VERSION_INT = 3;
+ public static final int VERSION_INT = 4;
/**
* Sequence number used when a controller method is triggered on the sesison side that wasn't
diff --git a/libraries/session/src/main/java/androidx/media3/session/PlayerInfo.java b/libraries/session/src/main/java/androidx/media3/session/PlayerInfo.java
index d1c15f7a3a..5dda00e9e8 100644
--- a/libraries/session/src/main/java/androidx/media3/session/PlayerInfo.java
+++ b/libraries/session/src/main/java/androidx/media3/session/PlayerInfo.java
@@ -30,6 +30,7 @@ import androidx.annotation.FloatRange;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.media3.common.AudioAttributes;
+import androidx.media3.common.C;
import androidx.media3.common.DeviceInfo;
import androidx.media3.common.MediaItem;
import androidx.media3.common.MediaMetadata;
@@ -457,9 +458,9 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue;
/* isPlaying= */ false,
/* isLoading= */ false,
MediaMetadata.EMPTY,
- /* seekBackIncrementMs= */ 0,
- /* seekForwardIncrementMs= */ 0,
- /* maxSeekToPreviousPositionMs= */ 0,
+ /* seekBackIncrementMs= */ C.DEFAULT_SEEK_BACK_INCREMENT_MS,
+ /* seekForwardIncrementMs= */ C.DEFAULT_SEEK_FORWARD_INCREMENT_MS,
+ /* maxSeekToPreviousPositionMs= */ C.DEFAULT_MAX_SEEK_TO_PREVIOUS_POSITION_MS,
/* currentTracks= */ Tracks.EMPTY,
TrackSelectionParameters.DEFAULT_WITHOUT_CONTEXT);
@@ -816,9 +817,16 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue;
private static final String FIELD_DISCONTINUITY_REASON = Util.intToStringMaxRadix(23);
private static final String FIELD_CUE_GROUP = Util.intToStringMaxRadix(24);
private static final String FIELD_MEDIA_METADATA = Util.intToStringMaxRadix(25);
- private static final String FIELD_SEEK_BACK_INCREMENT_MS = Util.intToStringMaxRadix(26);
- private static final String FIELD_SEEK_FORWARD_INCREMENT_MS = Util.intToStringMaxRadix(27);
- private static final String FIELD_MAX_SEEK_TO_PREVIOUS_POSITION_MS = Util.intToStringMaxRadix(28);
+
+ @VisibleForTesting
+ static final String FIELD_SEEK_BACK_INCREMENT_MS = Util.intToStringMaxRadix(26);
+
+ @VisibleForTesting
+ static final String FIELD_SEEK_FORWARD_INCREMENT_MS = Util.intToStringMaxRadix(27);
+
+ @VisibleForTesting
+ static final String FIELD_MAX_SEEK_TO_PREVIOUS_POSITION_MS = Util.intToStringMaxRadix(28);
+
private static final String FIELD_TRACK_SELECTION_PARAMETERS = Util.intToStringMaxRadix(29);
private static final String FIELD_CURRENT_TRACKS = Util.intToStringMaxRadix(30);
private static final String FIELD_TIMELINE_CHANGE_REASON = Util.intToStringMaxRadix(31);
@@ -977,13 +985,19 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue;
if (!mediaMetadata.equals(MediaMetadata.EMPTY)) {
bundle.putBundle(FIELD_MEDIA_METADATA, mediaMetadata.toBundle());
}
- if (seekBackIncrementMs != 0) {
+ long defaultSeekBackIncrementMs =
+ controllerInterfaceVersion < 6 ? 0 : C.DEFAULT_SEEK_BACK_INCREMENT_MS;
+ if (seekBackIncrementMs != defaultSeekBackIncrementMs) {
bundle.putLong(FIELD_SEEK_BACK_INCREMENT_MS, seekBackIncrementMs);
}
- if (seekForwardIncrementMs != 0) {
+ long defaultSeekForwardIncrementMs =
+ controllerInterfaceVersion < 6 ? 0 : C.DEFAULT_SEEK_FORWARD_INCREMENT_MS;
+ if (seekForwardIncrementMs != defaultSeekForwardIncrementMs) {
bundle.putLong(FIELD_SEEK_FORWARD_INCREMENT_MS, seekForwardIncrementMs);
}
- if (maxSeekToPreviousPositionMs != 0) {
+ long defaultMaxSeekToPreviousPositionMs =
+ controllerInterfaceVersion < 6 ? 0 : C.DEFAULT_MAX_SEEK_TO_PREVIOUS_POSITION_MS;
+ if (maxSeekToPreviousPositionMs != defaultMaxSeekToPreviousPositionMs) {
bundle.putLong(FIELD_MAX_SEEK_TO_PREVIOUS_POSITION_MS, maxSeekToPreviousPositionMs);
}
if (!currentTracks.equals(Tracks.EMPTY)) {
@@ -996,7 +1010,7 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue;
}
/** Restores a {@code PlayerInfo} from a {@link Bundle}. */
- public static PlayerInfo fromBundle(Bundle bundle) {
+ public static PlayerInfo fromBundle(Bundle bundle, int sessionInterfaceVersion) {
@Nullable IBinder inProcessBinder = bundle.getBinder(FIELD_IN_PROCESS_BINDER);
if (inProcessBinder instanceof InProcessBinder) {
return ((InProcessBinder) inProcessBinder).getPlayerInfo();
@@ -1080,11 +1094,22 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue;
mediaMetadataBundle == null
? MediaMetadata.EMPTY
: MediaMetadata.fromBundle(mediaMetadataBundle);
- long seekBackIncrementMs = bundle.getLong(FIELD_SEEK_BACK_INCREMENT_MS, /* defaultValue= */ 0);
+ long seekBackIncrementMs =
+ bundle.getLong(
+ FIELD_SEEK_BACK_INCREMENT_MS,
+ /* defaultValue= */ sessionInterfaceVersion < 4 ? 0 : C.DEFAULT_SEEK_BACK_INCREMENT_MS);
long seekForwardIncrementMs =
- bundle.getLong(FIELD_SEEK_FORWARD_INCREMENT_MS, /* defaultValue= */ 0);
+ bundle.getLong(
+ FIELD_SEEK_FORWARD_INCREMENT_MS,
+ /* defaultValue= */ sessionInterfaceVersion < 4
+ ? 0
+ : C.DEFAULT_SEEK_FORWARD_INCREMENT_MS);
long maxSeekToPreviousPosition =
- bundle.getLong(FIELD_MAX_SEEK_TO_PREVIOUS_POSITION_MS, /* defaultValue= */ 0);
+ bundle.getLong(
+ FIELD_MAX_SEEK_TO_PREVIOUS_POSITION_MS,
+ /* defaultValue= */ sessionInterfaceVersion < 4
+ ? 0
+ : C.DEFAULT_MAX_SEEK_TO_PREVIOUS_POSITION_MS);
Bundle currentTracksBundle = bundle.getBundle(FIELD_CURRENT_TRACKS);
Tracks currentTracks =
currentTracksBundle == null ? Tracks.EMPTY : Tracks.fromBundle(currentTracksBundle);
diff --git a/libraries/session/src/test/java/androidx/media3/session/PlayerInfoTest.java b/libraries/session/src/test/java/androidx/media3/session/PlayerInfoTest.java
index fd0520e1c0..c95f7f46df 100644
--- a/libraries/session/src/test/java/androidx/media3/session/PlayerInfoTest.java
+++ b/libraries/session/src/test/java/androidx/media3/session/PlayerInfoTest.java
@@ -163,7 +163,8 @@ public class PlayerInfoTest {
.setVideoSize(new VideoSize(/* width= */ 1024, /* height= */ 768))
.build();
- PlayerInfo infoAfterBundling = PlayerInfo.fromBundle(playerInfo.toBundleInProcess());
+ PlayerInfo infoAfterBundling =
+ PlayerInfo.fromBundle(playerInfo.toBundleInProcess(), MediaSessionStub.VERSION_INT);
assertThat(infoAfterBundling.oldPositionInfo.mediaItemIndex).isEqualTo(5);
assertThat(infoAfterBundling.oldPositionInfo.periodIndex).isEqualTo(4);
@@ -292,7 +293,8 @@ public class PlayerInfoTest {
.build(),
/* excludeTimeline= */ false,
/* excludeTracks= */ false)
- .toBundleInProcess());
+ .toBundleInProcess(),
+ MediaSessionStub.VERSION_INT);
assertThat(infoAfterBundling.oldPositionInfo.mediaItemIndex).isEqualTo(5);
assertThat(infoAfterBundling.oldPositionInfo.periodIndex).isEqualTo(4);
@@ -413,7 +415,8 @@ public class PlayerInfoTest {
.build(),
/* excludeTimeline= */ true,
/* excludeTracks= */ false)
- .toBundleInProcess());
+ .toBundleInProcess(),
+ MediaSessionStub.VERSION_INT);
assertThat(infoAfterBundling.oldPositionInfo.mediaItemIndex).isEqualTo(0);
assertThat(infoAfterBundling.oldPositionInfo.periodIndex).isEqualTo(0);
@@ -482,7 +485,8 @@ public class PlayerInfoTest {
.build(),
/* excludeTimeline= */ false,
/* excludeTracks= */ false)
- .toBundleInProcess());
+ .toBundleInProcess(),
+ MediaSessionStub.VERSION_INT);
assertThat(infoAfterBundling.mediaMetadata).isEqualTo(MediaMetadata.EMPTY);
assertThat(infoAfterBundling.playlistMetadata).isEqualTo(MediaMetadata.EMPTY);
@@ -502,7 +506,8 @@ public class PlayerInfoTest {
.build(),
/* excludeTimeline= */ false,
/* excludeTracks= */ false)
- .toBundleInProcess());
+ .toBundleInProcess(),
+ MediaSessionStub.VERSION_INT);
assertThat(infoAfterBundling.volume).isEqualTo(1f);
}
@@ -522,7 +527,8 @@ public class PlayerInfoTest {
.build(),
/* excludeTimeline= */ false,
/* excludeTracks= */ false)
- .toBundleInProcess());
+ .toBundleInProcess(),
+ MediaSessionStub.VERSION_INT);
assertThat(infoAfterBundling.deviceVolume).isEqualTo(0);
assertThat(infoAfterBundling.deviceMuted).isFalse();
@@ -546,7 +552,8 @@ public class PlayerInfoTest {
.build(),
/* excludeTimeline= */ false,
/* excludeTracks= */ false)
- .toBundleInProcess());
+ .toBundleInProcess(),
+ MediaSessionStub.VERSION_INT);
assertThat(infoAfterBundling.audioAttributes).isEqualTo(AudioAttributes.DEFAULT);
}
@@ -568,7 +575,8 @@ public class PlayerInfoTest {
.build(),
/* excludeTimeline= */ false,
/* excludeTracks= */ false)
- .toBundleInProcess());
+ .toBundleInProcess(),
+ MediaSessionStub.VERSION_INT);
assertThat(infoAfterBundling.cueGroup).isEqualTo(CueGroup.EMPTY_TIME_ZERO);
}
@@ -598,14 +606,16 @@ public class PlayerInfoTest {
.build(),
/* excludeTimeline= */ false,
/* excludeTracks= */ true)
- .toBundleInProcess());
+ .toBundleInProcess(),
+ MediaSessionStub.VERSION_INT);
assertThat(infoAfterBundling.currentTracks).isEqualTo(Tracks.EMPTY);
}
@Test
public void toBundleFromBundle_withDefaultValues_restoresAllData() {
- PlayerInfo roundTripValue = PlayerInfo.fromBundle(PlayerInfo.DEFAULT.toBundleInProcess());
+ PlayerInfo roundTripValue =
+ PlayerInfo.fromBundle(PlayerInfo.DEFAULT.toBundleInProcess(), MediaSessionStub.VERSION_INT);
assertThat(roundTripValue.oldPositionInfo).isEqualTo(PlayerInfo.DEFAULT.oldPositionInfo);
assertThat(roundTripValue.newPositionInfo).isEqualTo(PlayerInfo.DEFAULT.newPositionInfo);
@@ -659,6 +669,21 @@ public class PlayerInfoTest {
assertThat(bundle.isEmpty()).isTrue();
}
+ @Test
+ public void
+ toBundleForRemoteProcess_withDefaultValuesForControllerInterfaceBefore6_includesSeekLimits() {
+ // Controller before version 6 uses 0 values for the three seek limit default values. The
+ // Bundle should include these to overwrite the presumed 0 on the controller side.
+ Bundle bundle =
+ PlayerInfo.DEFAULT.toBundleForRemoteProcess(/* controllerInterfaceVersion= */ 5);
+
+ assertThat(bundle.keySet())
+ .containsAtLeast(
+ PlayerInfo.FIELD_SEEK_BACK_INCREMENT_MS,
+ PlayerInfo.FIELD_SEEK_FORWARD_INCREMENT_MS,
+ PlayerInfo.FIELD_MAX_SEEK_TO_PREVIOUS_POSITION_MS);
+ }
+
@Test
public void
toBundleForRemoteProcess_withDefaultValuesForControllerInterfaceBefore3_includesPositionInfos() {
@@ -673,4 +698,16 @@ public class PlayerInfoTest {
PlayerInfo.FIELD_NEW_POSITION_INFO,
PlayerInfo.FIELD_OLD_POSITION_INFO);
}
+
+ @Test
+ public void fromBundle_withEmptyBundleForSessionInterfaceBefore4_restoresSeekLimitsAsZero() {
+ // Session before version 4 uses 0 values for the three seek limit default values. We need to
+ // restore those instead of current default.
+
+ PlayerInfo playerInfo = PlayerInfo.fromBundle(Bundle.EMPTY, /* sessionInterfaceVersion= */ 3);
+
+ assertThat(playerInfo.seekBackIncrementMs).isEqualTo(0);
+ assertThat(playerInfo.seekForwardIncrementMs).isEqualTo(0);
+ assertThat(playerInfo.maxSeekToPreviousPositionMs).isEqualTo(0);
+ }
}
diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/TestExoPlayerBuilder.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/TestExoPlayerBuilder.java
index 79d438bee0..61cf20e26a 100644
--- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/TestExoPlayerBuilder.java
+++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/TestExoPlayerBuilder.java
@@ -325,7 +325,7 @@ public class TestExoPlayerBuilder {
* @return This builder.
*/
@CanIgnoreReturnValue
- public TestExoPlayerBuilder setMaxSeekToPreviousPosition(long maxSeekToPreviousPositionMs) {
+ public TestExoPlayerBuilder setMaxSeekToPreviousPositionMs(long maxSeekToPreviousPositionMs) {
this.maxSeekToPreviousPositionMs = maxSeekToPreviousPositionMs;
return this;
}
@@ -398,7 +398,7 @@ public class TestExoPlayerBuilder {
.setLooper(looper)
.setSeekBackIncrementMs(seekBackIncrementMs)
.setSeekForwardIncrementMs(seekForwardIncrementMs)
- .setMaxSeekToPreviousPosition(maxSeekToPreviousPositionMs)
+ .setMaxSeekToPreviousPositionMs(maxSeekToPreviousPositionMs)
.setDeviceVolumeControlEnabled(deviceVolumeControlEnabled)
.setSuppressPlaybackOnUnsuitableOutput(suppressPlaybackWhenUnsuitableOutput)
.experimentalSetDynamicSchedulingEnabled(dynamicSchedulingEnabled);