add playWhenReady changed callback with reasons
PiperOrigin-RevId: 292194805
This commit is contained in:
parent
6424403907
commit
21fe13d3d7
@ -6,6 +6,7 @@
|
||||
* Add playlist API ([#6161](https://github.com/google/ExoPlayer/issues/6161)).
|
||||
* Add `play` and `pause` methods to `Player`.
|
||||
* Add `Player.getCurrentLiveOffset` to conveniently return the live offset.
|
||||
* Add `Player.onPlayWhenReadyChanged` with reasons.
|
||||
* Make `MediaSourceEventListener.LoadEventInfo` and
|
||||
`MediaSourceEventListener.MediaLoadData` top-level classes.
|
||||
* Rename `MediaCodecRenderer.onOutputFormatChanged` to
|
||||
|
@ -351,7 +351,8 @@ public final class CastPlayer extends BasePlayer {
|
||||
// We update the local state and send the message to the receiver app, which will cause the
|
||||
// operation to be perceived as synchronous by the user. When the operation reports a result,
|
||||
// the local state will be updated to reflect the state reported by the Cast SDK.
|
||||
setPlayerStateAndNotifyIfChanged(playWhenReady, playbackState);
|
||||
setPlayerStateAndNotifyIfChanged(
|
||||
playWhenReady, PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST, playbackState);
|
||||
flushNotifications();
|
||||
PendingResult<MediaChannelResult> pendingResult =
|
||||
playWhenReady ? remoteMediaClient.play() : remoteMediaClient.pause();
|
||||
@ -625,8 +626,14 @@ public final class CastPlayer extends BasePlayer {
|
||||
newPlayWhenReadyValue = !remoteMediaClient.isPaused();
|
||||
playWhenReady.clearPendingResultCallback();
|
||||
}
|
||||
@PlayWhenReadyChangeReason
|
||||
int playWhenReadyChangeReason =
|
||||
newPlayWhenReadyValue != playWhenReady.value
|
||||
? PLAY_WHEN_READY_CHANGE_REASON_REMOTE
|
||||
: PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST;
|
||||
// We do not mask the playback state, so try setting it regardless of the playWhenReady masking.
|
||||
setPlayerStateAndNotifyIfChanged(newPlayWhenReadyValue, fetchPlaybackState(remoteMediaClient));
|
||||
setPlayerStateAndNotifyIfChanged(
|
||||
newPlayWhenReadyValue, playWhenReadyChangeReason, fetchPlaybackState(remoteMediaClient));
|
||||
}
|
||||
|
||||
@RequiresNonNull("remoteMediaClient")
|
||||
@ -718,13 +725,21 @@ public final class CastPlayer extends BasePlayer {
|
||||
}
|
||||
|
||||
private void setPlayerStateAndNotifyIfChanged(
|
||||
boolean playWhenReady, @Player.State int playbackState) {
|
||||
if (this.playWhenReady.value != playWhenReady || this.playbackState != playbackState) {
|
||||
this.playWhenReady.value = playWhenReady;
|
||||
boolean playWhenReady,
|
||||
@Player.PlayWhenReadyChangeReason int playWhenReadyChangeReason,
|
||||
@Player.State int playbackState) {
|
||||
boolean playWhenReadyChanged = this.playWhenReady.value != playWhenReady;
|
||||
if (playWhenReadyChanged || this.playbackState != playbackState) {
|
||||
this.playbackState = playbackState;
|
||||
this.playWhenReady.value = playWhenReady;
|
||||
notificationsBatch.add(
|
||||
new ListenerNotificationTask(
|
||||
listener -> listener.onPlayerStateChanged(playWhenReady, playbackState)));
|
||||
listener -> {
|
||||
listener.onPlayerStateChanged(playWhenReady, playbackState);
|
||||
if (playWhenReadyChanged) {
|
||||
listener.onPlayWhenReadyChanged(playWhenReady, playWhenReadyChangeReason);
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -89,6 +89,8 @@ public class CastPlayerTest {
|
||||
verify(mockPendingResult).setResultCallback(setResultCallbackArgumentCaptor.capture());
|
||||
assertThat(castPlayer.getPlayWhenReady()).isTrue();
|
||||
verify(mockListener).onPlayerStateChanged(true, Player.STATE_IDLE);
|
||||
verify(mockListener)
|
||||
.onPlayWhenReadyChanged(true, Player.PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST);
|
||||
|
||||
// There is a status update in the middle, which should be hidden by masking.
|
||||
remoteMediaClientListener.onStatusUpdated();
|
||||
@ -111,21 +113,42 @@ public class CastPlayerTest {
|
||||
verify(mockPendingResult).setResultCallback(setResultCallbackArgumentCaptor.capture());
|
||||
assertThat(castPlayer.getPlayWhenReady()).isTrue();
|
||||
verify(mockListener).onPlayerStateChanged(true, Player.STATE_IDLE);
|
||||
verify(mockListener)
|
||||
.onPlayWhenReadyChanged(true, Player.PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST);
|
||||
|
||||
// Upon result, the remote media client is still paused. The state should reflect that.
|
||||
setResultCallbackArgumentCaptor
|
||||
.getValue()
|
||||
.onResult(Mockito.mock(RemoteMediaClient.MediaChannelResult.class));
|
||||
verify(mockListener).onPlayerStateChanged(false, Player.STATE_IDLE);
|
||||
verify(mockListener).onPlayWhenReadyChanged(false, Player.PLAY_WHEN_READY_CHANGE_REASON_REMOTE);
|
||||
assertThat(castPlayer.getPlayWhenReady()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetPlayWhenReady_correctChangeReasonOnPause() {
|
||||
when(mockRemoteMediaClient.play()).thenReturn(mockPendingResult);
|
||||
when(mockRemoteMediaClient.pause()).thenReturn(mockPendingResult);
|
||||
castPlayer.play();
|
||||
assertThat(castPlayer.getPlayWhenReady()).isTrue();
|
||||
verify(mockListener).onPlayerStateChanged(true, Player.STATE_IDLE);
|
||||
verify(mockListener)
|
||||
.onPlayWhenReadyChanged(true, Player.PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST);
|
||||
|
||||
castPlayer.pause();
|
||||
assertThat(castPlayer.getPlayWhenReady()).isFalse();
|
||||
verify(mockListener).onPlayerStateChanged(false, Player.STATE_IDLE);
|
||||
verify(mockListener)
|
||||
.onPlayWhenReadyChanged(false, Player.PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPlayWhenReady_changesOnStatusUpdates() {
|
||||
assertThat(castPlayer.getPlayWhenReady()).isFalse();
|
||||
when(mockRemoteMediaClient.isPaused()).thenReturn(false);
|
||||
remoteMediaClientListener.onStatusUpdated();
|
||||
verify(mockListener).onPlayerStateChanged(true, Player.STATE_IDLE);
|
||||
verify(mockListener).onPlayWhenReadyChanged(true, Player.PLAY_WHEN_READY_CHANGE_REASON_REMOTE);
|
||||
assertThat(castPlayer.getPlayWhenReady()).isTrue();
|
||||
}
|
||||
|
||||
|
@ -93,12 +93,17 @@ import java.util.ArrayList;
|
||||
|
||||
/** Sets the {@link Player.State} of this player. */
|
||||
public void setState(@Player.State int state, boolean playWhenReady) {
|
||||
boolean notify = this.state != state || this.playWhenReady != playWhenReady;
|
||||
boolean playWhenReadyChanged = this.playWhenReady != playWhenReady;
|
||||
boolean playerStateChanged = this.state != state || playWhenReadyChanged;
|
||||
this.state = state;
|
||||
this.playWhenReady = playWhenReady;
|
||||
if (notify) {
|
||||
if (playerStateChanged) {
|
||||
for (Player.EventListener listener : listeners) {
|
||||
listener.onPlayerStateChanged(playWhenReady, state);
|
||||
if (playWhenReadyChanged) {
|
||||
listener.onPlayWhenReadyChanged(
|
||||
playWhenReady, PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -424,11 +424,16 @@ import java.util.concurrent.TimeoutException;
|
||||
|
||||
@Override
|
||||
public void setPlayWhenReady(boolean playWhenReady) {
|
||||
setPlayWhenReady(playWhenReady, PLAYBACK_SUPPRESSION_REASON_NONE);
|
||||
setPlayWhenReady(
|
||||
playWhenReady,
|
||||
PLAYBACK_SUPPRESSION_REASON_NONE,
|
||||
PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST);
|
||||
}
|
||||
|
||||
public void setPlayWhenReady(
|
||||
boolean playWhenReady, @PlaybackSuppressionReason int playbackSuppressionReason) {
|
||||
boolean playWhenReady,
|
||||
@PlaybackSuppressionReason int playbackSuppressionReason,
|
||||
@PlayWhenReadyChangeReason int playWhenReadyChangeReason) {
|
||||
boolean oldIsPlaying = isPlaying();
|
||||
boolean oldInternalPlayWhenReady =
|
||||
this.playWhenReady && this.playbackSuppressionReason == PLAYBACK_SUPPRESSION_REASON_NONE;
|
||||
@ -450,6 +455,9 @@ import java.util.concurrent.TimeoutException;
|
||||
if (playWhenReadyChanged) {
|
||||
listener.onPlayerStateChanged(playWhenReady, playbackState);
|
||||
}
|
||||
if (playWhenReadyChanged) {
|
||||
listener.onPlayWhenReadyChanged(playWhenReady, playWhenReadyChangeReason);
|
||||
}
|
||||
if (suppressionReasonChanged) {
|
||||
listener.onPlaybackSuppressionReasonChanged(playbackSuppressionReason);
|
||||
}
|
||||
|
@ -418,6 +418,15 @@ public interface Player {
|
||||
*/
|
||||
default void onPlayerStateChanged(boolean playWhenReady, @State int playbackState) {}
|
||||
|
||||
/**
|
||||
* Called when the value returned from {@link #getPlayWhenReady()} changes.
|
||||
*
|
||||
* @param playWhenReady Whether playback will proceed when ready.
|
||||
* @param reason The {@link PlayWhenReadyChangeReason reason} for the change.
|
||||
*/
|
||||
default void onPlayWhenReadyChanged(
|
||||
boolean playWhenReady, @PlayWhenReadyChangeReason int reason) {}
|
||||
|
||||
/**
|
||||
* Called when the value returned from {@link #getPlaybackSuppressionReason()} changes.
|
||||
*
|
||||
@ -549,6 +558,31 @@ public interface Player {
|
||||
*/
|
||||
int STATE_ENDED = 4;
|
||||
|
||||
/**
|
||||
* Reasons for {@link #getPlayWhenReady() playWhenReady} changes. One of {@link
|
||||
* #PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST}, {@link
|
||||
* #PLAY_WHEN_READY_CHANGE_REASON_AUDIO_FOCUS_LOSS}, {@link
|
||||
* #PLAY_WHEN_READY_CHANGE_REASON_AUDIO_BECOMING_NOISY} or {@link
|
||||
* #PLAY_WHEN_READY_CHANGE_REASON_REMOTE}.
|
||||
*/
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef({
|
||||
PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST,
|
||||
PLAY_WHEN_READY_CHANGE_REASON_AUDIO_FOCUS_LOSS,
|
||||
PLAY_WHEN_READY_CHANGE_REASON_AUDIO_BECOMING_NOISY,
|
||||
PLAY_WHEN_READY_CHANGE_REASON_REMOTE
|
||||
})
|
||||
@interface PlayWhenReadyChangeReason {}
|
||||
/** Playback has been started or paused by the user. */
|
||||
int PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST = 1;
|
||||
/** Playback has been paused because of a loss of audio focus. */
|
||||
int PLAY_WHEN_READY_CHANGE_REASON_AUDIO_FOCUS_LOSS = 2;
|
||||
/** Playback has been paused to avoid becoming noisy. */
|
||||
int PLAY_WHEN_READY_CHANGE_REASON_AUDIO_BECOMING_NOISY = 3;
|
||||
/** Playback has been started or paused because of a remote change. */
|
||||
int PLAY_WHEN_READY_CHANGE_REASON_REMOTE = 4;
|
||||
|
||||
/**
|
||||
* Reason why playback is suppressed even though {@link #getPlayWhenReady()} is {@code true}. One
|
||||
* of {@link #PLAYBACK_SUPPRESSION_REASON_NONE} or {@link
|
||||
|
@ -688,11 +688,13 @@ public class SimpleExoPlayer extends BasePlayer
|
||||
}
|
||||
}
|
||||
|
||||
boolean playWhenReady = getPlayWhenReady();
|
||||
@AudioFocusManager.PlayerCommand
|
||||
int playerCommand =
|
||||
audioFocusManager.setAudioAttributes(
|
||||
handleAudioFocus ? audioAttributes : null, getPlayWhenReady(), getPlaybackState());
|
||||
updatePlayWhenReady(getPlayWhenReady(), playerCommand);
|
||||
handleAudioFocus ? audioAttributes : null, playWhenReady, getPlaybackState());
|
||||
updatePlayWhenReady(
|
||||
playWhenReady, playerCommand, getPlayWhenReadyChangeReason(playWhenReady, playerCommand));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1179,9 +1181,11 @@ public class SimpleExoPlayer extends BasePlayer
|
||||
@Override
|
||||
public void prepare() {
|
||||
verifyApplicationThread();
|
||||
boolean playWhenReady = getPlayWhenReady();
|
||||
@AudioFocusManager.PlayerCommand
|
||||
int playerCommand = audioFocusManager.handlePrepare(getPlayWhenReady());
|
||||
updatePlayWhenReady(getPlayWhenReady(), playerCommand);
|
||||
int playerCommand = audioFocusManager.handlePrepare(playWhenReady);
|
||||
updatePlayWhenReady(
|
||||
playWhenReady, playerCommand, getPlayWhenReadyChangeReason(playWhenReady, playerCommand));
|
||||
player.prepare();
|
||||
}
|
||||
|
||||
@ -1318,7 +1322,8 @@ public class SimpleExoPlayer extends BasePlayer
|
||||
verifyApplicationThread();
|
||||
@AudioFocusManager.PlayerCommand
|
||||
int playerCommand = audioFocusManager.handleSetPlayWhenReady(playWhenReady, getPlaybackState());
|
||||
updatePlayWhenReady(playWhenReady, playerCommand);
|
||||
updatePlayWhenReady(
|
||||
playWhenReady, playerCommand, getPlayWhenReadyChangeReason(playWhenReady, playerCommand));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1634,14 +1639,16 @@ public class SimpleExoPlayer extends BasePlayer
|
||||
}
|
||||
|
||||
private void updatePlayWhenReady(
|
||||
boolean playWhenReady, @AudioFocusManager.PlayerCommand int playerCommand) {
|
||||
boolean playWhenReady,
|
||||
@AudioFocusManager.PlayerCommand int playerCommand,
|
||||
@Player.PlayWhenReadyChangeReason int playWhenReadyChangeReason) {
|
||||
playWhenReady = playWhenReady && playerCommand != AudioFocusManager.PLAYER_COMMAND_DO_NOT_PLAY;
|
||||
@PlaybackSuppressionReason
|
||||
int playbackSuppressionReason =
|
||||
playWhenReady && playerCommand != AudioFocusManager.PLAYER_COMMAND_PLAY_WHEN_READY
|
||||
? Player.PLAYBACK_SUPPRESSION_REASON_TRANSIENT_AUDIO_FOCUS_LOSS
|
||||
: Player.PLAYBACK_SUPPRESSION_REASON_NONE;
|
||||
player.setPlayWhenReady(playWhenReady, playbackSuppressionReason);
|
||||
player.setPlayWhenReady(playWhenReady, playbackSuppressionReason, playWhenReadyChangeReason);
|
||||
}
|
||||
|
||||
private void verifyApplicationThread() {
|
||||
@ -1655,6 +1662,12 @@ public class SimpleExoPlayer extends BasePlayer
|
||||
}
|
||||
}
|
||||
|
||||
private static int getPlayWhenReadyChangeReason(boolean playWhenReady, int playerCommand) {
|
||||
return playWhenReady && playerCommand != AudioFocusManager.PLAYER_COMMAND_PLAY_WHEN_READY
|
||||
? PLAY_WHEN_READY_CHANGE_REASON_AUDIO_FOCUS_LOSS
|
||||
: PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST;
|
||||
}
|
||||
|
||||
private final class ComponentListener
|
||||
implements VideoRendererEventListener,
|
||||
AudioRendererEventListener,
|
||||
@ -1872,14 +1885,23 @@ public class SimpleExoPlayer extends BasePlayer
|
||||
|
||||
@Override
|
||||
public void executePlayerCommand(@AudioFocusManager.PlayerCommand int playerCommand) {
|
||||
updatePlayWhenReady(getPlayWhenReady(), playerCommand);
|
||||
boolean playWhenReady = getPlayWhenReady();
|
||||
updatePlayWhenReady(
|
||||
playWhenReady, playerCommand, getPlayWhenReadyChangeReason(playWhenReady, playerCommand));
|
||||
}
|
||||
|
||||
// AudioBecomingNoisyManager.EventListener implementation.
|
||||
|
||||
@Override
|
||||
public void onAudioBecomingNoisy() {
|
||||
pause();
|
||||
// Command is always PLAYER_COMMAND_DO_NOT_PLAY but the call is needed to abandon the
|
||||
// audio focus if the focus is currently held.
|
||||
int playerCommand =
|
||||
audioFocusManager.handleSetPlayWhenReady(/* playWhenReady= */ false, getPlaybackState());
|
||||
updatePlayWhenReady(
|
||||
/* playWhenReady= */ false,
|
||||
playerCommand,
|
||||
Player.PLAY_WHEN_READY_CHANGE_REASON_AUDIO_BECOMING_NOISY);
|
||||
}
|
||||
|
||||
// Player.EventListener implementation.
|
||||
|
@ -446,6 +446,15 @@ public class AnalyticsCollector
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void onPlayWhenReadyChanged(
|
||||
boolean playWhenReady, @Player.PlayWhenReadyChangeReason int reason) {
|
||||
EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime();
|
||||
for (AnalyticsListener listener : listeners) {
|
||||
listener.onPlayWhenReadyChanged(eventTime, playWhenReady, reason);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlaybackSuppressionReasonChanged(
|
||||
@PlaybackSuppressionReason int playbackSuppressionReason) {
|
||||
|
@ -133,6 +133,16 @@ public interface AnalyticsListener {
|
||||
default void onPlayerStateChanged(
|
||||
EventTime eventTime, boolean playWhenReady, @Player.State int playbackState) {}
|
||||
|
||||
/**
|
||||
* Called when the value changed that indicates whether playback will proceed when ready.
|
||||
*
|
||||
* @param eventTime The event time.
|
||||
* @param playWhenReady Whether playback will proceed when ready.
|
||||
* @param reason The {@link Player.PlayWhenReadyChangeReason reason} of the change.
|
||||
*/
|
||||
default void onPlayWhenReadyChanged(
|
||||
EventTime eventTime, boolean playWhenReady, @Player.PlayWhenReadyChangeReason int reason) {}
|
||||
|
||||
/**
|
||||
* Called when playback suppression reason changed.
|
||||
*
|
||||
|
@ -98,7 +98,16 @@ public class EventLogger implements AnalyticsListener {
|
||||
@Override
|
||||
public void onPlayerStateChanged(
|
||||
EventTime eventTime, boolean playWhenReady, @Player.State int state) {
|
||||
logd(eventTime, "state", playWhenReady + ", " + getStateString(state));
|
||||
logd(eventTime, "state", getStateString(state));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayWhenReadyChanged(
|
||||
EventTime eventTime, boolean playWhenReady, @Player.PlayWhenReadyChangeReason int reason) {
|
||||
logd(
|
||||
eventTime,
|
||||
"playWhenReady",
|
||||
playWhenReady + ", " + getPlayWhenReadyChangeReasonString(reason));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -637,4 +646,20 @@ public class EventLogger implements AnalyticsListener {
|
||||
return "?";
|
||||
}
|
||||
}
|
||||
|
||||
private static String getPlayWhenReadyChangeReasonString(
|
||||
@Player.PlayWhenReadyChangeReason int reason) {
|
||||
switch (reason) {
|
||||
case Player.PLAY_WHEN_READY_CHANGE_REASON_AUDIO_BECOMING_NOISY:
|
||||
return "AUDIO_BECOMING_NOISY";
|
||||
case Player.PLAY_WHEN_READY_CHANGE_REASON_AUDIO_FOCUS_LOSS:
|
||||
return "AUDIO_FOCUS_LOSS";
|
||||
case Player.PLAY_WHEN_READY_CHANGE_REASON_REMOTE:
|
||||
return "REMOTE";
|
||||
case Player.PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST:
|
||||
return "USER_REQUEST";
|
||||
default:
|
||||
return "?";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user