From 4ebe630a80296cbb4437336a50abccb39da978f7 Mon Sep 17 00:00:00 2001 From: tonihei Date: Wed, 11 Oct 2023 09:06:31 -0700 Subject: [PATCH] Split available command filtering and bundling A few methods in PlayerInfo and related classes combine filtering information with bundling in one method. This makes it impossible to use just the filtering for example and it's also easier to reason about than two dedicated methods. This change splits these methods into two parts accordingly. PiperOrigin-RevId: 572592458 --- .../java/androidx/media3/common/Player.java | 96 ++++++--- .../java/androidx/media3/common/Timeline.java | 39 ++-- .../media3/session/ConnectionState.java | 6 +- .../media3/session/MediaSessionStub.java | 15 +- .../androidx/media3/session/PlayerInfo.java | 199 ++++++++++++------ .../media3/session/SessionPositionInfo.java | 86 ++++++-- .../media3/session/PlayerInfoTest.java | 141 +++++++------ 7 files changed, 374 insertions(+), 208 deletions(-) diff --git a/libraries/common/src/main/java/androidx/media3/common/Player.java b/libraries/common/src/main/java/androidx/media3/common/Player.java index 3a631280ca..6340f5b461 100644 --- a/libraries/common/src/main/java/androidx/media3/common/Player.java +++ b/libraries/common/src/main/java/androidx/media3/common/Player.java @@ -350,15 +350,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,6 +369,21 @@ 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); @@ -385,6 +394,36 @@ public interface Player { 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. + * + *

The filtered fields are reset to their default values. + * + *

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} * @@ -394,31 +433,28 @@ 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) { + if (mediaItemIndex != 0) { + bundle.putInt(FIELD_MEDIA_ITEM_INDEX, mediaItemIndex); + } + if (mediaItem != null) { 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); + if (periodIndex != 0) { + bundle.putInt(FIELD_PERIOD_INDEX, periodIndex); + } + if (positionMs != 0) { + bundle.putLong(FIELD_POSITION_MS, positionMs); + } + if (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; } diff --git a/libraries/common/src/main/java/androidx/media3/common/Timeline.java b/libraries/common/src/main/java/androidx/media3/common/Timeline.java index d92f586ccc..4b338ee44b 100644 --- a/libraries/common/src/main/java/androidx/media3/common/Timeline.java +++ b/libraries/common/src/main/java/androidx/media3/common/Timeline.java @@ -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}. * - *

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()}. + *

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 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 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}); } /** 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 ec631513fb..6205047a4d 100644 --- a/libraries/session/src/main/java/androidx/media3/session/ConnectionState.java +++ b/libraries/session/src/main/java/androidx/media3/session/ConnectionState.java @@ -111,8 +111,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()); bundle.putInt(FIELD_SESSION_INTERFACE_VERSION, sessionInterfaceVersion); return bundle; } 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 6b900a99c6..19c02cd068 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaSessionStub.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaSessionStub.java @@ -1913,17 +1913,20 @@ import java.util.concurrent.ExecutionException; boolean bundlingExclusionsTracks = excludeTracks || !availableCommands.contains(Player.COMMAND_GET_TRACKS); if (controllerInterfaceVersion >= 2) { + PlayerInfo filteredPlayerInfo = + playerInfo.filterByAvailableCommands(availableCommands, excludeTimeline, excludeTracks); iController.onPlayerInfoChangedWithExclusions( sequenceNumber, - playerInfo.toBundle(availableCommands, excludeTimeline, excludeTracks), + filteredPlayerInfo.toBundle(), 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), - bundlingExclusionsTimeline); + sequenceNumber, filteredPlayerInfo.toBundle(), bundlingExclusionsTimeline); } } @@ -1992,7 +1995,9 @@ import java.util.concurrent.ExecutionException; throws RemoteException { iController.onPeriodicSessionPositionInfoChanged( sequenceNumber, - sessionPositionInfo.toBundle(canAccessCurrentMediaItem, canAccessTimeline)); + sessionPositionInfo + .filterByAvailableCommands(canAccessCurrentMediaItem, canAccessTimeline) + .toBundle()); } @Override 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 00b02e3e08..fb5aebe91c 100644 --- a/libraries/session/src/main/java/androidx/media3/session/PlayerInfo.java +++ b/libraries/session/src/main/java/androidx/media3/session/PlayerInfo.java @@ -827,79 +827,158 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; // Next field key = 32 - public Bundle toBundle( + /** + * Returns a copy of this player info, filtered by the specified available commands. + * + *

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 bundle; + return builder.build(); } @Override public Bundle toBundle() { - return toBundle( - /* availableCommands= */ new Player.Commands.Builder().addAllCommands().build(), - /* excludeTimeline= */ false, - /* excludeTracks= */ false); + 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 (!sessionPositionInfo.equals(SessionPositionInfo.DEFAULT)) { + bundle.putBundle(FIELD_SESSION_POSITION_INFO, sessionPositionInfo.toBundle()); + } + if (!SessionPositionInfo.DEFAULT_POSITION_INFO.equalsForBundling(oldPositionInfo)) { + bundle.putBundle(FIELD_OLD_POSITION_INFO, oldPositionInfo.toBundle()); + } + if (!SessionPositionInfo.DEFAULT_POSITION_INFO.equalsForBundling(newPositionInfo)) { + bundle.putBundle(FIELD_NEW_POSITION_INFO, newPositionInfo.toBundle()); + } + 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}. */ @@ -911,7 +990,7 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; 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 +1007,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 +1053,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( diff --git a/libraries/session/src/main/java/androidx/media3/session/SessionPositionInfo.java b/libraries/session/src/main/java/androidx/media3/session/SessionPositionInfo.java index a4d537f0cc..fa811a6a69 100644 --- a/libraries/session/src/main/java/androidx/media3/session/SessionPositionInfo.java +++ b/libraries/session/src/main/java/androidx/media3/session/SessionPositionInfo.java @@ -21,6 +21,7 @@ import android.os.Bundle; import androidx.annotation.Nullable; 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 +102,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 @@ -168,30 +169,69 @@ import com.google.common.base.Objects; private static final String FIELD_CONTENT_DURATION_MS = Util.intToStringMaxRadix(8); private static final String FIELD_CONTENT_BUFFERED_POSITION_MS = Util.intToStringMaxRadix(9); - @Override - public Bundle toBundle() { - return toBundle(/* canAccessCurrentMediaItem= */ true, /* canAccessTimeline= */ true); + /** + * Returns a copy of this session position info, filtered by the specified available commands. + * + *

The filtered fields are reset to their default values. + * + *

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); } - public Bundle toBundle(boolean canAccessCurrentMediaItem, boolean canAccessTimeline) { + @Override + public Bundle toBundle() { 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 (!DEFAULT_POSITION_INFO.equalsForBundling(positionInfo)) { + bundle.putBundle(FIELD_POSITION_INFO, positionInfo.toBundle()); + } + 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 (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 (contentBufferedPositionMs != 0) { + bundle.putLong(FIELD_CONTENT_BUFFERED_POSITION_MS, contentBufferedPositionMs); + } return bundle; } 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 c3e6736ed3..63f672aa85 100644 --- a/libraries/session/src/test/java/androidx/media3/session/PlayerInfoTest.java +++ b/libraries/session/src/test/java/androidx/media3/session/PlayerInfoTest.java @@ -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); @@ -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,13 +590,15 @@ 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); }