Avoid out of bounds when setting less media items than in playlist

Issue: androidx/media#86
#minor-release
PiperOrigin-RevId: 455182232
This commit is contained in:
bachinger 2022-06-15 18:40:32 +00:00 committed by Marc Baechinger
parent 12e7562730
commit 8f844b32fd
3 changed files with 106 additions and 3 deletions

View File

@ -185,6 +185,9 @@
`MediaSession.Callback.onSetMediaUri`. The same functionality can be
achieved by using `MediaController.setMediaItem` and
`MediaSession.Callback.onAddMediaItems`.
* Fix `IndexOutOfBoundsException` when setting less media items than in
the current playlist
([#86](https://github.com/androidx/media/issues/86)).
* Data sources:
* Rename `DummyDataSource` to `PlaceholderDataSource`.
* Workaround OkHttp interrupt handling.

View File

@ -828,7 +828,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
Collections.singletonList(mediaItem),
/* startIndex= */ C.INDEX_UNSET,
/* startPositionMs= */ C.TIME_UNSET,
/* resetToDefaultPosition= */ false);
/* resetToDefaultPosition= */ true);
}
@Override
@ -887,7 +887,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
mediaItems,
/* startIndex= */ C.INDEX_UNSET,
/* startPositionMs= */ C.TIME_UNSET,
/* resetToDefaultPosition= */ false);
/* resetToDefaultPosition= */ true);
}
@Override
@ -1832,12 +1832,18 @@ import org.checkerframework.checker.nullness.qual.NonNull;
throw new IllegalSeekPositionException(newTimeline, startIndex, startPositionMs);
}
boolean correctedStartIndex = false;
if (resetToDefaultPosition) {
startIndex = newTimeline.getFirstWindowIndex(playerInfo.shuffleModeEnabled);
startPositionMs = C.TIME_UNSET;
} else if (startIndex == C.INDEX_UNSET) {
startIndex = playerInfo.sessionPositionInfo.positionInfo.mediaItemIndex;
startPositionMs = playerInfo.sessionPositionInfo.positionInfo.positionMs;
if (startIndex >= newTimeline.getWindowCount()) {
correctedStartIndex = true;
startIndex = newTimeline.getFirstWindowIndex(playerInfo.shuffleModeEnabled);
startPositionMs = C.TIME_UNSET;
}
}
PositionInfo newPositionInfo;
SessionPositionInfo newSessionPositionInfo;
@ -1905,7 +1911,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
// Mask the playback state.
int maskingPlaybackState = newPlayerInfo.playbackState;
if (startIndex != C.INDEX_UNSET && newPlayerInfo.playbackState != STATE_IDLE) {
if (newTimeline.isEmpty() || startIndex >= newTimeline.getWindowCount()) {
if (newTimeline.isEmpty() || correctedStartIndex) {
// Setting an empty timeline or invalid seek transitions to ended.
maskingPlaybackState = STATE_ENDED;
} else {

View File

@ -36,6 +36,7 @@ import android.os.RemoteException;
import androidx.media3.common.AudioAttributes;
import androidx.media3.common.C;
import androidx.media3.common.HeartRating;
import androidx.media3.common.IllegalSeekPositionException;
import androidx.media3.common.MediaItem;
import androidx.media3.common.MediaLibraryInfo;
import androidx.media3.common.MediaMetadata;
@ -56,6 +57,7 @@ import androidx.media3.test.session.common.TestUtils;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
@ -64,6 +66,7 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Rule;
@ -1055,4 +1058,95 @@ public class MediaControllerTest {
assertThat(mediaMetadata).isEqualTo(testMediaMetadata);
}
@Test
public void
setMediaItems_setLessMediaItemsThanCurrentMediaItemIndex_masksCurrentMediaItemIndexAndStateCorrectly()
throws Exception {
MediaController controller = controllerTestRule.createController(remoteSession.getToken());
List<MediaItem> threeItemsList =
ImmutableList.of(
MediaItem.fromUri("http://www.google.com/1"),
MediaItem.fromUri("http://www.google.com/2"),
MediaItem.fromUri("http://www.google.com/3"));
List<MediaItem> twoItemsList =
ImmutableList.of(
MediaItem.fromUri("http://www.google.com/1"),
MediaItem.fromUri("http://www.google.com/2"));
int[] currentMediaIndexAndState =
threadTestRule
.getHandler()
.postAndSync(
() -> {
controller.setMediaItems(threeItemsList);
controller.prepare();
controller.seekTo(/* mediaItemIndex= */ 2, /* positionMs= */ C.TIME_UNSET);
controller.setMediaItems(twoItemsList);
return new int[] {
controller.getCurrentMediaItemIndex(), controller.getPlaybackState()
};
});
assertThat(currentMediaIndexAndState[0]).isEqualTo(0);
assertThat(currentMediaIndexAndState[1]).isEqualTo(Player.STATE_BUFFERING);
}
@Test
public void
setMediaItems_setLessMediaItemsThanCurrentMediaItemIndexResetPositionFalse_masksCurrentMediaItemIndexAndStateCorrectly()
throws Exception {
MediaController controller = controllerTestRule.createController(remoteSession.getToken());
List<MediaItem> threeItemsList =
ImmutableList.of(
MediaItem.fromUri("http://www.google.com/1"),
MediaItem.fromUri("http://www.google.com/2"),
MediaItem.fromUri("http://www.google.com/3"));
List<MediaItem> twoItemsList =
ImmutableList.of(
MediaItem.fromUri("http://www.google.com/1"),
MediaItem.fromUri("http://www.google.com/2"));
int[] currentMediaItemIndexAndState =
threadTestRule
.getHandler()
.postAndSync(
() -> {
controller.setMediaItems(threeItemsList);
controller.prepare();
controller.seekTo(/* mediaItemIndex= */ 2, /* positionMs= */ C.TIME_UNSET);
controller.setMediaItems(twoItemsList, /* resetPosition= */ false);
return new int[] {
controller.getCurrentMediaItemIndex(), controller.getPlaybackState()
};
});
assertThat(currentMediaItemIndexAndState[0]).isEqualTo(0);
assertThat(currentMediaItemIndexAndState[1]).isEqualTo(Player.STATE_ENDED);
}
@Test
public void setMediaItems_startIndexTooLarge_throwIllegalSeekPositionException()
throws Exception {
MediaController controller = controllerTestRule.createController(remoteSession.getToken());
List<MediaItem> threeItemsList =
ImmutableList.of(
MediaItem.fromUri("http://www.google.com/1"),
MediaItem.fromUri("http://www.google.com/2"),
MediaItem.fromUri("http://www.google.com/3"));
Assert.assertThrows(
IllegalSeekPositionException.class,
() ->
threadTestRule
.getHandler()
.postAndSync(
() -> {
controller.setMediaItems(
threeItemsList,
/* startIndex= */ 99,
/* startPositionMs= */ C.TIME_UNSET);
return controller.getCurrentMediaItemIndex();
}));
}
}