Make sure CastPlayer calls onIsPlaying if required

Before this change we checked whether the playback state and playWhenReady have changed when the state from the cast device arrived. If we detected such a change we called the listener callback `onIsPlayingChanged`. However, in the case when `setPlayWhenReady(boolean)` is called on 'CastPlayer', we mask the change in `playWhenReady`, then send the play/pause request to the cast device and when the state from the cast device arrives we never detect a change because we have already masked `playWhenReady`.

This change now moves the check for `isPlaying` to the same place where the state and playWhenReady is updated, so we call the `onIsPlayingChanged` callback in either case, when masking or when a changed state from the server arrives.

Issue: google/ExoPlayer#9792
PiperOrigin-RevId: 418483509
This commit is contained in:
bachinger 2021-12-27 13:43:21 +00:00 committed by tonihei
parent b440b0fc03
commit 967acf3049
2 changed files with 37 additions and 12 deletions

View File

@ -818,13 +818,7 @@ public final class CastPlayer extends BasePlayer {
!getCurrentTimeline().isEmpty() !getCurrentTimeline().isEmpty()
? getCurrentTimeline().getPeriod(oldWindowIndex, period, /* setIds= */ true).uid ? getCurrentTimeline().getPeriod(oldWindowIndex, period, /* setIds= */ true).uid
: null; : null;
boolean wasPlaying = playbackState == Player.STATE_READY && playWhenReady.value;
updatePlayerStateAndNotifyIfChanged(/* resultCallback= */ null); updatePlayerStateAndNotifyIfChanged(/* resultCallback= */ null);
boolean isPlaying = playbackState == Player.STATE_READY && playWhenReady.value;
if (wasPlaying != isPlaying) {
listeners.queueEvent(
Player.EVENT_IS_PLAYING_CHANGED, listener -> listener.onIsPlayingChanged(isPlaying));
}
updateRepeatModeAndNotifyIfChanged(/* resultCallback= */ null); updateRepeatModeAndNotifyIfChanged(/* resultCallback= */ null);
updatePlaybackRateAndNotifyIfChanged(/* resultCallback= */ null); updatePlaybackRateAndNotifyIfChanged(/* resultCallback= */ null);
boolean playingPeriodChangedByTimelineChange = updateTimelineAndNotifyIfChanged(); boolean playingPeriodChangedByTimelineChange = updateTimelineAndNotifyIfChanged();
@ -1218,6 +1212,7 @@ public final class CastPlayer extends BasePlayer {
boolean playWhenReady, boolean playWhenReady,
@Player.PlayWhenReadyChangeReason int playWhenReadyChangeReason, @Player.PlayWhenReadyChangeReason int playWhenReadyChangeReason,
@Player.State int playbackState) { @Player.State int playbackState) {
boolean wasPlaying = this.playbackState == Player.STATE_READY && this.playWhenReady.value;
boolean playWhenReadyChanged = this.playWhenReady.value != playWhenReady; boolean playWhenReadyChanged = this.playWhenReady.value != playWhenReady;
boolean playbackStateChanged = this.playbackState != playbackState; boolean playbackStateChanged = this.playbackState != playbackState;
if (playWhenReadyChanged || playbackStateChanged) { if (playWhenReadyChanged || playbackStateChanged) {
@ -1236,6 +1231,11 @@ public final class CastPlayer extends BasePlayer {
Player.EVENT_PLAY_WHEN_READY_CHANGED, Player.EVENT_PLAY_WHEN_READY_CHANGED,
listener -> listener.onPlayWhenReadyChanged(playWhenReady, playWhenReadyChangeReason)); listener -> listener.onPlayWhenReadyChanged(playWhenReady, playWhenReadyChangeReason));
} }
boolean isPlaying = playbackState == Player.STATE_READY && playWhenReady;
if (wasPlaying != isPlaying) {
listeners.queueEvent(
Player.EVENT_IS_PLAYING_CHANGED, listener -> listener.onIsPlayingChanged(isPlaying));
}
} }
} }

View File

@ -52,6 +52,7 @@ import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions;
@ -138,13 +139,18 @@ public class CastPlayerTest {
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
@Test @Test
public void setPlayWhenReady_masksRemoteState() { public void setPlayWhenReady_masksRemoteState() {
when(mockRemoteMediaClient.getPlayerState()).thenReturn(MediaStatus.PLAYER_STATE_PLAYING);
// Trigger initial update to get out of STATE_IDLE to make onIsPlaying() be called.
remoteMediaClientCallback.onStatusUpdated();
reset(mockListener);
when(mockRemoteMediaClient.play()).thenReturn(mockPendingResult); when(mockRemoteMediaClient.play()).thenReturn(mockPendingResult);
assertThat(castPlayer.getPlayWhenReady()).isFalse(); assertThat(castPlayer.getPlayWhenReady()).isFalse();
castPlayer.play(); castPlayer.play();
verify(mockPendingResult).setResultCallback(setResultCallbackArgumentCaptor.capture()); verify(mockPendingResult).setResultCallback(setResultCallbackArgumentCaptor.capture());
assertThat(castPlayer.getPlayWhenReady()).isTrue(); assertThat(castPlayer.getPlayWhenReady()).isTrue();
verify(mockListener).onPlayerStateChanged(true, Player.STATE_IDLE); verify(mockListener).onPlayerStateChanged(true, Player.STATE_READY);
verify(mockListener).onIsPlayingChanged(true);
verify(mockListener) verify(mockListener)
.onPlayWhenReadyChanged(true, Player.PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST); .onPlayWhenReadyChanged(true, Player.PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST);
@ -163,13 +169,18 @@ public class CastPlayerTest {
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
@Test @Test
public void setPlayWhenReadyMasking_updatesUponResultChange() { public void setPlayWhenReadyMasking_updatesUponResultChange() {
when(mockRemoteMediaClient.getPlayerState()).thenReturn(MediaStatus.PLAYER_STATE_PLAYING);
// Trigger initial update to get out of STATE_IDLE to make onIsPlaying() be called.
remoteMediaClientCallback.onStatusUpdated();
reset(mockListener);
when(mockRemoteMediaClient.play()).thenReturn(mockPendingResult); when(mockRemoteMediaClient.play()).thenReturn(mockPendingResult);
assertThat(castPlayer.getPlayWhenReady()).isFalse(); assertThat(castPlayer.getPlayWhenReady()).isFalse();
castPlayer.play(); castPlayer.play();
verify(mockPendingResult).setResultCallback(setResultCallbackArgumentCaptor.capture()); verify(mockPendingResult).setResultCallback(setResultCallbackArgumentCaptor.capture());
assertThat(castPlayer.getPlayWhenReady()).isTrue(); assertThat(castPlayer.getPlayWhenReady()).isTrue();
verify(mockListener).onPlayerStateChanged(true, Player.STATE_IDLE); verify(mockListener).onIsPlayingChanged(true);
verify(mockListener).onPlayerStateChanged(true, Player.STATE_READY);
verify(mockListener) verify(mockListener)
.onPlayWhenReadyChanged(true, Player.PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST); .onPlayWhenReadyChanged(true, Player.PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST);
@ -177,38 +188,52 @@ public class CastPlayerTest {
setResultCallbackArgumentCaptor setResultCallbackArgumentCaptor
.getValue() .getValue()
.onResult(mock(RemoteMediaClient.MediaChannelResult.class)); .onResult(mock(RemoteMediaClient.MediaChannelResult.class));
verify(mockListener).onPlayerStateChanged(false, Player.STATE_IDLE); verify(mockListener).onPlayerStateChanged(false, Player.STATE_READY);
verify(mockListener).onIsPlayingChanged(false);
verify(mockListener).onPlayWhenReadyChanged(false, Player.PLAY_WHEN_READY_CHANGE_REASON_REMOTE); verify(mockListener).onPlayWhenReadyChanged(false, Player.PLAY_WHEN_READY_CHANGE_REASON_REMOTE);
assertThat(castPlayer.getPlayWhenReady()).isFalse(); assertThat(castPlayer.getPlayWhenReady()).isFalse();
verifyNoMoreInteractions(mockListener);
} }
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
@Test @Test
public void setPlayWhenReady_correctChangeReasonOnPause() { public void setPlayWhenReady_correctChangeReasonOnPause() {
when(mockRemoteMediaClient.getPlayerState()).thenReturn(MediaStatus.PLAYER_STATE_PLAYING);
// Trigger initial update to get out of STATE_IDLE to make onIsPlaying() be called.
remoteMediaClientCallback.onStatusUpdated();
reset(mockListener);
when(mockRemoteMediaClient.play()).thenReturn(mockPendingResult); when(mockRemoteMediaClient.play()).thenReturn(mockPendingResult);
when(mockRemoteMediaClient.pause()).thenReturn(mockPendingResult); when(mockRemoteMediaClient.pause()).thenReturn(mockPendingResult);
castPlayer.play(); castPlayer.play();
assertThat(castPlayer.getPlayWhenReady()).isTrue(); assertThat(castPlayer.getPlayWhenReady()).isTrue();
verify(mockListener).onPlayerStateChanged(true, Player.STATE_IDLE); verify(mockListener).onIsPlayingChanged(true);
verify(mockListener).onPlayerStateChanged(true, Player.STATE_READY);
verify(mockListener) verify(mockListener)
.onPlayWhenReadyChanged(true, Player.PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST); .onPlayWhenReadyChanged(true, Player.PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST);
castPlayer.pause(); castPlayer.pause();
assertThat(castPlayer.getPlayWhenReady()).isFalse(); assertThat(castPlayer.getPlayWhenReady()).isFalse();
verify(mockListener).onPlayerStateChanged(false, Player.STATE_IDLE); verify(mockListener).onIsPlayingChanged(false);
verify(mockListener) verify(mockListener)
.onPlayWhenReadyChanged(false, Player.PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST); .onPlayWhenReadyChanged(false, Player.PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST);
verify(mockListener).onPlayerStateChanged(false, Player.STATE_READY);
verifyNoMoreInteractions(mockListener);
} }
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
@Test @Test
public void playWhenReady_changesOnStatusUpdates() { public void playWhenReady_changesOnStatusUpdates() {
when(mockRemoteMediaClient.getPlayerState()).thenReturn(MediaStatus.PLAYER_STATE_PLAYING);
assertThat(castPlayer.getPlayWhenReady()).isFalse(); assertThat(castPlayer.getPlayWhenReady()).isFalse();
when(mockRemoteMediaClient.isPaused()).thenReturn(false); when(mockRemoteMediaClient.isPaused()).thenReturn(false);
remoteMediaClientCallback.onStatusUpdated(); remoteMediaClientCallback.onStatusUpdated();
verify(mockListener).onPlayerStateChanged(true, Player.STATE_IDLE); verify(mockListener).onPlayerStateChanged(true, Player.STATE_READY);
verify(mockListener).onPlaybackStateChanged(Player.STATE_READY);
verify(mockListener).onPlayWhenReadyChanged(true, Player.PLAY_WHEN_READY_CHANGE_REASON_REMOTE); verify(mockListener).onPlayWhenReadyChanged(true, Player.PLAY_WHEN_READY_CHANGE_REASON_REMOTE);
assertThat(castPlayer.getPlayWhenReady()).isTrue(); assertThat(castPlayer.getPlayWhenReady()).isTrue();
verify(mockListener).onIsPlayingChanged(true);
verifyNoMoreInteractions(mockListener);
} }
@Test @Test