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); }