diff --git a/library/common/src/main/java/com/google/android/exoplayer2/ForwardingPlayer.java b/library/common/src/main/java/com/google/android/exoplayer2/ForwardingPlayer.java
index e9202190f8..2925987807 100644
--- a/library/common/src/main/java/com/google/android/exoplayer2/ForwardingPlayer.java
+++ b/library/common/src/main/java/com/google/android/exoplayer2/ForwardingPlayer.java
@@ -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.
- *
- *
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}.
- *
- *
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 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 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 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 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 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 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();
}
}
diff --git a/library/common/src/test/java/com/google/android/exoplayer2/ForwardingPlayerTest.java b/library/common/src/test/java/com/google/android/exoplayer2/ForwardingPlayerTest.java
index 944db43330..edfe2693d5 100644
--- a/library/common/src/test/java/com/google/android/exoplayer2/ForwardingPlayerTest.java
+++ b/library/common/src/test/java/com/google/android/exoplayer2/ForwardingPlayerTest.java
@@ -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 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 playerMethods = getPublicMethods(Player.class);
- for (int i = 0; i < playerMethods.size(); i++) {
- Method method = playerMethods.get(i);
+ List 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 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 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 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 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 {
- 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 getPublicMethods(Class> anInterface) {
- assertThat(anInterface.isInterface()).isTrue();
+ checkArgument(anInterface.isInterface());
// Run a BFS over all extended interfaces to inspect them all.
Queue> 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 eventListeners = new HashSet<>();
+
+ private final Set 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);
+ }
+ }
}