Always return empty timeline when wrapped player is empty

In `PlayerWrapper.getCurrentTimelineWithCommandCheck()` we
always return `new CurrentMediaItemOnlyTimeline(this)` in
case the wrapped player doesn't have `COMMAND_GET_TIMELINE`
available but has `COMMAND_GET_CURRENT_MEDIA_ITEM`. This is
emulating a single item timeline with a static window count
of 1 which isn't correct when the wrapped player is empty.

Instead, when the wrapped player is empty we need to return
an empty timeline to match the wrapped player.

Issue: androidx/media#2320
PiperOrigin-RevId: 746071237
(cherry picked from commit 5f940af3df5e644b33d45c78cd5103f78566efd0)
This commit is contained in:
bachinger 2025-04-10 09:57:21 -07:00 committed by tonihei
parent 157fd8a260
commit 75e2522862
3 changed files with 56 additions and 1 deletions

View File

@ -67,6 +67,11 @@
configure this value. configure this value.
* Fix issue where notifications reappear after they have been dismissed by * Fix issue where notifications reappear after they have been dismissed by
the user ([#2302](https://github.com/androidx/media/issues/2302)). the user ([#2302](https://github.com/androidx/media/issues/2302)).
* Fix a bug where the `PlayerWrapper` returned a single-item timeline when
the wrapped player is actually empty. This happened when the wrapped
player doesn't have `COMMAND_GET_TIMELINE` available while
`COMMAND_GET_CURRENT_MEDIA_ITEM` is available and the wrapped player is
empty ([#2320](https://github.com/androidx/media/issues/2320)).
* UI: * UI:
* Enable `PlayerSurface` to work with `ExoPlayer.setVideoEffects` and * Enable `PlayerSurface` to work with `ExoPlayer.setVideoEffects` and
`CompositionPlayer`. `CompositionPlayer`.

View File

@ -710,7 +710,9 @@ import java.util.List;
if (isCommandAvailable(COMMAND_GET_TIMELINE)) { if (isCommandAvailable(COMMAND_GET_TIMELINE)) {
return getCurrentTimeline(); return getCurrentTimeline();
} else if (isCommandAvailable(COMMAND_GET_CURRENT_MEDIA_ITEM)) { } else if (isCommandAvailable(COMMAND_GET_CURRENT_MEDIA_ITEM)) {
return new CurrentMediaItemOnlyTimeline(this); return getCurrentTimeline().isEmpty()
? Timeline.EMPTY
: new CurrentMediaItemOnlyTimeline(this);
} }
return Timeline.EMPTY; return Timeline.EMPTY;
} }

View File

@ -22,6 +22,8 @@ import static org.mockito.Mockito.when;
import android.os.Bundle; import android.os.Bundle;
import android.os.Looper; import android.os.Looper;
import androidx.media3.common.Player; import androidx.media3.common.Player;
import androidx.media3.common.Timeline;
import androidx.media3.test.utils.FakeTimeline;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import org.junit.Before; import org.junit.Before;
@ -57,6 +59,52 @@ public class PlayerWrapperTest {
when(player.getApplicationLooper()).thenReturn(Looper.myLooper()); when(player.getApplicationLooper()).thenReturn(Looper.myLooper());
} }
@Test
public void
getCurrentTimelineWithCommandCheck_withoutCommandGetTimelineAndGetCurrentMediaItem_isEmpty() {
when(player.isCommandAvailable(Player.COMMAND_GET_TIMELINE)).thenReturn(false);
when(player.isCommandAvailable(Player.COMMAND_GET_CURRENT_MEDIA_ITEM)).thenReturn(false);
when(player.getCurrentTimeline()).thenReturn(new FakeTimeline(/* windowCount= */ 3));
Timeline currentTimeline = playerWrapper.getCurrentTimelineWithCommandCheck();
assertThat(currentTimeline.isEmpty()).isTrue();
}
@Test
public void getCurrentTimelineWithCommandCheck_withoutCommandGetTimelineWhenEmpty_isEmpty() {
when(player.isCommandAvailable(Player.COMMAND_GET_TIMELINE)).thenReturn(false);
when(player.isCommandAvailable(Player.COMMAND_GET_CURRENT_MEDIA_ITEM)).thenReturn(true);
when(player.getCurrentTimeline()).thenReturn(Timeline.EMPTY);
Timeline currentTimeline = playerWrapper.getCurrentTimelineWithCommandCheck();
assertThat(currentTimeline.isEmpty()).isTrue();
}
@Test
public void
getCurrentTimelineWithCommandCheck_withoutCommandGetTimelineWhenMultipleItems_hasSingleItemTimeline() {
when(player.isCommandAvailable(Player.COMMAND_GET_TIMELINE)).thenReturn(false);
when(player.isCommandAvailable(Player.COMMAND_GET_CURRENT_MEDIA_ITEM)).thenReturn(true);
when(player.getCurrentTimeline()).thenReturn(new FakeTimeline(/* windowCount= */ 3));
Timeline currentTimeline = playerWrapper.getCurrentTimelineWithCommandCheck();
assertThat(currentTimeline.getWindowCount()).isEqualTo(1);
}
@Test
public void getCurrentTimelineWithCommandCheck_withCommandGetTimeline_returnOriginalTimeline() {
when(player.isCommandAvailable(Player.COMMAND_GET_TIMELINE)).thenReturn(true);
when(player.isCommandAvailable(Player.COMMAND_GET_CURRENT_MEDIA_ITEM)).thenReturn(false);
when(player.getCurrentTimeline()).thenReturn(new FakeTimeline(/* windowCount= */ 3));
Timeline currentTimeline = playerWrapper.getCurrentTimelineWithCommandCheck();
assertThat(currentTimeline.getWindowCount()).isEqualTo(3);
}
@Test @Test
public void createSessionPositionInfoForBundling() { public void createSessionPositionInfoForBundling() {
int testAdGroupIndex = 12; int testAdGroupIndex = 12;