diff --git a/demos/cast/build.gradle b/demos/cast/build.gradle index c898557cab..cad97358e9 100644 --- a/demos/cast/build.gradle +++ b/demos/cast/build.gradle @@ -48,22 +48,6 @@ android { // The demo app does not have translations. disable 'MissingTranslation' } - - flavorDimensions "receiver" - - productFlavors { - exoCast { - dimension "receiver" - manifestPlaceholders = - [castOptionsProvider: "com.google.android.exoplayer2.ext.cast.ExoCastOptionsProvider"] - } - defaultCast { - dimension "receiver" - manifestPlaceholders = - [castOptionsProvider: "com.google.android.exoplayer2.ext.cast.DefaultCastOptionsProvider"] - } - } - } dependencies { diff --git a/demos/cast/src/main/AndroidManifest.xml b/demos/cast/src/main/AndroidManifest.xml index c556721863..40dfe22167 100644 --- a/demos/cast/src/main/AndroidManifest.xml +++ b/demos/cast/src/main/AndroidManifest.xml @@ -23,7 +23,7 @@ android:largeHeap="true" android:allowBackup="false"> + android:value="com.google.android.exoplayer2.ext.cast.ExoCastOptionsProvider" /> mediaQueue; - private final QueueChangesListener queueChangesListener; - private final ConcatenatingMediaSource concatenatingMediaSource; - - private boolean castMediaQueueCreationPending; - private int currentItemIndex; - private Player currentPlayer; - - /** - * Creates a new manager for {@link SimpleExoPlayer} and {@link CastPlayer}. - * - * @param queueChangesListener A {@link QueueChangesListener} for queue position changes. - * @param localPlayerView The {@link PlayerView} for local playback. - * @param castControlView The {@link PlayerControlView} to control remote playback. - * @param context A {@link Context}. - * @param castContext The {@link CastContext}. - */ - public DefaultReceiverPlayerManager( - QueueChangesListener queueChangesListener, - PlayerView localPlayerView, - PlayerControlView castControlView, - Context context, - CastContext castContext) { - this.queueChangesListener = queueChangesListener; - this.localPlayerView = localPlayerView; - this.castControlView = castControlView; - mediaQueue = new ArrayList<>(); - currentItemIndex = C.INDEX_UNSET; - concatenatingMediaSource = new ConcatenatingMediaSource(); - - DefaultTrackSelector trackSelector = new DefaultTrackSelector(); - RenderersFactory renderersFactory = new DefaultRenderersFactory(context); - exoPlayer = ExoPlayerFactory.newSimpleInstance(context, renderersFactory, trackSelector); - exoPlayer.addListener(this); - localPlayerView.setPlayer(exoPlayer); - - castPlayer = new CastPlayer(castContext); - castPlayer.addListener(this); - castPlayer.setSessionAvailabilityListener(this); - castControlView.setPlayer(castPlayer); - - setCurrentPlayer(castPlayer.isCastSessionAvailable() ? castPlayer : exoPlayer); - } - - // Queue manipulation methods. - - /** - * Plays a specified queue item in the current player. - * - * @param itemIndex The index of the item to play. - */ - public void selectQueueItem(int itemIndex) { - setCurrentItem(itemIndex, C.TIME_UNSET, true); - } - - /** - * Returns the index of the currently played item. - */ - public int getCurrentItemIndex() { - return currentItemIndex; - } - - /** - * Appends {@code item} to the media queue. - * - * @param item The {@link MediaItem} to append. - */ - public void addItem(MediaItem item) { - mediaQueue.add(item); - concatenatingMediaSource.addMediaSource(buildMediaSource(item)); - if (currentPlayer == castPlayer) { - castPlayer.addItems(buildMediaQueueItem(item)); - } - } - - /** - * Returns the size of the media queue. - */ - public int getMediaQueueSize() { - return mediaQueue.size(); - } - - /** - * Returns the item at the given index in the media queue. - * - * @param position The index of the item. - * @return The item at the given index in the media queue. - */ - public MediaItem getItem(int position) { - return mediaQueue.get(position); - } - - /** - * Removes the item at the given index from the media queue. - * - * @param itemIndex The index of the item to remove. - * @return Whether the removal was successful. - */ - public boolean removeItem(int itemIndex) { - concatenatingMediaSource.removeMediaSource(itemIndex); - if (currentPlayer == castPlayer) { - if (castPlayer.getPlaybackState() != Player.STATE_IDLE) { - Timeline castTimeline = castPlayer.getCurrentTimeline(); - if (castTimeline.getPeriodCount() <= itemIndex) { - return false; - } - castPlayer.removeItem((int) castTimeline.getPeriod(itemIndex, new Period()).id); - } - } - mediaQueue.remove(itemIndex); - if (itemIndex == currentItemIndex && itemIndex == mediaQueue.size()) { - maybeSetCurrentItemAndNotify(C.INDEX_UNSET); - } else if (itemIndex < currentItemIndex) { - maybeSetCurrentItemAndNotify(currentItemIndex - 1); - } - return true; - } - - /** - * Moves an item within the queue. - * - * @param fromIndex The index of the item to move. - * @param toIndex The target index of the item in the queue. - * @return Whether the item move was successful. - */ - public boolean moveItem(int fromIndex, int toIndex) { - // Player update. - concatenatingMediaSource.moveMediaSource(fromIndex, toIndex); - if (currentPlayer == castPlayer && castPlayer.getPlaybackState() != Player.STATE_IDLE) { - Timeline castTimeline = castPlayer.getCurrentTimeline(); - int periodCount = castTimeline.getPeriodCount(); - if (periodCount <= fromIndex || periodCount <= toIndex) { - return false; - } - int elementId = (int) castTimeline.getPeriod(fromIndex, new Period()).id; - castPlayer.moveItem(elementId, toIndex); - } - - mediaQueue.add(toIndex, mediaQueue.remove(fromIndex)); - - // Index update. - if (fromIndex == currentItemIndex) { - maybeSetCurrentItemAndNotify(toIndex); - } else if (fromIndex < currentItemIndex && toIndex >= currentItemIndex) { - maybeSetCurrentItemAndNotify(currentItemIndex - 1); - } else if (fromIndex > currentItemIndex && toIndex <= currentItemIndex) { - maybeSetCurrentItemAndNotify(currentItemIndex + 1); - } - - return true; - } - - // Miscellaneous methods. - - /** - * Dispatches a given {@link KeyEvent} to the corresponding view of the current player. - * - * @param event The {@link KeyEvent}. - * @return Whether the event was handled by the target view. - */ - public boolean dispatchKeyEvent(KeyEvent event) { - if (currentPlayer == exoPlayer) { - return localPlayerView.dispatchKeyEvent(event); - } else /* currentPlayer == castPlayer */ { - return castControlView.dispatchKeyEvent(event); - } - } - - /** - * Releases the manager and the players that it holds. - */ - public void release() { - currentItemIndex = C.INDEX_UNSET; - mediaQueue.clear(); - concatenatingMediaSource.clear(); - castPlayer.setSessionAvailabilityListener(null); - castPlayer.release(); - localPlayerView.setPlayer(null); - exoPlayer.release(); - } - - // Player.EventListener implementation. - - @Override - public void onPlayerStateChanged(boolean playWhenReady, int playbackState) { - updateCurrentItemIndex(); - } - - @Override - public void onPositionDiscontinuity(@DiscontinuityReason int reason) { - updateCurrentItemIndex(); - } - - @Override - public void onTimelineChanged( - Timeline timeline, @Nullable Object manifest, @TimelineChangeReason int reason) { - updateCurrentItemIndex(); - if (currentPlayer == castPlayer && timeline.isEmpty()) { - castMediaQueueCreationPending = true; - } - } - - // CastPlayer.SessionAvailabilityListener implementation. - - @Override - public void onCastSessionAvailable() { - setCurrentPlayer(castPlayer); - } - - @Override - public void onCastSessionUnavailable() { - setCurrentPlayer(exoPlayer); - } - - // Internal methods. - - private void updateCurrentItemIndex() { - int playbackState = currentPlayer.getPlaybackState(); - maybeSetCurrentItemAndNotify( - playbackState != Player.STATE_IDLE && playbackState != Player.STATE_ENDED - ? currentPlayer.getCurrentWindowIndex() : C.INDEX_UNSET); - } - - private void setCurrentPlayer(Player currentPlayer) { - if (this.currentPlayer == currentPlayer) { - return; - } - - // View management. - if (currentPlayer == exoPlayer) { - localPlayerView.setVisibility(View.VISIBLE); - castControlView.hide(); - } else /* currentPlayer == castPlayer */ { - localPlayerView.setVisibility(View.GONE); - castControlView.show(); - } - - // Player state management. - long playbackPositionMs = C.TIME_UNSET; - int windowIndex = C.INDEX_UNSET; - boolean playWhenReady = false; - if (this.currentPlayer != null) { - int playbackState = this.currentPlayer.getPlaybackState(); - if (playbackState != Player.STATE_ENDED) { - playbackPositionMs = this.currentPlayer.getCurrentPosition(); - playWhenReady = this.currentPlayer.getPlayWhenReady(); - windowIndex = this.currentPlayer.getCurrentWindowIndex(); - if (windowIndex != currentItemIndex) { - playbackPositionMs = C.TIME_UNSET; - windowIndex = currentItemIndex; - } - } - this.currentPlayer.stop(true); - } else { - // This is the initial setup. No need to save any state. - } - - this.currentPlayer = currentPlayer; - - // Media queue management. - castMediaQueueCreationPending = currentPlayer == castPlayer; - if (currentPlayer == exoPlayer) { - exoPlayer.prepare(concatenatingMediaSource); - } - - // Playback transition. - if (windowIndex != C.INDEX_UNSET) { - setCurrentItem(windowIndex, playbackPositionMs, playWhenReady); - } - } - - /** - * Starts playback of the item at the given position. - * - * @param itemIndex The index of the item to play. - * @param positionMs The position at which playback should start. - * @param playWhenReady Whether the player should proceed when ready to do so. - */ - private void setCurrentItem(int itemIndex, long positionMs, boolean playWhenReady) { - maybeSetCurrentItemAndNotify(itemIndex); - if (castMediaQueueCreationPending) { - MediaQueueItem[] items = new MediaQueueItem[mediaQueue.size()]; - for (int i = 0; i < items.length; i++) { - items[i] = buildMediaQueueItem(mediaQueue.get(i)); - } - castMediaQueueCreationPending = false; - castPlayer.loadItems(items, itemIndex, positionMs, Player.REPEAT_MODE_OFF); - } else { - currentPlayer.seekTo(itemIndex, positionMs); - currentPlayer.setPlayWhenReady(playWhenReady); - } - } - - private void maybeSetCurrentItemAndNotify(int currentItemIndex) { - if (this.currentItemIndex != currentItemIndex) { - int oldIndex = this.currentItemIndex; - this.currentItemIndex = currentItemIndex; - queueChangesListener.onQueuePositionChanged(oldIndex, currentItemIndex); - } - } - - private static MediaSource buildMediaSource(MediaItem item) { - Uri uri = item.media.uri; - switch (item.mimeType) { - case DemoUtil.MIME_TYPE_SS: - return new SsMediaSource.Factory(DATA_SOURCE_FACTORY).createMediaSource(uri); - case DemoUtil.MIME_TYPE_DASH: - return new DashMediaSource.Factory(DATA_SOURCE_FACTORY).createMediaSource(uri); - case DemoUtil.MIME_TYPE_HLS: - return new HlsMediaSource.Factory(DATA_SOURCE_FACTORY).createMediaSource(uri); - case DemoUtil.MIME_TYPE_VIDEO_MP4: - return new ProgressiveMediaSource.Factory(DATA_SOURCE_FACTORY).createMediaSource(uri); - default: { - throw new IllegalStateException("Unsupported type: " + item.mimeType); - } - } - } - - private static MediaQueueItem buildMediaQueueItem(MediaItem item) { - MediaMetadata movieMetadata = new MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE); - movieMetadata.putString(MediaMetadata.KEY_TITLE, item.title); - MediaInfo mediaInfo = - new MediaInfo.Builder(item.media.uri.toString()) - .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED) - .setContentType(item.mimeType) - .setMetadata(movieMetadata) - .build(); - return new MediaQueueItem.Builder(mediaInfo).build(); - } - -} diff --git a/demos/cast/src/main/java/com/google/android/exoplayer2/castdemo/MainActivity.java b/demos/cast/src/main/java/com/google/android/exoplayer2/castdemo/MainActivity.java index 7b525b04be..5b04502edb 100644 --- a/demos/cast/src/main/java/com/google/android/exoplayer2/castdemo/MainActivity.java +++ b/demos/cast/src/main/java/com/google/android/exoplayer2/castdemo/MainActivity.java @@ -38,7 +38,6 @@ import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.ext.cast.MediaItem; import com.google.android.exoplayer2.ui.PlayerControlView; import com.google.android.exoplayer2.ui.PlayerView; -import com.google.android.gms.cast.CastMediaControlIntent; import com.google.android.gms.cast.framework.CastButtonFactory; import com.google.android.gms.cast.framework.CastContext; import com.google.android.gms.dynamite.DynamiteModule; @@ -49,13 +48,13 @@ import java.util.Collections; * Cast extension. */ public class MainActivity extends AppCompatActivity - implements OnClickListener, PlayerManager.QueueChangesListener { + implements OnClickListener, ExoCastPlayerManager.QueueChangesListener { private final MediaItem.Builder mediaItemBuilder; private PlayerView localPlayerView; private PlayerControlView castControlView; - private PlayerManager playerManager; + private ExoCastPlayerManager playerManager; private RecyclerView mediaQueueList; private MediaQueueListAdapter mediaQueueListAdapter; private CastContext castContext; @@ -117,29 +116,13 @@ public class MainActivity extends AppCompatActivity // There is no Cast context to work with. Do nothing. return; } - String applicationId = castContext.getCastOptions().getReceiverApplicationId(); - switch (applicationId) { - case CastMediaControlIntent.DEFAULT_MEDIA_RECEIVER_APPLICATION_ID: - playerManager = - new DefaultReceiverPlayerManager( - /* queueChangesListener= */ this, - localPlayerView, - castControlView, - /* context= */ this, - castContext); - break; - case ExoCastOptionsProvider.RECEIVER_ID: - playerManager = - new ExoCastPlayerManager( - /* queueChangesListener= */ this, - localPlayerView, - castControlView, - /* context= */ this, - castContext); - break; - default: - throw new IllegalStateException("Illegal receiver app id: " + applicationId); - } + playerManager = + new ExoCastPlayerManager( + /* queueChangesListener= */ this, + localPlayerView, + castControlView, + /* context= */ this, + castContext); mediaQueueList.setAdapter(mediaQueueListAdapter); } 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 deleted file mode 100644 index 184dfe29b3..0000000000 --- a/demos/cast/src/main/java/com/google/android/exoplayer2/castdemo/PlayerManager.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * 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.castdemo; - -import android.view.KeyEvent; -import com.google.android.exoplayer2.C; -import com.google.android.exoplayer2.ext.cast.MediaItem; - -/** Manages the players in the Cast demo app. */ -interface PlayerManager { - - /** Listener for changes in the media queue. */ - interface QueueChangesListener { - - /** Called when the currently played item of the media queue changes. */ - void onQueuePositionChanged(int previousIndex, int newIndex); - - /** Called when the media queue changes due to modifications not caused by this manager. */ - void onQueueContentsExternallyChanged(); - } - - /** Redirects the given {@code keyEvent} to the active player. */ - boolean dispatchKeyEvent(KeyEvent keyEvent); - - /** Appends the given {@link MediaItem} to the media queue. */ - void addItem(MediaItem mediaItem); - - /** Returns the number of items in the media queue. */ - int getMediaQueueSize(); - - /** Selects the item at the given position for playback. */ - void selectQueueItem(int position); - - /** - * Returns the position of the item currently being played, or {@link C#INDEX_UNSET} if no item is - * being played. - */ - int getCurrentItemIndex(); - - /** Returns the {@link MediaItem} at the given {@code position}. */ - MediaItem getItem(int position); - - /** Moves the item at position {@code from} to position {@code to}. */ - boolean moveItem(int from, int to); - - /** Removes the item at position {@code index}. */ - boolean removeItem(int index); - - /** Releases any acquired resources. */ - void release(); -}