diff --git a/demos/cast/src/main/java/com/google/android/exoplayer2/castdemo/PlayerManager.java b/demos/cast/src/main/java/com/google/android/exoplayer2/castdemo/PlayerManager.java index d188469de8..0c69e40164 100644 --- a/demos/cast/src/main/java/com/google/android/exoplayer2/castdemo/PlayerManager.java +++ b/demos/cast/src/main/java/com/google/android/exoplayer2/castdemo/PlayerManager.java @@ -33,6 +33,7 @@ import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.Timeline.Period; import com.google.android.exoplayer2.castdemo.DemoUtil.Sample; import com.google.android.exoplayer2.ext.cast.CastPlayer; +import com.google.android.exoplayer2.ext.cast.RemotePlayer; import com.google.android.exoplayer2.source.ConcatenatingMediaSource; import com.google.android.exoplayer2.source.ExtractorMediaSource; import com.google.android.exoplayer2.source.MediaSource; @@ -51,7 +52,7 @@ import java.util.ArrayList; /** Manages players and an internal media queue for the ExoPlayer/Cast demo app. */ /* package */ final class PlayerManager - implements EventListener, CastPlayer.SessionAvailabilityListener { + implements EventListener, RemotePlayer.SessionAvailabilityListener { /** * Listener for changes in the media queue playback position. diff --git a/extensions/cast/build.gradle b/extensions/cast/build.gradle index bee73cac12..ab14b4034a 100644 --- a/extensions/cast/build.gradle +++ b/extensions/cast/build.gradle @@ -32,6 +32,8 @@ android { dependencies { api 'com.google.android.gms:play-services-cast-framework:16.0.1' + compileOnly 'org.checkerframework:checker-qual:' + checkerframeworkVersion + compileOnly 'org.checkerframework:checker-compat-qual:' + checkerframeworkVersion implementation project(modulePrefix + 'library-core') implementation project(modulePrefix + 'library-ui') testImplementation project(modulePrefix + 'testutils') diff --git a/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastPlayer.java b/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastPlayer.java index f630ea1628..ecaa56427f 100644 --- a/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastPlayer.java +++ b/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastPlayer.java @@ -52,35 +52,18 @@ import java.util.concurrent.CopyOnWriteArraySet; * {@link Player} implementation that communicates with a Cast receiver app. * *
The behavior of this class depends on the underlying Cast session, which is obtained from the - * Cast context passed to {@link #CastPlayer}. To keep track of the session, - * {@link #isCastSessionAvailable()} can be queried and {@link SessionAvailabilityListener} can be - * implemented and attached to the player.
+ * Cast context passed to {@link #CastPlayer}. To keep track of the session, {@link + * #isCastSessionAvailable()} can be queried and {@link RemotePlayer.SessionAvailabilityListener} + * can be implemented and attached to the player. * - *If no session is available, the player state will remain unchanged and calls to methods that + *
If no session is available, the player state will remain unchanged and calls to methods that * alter it will be ignored. Querying the player state is possible even when no session is - * available, in which case, the last observed receiver app state is reported.
+ * available, in which case, the last observed receiver app state is reported. * - *Methods should be called on the application's main thread.
+ *Methods should be called on the application's main thread.
*/
public final class CastPlayer implements Player {
- /**
- * Listener of changes in the cast session availability.
- */
- public interface SessionAvailabilityListener {
-
- /**
- * Called when a cast session becomes available to the player.
- */
- void onCastSessionAvailable();
-
- /**
- * Called when the cast session becomes unavailable.
- */
- void onCastSessionUnavailable();
-
- }
-
private static final String TAG = "CastPlayer";
private static final int RENDERER_COUNT = 3;
@@ -106,7 +89,7 @@ public final class CastPlayer implements Player {
// Listeners.
private final CopyOnWriteArraySet The usage of this mime type is optional and player implementation specific.
+ */
+ public final String mimeType;
+
+ // TODO: Add support for sideloaded tracks, artwork, icon, and subtitle.
+
+ private MediaItem(
+ UUID uuid,
+ String title,
+ String description,
+ UriBundle media,
+ @Nullable Object attachment,
+ List Calling this method is equivalent to removing the item at position {@code indexFrom} and
+ * immediately inserting it at position {@code indexTo}. If the moved item is being played at the
+ * moment of the invocation, playback will stick with the moved item.
+ *
+ * @param indexFrom The index of the item to move.
+ * @param indexTo The index at which the item will be placed after this operation.
+ * @throws IndexOutOfBoundsException If for either index, {@code index < 0 || index >= getSize()}.
+ */
+ void move(int indexFrom, int indexTo);
+
+ /**
+ * Removes an item from the queue.
+ *
+ * @param index The index of the item to remove from the queue.
+ * @throws IndexOutOfBoundsException If {@code index < 0 || index >= getSize()}.
+ */
+ void remove(int index);
+
+ /**
+ * Removes a range of items from the queue.
+ *
+ * Does nothing if an empty range ({@code from == exclusiveTo}) is passed.
+ *
+ * @param from The inclusive index at which the range to remove starts.
+ * @param exclusiveTo The exclusive index at which the range to remove ends.
+ * @throws IndexOutOfBoundsException If {@code from < 0 || exclusiveTo > getSize() || from >
+ * exclusiveTo}.
+ */
+ void removeRange(int from, int exclusiveTo);
+
+ /** Removes all items in the queue. */
+ void clear();
+}
diff --git a/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/RemotePlayer.java b/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/RemotePlayer.java
new file mode 100644
index 0000000000..98d5895db8
--- /dev/null
+++ b/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/RemotePlayer.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.android.exoplayer2.ext.cast;
+
+import com.google.android.exoplayer2.Player;
+
+/** A {@link Player} for playing media remotely using the Google Cast framework. */
+public interface RemotePlayer extends Player {
+
+ /** Listener of changes in the cast session availability. */
+ interface SessionAvailabilityListener {
+
+ /** Called when a cast session becomes available to the player. */
+ void onCastSessionAvailable();
+
+ /** Called when the cast session becomes unavailable. */
+ void onCastSessionUnavailable();
+ }
+
+ /** Returns whether a cast session is available. */
+ boolean isCastSessionAvailable();
+
+ /**
+ * Sets a listener for updates on the cast session availability.
+ *
+ * @param listener The {@link SessionAvailabilityListener}.
+ */
+ void setSessionAvailabilityListener(SessionAvailabilityListener listener);
+
+ /** Returns the {@link MediaItemQueue} associated to this player. */
+ MediaItemQueue getMediaItemQueue();
+}
diff --git a/extensions/cast/src/test/java/com/google/android/exoplayer2/ext/cast/MediaItemTest.java b/extensions/cast/src/test/java/com/google/android/exoplayer2/ext/cast/MediaItemTest.java
new file mode 100644
index 0000000000..3c7ec6cab3
--- /dev/null
+++ b/extensions/cast/src/test/java/com/google/android/exoplayer2/ext/cast/MediaItemTest.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.android.exoplayer2.ext.cast;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.net.Uri;
+import com.google.android.exoplayer2.C;
+import com.google.android.exoplayer2.util.MimeTypes;
+import java.util.UUID;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+/** Test for {@link MediaItem}. */
+@RunWith(RobolectricTestRunner.class)
+public class MediaItemTest {
+
+ @Test
+ public void buildMediaItem_resetsUuid() {
+ MediaItem.Builder builder = new MediaItem.Builder();
+ UUID uuid = new UUID(1, 1);
+ MediaItem item1 = builder.setUuid(uuid).build();
+ MediaItem item2 = builder.build();
+ MediaItem item3 = builder.build();
+ assertThat(item1.uuid).isEqualTo(uuid);
+ assertThat(item2.uuid).isNotEqualTo(uuid);
+ assertThat(item3.uuid).isNotEqualTo(item2.uuid);
+ assertThat(item3.uuid).isNotEqualTo(uuid);
+ }
+
+ @Test
+ public void buildMediaItem_doesNotChangeState() {
+ MediaItem.Builder builder = new MediaItem.Builder();
+ MediaItem item1 =
+ builder
+ .setMedia("http://example.com")
+ .setTitle("title")
+ .setMimeType(MimeTypes.AUDIO_MP4)
+ .setStartPositionUs(3)
+ .setEndPositionUs(4)
+ .build();
+ MediaItem item2 = builder.build();
+ assertThat(item1.title).isEqualTo(item2.title);
+ assertThat(item1.media.uri).isEqualTo(item2.media.uri);
+ assertThat(item1.mimeType).isEqualTo(item2.mimeType);
+ assertThat(item1.startPositionUs).isEqualTo(item2.startPositionUs);
+ assertThat(item1.endPositionUs).isEqualTo(item2.endPositionUs);
+ }
+
+ @Test
+ public void buildMediaItem_assertDefaultValues() {
+ assertDefaultValues(new MediaItem.Builder().build());
+ }
+
+ @Test
+ public void buildMediaItem_testClear() {
+ MediaItem.Builder builder = new MediaItem.Builder();
+ builder
+ .setMedia("http://example.com")
+ .setTitle("title")
+ .setMimeType(MimeTypes.AUDIO_MP4)
+ .setStartPositionUs(3)
+ .setEndPositionUs(4)
+ .buildAndClear();
+ assertDefaultValues(builder.build());
+ }
+
+ private static void assertDefaultValues(MediaItem item) {
+ assertThat(item.title).isEmpty();
+ assertThat(item.description).isEmpty();
+ assertThat(item.media.uri).isEqualTo(Uri.EMPTY);
+ assertThat(item.attachment).isNull();
+ assertThat(item.drmSchemes).isEmpty();
+ assertThat(item.startPositionUs).isEqualTo(C.TIME_UNSET);
+ assertThat(item.endPositionUs).isEqualTo(C.TIME_UNSET);
+ assertThat(item.mimeType).isEmpty();
+ }
+}