Add dispatchPrepare(player) to ControlDispatcher

Issue: #7882
PiperOrigin-RevId: 341394254
This commit is contained in:
bachinger 2020-11-09 14:35:16 +00:00 committed by kim-vde
parent 1d4321b86e
commit b03df4e8b5
12 changed files with 92 additions and 36 deletions

View File

@ -29,6 +29,13 @@
* UI: * UI:
* Show overflow button in `StyledPlayerControlView` only when there is not * Show overflow button in `StyledPlayerControlView` only when there is not
enough space. enough space.
* Add `dispatchPrepare(Player)` to `ControlDispatcher` and implement it in
`DefaultControlDispatcher`. Deprecate `PlaybackPreparer` and
`setPlaybackPreparer` in `StyledPlayerView`, `StyledPlayerControlView`,
`PlayerView`, `PlayerControlView`, `PlayerNotificationManager` and
`LeanbackPlayerAdapter` and use `ControlDispatcher` for dispatching
prepare instead
([#7882](https://github.com/google/ExoPlayer/issues/7882)).
* Audio: * Audio:
* Retry playback after some types of `AudioTrack` error. * Retry playback after some types of `AudioTrack` error.
* Extractors: * Extractors:

View File

@ -34,7 +34,6 @@ import androidx.appcompat.app.AppCompatActivity;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.MediaItem; import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.PlaybackPreparer;
import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.RenderersFactory; import com.google.android.exoplayer2.RenderersFactory;
import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.SimpleExoPlayer;
@ -68,7 +67,7 @@ import java.util.List;
/** An activity that plays media using {@link SimpleExoPlayer}. */ /** An activity that plays media using {@link SimpleExoPlayer}. */
public class PlayerActivity extends AppCompatActivity public class PlayerActivity extends AppCompatActivity
implements OnClickListener, PlaybackPreparer, StyledPlayerControlView.VisibilityListener { implements OnClickListener, StyledPlayerControlView.VisibilityListener {
// Saved instance state keys. // Saved instance state keys.
@ -250,13 +249,6 @@ public class PlayerActivity extends AppCompatActivity
} }
} }
// PlaybackPreparer implementation
@Override
public void preparePlayback() {
player.prepare();
}
// PlayerControlView.VisibilityListener implementation // PlayerControlView.VisibilityListener implementation
@Override @Override
@ -302,7 +294,6 @@ public class PlayerActivity extends AppCompatActivity
player.setAudioAttributes(AudioAttributes.DEFAULT, /* handleAudioFocus= */ true); player.setAudioAttributes(AudioAttributes.DEFAULT, /* handleAudioFocus= */ true);
player.setPlayWhenReady(startAutoPlay); player.setPlayWhenReady(startAutoPlay);
playerView.setPlayer(player); playerView.setPlayer(player);
playerView.setPlaybackPreparer(this);
debugViewHelper = new DebugTextViewHelper(player, debugTextView); debugViewHelper = new DebugTextViewHelper(player, debugTextView);
debugViewHelper.start(); debugViewHelper.start();
} }

View File

@ -78,10 +78,15 @@ public final class LeanbackPlayerAdapter extends PlayerAdapter implements Runnab
} }
/** /**
* Sets the {@link PlaybackPreparer}. * @deprecated Use {@link #setControlDispatcher(ControlDispatcher)} instead. The adapter calls
* * {@link ControlDispatcher#dispatchPrepare(Player)} instead of {@link
* @param playbackPreparer The {@link PlaybackPreparer}. * PlaybackPreparer#preparePlayback()}. The {@link DefaultControlDispatcher} that the adapter
* uses by default, calls {@link Player#prepare()}. If you wish to customize this behaviour,
* you can provide a custom implementation of {@link
* ControlDispatcher#dispatchPrepare(Player)}.
*/ */
@SuppressWarnings("deprecation")
@Deprecated
public void setPlaybackPreparer(@Nullable PlaybackPreparer playbackPreparer) { public void setPlaybackPreparer(@Nullable PlaybackPreparer playbackPreparer) {
this.playbackPreparer = playbackPreparer; this.playbackPreparer = playbackPreparer;
} }
@ -167,11 +172,15 @@ public final class LeanbackPlayerAdapter extends PlayerAdapter implements Runnab
return player.getPlaybackState() == Player.STATE_IDLE ? -1 : player.getCurrentPosition(); return player.getPlaybackState() == Player.STATE_IDLE ? -1 : player.getCurrentPosition();
} }
// Calls deprecated method to provide backwards compatibility.
@SuppressWarnings("deprecation")
@Override @Override
public void play() { public void play() {
if (player.getPlaybackState() == Player.STATE_IDLE) { if (player.getPlaybackState() == Player.STATE_IDLE) {
if (playbackPreparer != null) { if (playbackPreparer != null) {
playbackPreparer.preparePlayback(); playbackPreparer.preparePlayback();
} else {
controlDispatcher.dispatchPrepare(player);
} }
} else if (player.getPlaybackState() == Player.STATE_ENDED) { } else if (player.getPlaybackState() == Player.STATE_ENDED) {
controlDispatcher.dispatchSeekTo(player, player.getCurrentWindowIndex(), C.TIME_UNSET); controlDispatcher.dispatchSeekTo(player, player.getCurrentWindowIndex(), C.TIME_UNSET);

View File

@ -1147,6 +1147,8 @@ public final class MediaSessionConnector {
if (player.getPlaybackState() == Player.STATE_IDLE) { if (player.getPlaybackState() == Player.STATE_IDLE) {
if (playbackPreparer != null) { if (playbackPreparer != null) {
playbackPreparer.onPrepare(/* playWhenReady= */ true); playbackPreparer.onPrepare(/* playWhenReady= */ true);
} else {
controlDispatcher.dispatchPrepare(player);
} }
} else if (player.getPlaybackState() == Player.STATE_ENDED) { } else if (player.getPlaybackState() == Player.STATE_ENDED) {
seekTo(player, player.getCurrentWindowIndex(), C.TIME_UNSET); seekTo(player, player.getCurrentWindowIndex(), C.TIME_UNSET);

View File

@ -26,6 +26,14 @@ import com.google.android.exoplayer2.Player.RepeatMode;
*/ */
public interface ControlDispatcher { public interface ControlDispatcher {
/**
* Dispatches a {@link Player#prepare()} operation.
*
* @param player The {@link Player} to which the operation should be dispatched.
* @return True if the operation was dispatched. False if suppressed.
*/
boolean dispatchPrepare(Player player);
/** /**
* Dispatches a {@link Player#setPlayWhenReady(boolean)} operation. * Dispatches a {@link Player#setPlayWhenReady(boolean)} operation.
* *

View File

@ -52,6 +52,12 @@ public class DefaultControlDispatcher implements ControlDispatcher {
window = new Timeline.Window(); window = new Timeline.Window();
} }
@Override
public boolean dispatchPrepare(Player player) {
player.prepare();
return true;
}
@Override @Override
public boolean dispatchSetPlayWhenReady(Player player, boolean playWhenReady) { public boolean dispatchSetPlayWhenReady(Player player, boolean playWhenReady) {
player.setPlayWhenReady(playWhenReady); player.setPlayWhenReady(playWhenReady);

View File

@ -15,9 +15,11 @@
*/ */
package com.google.android.exoplayer2; package com.google.android.exoplayer2;
/** Called to prepare a playback. */ /** @deprecated Use {@link ControlDispatcher} instead. */
@Deprecated
public interface PlaybackPreparer { public interface PlaybackPreparer {
/** Called to prepare a playback. */ /** @deprecated Use {@link ControlDispatcher#dispatchPrepare(Player)} instead. */
@Deprecated
void preparePlayback(); void preparePlayback();
} }

View File

@ -611,11 +611,15 @@ public class PlayerControlView extends FrameLayout {
} }
/** /**
* Sets the {@link PlaybackPreparer}. * @deprecated Use {@link #setControlDispatcher(ControlDispatcher)} instead. The view calls {@link
* * ControlDispatcher#dispatchPrepare(Player)} instead of {@link
* @param playbackPreparer The {@link PlaybackPreparer}, or null to remove the current playback * PlaybackPreparer#preparePlayback()}. The {@link DefaultControlDispatcher} that the view
* preparer. * uses by default, calls {@link Player#prepare()}. If you wish to customize this behaviour,
* you can provide a custom implementation of {@link
* ControlDispatcher#dispatchPrepare(Player)}.
*/ */
@SuppressWarnings("deprecation")
@Deprecated
public void setPlaybackPreparer(@Nullable PlaybackPreparer playbackPreparer) { public void setPlaybackPreparer(@Nullable PlaybackPreparer playbackPreparer) {
this.playbackPreparer = playbackPreparer; this.playbackPreparer = playbackPreparer;
} }
@ -1254,11 +1258,14 @@ public class PlayerControlView extends FrameLayout {
} }
} }
@SuppressWarnings("deprecation")
private void dispatchPlay(Player player) { private void dispatchPlay(Player player) {
@State int state = player.getPlaybackState(); @State int state = player.getPlaybackState();
if (state == Player.STATE_IDLE) { if (state == Player.STATE_IDLE) {
if (playbackPreparer != null) { if (playbackPreparer != null) {
playbackPreparer.preparePlayback(); playbackPreparer.preparePlayback();
} else {
controlDispatcher.dispatchPrepare(player);
} }
} else if (state == Player.STATE_ENDED) { } else if (state == Player.STATE_ENDED) {
seekTo(player, player.getCurrentWindowIndex(), C.TIME_UNSET); seekTo(player, player.getCurrentWindowIndex(), C.TIME_UNSET);

View File

@ -682,10 +682,16 @@ public class PlayerNotificationManager {
} }
/** /**
* Sets the {@link PlaybackPreparer}. * @deprecated Use {@link #setControlDispatcher(ControlDispatcher)} instead. The manager calls
* * {@link ControlDispatcher#dispatchPrepare(Player)} instead of {@link
* @param playbackPreparer The {@link PlaybackPreparer}. * PlaybackPreparer#preparePlayback()}. The {@link DefaultControlDispatcher} that this manager
* uses by default, calls {@link Player#prepare()}. If you wish to intercept or customize this
* behaviour, you can provide a custom implementation of {@link
* ControlDispatcher#dispatchPrepare(Player)} and pass it to {@link
* #setControlDispatcher(ControlDispatcher)}.
*/ */
@SuppressWarnings("deprecation")
@Deprecated
public void setPlaybackPreparer(@Nullable PlaybackPreparer playbackPreparer) { public void setPlaybackPreparer(@Nullable PlaybackPreparer playbackPreparer) {
this.playbackPreparer = playbackPreparer; this.playbackPreparer = playbackPreparer;
} }
@ -1039,8 +1045,7 @@ public class PlayerNotificationManager {
@Nullable NotificationCompat.Builder builder, @Nullable NotificationCompat.Builder builder,
boolean ongoing, boolean ongoing,
@Nullable Bitmap largeIcon) { @Nullable Bitmap largeIcon) {
if (player.getPlaybackState() == Player.STATE_IDLE if (player.getPlaybackState() == Player.STATE_IDLE && player.getCurrentTimeline().isEmpty()) {
&& (player.getCurrentTimeline().isEmpty() || playbackPreparer == null)) {
builderActions = null; builderActions = null;
return null; return null;
} }
@ -1369,6 +1374,7 @@ public class PlayerNotificationManager {
private class NotificationBroadcastReceiver extends BroadcastReceiver { private class NotificationBroadcastReceiver extends BroadcastReceiver {
@SuppressWarnings("deprecation")
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
Player player = PlayerNotificationManager.this.player; Player player = PlayerNotificationManager.this.player;
@ -1382,6 +1388,8 @@ public class PlayerNotificationManager {
if (player.getPlaybackState() == Player.STATE_IDLE) { if (player.getPlaybackState() == Player.STATE_IDLE) {
if (playbackPreparer != null) { if (playbackPreparer != null) {
playbackPreparer.preparePlayback(); playbackPreparer.preparePlayback();
} else {
controlDispatcher.dispatchPrepare(player);
} }
} else if (player.getPlaybackState() == Player.STATE_ENDED) { } else if (player.getPlaybackState() == Player.STATE_ENDED) {
controlDispatcher.dispatchSeekTo(player, player.getCurrentWindowIndex(), C.TIME_UNSET); controlDispatcher.dispatchSeekTo(player, player.getCurrentWindowIndex(), C.TIME_UNSET);

View File

@ -982,11 +982,15 @@ public class PlayerView extends FrameLayout implements AdsLoader.AdViewProvider
} }
/** /**
* Sets the {@link PlaybackPreparer}. * @deprecated Use {@link #setControlDispatcher(ControlDispatcher)} instead. The view calls {@link
* * ControlDispatcher#dispatchPrepare(Player)} instead of {@link
* @param playbackPreparer The {@link PlaybackPreparer}, or null to remove the current playback * PlaybackPreparer#preparePlayback()}. The {@link DefaultControlDispatcher} that the view
* preparer. * uses by default, calls {@link Player#prepare()}. If you wish to customize this behaviour,
* you can provide a custom implementation of {@link
* ControlDispatcher#dispatchPrepare(Player)}.
*/ */
@SuppressWarnings("deprecation")
@Deprecated
public void setPlaybackPreparer(@Nullable PlaybackPreparer playbackPreparer) { public void setPlaybackPreparer(@Nullable PlaybackPreparer playbackPreparer) {
Assertions.checkStateNotNull(controller); Assertions.checkStateNotNull(controller);
controller.setPlaybackPreparer(playbackPreparer); controller.setPlaybackPreparer(playbackPreparer);

View File

@ -834,11 +834,15 @@ public class StyledPlayerControlView extends FrameLayout {
} }
/** /**
* Sets the {@link PlaybackPreparer}. * @deprecated Use {@link #setControlDispatcher(ControlDispatcher)} instead. The view calls {@link
* * ControlDispatcher#dispatchPrepare(Player)} instead of {@link
* @param playbackPreparer The {@link PlaybackPreparer}, or null to remove the current playback * PlaybackPreparer#preparePlayback()}. The {@link DefaultControlDispatcher} that the view
* preparer. * uses by default, calls {@link Player#prepare()}. If you wish to customize this behaviour,
* you can provide a custom implementation of {@link
* ControlDispatcher#dispatchPrepare(Player)}.
*/ */
@SuppressWarnings("deprecation")
@Deprecated
public void setPlaybackPreparer(@Nullable PlaybackPreparer playbackPreparer) { public void setPlaybackPreparer(@Nullable PlaybackPreparer playbackPreparer) {
this.playbackPreparer = playbackPreparer; this.playbackPreparer = playbackPreparer;
} }
@ -1698,11 +1702,14 @@ public class StyledPlayerControlView extends FrameLayout {
} }
} }
@SuppressWarnings("deprecation")
private void dispatchPlay(Player player) { private void dispatchPlay(Player player) {
@State int state = player.getPlaybackState(); @State int state = player.getPlaybackState();
if (state == Player.STATE_IDLE) { if (state == Player.STATE_IDLE) {
if (playbackPreparer != null) { if (playbackPreparer != null) {
playbackPreparer.preparePlayback(); playbackPreparer.preparePlayback();
} else {
controlDispatcher.dispatchPrepare(player);
} }
} else if (state == Player.STATE_ENDED) { } else if (state == Player.STATE_ENDED) {
seekTo(player, player.getCurrentWindowIndex(), C.TIME_UNSET); seekTo(player, player.getCurrentWindowIndex(), C.TIME_UNSET);

View File

@ -45,6 +45,7 @@ import androidx.annotation.RequiresApi;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ControlDispatcher; import com.google.android.exoplayer2.ControlDispatcher;
import com.google.android.exoplayer2.DefaultControlDispatcher;
import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.PlaybackPreparer; import com.google.android.exoplayer2.PlaybackPreparer;
import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Player;
@ -977,11 +978,15 @@ public class StyledPlayerView extends FrameLayout implements AdsLoader.AdViewPro
} }
/** /**
* Sets the {@link PlaybackPreparer}. * @deprecated Use {@link #setControlDispatcher(ControlDispatcher)} instead. The view calls {@link
* * ControlDispatcher#dispatchPrepare(Player)} instead of {@link
* @param playbackPreparer The {@link PlaybackPreparer}, or null to remove the current playback * PlaybackPreparer#preparePlayback()}. The {@link DefaultControlDispatcher} that the view
* preparer. * uses by default, calls {@link Player#prepare()}. If you wish to customize this behaviour,
* you can provide a custom implementation of {@link
* ControlDispatcher#dispatchPrepare(Player)}.
*/ */
@SuppressWarnings("deprecation")
@Deprecated
public void setPlaybackPreparer(@Nullable PlaybackPreparer playbackPreparer) { public void setPlaybackPreparer(@Nullable PlaybackPreparer playbackPreparer) {
Assertions.checkStateNotNull(controller); Assertions.checkStateNotNull(controller);
controller.setPlaybackPreparer(playbackPreparer); controller.setPlaybackPreparer(playbackPreparer);