ForwardingPlayer only forwards Player operations
PiperOrigin-RevId: 374621615
This commit is contained in:
parent
41afb6ac4e
commit
65d8ff80db
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
* Copyright 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -15,74 +15,32 @@
|
||||
*/
|
||||
package com.google.android.exoplayer2;
|
||||
|
||||
import static com.google.android.exoplayer2.util.Assertions.checkState;
|
||||
|
||||
import android.os.Looper;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.SurfaceView;
|
||||
import android.view.TextureView;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import com.google.android.exoplayer2.audio.AudioAttributes;
|
||||
import com.google.android.exoplayer2.device.DeviceInfo;
|
||||
import com.google.android.exoplayer2.metadata.Metadata;
|
||||
import com.google.android.exoplayer2.source.TrackGroupArray;
|
||||
import com.google.android.exoplayer2.text.Cue;
|
||||
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
|
||||
import com.google.android.exoplayer2.util.Clock;
|
||||
import com.google.android.exoplayer2.util.ListenerSet;
|
||||
import com.google.android.exoplayer2.video.VideoSize;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A {@link Player} that forwards operations to another {@link Player}. Applications can use this
|
||||
* class to suppress or modify specific operations, by overriding the respective methods.
|
||||
*
|
||||
* <p>An application can {@link #setDisabledCommands disable available commands}. When the wrapped
|
||||
* player advertises available commands, either with {@link Player#isCommandAvailable(int)} or with
|
||||
* {@link Listener#onAvailableCommandsChanged}, the disabled commands will be filtered out.
|
||||
*/
|
||||
public class ForwardingPlayer implements Player {
|
||||
private final Player player;
|
||||
private final Clock clock;
|
||||
@Nullable private ForwardingListener forwardingListener;
|
||||
|
||||
private Commands disabledCommands;
|
||||
@Nullable private Commands unfilteredCommands;
|
||||
@Nullable private Commands filteredCommands;
|
||||
private final Player player;
|
||||
|
||||
/** Creates a new instance that forwards all operations to {@code player}. */
|
||||
public ForwardingPlayer(Player player) {
|
||||
this(player, Clock.DEFAULT);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
/* package */ ForwardingPlayer(Player player, Clock clock) {
|
||||
this.player = player;
|
||||
this.clock = clock;
|
||||
this.disabledCommands = Commands.EMPTY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the disabled {@link Commands}.
|
||||
*
|
||||
* <p>When querying for available commands with {@link #isCommandAvailable(int)}, or when the
|
||||
* wrapped player advertises available commands with {@link Listener#isCommandAvailable}, disabled
|
||||
* commands will be filtered out.
|
||||
*/
|
||||
public void setDisabledCommands(Commands commands) {
|
||||
checkState(player.getApplicationLooper().equals(Looper.myLooper()));
|
||||
disabledCommands = commands;
|
||||
filteredCommands = null;
|
||||
if (forwardingListener != null) {
|
||||
forwardingListener.maybeAdvertiseAvailableCommands();
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the disabled commands. */
|
||||
public Commands getDisabledCommands() {
|
||||
return disabledCommands;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -91,35 +49,25 @@ public class ForwardingPlayer implements Player {
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation") // Implementing deprecated method.
|
||||
public void addListener(EventListener listener) {
|
||||
addListener(new EventListenerWrapper(listener));
|
||||
player.addListener(new ForwardingEventListener(this, listener));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addListener(Listener listener) {
|
||||
if (forwardingListener == null) {
|
||||
forwardingListener = new ForwardingListener(this);
|
||||
}
|
||||
if (!forwardingListener.isRegistered()) {
|
||||
forwardingListener.registerTo(player);
|
||||
}
|
||||
forwardingListener.addListener(listener);
|
||||
player.addListener(new ForwardingListener(this, listener));
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation") // Implementing deprecated method.
|
||||
public void removeListener(EventListener listener) {
|
||||
removeListener(new EventListenerWrapper(listener));
|
||||
player.removeListener(new ForwardingEventListener(this, listener));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeListener(Listener listener) {
|
||||
if (forwardingListener == null) {
|
||||
return;
|
||||
}
|
||||
forwardingListener.removeListener(listener);
|
||||
if (!forwardingListener.hasListeners()) {
|
||||
forwardingListener.unregisterFrom(player);
|
||||
}
|
||||
player.removeListener(new ForwardingListener(this, listener));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -200,17 +148,12 @@ public class ForwardingPlayer implements Player {
|
||||
|
||||
@Override
|
||||
public boolean isCommandAvailable(@Command int command) {
|
||||
return !disabledCommands.contains(command) && player.isCommandAvailable(command);
|
||||
return player.isCommandAvailable(command);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Commands getAvailableCommands() {
|
||||
Commands commands = player.getAvailableCommands();
|
||||
if (filteredCommands == null || !commands.equals(unfilteredCommands)) {
|
||||
filteredCommands = filterCommands(commands, disabledCommands);
|
||||
unfilteredCommands = commands;
|
||||
}
|
||||
return filteredCommands;
|
||||
return player.getAvailableCommands();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -602,424 +545,126 @@ public class ForwardingPlayer implements Player {
|
||||
player.setDeviceMuted(muted);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps a {@link Listener} and intercepts {@link Listener#onAvailableCommandsChanged} in order to
|
||||
* filter disabled commands. All other operations are forwarded to the wrapped {@link Listener}.
|
||||
*/
|
||||
private static class ForwardingListener implements Listener {
|
||||
private final ForwardingPlayer player;
|
||||
private final ListenerSet<Listener> listeners;
|
||||
private boolean registered;
|
||||
private Commands lastReceivedCommands;
|
||||
private Commands lastAdvertisedCommands;
|
||||
@SuppressWarnings("deprecation") // Use of deprecated type for backwards compatibility.
|
||||
private static class ForwardingEventListener implements EventListener {
|
||||
|
||||
public ForwardingListener(ForwardingPlayer forwardingPlayer) {
|
||||
this.player = forwardingPlayer;
|
||||
listeners =
|
||||
new ListenerSet<>(
|
||||
forwardingPlayer.player.getApplicationLooper(),
|
||||
forwardingPlayer.clock,
|
||||
(listener, flags) -> listener.onEvents(forwardingPlayer, new Events(flags)));
|
||||
lastReceivedCommands = Commands.EMPTY;
|
||||
lastAdvertisedCommands = Commands.EMPTY;
|
||||
private final ForwardingPlayer forwardingPlayer;
|
||||
private final EventListener eventListener;
|
||||
|
||||
private ForwardingEventListener(
|
||||
ForwardingPlayer forwardingPlayer, EventListener eventListener) {
|
||||
this.forwardingPlayer = forwardingPlayer;
|
||||
this.eventListener = eventListener;
|
||||
}
|
||||
|
||||
public void registerTo(Player player) {
|
||||
checkState(!registered);
|
||||
player.addListener(this);
|
||||
lastReceivedCommands = player.getAvailableCommands();
|
||||
lastAdvertisedCommands = lastReceivedCommands;
|
||||
registered = true;
|
||||
}
|
||||
|
||||
public void unregisterFrom(Player player) {
|
||||
checkState(registered);
|
||||
player.removeListener(this);
|
||||
registered = false;
|
||||
}
|
||||
|
||||
public boolean isRegistered() {
|
||||
return registered;
|
||||
}
|
||||
|
||||
public void addListener(Listener listener) {
|
||||
listeners.add(listener);
|
||||
}
|
||||
|
||||
public void removeListener(Listener listener) {
|
||||
listeners.remove(listener);
|
||||
}
|
||||
|
||||
public boolean hasListeners() {
|
||||
return listeners.size() > 0;
|
||||
}
|
||||
|
||||
// VideoListener callbacks
|
||||
@Override
|
||||
public void onVideoSizeChanged(VideoSize videoSize) {
|
||||
listeners.sendEvent(C.INDEX_UNSET, listener -> listener.onVideoSizeChanged(videoSize));
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation") // Forwarding to deprecated method.
|
||||
public void onVideoSizeChanged(
|
||||
int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) {
|
||||
listeners.sendEvent(
|
||||
C.INDEX_UNSET,
|
||||
listener ->
|
||||
listener.onVideoSizeChanged(
|
||||
width, height, unappliedRotationDegrees, pixelWidthHeightRatio));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSurfaceSizeChanged(int width, int height) {
|
||||
listeners.sendEvent(C.INDEX_UNSET, listener -> listener.onSurfaceSizeChanged(width, height));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRenderedFirstFrame() {
|
||||
listeners.sendEvent(C.INDEX_UNSET, Listener::onRenderedFirstFrame);
|
||||
}
|
||||
|
||||
// AudioListener callbacks
|
||||
|
||||
@Override
|
||||
public void onAudioSessionIdChanged(int audioSessionId) {
|
||||
listeners.sendEvent(
|
||||
C.INDEX_UNSET, listener -> listener.onAudioSessionIdChanged(audioSessionId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAudioAttributesChanged(AudioAttributes audioAttributes) {
|
||||
listeners.sendEvent(
|
||||
C.INDEX_UNSET, listener -> listener.onAudioAttributesChanged(audioAttributes));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVolumeChanged(float volume) {
|
||||
listeners.sendEvent(C.INDEX_UNSET, listener -> listener.onVolumeChanged(volume));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSkipSilenceEnabledChanged(boolean skipSilenceEnabled) {
|
||||
listeners.sendEvent(
|
||||
C.INDEX_UNSET, listener -> listener.onSkipSilenceEnabledChanged(skipSilenceEnabled));
|
||||
}
|
||||
|
||||
// TextOutput callbacks
|
||||
|
||||
@Override
|
||||
public void onCues(List<Cue> cues) {
|
||||
listeners.sendEvent(C.INDEX_UNSET, listener -> listener.onCues(cues));
|
||||
}
|
||||
|
||||
// MetadataOutput callbacks
|
||||
|
||||
@Override
|
||||
public void onMetadata(Metadata metadata) {
|
||||
listeners.sendEvent(C.INDEX_UNSET, listener -> listener.onMetadata(metadata));
|
||||
}
|
||||
|
||||
// DeviceListener callbacks
|
||||
|
||||
@Override
|
||||
public void onDeviceInfoChanged(DeviceInfo deviceInfo) {
|
||||
listeners.sendEvent(C.INDEX_UNSET, listener -> listener.onDeviceInfoChanged(deviceInfo));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeviceVolumeChanged(int volume, boolean muted) {
|
||||
listeners.sendEvent(C.INDEX_UNSET, listener -> listener.onDeviceVolumeChanged(volume, muted));
|
||||
}
|
||||
|
||||
// EventListener callbacks
|
||||
|
||||
@Override
|
||||
public void onTimelineChanged(Timeline timeline, @TimelineChangeReason int reason) {
|
||||
listeners.sendEvent(
|
||||
EVENT_TIMELINE_CHANGED, listener -> listener.onTimelineChanged(timeline, reason));
|
||||
eventListener.onTimelineChanged(timeline, reason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMediaItemTransition(
|
||||
@Nullable MediaItem mediaItem, @MediaItemTransitionReason int reason) {
|
||||
listeners.sendEvent(
|
||||
EVENT_MEDIA_ITEM_TRANSITION,
|
||||
listener -> listener.onMediaItemTransition(mediaItem, reason));
|
||||
eventListener.onMediaItemTransition(mediaItem, reason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
|
||||
listeners.sendEvent(
|
||||
EVENT_TRACKS_CHANGED, listener -> listener.onTracksChanged(trackGroups, trackSelections));
|
||||
eventListener.onTracksChanged(trackGroups, trackSelections);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStaticMetadataChanged(List<Metadata> metadataList) {
|
||||
listeners.sendEvent(
|
||||
EVENT_STATIC_METADATA_CHANGED,
|
||||
listener -> listener.onStaticMetadataChanged(metadataList));
|
||||
eventListener.onStaticMetadataChanged(metadataList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMediaMetadataChanged(MediaMetadata mediaMetadata) {
|
||||
listeners.sendEvent(
|
||||
EVENT_MEDIA_METADATA_CHANGED, listener -> listener.onMediaMetadataChanged(mediaMetadata));
|
||||
eventListener.onMediaMetadataChanged(mediaMetadata);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onIsLoadingChanged(boolean isLoading) {
|
||||
listeners.sendEvent(
|
||||
EVENT_IS_LOADING_CHANGED, listener -> listener.onIsLoadingChanged(isLoading));
|
||||
eventListener.onIsLoadingChanged(isLoading);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation") // Forwarding to deprecated method.
|
||||
public void onLoadingChanged(boolean isLoading) {
|
||||
listeners.sendEvent(
|
||||
EVENT_IS_LOADING_CHANGED, listener -> listener.onLoadingChanged(isLoading));
|
||||
eventListener.onIsLoadingChanged(isLoading);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAvailableCommandsChanged(Commands availableCommands) {
|
||||
lastReceivedCommands = availableCommands;
|
||||
maybeAdvertiseAvailableCommands();
|
||||
eventListener.onAvailableCommandsChanged(availableCommands);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation") // Forwarding to deprecated method.
|
||||
public void onPlayerStateChanged(boolean playWhenReady, @State int playbackState) {
|
||||
listeners.sendEvent(
|
||||
C.INDEX_UNSET, listener -> listener.onPlayerStateChanged(playWhenReady, playbackState));
|
||||
eventListener.onPlayerStateChanged(playWhenReady, playbackState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlaybackStateChanged(@State int state) {
|
||||
listeners.sendEvent(
|
||||
EVENT_PLAYBACK_STATE_CHANGED, listener -> listener.onPlaybackStateChanged(state));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayWhenReadyChanged(boolean playWhenReady, @State int reason) {
|
||||
listeners.sendEvent(
|
||||
EVENT_PLAY_WHEN_READY_CHANGED,
|
||||
listener -> listener.onPlayWhenReadyChanged(playWhenReady, reason));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlaybackSuppressionReasonChanged(
|
||||
@PlaybackSuppressionReason int playbackSuppressionReason) {
|
||||
listeners.sendEvent(
|
||||
EVENT_PLAYBACK_SUPPRESSION_REASON_CHANGED,
|
||||
listener -> listener.onPlaybackSuppressionReasonChanged(playbackSuppressionReason));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onIsPlayingChanged(boolean isPlaying) {
|
||||
listeners.sendEvent(
|
||||
EVENT_IS_PLAYING_CHANGED, listener -> listener.onIsPlayingChanged(isPlaying));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRepeatModeChanged(@RepeatMode int repeatMode) {
|
||||
listeners.sendEvent(
|
||||
EVENT_REPEAT_MODE_CHANGED, listener -> listener.onRepeatModeChanged(repeatMode));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {
|
||||
listeners.sendEvent(
|
||||
EVENT_SHUFFLE_MODE_ENABLED_CHANGED,
|
||||
listener -> listener.onShuffleModeEnabledChanged(shuffleModeEnabled));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerError(ExoPlaybackException error) {
|
||||
listeners.sendEvent(EVENT_PLAYER_ERROR, listener -> listener.onPlayerError(error));
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation") // Forwarding to deprecated method.
|
||||
public void onPositionDiscontinuity(@DiscontinuityReason int reason) {
|
||||
listeners.sendEvent(
|
||||
EVENT_POSITION_DISCONTINUITY, listener -> listener.onPositionDiscontinuity(reason));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPositionDiscontinuity(
|
||||
PositionInfo oldPosition, PositionInfo newPosition, @DiscontinuityReason int reason) {
|
||||
listeners.sendEvent(
|
||||
EVENT_POSITION_DISCONTINUITY,
|
||||
listener -> listener.onPositionDiscontinuity(oldPosition, newPosition, reason));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
|
||||
listeners.sendEvent(
|
||||
EVENT_PLAYBACK_PARAMETERS_CHANGED,
|
||||
listener -> listener.onPlaybackParametersChanged(playbackParameters));
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation") // Forwarding to deprecated method.
|
||||
public void onSeekProcessed() {
|
||||
listeners.sendEvent(C.INDEX_UNSET, EventListener::onSeekProcessed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvents(Player player, Events events) {
|
||||
// Do nothing, individual callbacks will trigger this event on behalf of the forwarding
|
||||
// player.
|
||||
}
|
||||
|
||||
public void maybeAdvertiseAvailableCommands() {
|
||||
Commands commandsToAdvertise = filterCommands(lastReceivedCommands, player.disabledCommands);
|
||||
if (!commandsToAdvertise.equals(lastAdvertisedCommands)) {
|
||||
lastAdvertisedCommands = commandsToAdvertise;
|
||||
listeners.sendEvent(
|
||||
EVENT_AVAILABLE_COMMANDS_CHANGED,
|
||||
listener -> listener.onAvailableCommandsChanged(commandsToAdvertise));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps an {@link EventListener} as a {@link Listener} so that it can be used by the {@link
|
||||
* ForwardingListener}.
|
||||
*/
|
||||
private static class EventListenerWrapper implements Listener {
|
||||
private final EventListener listener;
|
||||
|
||||
/** Wraps an {@link EventListener}. */
|
||||
public EventListenerWrapper(EventListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
// EventListener callbacks
|
||||
|
||||
@Override
|
||||
public void onTimelineChanged(Timeline timeline, @TimelineChangeReason int reason) {
|
||||
listener.onTimelineChanged(timeline, reason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMediaItemTransition(
|
||||
@Nullable MediaItem mediaItem, @MediaItemTransitionReason int reason) {
|
||||
listener.onMediaItemTransition(mediaItem, reason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
|
||||
listener.onTracksChanged(trackGroups, trackSelections);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStaticMetadataChanged(List<Metadata> metadataList) {
|
||||
listener.onStaticMetadataChanged(metadataList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMediaMetadataChanged(MediaMetadata mediaMetadata) {
|
||||
listener.onMediaMetadataChanged(mediaMetadata);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onIsLoadingChanged(boolean isLoading) {
|
||||
listener.onIsLoadingChanged(isLoading);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation") // Forwarding to deprecated method.
|
||||
public void onLoadingChanged(boolean isLoading) {
|
||||
listener.onLoadingChanged(isLoading);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAvailableCommandsChanged(Commands availableCommands) {
|
||||
listener.onAvailableCommandsChanged(availableCommands);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation") // Forwarding to deprecated method.
|
||||
public void onPlayerStateChanged(boolean playWhenReady, @State int playbackState) {
|
||||
listener.onPlayerStateChanged(playWhenReady, playbackState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlaybackStateChanged(@State int state) {
|
||||
listener.onPlaybackStateChanged(state);
|
||||
eventListener.onPlaybackStateChanged(state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayWhenReadyChanged(
|
||||
boolean playWhenReady, @PlayWhenReadyChangeReason int reason) {
|
||||
listener.onPlayWhenReadyChanged(playWhenReady, reason);
|
||||
eventListener.onPlayWhenReadyChanged(playWhenReady, reason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlaybackSuppressionReasonChanged(
|
||||
@PlaybackSuppressionReason int playbackSuppressionReason) {
|
||||
listener.onPlaybackSuppressionReasonChanged(playbackSuppressionReason);
|
||||
@PlayWhenReadyChangeReason int playbackSuppressionReason) {
|
||||
eventListener.onPlaybackStateChanged(playbackSuppressionReason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onIsPlayingChanged(boolean isPlaying) {
|
||||
listener.onIsPlayingChanged(isPlaying);
|
||||
eventListener.onIsPlayingChanged(isPlaying);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRepeatModeChanged(@RepeatMode int repeatMode) {
|
||||
listener.onRepeatModeChanged(repeatMode);
|
||||
eventListener.onRepeatModeChanged(repeatMode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {
|
||||
listener.onShuffleModeEnabledChanged(shuffleModeEnabled);
|
||||
eventListener.onShuffleModeEnabledChanged(shuffleModeEnabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerError(ExoPlaybackException error) {
|
||||
listener.onPlayerError(error);
|
||||
eventListener.onPlayerError(error);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation") // Forwarding to deprecated method.
|
||||
public void onPositionDiscontinuity(@DiscontinuityReason int reason) {
|
||||
listener.onPositionDiscontinuity(reason);
|
||||
eventListener.onPositionDiscontinuity(reason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPositionDiscontinuity(
|
||||
PositionInfo oldPosition, PositionInfo newPosition, @DiscontinuityReason int reason) {
|
||||
listener.onPositionDiscontinuity(oldPosition, newPosition, reason);
|
||||
eventListener.onPositionDiscontinuity(oldPosition, newPosition, reason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
|
||||
listener.onPlaybackParametersChanged(playbackParameters);
|
||||
eventListener.onPlaybackParametersChanged(playbackParameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation") // Forwarding to deprecated method.
|
||||
public void onSeekProcessed() {
|
||||
listener.onSeekProcessed();
|
||||
eventListener.onSeekProcessed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvents(Player player, Events events) {
|
||||
listener.onEvents(player, events);
|
||||
}
|
||||
|
||||
// Other Listener callbacks, they should never be invoked on this wrapper.
|
||||
|
||||
@Override
|
||||
public void onMetadata(Metadata metadata) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCues(List<Cue> cues) {
|
||||
throw new IllegalStateException();
|
||||
// Replace player with forwarding player.
|
||||
eventListener.onEvents(forwardingPlayer, events);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1027,31 +672,106 @@ public class ForwardingPlayer implements Player {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof EventListenerWrapper)) {
|
||||
if (!(o instanceof ForwardingEventListener)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
EventListenerWrapper that = (EventListenerWrapper) o;
|
||||
return listener.equals(that.listener);
|
||||
ForwardingEventListener that = (ForwardingEventListener) o;
|
||||
|
||||
if (!forwardingPlayer.equals(that.forwardingPlayer)) {
|
||||
return false;
|
||||
}
|
||||
return eventListener.equals(that.eventListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return listener.hashCode();
|
||||
int result = forwardingPlayer.hashCode();
|
||||
result = 31 * result + eventListener.hashCode();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the remaining available commands after removing disabled commands. */
|
||||
private static Commands filterCommands(Commands availableCommands, Commands disabledCommands) {
|
||||
if (disabledCommands.size() == 0) {
|
||||
return availableCommands;
|
||||
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;
|
||||
}
|
||||
|
||||
Commands.Builder builder = new Commands.Builder();
|
||||
for (int i = 0; i < availableCommands.size(); i++) {
|
||||
int command = availableCommands.get(i);
|
||||
builder.addIf(command, !disabledCommands.contains(command));
|
||||
// VideoListener methods.
|
||||
|
||||
@Override
|
||||
public void onVideoSizeChanged(VideoSize videoSize) {
|
||||
listener.onVideoSizeChanged(videoSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation") // Forwarding to deprecated method.
|
||||
public void onVideoSizeChanged(
|
||||
int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) {
|
||||
listener.onVideoSizeChanged(width, height, unappliedRotationDegrees, pixelWidthHeightRatio);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSurfaceSizeChanged(int width, int height) {
|
||||
listener.onSurfaceSizeChanged(width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRenderedFirstFrame() {
|
||||
listener.onRenderedFirstFrame();
|
||||
}
|
||||
|
||||
// AudioListener methods
|
||||
|
||||
@Override
|
||||
public void onAudioSessionIdChanged(int audioSessionId) {
|
||||
listener.onAudioSessionIdChanged(audioSessionId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAudioAttributesChanged(AudioAttributes audioAttributes) {
|
||||
listener.onAudioAttributesChanged(audioAttributes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVolumeChanged(float volume) {
|
||||
listener.onVolumeChanged(volume);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSkipSilenceEnabledChanged(boolean skipSilenceEnabled) {
|
||||
listener.onSkipSilenceEnabledChanged(skipSilenceEnabled);
|
||||
}
|
||||
|
||||
// TextOutput methods.
|
||||
|
||||
@Override
|
||||
public void onCues(List<Cue> cues) {
|
||||
listener.onCues(cues);
|
||||
}
|
||||
|
||||
// MetadataOutput methods.
|
||||
|
||||
@Override
|
||||
public void onMetadata(Metadata metadata) {
|
||||
listener.onMetadata(metadata);
|
||||
}
|
||||
|
||||
// DeviceListener callbacks
|
||||
|
||||
@Override
|
||||
public void onDeviceInfoChanged(DeviceInfo deviceInfo) {
|
||||
listener.onDeviceInfoChanged(deviceInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeviceVolumeChanged(int volume, boolean muted) {
|
||||
listener.onDeviceVolumeChanged(volume, muted);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
* Copyright 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -15,26 +15,18 @@
|
||||
*/
|
||||
package com.google.android.exoplayer2;
|
||||
|
||||
import static com.google.android.exoplayer2.Player.COMMAND_PLAY_PAUSE;
|
||||
import static com.google.android.exoplayer2.Player.COMMAND_PREPARE_STOP;
|
||||
import static com.google.android.exoplayer2.Player.COMMAND_SEEK_TO_MEDIA_ITEM;
|
||||
import static com.google.android.exoplayer2.Player.EVENT_AVAILABLE_COMMANDS_CHANGED;
|
||||
import static com.google.android.exoplayer2.Player.EVENT_PLAYBACK_STATE_CHANGED;
|
||||
import static com.google.android.exoplayer2.Player.STATE_READY;
|
||||
import static com.google.android.exoplayer2.util.Assertions.checkState;
|
||||
import static com.google.android.exoplayer2.Player.EVENT_IS_PLAYING_CHANGED;
|
||||
import static com.google.android.exoplayer2.Player.EVENT_MEDIA_ITEM_TRANSITION;
|
||||
import static com.google.android.exoplayer2.Player.EVENT_TIMELINE_CHANGED;
|
||||
import static com.google.android.exoplayer2.util.Assertions.checkArgument;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.argThat;
|
||||
import static org.mockito.ArgumentMatchers.same;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
|
||||
import android.os.Looper;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import com.google.android.exoplayer2.testutil.StubExoPlayer;
|
||||
import com.google.android.exoplayer2.util.ExoFlags;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayDeque;
|
||||
@ -46,220 +38,112 @@ import java.util.Queue;
|
||||
import java.util.Set;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentMatcher;
|
||||
import org.mockito.InOrder;
|
||||
import org.robolectric.shadows.ShadowLooper;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
||||
/** Unit test for {@link ForwardingPlayer}. */
|
||||
/** Unit tests for {@link ForwardingPlayer}. */
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ForwardingPlayerTest {
|
||||
|
||||
@Test
|
||||
public void getAvailableCommands_withDisabledCommands_filtersDisabledCommands() {
|
||||
Player player = new FakePlayer(COMMAND_PLAY_PAUSE, COMMAND_PREPARE_STOP);
|
||||
public void addListener_addsForwardingListener() {
|
||||
FakePlayer player = new FakePlayer();
|
||||
Player.Listener listener1 = mock(Player.Listener.class);
|
||||
Player.Listener listener2 = mock(Player.Listener.class);
|
||||
|
||||
ForwardingPlayer forwardingPlayer = new ForwardingPlayer(player);
|
||||
forwardingPlayer.setDisabledCommands(buildCommands(COMMAND_PREPARE_STOP));
|
||||
forwardingPlayer.addListener(listener1);
|
||||
// Add listener1 again.
|
||||
forwardingPlayer.addListener(listener1);
|
||||
forwardingPlayer.addListener(listener2);
|
||||
|
||||
assertThat(forwardingPlayer.getAvailableCommands())
|
||||
.isEqualTo(buildCommands(COMMAND_PLAY_PAUSE));
|
||||
assertThat(player.listeners).hasSize(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailableCommands_playerAvailableCommandsChanged_returnsFreshCommands() {
|
||||
FakePlayer player = new FakePlayer(COMMAND_PLAY_PAUSE, COMMAND_PREPARE_STOP);
|
||||
@SuppressWarnings("deprecation") // Testing backwards compatibility with deprecated method.
|
||||
public void addEventListener_addsForwardingListener() {
|
||||
FakePlayer player = new FakePlayer();
|
||||
Player.EventListener listener1 = mock(Player.EventListener.class);
|
||||
Player.EventListener listener2 = mock(Player.EventListener.class);
|
||||
|
||||
ForwardingPlayer forwardingPlayer = new ForwardingPlayer(player);
|
||||
forwardingPlayer.addListener(listener1);
|
||||
// Add listener1 again.
|
||||
forwardingPlayer.addListener(listener1);
|
||||
forwardingPlayer.addListener(listener2);
|
||||
|
||||
forwardingPlayer.setDisabledCommands(buildCommands(COMMAND_PREPARE_STOP));
|
||||
assertThat(forwardingPlayer.getAvailableCommands())
|
||||
.isEqualTo(buildCommands(COMMAND_PLAY_PAUSE));
|
||||
player.setAvailableCommands(
|
||||
buildCommands(COMMAND_PLAY_PAUSE, COMMAND_PREPARE_STOP, COMMAND_SEEK_TO_MEDIA_ITEM));
|
||||
|
||||
assertThat(forwardingPlayer.getAvailableCommands())
|
||||
.isEqualTo(buildCommands(COMMAND_PLAY_PAUSE, COMMAND_SEEK_TO_MEDIA_ITEM));
|
||||
assertThat(player.eventListeners).hasSize(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isCommandAvailable_withDisabledCommands_filtersDisabledCommands() {
|
||||
Player player = new FakePlayer(COMMAND_PLAY_PAUSE, COMMAND_PREPARE_STOP);
|
||||
|
||||
public void removeListener_removesForwardingListener() {
|
||||
FakePlayer player = new FakePlayer();
|
||||
Player.Listener listener1 = mock(Player.Listener.class);
|
||||
Player.Listener listener2 = mock(Player.Listener.class);
|
||||
ForwardingPlayer forwardingPlayer = new ForwardingPlayer(player);
|
||||
forwardingPlayer.setDisabledCommands(buildCommands(COMMAND_PREPARE_STOP));
|
||||
forwardingPlayer.addListener(listener1);
|
||||
forwardingPlayer.addListener(listener2);
|
||||
|
||||
assertThat(forwardingPlayer.isCommandAvailable(COMMAND_PLAY_PAUSE)).isTrue();
|
||||
assertThat(forwardingPlayer.isCommandAvailable(COMMAND_PREPARE_STOP)).isFalse();
|
||||
forwardingPlayer.removeListener(listener1);
|
||||
assertThat(player.listeners).hasSize(1);
|
||||
// Remove same listener again.
|
||||
forwardingPlayer.removeListener(listener1);
|
||||
assertThat(player.listeners).hasSize(1);
|
||||
forwardingPlayer.removeListener(listener2);
|
||||
assertThat(player.listeners).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setDisabledCommands_triggersOnCommandsAvailableChanged() {
|
||||
Player player = new FakePlayer(COMMAND_PLAY_PAUSE, COMMAND_PREPARE_STOP);
|
||||
Player.Listener listener = mock(Player.Listener.class);
|
||||
@SuppressWarnings("deprecation") // Testing backwards compatibility with deprecated method.
|
||||
public void removeEventListener_removesForwardingListener() {
|
||||
FakePlayer player = new FakePlayer();
|
||||
Player.EventListener listener1 = mock(Player.EventListener.class);
|
||||
Player.EventListener listener2 = mock(Player.EventListener.class);
|
||||
ForwardingPlayer forwardingPlayer = new ForwardingPlayer(player);
|
||||
forwardingPlayer.addListener(listener);
|
||||
forwardingPlayer.addListener(listener1);
|
||||
forwardingPlayer.addListener(listener2);
|
||||
|
||||
forwardingPlayer.setDisabledCommands(buildCommands(COMMAND_PREPARE_STOP));
|
||||
ShadowLooper.idleMainLooper();
|
||||
|
||||
InOrder inOrder = inOrder(listener);
|
||||
inOrder.verify(listener).onAvailableCommandsChanged(buildCommands(COMMAND_PLAY_PAUSE));
|
||||
inOrder
|
||||
.verify(listener)
|
||||
.onEvents(
|
||||
same(forwardingPlayer), argThat(new EventsMatcher(EVENT_AVAILABLE_COMMANDS_CHANGED)));
|
||||
inOrder.verifyNoMoreInteractions();
|
||||
forwardingPlayer.removeListener(listener1);
|
||||
assertThat(player.eventListeners).hasSize(1);
|
||||
// Remove same listener again.
|
||||
forwardingPlayer.removeListener(listener1);
|
||||
assertThat(player.eventListeners).hasSize(1);
|
||||
forwardingPlayer.removeListener(listener2);
|
||||
assertThat(player.eventListeners).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setDisabledCommands_withoutChangingAvailableCommands_noCallbackTriggered() {
|
||||
Player player = new FakePlayer(COMMAND_PLAY_PAUSE, COMMAND_PREPARE_STOP);
|
||||
Player.Listener listener = mock(Player.Listener.class);
|
||||
ForwardingPlayer forwardingPlayer = new ForwardingPlayer(player);
|
||||
forwardingPlayer.addListener(listener);
|
||||
|
||||
forwardingPlayer.setDisabledCommands(buildCommands(COMMAND_SEEK_TO_MEDIA_ITEM));
|
||||
ShadowLooper.idleMainLooper();
|
||||
|
||||
verifyNoMoreInteractions(listener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setDisabledCommands_multipleTimes_availableCommandsUpdated() {
|
||||
Player player = new FakePlayer(COMMAND_PLAY_PAUSE, COMMAND_PREPARE_STOP);
|
||||
ForwardingPlayer forwardingPlayer = new ForwardingPlayer(player);
|
||||
|
||||
forwardingPlayer.setDisabledCommands(buildCommands(COMMAND_SEEK_TO_MEDIA_ITEM));
|
||||
assertThat(forwardingPlayer.getAvailableCommands())
|
||||
.isEqualTo(buildCommands(COMMAND_PLAY_PAUSE, COMMAND_PREPARE_STOP));
|
||||
|
||||
forwardingPlayer.setDisabledCommands(
|
||||
buildCommands(COMMAND_PREPARE_STOP, COMMAND_SEEK_TO_MEDIA_ITEM));
|
||||
assertThat(forwardingPlayer.getAvailableCommands())
|
||||
.isEqualTo(buildCommands(COMMAND_PLAY_PAUSE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onCommandsAvailableChanged_listenerChangesCommandsRecursively_secondCallbackCalled() {
|
||||
FakePlayer player = new FakePlayer(COMMAND_PLAY_PAUSE, COMMAND_PREPARE_STOP);
|
||||
ForwardingPlayer forwardingPlayer = new ForwardingPlayer(player);
|
||||
Player.Listener listener =
|
||||
spy(
|
||||
new Player.Listener() {
|
||||
@Override
|
||||
public void onAvailableCommandsChanged(Player.Commands availableCommands) {
|
||||
// The callback changes the forwarding player's disabled commands triggering
|
||||
// exactly one more callback.
|
||||
forwardingPlayer.setDisabledCommands(buildCommands(COMMAND_PREPARE_STOP));
|
||||
}
|
||||
});
|
||||
forwardingPlayer.addListener(listener);
|
||||
|
||||
Player.Commands updatedCommands =
|
||||
buildCommands(COMMAND_PLAY_PAUSE, COMMAND_PREPARE_STOP, COMMAND_SEEK_TO_MEDIA_ITEM);
|
||||
player.setAvailableCommands(updatedCommands);
|
||||
player.forwardingListener.onAvailableCommandsChanged(updatedCommands);
|
||||
ShadowLooper.idleMainLooper();
|
||||
|
||||
InOrder inOrder = inOrder(listener);
|
||||
inOrder
|
||||
.verify(listener)
|
||||
.onAvailableCommandsChanged(
|
||||
buildCommands(COMMAND_PLAY_PAUSE, COMMAND_PREPARE_STOP, COMMAND_SEEK_TO_MEDIA_ITEM));
|
||||
inOrder
|
||||
.verify(listener)
|
||||
.onAvailableCommandsChanged(buildCommands(COMMAND_PLAY_PAUSE, COMMAND_SEEK_TO_MEDIA_ITEM));
|
||||
inOrder
|
||||
.verify(listener)
|
||||
.onEvents(
|
||||
same(forwardingPlayer), argThat(new EventsMatcher(EVENT_AVAILABLE_COMMANDS_CHANGED)));
|
||||
inOrder.verifyNoMoreInteractions();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
interceptingOnAvailableCommandsChanged_withDisabledCommands_filtersDisabledCommands() {
|
||||
public void onEvents_passesForwardingPlayerAsArgument() {
|
||||
FakePlayer player = new FakePlayer();
|
||||
Player.Listener listener = mock(Player.Listener.class);
|
||||
ForwardingPlayer forwardingPlayer = new ForwardingPlayer(player);
|
||||
forwardingPlayer.addListener(listener);
|
||||
Player.Listener forwardingListener = player.listeners.iterator().next();
|
||||
|
||||
forwardingPlayer.setDisabledCommands(buildCommands(COMMAND_PREPARE_STOP));
|
||||
ShadowLooper.idleMainLooper();
|
||||
// Setting the disabled commands did not affect the available commands, hence no callback was
|
||||
// triggered.
|
||||
verifyNoMoreInteractions(listener);
|
||||
forwardingListener.onEvents(
|
||||
player,
|
||||
new Player.Events(
|
||||
new ExoFlags.Builder()
|
||||
.addAll(
|
||||
EVENT_TIMELINE_CHANGED, EVENT_MEDIA_ITEM_TRANSITION, EVENT_IS_PLAYING_CHANGED)
|
||||
.build()));
|
||||
|
||||
// The wrapped player advertises new available commands.
|
||||
Player.Commands updatedCommands = buildCommands(COMMAND_PLAY_PAUSE, COMMAND_PREPARE_STOP);
|
||||
player.setAvailableCommands(updatedCommands);
|
||||
player.forwardingListener.onAvailableCommandsChanged(updatedCommands);
|
||||
ShadowLooper.idleMainLooper();
|
||||
verify(listener).onAvailableCommandsChanged(buildCommands(COMMAND_PLAY_PAUSE));
|
||||
verify(listener)
|
||||
.onEvents(
|
||||
same(forwardingPlayer), argThat(new EventsMatcher(EVENT_AVAILABLE_COMMANDS_CHANGED)));
|
||||
verifyNoMoreInteractions(listener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
interceptingOnAvailableCommandsChanged_withDisabledCommandsButAvailableCommandsNotChanged_doesNotForwardCallback() {
|
||||
FakePlayer player = new FakePlayer(COMMAND_PLAY_PAUSE, COMMAND_PREPARE_STOP);
|
||||
Player.Listener listener = mock(Player.Listener.class);
|
||||
ForwardingPlayer forwardingPlayer = new ForwardingPlayer(player);
|
||||
forwardingPlayer.addListener(listener);
|
||||
|
||||
// Disable commands that do not affect the available commands.
|
||||
forwardingPlayer.setDisabledCommands(buildCommands(COMMAND_SEEK_TO_MEDIA_ITEM));
|
||||
ShadowLooper.idleMainLooper();
|
||||
verifyNoMoreInteractions(listener);
|
||||
|
||||
// The wrapped player advertises new available commands which, after filtering the disabled
|
||||
// commands, do not change the available commands.
|
||||
Player.Commands updatedCommands =
|
||||
buildCommands(COMMAND_PLAY_PAUSE, COMMAND_PREPARE_STOP, COMMAND_SEEK_TO_MEDIA_ITEM);
|
||||
player.setAvailableCommands(updatedCommands);
|
||||
player.forwardingListener.onAvailableCommandsChanged(updatedCommands);
|
||||
ShadowLooper.idleMainLooper();
|
||||
|
||||
verifyNoMoreInteractions(listener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeListener_removesListenerFromPlayer() {
|
||||
FakePlayer player = new FakePlayer(COMMAND_PLAY_PAUSE, COMMAND_PREPARE_STOP);
|
||||
Player.Listener listener = mock(Player.Listener.class);
|
||||
ForwardingPlayer forwardingPlayer = new ForwardingPlayer(player);
|
||||
|
||||
forwardingPlayer.addListener(listener);
|
||||
assertThat(player.forwardingListener).isNotNull();
|
||||
forwardingPlayer.removeListener(listener);
|
||||
assertThat(player.forwardingListener).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addEventListener_forwardsEventListenerEvents() {
|
||||
FakePlayer player = new FakePlayer(COMMAND_PLAY_PAUSE, COMMAND_PREPARE_STOP);
|
||||
Player.EventListener eventListener = mock(Player.EventListener.class);
|
||||
ForwardingPlayer forwardingPlayer = new ForwardingPlayer(player);
|
||||
|
||||
forwardingPlayer.addListener(eventListener);
|
||||
player.forwardingListener.onPlaybackStateChanged(STATE_READY);
|
||||
ShadowLooper.idleMainLooper();
|
||||
|
||||
InOrder inOrder = inOrder(eventListener);
|
||||
inOrder.verify(eventListener).onPlaybackStateChanged(STATE_READY);
|
||||
inOrder
|
||||
.verify(eventListener)
|
||||
.onEvents(same(forwardingPlayer), argThat(new EventsMatcher(EVENT_PLAYBACK_STATE_CHANGED)));
|
||||
inOrder.verifyNoMoreInteractions();
|
||||
ArgumentCaptor<Player.Events> eventsArgumentCaptor =
|
||||
ArgumentCaptor.forClass(Player.Events.class);
|
||||
verify(listener).onEvents(same(forwardingPlayer), eventsArgumentCaptor.capture());
|
||||
Player.Events receivedEvents = eventsArgumentCaptor.getValue();
|
||||
assertThat(receivedEvents.size()).isEqualTo(3);
|
||||
assertThat(receivedEvents.contains(EVENT_TIMELINE_CHANGED)).isTrue();
|
||||
assertThat(receivedEvents.contains(EVENT_MEDIA_ITEM_TRANSITION)).isTrue();
|
||||
assertThat(receivedEvents.contains(EVENT_IS_PLAYING_CHANGED)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void forwardingPlayer_overridesAllPlayerMethods() throws Exception {
|
||||
// Check with reflection that ForwardingPlayer overrides all Player methods.
|
||||
List<Method> playerMethods = getPublicMethods(Player.class);
|
||||
for (int i = 0; i < playerMethods.size(); i++) {
|
||||
Method method = playerMethods.get(i);
|
||||
List<Method> methods = getPublicMethods(Player.class);
|
||||
for (int i = 0; i < methods.size(); i++) {
|
||||
Method method = methods.get(i);
|
||||
assertThat(
|
||||
ForwardingPlayer.class.getDeclaredMethod(
|
||||
method.getName(), method.getParameterTypes()))
|
||||
@ -268,13 +152,13 @@ public class ForwardingPlayerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void forwardingListener_overridesAllListenerMethods() throws Exception {
|
||||
// Check with reflection that ForwardingListener in ForwardingPlayer overrides all Listener
|
||||
// methods.
|
||||
Class<?> forwardingListenerClass = getNestedClass("ForwardingListener");
|
||||
List<Method> publicListenerMethods = getPublicMethods(Player.Listener.class);
|
||||
for (int i = 0; i < publicListenerMethods.size(); i++) {
|
||||
Method method = publicListenerMethods.get(i);
|
||||
@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()))
|
||||
@ -283,99 +167,20 @@ public class ForwardingPlayerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void eventListenerWrapper_overridesAllEventListenerMethods() throws Exception {
|
||||
// Check with reflection that EventListenerWrapper in ForwardingPlayer overrides all
|
||||
// EventListener methods.
|
||||
Class<?> listenerWrapperClass = getNestedClass("EventListenerWrapper");
|
||||
List<Method> publicListenerMethods = getPublicMethods(Player.EventListener.class);
|
||||
for (int i = 0; i < publicListenerMethods.size(); i++) {
|
||||
Method method = publicListenerMethods.get(i);
|
||||
assertThat(
|
||||
listenerWrapperClass.getDeclaredMethod(method.getName(), method.getParameterTypes()))
|
||||
public void forwardingListener_overridesAllListenerMethods() throws Exception {
|
||||
// Check with reflection that ForwardingListener overrides all Listener methods.
|
||||
Class<?> forwardingListenerClass = getInnerClass("ForwardingListener");
|
||||
List<Method> methods = getPublicMethods(Player.Listener.class);
|
||||
for (int i = 0; i < methods.size(); i++) {
|
||||
Method method = methods.get(i);
|
||||
assertThat(forwardingListenerClass.getMethod(method.getName(), method.getParameterTypes()))
|
||||
.isNotNull();
|
||||
}
|
||||
}
|
||||
|
||||
private static class FakePlayer extends StubExoPlayer {
|
||||
private Commands availableCommands;
|
||||
/**
|
||||
* Supports up to 1 registered listener, named deliberately forwardingListener to emphasize its
|
||||
* purpose.
|
||||
*/
|
||||
@Nullable private Listener forwardingListener;
|
||||
|
||||
public FakePlayer() {
|
||||
this.availableCommands = Commands.EMPTY;
|
||||
}
|
||||
|
||||
public FakePlayer(@Command int... commands) {
|
||||
this.availableCommands = new Commands.Builder().addAll(commands).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addListener(Listener listener) {
|
||||
checkState(this.forwardingListener == null);
|
||||
this.forwardingListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeListener(Listener listener) {
|
||||
checkState(this.forwardingListener.equals(listener));
|
||||
this.forwardingListener = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Commands getAvailableCommands() {
|
||||
return availableCommands;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Looper getApplicationLooper() {
|
||||
return Looper.getMainLooper();
|
||||
}
|
||||
|
||||
public void setAvailableCommands(Commands availableCommands) {
|
||||
this.availableCommands = availableCommands;
|
||||
}
|
||||
}
|
||||
|
||||
private static Player.Commands buildCommands(@Player.Command int... commands) {
|
||||
return new Player.Commands.Builder().addAll(commands).build();
|
||||
}
|
||||
|
||||
private Class<?> getNestedClass(String className) {
|
||||
for (Class<?> declaredClass : ForwardingPlayer.class.getDeclaredClasses()) {
|
||||
if (declaredClass.getSimpleName().equals(className)) {
|
||||
return declaredClass;
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
private static class EventsMatcher implements ArgumentMatcher<Player.Events> {
|
||||
private final int[] events;
|
||||
|
||||
private EventsMatcher(int... events) {
|
||||
this.events = events;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(Player.Events argument) {
|
||||
if (events.length != argument.size()) {
|
||||
return false;
|
||||
}
|
||||
for (int event : events) {
|
||||
if (!argument.contains(event)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns all the methods of Java interface. */
|
||||
/** Returns all the public methods of a Java interface. */
|
||||
private static List<Method> getPublicMethods(Class<?> anInterface) {
|
||||
assertThat(anInterface.isInterface()).isTrue();
|
||||
checkArgument(anInterface.isInterface());
|
||||
// Run a BFS over all extended interfaces to inspect them all.
|
||||
Queue<Class<?>> interfacesQueue = new ArrayDeque<>();
|
||||
interfacesQueue.add(anInterface);
|
||||
@ -398,4 +203,43 @@ public class ForwardingPlayerTest {
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
private static Class<?> getInnerClass(String className) {
|
||||
for (Class<?> innerClass : ForwardingPlayer.class.getDeclaredClasses()) {
|
||||
if (innerClass.getSimpleName().equals(className)) {
|
||||
return innerClass;
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
private static class FakePlayer extends StubExoPlayer {
|
||||
|
||||
@SuppressWarnings("deprecation") // Use of deprecated type for backwards compatibility.
|
||||
private final Set<EventListener> eventListeners = new HashSet<>();
|
||||
|
||||
private final Set<Listener> listeners = new HashSet<>();
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation") // Implementing deprecated method.
|
||||
public void addListener(EventListener listener) {
|
||||
eventListeners.add(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addListener(Listener listener) {
|
||||
listeners.add(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation") // Implementing deprecated method.
|
||||
public void removeListener(EventListener listener) {
|
||||
eventListeners.remove(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeListener(Listener listener) {
|
||||
listeners.remove(listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user