diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java b/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java index 40d370d6a7..6e0f0ce326 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java @@ -428,12 +428,10 @@ import org.checkerframework.checker.nullness.qual.NonNull; updatePlayerInfo( newPlayerInfo, - /* ignored */ Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, - /* ignored */ Player.PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST, - /* positionDiscontinuity= */ false, - /* ignored */ Player.DISCONTINUITY_REASON_INTERNAL, - /* mediaItemTransition= */ false, - /* ignored */ Player.MEDIA_ITEM_TRANSITION_REASON_REPEAT); + /* timelineChangeReason= */ null, + /* playWhenReadyChangeReason= */ null, + /* positionDiscontinuityReason= */ null, + /* mediaItemTransitionReason= */ null); } } @@ -949,14 +947,16 @@ import org.checkerframework.checker.nullness.qual.NonNull; // Add media items to the end of the timeline if the index exceeds the window count. index = min(index, playerInfo.timeline.getWindowCount()); PlayerInfo newPlayerInfo = maskPlaybackInfoForAddedItems(playerInfo, index, mediaItems); + @Nullable + @Player.MediaItemTransitionReason + Integer mediaItemTransitionReason = + playerInfo.timeline.isEmpty() ? Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED : null; updatePlayerInfo( newPlayerInfo, /* timelineChangeReason= */ Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, - /* ignored */ Player.PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST, - /* positionDiscontinuity= */ false, - /* ignored */ Player.DISCONTINUITY_REASON_INTERNAL, - /* mediaItemTransition= */ playerInfo.timeline.isEmpty(), - Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED); + /* playWhenReadyChangeReason= */ null, + /* positionDiscontinuityReason= */ null, + /* mediaItemTransitionReason= */ mediaItemTransitionReason); } private static PlayerInfo maskPlaybackInfoForAddedItems( @@ -1040,19 +1040,22 @@ import org.checkerframework.checker.nullness.qual.NonNull; if (fromIndex >= playlistSize || fromIndex == toIndex || playlistSize == 0) { return; } - boolean currentItemRemoved = + boolean wasCurrentItemRemoved = getCurrentMediaItemIndex() >= fromIndex && getCurrentMediaItemIndex() < toIndex; PlayerInfo newPlayerInfo = maskPlayerInfoForRemovedItems(playerInfo, fromIndex, toIndex); + boolean didMediaItemTransitionHappen = + playerInfo.sessionPositionInfo.positionInfo.mediaItemIndex >= fromIndex + && playerInfo.sessionPositionInfo.positionInfo.mediaItemIndex < toIndex; updatePlayerInfo( newPlayerInfo, /* timelineChangeReason= */ Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, - /* ignored */ Player.PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST, - /* positionDiscontinuity= */ currentItemRemoved, - Player.DISCONTINUITY_REASON_REMOVE, - /* mediaItemTransition= */ playerInfo.sessionPositionInfo.positionInfo.mediaItemIndex - >= fromIndex - && playerInfo.sessionPositionInfo.positionInfo.mediaItemIndex < toIndex, - Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED); + /* playWhenReadyChangeReason= */ null, + /* positionDiscontinuityReason= */ wasCurrentItemRemoved + ? Player.DISCONTINUITY_REASON_REMOVE + : null, + /* mediaItemTransitionReason= */ didMediaItemTransitionHappen + ? Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED + : null); } private static PlayerInfo maskPlayerInfoForRemovedItems( @@ -1265,17 +1268,19 @@ import org.checkerframework.checker.nullness.qual.NonNull; toIndex = min(toIndex, playlistSize); PlayerInfo newPlayerInfo = maskPlaybackInfoForAddedItems(playerInfo, toIndex, mediaItems); newPlayerInfo = maskPlayerInfoForRemovedItems(newPlayerInfo, fromIndex, toIndex); - boolean replacedCurrentItem = + boolean wasCurrentItemReplaced = playerInfo.sessionPositionInfo.positionInfo.mediaItemIndex >= fromIndex && playerInfo.sessionPositionInfo.positionInfo.mediaItemIndex < toIndex; updatePlayerInfo( newPlayerInfo, /* timelineChangeReason= */ Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, - /* ignored */ Player.PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST, - /* positionDiscontinuity= */ replacedCurrentItem, - Player.DISCONTINUITY_REASON_REMOVE, - /* mediaItemTransition= */ replacedCurrentItem, - Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED); + /* playWhenReadyChangeReason= */ null, + /* positionDiscontinuityReason= */ wasCurrentItemReplaced + ? Player.DISCONTINUITY_REASON_REMOVE + : null, + /* mediaItemTransitionReason= */ wasCurrentItemReplaced + ? Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED + : null); } @Override @@ -1996,12 +2001,14 @@ import org.checkerframework.checker.nullness.qual.NonNull; updatePlayerInfo( newPlayerInfo, /* timelineChangeReason= */ Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, - /* ignored */ Player.PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST, - /* positionDiscontinuity= */ !playerInfo.timeline.isEmpty(), - Player.DISCONTINUITY_REASON_REMOVE, - /* mediaItemTransition= */ !playerInfo.timeline.isEmpty() - || !newPlayerInfo.timeline.isEmpty(), - Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED); + /* playWhenReadyChangeReason= */ null, + /* positionDiscontinuityReason= */ !playerInfo.timeline.isEmpty() + ? Player.DISCONTINUITY_REASON_REMOVE + : null, + /* mediaItemTransitionReason= */ !playerInfo.timeline.isEmpty() + || !newPlayerInfo.timeline.isEmpty() + ? Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED + : null); } private void moveMediaItemsInternal(int fromIndex, int toIndex, int newIndex) { @@ -2056,11 +2063,9 @@ import org.checkerframework.checker.nullness.qual.NonNull; updatePlayerInfo( newPlayerInfo, /* timelineChangeReason= */ Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, - /* ignored */ Player.PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST, - /* positionDiscontinuity= */ false, - /* ignored */ Player.DISCONTINUITY_REASON_INTERNAL, - /* mediaItemTransition= */ false, - /* ignored */ Player.MEDIA_ITEM_TRANSITION_REASON_REPEAT); + /* playWhenReadyChangeReason= */ null, + /* positionDiscontinuityReason= */ null, + /* mediaItemTransitionReason= */ null); } } @@ -2132,12 +2137,12 @@ import org.checkerframework.checker.nullness.qual.NonNull; } updatePlayerInfo( newPlayerInfo, - /* ignored */ Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, - /* ignored */ Player.PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST, - positionDiscontinuity, + /* timelineChangeReason= */ null, + /* playWhenReadyChangeReason= */ null, /* positionDiscontinuityReason= */ Player.DISCONTINUITY_REASON_SEEK, - mediaItemTransition, - Player.MEDIA_ITEM_TRANSITION_REASON_SEEK); + /* mediaItemTransitionReason= */ mediaItemTransition + ? Player.MEDIA_ITEM_TRANSITION_REASON_SEEK + : null); } private void setPlayWhenReady( @@ -2157,33 +2162,47 @@ import org.checkerframework.checker.nullness.qual.NonNull; playWhenReady, playWhenReadyChangeReason, playbackSuppressionReason); updatePlayerInfo( newPlayerInfo, - /* ignored */ Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, + /* timelineChangeReason= */ null, playWhenReadyChangeReason, - /* positionDiscontinuity= */ false, - /* ignored */ Player.DISCONTINUITY_REASON_INTERNAL, - /* mediaItemTransition= */ false, - /* ignored */ Player.MEDIA_ITEM_TRANSITION_REASON_REPEAT); + /* positionDiscontinuityReason= */ null, + /* mediaItemTransitionReason= */ null); } private void updatePlayerInfo( PlayerInfo newPlayerInfo, - @Player.TimelineChangeReason int timelineChangeReason, - @Player.PlayWhenReadyChangeReason int playWhenReadyChangeReason, - boolean positionDiscontinuity, - @Player.DiscontinuityReason int positionDiscontinuityReason, - boolean mediaItemTransition, - @Player.MediaItemTransitionReason int mediaItemTransitionReason) { + @Nullable @Player.TimelineChangeReason Integer timelineChangeReason, + @Nullable @Player.PlayWhenReadyChangeReason Integer playWhenReadyChangeReason, + @Nullable @Player.DiscontinuityReason Integer positionDiscontinuityReason, + @Nullable @Player.MediaItemTransitionReason Integer mediaItemTransitionReason) { // Assign player info immediately such that all getters return the right values, but keep // snapshot of previous and new states so that listener invocations are triggered correctly. PlayerInfo oldPlayerInfo = this.playerInfo; this.playerInfo = newPlayerInfo; - if (!oldPlayerInfo.timeline.equals(newPlayerInfo.timeline)) { + notifyPlayerInfoListenersWithReasons( + oldPlayerInfo, + newPlayerInfo, + timelineChangeReason, + playWhenReadyChangeReason, + positionDiscontinuityReason, + mediaItemTransitionReason); + } + + @SuppressWarnings("deprecation") // Implementing and calling deprecated listener methods. + private void notifyPlayerInfoListenersWithReasons( + PlayerInfo oldPlayerInfo, + PlayerInfo newPlayerInfo, + @Nullable @Player.TimelineChangeReason Integer timelineChangeReason, + @Nullable @Player.PlayWhenReadyChangeReason Integer playWhenReadyChangeReason, + @Nullable @Player.DiscontinuityReason Integer positionDiscontinuityReason, + @Nullable @Player.MediaItemTransitionReason Integer mediaItemTransitionReason) { + + if (timelineChangeReason != null) { listeners.queueEvent( /* eventFlag= */ Player.EVENT_TIMELINE_CHANGED, listener -> listener.onTimelineChanged(newPlayerInfo.timeline, timelineChangeReason)); } - if (positionDiscontinuity) { + if (positionDiscontinuityReason != null) { listeners.queueEvent( /* eventFlag= */ Player.EVENT_POSITION_DISCONTINUITY, listener -> @@ -2192,12 +2211,11 @@ import org.checkerframework.checker.nullness.qual.NonNull; newPlayerInfo.newPositionInfo, positionDiscontinuityReason)); } - if (mediaItemTransition) { + MediaItem currentMediaItem = newPlayerInfo.getCurrentMediaItem(); + if (mediaItemTransitionReason != null) { listeners.queueEvent( /* eventFlag= */ Player.EVENT_MEDIA_ITEM_TRANSITION, - listener -> - listener.onMediaItemTransition( - newPlayerInfo.getCurrentMediaItem(), mediaItemTransitionReason)); + listener -> listener.onMediaItemTransition(currentMediaItem, mediaItemTransitionReason)); } PlaybackException oldPlayerError = oldPlayerInfo.playerError; PlaybackException newPlayerError = newPlayerInfo.playerError; @@ -2214,12 +2232,27 @@ import org.checkerframework.checker.nullness.qual.NonNull; listener -> listener.onPlayerError(newPlayerError)); } } + if (!oldPlayerInfo.currentTracks.equals(newPlayerInfo.currentTracks)) { + listeners.queueEvent( + /* eventFlag= */ Player.EVENT_TRACKS_CHANGED, + listener -> listener.onTracksChanged(newPlayerInfo.currentTracks)); + } + if (!oldPlayerInfo.mediaMetadata.equals(newPlayerInfo.mediaMetadata)) { + listeners.queueEvent( + /* eventFlag= */ Player.EVENT_MEDIA_METADATA_CHANGED, + listener -> listener.onMediaMetadataChanged(newPlayerInfo.mediaMetadata)); + } + if (oldPlayerInfo.isLoading != newPlayerInfo.isLoading) { + listeners.queueEvent( + /* eventFlag= */ Player.EVENT_IS_LOADING_CHANGED, + listener -> listener.onIsLoadingChanged(newPlayerInfo.isLoading)); + } if (oldPlayerInfo.playbackState != newPlayerInfo.playbackState) { listeners.queueEvent( /* eventFlag= */ Player.EVENT_PLAYBACK_STATE_CHANGED, listener -> listener.onPlaybackStateChanged(newPlayerInfo.playbackState)); } - if (oldPlayerInfo.playWhenReady != newPlayerInfo.playWhenReady) { + if (playWhenReadyChangeReason != null) { listeners.queueEvent( /* eventFlag= */ Player.EVENT_PLAY_WHEN_READY_CHANGED, listener -> @@ -2237,6 +2270,85 @@ import org.checkerframework.checker.nullness.qual.NonNull; /* eventFlag= */ Player.EVENT_IS_PLAYING_CHANGED, listener -> listener.onIsPlayingChanged(newPlayerInfo.isPlaying)); } + if (!oldPlayerInfo.playbackParameters.equals(newPlayerInfo.playbackParameters)) { + listeners.queueEvent( + /* eventFlag= */ Player.EVENT_PLAYBACK_PARAMETERS_CHANGED, + listener -> listener.onPlaybackParametersChanged(newPlayerInfo.playbackParameters)); + } + + if (oldPlayerInfo.repeatMode != newPlayerInfo.repeatMode) { + listeners.queueEvent( + /* eventFlag= */ Player.EVENT_REPEAT_MODE_CHANGED, + listener -> listener.onRepeatModeChanged(newPlayerInfo.repeatMode)); + } + if (oldPlayerInfo.shuffleModeEnabled != newPlayerInfo.shuffleModeEnabled) { + listeners.queueEvent( + /* eventFlag= */ Player.EVENT_SHUFFLE_MODE_ENABLED_CHANGED, + listener -> listener.onShuffleModeEnabledChanged(newPlayerInfo.shuffleModeEnabled)); + } + if (!oldPlayerInfo.playlistMetadata.equals(newPlayerInfo.playlistMetadata)) { + listeners.queueEvent( + /* eventFlag= */ Player.EVENT_PLAYLIST_METADATA_CHANGED, + listener -> listener.onPlaylistMetadataChanged(newPlayerInfo.playlistMetadata)); + } + if (oldPlayerInfo.volume != newPlayerInfo.volume) { + listeners.queueEvent( + /* eventFlag= */ Player.EVENT_VOLUME_CHANGED, + listener -> listener.onVolumeChanged(newPlayerInfo.volume)); + } + if (!oldPlayerInfo.audioAttributes.equals(newPlayerInfo.audioAttributes)) { + listeners.queueEvent( + /* eventFlag= */ Player.EVENT_AUDIO_ATTRIBUTES_CHANGED, + listener -> listener.onAudioAttributesChanged(newPlayerInfo.audioAttributes)); + } + if (!oldPlayerInfo.cueGroup.cues.equals(newPlayerInfo.cueGroup.cues)) { + listeners.queueEvent( + /* eventFlag= */ Player.EVENT_CUES, + listener -> listener.onCues(newPlayerInfo.cueGroup.cues)); + listeners.queueEvent( + /* eventFlag= */ Player.EVENT_CUES, listener -> listener.onCues(newPlayerInfo.cueGroup)); + } + if (!oldPlayerInfo.deviceInfo.equals(newPlayerInfo.deviceInfo)) { + listeners.queueEvent( + /* eventFlag= */ Player.EVENT_DEVICE_INFO_CHANGED, + listener -> listener.onDeviceInfoChanged(newPlayerInfo.deviceInfo)); + } + if (oldPlayerInfo.deviceVolume != newPlayerInfo.deviceVolume + || oldPlayerInfo.deviceMuted != newPlayerInfo.deviceMuted) { + listeners.queueEvent( + /* eventFlag= */ Player.EVENT_DEVICE_VOLUME_CHANGED, + listener -> + listener.onDeviceVolumeChanged( + newPlayerInfo.deviceVolume, newPlayerInfo.deviceMuted)); + } + if (!oldPlayerInfo.videoSize.equals(newPlayerInfo.videoSize)) { + listeners.queueEvent( + /* eventFlag= */ Player.EVENT_VIDEO_SIZE_CHANGED, + listener -> listener.onVideoSizeChanged(newPlayerInfo.videoSize)); + } + if (oldPlayerInfo.seekBackIncrementMs != newPlayerInfo.seekBackIncrementMs) { + listeners.queueEvent( + /* eventFlag= */ Player.EVENT_SEEK_BACK_INCREMENT_CHANGED, + listener -> listener.onSeekBackIncrementChanged(newPlayerInfo.seekBackIncrementMs)); + } + if (oldPlayerInfo.seekForwardIncrementMs != newPlayerInfo.seekForwardIncrementMs) { + listeners.queueEvent( + /* eventFlag= */ Player.EVENT_SEEK_FORWARD_INCREMENT_CHANGED, + listener -> listener.onSeekForwardIncrementChanged(newPlayerInfo.seekForwardIncrementMs)); + } + if (oldPlayerInfo.maxSeekToPreviousPositionMs != newPlayerInfo.maxSeekToPreviousPositionMs) { + listeners.queueEvent( + /* eventFlag= */ Player.EVENT_MAX_SEEK_TO_PREVIOUS_POSITION_CHANGED, + listener -> + listener.onMaxSeekToPreviousPositionChanged( + newPlayerInfo.maxSeekToPreviousPositionMs)); + } + if (!oldPlayerInfo.trackSelectionParameters.equals(newPlayerInfo.trackSelectionParameters)) { + listeners.queueEvent( + /* eventFlag= */ Player.EVENT_TRACK_SELECTION_PARAMETERS_CHANGED, + listener -> + listener.onTrackSelectionParametersChanged(newPlayerInfo.trackSelectionParameters)); + } listeners.flushEvents(); } @@ -2431,7 +2543,6 @@ import org.checkerframework.checker.nullness.qual.NonNull; }); } - @SuppressWarnings("deprecation") // Implementing and calling deprecated listener method. void onPlayerInfoChanged(PlayerInfo newPlayerInfo, BundlingExclusions bundlingExclusions) { if (!isConnected()) { return; @@ -2467,168 +2578,43 @@ import org.checkerframework.checker.nullness.qual.NonNull; intersectedPlayerCommands) .first; PlayerInfo finalPlayerInfo = playerInfo; - if (!oldPlayerInfo.timeline.equals(finalPlayerInfo.timeline)) { - listeners.queueEvent( - /* eventFlag= */ Player.EVENT_TIMELINE_CHANGED, - listener -> - listener.onTimelineChanged( - finalPlayerInfo.timeline, Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE)); - } - if (!oldPlayerInfo.oldPositionInfo.equals(finalPlayerInfo.oldPositionInfo) - || !oldPlayerInfo.newPositionInfo.equals(finalPlayerInfo.newPositionInfo)) { - listeners.queueEvent( - /* eventFlag= */ Player.EVENT_POSITION_DISCONTINUITY, - listener -> - listener.onPositionDiscontinuity( - finalPlayerInfo.oldPositionInfo, - finalPlayerInfo.newPositionInfo, - finalPlayerInfo.discontinuityReason)); - } - MediaItem oldCurrentMediaItem = oldPlayerInfo.getCurrentMediaItem(); - MediaItem currentMediaItem = finalPlayerInfo.getCurrentMediaItem(); - if (!Util.areEqual(oldCurrentMediaItem, currentMediaItem)) { - listeners.queueEvent( - /* eventFlag= */ Player.EVENT_MEDIA_ITEM_TRANSITION, - listener -> - listener.onMediaItemTransition( - currentMediaItem, finalPlayerInfo.mediaItemTransitionReason)); - } - PlaybackException oldPlayerError = oldPlayerInfo.playerError; - PlaybackException playerError = finalPlayerInfo.playerError; - boolean errorsMatch = - oldPlayerError == playerError - || (oldPlayerError != null && oldPlayerError.errorInfoEquals(playerError)); - if (!errorsMatch) { - listeners.queueEvent( - /* eventFlag= */ Player.EVENT_PLAYER_ERROR, - listener -> listener.onPlayerErrorChanged(finalPlayerInfo.playerError)); - if (finalPlayerInfo.playerError != null) { - listeners.queueEvent( - /* eventFlag= */ Player.EVENT_PLAYER_ERROR, - listener -> listener.onPlayerError(finalPlayerInfo.playerError)); - } - } - if (!Util.areEqual(oldPlayerInfo.currentTracks, finalPlayerInfo.currentTracks)) { - listeners.queueEvent( - /* eventFlag= */ Player.EVENT_TRACKS_CHANGED, - listener -> listener.onTracksChanged(finalPlayerInfo.currentTracks)); - } - if (!oldPlayerInfo.mediaMetadata.equals(finalPlayerInfo.mediaMetadata)) { - listeners.queueEvent( - /* eventFlag= */ Player.EVENT_MEDIA_METADATA_CHANGED, - listener -> listener.onMediaMetadataChanged(finalPlayerInfo.mediaMetadata)); - } - if (oldPlayerInfo.isLoading != finalPlayerInfo.isLoading) { - listeners.queueEvent( - /* eventFlag= */ Player.EVENT_IS_LOADING_CHANGED, - listener -> listener.onIsLoadingChanged(finalPlayerInfo.isLoading)); - } - if (oldPlayerInfo.playbackState != finalPlayerInfo.playbackState) { - listeners.queueEvent( - /* eventFlag= */ Player.EVENT_PLAYBACK_STATE_CHANGED, - listener -> listener.onPlaybackStateChanged(finalPlayerInfo.playbackState)); - } - if (oldPlayerInfo.playWhenReady != finalPlayerInfo.playWhenReady) { - listeners.queueEvent( - /* eventFlag= */ Player.EVENT_PLAY_WHEN_READY_CHANGED, - listener -> - listener.onPlayWhenReadyChanged( - finalPlayerInfo.playWhenReady, finalPlayerInfo.playWhenReadyChangeReason)); - } - if (oldPlayerInfo.playbackSuppressionReason != finalPlayerInfo.playbackSuppressionReason) { - listeners.queueEvent( - /* eventFlag= */ Player.EVENT_PLAYBACK_SUPPRESSION_REASON_CHANGED, - listener -> - listener.onPlaybackSuppressionReasonChanged( - finalPlayerInfo.playbackSuppressionReason)); - } - if (oldPlayerInfo.isPlaying != finalPlayerInfo.isPlaying) { - listeners.queueEvent( - /* eventFlag= */ Player.EVENT_IS_PLAYING_CHANGED, - listener -> listener.onIsPlayingChanged(finalPlayerInfo.isPlaying)); - } - if (!Util.areEqual(oldPlayerInfo.playbackParameters, finalPlayerInfo.playbackParameters)) { - listeners.queueEvent( - /* eventFlag= */ Player.EVENT_PLAYBACK_PARAMETERS_CHANGED, - listener -> listener.onPlaybackParametersChanged(finalPlayerInfo.playbackParameters)); - } - if (oldPlayerInfo.repeatMode != finalPlayerInfo.repeatMode) { - listeners.queueEvent( - /* eventFlag= */ Player.EVENT_REPEAT_MODE_CHANGED, - listener -> listener.onRepeatModeChanged(finalPlayerInfo.repeatMode)); - } - if (oldPlayerInfo.shuffleModeEnabled != finalPlayerInfo.shuffleModeEnabled) { - listeners.queueEvent( - /* eventFlag= */ Player.EVENT_SHUFFLE_MODE_ENABLED_CHANGED, - listener -> listener.onShuffleModeEnabledChanged(finalPlayerInfo.shuffleModeEnabled)); - } - if (!Util.areEqual(oldPlayerInfo.playlistMetadata, finalPlayerInfo.playlistMetadata)) { - listeners.queueEvent( - /* eventFlag= */ Player.EVENT_PLAYLIST_METADATA_CHANGED, - listener -> listener.onPlaylistMetadataChanged(finalPlayerInfo.playlistMetadata)); - } - if (oldPlayerInfo.volume != finalPlayerInfo.volume) { - listeners.queueEvent( - /* eventFlag= */ Player.EVENT_VOLUME_CHANGED, - listener -> listener.onVolumeChanged(finalPlayerInfo.volume)); - } - if (!Util.areEqual(oldPlayerInfo.audioAttributes, finalPlayerInfo.audioAttributes)) { - listeners.queueEvent( - /* eventFlag= */ Player.EVENT_AUDIO_ATTRIBUTES_CHANGED, - listener -> listener.onAudioAttributesChanged(finalPlayerInfo.audioAttributes)); - } - if (!oldPlayerInfo.cueGroup.cues.equals(finalPlayerInfo.cueGroup.cues)) { - listeners.queueEvent( - /* eventFlag= */ Player.EVENT_CUES, - listener -> listener.onCues(finalPlayerInfo.cueGroup.cues)); - listeners.queueEvent( - /* eventFlag= */ Player.EVENT_CUES, - listener -> listener.onCues(finalPlayerInfo.cueGroup)); - } - if (!Util.areEqual(oldPlayerInfo.deviceInfo, finalPlayerInfo.deviceInfo)) { - listeners.queueEvent( - /* eventFlag= */ Player.EVENT_DEVICE_INFO_CHANGED, - listener -> listener.onDeviceInfoChanged(finalPlayerInfo.deviceInfo)); - } - if (oldPlayerInfo.deviceVolume != finalPlayerInfo.deviceVolume - || oldPlayerInfo.deviceMuted != finalPlayerInfo.deviceMuted) { - listeners.queueEvent( - /* eventFlag= */ Player.EVENT_DEVICE_VOLUME_CHANGED, - listener -> - listener.onDeviceVolumeChanged( - finalPlayerInfo.deviceVolume, finalPlayerInfo.deviceMuted)); - } - if (!oldPlayerInfo.videoSize.equals(finalPlayerInfo.videoSize)) { - listeners.queueEvent( - /* eventFlag= */ Player.EVENT_VIDEO_SIZE_CHANGED, - listener -> listener.onVideoSizeChanged(finalPlayerInfo.videoSize)); - } - if (oldPlayerInfo.seekBackIncrementMs != finalPlayerInfo.seekBackIncrementMs) { - listeners.queueEvent( - /* eventFlag= */ Player.EVENT_SEEK_BACK_INCREMENT_CHANGED, - listener -> listener.onSeekBackIncrementChanged(finalPlayerInfo.seekBackIncrementMs)); - } - if (oldPlayerInfo.seekForwardIncrementMs != finalPlayerInfo.seekForwardIncrementMs) { - listeners.queueEvent( - /* eventFlag= */ Player.EVENT_SEEK_FORWARD_INCREMENT_CHANGED, - listener -> - listener.onSeekForwardIncrementChanged(finalPlayerInfo.seekForwardIncrementMs)); - } - if (oldPlayerInfo.maxSeekToPreviousPositionMs != finalPlayerInfo.maxSeekToPreviousPositionMs) { - listeners.queueEvent( - /* eventFlag= */ Player.EVENT_MAX_SEEK_TO_PREVIOUS_POSITION_CHANGED, - listener -> - listener.onMaxSeekToPreviousPositionChanged( - finalPlayerInfo.maxSeekToPreviousPositionMs)); - } - if (!oldPlayerInfo.trackSelectionParameters.equals(finalPlayerInfo.trackSelectionParameters)) { - listeners.queueEvent( - /* eventFlag= */ Player.EVENT_TRACK_SELECTION_PARAMETERS_CHANGED, - listener -> - listener.onTrackSelectionParametersChanged(finalPlayerInfo.trackSelectionParameters)); - } - listeners.flushEvents(); + @Nullable + @Player.DiscontinuityReason + Integer positionDiscontinuityReasonIfAny = + (!oldPlayerInfo.oldPositionInfo.equals(newPlayerInfo.oldPositionInfo) + || !oldPlayerInfo.newPositionInfo.equals(newPlayerInfo.newPositionInfo)) + ? finalPlayerInfo.discontinuityReason + : null; + + @Nullable + @Player.MediaItemTransitionReason + Integer mediaItemTransitionReasonIfAny = + !Util.areEqual(oldPlayerInfo.getCurrentMediaItem(), finalPlayerInfo.getCurrentMediaItem()) + ? finalPlayerInfo.mediaItemTransitionReason + : null; + + @Nullable + @Player.TimelineChangeReason + Integer timelineChangeReasonIfAny = + !oldPlayerInfo.timeline.equals(finalPlayerInfo.timeline) + ? finalPlayerInfo.timelineChangeReason + : null; + + @Nullable + @Player.PlayWhenReadyChangeReason + Integer playWhenReadyChangeReasonIfAny = + oldPlayerInfo.playWhenReady != finalPlayerInfo.playWhenReady + ? finalPlayerInfo.playWhenReadyChangeReason + : null; + + notifyPlayerInfoListenersWithReasons( + oldPlayerInfo, + finalPlayerInfo, + timelineChangeReasonIfAny, + playWhenReadyChangeReasonIfAny, + positionDiscontinuityReasonIfAny, + mediaItemTransitionReasonIfAny); } void onAvailableCommandsChangedFromSession( diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplLegacy.java b/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplLegacy.java index 8698894a6f..339295fca3 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplLegacy.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplLegacy.java @@ -650,7 +650,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; /* durationMs= */ C.TIME_UNSET, /* bufferedPositionMs= */ 0, /* bufferedPercentage= */ 0, - /* totalBufferedDurationMs= */ 0)); + /* totalBufferedDurationMs= */ 0), + Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED); ControllerInfo maskedControllerInfo = new ControllerInfo( maskedPlayerInfo, @@ -711,7 +712,9 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; calculateCurrentItemIndexAfterAddItems(currentMediaItemIndex, index, mediaItems.size()); PlayerInfo maskedPlayerInfo = controllerInfo.playerInfo.copyWithTimelineAndMediaItemIndex( - newQueueTimeline, newCurrentMediaItemIndex); + newQueueTimeline, + newCurrentMediaItemIndex, + Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED); ControllerInfo maskedControllerInfo = new ControllerInfo( maskedPlayerInfo, @@ -759,7 +762,9 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; } PlayerInfo maskedPlayerInfo = controllerInfo.playerInfo.copyWithTimelineAndMediaItemIndex( - newQueueTimeline, newCurrentMediaItemIndex); + newQueueTimeline, + newCurrentMediaItemIndex, + Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED); ControllerInfo maskedControllerInfo = new ControllerInfo( @@ -823,7 +828,9 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; queueTimeline.copyWithMovedMediaItems(fromIndex, toIndex, newIndex); PlayerInfo maskedPlayerInfo = controllerInfo.playerInfo.copyWithTimelineAndMediaItemIndex( - newQueueTimeline, newCurrentMediaItemIndex); + newQueueTimeline, + newCurrentMediaItemIndex, + Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED); ControllerInfo maskedControllerInfo = new ControllerInfo( @@ -1536,7 +1543,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; (listener) -> listener.onTimelineChanged( newControllerInfo.playerInfo.timeline, - Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED)); + newControllerInfo.playerInfo.timelineChangeReason)); } if (!Util.areEqual(oldLegacyPlayerInfo.queueTitle, newLegacyPlayerInfo.queueTitle)) { listeners.queueEvent( @@ -2232,6 +2239,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; /* shuffleModeEnabled= */ shuffleModeEnabled, /* videoSize= */ VideoSize.UNKNOWN, /* timeline= */ currentTimeline, + /* timelineChangeReason= */ PlayerInfo.TIMELINE_CHANGE_REASON_DEFAULT, /* playlistMetadata= */ playlistMetadata, /* volume= */ 1.0f, /* audioAttributes= */ audioAttributes, diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java b/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java index 6b7f79114a..2f1fbd7dc3 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java @@ -1086,7 +1086,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; } session.playerInfo = session.playerInfo.copyWithTimelineAndSessionPositionInfo( - timeline, player.createSessionPositionInfoForBundling()); + timeline, player.createSessionPositionInfoForBundling(), reason); session.onPlayerInfoChangedHandler.sendPlayerInfoChangedMessage( /* excludeTimeline= */ false, /* excludeTracks= */ true); session.dispatchRemoteControllerTaskToLegacyStub( @@ -1361,7 +1361,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; playerInfo = playerInfo.copyWithTimelineAndSessionPositionInfo( getPlayerWrapper().getCurrentTimelineWithCommandCheck(), - getPlayerWrapper().createSessionPositionInfoForBundling()); + getPlayerWrapper().createSessionPositionInfoForBundling(), + playerInfo.timelineChangeReason); dispatchOnPlayerInfoChanged(playerInfo, excludeTimeline, excludeTracks); excludeTimeline = true; excludeTracks = true; 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 b560e83a14..4fa7ac0714 100644 --- a/libraries/session/src/main/java/androidx/media3/session/PlayerInfo.java +++ b/libraries/session/src/main/java/androidx/media3/session/PlayerInfo.java @@ -20,6 +20,7 @@ import static androidx.media3.common.Player.MEDIA_ITEM_TRANSITION_REASON_REPEAT; import static androidx.media3.common.Player.PLAYBACK_SUPPRESSION_REASON_NONE; import static androidx.media3.common.Player.PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST; import static androidx.media3.common.Player.STATE_IDLE; +import static androidx.media3.common.Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED; import android.os.Bundle; import android.os.SystemClock; @@ -128,6 +129,7 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; private @Player.RepeatMode int repeatMode; private boolean shuffleModeEnabled; private Timeline timeline; + private @Player.TimelineChangeReason int timelineChangeReason; private VideoSize videoSize; private MediaMetadata playlistMetadata; private float volume; @@ -160,6 +162,7 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; repeatMode = playerInfo.repeatMode; shuffleModeEnabled = playerInfo.shuffleModeEnabled; timeline = playerInfo.timeline; + timelineChangeReason = playerInfo.timelineChangeReason; videoSize = playerInfo.videoSize; playlistMetadata = playerInfo.playlistMetadata; volume = playerInfo.volume; @@ -243,6 +246,12 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; return this; } + @CanIgnoreReturnValue + public Builder setTimelineChangeReason(@Player.TimelineChangeReason int timelineChangeReason) { + this.timelineChangeReason = timelineChangeReason; + return this; + } + @CanIgnoreReturnValue public Builder setVideoSize(VideoSize videoSize) { this.videoSize = videoSize; @@ -381,6 +390,7 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; shuffleModeEnabled, videoSize, timeline, + timelineChangeReason, playlistMetadata, volume, audioAttributes, @@ -414,6 +424,9 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; public static final int PLAY_WHEN_READY_CHANGE_REASON_DEFAULT = PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST; + /** Default timeline change reason. */ + public static final int TIMELINE_CHANGE_REASON_DEFAULT = TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED; + public static final PlayerInfo DEFAULT = new PlayerInfo( /* playerError= */ null, @@ -427,6 +440,7 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; /* shuffleModeEnabled= */ false, VideoSize.UNKNOWN, Timeline.EMPTY, + TIMELINE_CHANGE_REASON_DEFAULT, MediaMetadata.EMPTY, /* volume= */ 1f, AudioAttributes.DEFAULT, @@ -467,6 +481,8 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; public final Timeline timeline; + public final @Player.TimelineChangeReason int timelineChangeReason; + public final VideoSize videoSize; public final MediaMetadata playlistMetadata; @@ -580,17 +596,24 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; @CheckResult public PlayerInfo copyWithTimelineAndSessionPositionInfo( - Timeline timeline, SessionPositionInfo sessionPositionInfo) { + Timeline timeline, + SessionPositionInfo sessionPositionInfo, + @Player.TimelineChangeReason int timelineChangeReason) { return new Builder(this) .setTimeline(timeline) .setSessionPositionInfo(sessionPositionInfo) + .setTimelineChangeReason(timelineChangeReason) .build(); } @CheckResult - public PlayerInfo copyWithTimelineAndMediaItemIndex(Timeline timeline, int mediaItemIndex) { + public PlayerInfo copyWithTimelineAndMediaItemIndex( + Timeline timeline, + int mediaItemIndex, + @Player.TimelineChangeReason int timelineChangeReason) { return new Builder(this) .setTimeline(timeline) + .setTimelineChangeReason(timelineChangeReason) .setSessionPositionInfo( new SessionPositionInfo( new PositionInfo( @@ -696,6 +719,7 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; boolean shuffleModeEnabled, VideoSize videoSize, Timeline timeline, + @Player.TimelineChangeReason int timelineChangeReason, MediaMetadata playlistMetadata, float volume, AudioAttributes audioAttributes, @@ -726,6 +750,7 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; this.shuffleModeEnabled = shuffleModeEnabled; this.videoSize = videoSize; this.timeline = timeline; + this.timelineChangeReason = timelineChangeReason; this.playlistMetadata = playlistMetadata; this.volume = volume; this.audioAttributes = audioAttributes; @@ -796,7 +821,8 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; private static final String FIELD_MAX_SEEK_TO_PREVIOUS_POSITION_MS = Util.intToStringMaxRadix(28); private static final String FIELD_TRACK_SELECTION_PARAMETERS = Util.intToStringMaxRadix(29); private static final String FIELD_CURRENT_TRACKS = Util.intToStringMaxRadix(30); - // Next field key = 31 + private static final String FIELD_TIMELINE_CHANGE_REASON = Util.intToStringMaxRadix(31); + // Next field key = 32 public Bundle toBundle( Player.Commands availableCommands, boolean excludeTimeline, boolean excludeTracks) { @@ -828,6 +854,7 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; FIELD_TIMELINE, timeline.toBundleWithOneWindowOnly(sessionPositionInfo.positionInfo.mediaItemIndex)); } + 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()); @@ -911,6 +938,9 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; @Nullable Bundle timelineBundle = bundle.getBundle(FIELD_TIMELINE); Timeline timeline = timelineBundle == null ? Timeline.EMPTY : Timeline.CREATOR.fromBundle(timelineBundle); + int timelineChangeReason = + bundle.getInt( + FIELD_TIMELINE_CHANGE_REASON, /* defaultValue= */ TIMELINE_CHANGE_REASON_DEFAULT); @Nullable Bundle videoSizeBundle = bundle.getBundle(FIELD_VIDEO_SIZE); VideoSize videoSize = videoSizeBundle == null ? VideoSize.UNKNOWN : VideoSize.CREATOR.fromBundle(videoSizeBundle); @@ -982,6 +1012,7 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; shuffleModeEnabled, videoSize, timeline, + timelineChangeReason, playlistMetadata, volume, audioAttributes, diff --git a/libraries/session/src/main/java/androidx/media3/session/PlayerWrapper.java b/libraries/session/src/main/java/androidx/media3/session/PlayerWrapper.java index 1d19bd5344..60aa78d6d7 100644 --- a/libraries/session/src/main/java/androidx/media3/session/PlayerWrapper.java +++ b/libraries/session/src/main/java/androidx/media3/session/PlayerWrapper.java @@ -1169,6 +1169,7 @@ import java.util.List; getShuffleModeEnabled(), getVideoSize(), getCurrentTimelineWithCommandCheck(), + PlayerInfo.TIMELINE_CHANGE_REASON_DEFAULT, getPlaylistMetadataWithCommandCheck(), getVolumeWithCommandCheck(), getAudioAttributesWithCommandCheck(), 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 9c59bc326f..c3e6736ed3 100644 --- a/libraries/session/src/test/java/androidx/media3/session/PlayerInfoTest.java +++ b/libraries/session/src/test/java/androidx/media3/session/PlayerInfoTest.java @@ -118,6 +118,7 @@ public class PlayerInfoTest { /* contentDurationMs= */ 27000, /* contentBufferedPositionMs= */ 15000)) .setTimeline(new FakeTimeline(/* windowCount= */ 10)) + .setTimelineChangeReason(Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE) .setMediaMetadata(new MediaMetadata.Builder().setTitle("title").build()) .setPlaylistMetadata(new MediaMetadata.Builder().setArtist("artist").build()) .setVolume(0.5f) @@ -202,6 +203,8 @@ public class PlayerInfoTest { assertThat(infoAfterBundling.sessionPositionInfo.contentDurationMs).isEqualTo(27000); assertThat(infoAfterBundling.sessionPositionInfo.contentBufferedPositionMs).isEqualTo(15000); 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.volume).isEqualTo(0.5f); diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerStateMaskingTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerStateMaskingTest.java index 04a5b7cbbc..a221f256b5 100644 --- a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerStateMaskingTest.java +++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerStateMaskingTest.java @@ -63,7 +63,7 @@ import org.junit.rules.RuleChain; import org.junit.rules.TestRule; import org.junit.runner.RunWith; -/** Tests for state masking {@link MediaController} calls. */ +/** Tests for state masking {@link MediaController} ({@link MediaControllerImplBase}) calls. */ @RunWith(AndroidJUnit4.class) @LargeTest public class MediaControllerStateMaskingTest { diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerStateMaskingWithMediaSessionCompatTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerStateMaskingWithMediaSessionCompatTest.java index a3a9f29c95..8d37444d8d 100644 --- a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerStateMaskingWithMediaSessionCompatTest.java +++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerStateMaskingWithMediaSessionCompatTest.java @@ -75,7 +75,7 @@ import org.junit.rules.RuleChain; import org.junit.rules.TestRule; import org.junit.runner.RunWith; -/** Tests for state masking {@link MediaController} calls. */ +/** Tests for state masking {@link MediaController} ({@link MediaControllerImplLegacy}) calls. */ @RunWith(AndroidJUnit4.class) @LargeTest public class MediaControllerStateMaskingWithMediaSessionCompatTest {