parent
aec6db77fa
commit
c0759a4e62
@ -31,6 +31,7 @@ import androidx.annotation.FloatRange;
|
||||
import androidx.annotation.IntDef;
|
||||
import androidx.annotation.IntRange;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.media3.common.text.Cue;
|
||||
import androidx.media3.common.text.CueGroup;
|
||||
import androidx.media3.common.util.Size;
|
||||
@ -350,15 +351,9 @@ public interface Player {
|
||||
return false;
|
||||
}
|
||||
PositionInfo that = (PositionInfo) o;
|
||||
return mediaItemIndex == that.mediaItemIndex
|
||||
&& periodIndex == that.periodIndex
|
||||
&& positionMs == that.positionMs
|
||||
&& contentPositionMs == that.contentPositionMs
|
||||
&& adGroupIndex == that.adGroupIndex
|
||||
&& adIndexInAdGroup == that.adIndexInAdGroup
|
||||
return equalsForBundling(that)
|
||||
&& Objects.equal(windowUid, that.windowUid)
|
||||
&& Objects.equal(periodUid, that.periodUid)
|
||||
&& Objects.equal(mediaItem, that.mediaItem);
|
||||
&& Objects.equal(periodUid, that.periodUid);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -375,16 +370,97 @@ public interface Player {
|
||||
adIndexInAdGroup);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this position info and the other position info would result in the same
|
||||
* {@link #toBundle() Bundle}.
|
||||
*/
|
||||
@UnstableApi
|
||||
public boolean equalsForBundling(PositionInfo other) {
|
||||
return mediaItemIndex == other.mediaItemIndex
|
||||
&& periodIndex == other.periodIndex
|
||||
&& positionMs == other.positionMs
|
||||
&& contentPositionMs == other.contentPositionMs
|
||||
&& adGroupIndex == other.adGroupIndex
|
||||
&& adIndexInAdGroup == other.adIndexInAdGroup
|
||||
&& Objects.equal(mediaItem, other.mediaItem);
|
||||
}
|
||||
|
||||
// Bundleable implementation.
|
||||
|
||||
private static final String FIELD_MEDIA_ITEM_INDEX = Util.intToStringMaxRadix(0);
|
||||
@VisibleForTesting static final String FIELD_MEDIA_ITEM_INDEX = Util.intToStringMaxRadix(0);
|
||||
private static final String FIELD_MEDIA_ITEM = Util.intToStringMaxRadix(1);
|
||||
private static final String FIELD_PERIOD_INDEX = Util.intToStringMaxRadix(2);
|
||||
private static final String FIELD_POSITION_MS = Util.intToStringMaxRadix(3);
|
||||
private static final String FIELD_CONTENT_POSITION_MS = Util.intToStringMaxRadix(4);
|
||||
@VisibleForTesting static final String FIELD_PERIOD_INDEX = Util.intToStringMaxRadix(2);
|
||||
@VisibleForTesting static final String FIELD_POSITION_MS = Util.intToStringMaxRadix(3);
|
||||
@VisibleForTesting static final String FIELD_CONTENT_POSITION_MS = Util.intToStringMaxRadix(4);
|
||||
private static final String FIELD_AD_GROUP_INDEX = Util.intToStringMaxRadix(5);
|
||||
private static final String FIELD_AD_INDEX_IN_AD_GROUP = Util.intToStringMaxRadix(6);
|
||||
|
||||
/**
|
||||
* Returns a copy of this position info, filtered by the specified available commands.
|
||||
*
|
||||
* <p>The filtered fields are reset to their default values.
|
||||
*
|
||||
* <p>The return value may be the same object if nothing is filtered.
|
||||
*
|
||||
* @param canAccessCurrentMediaItem Whether {@link Player#COMMAND_GET_CURRENT_MEDIA_ITEM} is
|
||||
* available.
|
||||
* @param canAccessTimeline Whether {@link Player#COMMAND_GET_TIMELINE} is available.
|
||||
* @return The filtered position info.
|
||||
*/
|
||||
@UnstableApi
|
||||
public PositionInfo filterByAvailableCommands(
|
||||
boolean canAccessCurrentMediaItem, boolean canAccessTimeline) {
|
||||
if (canAccessCurrentMediaItem && canAccessTimeline) {
|
||||
return this;
|
||||
}
|
||||
return new PositionInfo(
|
||||
windowUid,
|
||||
canAccessTimeline ? mediaItemIndex : 0,
|
||||
canAccessCurrentMediaItem ? mediaItem : null,
|
||||
periodUid,
|
||||
canAccessTimeline ? periodIndex : 0,
|
||||
canAccessCurrentMediaItem ? positionMs : 0,
|
||||
canAccessCurrentMediaItem ? contentPositionMs : 0,
|
||||
canAccessCurrentMediaItem ? adGroupIndex : C.INDEX_UNSET,
|
||||
canAccessCurrentMediaItem ? adIndexInAdGroup : C.INDEX_UNSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>It omits the {@link #windowUid} and {@link #periodUid} fields. The {@link #windowUid} and
|
||||
* {@link #periodUid} of an instance restored by {@link #CREATOR} will always be {@code null}.
|
||||
*
|
||||
* @param controllerInterfaceVersion The interface version of the media controller this Bundle
|
||||
* will be sent to.
|
||||
*/
|
||||
@UnstableApi
|
||||
public Bundle toBundle(int controllerInterfaceVersion) {
|
||||
Bundle bundle = new Bundle();
|
||||
if (controllerInterfaceVersion < 3 || mediaItemIndex != 0) {
|
||||
bundle.putInt(FIELD_MEDIA_ITEM_INDEX, mediaItemIndex);
|
||||
}
|
||||
if (mediaItem != null) {
|
||||
bundle.putBundle(FIELD_MEDIA_ITEM, mediaItem.toBundle());
|
||||
}
|
||||
if (controllerInterfaceVersion < 3 || periodIndex != 0) {
|
||||
bundle.putInt(FIELD_PERIOD_INDEX, periodIndex);
|
||||
}
|
||||
if (controllerInterfaceVersion < 3 || positionMs != 0) {
|
||||
bundle.putLong(FIELD_POSITION_MS, positionMs);
|
||||
}
|
||||
if (controllerInterfaceVersion < 3 || contentPositionMs != 0) {
|
||||
bundle.putLong(FIELD_CONTENT_POSITION_MS, contentPositionMs);
|
||||
}
|
||||
if (adGroupIndex != C.INDEX_UNSET) {
|
||||
bundle.putInt(FIELD_AD_GROUP_INDEX, adGroupIndex);
|
||||
}
|
||||
if (adIndexInAdGroup != C.INDEX_UNSET) {
|
||||
bundle.putInt(FIELD_AD_INDEX_IN_AD_GROUP, adIndexInAdGroup);
|
||||
}
|
||||
return bundle;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
@ -394,32 +470,7 @@ public interface Player {
|
||||
@UnstableApi
|
||||
@Override
|
||||
public Bundle toBundle() {
|
||||
return toBundle(/* canAccessCurrentMediaItem= */ true, /* canAccessTimeline= */ true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link Bundle} representing the information stored in this object, filtered by
|
||||
* available commands.
|
||||
*
|
||||
* @param canAccessCurrentMediaItem Whether the {@link Bundle} should contain information
|
||||
* accessbile with {@link #COMMAND_GET_CURRENT_MEDIA_ITEM}.
|
||||
* @param canAccessTimeline Whether the {@link Bundle} should contain information accessbile
|
||||
* with {@link #COMMAND_GET_TIMELINE}.
|
||||
*/
|
||||
@UnstableApi
|
||||
public Bundle toBundle(boolean canAccessCurrentMediaItem, boolean canAccessTimeline) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putInt(FIELD_MEDIA_ITEM_INDEX, canAccessTimeline ? mediaItemIndex : 0);
|
||||
if (mediaItem != null && canAccessCurrentMediaItem) {
|
||||
bundle.putBundle(FIELD_MEDIA_ITEM, mediaItem.toBundle());
|
||||
}
|
||||
bundle.putInt(FIELD_PERIOD_INDEX, canAccessTimeline ? periodIndex : 0);
|
||||
bundle.putLong(FIELD_POSITION_MS, canAccessCurrentMediaItem ? positionMs : 0);
|
||||
bundle.putLong(FIELD_CONTENT_POSITION_MS, canAccessCurrentMediaItem ? contentPositionMs : 0);
|
||||
bundle.putInt(FIELD_AD_GROUP_INDEX, canAccessCurrentMediaItem ? adGroupIndex : C.INDEX_UNSET);
|
||||
bundle.putInt(
|
||||
FIELD_AD_INDEX_IN_AD_GROUP, canAccessCurrentMediaItem ? adIndexInAdGroup : C.INDEX_UNSET);
|
||||
return bundle;
|
||||
return toBundle(Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
/** Object that can restore {@link PositionInfo} from a {@link Bundle}. */
|
||||
@ -3320,7 +3371,7 @@ public interface Player {
|
||||
* remote device is returned.
|
||||
*
|
||||
* <p>Note that this method returns the volume of the device. To check the current stream volume,
|
||||
* use {@link getVolume()}.
|
||||
* use {@link #getVolume()}.
|
||||
*
|
||||
* <p>This method must only be called if {@link #COMMAND_GET_DEVICE_VOLUME} is {@linkplain
|
||||
* #getAvailableCommands() available}.
|
||||
|
@ -1443,36 +1443,29 @@ public abstract class Timeline implements Bundleable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link Bundle} containing just the specified {@link Window}.
|
||||
* Returns a copy of this timeline containing just the single specified {@link Window}.
|
||||
*
|
||||
* <p>The {@link #getWindow(int, Window)} windows} and {@link #getPeriod(int, Period) periods} of
|
||||
* an instance restored by {@link #CREATOR} may have missing fields as described in {@link
|
||||
* Window#toBundle()} and {@link Period#toBundle()}.
|
||||
* <p>The method returns the same instance if there is only one window.
|
||||
*
|
||||
* @param windowIndex The index of the {@link Window} to include in the {@link Bundle}.
|
||||
* @param windowIndex The index of the {@link Window} to include in the copy.
|
||||
* @return A {@link Timeline} with just the single specified {@link Window}.
|
||||
*/
|
||||
@UnstableApi
|
||||
public final Bundle toBundleWithOneWindowOnly(int windowIndex) {
|
||||
Window window = getWindow(windowIndex, new Window(), /* defaultPositionProjectionUs= */ 0);
|
||||
|
||||
List<Bundle> periodBundles = new ArrayList<>();
|
||||
Period period = new Period();
|
||||
for (int i = window.firstPeriodIndex; i <= window.lastPeriodIndex; i++) {
|
||||
getPeriod(i, period, /* setIds= */ false);
|
||||
period.windowIndex = 0;
|
||||
periodBundles.add(period.toBundle());
|
||||
public final Timeline copyWithSingleWindow(int windowIndex) {
|
||||
if (getWindowCount() == 1) {
|
||||
return this;
|
||||
}
|
||||
Window window = getWindow(windowIndex, new Window(), /* defaultPositionProjectionUs= */ 0);
|
||||
ImmutableList.Builder<Period> periods = ImmutableList.builder();
|
||||
for (int i = window.firstPeriodIndex; i <= window.lastPeriodIndex; i++) {
|
||||
Period period = getPeriod(i, new Period(), /* setIds= */ true);
|
||||
period.windowIndex = 0;
|
||||
periods.add(period);
|
||||
}
|
||||
|
||||
window.lastPeriodIndex = window.lastPeriodIndex - window.firstPeriodIndex;
|
||||
window.firstPeriodIndex = 0;
|
||||
Bundle windowBundle = window.toBundle();
|
||||
|
||||
Bundle bundle = new Bundle();
|
||||
BundleUtil.putBinder(
|
||||
bundle, FIELD_WINDOWS, new BundleListRetriever(ImmutableList.of(windowBundle)));
|
||||
BundleUtil.putBinder(bundle, FIELD_PERIODS, new BundleListRetriever(periodBundles));
|
||||
bundle.putIntArray(FIELD_SHUFFLED_WINDOW_INDICES, new int[] {0});
|
||||
return bundle;
|
||||
return new RemotableTimeline(
|
||||
ImmutableList.of(window), periods.build(), /* shuffledWindowIndices= */ new int[] {0});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -17,6 +17,7 @@ package androidx.media3.common;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.os.Bundle;
|
||||
import androidx.media3.common.Player.PositionInfo;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import org.junit.Test;
|
||||
@ -31,7 +32,7 @@ public class PositionInfoTest {
|
||||
PositionInfo positionInfo =
|
||||
new PositionInfo(
|
||||
/* windowUid= */ null,
|
||||
/* windowIndex= */ 23,
|
||||
/* mediaItemIndex= */ 23,
|
||||
new MediaItem.Builder().setMediaId("1234").build(),
|
||||
/* periodUid= */ null,
|
||||
/* periodIndex= */ 11,
|
||||
@ -48,7 +49,7 @@ public class PositionInfoTest {
|
||||
PositionInfo positionInfo =
|
||||
new PositionInfo(
|
||||
/* windowUid= */ new Object(),
|
||||
/* windowIndex= */ 23,
|
||||
/* mediaItemIndex= */ 23,
|
||||
MediaItem.fromUri("https://exoplayer.dev"),
|
||||
/* periodUid= */ null,
|
||||
/* periodIndex= */ 11,
|
||||
@ -66,7 +67,7 @@ public class PositionInfoTest {
|
||||
PositionInfo positionInfo =
|
||||
new PositionInfo(
|
||||
/* windowUid= */ null,
|
||||
/* windowIndex= */ 23,
|
||||
/* mediaItemIndex= */ 23,
|
||||
MediaItem.fromUri("https://exoplayer.dev"),
|
||||
/* periodUid= */ new Object(),
|
||||
/* periodIndex= */ 11,
|
||||
@ -78,4 +79,69 @@ public class PositionInfoTest {
|
||||
PositionInfo positionInfoFromBundle = PositionInfo.CREATOR.fromBundle(positionInfo.toBundle());
|
||||
assertThat(positionInfoFromBundle.periodUid).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void roundTripViaBundle_withDefaultValues_yieldsEqualInstance() {
|
||||
PositionInfo defaultPositionInfo =
|
||||
new PositionInfo(
|
||||
/* windowUid= */ null,
|
||||
/* mediaItemIndex= */ 0,
|
||||
/* mediaItem= */ null,
|
||||
/* periodUid= */ null,
|
||||
/* periodIndex= */ 0,
|
||||
/* positionMs= */ 0,
|
||||
/* contentPositionMs= */ 0,
|
||||
/* adGroupIndex= */ C.INDEX_UNSET,
|
||||
/* adIndexInAdGroup= */ C.INDEX_UNSET);
|
||||
|
||||
PositionInfo roundTripValue = PositionInfo.CREATOR.fromBundle(defaultPositionInfo.toBundle());
|
||||
|
||||
assertThat(roundTripValue).isEqualTo(defaultPositionInfo);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toBundle_withDefaultValues_omitsAllData() {
|
||||
PositionInfo defaultPositionInfo =
|
||||
new PositionInfo(
|
||||
/* windowUid= */ null,
|
||||
/* mediaItemIndex= */ 0,
|
||||
/* mediaItem= */ null,
|
||||
/* periodUid= */ null,
|
||||
/* periodIndex= */ 0,
|
||||
/* positionMs= */ 0,
|
||||
/* contentPositionMs= */ 0,
|
||||
/* adGroupIndex= */ C.INDEX_UNSET,
|
||||
/* adIndexInAdGroup= */ C.INDEX_UNSET);
|
||||
|
||||
Bundle bundle =
|
||||
defaultPositionInfo.toBundle(/* controllerInterfaceVersion= */ Integer.MAX_VALUE);
|
||||
|
||||
assertThat(bundle.isEmpty()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toBundle_withDefaultValuesForControllerInterfaceBefore3_includesDefaultValues() {
|
||||
// Controller before version 3 uses invalid default values for indices and the Bundle should
|
||||
// always include them to avoid using the default values in the controller code.
|
||||
PositionInfo defaultPositionInfo =
|
||||
new PositionInfo(
|
||||
/* windowUid= */ null,
|
||||
/* mediaItemIndex= */ 0,
|
||||
/* mediaItem= */ null,
|
||||
/* periodUid= */ null,
|
||||
/* periodIndex= */ 0,
|
||||
/* positionMs= */ 0,
|
||||
/* contentPositionMs= */ 0,
|
||||
/* adGroupIndex= */ C.INDEX_UNSET,
|
||||
/* adIndexInAdGroup= */ C.INDEX_UNSET);
|
||||
|
||||
Bundle bundle = defaultPositionInfo.toBundle(/* controllerInterfaceVersion= */ 2);
|
||||
|
||||
assertThat(bundle.keySet())
|
||||
.containsAtLeast(
|
||||
PositionInfo.FIELD_MEDIA_ITEM_INDEX,
|
||||
PositionInfo.FIELD_CONTENT_POSITION_MS,
|
||||
PositionInfo.FIELD_PERIOD_INDEX,
|
||||
PositionInfo.FIELD_POSITION_MS);
|
||||
}
|
||||
}
|
||||
|
@ -98,6 +98,10 @@ import java.util.List;
|
||||
|
||||
@Override
|
||||
public Bundle toBundle() {
|
||||
return toBundle(Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
public Bundle toBundle(int controllerInterfaceVersion) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putInt(FIELD_LIBRARY_VERSION, libraryVersion);
|
||||
BundleCompat.putBinder(bundle, FIELD_SESSION_BINDER, sessionBinder.asBinder());
|
||||
@ -114,8 +118,10 @@ import java.util.List;
|
||||
MediaUtils.intersect(playerCommandsFromSession, playerCommandsFromPlayer);
|
||||
bundle.putBundle(
|
||||
FIELD_PLAYER_INFO,
|
||||
playerInfo.toBundle(
|
||||
intersectedCommands, /* excludeTimeline= */ false, /* excludeTracks= */ false));
|
||||
playerInfo
|
||||
.filterByAvailableCommands(
|
||||
intersectedCommands, /* excludeTimeline= */ false, /* excludeTracks= */ false)
|
||||
.toBundle(controllerInterfaceVersion));
|
||||
bundle.putInt(FIELD_SESSION_INTERFACE_VERSION, sessionInterfaceVersion);
|
||||
return bundle;
|
||||
}
|
||||
|
@ -1668,7 +1668,8 @@ public class MediaSession {
|
||||
int seq,
|
||||
SessionPositionInfo sessionPositionInfo,
|
||||
boolean canAccessCurrentMediaItem,
|
||||
boolean canAccessTimeline)
|
||||
boolean canAccessTimeline,
|
||||
int controllerInterfaceVersion)
|
||||
throws RemoteException {}
|
||||
|
||||
// Mostly matched with MediaController.ControllerCallback
|
||||
|
@ -932,7 +932,11 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
controller,
|
||||
(controllerCb, seq) ->
|
||||
controllerCb.onPeriodicSessionPositionInfoChanged(
|
||||
seq, sessionPositionInfo, canAccessCurrentMediaItem, canAccessTimeline));
|
||||
seq,
|
||||
sessionPositionInfo,
|
||||
canAccessCurrentMediaItem,
|
||||
canAccessTimeline,
|
||||
controller.getInterfaceVersion()));
|
||||
}
|
||||
try {
|
||||
sessionLegacyStub
|
||||
@ -941,7 +945,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
/* seq= */ 0,
|
||||
sessionPositionInfo,
|
||||
/* canAccessCurrentMediaItem= */ true,
|
||||
/* canAccessTimeline= */ true);
|
||||
/* canAccessTimeline= */ true,
|
||||
ControllerInfo.LEGACY_CONTROLLER_INTERFACE_VERSION);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Exception in using media1 API", e);
|
||||
}
|
||||
|
@ -1331,7 +1331,8 @@ import org.checkerframework.checker.initialization.qual.Initialized;
|
||||
int unusedSeq,
|
||||
SessionPositionInfo unusedSessionPositionInfo,
|
||||
boolean unusedCanAccessCurrentMediaItem,
|
||||
boolean unusedCanAccessTimeline)
|
||||
boolean unusedCanAccessTimeline,
|
||||
int controllerInterfaceVersion)
|
||||
throws RemoteException {
|
||||
updateLegacySessionPlaybackState(sessionImpl.getPlayerWrapper());
|
||||
}
|
||||
|
@ -117,6 +117,12 @@ import java.util.concurrent.ExecutionException;
|
||||
/** The version of the IMediaSession interface. */
|
||||
public static final int VERSION_INT = 2;
|
||||
|
||||
/**
|
||||
* Sequence number used when a controller method is triggered on the sesison side that wasn't
|
||||
* initiated by the controller itself.
|
||||
*/
|
||||
public static final int UNKNOWN_SEQUENCE_NUMBER = Integer.MIN_VALUE;
|
||||
|
||||
private final WeakReference<MediaSessionImpl> sessionImpl;
|
||||
private final MediaSessionManager sessionManager;
|
||||
private final ConnectedControllersManager<IBinder> connectedControllersManager;
|
||||
@ -285,6 +291,18 @@ import java.util.concurrent.ExecutionException;
|
||||
int sequenceNumber,
|
||||
@Player.Command int command,
|
||||
SessionTask<ListenableFuture<Void>, K> task) {
|
||||
ControllerInfo controllerInfo = connectedControllersManager.getController(caller.asBinder());
|
||||
if (controllerInfo != null) {
|
||||
queueSessionTaskWithPlayerCommandForControllerInfo(
|
||||
controllerInfo, sequenceNumber, command, task);
|
||||
}
|
||||
}
|
||||
|
||||
private <K extends MediaSessionImpl> void queueSessionTaskWithPlayerCommandForControllerInfo(
|
||||
ControllerInfo controller,
|
||||
int sequenceNumber,
|
||||
@Player.Command int command,
|
||||
SessionTask<ListenableFuture<Void>, K> task) {
|
||||
long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
@SuppressWarnings({"unchecked", "cast.unsafe"})
|
||||
@ -293,11 +311,6 @@ import java.util.concurrent.ExecutionException;
|
||||
if (sessionImpl == null || sessionImpl.isReleased()) {
|
||||
return;
|
||||
}
|
||||
@Nullable
|
||||
ControllerInfo controller = connectedControllersManager.getController(caller.asBinder());
|
||||
if (controller == null) {
|
||||
return;
|
||||
}
|
||||
postOrRun(
|
||||
sessionImpl.getApplicationHandler(),
|
||||
() -> {
|
||||
@ -524,7 +537,10 @@ import java.util.concurrent.ExecutionException;
|
||||
}
|
||||
try {
|
||||
caller.onConnected(
|
||||
sequencedFutureManager.obtainNextSequenceNumber(), state.toBundle());
|
||||
sequencedFutureManager.obtainNextSequenceNumber(),
|
||||
caller instanceof MediaControllerStub
|
||||
? state.toBundleInProcess()
|
||||
: state.toBundle(controllerInfo.getInterfaceVersion()));
|
||||
connected = true;
|
||||
} catch (RemoteException e) {
|
||||
// Controller may be died prematurely.
|
||||
@ -618,8 +634,19 @@ import java.util.concurrent.ExecutionException;
|
||||
if (caller == null) {
|
||||
return;
|
||||
}
|
||||
queueSessionTaskWithPlayerCommand(
|
||||
caller, sequenceNumber, COMMAND_STOP, sendSessionResultSuccess(player -> player.stop()));
|
||||
@Nullable
|
||||
ControllerInfo controllerInfo = connectedControllersManager.getController(caller.asBinder());
|
||||
if (controllerInfo != null) {
|
||||
stopForControllerInfo(controllerInfo, sequenceNumber);
|
||||
}
|
||||
}
|
||||
|
||||
public void stopForControllerInfo(ControllerInfo controllerInfo, int sequenceNumber) {
|
||||
queueSessionTaskWithPlayerCommandForControllerInfo(
|
||||
controllerInfo,
|
||||
sequenceNumber,
|
||||
COMMAND_STOP,
|
||||
sendSessionResultSuccess(PlayerWrapper::stop));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -674,27 +701,30 @@ import java.util.concurrent.ExecutionException;
|
||||
if (caller == null) {
|
||||
return;
|
||||
}
|
||||
@Nullable
|
||||
ControllerInfo controller = connectedControllersManager.getController(caller.asBinder());
|
||||
if (controller == null) {
|
||||
return;
|
||||
if (controller != null) {
|
||||
playForControllerInfo(controller, sequenceNumber);
|
||||
}
|
||||
queueSessionTaskWithPlayerCommand(
|
||||
caller,
|
||||
}
|
||||
|
||||
public void playForControllerInfo(ControllerInfo controller, int sequenceNumber) {
|
||||
queueSessionTaskWithPlayerCommandForControllerInfo(
|
||||
controller,
|
||||
sequenceNumber,
|
||||
COMMAND_PLAY_PAUSE,
|
||||
sendSessionResultSuccess(
|
||||
player -> {
|
||||
@Nullable MediaSessionImpl sessionImpl = this.sessionImpl.get();
|
||||
if (sessionImpl == null || sessionImpl.isReleased()) {
|
||||
@Nullable MediaSessionImpl impl = sessionImpl.get();
|
||||
if (impl == null || impl.isReleased()) {
|
||||
return;
|
||||
}
|
||||
if (sessionImpl.onPlayRequested()) {
|
||||
if (impl.onPlayRequested()) {
|
||||
if (player.getMediaItemCount() == 0) {
|
||||
// The player is in IDLE or ENDED state and has no media items in the playlist
|
||||
// yet.
|
||||
// Handle the play command as a playback resumption command to try resume
|
||||
// yet. Handle the play command as a playback resumption command to try resume
|
||||
// playback.
|
||||
sessionImpl.prepareAndPlayForPlaybackResumption(controller, player);
|
||||
impl.prepareAndPlayForPlaybackResumption(controller, player);
|
||||
} else {
|
||||
Util.handlePlayButtonAction(player);
|
||||
}
|
||||
@ -707,8 +737,16 @@ import java.util.concurrent.ExecutionException;
|
||||
if (caller == null) {
|
||||
return;
|
||||
}
|
||||
queueSessionTaskWithPlayerCommand(
|
||||
caller, sequenceNumber, COMMAND_PLAY_PAUSE, sendSessionResultSuccess(Player::pause));
|
||||
@Nullable
|
||||
ControllerInfo controllerInfo = connectedControllersManager.getController(caller.asBinder());
|
||||
if (controllerInfo != null) {
|
||||
pauseForControllerInfo(controllerInfo, sequenceNumber);
|
||||
}
|
||||
}
|
||||
|
||||
public void pauseForControllerInfo(ControllerInfo controller, int sequenceNumber) {
|
||||
queueSessionTaskWithPlayerCommandForControllerInfo(
|
||||
controller, sequenceNumber, COMMAND_PLAY_PAUSE, sendSessionResultSuccess(Player::pause));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -784,8 +822,19 @@ import java.util.concurrent.ExecutionException;
|
||||
if (caller == null) {
|
||||
return;
|
||||
}
|
||||
queueSessionTaskWithPlayerCommand(
|
||||
caller, sequenceNumber, COMMAND_SEEK_BACK, sendSessionResultSuccess(Player::seekBack));
|
||||
@Nullable
|
||||
ControllerInfo controllerInfo = connectedControllersManager.getController(caller.asBinder());
|
||||
if (controllerInfo != null) {
|
||||
seekBackForControllerInfo(controllerInfo, sequenceNumber);
|
||||
}
|
||||
}
|
||||
|
||||
public void seekBackForControllerInfo(ControllerInfo controllerInfo, int sequenceNumber) {
|
||||
queueSessionTaskWithPlayerCommandForControllerInfo(
|
||||
controllerInfo,
|
||||
sequenceNumber,
|
||||
COMMAND_SEEK_BACK,
|
||||
sendSessionResultSuccess(Player::seekBack));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -793,8 +842,16 @@ import java.util.concurrent.ExecutionException;
|
||||
if (caller == null) {
|
||||
return;
|
||||
}
|
||||
queueSessionTaskWithPlayerCommand(
|
||||
caller,
|
||||
@Nullable
|
||||
ControllerInfo controllerInfo = connectedControllersManager.getController(caller.asBinder());
|
||||
if (controllerInfo != null) {
|
||||
seekForwardForControllerInfo(controllerInfo, sequenceNumber);
|
||||
}
|
||||
}
|
||||
|
||||
public void seekForwardForControllerInfo(ControllerInfo controllerInfo, int sequenceNumber) {
|
||||
queueSessionTaskWithPlayerCommandForControllerInfo(
|
||||
controllerInfo,
|
||||
sequenceNumber,
|
||||
COMMAND_SEEK_FORWARD,
|
||||
sendSessionResultSuccess(Player::seekForward));
|
||||
@ -1362,8 +1419,16 @@ import java.util.concurrent.ExecutionException;
|
||||
if (caller == null) {
|
||||
return;
|
||||
}
|
||||
queueSessionTaskWithPlayerCommand(
|
||||
caller,
|
||||
@Nullable
|
||||
ControllerInfo controllerInfo = connectedControllersManager.getController(caller.asBinder());
|
||||
if (controllerInfo != null) {
|
||||
seekToPreviousForControllerInfo(controllerInfo, sequenceNumber);
|
||||
}
|
||||
}
|
||||
|
||||
public void seekToPreviousForControllerInfo(ControllerInfo controllerInfo, int sequenceNumber) {
|
||||
queueSessionTaskWithPlayerCommandForControllerInfo(
|
||||
controllerInfo,
|
||||
sequenceNumber,
|
||||
COMMAND_SEEK_TO_PREVIOUS,
|
||||
sendSessionResultSuccess(Player::seekToPrevious));
|
||||
@ -1374,8 +1439,19 @@ import java.util.concurrent.ExecutionException;
|
||||
if (caller == null) {
|
||||
return;
|
||||
}
|
||||
queueSessionTaskWithPlayerCommand(
|
||||
caller, sequenceNumber, COMMAND_SEEK_TO_NEXT, sendSessionResultSuccess(Player::seekToNext));
|
||||
@Nullable
|
||||
ControllerInfo controllerInfo = connectedControllersManager.getController(caller.asBinder());
|
||||
if (controllerInfo != null) {
|
||||
seekToNextForControllerInfo(controllerInfo, sequenceNumber);
|
||||
}
|
||||
}
|
||||
|
||||
public void seekToNextForControllerInfo(ControllerInfo controllerInfo, int sequenceNumber) {
|
||||
queueSessionTaskWithPlayerCommandForControllerInfo(
|
||||
controllerInfo,
|
||||
sequenceNumber,
|
||||
COMMAND_SEEK_TO_NEXT,
|
||||
sendSessionResultSuccess(Player::seekToNext));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1913,16 +1989,25 @@ import java.util.concurrent.ExecutionException;
|
||||
boolean bundlingExclusionsTracks =
|
||||
excludeTracks || !availableCommands.contains(Player.COMMAND_GET_TRACKS);
|
||||
if (controllerInterfaceVersion >= 2) {
|
||||
PlayerInfo filteredPlayerInfo =
|
||||
playerInfo.filterByAvailableCommands(availableCommands, excludeTimeline, excludeTracks);
|
||||
Bundle playerInfoBundle =
|
||||
iController instanceof MediaControllerStub
|
||||
? filteredPlayerInfo.toBundleInProcess()
|
||||
: filteredPlayerInfo.toBundle(controllerInterfaceVersion);
|
||||
iController.onPlayerInfoChangedWithExclusions(
|
||||
sequenceNumber,
|
||||
playerInfo.toBundle(availableCommands, excludeTimeline, excludeTracks),
|
||||
playerInfoBundle,
|
||||
new PlayerInfo.BundlingExclusions(bundlingExclusionsTimeline, bundlingExclusionsTracks)
|
||||
.toBundle());
|
||||
} else {
|
||||
PlayerInfo filteredPlayerInfo =
|
||||
playerInfo.filterByAvailableCommands(
|
||||
availableCommands, excludeTimeline, /* excludeTracks= */ true);
|
||||
//noinspection deprecation
|
||||
iController.onPlayerInfoChanged(
|
||||
sequenceNumber,
|
||||
playerInfo.toBundle(availableCommands, excludeTimeline, /* excludeTracks= */ true),
|
||||
filteredPlayerInfo.toBundle(controllerInterfaceVersion),
|
||||
bundlingExclusionsTimeline);
|
||||
}
|
||||
}
|
||||
@ -1988,11 +2073,14 @@ import java.util.concurrent.ExecutionException;
|
||||
int sequenceNumber,
|
||||
SessionPositionInfo sessionPositionInfo,
|
||||
boolean canAccessCurrentMediaItem,
|
||||
boolean canAccessTimeline)
|
||||
boolean canAccessTimeline,
|
||||
int controllerInterfaceVersion)
|
||||
throws RemoteException {
|
||||
iController.onPeriodicSessionPositionInfoChanged(
|
||||
sequenceNumber,
|
||||
sessionPositionInfo.toBundle(canAccessCurrentMediaItem, canAccessTimeline));
|
||||
sessionPositionInfo
|
||||
.filterByAvailableCommands(canAccessCurrentMediaItem, canAccessTimeline)
|
||||
.toBundle(controllerInterfaceVersion));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -22,10 +22,13 @@ import static androidx.media3.common.Player.PLAY_WHEN_READY_CHANGE_REASON_USER_R
|
||||
import static androidx.media3.common.Player.STATE_IDLE;
|
||||
import static androidx.media3.common.Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED;
|
||||
|
||||
import android.os.Binder;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import androidx.annotation.CheckResult;
|
||||
import androidx.annotation.FloatRange;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.media3.common.AudioAttributes;
|
||||
import androidx.media3.common.Bundleable;
|
||||
import androidx.media3.common.DeviceInfo;
|
||||
@ -44,6 +47,7 @@ import androidx.media3.common.Tracks;
|
||||
import androidx.media3.common.VideoSize;
|
||||
import androidx.media3.common.text.CueGroup;
|
||||
import androidx.media3.common.util.Assertions;
|
||||
import androidx.media3.common.util.BundleUtil;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import androidx.media3.common.util.Util;
|
||||
import com.google.common.base.Objects;
|
||||
@ -811,10 +815,10 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
||||
private static final String FIELD_IS_PLAYING = Util.intToStringMaxRadix(16);
|
||||
private static final String FIELD_IS_LOADING = Util.intToStringMaxRadix(17);
|
||||
private static final String FIELD_PLAYBACK_ERROR = Util.intToStringMaxRadix(18);
|
||||
private static final String FIELD_SESSION_POSITION_INFO = Util.intToStringMaxRadix(19);
|
||||
@VisibleForTesting static final String FIELD_SESSION_POSITION_INFO = Util.intToStringMaxRadix(19);
|
||||
private static final String FIELD_MEDIA_ITEM_TRANSITION_REASON = Util.intToStringMaxRadix(20);
|
||||
private static final String FIELD_OLD_POSITION_INFO = Util.intToStringMaxRadix(21);
|
||||
private static final String FIELD_NEW_POSITION_INFO = Util.intToStringMaxRadix(22);
|
||||
@VisibleForTesting static final String FIELD_OLD_POSITION_INFO = Util.intToStringMaxRadix(21);
|
||||
@VisibleForTesting static final String FIELD_NEW_POSITION_INFO = Util.intToStringMaxRadix(22);
|
||||
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);
|
||||
@ -824,94 +828,198 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
||||
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);
|
||||
private static final String FIELD_IN_PROCESS_BINDER = Util.intToStringMaxRadix(32);
|
||||
|
||||
// Next field key = 32
|
||||
// Next field key = 33
|
||||
|
||||
public Bundle toBundle(
|
||||
/**
|
||||
* Returns a copy of this player info, filtered by the specified available commands.
|
||||
*
|
||||
* <p>The filtered fields are reset to their default values.
|
||||
*
|
||||
* @param availableCommands The available {@link Player.Commands} used to filter values.
|
||||
* @param excludeTimeline Whether to filter the {@link #timeline} even if {@link
|
||||
* Player#COMMAND_GET_TIMELINE} is available.
|
||||
* @param excludeTracks Whether to filter the {@link #currentTracks} even if {@link
|
||||
* Player#COMMAND_GET_TRACKS} is available.
|
||||
* @return The filtered player info.
|
||||
*/
|
||||
public PlayerInfo filterByAvailableCommands(
|
||||
Player.Commands availableCommands, boolean excludeTimeline, boolean excludeTracks) {
|
||||
Bundle bundle = new Bundle();
|
||||
PlayerInfo.Builder builder = new Builder(this);
|
||||
boolean canAccessCurrentMediaItem =
|
||||
availableCommands.contains(Player.COMMAND_GET_CURRENT_MEDIA_ITEM);
|
||||
boolean canAccessTimeline = availableCommands.contains(Player.COMMAND_GET_TIMELINE);
|
||||
if (playerError != null) {
|
||||
bundle.putBundle(FIELD_PLAYBACK_ERROR, playerError.toBundle());
|
||||
builder.setSessionPositionInfo(
|
||||
sessionPositionInfo.filterByAvailableCommands(
|
||||
canAccessCurrentMediaItem, canAccessTimeline));
|
||||
builder.setOldPositionInfo(
|
||||
oldPositionInfo.filterByAvailableCommands(canAccessCurrentMediaItem, canAccessTimeline));
|
||||
builder.setNewPositionInfo(
|
||||
newPositionInfo.filterByAvailableCommands(canAccessCurrentMediaItem, canAccessTimeline));
|
||||
if (!canAccessTimeline && canAccessCurrentMediaItem && !timeline.isEmpty()) {
|
||||
builder.setTimeline(
|
||||
timeline.copyWithSingleWindow(sessionPositionInfo.positionInfo.mediaItemIndex));
|
||||
} else if (excludeTimeline || !canAccessTimeline) {
|
||||
builder.setTimeline(Timeline.EMPTY);
|
||||
}
|
||||
bundle.putInt(FIELD_MEDIA_ITEM_TRANSITION_REASON, mediaItemTransitionReason);
|
||||
bundle.putBundle(
|
||||
FIELD_SESSION_POSITION_INFO,
|
||||
sessionPositionInfo.toBundle(canAccessCurrentMediaItem, canAccessTimeline));
|
||||
bundle.putBundle(
|
||||
FIELD_OLD_POSITION_INFO,
|
||||
oldPositionInfo.toBundle(canAccessCurrentMediaItem, canAccessTimeline));
|
||||
bundle.putBundle(
|
||||
FIELD_NEW_POSITION_INFO,
|
||||
newPositionInfo.toBundle(canAccessCurrentMediaItem, canAccessTimeline));
|
||||
bundle.putInt(FIELD_DISCONTINUITY_REASON, discontinuityReason);
|
||||
bundle.putBundle(FIELD_PLAYBACK_PARAMETERS, playbackParameters.toBundle());
|
||||
bundle.putInt(FIELD_REPEAT_MODE, repeatMode);
|
||||
bundle.putBoolean(FIELD_SHUFFLE_MODE_ENABLED, shuffleModeEnabled);
|
||||
if (!excludeTimeline && canAccessTimeline) {
|
||||
bundle.putBundle(FIELD_TIMELINE, timeline.toBundle());
|
||||
} else if (!canAccessTimeline && canAccessCurrentMediaItem && !timeline.isEmpty()) {
|
||||
bundle.putBundle(
|
||||
FIELD_TIMELINE,
|
||||
timeline.toBundleWithOneWindowOnly(sessionPositionInfo.positionInfo.mediaItemIndex));
|
||||
if (!availableCommands.contains(Player.COMMAND_GET_METADATA)) {
|
||||
builder.setPlaylistMetadata(MediaMetadata.EMPTY);
|
||||
}
|
||||
bundle.putInt(FIELD_TIMELINE_CHANGE_REASON, timelineChangeReason);
|
||||
bundle.putBundle(FIELD_VIDEO_SIZE, videoSize.toBundle());
|
||||
if (availableCommands.contains(Player.COMMAND_GET_METADATA)) {
|
||||
bundle.putBundle(FIELD_PLAYLIST_METADATA, playlistMetadata.toBundle());
|
||||
if (!availableCommands.contains(Player.COMMAND_GET_VOLUME)) {
|
||||
builder.setVolume(1);
|
||||
}
|
||||
if (availableCommands.contains(Player.COMMAND_GET_VOLUME)) {
|
||||
bundle.putFloat(FIELD_VOLUME, volume);
|
||||
if (!availableCommands.contains(Player.COMMAND_GET_AUDIO_ATTRIBUTES)) {
|
||||
builder.setAudioAttributes(AudioAttributes.DEFAULT);
|
||||
}
|
||||
if (availableCommands.contains(Player.COMMAND_GET_AUDIO_ATTRIBUTES)) {
|
||||
bundle.putBundle(FIELD_AUDIO_ATTRIBUTES, audioAttributes.toBundle());
|
||||
if (!availableCommands.contains(Player.COMMAND_GET_TEXT)) {
|
||||
builder.setCues(CueGroup.EMPTY_TIME_ZERO);
|
||||
}
|
||||
if (availableCommands.contains(Player.COMMAND_GET_TEXT)) {
|
||||
bundle.putBundle(FIELD_CUE_GROUP, cueGroup.toBundle());
|
||||
if (!availableCommands.contains(Player.COMMAND_GET_DEVICE_VOLUME)) {
|
||||
builder.setDeviceVolume(0).setDeviceMuted(false);
|
||||
}
|
||||
bundle.putBundle(FIELD_DEVICE_INFO, deviceInfo.toBundle());
|
||||
if (availableCommands.contains(Player.COMMAND_GET_DEVICE_VOLUME)) {
|
||||
bundle.putInt(FIELD_DEVICE_VOLUME, deviceVolume);
|
||||
bundle.putBoolean(FIELD_DEVICE_MUTED, deviceMuted);
|
||||
if (!availableCommands.contains(Player.COMMAND_GET_METADATA)) {
|
||||
builder.setMediaMetadata(MediaMetadata.EMPTY);
|
||||
}
|
||||
bundle.putBoolean(FIELD_PLAY_WHEN_READY, playWhenReady);
|
||||
bundle.putInt(FIELD_PLAYBACK_SUPPRESSION_REASON, playbackSuppressionReason);
|
||||
bundle.putInt(FIELD_PLAYBACK_STATE, playbackState);
|
||||
bundle.putBoolean(FIELD_IS_PLAYING, isPlaying);
|
||||
bundle.putBoolean(FIELD_IS_LOADING, isLoading);
|
||||
if (availableCommands.contains(Player.COMMAND_GET_METADATA)) {
|
||||
bundle.putBundle(FIELD_MEDIA_METADATA, mediaMetadata.toBundle());
|
||||
if (excludeTracks || !availableCommands.contains(Player.COMMAND_GET_TRACKS)) {
|
||||
builder.setCurrentTracks(Tracks.EMPTY);
|
||||
}
|
||||
bundle.putLong(FIELD_SEEK_BACK_INCREMENT_MS, seekBackIncrementMs);
|
||||
bundle.putLong(FIELD_SEEK_FORWARD_INCREMENT_MS, seekForwardIncrementMs);
|
||||
bundle.putLong(FIELD_MAX_SEEK_TO_PREVIOUS_POSITION_MS, maxSeekToPreviousPositionMs);
|
||||
if (!excludeTracks && availableCommands.contains(Player.COMMAND_GET_TRACKS)) {
|
||||
bundle.putBundle(FIELD_CURRENT_TRACKS, currentTracks.toBundle());
|
||||
}
|
||||
bundle.putBundle(FIELD_TRACK_SELECTION_PARAMETERS, trackSelectionParameters.toBundle());
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link Bundle} that stores a direct object reference to this class for in-process
|
||||
* sharing.
|
||||
*/
|
||||
public Bundle toBundleInProcess() {
|
||||
Bundle bundle = new Bundle();
|
||||
BundleUtil.putBinder(bundle, FIELD_IN_PROCESS_BINDER, new InProcessBinder());
|
||||
return bundle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle toBundle() {
|
||||
return toBundle(
|
||||
/* availableCommands= */ new Player.Commands.Builder().addAllCommands().build(),
|
||||
/* excludeTimeline= */ false,
|
||||
/* excludeTracks= */ false);
|
||||
return toBundle(Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
public Bundle toBundle(int controllerInterfaceVersion) {
|
||||
Bundle bundle = new Bundle();
|
||||
if (playerError != null) {
|
||||
bundle.putBundle(FIELD_PLAYBACK_ERROR, playerError.toBundle());
|
||||
}
|
||||
if (mediaItemTransitionReason != MEDIA_ITEM_TRANSITION_REASON_DEFAULT) {
|
||||
bundle.putInt(FIELD_MEDIA_ITEM_TRANSITION_REASON, mediaItemTransitionReason);
|
||||
}
|
||||
if (controllerInterfaceVersion < 3
|
||||
|| !sessionPositionInfo.equals(SessionPositionInfo.DEFAULT)) {
|
||||
bundle.putBundle(
|
||||
FIELD_SESSION_POSITION_INFO, sessionPositionInfo.toBundle(controllerInterfaceVersion));
|
||||
}
|
||||
if (controllerInterfaceVersion < 3
|
||||
|| !SessionPositionInfo.DEFAULT_POSITION_INFO.equalsForBundling(oldPositionInfo)) {
|
||||
bundle.putBundle(
|
||||
FIELD_OLD_POSITION_INFO, oldPositionInfo.toBundle(controllerInterfaceVersion));
|
||||
}
|
||||
if (controllerInterfaceVersion < 3
|
||||
|| !SessionPositionInfo.DEFAULT_POSITION_INFO.equalsForBundling(newPositionInfo)) {
|
||||
bundle.putBundle(
|
||||
FIELD_NEW_POSITION_INFO, newPositionInfo.toBundle(controllerInterfaceVersion));
|
||||
}
|
||||
if (discontinuityReason != DISCONTINUITY_REASON_DEFAULT) {
|
||||
bundle.putInt(FIELD_DISCONTINUITY_REASON, discontinuityReason);
|
||||
}
|
||||
if (!playbackParameters.equals(PlaybackParameters.DEFAULT)) {
|
||||
bundle.putBundle(FIELD_PLAYBACK_PARAMETERS, playbackParameters.toBundle());
|
||||
}
|
||||
if (repeatMode != Player.REPEAT_MODE_OFF) {
|
||||
bundle.putInt(FIELD_REPEAT_MODE, repeatMode);
|
||||
}
|
||||
if (shuffleModeEnabled) {
|
||||
bundle.putBoolean(FIELD_SHUFFLE_MODE_ENABLED, shuffleModeEnabled);
|
||||
}
|
||||
if (!timeline.equals(Timeline.EMPTY)) {
|
||||
bundle.putBundle(FIELD_TIMELINE, timeline.toBundle());
|
||||
}
|
||||
if (timelineChangeReason != TIMELINE_CHANGE_REASON_DEFAULT) {
|
||||
bundle.putInt(FIELD_TIMELINE_CHANGE_REASON, timelineChangeReason);
|
||||
}
|
||||
if (!videoSize.equals(VideoSize.UNKNOWN)) {
|
||||
bundle.putBundle(FIELD_VIDEO_SIZE, videoSize.toBundle());
|
||||
}
|
||||
if (!playlistMetadata.equals(MediaMetadata.EMPTY)) {
|
||||
bundle.putBundle(FIELD_PLAYLIST_METADATA, playlistMetadata.toBundle());
|
||||
}
|
||||
if (volume != 1) {
|
||||
bundle.putFloat(FIELD_VOLUME, volume);
|
||||
}
|
||||
if (!audioAttributes.equals(AudioAttributes.DEFAULT)) {
|
||||
bundle.putBundle(FIELD_AUDIO_ATTRIBUTES, audioAttributes.toBundle());
|
||||
}
|
||||
if (!cueGroup.equals(CueGroup.EMPTY_TIME_ZERO)) {
|
||||
bundle.putBundle(FIELD_CUE_GROUP, cueGroup.toBundle());
|
||||
}
|
||||
if (!deviceInfo.equals(DeviceInfo.UNKNOWN)) {
|
||||
bundle.putBundle(FIELD_DEVICE_INFO, deviceInfo.toBundle());
|
||||
}
|
||||
if (deviceVolume != 0) {
|
||||
bundle.putInt(FIELD_DEVICE_VOLUME, deviceVolume);
|
||||
}
|
||||
if (deviceMuted) {
|
||||
bundle.putBoolean(FIELD_DEVICE_MUTED, deviceMuted);
|
||||
}
|
||||
if (playWhenReady) {
|
||||
bundle.putBoolean(FIELD_PLAY_WHEN_READY, playWhenReady);
|
||||
}
|
||||
if (playWhenReadyChangeReason != PLAY_WHEN_READY_CHANGE_REASON_DEFAULT) {
|
||||
bundle.putInt(FIELD_PLAY_WHEN_READY_CHANGE_REASON, playWhenReadyChangeReason);
|
||||
}
|
||||
if (playbackSuppressionReason != PLAYBACK_SUPPRESSION_REASON_NONE) {
|
||||
bundle.putInt(FIELD_PLAYBACK_SUPPRESSION_REASON, playbackSuppressionReason);
|
||||
}
|
||||
if (playbackState != STATE_IDLE) {
|
||||
bundle.putInt(FIELD_PLAYBACK_STATE, playbackState);
|
||||
}
|
||||
if (isPlaying) {
|
||||
bundle.putBoolean(FIELD_IS_PLAYING, isPlaying);
|
||||
}
|
||||
if (isLoading) {
|
||||
bundle.putBoolean(FIELD_IS_LOADING, isLoading);
|
||||
}
|
||||
if (!mediaMetadata.equals(MediaMetadata.EMPTY)) {
|
||||
bundle.putBundle(FIELD_MEDIA_METADATA, mediaMetadata.toBundle());
|
||||
}
|
||||
if (seekBackIncrementMs != 0) {
|
||||
bundle.putLong(FIELD_SEEK_BACK_INCREMENT_MS, seekBackIncrementMs);
|
||||
}
|
||||
if (seekForwardIncrementMs != 0) {
|
||||
bundle.putLong(FIELD_SEEK_FORWARD_INCREMENT_MS, seekForwardIncrementMs);
|
||||
}
|
||||
if (maxSeekToPreviousPositionMs != 0) {
|
||||
bundle.putLong(FIELD_MAX_SEEK_TO_PREVIOUS_POSITION_MS, maxSeekToPreviousPositionMs);
|
||||
}
|
||||
if (!currentTracks.equals(Tracks.EMPTY)) {
|
||||
bundle.putBundle(FIELD_CURRENT_TRACKS, currentTracks.toBundle());
|
||||
}
|
||||
if (!trackSelectionParameters.equals(TrackSelectionParameters.DEFAULT_WITHOUT_CONTEXT)) {
|
||||
bundle.putBundle(FIELD_TRACK_SELECTION_PARAMETERS, trackSelectionParameters.toBundle());
|
||||
}
|
||||
return bundle;
|
||||
}
|
||||
|
||||
/** Object that can restore {@link PlayerInfo} from a {@link Bundle}. */
|
||||
public static final Creator<PlayerInfo> CREATOR = PlayerInfo::fromBundle;
|
||||
|
||||
private static PlayerInfo fromBundle(Bundle bundle) {
|
||||
@Nullable IBinder inProcessBinder = BundleUtil.getBinder(bundle, FIELD_IN_PROCESS_BINDER);
|
||||
if (inProcessBinder instanceof InProcessBinder) {
|
||||
return ((InProcessBinder) inProcessBinder).getPlayerInfo();
|
||||
}
|
||||
@Nullable Bundle playerErrorBundle = bundle.getBundle(FIELD_PLAYBACK_ERROR);
|
||||
@Nullable
|
||||
PlaybackException playerError =
|
||||
playerErrorBundle == null ? null : PlaybackException.CREATOR.fromBundle(playerErrorBundle);
|
||||
int mediaItemTransitionReason =
|
||||
bundle.getInt(FIELD_MEDIA_ITEM_TRANSITION_REASON, MEDIA_ITEM_TRANSITION_REASON_REPEAT);
|
||||
bundle.getInt(FIELD_MEDIA_ITEM_TRANSITION_REASON, MEDIA_ITEM_TRANSITION_REASON_DEFAULT);
|
||||
@Nullable Bundle sessionPositionInfoBundle = bundle.getBundle(FIELD_SESSION_POSITION_INFO);
|
||||
SessionPositionInfo sessionPositionInfo =
|
||||
sessionPositionInfoBundle == null
|
||||
@ -928,7 +1036,7 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
||||
? SessionPositionInfo.DEFAULT_POSITION_INFO
|
||||
: PositionInfo.CREATOR.fromBundle(newPositionInfoBundle);
|
||||
int discontinuityReason =
|
||||
bundle.getInt(FIELD_DISCONTINUITY_REASON, DISCONTINUITY_REASON_AUTO_TRANSITION);
|
||||
bundle.getInt(FIELD_DISCONTINUITY_REASON, DISCONTINUITY_REASON_DEFAULT);
|
||||
@Nullable Bundle playbackParametersBundle = bundle.getBundle(FIELD_PLAYBACK_PARAMETERS);
|
||||
PlaybackParameters playbackParameters =
|
||||
playbackParametersBundle == null
|
||||
@ -974,7 +1082,7 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
||||
int playWhenReadyChangeReason =
|
||||
bundle.getInt(
|
||||
FIELD_PLAY_WHEN_READY_CHANGE_REASON,
|
||||
/* defaultValue= */ PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST);
|
||||
/* defaultValue= */ PLAY_WHEN_READY_CHANGE_REASON_DEFAULT);
|
||||
@Player.PlaybackSuppressionReason
|
||||
int playbackSuppressionReason =
|
||||
bundle.getInt(
|
||||
@ -1036,4 +1144,10 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
||||
currentTracks,
|
||||
trackSelectionParameters);
|
||||
}
|
||||
|
||||
private final class InProcessBinder extends Binder {
|
||||
public PlayerInfo getPlayerInfo() {
|
||||
return PlayerInfo.this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,8 +19,10 @@ import static androidx.media3.common.util.Assertions.checkArgument;
|
||||
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.media3.common.Bundleable;
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.Player;
|
||||
import androidx.media3.common.Player.PositionInfo;
|
||||
import androidx.media3.common.util.Util;
|
||||
import com.google.common.base.Objects;
|
||||
@ -101,9 +103,9 @@ import com.google.common.base.Objects;
|
||||
return false;
|
||||
}
|
||||
SessionPositionInfo other = (SessionPositionInfo) obj;
|
||||
return positionInfo.equals(other.positionInfo)
|
||||
return eventTimeMs == other.eventTimeMs
|
||||
&& positionInfo.equals(other.positionInfo)
|
||||
&& isPlayingAd == other.isPlayingAd
|
||||
&& eventTimeMs == other.eventTimeMs
|
||||
&& durationMs == other.durationMs
|
||||
&& bufferedPositionMs == other.bufferedPositionMs
|
||||
&& bufferedPercentage == other.bufferedPercentage
|
||||
@ -157,41 +159,86 @@ import com.google.common.base.Objects;
|
||||
|
||||
// Bundleable implementation.
|
||||
|
||||
private static final String FIELD_POSITION_INFO = Util.intToStringMaxRadix(0);
|
||||
@VisibleForTesting static final String FIELD_POSITION_INFO = Util.intToStringMaxRadix(0);
|
||||
private static final String FIELD_IS_PLAYING_AD = Util.intToStringMaxRadix(1);
|
||||
private static final String FIELD_EVENT_TIME_MS = Util.intToStringMaxRadix(2);
|
||||
private static final String FIELD_DURATION_MS = Util.intToStringMaxRadix(3);
|
||||
private static final String FIELD_BUFFERED_POSITION_MS = Util.intToStringMaxRadix(4);
|
||||
@VisibleForTesting static final String FIELD_BUFFERED_POSITION_MS = Util.intToStringMaxRadix(4);
|
||||
private static final String FIELD_BUFFERED_PERCENTAGE = Util.intToStringMaxRadix(5);
|
||||
private static final String FIELD_TOTAL_BUFFERED_DURATION_MS = Util.intToStringMaxRadix(6);
|
||||
private static final String FIELD_CURRENT_LIVE_OFFSET_MS = Util.intToStringMaxRadix(7);
|
||||
private static final String FIELD_CONTENT_DURATION_MS = Util.intToStringMaxRadix(8);
|
||||
private static final String FIELD_CONTENT_BUFFERED_POSITION_MS = Util.intToStringMaxRadix(9);
|
||||
|
||||
@VisibleForTesting
|
||||
static final String FIELD_CONTENT_BUFFERED_POSITION_MS = Util.intToStringMaxRadix(9);
|
||||
|
||||
/**
|
||||
* Returns a copy of this session position info, filtered by the specified available commands.
|
||||
*
|
||||
* <p>The filtered fields are reset to their default values.
|
||||
*
|
||||
* <p>The return value may be the same object if nothing is filtered.
|
||||
*
|
||||
* @param canAccessCurrentMediaItem Whether {@link Player#COMMAND_GET_CURRENT_MEDIA_ITEM} is
|
||||
* available.
|
||||
* @param canAccessTimeline Whether {@link Player#COMMAND_GET_TIMELINE} is available.
|
||||
* @return The filtered session position info.
|
||||
*/
|
||||
public SessionPositionInfo filterByAvailableCommands(
|
||||
boolean canAccessCurrentMediaItem, boolean canAccessTimeline) {
|
||||
if (canAccessCurrentMediaItem && canAccessTimeline) {
|
||||
return this;
|
||||
}
|
||||
return new SessionPositionInfo(
|
||||
positionInfo.filterByAvailableCommands(canAccessCurrentMediaItem, canAccessTimeline),
|
||||
canAccessCurrentMediaItem && isPlayingAd,
|
||||
eventTimeMs,
|
||||
canAccessCurrentMediaItem ? durationMs : C.TIME_UNSET,
|
||||
canAccessCurrentMediaItem ? bufferedPositionMs : 0,
|
||||
canAccessCurrentMediaItem ? bufferedPercentage : 0,
|
||||
canAccessCurrentMediaItem ? totalBufferedDurationMs : 0,
|
||||
canAccessCurrentMediaItem ? currentLiveOffsetMs : C.TIME_UNSET,
|
||||
canAccessCurrentMediaItem ? contentDurationMs : C.TIME_UNSET,
|
||||
canAccessCurrentMediaItem ? contentBufferedPositionMs : 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle toBundle() {
|
||||
return toBundle(/* canAccessCurrentMediaItem= */ true, /* canAccessTimeline= */ true);
|
||||
return toBundle(Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
public Bundle toBundle(boolean canAccessCurrentMediaItem, boolean canAccessTimeline) {
|
||||
public Bundle toBundle(int controllerInterfaceVersion) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putBundle(
|
||||
FIELD_POSITION_INFO, positionInfo.toBundle(canAccessCurrentMediaItem, canAccessTimeline));
|
||||
bundle.putBoolean(FIELD_IS_PLAYING_AD, canAccessCurrentMediaItem && isPlayingAd);
|
||||
bundle.putLong(FIELD_EVENT_TIME_MS, eventTimeMs);
|
||||
bundle.putLong(FIELD_DURATION_MS, canAccessCurrentMediaItem ? durationMs : C.TIME_UNSET);
|
||||
bundle.putLong(FIELD_BUFFERED_POSITION_MS, canAccessCurrentMediaItem ? bufferedPositionMs : 0);
|
||||
bundle.putInt(FIELD_BUFFERED_PERCENTAGE, canAccessCurrentMediaItem ? bufferedPercentage : 0);
|
||||
bundle.putLong(
|
||||
FIELD_TOTAL_BUFFERED_DURATION_MS, canAccessCurrentMediaItem ? totalBufferedDurationMs : 0);
|
||||
bundle.putLong(
|
||||
FIELD_CURRENT_LIVE_OFFSET_MS,
|
||||
canAccessCurrentMediaItem ? currentLiveOffsetMs : C.TIME_UNSET);
|
||||
bundle.putLong(
|
||||
FIELD_CONTENT_DURATION_MS, canAccessCurrentMediaItem ? contentDurationMs : C.TIME_UNSET);
|
||||
bundle.putLong(
|
||||
FIELD_CONTENT_BUFFERED_POSITION_MS,
|
||||
canAccessCurrentMediaItem ? contentBufferedPositionMs : 0);
|
||||
if (controllerInterfaceVersion < 3 || !DEFAULT_POSITION_INFO.equalsForBundling(positionInfo)) {
|
||||
bundle.putBundle(FIELD_POSITION_INFO, positionInfo.toBundle(controllerInterfaceVersion));
|
||||
}
|
||||
if (isPlayingAd) {
|
||||
bundle.putBoolean(FIELD_IS_PLAYING_AD, isPlayingAd);
|
||||
}
|
||||
if (eventTimeMs != C.TIME_UNSET) {
|
||||
bundle.putLong(FIELD_EVENT_TIME_MS, eventTimeMs);
|
||||
}
|
||||
if (durationMs != C.TIME_UNSET) {
|
||||
bundle.putLong(FIELD_DURATION_MS, durationMs);
|
||||
}
|
||||
if (controllerInterfaceVersion < 3 || bufferedPositionMs != 0) {
|
||||
bundle.putLong(FIELD_BUFFERED_POSITION_MS, bufferedPositionMs);
|
||||
}
|
||||
if (bufferedPercentage != 0) {
|
||||
bundle.putInt(FIELD_BUFFERED_PERCENTAGE, bufferedPercentage);
|
||||
}
|
||||
if (totalBufferedDurationMs != 0) {
|
||||
bundle.putLong(FIELD_TOTAL_BUFFERED_DURATION_MS, totalBufferedDurationMs);
|
||||
}
|
||||
if (currentLiveOffsetMs != C.TIME_UNSET) {
|
||||
bundle.putLong(FIELD_CURRENT_LIVE_OFFSET_MS, currentLiveOffsetMs);
|
||||
}
|
||||
if (contentDurationMs != C.TIME_UNSET) {
|
||||
bundle.putLong(FIELD_CONTENT_DURATION_MS, contentDurationMs);
|
||||
}
|
||||
if (controllerInterfaceVersion < 3 || contentBufferedPositionMs != 0) {
|
||||
bundle.putLong(FIELD_CONTENT_BUFFERED_POSITION_MS, contentBufferedPositionMs);
|
||||
}
|
||||
return bundle;
|
||||
}
|
||||
|
||||
|
@ -71,7 +71,7 @@ public class PlayerInfoTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toBundleFromBundle_withAllCommands_restoresAllData() {
|
||||
public void toBundleFromBundle_restoresAllData() {
|
||||
PlayerInfo playerInfo =
|
||||
new PlayerInfo.Builder(PlayerInfo.DEFAULT)
|
||||
.setOldPositionInfo(
|
||||
@ -151,7 +151,7 @@ public class PlayerInfoTest {
|
||||
new PlaybackException(
|
||||
/* message= */ null, /* cause= */ null, PlaybackException.ERROR_CODE_TIMEOUT))
|
||||
.setPlayWhenReady(true)
|
||||
.setPlayWhenReadyChangeReason(Player.PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST)
|
||||
.setPlayWhenReadyChangeReason(Player.PLAY_WHEN_READY_CHANGE_REASON_AUDIO_FOCUS_LOSS)
|
||||
.setRepeatMode(Player.REPEAT_MODE_ONE)
|
||||
.setSeekBackIncrement(7000)
|
||||
.setSeekForwardIncrement(6000)
|
||||
@ -163,12 +163,7 @@ public class PlayerInfoTest {
|
||||
.setVideoSize(new VideoSize(/* width= */ 1024, /* height= */ 768))
|
||||
.build();
|
||||
|
||||
PlayerInfo infoAfterBundling =
|
||||
PlayerInfo.CREATOR.fromBundle(
|
||||
playerInfo.toBundle(
|
||||
new Player.Commands.Builder().addAllCommands().build(),
|
||||
/* excludeTimeline= */ false,
|
||||
/* excludeTracks= */ false));
|
||||
PlayerInfo infoAfterBundling = PlayerInfo.CREATOR.fromBundle(playerInfo.toBundle());
|
||||
|
||||
assertThat(infoAfterBundling.oldPositionInfo.mediaItemIndex).isEqualTo(5);
|
||||
assertThat(infoAfterBundling.oldPositionInfo.periodIndex).isEqualTo(4);
|
||||
@ -205,8 +200,8 @@ public class PlayerInfoTest {
|
||||
assertThat(infoAfterBundling.timeline.getWindowCount()).isEqualTo(10);
|
||||
assertThat(infoAfterBundling.timelineChangeReason)
|
||||
.isEqualTo(Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE);
|
||||
assertThat(infoAfterBundling.mediaMetadata.title).isEqualTo("title");
|
||||
assertThat(infoAfterBundling.playlistMetadata.artist).isEqualTo("artist");
|
||||
assertThat(infoAfterBundling.mediaMetadata.title.toString()).isEqualTo("title");
|
||||
assertThat(infoAfterBundling.playlistMetadata.artist.toString()).isEqualTo("artist");
|
||||
assertThat(infoAfterBundling.volume).isEqualTo(0.5f);
|
||||
assertThat(infoAfterBundling.deviceVolume).isEqualTo(10);
|
||||
assertThat(infoAfterBundling.deviceMuted).isTrue();
|
||||
@ -229,7 +224,7 @@ public class PlayerInfoTest {
|
||||
.isEqualTo(PlaybackException.ERROR_CODE_TIMEOUT);
|
||||
assertThat(infoAfterBundling.playWhenReady).isTrue();
|
||||
assertThat(infoAfterBundling.playWhenReadyChangeReason)
|
||||
.isEqualTo(Player.PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST);
|
||||
.isEqualTo(Player.PLAY_WHEN_READY_CHANGE_REASON_AUDIO_FOCUS_LOSS);
|
||||
assertThat(infoAfterBundling.repeatMode).isEqualTo(Player.REPEAT_MODE_ONE);
|
||||
assertThat(infoAfterBundling.seekBackIncrementMs).isEqualTo(7000);
|
||||
assertThat(infoAfterBundling.seekForwardIncrementMs).isEqualTo(6000);
|
||||
@ -289,13 +284,15 @@ public class PlayerInfoTest {
|
||||
|
||||
PlayerInfo infoAfterBundling =
|
||||
PlayerInfo.CREATOR.fromBundle(
|
||||
playerInfo.toBundle(
|
||||
new Player.Commands.Builder()
|
||||
.addAllCommands()
|
||||
.remove(Player.COMMAND_GET_CURRENT_MEDIA_ITEM)
|
||||
.build(),
|
||||
/* excludeTimeline= */ false,
|
||||
/* excludeTracks= */ false));
|
||||
playerInfo
|
||||
.filterByAvailableCommands(
|
||||
new Player.Commands.Builder()
|
||||
.addAllCommands()
|
||||
.remove(Player.COMMAND_GET_CURRENT_MEDIA_ITEM)
|
||||
.build(),
|
||||
/* excludeTimeline= */ false,
|
||||
/* excludeTracks= */ false)
|
||||
.toBundle());
|
||||
|
||||
assertThat(infoAfterBundling.oldPositionInfo.mediaItemIndex).isEqualTo(5);
|
||||
assertThat(infoAfterBundling.oldPositionInfo.periodIndex).isEqualTo(4);
|
||||
@ -408,13 +405,15 @@ public class PlayerInfoTest {
|
||||
|
||||
PlayerInfo infoAfterBundling =
|
||||
PlayerInfo.CREATOR.fromBundle(
|
||||
playerInfo.toBundle(
|
||||
new Player.Commands.Builder()
|
||||
.addAllCommands()
|
||||
.remove(Player.COMMAND_GET_TIMELINE)
|
||||
.build(),
|
||||
/* excludeTimeline= */ true,
|
||||
/* excludeTracks= */ false));
|
||||
playerInfo
|
||||
.filterByAvailableCommands(
|
||||
new Player.Commands.Builder()
|
||||
.addAllCommands()
|
||||
.remove(Player.COMMAND_GET_TIMELINE)
|
||||
.build(),
|
||||
/* excludeTimeline= */ true,
|
||||
/* excludeTracks= */ false)
|
||||
.toBundle());
|
||||
|
||||
assertThat(infoAfterBundling.oldPositionInfo.mediaItemIndex).isEqualTo(0);
|
||||
assertThat(infoAfterBundling.oldPositionInfo.periodIndex).isEqualTo(0);
|
||||
@ -475,13 +474,15 @@ public class PlayerInfoTest {
|
||||
|
||||
PlayerInfo infoAfterBundling =
|
||||
PlayerInfo.CREATOR.fromBundle(
|
||||
playerInfo.toBundle(
|
||||
new Player.Commands.Builder()
|
||||
.addAllCommands()
|
||||
.remove(Player.COMMAND_GET_METADATA)
|
||||
.build(),
|
||||
/* excludeTimeline= */ false,
|
||||
/* excludeTracks= */ false));
|
||||
playerInfo
|
||||
.filterByAvailableCommands(
|
||||
new Player.Commands.Builder()
|
||||
.addAllCommands()
|
||||
.remove(Player.COMMAND_GET_METADATA)
|
||||
.build(),
|
||||
/* excludeTimeline= */ false,
|
||||
/* excludeTracks= */ false)
|
||||
.toBundle());
|
||||
|
||||
assertThat(infoAfterBundling.mediaMetadata).isEqualTo(MediaMetadata.EMPTY);
|
||||
assertThat(infoAfterBundling.playlistMetadata).isEqualTo(MediaMetadata.EMPTY);
|
||||
@ -493,13 +494,15 @@ public class PlayerInfoTest {
|
||||
|
||||
PlayerInfo infoAfterBundling =
|
||||
PlayerInfo.CREATOR.fromBundle(
|
||||
playerInfo.toBundle(
|
||||
new Player.Commands.Builder()
|
||||
.addAllCommands()
|
||||
.remove(Player.COMMAND_GET_VOLUME)
|
||||
.build(),
|
||||
/* excludeTimeline= */ false,
|
||||
/* excludeTracks= */ false));
|
||||
playerInfo
|
||||
.filterByAvailableCommands(
|
||||
new Player.Commands.Builder()
|
||||
.addAllCommands()
|
||||
.remove(Player.COMMAND_GET_VOLUME)
|
||||
.build(),
|
||||
/* excludeTimeline= */ false,
|
||||
/* excludeTracks= */ false)
|
||||
.toBundle());
|
||||
|
||||
assertThat(infoAfterBundling.volume).isEqualTo(1f);
|
||||
}
|
||||
@ -511,13 +514,15 @@ public class PlayerInfoTest {
|
||||
|
||||
PlayerInfo infoAfterBundling =
|
||||
PlayerInfo.CREATOR.fromBundle(
|
||||
playerInfo.toBundle(
|
||||
new Player.Commands.Builder()
|
||||
.addAllCommands()
|
||||
.remove(Player.COMMAND_GET_DEVICE_VOLUME)
|
||||
.build(),
|
||||
/* excludeTimeline= */ false,
|
||||
/* excludeTracks= */ false));
|
||||
playerInfo
|
||||
.filterByAvailableCommands(
|
||||
new Player.Commands.Builder()
|
||||
.addAllCommands()
|
||||
.remove(Player.COMMAND_GET_DEVICE_VOLUME)
|
||||
.build(),
|
||||
/* excludeTimeline= */ false,
|
||||
/* excludeTracks= */ false)
|
||||
.toBundle());
|
||||
|
||||
assertThat(infoAfterBundling.deviceVolume).isEqualTo(0);
|
||||
assertThat(infoAfterBundling.deviceMuted).isFalse();
|
||||
@ -533,13 +538,15 @@ public class PlayerInfoTest {
|
||||
|
||||
PlayerInfo infoAfterBundling =
|
||||
PlayerInfo.CREATOR.fromBundle(
|
||||
playerInfo.toBundle(
|
||||
new Player.Commands.Builder()
|
||||
.addAllCommands()
|
||||
.remove(Player.COMMAND_GET_AUDIO_ATTRIBUTES)
|
||||
.build(),
|
||||
/* excludeTimeline= */ false,
|
||||
/* excludeTracks= */ false));
|
||||
playerInfo
|
||||
.filterByAvailableCommands(
|
||||
new Player.Commands.Builder()
|
||||
.addAllCommands()
|
||||
.remove(Player.COMMAND_GET_AUDIO_ATTRIBUTES)
|
||||
.build(),
|
||||
/* excludeTimeline= */ false,
|
||||
/* excludeTracks= */ false)
|
||||
.toBundle());
|
||||
|
||||
assertThat(infoAfterBundling.audioAttributes).isEqualTo(AudioAttributes.DEFAULT);
|
||||
}
|
||||
@ -553,13 +560,15 @@ public class PlayerInfoTest {
|
||||
|
||||
PlayerInfo infoAfterBundling =
|
||||
PlayerInfo.CREATOR.fromBundle(
|
||||
playerInfo.toBundle(
|
||||
new Player.Commands.Builder()
|
||||
.addAllCommands()
|
||||
.remove(Player.COMMAND_GET_TEXT)
|
||||
.build(),
|
||||
/* excludeTimeline= */ false,
|
||||
/* excludeTracks= */ false));
|
||||
playerInfo
|
||||
.filterByAvailableCommands(
|
||||
new Player.Commands.Builder()
|
||||
.addAllCommands()
|
||||
.remove(Player.COMMAND_GET_TEXT)
|
||||
.build(),
|
||||
/* excludeTimeline= */ false,
|
||||
/* excludeTracks= */ false)
|
||||
.toBundle());
|
||||
|
||||
assertThat(infoAfterBundling.cueGroup).isEqualTo(CueGroup.EMPTY_TIME_ZERO);
|
||||
}
|
||||
@ -581,14 +590,84 @@ public class PlayerInfoTest {
|
||||
|
||||
PlayerInfo infoAfterBundling =
|
||||
PlayerInfo.CREATOR.fromBundle(
|
||||
playerInfo.toBundle(
|
||||
new Player.Commands.Builder()
|
||||
.addAllCommands()
|
||||
.remove(Player.COMMAND_GET_TRACKS)
|
||||
.build(),
|
||||
/* excludeTimeline= */ false,
|
||||
/* excludeTracks= */ true));
|
||||
playerInfo
|
||||
.filterByAvailableCommands(
|
||||
new Player.Commands.Builder()
|
||||
.addAllCommands()
|
||||
.remove(Player.COMMAND_GET_TRACKS)
|
||||
.build(),
|
||||
/* excludeTimeline= */ false,
|
||||
/* excludeTracks= */ true)
|
||||
.toBundle());
|
||||
|
||||
assertThat(infoAfterBundling.currentTracks).isEqualTo(Tracks.EMPTY);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toBundleFromBundle_withDefaultValues_restoresAllData() {
|
||||
PlayerInfo roundTripValue = PlayerInfo.CREATOR.fromBundle(PlayerInfo.DEFAULT.toBundle());
|
||||
|
||||
assertThat(roundTripValue.oldPositionInfo).isEqualTo(PlayerInfo.DEFAULT.oldPositionInfo);
|
||||
assertThat(roundTripValue.newPositionInfo).isEqualTo(PlayerInfo.DEFAULT.newPositionInfo);
|
||||
assertThat(roundTripValue.sessionPositionInfo)
|
||||
.isEqualTo(PlayerInfo.DEFAULT.sessionPositionInfo);
|
||||
assertThat(roundTripValue.timeline).isEqualTo(PlayerInfo.DEFAULT.timeline);
|
||||
assertThat(roundTripValue.timelineChangeReason)
|
||||
.isEqualTo(PlayerInfo.DEFAULT.timelineChangeReason);
|
||||
assertThat(roundTripValue.mediaMetadata).isEqualTo(PlayerInfo.DEFAULT.mediaMetadata);
|
||||
assertThat(roundTripValue.playlistMetadata).isEqualTo(PlayerInfo.DEFAULT.playlistMetadata);
|
||||
assertThat(roundTripValue.volume).isEqualTo(PlayerInfo.DEFAULT.volume);
|
||||
assertThat(roundTripValue.deviceVolume).isEqualTo(PlayerInfo.DEFAULT.deviceVolume);
|
||||
assertThat(roundTripValue.deviceMuted).isEqualTo(PlayerInfo.DEFAULT.deviceMuted);
|
||||
assertThat(roundTripValue.audioAttributes).isEqualTo(PlayerInfo.DEFAULT.audioAttributes);
|
||||
assertThat(roundTripValue.cueGroup).isEqualTo(PlayerInfo.DEFAULT.cueGroup);
|
||||
assertThat(roundTripValue.currentTracks).isEqualTo(PlayerInfo.DEFAULT.currentTracks);
|
||||
assertThat(roundTripValue.deviceInfo).isEqualTo(PlayerInfo.DEFAULT.deviceInfo);
|
||||
assertThat(roundTripValue.discontinuityReason)
|
||||
.isEqualTo(PlayerInfo.DEFAULT.discontinuityReason);
|
||||
assertThat(roundTripValue.isLoading).isEqualTo(PlayerInfo.DEFAULT.isLoading);
|
||||
assertThat(roundTripValue.isPlaying).isEqualTo(PlayerInfo.DEFAULT.isPlaying);
|
||||
assertThat(roundTripValue.maxSeekToPreviousPositionMs)
|
||||
.isEqualTo(PlayerInfo.DEFAULT.maxSeekToPreviousPositionMs);
|
||||
assertThat(roundTripValue.mediaItemTransitionReason)
|
||||
.isEqualTo(PlayerInfo.DEFAULT.mediaItemTransitionReason);
|
||||
assertThat(roundTripValue.playbackParameters).isEqualTo(PlayerInfo.DEFAULT.playbackParameters);
|
||||
assertThat(roundTripValue.playbackState).isEqualTo(PlayerInfo.DEFAULT.playbackState);
|
||||
assertThat(roundTripValue.playbackSuppressionReason)
|
||||
.isEqualTo(PlayerInfo.DEFAULT.playbackSuppressionReason);
|
||||
assertThat(roundTripValue.playerError).isEqualTo(PlayerInfo.DEFAULT.playerError);
|
||||
assertThat(roundTripValue.playWhenReady).isEqualTo(PlayerInfo.DEFAULT.playWhenReady);
|
||||
assertThat(roundTripValue.playWhenReadyChangeReason)
|
||||
.isEqualTo(PlayerInfo.DEFAULT.playWhenReadyChangeReason);
|
||||
assertThat(roundTripValue.repeatMode).isEqualTo(PlayerInfo.DEFAULT.repeatMode);
|
||||
assertThat(roundTripValue.seekBackIncrementMs)
|
||||
.isEqualTo(PlayerInfo.DEFAULT.seekBackIncrementMs);
|
||||
assertThat(roundTripValue.seekForwardIncrementMs)
|
||||
.isEqualTo(PlayerInfo.DEFAULT.seekForwardIncrementMs);
|
||||
assertThat(roundTripValue.shuffleModeEnabled).isEqualTo(PlayerInfo.DEFAULT.shuffleModeEnabled);
|
||||
assertThat(roundTripValue.trackSelectionParameters)
|
||||
.isEqualTo(PlayerInfo.DEFAULT.trackSelectionParameters);
|
||||
assertThat(roundTripValue.videoSize).isEqualTo(PlayerInfo.DEFAULT.videoSize);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toBundle_withDefaultValues_omitsAllData() {
|
||||
Bundle bundle =
|
||||
PlayerInfo.DEFAULT.toBundle(/* controllerInterfaceVersion= */ Integer.MAX_VALUE);
|
||||
|
||||
assertThat(bundle.isEmpty()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toBundle_withDefaultValuesForControllerInterfaceBefore3_includesPositionInfos() {
|
||||
// Controller before version 3 uses invalid default values for indices in (Session)PositionInfo.
|
||||
// The Bundle should always include these fields to avoid using the invalid defaults.
|
||||
Bundle bundle = PlayerInfo.DEFAULT.toBundle(/* controllerInterfaceVersion= */ 2);
|
||||
|
||||
assertThat(bundle.keySet())
|
||||
.containsAtLeast(
|
||||
PlayerInfo.FIELD_SESSION_POSITION_INFO,
|
||||
PlayerInfo.FIELD_NEW_POSITION_INFO,
|
||||
PlayerInfo.FIELD_OLD_POSITION_INFO);
|
||||
}
|
||||
}
|
||||
|
@ -85,4 +85,35 @@ public class SessionPositionInfoTest {
|
||||
/* contentDurationMs= */ 400L,
|
||||
/* contentBufferedPositionMs= */ 223L));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void roundTripViaBundle_withDefaultValues_yieldsEqualInstance() {
|
||||
SessionPositionInfo roundTripValue =
|
||||
SessionPositionInfo.CREATOR.fromBundle(SessionPositionInfo.DEFAULT.toBundle());
|
||||
|
||||
assertThat(roundTripValue).isEqualTo(SessionPositionInfo.DEFAULT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toBundle_withDefaultValues_omitsAllData() {
|
||||
Bundle bundle =
|
||||
SessionPositionInfo.DEFAULT.toBundle(/* controllerInterfaceVersion= */ Integer.MAX_VALUE);
|
||||
|
||||
assertThat(bundle.isEmpty()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
toBundle_withDefaultValuesForControllerInterfaceBefore3_includesPositionInfoAndBufferedValues() {
|
||||
// Controller before version 3 uses invalid default values for indices in PositionInfo and for
|
||||
// the buffered positions. The Bundle should always include these fields to avoid using the
|
||||
// invalid defaults.
|
||||
Bundle bundle = SessionPositionInfo.DEFAULT.toBundle(/* controllerInterfaceVersion= */ 2);
|
||||
|
||||
assertThat(bundle.keySet())
|
||||
.containsAtLeast(
|
||||
SessionPositionInfo.FIELD_BUFFERED_POSITION_MS,
|
||||
SessionPositionInfo.FIELD_CONTENT_BUFFERED_POSITION_MS,
|
||||
SessionPositionInfo.FIELD_POSITION_INFO);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user