Revert of 9c8c0a59823e2a49451fbdf7040107c6cea43cc5

PiperOrigin-RevId: 426994820
This commit is contained in:
olly 2022-02-07 21:04:58 +00:00 committed by Ian Baker
parent bc118097d8
commit 631b107bda
8 changed files with 602 additions and 405 deletions

View File

@ -137,7 +137,7 @@ public final class CastPlayer extends BasePlayer {
private final SeekResultCallback seekResultCallback; private final SeekResultCallback seekResultCallback;
// Listeners and notification. // Listeners and notification.
private final ListenerSet<Listener> listeners; private final ListenerSet<Player.EventListener> listeners;
@Nullable private SessionAvailabilityListener sessionAvailabilityListener; @Nullable private SessionAvailabilityListener sessionAvailabilityListener;
// Internal state. // Internal state.
@ -280,11 +280,41 @@ public final class CastPlayer extends BasePlayer {
@Override @Override
public void addListener(Listener listener) { public void addListener(Listener listener) {
EventListener eventListener = listener;
addListener(eventListener);
}
/**
* Registers a listener to receive events from the player.
*
* <p>The listener's methods will be called on the thread associated with {@link
* #getApplicationLooper()}.
*
* @param listener The listener to register.
* @deprecated Use {@link #addListener(Listener)} and {@link #removeListener(Listener)} instead.
*/
@Deprecated
@SuppressWarnings("deprecation")
public void addListener(EventListener listener) {
listeners.add(listener); listeners.add(listener);
} }
@Override @Override
public void removeListener(Listener listener) { public void removeListener(Listener listener) {
EventListener eventListener = listener;
removeListener(eventListener);
}
/**
* Unregister a listener registered through {@link #addListener(EventListener)}. The listener will
* no longer receive events from the player.
*
* @param listener The listener to unregister.
* @deprecated Use {@link #addListener(Listener)} and {@link #removeListener(Listener)} instead.
*/
@Deprecated
@SuppressWarnings("deprecation")
public void removeListener(EventListener listener) {
listeners.remove(listener); listeners.remove(listener);
} }
@ -443,7 +473,7 @@ public final class CastPlayer extends BasePlayer {
} }
updateAvailableCommandsAndNotifyIfChanged(); updateAvailableCommandsAndNotifyIfChanged();
} else if (pendingSeekCount == 0) { } else if (pendingSeekCount == 0) {
listeners.queueEvent(/* eventFlag= */ C.INDEX_UNSET, Listener::onSeekProcessed); listeners.queueEvent(/* eventFlag= */ C.INDEX_UNSET, EventListener::onSeekProcessed);
} }
listeners.flushEvents(); listeners.flushEvents();
} }
@ -1447,7 +1477,7 @@ public final class CastPlayer extends BasePlayer {
currentWindowIndex = pendingSeekWindowIndex; currentWindowIndex = pendingSeekWindowIndex;
pendingSeekWindowIndex = C.INDEX_UNSET; pendingSeekWindowIndex = C.INDEX_UNSET;
pendingSeekPositionMs = C.TIME_UNSET; pendingSeekPositionMs = C.TIME_UNSET;
listeners.sendEvent(/* eventFlag= */ C.INDEX_UNSET, Listener::onSeekProcessed); listeners.sendEvent(/* eventFlag= */ C.INDEX_UNSET, EventListener::onSeekProcessed);
} }
} }
} }

View File

@ -819,159 +819,191 @@ public class ForwardingPlayer implements Player {
return player; return player;
} }
private static final class ForwardingListener implements Listener { @SuppressWarnings("deprecation") // Use of deprecated type for backwards compatibility.
private static class ForwardingEventListener implements EventListener {
private final ForwardingPlayer forwardingPlayer; private final ForwardingPlayer forwardingPlayer;
private final Listener listener; private final EventListener eventListener;
public ForwardingListener(ForwardingPlayer forwardingPlayer, Listener listener) { private ForwardingEventListener(
ForwardingPlayer forwardingPlayer, EventListener eventListener) {
this.forwardingPlayer = forwardingPlayer; this.forwardingPlayer = forwardingPlayer;
this.listener = listener; this.eventListener = eventListener;
}
@Override
public void onEvents(Player player, Events events) {
// Replace player with forwarding player.
listener.onEvents(forwardingPlayer, events);
} }
@Override @Override
public void onTimelineChanged(Timeline timeline, @TimelineChangeReason int reason) { public void onTimelineChanged(Timeline timeline, @TimelineChangeReason int reason) {
listener.onTimelineChanged(timeline, reason); eventListener.onTimelineChanged(timeline, reason);
} }
@Override @Override
public void onMediaItemTransition( public void onMediaItemTransition(
@Nullable MediaItem mediaItem, @MediaItemTransitionReason int reason) { @Nullable MediaItem mediaItem, @MediaItemTransitionReason int reason) {
listener.onMediaItemTransition(mediaItem, reason); eventListener.onMediaItemTransition(mediaItem, reason);
} }
@Override @Override
@SuppressWarnings("deprecation")
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) { public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
listener.onTracksChanged(trackGroups, trackSelections); eventListener.onTracksChanged(trackGroups, trackSelections);
} }
@Override @Override
public void onTracksInfoChanged(TracksInfo tracksInfo) { public void onTracksInfoChanged(TracksInfo tracksInfo) {
listener.onTracksInfoChanged(tracksInfo); eventListener.onTracksInfoChanged(tracksInfo);
} }
@Override @Override
public void onMediaMetadataChanged(MediaMetadata mediaMetadata) { public void onMediaMetadataChanged(MediaMetadata mediaMetadata) {
listener.onMediaMetadataChanged(mediaMetadata); eventListener.onMediaMetadataChanged(mediaMetadata);
} }
@Override @Override
public void onPlaylistMetadataChanged(MediaMetadata mediaMetadata) { public void onPlaylistMetadataChanged(MediaMetadata mediaMetadata) {
listener.onPlaylistMetadataChanged(mediaMetadata); eventListener.onPlaylistMetadataChanged(mediaMetadata);
} }
@Override @Override
public void onIsLoadingChanged(boolean isLoading) { public void onIsLoadingChanged(boolean isLoading) {
listener.onIsLoadingChanged(isLoading); eventListener.onIsLoadingChanged(isLoading);
} }
@Override @Override
@SuppressWarnings("deprecation")
public void onLoadingChanged(boolean isLoading) { public void onLoadingChanged(boolean isLoading) {
listener.onIsLoadingChanged(isLoading); eventListener.onIsLoadingChanged(isLoading);
} }
@Override @Override
public void onAvailableCommandsChanged(Commands availableCommands) { public void onAvailableCommandsChanged(Commands availableCommands) {
listener.onAvailableCommandsChanged(availableCommands); eventListener.onAvailableCommandsChanged(availableCommands);
} }
@Override @Override
public void onTrackSelectionParametersChanged(TrackSelectionParameters parameters) { public void onTrackSelectionParametersChanged(TrackSelectionParameters parameters) {
listener.onTrackSelectionParametersChanged(parameters); eventListener.onTrackSelectionParametersChanged(parameters);
} }
@Override @Override
@SuppressWarnings("deprecation")
public void onPlayerStateChanged(boolean playWhenReady, @State int playbackState) { public void onPlayerStateChanged(boolean playWhenReady, @State int playbackState) {
listener.onPlayerStateChanged(playWhenReady, playbackState); eventListener.onPlayerStateChanged(playWhenReady, playbackState);
} }
@Override @Override
public void onPlaybackStateChanged(@State int playbackState) { public void onPlaybackStateChanged(@State int playbackState) {
listener.onPlaybackStateChanged(playbackState); eventListener.onPlaybackStateChanged(playbackState);
} }
@Override @Override
public void onPlayWhenReadyChanged( public void onPlayWhenReadyChanged(
boolean playWhenReady, @PlayWhenReadyChangeReason int reason) { boolean playWhenReady, @PlayWhenReadyChangeReason int reason) {
listener.onPlayWhenReadyChanged(playWhenReady, reason); eventListener.onPlayWhenReadyChanged(playWhenReady, reason);
} }
@Override @Override
public void onPlaybackSuppressionReasonChanged( public void onPlaybackSuppressionReasonChanged(
@PlayWhenReadyChangeReason int playbackSuppressionReason) { @PlayWhenReadyChangeReason int playbackSuppressionReason) {
listener.onPlaybackSuppressionReasonChanged(playbackSuppressionReason); eventListener.onPlaybackSuppressionReasonChanged(playbackSuppressionReason);
} }
@Override @Override
public void onIsPlayingChanged(boolean isPlaying) { public void onIsPlayingChanged(boolean isPlaying) {
listener.onIsPlayingChanged(isPlaying); eventListener.onIsPlayingChanged(isPlaying);
} }
@Override @Override
public void onRepeatModeChanged(@RepeatMode int repeatMode) { public void onRepeatModeChanged(@RepeatMode int repeatMode) {
listener.onRepeatModeChanged(repeatMode); eventListener.onRepeatModeChanged(repeatMode);
} }
@Override @Override
public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) { public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {
listener.onShuffleModeEnabledChanged(shuffleModeEnabled); eventListener.onShuffleModeEnabledChanged(shuffleModeEnabled);
} }
@Override @Override
public void onPlayerError(PlaybackException error) { public void onPlayerError(PlaybackException error) {
listener.onPlayerError(error); eventListener.onPlayerError(error);
} }
@Override @Override
public void onPlayerErrorChanged(@Nullable PlaybackException error) { public void onPlayerErrorChanged(@Nullable PlaybackException error) {
listener.onPlayerErrorChanged(error); eventListener.onPlayerErrorChanged(error);
} }
@Override @Override
@SuppressWarnings("deprecation")
public void onPositionDiscontinuity(@DiscontinuityReason int reason) { public void onPositionDiscontinuity(@DiscontinuityReason int reason) {
listener.onPositionDiscontinuity(reason); eventListener.onPositionDiscontinuity(reason);
} }
@Override @Override
public void onPositionDiscontinuity( public void onPositionDiscontinuity(
PositionInfo oldPosition, PositionInfo newPosition, @DiscontinuityReason int reason) { PositionInfo oldPosition, PositionInfo newPosition, @DiscontinuityReason int reason) {
listener.onPositionDiscontinuity(oldPosition, newPosition, reason); eventListener.onPositionDiscontinuity(oldPosition, newPosition, reason);
} }
@Override @Override
public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) { public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
listener.onPlaybackParametersChanged(playbackParameters); eventListener.onPlaybackParametersChanged(playbackParameters);
} }
@Override @Override
public void onSeekBackIncrementChanged(long seekBackIncrementMs) { public void onSeekBackIncrementChanged(long seekBackIncrementMs) {
listener.onSeekBackIncrementChanged(seekBackIncrementMs); eventListener.onSeekBackIncrementChanged(seekBackIncrementMs);
} }
@Override @Override
public void onSeekForwardIncrementChanged(long seekForwardIncrementMs) { public void onSeekForwardIncrementChanged(long seekForwardIncrementMs) {
listener.onSeekForwardIncrementChanged(seekForwardIncrementMs); eventListener.onSeekForwardIncrementChanged(seekForwardIncrementMs);
} }
@Override @Override
public void onMaxSeekToPreviousPositionChanged(long maxSeekToPreviousPositionMs) { public void onMaxSeekToPreviousPositionChanged(long maxSeekToPreviousPositionMs) {
listener.onMaxSeekToPreviousPositionChanged(maxSeekToPreviousPositionMs); eventListener.onMaxSeekToPreviousPositionChanged(maxSeekToPreviousPositionMs);
} }
@Override @Override
@SuppressWarnings("deprecation")
public void onSeekProcessed() { public void onSeekProcessed() {
listener.onSeekProcessed(); eventListener.onSeekProcessed();
}
@Override
public void onEvents(Player player, Events events) {
// Replace player with forwarding player.
eventListener.onEvents(forwardingPlayer, events);
}
@Override
public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
if (!(o instanceof ForwardingEventListener)) {
return false;
}
ForwardingEventListener that = (ForwardingEventListener) o;
if (!forwardingPlayer.equals(that.forwardingPlayer)) {
return false;
}
return eventListener.equals(that.eventListener);
}
@Override
public int hashCode() {
int result = forwardingPlayer.hashCode();
result = 31 * result + eventListener.hashCode();
return result;
}
}
private static final class ForwardingListener extends ForwardingEventListener
implements Listener {
private final Listener listener;
public ForwardingListener(ForwardingPlayer forwardingPlayer, Listener listener) {
super(forwardingPlayer, listener);
this.listener = listener;
} }
@Override @Override
@ -1028,27 +1060,5 @@ public class ForwardingPlayer implements Player {
public void onDeviceVolumeChanged(int volume, boolean muted) { public void onDeviceVolumeChanged(int volume, boolean muted) {
listener.onDeviceVolumeChanged(volume, muted); listener.onDeviceVolumeChanged(volume, muted);
} }
@Override
public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
if (!(o instanceof ForwardingListener)) {
return false;
}
ForwardingListener that = (ForwardingListener) o;
if (!forwardingPlayer.equals(that.forwardingPlayer)) {
return false;
}
return listener.equals(that.listener);
}
@Override
public int hashCode() {
int result = forwardingPlayer.hashCode();
result = 31 * result + listener.hashCode();
return result;
}
} }
} }

View File

@ -63,6 +63,343 @@ import java.util.List;
*/ */
public interface Player { public interface Player {
/**
* Listener of changes in player state.
*
* <p>All methods have no-op default implementations to allow selective overrides.
*
* <p>Listeners can choose to implement individual events (e.g. {@link
* #onIsPlayingChanged(boolean)}) or {@link #onEvents(Player, Events)}, which is called after one
* or more events occurred together.
*
* @deprecated Use {@link Player.Listener}.
*/
@UnstableApi
@Deprecated
interface EventListener {
/**
* Called when the timeline has been refreshed.
*
* <p>Note that the current {@link MediaItem} or playback position may change as a result of a
* timeline change. If playback can't continue smoothly because of this timeline change, a
* separate {@link #onPositionDiscontinuity(PositionInfo, PositionInfo, int)} callback will be
* triggered.
*
* <p>{@link #onEvents(Player, Events)} will also be called to report this event along with
* other events that happen in the same {@link Looper} message queue iteration.
*
* @param timeline The latest timeline. Never null, but may be empty.
* @param reason The {@link TimelineChangeReason} responsible for this timeline change.
*/
default void onTimelineChanged(Timeline timeline, @TimelineChangeReason int reason) {}
/**
* Called when playback transitions to a media item or starts repeating a media item according
* to the current {@link #getRepeatMode() repeat mode}.
*
* <p>Note that this callback is also called when the playlist becomes non-empty or empty as a
* consequence of a playlist change.
*
* <p>{@link #onEvents(Player, Events)} will also be called to report this event along with
* other events that happen in the same {@link Looper} message queue iteration.
*
* @param mediaItem The {@link MediaItem}. May be null if the playlist becomes empty.
* @param reason The reason for the transition.
*/
default void onMediaItemTransition(
@Nullable MediaItem mediaItem, @MediaItemTransitionReason int reason) {}
/**
* Called when the available or selected tracks change.
*
* <p>{@link #onEvents(Player, Events)} will also be called to report this event along with
* other events that happen in the same {@link Looper} message queue iteration.
*
* @param trackGroups The available tracks. Never null, but may be of length zero.
* @param trackSelections The selected tracks. Never null, but may contain null elements. A
* concrete implementation may include null elements if it has a fixed number of renderer
* components, wishes to report a TrackSelection for each of them, and has one or more
* renderer components that is not assigned any selected tracks.
* @deprecated Use {@link #onTracksInfoChanged(TracksInfo)} instead.
*/
@UnstableApi
@Deprecated
default void onTracksChanged(
TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {}
/**
* Called when the available or selected tracks change.
*
* <p>{@link #onEvents(Player, Events)} will also be called to report this event along with
* other events that happen in the same {@link Looper} message queue iteration.
*
* @param tracksInfo The available tracks information. Never null, but may be of length zero.
*/
default void onTracksInfoChanged(TracksInfo tracksInfo) {}
/**
* Called when the combined {@link MediaMetadata} changes.
*
* <p>The provided {@link MediaMetadata} is a combination of the {@link MediaItem#mediaMetadata}
* and the static and dynamic metadata from the {@link TrackSelection#getFormat(int) track
* selections' formats} and {@link Listener#onMetadata(Metadata)}. If a field is populated in
* the {@link MediaItem#mediaMetadata}, it will be prioritised above the same field coming from
* static or dynamic metadata.
*
* <p>This method may be called multiple times in quick succession.
*
* <p>{@link #onEvents(Player, Events)} will also be called to report this event along with
* other events that happen in the same {@link Looper} message queue iteration.
*
* @param mediaMetadata The combined {@link MediaMetadata}.
*/
default void onMediaMetadataChanged(MediaMetadata mediaMetadata) {}
/** Called when the playlist {@link MediaMetadata} changes. */
default void onPlaylistMetadataChanged(MediaMetadata mediaMetadata) {}
/**
* Called when the player starts or stops loading the source.
*
* <p>{@link #onEvents(Player, Events)} will also be called to report this event along with
* other events that happen in the same {@link Looper} message queue iteration.
*
* @param isLoading Whether the source is currently being loaded.
*/
default void onIsLoadingChanged(boolean isLoading) {}
/** @deprecated Use {@link #onIsLoadingChanged(boolean)} instead. */
@Deprecated
default void onLoadingChanged(boolean isLoading) {}
/**
* Called when the value returned from {@link #isCommandAvailable(int)} changes for at least one
* {@link Command}.
*
* <p>{@link #onEvents(Player, Events)} will also be called to report this event along with
* other events that happen in the same {@link Looper} message queue iteration.
*
* @param availableCommands The available {@link Commands}.
*/
default void onAvailableCommandsChanged(Commands availableCommands) {}
/**
* Called when the value returned from {@link #getTrackSelectionParameters()} changes.
*
* <p>{@link #onEvents(Player, Events)} will also be called to report this event along with
* other events that happen in the same {@link Looper} message queue iteration.
*
* @param parameters The new {@link TrackSelectionParameters}.
*/
default void onTrackSelectionParametersChanged(TrackSelectionParameters parameters) {}
/**
* @deprecated Use {@link #onPlaybackStateChanged(int)} and {@link
* #onPlayWhenReadyChanged(boolean, int)} instead.
*/
@Deprecated
default void onPlayerStateChanged(boolean playWhenReady, @State int playbackState) {}
/**
* Called when the value returned from {@link #getPlaybackState()} changes.
*
* <p>{@link #onEvents(Player, Events)} will also be called to report this event along with
* other events that happen in the same {@link Looper} message queue iteration.
*
* @param playbackState The new playback {@link State state}.
*/
default void onPlaybackStateChanged(@State int playbackState) {}
/**
* Called when the value returned from {@link #getPlayWhenReady()} changes.
*
* <p>{@link #onEvents(Player, Events)} will also be called to report this event along with
* other events that happen in the same {@link Looper} message queue iteration.
*
* @param playWhenReady Whether playback will proceed when ready.
* @param reason The {@link PlayWhenReadyChangeReason reason} for the change.
*/
default void onPlayWhenReadyChanged(
boolean playWhenReady, @PlayWhenReadyChangeReason int reason) {}
/**
* Called when the value returned from {@link #getPlaybackSuppressionReason()} changes.
*
* <p>{@link #onEvents(Player, Events)} will also be called to report this event along with
* other events that happen in the same {@link Looper} message queue iteration.
*
* @param playbackSuppressionReason The current {@link PlaybackSuppressionReason}.
*/
default void onPlaybackSuppressionReasonChanged(
@PlaybackSuppressionReason int playbackSuppressionReason) {}
/**
* Called when the value of {@link #isPlaying()} changes.
*
* <p>{@link #onEvents(Player, Events)} will also be called to report this event along with
* other events that happen in the same {@link Looper} message queue iteration.
*
* @param isPlaying Whether the player is playing.
*/
default void onIsPlayingChanged(boolean isPlaying) {}
/**
* Called when the value of {@link #getRepeatMode()} changes.
*
* <p>{@link #onEvents(Player, Events)} will also be called to report this event along with
* other events that happen in the same {@link Looper} message queue iteration.
*
* @param repeatMode The {@link RepeatMode} used for playback.
*/
default void onRepeatModeChanged(@RepeatMode int repeatMode) {}
/**
* Called when the value of {@link #getShuffleModeEnabled()} changes.
*
* <p>{@link #onEvents(Player, Events)} will also be called to report this event along with
* other events that happen in the same {@link Looper} message queue iteration.
*
* @param shuffleModeEnabled Whether shuffling of {@link MediaItem media items} is enabled.
*/
default void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {}
/**
* Called when an error occurs. The playback state will transition to {@link #STATE_IDLE}
* immediately after this method is called. The player instance can still be used, and {@link
* #release()} must still be called on the player should it no longer be required.
*
* <p>{@link #onEvents(Player, Events)} will also be called to report this event along with
* other events that happen in the same {@link Looper} message queue iteration.
*
* <p>Implementations of Player may pass an instance of a subclass of {@link PlaybackException}
* to this method in order to include more information about the error.
*
* @param error The error.
*/
default void onPlayerError(PlaybackException error) {}
/**
* Called when the {@link PlaybackException} returned by {@link #getPlayerError()} changes.
*
* <p>{@link #onEvents(Player, Events)} will also be called to report this event along with
* other events that happen in the same {@link Looper} message queue iteration.
*
* <p>Implementations of Player may pass an instance of a subclass of {@link PlaybackException}
* to this method in order to include more information about the error.
*
* @param error The new error, or null if the error is being cleared.
*/
default void onPlayerErrorChanged(@Nullable PlaybackException error) {}
/**
* @deprecated Use {@link #onPositionDiscontinuity(PositionInfo, PositionInfo, int)} instead.
*/
@Deprecated
default void onPositionDiscontinuity(@DiscontinuityReason int reason) {}
/**
* Called when a position discontinuity occurs.
*
* <p>A position discontinuity occurs when the playing period changes, the playback position
* jumps within the period currently being played, or when the playing period has been skipped
* or removed.
*
* <p>{@link #onEvents(Player, Events)} will also be called to report this event along with
* other events that happen in the same {@link Looper} message queue iteration.
*
* @param oldPosition The position before the discontinuity.
* @param newPosition The position after the discontinuity.
* @param reason The {@link DiscontinuityReason} responsible for the discontinuity.
*/
default void onPositionDiscontinuity(
PositionInfo oldPosition, PositionInfo newPosition, @DiscontinuityReason int reason) {}
/**
* Called when the current playback parameters change. The playback parameters may change due to
* a call to {@link #setPlaybackParameters(PlaybackParameters)}, or the player itself may change
* them (for example, if audio playback switches to passthrough or offload mode, where speed
* adjustment is no longer possible).
*
* <p>{@link #onEvents(Player, Events)} will also be called to report this event along with
* other events that happen in the same {@link Looper} message queue iteration.
*
* @param playbackParameters The playback parameters.
*/
default void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {}
/**
* Called when the value of {@link #getSeekBackIncrement()} changes.
*
* <p>{@link #onEvents(Player, Events)} will also be called to report this event along with
* other events that happen in the same {@link Looper} message queue iteration.
*
* @param seekBackIncrementMs The {@link #seekBack()} increment, in milliseconds.
*/
default void onSeekBackIncrementChanged(long seekBackIncrementMs) {}
/**
* Called when the value of {@link #getSeekForwardIncrement()} changes.
*
* <p>{@link #onEvents(Player, Events)} will also be called to report this event along with
* other events that happen in the same {@link Looper} message queue iteration.
*
* @param seekForwardIncrementMs The {@link #seekForward()} increment, in milliseconds.
*/
default void onSeekForwardIncrementChanged(long seekForwardIncrementMs) {}
/**
* Called when the value of {@link #getMaxSeekToPreviousPosition()} changes.
*
* <p>{@link #onEvents(Player, Events)} will also be called to report this event along with
* other events that happen in the same {@link Looper} message queue iteration.
*
* @param maxSeekToPreviousPositionMs The maximum position for which {@link #seekToPrevious()}
* seeks to the previous position, in milliseconds.
*/
default void onMaxSeekToPreviousPositionChanged(long maxSeekToPreviousPositionMs) {}
/**
* @deprecated Seeks are processed without delay. Listen to {@link
* #onPositionDiscontinuity(PositionInfo, PositionInfo, int)} with reason {@link
* #DISCONTINUITY_REASON_SEEK} instead.
*/
@Deprecated
default void onSeekProcessed() {}
/**
* Called when one or more player states changed.
*
* <p>State changes and events that happen within one {@link Looper} message queue iteration are
* reported together and only after all individual callbacks were triggered.
*
* <p>Only state changes represented by {@link Event events} are reported through this method.
*
* <p>Listeners should prefer this method over individual callbacks in the following cases:
*
* <ul>
* <li>They intend to trigger the same logic for multiple events (e.g. when updating a UI for
* both {@link #onPlaybackStateChanged(int)} and {@link #onPlayWhenReadyChanged(boolean,
* int)}).
* <li>They need access to the {@link Player} object to trigger further events (e.g. to call
* {@link Player#seekTo(long)} after a {@link #onMediaItemTransition(MediaItem, int)}).
* <li>They intend to use multiple state values together or in combination with {@link Player}
* getter methods. For example using {@link #getCurrentMediaItemIndex()} with the {@code
* timeline} provided in {@link #onTimelineChanged(Timeline, int)} is only safe from
* within this method.
* <li>They are interested in events that logically happened together (e.g {@link
* #onPlaybackStateChanged(int)} to {@link #STATE_BUFFERING} because of {@link
* #onMediaItemTransition(MediaItem, int)}).
* </ul>
*
* @param player The {@link Player} whose state changed. Use the getters to obtain the latest
* states.
* @param events The {@link Events} that happened in this iteration, indicating which player
* states changed.
*/
default void onEvents(Player player, Events events) {}
}
/** A set of {@link Event events}. */ /** A set of {@link Event events}. */
final class Events { final class Events {
@ -602,332 +939,62 @@ public interface Player {
* *
* <p>All methods have no-op default implementations to allow selective overrides. * <p>All methods have no-op default implementations to allow selective overrides.
*/ */
interface Listener { interface Listener extends EventListener {
/** @Override
* Called when one or more player states changed.
*
* <p>State changes and events that happen within one {@link Looper} message queue iteration are
* reported together and only after all individual callbacks were triggered.
*
* <p>Only state changes represented by {@link Event events} are reported through this method.
*
* <p>Listeners should prefer this method over individual callbacks in the following cases:
*
* <ul>
* <li>They intend to trigger the same logic for multiple events (e.g. when updating a UI for
* both {@link #onPlaybackStateChanged(int)} and {@link #onPlayWhenReadyChanged(boolean,
* int)}).
* <li>They need access to the {@link Player} object to trigger further events (e.g. to call
* {@link Player#seekTo(long)} after a {@link #onMediaItemTransition(MediaItem, int)}).
* <li>They intend to use multiple state values together or in combination with {@link Player}
* getter methods. For example using {@link #getCurrentMediaItemIndex()} with the {@code
* timeline} provided in {@link #onTimelineChanged(Timeline, int)} is only safe from
* within this method.
* <li>They are interested in events that logically happened together (e.g {@link
* #onPlaybackStateChanged(int)} to {@link #STATE_BUFFERING} because of {@link
* #onMediaItemTransition(MediaItem, int)}).
* </ul>
*
* @param player The {@link Player} whose state changed. Use the getters to obtain the latest
* states.
* @param events The {@link Events} that happened in this iteration, indicating which player
* states changed.
*/
default void onEvents(Player player, Events events) {}
/**
* Called when the timeline has been refreshed.
*
* <p>Note that the current {@link MediaItem} or playback position may change as a result of a
* timeline change. If playback can't continue smoothly because of this timeline change, a
* separate {@link #onPositionDiscontinuity(PositionInfo, PositionInfo, int)} callback will be
* triggered.
*
* <p>{@link #onEvents(Player, Events)} will also be called to report this event along with
* other events that happen in the same {@link Looper} message queue iteration.
*
* @param timeline The latest timeline. Never null, but may be empty.
* @param reason The {@link TimelineChangeReason} responsible for this timeline change.
*/
default void onTimelineChanged(Timeline timeline, @TimelineChangeReason int reason) {} default void onTimelineChanged(Timeline timeline, @TimelineChangeReason int reason) {}
/** @Override
* Called when playback transitions to a media item or starts repeating a media item according
* to the current {@link #getRepeatMode() repeat mode}.
*
* <p>Note that this callback is also called when the playlist becomes non-empty or empty as a
* consequence of a playlist change.
*
* <p>{@link #onEvents(Player, Events)} will also be called to report this event along with
* other events that happen in the same {@link Looper} message queue iteration.
*
* @param mediaItem The {@link MediaItem}. May be null if the playlist becomes empty.
* @param reason The reason for the transition.
*/
default void onMediaItemTransition( default void onMediaItemTransition(
@Nullable MediaItem mediaItem, @MediaItemTransitionReason int reason) {} @Nullable MediaItem mediaItem, @MediaItemTransitionReason int reason) {}
/** @Override
* Called when the available or selected tracks change.
*
* <p>{@link #onEvents(Player, Events)} will also be called to report this event along with
* other events that happen in the same {@link Looper} message queue iteration.
*
* @param trackGroups The available tracks. Never null, but may be of length zero.
* @param trackSelections The selected tracks. Never null, but may contain null elements. A
* concrete implementation may include null elements if it has a fixed number of renderer
* components, wishes to report a TrackSelection for each of them, and has one or more
* renderer components that is not assigned any selected tracks.
* @deprecated Use {@link #onTracksInfoChanged(TracksInfo)} instead.
*/
@UnstableApi
@Deprecated
default void onTracksChanged(
TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {}
/**
* Called when the available or selected tracks change.
*
* <p>{@link #onEvents(Player, Events)} will also be called to report this event along with
* other events that happen in the same {@link Looper} message queue iteration.
*
* @param tracksInfo The available tracks information. Never null, but may be of length zero.
*/
default void onTracksInfoChanged(TracksInfo tracksInfo) {} default void onTracksInfoChanged(TracksInfo tracksInfo) {}
/** @Override
* Called when the combined {@link MediaMetadata} changes.
*
* <p>The provided {@link MediaMetadata} is a combination of the {@link MediaItem#mediaMetadata}
* and the static and dynamic metadata from the {@link TrackSelection#getFormat(int) track
* selections' formats} and {@link Listener#onMetadata(Metadata)}. If a field is populated in
* the {@link MediaItem#mediaMetadata}, it will be prioritised above the same field coming from
* static or dynamic metadata.
*
* <p>This method may be called multiple times in quick succession.
*
* <p>{@link #onEvents(Player, Events)} will also be called to report this event along with
* other events that happen in the same {@link Looper} message queue iteration.
*
* @param mediaMetadata The combined {@link MediaMetadata}.
*/
default void onMediaMetadataChanged(MediaMetadata mediaMetadata) {}
/** Called when the playlist {@link MediaMetadata} changes. */
default void onPlaylistMetadataChanged(MediaMetadata mediaMetadata) {}
/**
* Called when the player starts or stops loading the source.
*
* <p>{@link #onEvents(Player, Events)} will also be called to report this event along with
* other events that happen in the same {@link Looper} message queue iteration.
*
* @param isLoading Whether the source is currently being loaded.
*/
default void onIsLoadingChanged(boolean isLoading) {} default void onIsLoadingChanged(boolean isLoading) {}
/** @deprecated Use {@link #onIsLoadingChanged(boolean)} instead. */ @Override
@Deprecated
@UnstableApi
default void onLoadingChanged(boolean isLoading) {}
/**
* Called when the value returned from {@link #isCommandAvailable(int)} changes for at least one
* {@link Command}.
*
* <p>{@link #onEvents(Player, Events)} will also be called to report this event along with
* other events that happen in the same {@link Looper} message queue iteration.
*
* @param availableCommands The available {@link Commands}.
*/
default void onAvailableCommandsChanged(Commands availableCommands) {} default void onAvailableCommandsChanged(Commands availableCommands) {}
/** @Override
* Called when the value returned from {@link #getTrackSelectionParameters()} changes.
*
* <p>{@link #onEvents(Player, Events)} will also be called to report this event along with
* other events that happen in the same {@link Looper} message queue iteration.
*
* @param parameters The new {@link TrackSelectionParameters}.
*/
default void onTrackSelectionParametersChanged(TrackSelectionParameters parameters) {}
/**
* @deprecated Use {@link #onPlaybackStateChanged(int)} and {@link
* #onPlayWhenReadyChanged(boolean, int)} instead.
*/
@Deprecated
@UnstableApi
default void onPlayerStateChanged(boolean playWhenReady, @State int playbackState) {}
/**
* Called when the value returned from {@link #getPlaybackState()} changes.
*
* <p>{@link #onEvents(Player, Events)} will also be called to report this event along with
* other events that happen in the same {@link Looper} message queue iteration.
*
* @param playbackState The new playback {@link State state}.
*/
default void onPlaybackStateChanged(@State int playbackState) {} default void onPlaybackStateChanged(@State int playbackState) {}
/** @Override
* Called when the value returned from {@link #getPlayWhenReady()} changes.
*
* <p>{@link #onEvents(Player, Events)} will also be called to report this event along with
* other events that happen in the same {@link Looper} message queue iteration.
*
* @param playWhenReady Whether playback will proceed when ready.
* @param reason The {@link PlayWhenReadyChangeReason reason} for the change.
*/
default void onPlayWhenReadyChanged( default void onPlayWhenReadyChanged(
boolean playWhenReady, @PlayWhenReadyChangeReason int reason) {} boolean playWhenReady, @PlayWhenReadyChangeReason int reason) {}
/** @Override
* Called when the value returned from {@link #getPlaybackSuppressionReason()} changes.
*
* <p>{@link #onEvents(Player, Events)} will also be called to report this event along with
* other events that happen in the same {@link Looper} message queue iteration.
*
* @param playbackSuppressionReason The current {@link PlaybackSuppressionReason}.
*/
default void onPlaybackSuppressionReasonChanged( default void onPlaybackSuppressionReasonChanged(
@PlaybackSuppressionReason int playbackSuppressionReason) {} @PlaybackSuppressionReason int playbackSuppressionReason) {}
/** @Override
* Called when the value of {@link #isPlaying()} changes.
*
* <p>{@link #onEvents(Player, Events)} will also be called to report this event along with
* other events that happen in the same {@link Looper} message queue iteration.
*
* @param isPlaying Whether the player is playing.
*/
default void onIsPlayingChanged(boolean isPlaying) {} default void onIsPlayingChanged(boolean isPlaying) {}
/** @Override
* Called when the value of {@link #getRepeatMode()} changes.
*
* <p>{@link #onEvents(Player, Events)} will also be called to report this event along with
* other events that happen in the same {@link Looper} message queue iteration.
*
* @param repeatMode The {@link RepeatMode} used for playback.
*/
default void onRepeatModeChanged(@RepeatMode int repeatMode) {} default void onRepeatModeChanged(@RepeatMode int repeatMode) {}
/** @Override
* Called when the value of {@link #getShuffleModeEnabled()} changes.
*
* <p>{@link #onEvents(Player, Events)} will also be called to report this event along with
* other events that happen in the same {@link Looper} message queue iteration.
*
* @param shuffleModeEnabled Whether shuffling of {@link MediaItem media items} is enabled.
*/
default void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {} default void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {}
/** @Override
* Called when an error occurs. The playback state will transition to {@link #STATE_IDLE}
* immediately after this method is called. The player instance can still be used, and {@link
* #release()} must still be called on the player should it no longer be required.
*
* <p>{@link #onEvents(Player, Events)} will also be called to report this event along with
* other events that happen in the same {@link Looper} message queue iteration.
*
* <p>Implementations of Player may pass an instance of a subclass of {@link PlaybackException}
* to this method in order to include more information about the error.
*
* @param error The error.
*/
default void onPlayerError(PlaybackException error) {} default void onPlayerError(PlaybackException error) {}
/** @Override
* Called when the {@link PlaybackException} returned by {@link #getPlayerError()} changes.
*
* <p>{@link #onEvents(Player, Events)} will also be called to report this event along with
* other events that happen in the same {@link Looper} message queue iteration.
*
* <p>Implementations of Player may pass an instance of a subclass of {@link PlaybackException}
* to this method in order to include more information about the error.
*
* @param error The new error, or null if the error is being cleared.
*/
default void onPlayerErrorChanged(@Nullable PlaybackException error) {} default void onPlayerErrorChanged(@Nullable PlaybackException error) {}
/** @Override
* @deprecated Use {@link #onPositionDiscontinuity(PositionInfo, PositionInfo, int)} instead.
*/
@Deprecated
@UnstableApi
default void onPositionDiscontinuity(@DiscontinuityReason int reason) {}
/**
* Called when a position discontinuity occurs.
*
* <p>A position discontinuity occurs when the playing period changes, the playback position
* jumps within the period currently being played, or when the playing period has been skipped
* or removed.
*
* <p>{@link #onEvents(Player, Events)} will also be called to report this event along with
* other events that happen in the same {@link Looper} message queue iteration.
*
* @param oldPosition The position before the discontinuity.
* @param newPosition The position after the discontinuity.
* @param reason The {@link DiscontinuityReason} responsible for the discontinuity.
*/
default void onPositionDiscontinuity( default void onPositionDiscontinuity(
PositionInfo oldPosition, PositionInfo newPosition, @DiscontinuityReason int reason) {} PositionInfo oldPosition, PositionInfo newPosition, @DiscontinuityReason int reason) {}
/** @Override
* Called when the current playback parameters change. The playback parameters may change due to
* a call to {@link #setPlaybackParameters(PlaybackParameters)}, or the player itself may change
* them (for example, if audio playback switches to passthrough or offload mode, where speed
* adjustment is no longer possible).
*
* <p>{@link #onEvents(Player, Events)} will also be called to report this event along with
* other events that happen in the same {@link Looper} message queue iteration.
*
* @param playbackParameters The playback parameters.
*/
default void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {} default void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {}
/** @Override
* Called when the value of {@link #getSeekBackIncrement()} changes.
*
* <p>{@link #onEvents(Player, Events)} will also be called to report this event along with
* other events that happen in the same {@link Looper} message queue iteration.
*
* @param seekBackIncrementMs The {@link #seekBack()} increment, in milliseconds.
*/
default void onSeekBackIncrementChanged(long seekBackIncrementMs) {}
/**
* Called when the value of {@link #getSeekForwardIncrement()} changes.
*
* <p>{@link #onEvents(Player, Events)} will also be called to report this event along with
* other events that happen in the same {@link Looper} message queue iteration.
*
* @param seekForwardIncrementMs The {@link #seekForward()} increment, in milliseconds.
*/
default void onSeekForwardIncrementChanged(long seekForwardIncrementMs) {} default void onSeekForwardIncrementChanged(long seekForwardIncrementMs) {}
/** @Override
* Called when the value of {@link #getMaxSeekToPreviousPosition()} changes. default void onSeekBackIncrementChanged(long seekBackIncrementMs) {}
*
* <p>{@link #onEvents(Player, Events)} will also be called to report this event along with
* other events that happen in the same {@link Looper} message queue iteration.
*
* @param maxSeekToPreviousPositionMs The maximum position for which {@link #seekToPrevious()}
* seeks to the previous position, in milliseconds.
*/
default void onMaxSeekToPreviousPositionChanged(long maxSeekToPreviousPositionMs) {}
/**
* @deprecated Seeks are processed without delay. Listen to {@link
* #onPositionDiscontinuity(PositionInfo, PositionInfo, int)} with reason {@link
* #DISCONTINUITY_REASON_SEEK} instead.
*/
@Deprecated
@UnstableApi
default void onSeekProcessed() {}
/** /**
* Called when the audio session ID changes. * Called when the audio session ID changes.
@ -964,6 +1031,9 @@ public interface Player {
/** Called when the device volume or mute state changes. */ /** Called when the device volume or mute state changes. */
default void onDeviceVolumeChanged(int volume, boolean muted) {} default void onDeviceVolumeChanged(int volume, boolean muted) {}
@Override
default void onEvents(Player player, Events events) {}
/** /**
* Called each time there's a change in the size of the video being rendered. * Called each time there's a change in the size of the video being rendered.
* *
@ -1005,6 +1075,12 @@ public interface Player {
*/ */
@UnstableApi @UnstableApi
default void onMetadata(Metadata metadata) {} default void onMetadata(Metadata metadata) {}
@Override
default void onMediaMetadataChanged(MediaMetadata mediaMetadata) {}
@Override
default void onPlaylistMetadataChanged(MediaMetadata mediaMetadata) {}
} }
/** /**

View File

@ -115,6 +115,21 @@ public class ForwardingPlayerTest {
} }
} }
@Test
@SuppressWarnings("deprecation") // Testing backwards compatibility with deprecated type.
public void forwardingEventListener_overridesAllEventListenerMethods() throws Exception {
// Check with reflection that ForwardingListener overrides all Listener methods.
Class<?> forwardingListenerClass = getInnerClass("ForwardingEventListener");
List<Method> methods = getPublicMethods(Player.EventListener.class);
for (int i = 0; i < methods.size(); i++) {
Method method = methods.get(i);
assertThat(
forwardingListenerClass.getDeclaredMethod(
method.getName(), method.getParameterTypes()))
.isNotNull();
}
}
@Test @Test
public void forwardingListener_overridesAllListenerMethods() throws Exception { public void forwardingListener_overridesAllListenerMethods() throws Exception {
// Check with reflection that ForwardingListener overrides all Listener methods. // Check with reflection that ForwardingListener overrides all Listener methods.

View File

@ -1081,6 +1081,32 @@ public interface ExoPlayer extends Player {
@Deprecated @Deprecated
DeviceComponent getDeviceComponent(); DeviceComponent getDeviceComponent();
/**
* Registers a listener to receive events from the player.
*
* <p>The listener's methods will be called on the thread associated with {@link
* #getApplicationLooper()}.
*
* @param listener The listener to register.
* @deprecated Use {@link #addListener(Listener)} and {@link #removeListener(Listener)} instead.
*/
@UnstableApi
@Deprecated
@SuppressWarnings("deprecation")
void addListener(EventListener listener);
/**
* Unregister a listener registered through {@link #addListener(EventListener)}. The listener will
* no longer receive events from the player.
*
* @param listener The listener to unregister.
* @deprecated Use {@link #addListener(Listener)} and {@link #removeListener(Listener)} instead.
*/
@UnstableApi
@Deprecated
@SuppressWarnings("deprecation")
void removeListener(EventListener listener);
/** /**
* Adds a listener to receive audio offload events. * Adds a listener to receive audio offload events.
* *

View File

@ -109,6 +109,7 @@ import androidx.media3.common.PlaybackParameters;
import androidx.media3.common.Player; import androidx.media3.common.Player;
import androidx.media3.common.Player.Commands; import androidx.media3.common.Player.Commands;
import androidx.media3.common.Player.DiscontinuityReason; import androidx.media3.common.Player.DiscontinuityReason;
import androidx.media3.common.Player.EventListener;
import androidx.media3.common.Player.Events; import androidx.media3.common.Player.Events;
import androidx.media3.common.Player.Listener; import androidx.media3.common.Player.Listener;
import androidx.media3.common.Player.PlayWhenReadyChangeReason; import androidx.media3.common.Player.PlayWhenReadyChangeReason;
@ -190,9 +191,9 @@ import java.util.concurrent.TimeoutException;
private final ExoPlayerImplInternal.PlaybackInfoUpdateListener playbackInfoUpdateListener; private final ExoPlayerImplInternal.PlaybackInfoUpdateListener playbackInfoUpdateListener;
private final ExoPlayerImplInternal internalPlayer; private final ExoPlayerImplInternal internalPlayer;
private final ListenerSet<Listener> listeners; @SuppressWarnings("deprecation") // TODO(b/187152483): Merge with non-deprecated listeners.
// TODO(b/187152483): Remove this once all events are dispatched via ListenerSet. private final ListenerSet<Player.EventListener> eventListeners;
private final CopyOnWriteArraySet<Listener> listenerArraySet;
private final CopyOnWriteArraySet<AudioOffloadListener> audioOffloadListeners; private final CopyOnWriteArraySet<AudioOffloadListener> audioOffloadListeners;
private final Timeline.Period period; private final Timeline.Period period;
private final Timeline.Window window; private final Timeline.Window window;
@ -207,6 +208,7 @@ import java.util.concurrent.TimeoutException;
private final Clock clock; private final Clock clock;
private final ComponentListener componentListener; private final ComponentListener componentListener;
private final FrameMetadataListener frameMetadataListener; private final FrameMetadataListener frameMetadataListener;
private final CopyOnWriteArraySet<Listener> listeners;
private final AudioBecomingNoisyManager audioBecomingNoisyManager; private final AudioBecomingNoisyManager audioBecomingNoisyManager;
private final AudioFocusManager audioFocusManager; private final AudioFocusManager audioFocusManager;
private final StreamVolumeManager streamVolumeManager; private final StreamVolumeManager streamVolumeManager;
@ -292,6 +294,7 @@ import java.util.concurrent.TimeoutException;
detachSurfaceTimeoutMs = builder.detachSurfaceTimeoutMs; detachSurfaceTimeoutMs = builder.detachSurfaceTimeoutMs;
componentListener = new ComponentListener(); componentListener = new ComponentListener();
frameMetadataListener = new FrameMetadataListener(); frameMetadataListener = new FrameMetadataListener();
listeners = new CopyOnWriteArraySet<>();
Handler eventHandler = new Handler(builder.looper); Handler eventHandler = new Handler(builder.looper);
renderers = renderers =
builder builder
@ -315,12 +318,11 @@ import java.util.concurrent.TimeoutException;
this.applicationLooper = builder.looper; this.applicationLooper = builder.looper;
this.clock = builder.clock; this.clock = builder.clock;
this.wrappingPlayer = wrappingPlayer; this.wrappingPlayer = wrappingPlayer;
listeners = eventListeners =
new ListenerSet<>( new ListenerSet<>(
applicationLooper, applicationLooper,
clock, clock,
(listener, flags) -> listener.onEvents(wrappingPlayer, new Events(flags))); (listener, flags) -> listener.onEvents(wrappingPlayer, new Events(flags)));
listenerArraySet = new CopyOnWriteArraySet<>();
audioOffloadListeners = new CopyOnWriteArraySet<>(); audioOffloadListeners = new CopyOnWriteArraySet<>();
mediaSourceHolderSnapshots = new ArrayList<>(); mediaSourceHolderSnapshots = new ArrayList<>();
shuffleOrder = new ShuffleOrder.DefaultShuffleOrder(/* length= */ 0); shuffleOrder = new ShuffleOrder.DefaultShuffleOrder(/* length= */ 0);
@ -408,9 +410,9 @@ import java.util.concurrent.TimeoutException;
currentCues = ImmutableList.of(); currentCues = ImmutableList.of();
throwsWhenUsingWrongThread = true; throwsWhenUsingWrongThread = true;
listeners.add(analyticsCollector); addEventListener(analyticsCollector);
bandwidthMeter.addEventListener(new Handler(applicationLooper), analyticsCollector); bandwidthMeter.addEventListener(new Handler(applicationLooper), analyticsCollector);
listeners.add(componentListener); addEventListener(componentListener);
addAudioOffloadListener(componentListener); addAudioOffloadListener(componentListener);
if (builder.foregroundModeTimeoutMs > 0) { if (builder.foregroundModeTimeoutMs > 0) {
experimentalSetForegroundModeTimeoutMs(builder.foregroundModeTimeoutMs); experimentalSetForegroundModeTimeoutMs(builder.foregroundModeTimeoutMs);
@ -486,6 +488,18 @@ import java.util.concurrent.TimeoutException;
return clock; return clock;
} }
@SuppressWarnings("deprecation") // Register deprecated EventListener.
public void addEventListener(Player.EventListener eventListener) {
// Don't verify application thread. We allow calls to this method from any thread.
eventListeners.add(eventListener);
}
@SuppressWarnings("deprecation") // Deregister deprecated EventListener.
public void removeEventListener(Player.EventListener eventListener) {
// Don't verify application thread. We allow calls to this method from any thread.
eventListeners.remove(eventListener);
}
public void addAudioOffloadListener(AudioOffloadListener listener) { public void addAudioOffloadListener(AudioOffloadListener listener) {
// Don't verify application thread. We allow calls to this method from any thread. // Don't verify application thread. We allow calls to this method from any thread.
audioOffloadListeners.add(listener); audioOffloadListeners.add(listener);
@ -791,10 +805,10 @@ import java.util.concurrent.TimeoutException;
if (this.repeatMode != repeatMode) { if (this.repeatMode != repeatMode) {
this.repeatMode = repeatMode; this.repeatMode = repeatMode;
internalPlayer.setRepeatMode(repeatMode); internalPlayer.setRepeatMode(repeatMode);
listeners.queueEvent( eventListeners.queueEvent(
Player.EVENT_REPEAT_MODE_CHANGED, listener -> listener.onRepeatModeChanged(repeatMode)); Player.EVENT_REPEAT_MODE_CHANGED, listener -> listener.onRepeatModeChanged(repeatMode));
updateAvailableCommands(); updateAvailableCommands();
listeners.flushEvents(); eventListeners.flushEvents();
} }
} }
@ -808,11 +822,11 @@ import java.util.concurrent.TimeoutException;
if (this.shuffleModeEnabled != shuffleModeEnabled) { if (this.shuffleModeEnabled != shuffleModeEnabled) {
this.shuffleModeEnabled = shuffleModeEnabled; this.shuffleModeEnabled = shuffleModeEnabled;
internalPlayer.setShuffleModeEnabled(shuffleModeEnabled); internalPlayer.setShuffleModeEnabled(shuffleModeEnabled);
listeners.queueEvent( eventListeners.queueEvent(
Player.EVENT_SHUFFLE_MODE_ENABLED_CHANGED, Player.EVENT_SHUFFLE_MODE_ENABLED_CHANGED,
listener -> listener.onShuffleModeEnabledChanged(shuffleModeEnabled)); listener -> listener.onShuffleModeEnabledChanged(shuffleModeEnabled));
updateAvailableCommands(); updateAvailableCommands();
listeners.flushEvents(); eventListeners.flushEvents();
} }
} }
@ -1014,7 +1028,7 @@ import java.util.concurrent.TimeoutException;
audioFocusManager.release(); audioFocusManager.release();
if (!internalPlayer.release()) { if (!internalPlayer.release()) {
// One of the renderers timed out releasing its resources. // One of the renderers timed out releasing its resources.
listeners.sendEvent( eventListeners.sendEvent(
Player.EVENT_PLAYER_ERROR, Player.EVENT_PLAYER_ERROR,
listener -> listener ->
listener.onPlayerError( listener.onPlayerError(
@ -1022,7 +1036,7 @@ import java.util.concurrent.TimeoutException;
new ExoTimeoutException(ExoTimeoutException.TIMEOUT_OPERATION_RELEASE), new ExoTimeoutException(ExoTimeoutException.TIMEOUT_OPERATION_RELEASE),
PlaybackException.ERROR_CODE_TIMEOUT))); PlaybackException.ERROR_CODE_TIMEOUT)));
} }
listeners.release(); eventListeners.release();
playbackInfoUpdateHandler.removeCallbacksAndMessages(null); playbackInfoUpdateHandler.removeCallbacksAndMessages(null);
bandwidthMeter.removeEventListener(analyticsCollector); bandwidthMeter.removeEventListener(analyticsCollector);
playbackInfo = playbackInfo.copyWithPlaybackState(Player.STATE_IDLE); playbackInfo = playbackInfo.copyWithPlaybackState(Player.STATE_IDLE);
@ -1202,7 +1216,7 @@ import java.util.concurrent.TimeoutException;
return; return;
} }
trackSelector.setParameters(parameters); trackSelector.setParameters(parameters);
listeners.queueEvent( eventListeners.queueEvent(
EVENT_TRACK_SELECTION_PARAMETERS_CHANGED, EVENT_TRACK_SELECTION_PARAMETERS_CHANGED,
listener -> listener.onTrackSelectionParametersChanged(parameters)); listener -> listener.onTrackSelectionParametersChanged(parameters));
} }
@ -1222,7 +1236,7 @@ import java.util.concurrent.TimeoutException;
return; return;
} }
mediaMetadata = newMediaMetadata; mediaMetadata = newMediaMetadata;
listeners.sendEvent( eventListeners.sendEvent(
EVENT_MEDIA_METADATA_CHANGED, listener -> listener.onMediaMetadataChanged(mediaMetadata)); EVENT_MEDIA_METADATA_CHANGED, listener -> listener.onMediaMetadataChanged(mediaMetadata));
} }
@ -1238,7 +1252,7 @@ import java.util.concurrent.TimeoutException;
return; return;
} }
this.playlistMetadata = playlistMetadata; this.playlistMetadata = playlistMetadata;
listeners.sendEvent( eventListeners.sendEvent(
EVENT_PLAYLIST_METADATA_CHANGED, EVENT_PLAYLIST_METADATA_CHANGED,
listener -> listener.onPlaylistMetadataChanged(this.playlistMetadata)); listener -> listener.onPlaylistMetadataChanged(this.playlistMetadata));
} }
@ -1395,7 +1409,7 @@ import java.util.concurrent.TimeoutException;
streamVolumeManager.setStreamType(Util.getStreamTypeForAudioUsage(audioAttributes.usage)); streamVolumeManager.setStreamType(Util.getStreamTypeForAudioUsage(audioAttributes.usage));
analyticsCollector.onAudioAttributesChanged(audioAttributes); analyticsCollector.onAudioAttributesChanged(audioAttributes);
// TODO(internal b/187152483): Events should be dispatched via ListenerSet // TODO(internal b/187152483): Events should be dispatched via ListenerSet
for (Listener listener : listenerArraySet) { for (Listener listener : listeners) {
listener.onAudioAttributesChanged(audioAttributes); listener.onAudioAttributesChanged(audioAttributes);
} }
} }
@ -1433,7 +1447,7 @@ import java.util.concurrent.TimeoutException;
sendRendererMessage(TRACK_TYPE_VIDEO, MSG_SET_AUDIO_SESSION_ID, audioSessionId); sendRendererMessage(TRACK_TYPE_VIDEO, MSG_SET_AUDIO_SESSION_ID, audioSessionId);
analyticsCollector.onAudioSessionIdChanged(audioSessionId); analyticsCollector.onAudioSessionIdChanged(audioSessionId);
// TODO(internal b/187152483): Events should be dispatched via ListenerSet // TODO(internal b/187152483): Events should be dispatched via ListenerSet
for (Listener listener : listenerArraySet) { for (Listener listener : listeners) {
listener.onAudioSessionIdChanged(audioSessionId); listener.onAudioSessionIdChanged(audioSessionId);
} }
} }
@ -1461,7 +1475,7 @@ import java.util.concurrent.TimeoutException;
sendVolumeToRenderers(); sendVolumeToRenderers();
analyticsCollector.onVolumeChanged(volume); analyticsCollector.onVolumeChanged(volume);
// TODO(internal b/187152483): Events should be dispatched via ListenerSet // TODO(internal b/187152483): Events should be dispatched via ListenerSet
for (Listener listener : listenerArraySet) { for (Listener listener : listeners) {
listener.onVolumeChanged(volume); listener.onVolumeChanged(volume);
} }
} }
@ -1593,14 +1607,14 @@ import java.util.concurrent.TimeoutException;
// Don't verify application thread. We allow calls to this method from any thread. // Don't verify application thread. We allow calls to this method from any thread.
checkNotNull(listener); checkNotNull(listener);
listeners.add(listener); listeners.add(listener);
listenerArraySet.add(listener); addEventListener(listener);
} }
public void removeListener(Listener listener) { public void removeListener(Listener listener) {
// Don't verify application thread. We allow calls to this method from any thread. // Don't verify application thread. We allow calls to this method from any thread.
checkNotNull(listener); checkNotNull(listener);
listeners.remove(listener); listeners.remove(listener);
listenerArraySet.remove(listener); removeEventListener(listener);
} }
public void setHandleWakeLock(boolean handleWakeLock) { public void setHandleWakeLock(boolean handleWakeLock) {
@ -1801,7 +1815,7 @@ import java.util.concurrent.TimeoutException;
mediaMetadata = newMediaMetadata; mediaMetadata = newMediaMetadata;
if (!previousPlaybackInfo.timeline.equals(newPlaybackInfo.timeline)) { if (!previousPlaybackInfo.timeline.equals(newPlaybackInfo.timeline)) {
listeners.queueEvent( eventListeners.queueEvent(
Player.EVENT_TIMELINE_CHANGED, Player.EVENT_TIMELINE_CHANGED,
listener -> listener.onTimelineChanged(newPlaybackInfo.timeline, timelineChangeReason)); listener -> listener.onTimelineChanged(newPlaybackInfo.timeline, timelineChangeReason));
} }
@ -1810,7 +1824,7 @@ import java.util.concurrent.TimeoutException;
getPreviousPositionInfo( getPreviousPositionInfo(
positionDiscontinuityReason, previousPlaybackInfo, oldMaskingMediaItemIndex); positionDiscontinuityReason, previousPlaybackInfo, oldMaskingMediaItemIndex);
PositionInfo positionInfo = getPositionInfo(discontinuityWindowStartPositionUs); PositionInfo positionInfo = getPositionInfo(discontinuityWindowStartPositionUs);
listeners.queueEvent( eventListeners.queueEvent(
Player.EVENT_POSITION_DISCONTINUITY, Player.EVENT_POSITION_DISCONTINUITY,
listener -> { listener -> {
listener.onPositionDiscontinuity(positionDiscontinuityReason); listener.onPositionDiscontinuity(positionDiscontinuityReason);
@ -1820,16 +1834,16 @@ import java.util.concurrent.TimeoutException;
} }
if (mediaItemTransitioned) { if (mediaItemTransitioned) {
@Nullable final MediaItem finalMediaItem = mediaItem; @Nullable final MediaItem finalMediaItem = mediaItem;
listeners.queueEvent( eventListeners.queueEvent(
Player.EVENT_MEDIA_ITEM_TRANSITION, Player.EVENT_MEDIA_ITEM_TRANSITION,
listener -> listener.onMediaItemTransition(finalMediaItem, mediaItemTransitionReason)); listener -> listener.onMediaItemTransition(finalMediaItem, mediaItemTransitionReason));
} }
if (previousPlaybackInfo.playbackError != newPlaybackInfo.playbackError) { if (previousPlaybackInfo.playbackError != newPlaybackInfo.playbackError) {
listeners.queueEvent( eventListeners.queueEvent(
Player.EVENT_PLAYER_ERROR, Player.EVENT_PLAYER_ERROR,
listener -> listener.onPlayerErrorChanged(newPlaybackInfo.playbackError)); listener -> listener.onPlayerErrorChanged(newPlaybackInfo.playbackError));
if (newPlaybackInfo.playbackError != null) { if (newPlaybackInfo.playbackError != null) {
listeners.queueEvent( eventListeners.queueEvent(
Player.EVENT_PLAYER_ERROR, Player.EVENT_PLAYER_ERROR,
listener -> listener.onPlayerError(newPlaybackInfo.playbackError)); listener -> listener.onPlayerError(newPlaybackInfo.playbackError));
} }
@ -1838,21 +1852,21 @@ import java.util.concurrent.TimeoutException;
trackSelector.onSelectionActivated(newPlaybackInfo.trackSelectorResult.info); trackSelector.onSelectionActivated(newPlaybackInfo.trackSelectorResult.info);
TrackSelectionArray newSelection = TrackSelectionArray newSelection =
new TrackSelectionArray(newPlaybackInfo.trackSelectorResult.selections); new TrackSelectionArray(newPlaybackInfo.trackSelectorResult.selections);
listeners.queueEvent( eventListeners.queueEvent(
Player.EVENT_TRACKS_CHANGED, Player.EVENT_TRACKS_CHANGED,
listener -> listener.onTracksChanged(newPlaybackInfo.trackGroups, newSelection)); listener -> listener.onTracksChanged(newPlaybackInfo.trackGroups, newSelection));
listeners.queueEvent( eventListeners.queueEvent(
Player.EVENT_TRACKS_CHANGED, Player.EVENT_TRACKS_CHANGED,
listener -> listener.onTracksInfoChanged(newPlaybackInfo.trackSelectorResult.tracksInfo)); listener -> listener.onTracksInfoChanged(newPlaybackInfo.trackSelectorResult.tracksInfo));
} }
if (metadataChanged) { if (metadataChanged) {
final MediaMetadata finalMediaMetadata = mediaMetadata; final MediaMetadata finalMediaMetadata = mediaMetadata;
listeners.queueEvent( eventListeners.queueEvent(
EVENT_MEDIA_METADATA_CHANGED, EVENT_MEDIA_METADATA_CHANGED,
listener -> listener.onMediaMetadataChanged(finalMediaMetadata)); listener -> listener.onMediaMetadataChanged(finalMediaMetadata));
} }
if (previousPlaybackInfo.isLoading != newPlaybackInfo.isLoading) { if (previousPlaybackInfo.isLoading != newPlaybackInfo.isLoading) {
listeners.queueEvent( eventListeners.queueEvent(
Player.EVENT_IS_LOADING_CHANGED, Player.EVENT_IS_LOADING_CHANGED,
listener -> { listener -> {
listener.onLoadingChanged(newPlaybackInfo.isLoading); listener.onLoadingChanged(newPlaybackInfo.isLoading);
@ -1861,19 +1875,19 @@ import java.util.concurrent.TimeoutException;
} }
if (previousPlaybackInfo.playbackState != newPlaybackInfo.playbackState if (previousPlaybackInfo.playbackState != newPlaybackInfo.playbackState
|| previousPlaybackInfo.playWhenReady != newPlaybackInfo.playWhenReady) { || previousPlaybackInfo.playWhenReady != newPlaybackInfo.playWhenReady) {
listeners.queueEvent( eventListeners.queueEvent(
/* eventFlag= */ C.INDEX_UNSET, /* eventFlag= */ C.INDEX_UNSET,
listener -> listener ->
listener.onPlayerStateChanged( listener.onPlayerStateChanged(
newPlaybackInfo.playWhenReady, newPlaybackInfo.playbackState)); newPlaybackInfo.playWhenReady, newPlaybackInfo.playbackState));
} }
if (previousPlaybackInfo.playbackState != newPlaybackInfo.playbackState) { if (previousPlaybackInfo.playbackState != newPlaybackInfo.playbackState) {
listeners.queueEvent( eventListeners.queueEvent(
Player.EVENT_PLAYBACK_STATE_CHANGED, Player.EVENT_PLAYBACK_STATE_CHANGED,
listener -> listener.onPlaybackStateChanged(newPlaybackInfo.playbackState)); listener -> listener.onPlaybackStateChanged(newPlaybackInfo.playbackState));
} }
if (previousPlaybackInfo.playWhenReady != newPlaybackInfo.playWhenReady) { if (previousPlaybackInfo.playWhenReady != newPlaybackInfo.playWhenReady) {
listeners.queueEvent( eventListeners.queueEvent(
Player.EVENT_PLAY_WHEN_READY_CHANGED, Player.EVENT_PLAY_WHEN_READY_CHANGED,
listener -> listener ->
listener.onPlayWhenReadyChanged( listener.onPlayWhenReadyChanged(
@ -1881,27 +1895,27 @@ import java.util.concurrent.TimeoutException;
} }
if (previousPlaybackInfo.playbackSuppressionReason if (previousPlaybackInfo.playbackSuppressionReason
!= newPlaybackInfo.playbackSuppressionReason) { != newPlaybackInfo.playbackSuppressionReason) {
listeners.queueEvent( eventListeners.queueEvent(
Player.EVENT_PLAYBACK_SUPPRESSION_REASON_CHANGED, Player.EVENT_PLAYBACK_SUPPRESSION_REASON_CHANGED,
listener -> listener ->
listener.onPlaybackSuppressionReasonChanged( listener.onPlaybackSuppressionReasonChanged(
newPlaybackInfo.playbackSuppressionReason)); newPlaybackInfo.playbackSuppressionReason));
} }
if (isPlaying(previousPlaybackInfo) != isPlaying(newPlaybackInfo)) { if (isPlaying(previousPlaybackInfo) != isPlaying(newPlaybackInfo)) {
listeners.queueEvent( eventListeners.queueEvent(
Player.EVENT_IS_PLAYING_CHANGED, Player.EVENT_IS_PLAYING_CHANGED,
listener -> listener.onIsPlayingChanged(isPlaying(newPlaybackInfo))); listener -> listener.onIsPlayingChanged(isPlaying(newPlaybackInfo)));
} }
if (!previousPlaybackInfo.playbackParameters.equals(newPlaybackInfo.playbackParameters)) { if (!previousPlaybackInfo.playbackParameters.equals(newPlaybackInfo.playbackParameters)) {
listeners.queueEvent( eventListeners.queueEvent(
Player.EVENT_PLAYBACK_PARAMETERS_CHANGED, Player.EVENT_PLAYBACK_PARAMETERS_CHANGED,
listener -> listener.onPlaybackParametersChanged(newPlaybackInfo.playbackParameters)); listener -> listener.onPlaybackParametersChanged(newPlaybackInfo.playbackParameters));
} }
if (seekProcessed) { if (seekProcessed) {
listeners.queueEvent(/* eventFlag= */ C.INDEX_UNSET, Listener::onSeekProcessed); eventListeners.queueEvent(/* eventFlag= */ C.INDEX_UNSET, EventListener::onSeekProcessed);
} }
updateAvailableCommands(); updateAvailableCommands();
listeners.flushEvents(); eventListeners.flushEvents();
if (previousPlaybackInfo.offloadSchedulingEnabled != newPlaybackInfo.offloadSchedulingEnabled) { if (previousPlaybackInfo.offloadSchedulingEnabled != newPlaybackInfo.offloadSchedulingEnabled) {
for (AudioOffloadListener listener : audioOffloadListeners) { for (AudioOffloadListener listener : audioOffloadListeners) {
@ -2058,7 +2072,7 @@ import java.util.concurrent.TimeoutException;
Commands previousAvailableCommands = availableCommands; Commands previousAvailableCommands = availableCommands;
availableCommands = Util.getAvailableCommands(wrappingPlayer, permanentAvailableCommands); availableCommands = Util.getAvailableCommands(wrappingPlayer, permanentAvailableCommands);
if (!availableCommands.equals(previousAvailableCommands)) { if (!availableCommands.equals(previousAvailableCommands)) {
listeners.queueEvent( eventListeners.queueEvent(
Player.EVENT_AVAILABLE_COMMANDS_CHANGED, Player.EVENT_AVAILABLE_COMMANDS_CHANGED,
listener -> listener.onAvailableCommandsChanged(availableCommands)); listener -> listener.onAvailableCommandsChanged(availableCommands));
} }
@ -2478,7 +2492,7 @@ import java.util.concurrent.TimeoutException;
surfaceHeight = height; surfaceHeight = height;
analyticsCollector.onSurfaceSizeChanged(width, height); analyticsCollector.onSurfaceSizeChanged(width, height);
// TODO(internal b/187152483): Events should be dispatched via ListenerSet // TODO(internal b/187152483): Events should be dispatched via ListenerSet
for (Listener listener : listenerArraySet) { for (Listener listener : listeners) {
listener.onSurfaceSizeChanged(width, height); listener.onSurfaceSizeChanged(width, height);
} }
} }
@ -2492,7 +2506,7 @@ import java.util.concurrent.TimeoutException;
private void notifySkipSilenceEnabledChanged() { private void notifySkipSilenceEnabledChanged() {
analyticsCollector.onSkipSilenceEnabledChanged(skipSilenceEnabled); analyticsCollector.onSkipSilenceEnabledChanged(skipSilenceEnabled);
// TODO(internal b/187152483): Events should be dispatched via ListenerSet // TODO(internal b/187152483): Events should be dispatched via ListenerSet
for (Listener listener : listenerArraySet) { for (Listener listener : listeners) {
listener.onSkipSilenceEnabledChanged(skipSilenceEnabled); listener.onSkipSilenceEnabledChanged(skipSilenceEnabled);
} }
} }
@ -2634,9 +2648,10 @@ import java.util.concurrent.TimeoutException;
} }
} }
// TODO(b/204189802): Remove self-listening to deprecated EventListener.
@SuppressWarnings("deprecation")
private final class ComponentListener private final class ComponentListener
implements Player.Listener, implements VideoRendererEventListener,
VideoRendererEventListener,
AudioRendererEventListener, AudioRendererEventListener,
TextOutput, TextOutput,
MetadataOutput, MetadataOutput,
@ -2646,6 +2661,7 @@ import java.util.concurrent.TimeoutException;
AudioFocusManager.PlayerControl, AudioFocusManager.PlayerControl,
AudioBecomingNoisyManager.EventListener, AudioBecomingNoisyManager.EventListener,
StreamVolumeManager.Listener, StreamVolumeManager.Listener,
Player.EventListener,
AudioOffloadListener { AudioOffloadListener {
// VideoRendererEventListener implementation // VideoRendererEventListener implementation
@ -2680,7 +2696,7 @@ import java.util.concurrent.TimeoutException;
ExoPlayerImpl.this.videoSize = videoSize; ExoPlayerImpl.this.videoSize = videoSize;
analyticsCollector.onVideoSizeChanged(videoSize); analyticsCollector.onVideoSizeChanged(videoSize);
// TODO(internal b/187152483): Events should be dispatched via ListenerSet // TODO(internal b/187152483): Events should be dispatched via ListenerSet
for (Listener listener : listenerArraySet) { for (Listener listener : listeners) {
listener.onVideoSizeChanged(videoSize); listener.onVideoSizeChanged(videoSize);
} }
} }
@ -2690,7 +2706,7 @@ import java.util.concurrent.TimeoutException;
analyticsCollector.onRenderedFirstFrame(output, renderTimeMs); analyticsCollector.onRenderedFirstFrame(output, renderTimeMs);
if (videoOutput == output) { if (videoOutput == output) {
// TODO(internal b/187152483): Events should be dispatched via ListenerSet // TODO(internal b/187152483): Events should be dispatched via ListenerSet
for (Listener listener : listenerArraySet) { for (Listener listener : listeners) {
listener.onRenderedFirstFrame(); listener.onRenderedFirstFrame();
} }
} }
@ -2787,7 +2803,7 @@ import java.util.concurrent.TimeoutException;
public void onCues(List<Cue> cues) { public void onCues(List<Cue> cues) {
currentCues = cues; currentCues = cues;
// TODO(internal b/187152483): Events should be dispatched via ListenerSet // TODO(internal b/187152483): Events should be dispatched via ListenerSet
for (Listener listeners : listenerArraySet) { for (Listener listeners : listeners) {
listeners.onCues(cues); listeners.onCues(cues);
} }
} }
@ -2799,7 +2815,7 @@ import java.util.concurrent.TimeoutException;
analyticsCollector.onMetadata(metadata); analyticsCollector.onMetadata(metadata);
ExoPlayerImpl.this.onMetadata(metadata); ExoPlayerImpl.this.onMetadata(metadata);
// TODO(internal b/187152483): Events should be dispatched via ListenerSet // TODO(internal b/187152483): Events should be dispatched via ListenerSet
for (Listener listener : listenerArraySet) { for (Listener listener : listeners) {
listener.onMetadata(metadata); listener.onMetadata(metadata);
} }
} }
@ -2895,7 +2911,7 @@ import java.util.concurrent.TimeoutException;
if (!deviceInfo.equals(ExoPlayerImpl.this.deviceInfo)) { if (!deviceInfo.equals(ExoPlayerImpl.this.deviceInfo)) {
ExoPlayerImpl.this.deviceInfo = deviceInfo; ExoPlayerImpl.this.deviceInfo = deviceInfo;
// TODO(internal b/187152483): Events should be dispatched via ListenerSet // TODO(internal b/187152483): Events should be dispatched via ListenerSet
for (Listener listener : listenerArraySet) { for (Listener listener : listeners) {
listener.onDeviceInfoChanged(deviceInfo); listener.onDeviceInfoChanged(deviceInfo);
} }
} }
@ -2904,12 +2920,12 @@ import java.util.concurrent.TimeoutException;
@Override @Override
public void onStreamVolumeChanged(int streamVolume, boolean streamMuted) { public void onStreamVolumeChanged(int streamVolume, boolean streamMuted) {
// TODO(internal b/187152483): Events should be dispatched via ListenerSet // TODO(internal b/187152483): Events should be dispatched via ListenerSet
for (Listener listener : listenerArraySet) { for (Listener listener : listeners) {
listener.onDeviceVolumeChanged(streamVolume, streamMuted); listener.onDeviceVolumeChanged(streamVolume, streamMuted);
} }
} }
// Player.Listener implementation. // Player.EventListener implementation.
@Override @Override
public void onIsLoadingChanged(boolean isLoading) { public void onIsLoadingChanged(boolean isLoading) {

View File

@ -33,6 +33,7 @@ import androidx.media3.common.Format;
import androidx.media3.common.MediaItem; import androidx.media3.common.MediaItem;
import androidx.media3.common.MediaMetadata; import androidx.media3.common.MediaMetadata;
import androidx.media3.common.PlaybackParameters; import androidx.media3.common.PlaybackParameters;
import androidx.media3.common.Player;
import androidx.media3.common.PriorityTaskManager; import androidx.media3.common.PriorityTaskManager;
import androidx.media3.common.Timeline; import androidx.media3.common.Timeline;
import androidx.media3.common.TrackGroupArray; import androidx.media3.common.TrackGroupArray;
@ -618,11 +619,23 @@ public class SimpleExoPlayer extends BasePlayer
player.addListener(listener); player.addListener(listener);
} }
@Deprecated
@Override
public void addListener(Player.EventListener listener) {
player.addEventListener(listener);
}
@Override @Override
public void removeListener(Listener listener) { public void removeListener(Listener listener) {
player.removeListener(listener); player.removeListener(listener);
} }
@Deprecated
@Override
public void removeListener(Player.EventListener listener) {
player.removeEventListener(listener);
}
@Override @Override
public @State int getPlaybackState() { public @State int getPlaybackState() {
return player.getPlaybackState(); return player.getPlaybackState();

View File

@ -20,6 +20,7 @@ import androidx.annotation.Nullable;
import androidx.media3.common.AudioAttributes; import androidx.media3.common.AudioAttributes;
import androidx.media3.common.AuxEffectInfo; import androidx.media3.common.AuxEffectInfo;
import androidx.media3.common.Format; import androidx.media3.common.Format;
import androidx.media3.common.Player;
import androidx.media3.common.PriorityTaskManager; import androidx.media3.common.PriorityTaskManager;
import androidx.media3.common.util.Clock; import androidx.media3.common.util.Clock;
import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.UnstableApi;
@ -79,6 +80,16 @@ public class StubExoPlayer extends StubPlayer implements ExoPlayer {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override
public void addListener(Player.EventListener listener) {
throw new UnsupportedOperationException();
}
@Override
public void removeListener(Player.EventListener listener) {
throw new UnsupportedOperationException();
}
@Override @Override
public void addAudioOffloadListener(AudioOffloadListener listener) { public void addAudioOffloadListener(AudioOffloadListener listener) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();