summaryrefslogtreecommitdiffstats
path: root/samples/SupportLeanbackShowcase/app/src/main/java/android/support/v17/leanback/supportleanbackshowcase/app/media
diff options
context:
space:
mode:
Diffstat (limited to 'samples/SupportLeanbackShowcase/app/src/main/java/android/support/v17/leanback/supportleanbackshowcase/app/media')
-rw-r--r--samples/SupportLeanbackShowcase/app/src/main/java/android/support/v17/leanback/supportleanbackshowcase/app/media/MediaPlayerGlue.java446
-rw-r--r--samples/SupportLeanbackShowcase/app/src/main/java/android/support/v17/leanback/supportleanbackshowcase/app/media/MusicConsumptionExampleFragment.java155
-rw-r--r--samples/SupportLeanbackShowcase/app/src/main/java/android/support/v17/leanback/supportleanbackshowcase/app/media/MusicExampleActivity.java30
-rw-r--r--samples/SupportLeanbackShowcase/app/src/main/java/android/support/v17/leanback/supportleanbackshowcase/app/media/SongListRow.java20
-rw-r--r--samples/SupportLeanbackShowcase/app/src/main/java/android/support/v17/leanback/supportleanbackshowcase/app/media/TrackListHeader.java50
-rw-r--r--samples/SupportLeanbackShowcase/app/src/main/java/android/support/v17/leanback/supportleanbackshowcase/app/media/VideoConsumptionExampleFragment.java117
-rw-r--r--samples/SupportLeanbackShowcase/app/src/main/java/android/support/v17/leanback/supportleanbackshowcase/app/media/VideoExampleActivity.java39
-rw-r--r--samples/SupportLeanbackShowcase/app/src/main/java/android/support/v17/leanback/supportleanbackshowcase/app/media/VideoMediaPlayerGlue.java57
-rw-r--r--samples/SupportLeanbackShowcase/app/src/main/java/android/support/v17/leanback/supportleanbackshowcase/app/media/VideoSurfaceFragment.java39
9 files changed, 953 insertions, 0 deletions
diff --git a/samples/SupportLeanbackShowcase/app/src/main/java/android/support/v17/leanback/supportleanbackshowcase/app/media/MediaPlayerGlue.java b/samples/SupportLeanbackShowcase/app/src/main/java/android/support/v17/leanback/supportleanbackshowcase/app/media/MediaPlayerGlue.java
new file mode 100644
index 000000000..bc757cd19
--- /dev/null
+++ b/samples/SupportLeanbackShowcase/app/src/main/java/android/support/v17/leanback/supportleanbackshowcase/app/media/MediaPlayerGlue.java
@@ -0,0 +1,446 @@
+/*
+ * Copyright (C) 2015 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 android.support.v17.leanback.supportleanbackshowcase.app.media;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.drawable.Drawable;
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import android.net.Uri;
+import android.os.Handler;
+import android.support.v17.leanback.app.PlaybackControlGlue;
+import android.support.v17.leanback.app.PlaybackOverlayFragment;
+import android.support.v17.leanback.widget.Action;
+import android.support.v17.leanback.widget.ArrayObjectAdapter;
+import android.support.v17.leanback.widget.ControlButtonPresenterSelector;
+import android.support.v17.leanback.widget.OnItemViewSelectedListener;
+import android.support.v17.leanback.widget.PlaybackControlsRow;
+import android.support.v17.leanback.widget.PlaybackControlsRowPresenter;
+import android.support.v17.leanback.widget.Presenter;
+import android.support.v17.leanback.widget.Row;
+import android.support.v17.leanback.widget.RowPresenter;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.SurfaceHolder;
+import android.view.View;
+
+import java.io.IOException;
+
+/**
+ * This glue extends the {@link PlaybackControlGlue} with a {@link MediaPlayer} synchronization. It
+ * supports 7 actions: <ul> <li>{@link android.support.v17.leanback.widget.PlaybackControlsRow.FastForwardAction}</li>
+ * <li>{@link android.support.v17.leanback.widget.PlaybackControlsRow.RewindAction}</li> <li>{@link
+ * android.support.v17.leanback.widget.PlaybackControlsRow.PlayPauseAction}</li> <li>{@link
+ * android.support.v17.leanback.widget.PlaybackControlsRow.ShuffleAction}</li> <li>{@link
+ * android.support.v17.leanback.widget.PlaybackControlsRow.RepeatAction}</li> <li>{@link
+ * android.support.v17.leanback.widget.PlaybackControlsRow.ThumbsDownAction}</li> <li>{@link
+ * android.support.v17.leanback.widget.PlaybackControlsRow.ThumbsUpAction}</li> </ul>
+ * <p/>
+ */
+public abstract class MediaPlayerGlue extends PlaybackControlGlue implements
+ OnItemViewSelectedListener {
+
+ public static final int FAST_FORWARD_REWIND_STEP = 10 * 1000; // in milliseconds
+ public static final int FAST_FORWARD_REWIND_REPEAT_DELAY = 200; // in milliseconds
+ private static final String TAG = "MediaPlayerGlue";
+ protected final PlaybackControlsRow.ThumbsDownAction mThumbsDownAction;
+ protected final PlaybackControlsRow.ThumbsUpAction mThumbsUpAction;
+ private final Context mContext;
+ private final MediaPlayer mPlayer = new MediaPlayer();
+ private final PlaybackControlsRow.RepeatAction mRepeatAction;
+ private final PlaybackControlsRow.ShuffleAction mShuffleAction;
+ private PlaybackControlsRow mControlsRow;
+ private Runnable mRunnable;
+ private Handler mHandler = new Handler();
+ private boolean mPaused = false;
+ private boolean mInitialized = false; // true when the MediaPlayer is prepared/initialized
+ private OnMediaFileFinishedPlayingListener mMediaFileFinishedPlayingListener;
+ private Action mSelectedAction; // the action which is currently selected by the user
+ private long mLastKeyDownEvent = 0L; // timestamp when the last DPAD_CENTER KEY_DOWN occurred
+ private MetaData mMetaData;
+ private Uri mMediaSourceUri = null;
+ private String mMediaSourcePath = null;
+
+ public MediaPlayerGlue(Context context, PlaybackOverlayFragment fragment) {
+ super(context, fragment, new int[]{1});
+ mContext = context;
+
+ // Instantiate secondary actions
+ mShuffleAction = new PlaybackControlsRow.ShuffleAction(mContext);
+ mRepeatAction = new PlaybackControlsRow.RepeatAction(mContext);
+ mThumbsDownAction = new PlaybackControlsRow.ThumbsDownAction(mContext);
+ mThumbsUpAction = new PlaybackControlsRow.ThumbsUpAction(mContext);
+ mThumbsDownAction.setIndex(PlaybackControlsRow.ThumbsAction.OUTLINE);
+ mThumbsUpAction.setIndex(PlaybackControlsRow.ThumbsAction.OUTLINE);
+
+ // Setup controls and notify UI about change.
+ setFadingEnabled(false);
+ onStateChanged();
+
+ // Register selected listener such that we know what action the user currently has focused.
+ fragment.setOnItemViewSelectedListener(this);
+ }
+
+ /**
+ * Will reset the {@link MediaPlayer} and the glue such that a new file can be played. You are
+ * not required to call this method before playing the first file. However you have to call it
+ * before playing a second one.
+ */
+ public void reset() {
+ mPaused = mInitialized = false;
+ mPlayer.reset();
+ }
+
+ public void setOnMediaFileFinishedPlayingListener(OnMediaFileFinishedPlayingListener listener) {
+ mMediaFileFinishedPlayingListener = listener;
+ }
+
+ /**
+ * Override this method in case you need to add different secondary actions.
+ *
+ * @param secondaryActionsAdapter The adapter you need to add the {@link Action}s to.
+ */
+ protected void addSecondaryActions(ArrayObjectAdapter secondaryActionsAdapter) {
+ secondaryActionsAdapter.add(mShuffleAction);
+ secondaryActionsAdapter.add(mRepeatAction);
+ secondaryActionsAdapter.add(mThumbsDownAction);
+ secondaryActionsAdapter.add(mThumbsUpAction);
+ }
+
+ /**
+ * @see MediaPlayer#setDisplay(SurfaceHolder)
+ */
+ public void setDisplay(SurfaceHolder surfaceHolder) {
+ mPlayer.setDisplay(surfaceHolder);
+ }
+
+ /**
+ * Use this method to setup the {@link PlaybackControlsRowPresenter}. It'll be called
+ * <u>after</u> the {@link PlaybackControlsRowPresenter} has been created and the primary and
+ * secondary actions have been added.
+ *
+ * @param presenter The PlaybackControlsRowPresenter used to display the controls.
+ */
+ public void setupControlsRowPresenter(PlaybackControlsRowPresenter presenter) {
+ // TODO: hahnr@ move into resources
+ presenter.setProgressColor(Color.parseColor("#feab91"));
+ presenter.setBackgroundColor(Color.parseColor("#db2a0f"));
+ }
+
+ @Override public PlaybackControlsRowPresenter createControlsRowAndPresenter() {
+ PlaybackControlsRowPresenter presenter = super.createControlsRowAndPresenter();
+ mControlsRow = getControlsRow();
+
+ // Add secondary actions and change the control row color.
+ ArrayObjectAdapter secondaryActions = new ArrayObjectAdapter(
+ new ControlButtonPresenterSelector());
+ mControlsRow.setSecondaryActionsAdapter(secondaryActions);
+ addSecondaryActions(secondaryActions);
+ setupControlsRowPresenter(presenter);
+ return presenter;
+ }
+
+ @Override public void enableProgressUpdating(final boolean enabled) {
+ if (!enabled) {
+ if (mRunnable != null) mHandler.removeCallbacks(mRunnable);
+ return;
+ }
+ mRunnable = new Runnable() {
+ @Override public void run() {
+ updateProgress();
+ Log.d(TAG, "enableProgressUpdating(boolean)");
+ mHandler.postDelayed(this, getUpdatePeriod());
+ }
+ };
+ mHandler.postDelayed(mRunnable, getUpdatePeriod());
+ }
+
+ @Override public void onActionClicked(Action action) {
+ // If either 'Shuffle' or 'Repeat' has been clicked we need to make sure the acitons index
+ // is incremented and the UI updated such that we can display the new state.
+ super.onActionClicked(action);
+ if (action instanceof PlaybackControlsRow.ShuffleAction) {
+ mShuffleAction.nextIndex();
+ } else if (action instanceof PlaybackControlsRow.RepeatAction) {
+ mRepeatAction.nextIndex();
+ } else if (action instanceof PlaybackControlsRow.ThumbsUpAction) {
+ if (mThumbsUpAction.getIndex() == PlaybackControlsRow.ThumbsAction.SOLID) {
+ mThumbsUpAction.setIndex(PlaybackControlsRow.ThumbsAction.OUTLINE);
+ } else {
+ mThumbsUpAction.setIndex(PlaybackControlsRow.ThumbsAction.SOLID);
+ mThumbsDownAction.setIndex(PlaybackControlsRow.ThumbsAction.OUTLINE);
+ }
+ } else if (action instanceof PlaybackControlsRow.ThumbsDownAction) {
+ if (mThumbsDownAction.getIndex() == PlaybackControlsRow.ThumbsAction.SOLID) {
+ mThumbsDownAction.setIndex(PlaybackControlsRow.ThumbsAction.OUTLINE);
+ } else {
+ mThumbsDownAction.setIndex(PlaybackControlsRow.ThumbsAction.SOLID);
+ mThumbsUpAction.setIndex(PlaybackControlsRow.ThumbsAction.OUTLINE);
+ }
+ }
+ onMetadataChanged();
+ }
+
+ @Override public boolean onKey(View v, int keyCode, KeyEvent event) {
+ // This method is overridden in order to make implement fast forwarding and rewinding when
+ // the user keeps the corresponding action pressed.
+ // We only consume DPAD_CENTER Action_DOWN events on the Fast-Forward and Rewind action and
+ // only if it has not been pressed in the last X milliseconds.
+ boolean consume = mSelectedAction instanceof PlaybackControlsRow.RewindAction;
+ consume = consume || mSelectedAction instanceof PlaybackControlsRow.FastForwardAction;
+ consume = consume && mInitialized;
+ consume = consume && event.getKeyCode() == KeyEvent.KEYCODE_DPAD_CENTER;
+ consume = consume && event.getAction() == KeyEvent.ACTION_DOWN;
+ consume = consume && System
+ .currentTimeMillis() - mLastKeyDownEvent > FAST_FORWARD_REWIND_REPEAT_DELAY;
+ if (consume) {
+ mLastKeyDownEvent = System.currentTimeMillis();
+ int newPosition = getCurrentPosition() + FAST_FORWARD_REWIND_STEP;
+ if (mSelectedAction instanceof PlaybackControlsRow.RewindAction) {
+ newPosition = getCurrentPosition() - FAST_FORWARD_REWIND_STEP;
+ }
+ // Make sure the new calculated duration is in the range 0 >= X >= MediaDuration
+ if (newPosition < 0) newPosition = 0;
+ if (newPosition > getMediaDuration()) newPosition = getMediaDuration();
+ seekTo(newPosition);
+ return true;
+ }
+ return super.onKey(v, keyCode, event);
+ }
+
+ @Override public boolean hasValidMedia() {
+ return mMetaData != null;
+ }
+
+ @Override public boolean isMediaPlaying() {
+ return mPlayer.isPlaying();
+ }
+
+ @Override public CharSequence getMediaTitle() {
+ return hasValidMedia() ? mMetaData.getTitle() : "N/a";
+ }
+
+ @Override public CharSequence getMediaSubtitle() {
+ return hasValidMedia() ? mMetaData.getArtist() : "N/a";
+ }
+
+ @Override public int getMediaDuration() {
+ return mInitialized ? mPlayer.getDuration() : 0;
+ }
+
+ @Override public Drawable getMediaArt() {
+ return hasValidMedia() ? mMetaData.getCover() : null;
+ }
+
+ @Override public long getSupportedActions() {
+ return PlaybackControlGlue.ACTION_PLAY_PAUSE | PlaybackControlGlue.ACTION_FAST_FORWARD | PlaybackControlGlue.ACTION_REWIND;
+ }
+
+ @Override public int getCurrentSpeedId() {
+ // 0 = Pause, 1 = Normal Playback Speed
+ return mPlayer.isPlaying() ? 1 : 0;
+ }
+
+ @Override public int getCurrentPosition() {
+ return mInitialized ? mPlayer.getCurrentPosition() : 0;
+ }
+
+ @Override protected void startPlayback(int speed) throws IllegalStateException {
+ if (mPaused) {
+ mPlayer.start();
+ } else if (!isMediaPlaying()) {
+ reset();
+ try {
+ if (mMediaSourceUri != null) mPlayer.setDataSource(getContext(), mMediaSourceUri);
+ else mPlayer.setDataSource(mMediaSourcePath);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
+ mPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
+ @Override public void onPrepared(MediaPlayer mp) {
+ mInitialized = true;
+ mPaused = false;
+ mPlayer.start();
+ onMetadataChanged();
+ onStateChanged();
+ updateProgress();
+ }
+ });
+ mPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
+ @Override public void onCompletion(MediaPlayer mp) {
+ if (mInitialized && mMediaFileFinishedPlayingListener != null)
+ mMediaFileFinishedPlayingListener.onMediaFileFinishedPlaying(mMetaData);
+ }
+ });
+ mPlayer.setOnBufferingUpdateListener(new MediaPlayer.OnBufferingUpdateListener() {
+ @Override public void onBufferingUpdate(MediaPlayer mp, int percent) {
+ mControlsRow.setBufferedProgress((int) (mp.getDuration() * (percent / 100f)));
+ }
+ });
+ mPlayer.prepareAsync();
+ onStateChanged();
+ }
+ }
+
+ @Override protected void pausePlayback() {
+ if (mPlayer.isPlaying()) {
+ mPlayer.pause();
+ mPaused = true;
+ }
+ }
+
+ @Override protected void skipToNext() {
+ // Not supported.
+ }
+
+ @Override protected void skipToPrevious() {
+ // Not supported.
+ }
+
+ /**
+ * Called whenever the user presses fast-forward/rewind or when the user keeps the corresponding
+ * action pressed.
+ *
+ * @param newPosition The new position of the media track in milliseconds.
+ */
+ protected void seekTo(int newPosition) {
+ mPlayer.seekTo(newPosition);
+ }
+
+ /**
+ * @see MediaPlayer#setDataSource(Context, Uri)
+ */
+ public void setMediaSource(Uri uri) {
+ mMediaSourceUri = uri;
+ }
+
+ /**
+ * @see MediaPlayer#setDataSource(String)
+ */
+ public void setMediaSource(String path) {
+ mMediaSourcePath = path;
+ }
+
+ /**
+ * Call to <code>startPlayback(1)</code>.
+ *
+ * @throws IllegalStateException See {@link MediaPlayer} for further information about it's
+ * different states when setting a data source and preparing it to be played.
+ */
+ public void startPlayback() throws IllegalStateException {
+ startPlayback(1);
+ }
+
+ /**
+ * @return Returns <code>true</code> iff 'Shuffle' is <code>ON</code>.
+ */
+ public boolean useShuffle() {
+ return mShuffleAction.getIndex() == PlaybackControlsRow.ShuffleAction.ON;
+ }
+
+ /**
+ * @return Returns <code>true</code> iff 'Repeat-One' is <code>ON</code>.
+ */
+ public boolean repeatOne() {
+ return mRepeatAction.getIndex() == PlaybackControlsRow.RepeatAction.ONE;
+ }
+
+ /**
+ * @return Returns <code>true</code> iff 'Repeat-All' is <code>ON</code>.
+ */
+ public boolean repeatAll() {
+ return mRepeatAction.getIndex() == PlaybackControlsRow.RepeatAction.ALL;
+ }
+
+ public void setMetaData(MetaData metaData) {
+ mMetaData = metaData;
+ onMetadataChanged();
+ }
+
+ /**
+ * This is a listener implementation for the {@link OnItemViewSelectedListener} of the {@link
+ * PlaybackOverlayFragment}. This implementation is required in order to detect KEY_DOWN events
+ * on the {@link android.support.v17.leanback.widget.PlaybackControlsRow.FastForwardAction} and
+ * {@link android.support.v17.leanback.widget.PlaybackControlsRow.RewindAction}. Thus you should
+ * <u>NOT</u> set another {@link OnItemViewSelectedListener} on your {@link
+ * PlaybackOverlayFragment}. Instead, override this method and call its super (this)
+ * implementation.
+ *
+ * @see OnItemViewSelectedListener#onItemSelected(Presenter.ViewHolder, Object,
+ * RowPresenter.ViewHolder, Row)
+ */
+ @Override public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item,
+ RowPresenter.ViewHolder rowViewHolder, Row row) {
+ if (item instanceof Action) {
+ mSelectedAction = (Action) item;
+ } else {
+ mSelectedAction = null;
+ }
+ }
+
+ /**
+ * A listener which will be called whenever a track is finished playing.
+ */
+ public interface OnMediaFileFinishedPlayingListener {
+
+ /**
+ * Called when a track is finished playing.
+ *
+ * @param metaData The track's {@link MetaData} which just finished playing.
+ */
+ void onMediaFileFinishedPlaying(MetaData metaData);
+
+ }
+
+ /**
+ * Holds the meta data such as track title, artist and cover art. It'll be used by the {@link
+ * MediaPlayerGlue}.
+ */
+ public static class MetaData {
+
+ private String mTitle;
+ private String mArtist;
+ private Drawable mCover;
+
+
+ public String getTitle() {
+ return mTitle;
+ }
+
+ public void setTitle(String title) {
+ this.mTitle = title;
+ }
+
+ public String getArtist() {
+ return mArtist;
+ }
+
+ public void setArtist(String artist) {
+ this.mArtist = artist;
+ }
+
+ public Drawable getCover() {
+ return mCover;
+ }
+
+ public void setCover(Drawable cover) {
+ this.mCover = cover;
+ }
+ }
+
+}
diff --git a/samples/SupportLeanbackShowcase/app/src/main/java/android/support/v17/leanback/supportleanbackshowcase/app/media/MusicConsumptionExampleFragment.java b/samples/SupportLeanbackShowcase/app/src/main/java/android/support/v17/leanback/supportleanbackshowcase/app/media/MusicConsumptionExampleFragment.java
new file mode 100644
index 000000000..fdb8ec41f
--- /dev/null
+++ b/samples/SupportLeanbackShowcase/app/src/main/java/android/support/v17/leanback/supportleanbackshowcase/app/media/MusicConsumptionExampleFragment.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2015 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 android.support.v17.leanback.supportleanbackshowcase.app.media;
+
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.v17.leanback.app.PlaybackOverlayFragment;
+import android.support.v17.leanback.supportleanbackshowcase.utils.Constants;
+import android.support.v17.leanback.supportleanbackshowcase.app.media.MediaPlayerGlue;
+import android.support.v17.leanback.supportleanbackshowcase.R;
+import android.support.v17.leanback.supportleanbackshowcase.app.media.TrackListHeader;
+import android.support.v17.leanback.supportleanbackshowcase.utils.Utils;
+import android.support.v17.leanback.supportleanbackshowcase.models.Song;
+import android.support.v17.leanback.supportleanbackshowcase.models.SongList;
+import android.support.v17.leanback.widget.Action;
+import android.support.v17.leanback.widget.ArrayObjectAdapter;
+import android.support.v17.leanback.widget.ClassPresenterSelector;
+import android.support.v17.leanback.widget.OnItemViewClickedListener;
+import android.support.v17.leanback.widget.PlaybackControlsRow;
+import android.support.v17.leanback.widget.PlaybackControlsRowPresenter;
+import android.support.v17.leanback.widget.Presenter;
+import android.support.v17.leanback.widget.Row;
+import android.support.v17.leanback.widget.RowPresenter;
+import android.util.Log;
+
+import com.google.gson.Gson;
+
+import java.util.List;
+
+/**
+ * This example shows how to play music files and build a simple track list.
+ */
+public class MusicConsumptionExampleFragment extends PlaybackOverlayFragment implements
+ OnItemViewClickedListener, Song.OnSongRowClickListener,
+ MediaPlayerGlue.OnMediaFileFinishedPlayingListener {
+
+ private static final String TAG = "MusicConsumptionExampleFragment";
+ private ArrayObjectAdapter mRowsAdapter;
+ private MediaPlayerGlue mGlue;
+ private int mCurrentSongIndex = 0;
+ private List<Song> mSongList;
+ private boolean mAdapterNotified = false;
+
+ @Override public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (Constants.LOCAL_LOGD) Log.d(TAG, "onCreate");
+
+ mGlue = new MediaPlayerGlue(getActivity(), this) {
+
+ @Override protected void onRowChanged(PlaybackControlsRow row) {
+ if (mRowsAdapter == null || mAdapterNotified) return;
+ //mAdapterNotified = true;
+ mRowsAdapter.notifyArrayItemRangeChanged(0, 1);
+ }
+ };
+ mGlue.setOnMediaFileFinishedPlayingListener(this);
+
+ String json = Utils.inputStreamToString(
+ getResources().openRawResource(R.raw.music_consumption_example));
+ mSongList = new Gson().fromJson(json, SongList.class).getSongs();
+ Song song = mSongList.get(mCurrentSongIndex);
+ MediaPlayerGlue.MetaData metaData = new MediaPlayerGlue.MetaData();
+ metaData.setArtist(song.getDescription());
+ metaData.setTitle(song.getTitle());
+ metaData.setCover(getResources().getDrawable(song.getImageResource(getActivity()), null));
+ Uri uri = Utils.getResourceUri(getActivity(), song.getFileResource(getActivity()));
+ mGlue.setMetaData(metaData);
+ mGlue.setMediaSource(uri);
+
+ setBackgroundType(PlaybackOverlayFragment.BG_LIGHT);
+ addPlaybackControlsRow();
+ }
+
+ @Override public void onStart() {
+ super.onStart();
+ mGlue.enableProgressUpdating(mGlue.hasValidMedia() && mGlue.isMediaPlaying());
+ }
+
+ @Override public void onStop() {
+ super.onStop();
+ mGlue.enableProgressUpdating(false);
+ mGlue.reset();
+ }
+
+ private void addPlaybackControlsRow() {
+ final PlaybackControlsRowPresenter controlsPresenter = mGlue
+ .createControlsRowAndPresenter();
+ ClassPresenterSelector selector = new ClassPresenterSelector();
+ Song.Presenter songPresenter = new Song.Presenter(getActivity());
+ songPresenter.setOnClickListener(this);
+ selector.addClassPresenter(Song.class, songPresenter);
+ selector.addClassPresenter(TrackListHeader.class,
+ new TrackListHeader.Presenter(getActivity()));
+ selector.addClassPresenter(PlaybackControlsRow.class, controlsPresenter);
+ mRowsAdapter = new ArrayObjectAdapter(selector);
+ mRowsAdapter.add(mGlue.getControlsRow());
+ mRowsAdapter.add(new TrackListHeader());
+ mRowsAdapter.addAll(2, mSongList);
+ setAdapter(mRowsAdapter);
+ setOnItemViewClickedListener(this);
+ }
+
+ @Override public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item,
+ RowPresenter.ViewHolder rowViewHolder, Row row) {
+ if (!(item instanceof Action)) return;
+ mGlue.onActionClicked((Action) item);
+ }
+
+
+ @Override public void onSongRowClicked(Song song) {
+ mCurrentSongIndex = mSongList.indexOf(song);
+ startPlayback();
+ }
+
+
+ @Override public void onMediaFileFinishedPlaying(MediaPlayerGlue.MetaData song) {
+ if (mGlue.repeatOne()) {
+ startPlayback();
+ return;
+ }
+ if (mGlue.useShuffle()) {
+ mCurrentSongIndex = (int) (Math.random() * mSongList.size());
+ } else mCurrentSongIndex++;
+ if (mCurrentSongIndex >= mSongList.size()) {
+ mCurrentSongIndex = 0;
+ if (!mGlue.repeatAll()) return;
+ }
+ startPlayback();
+ }
+
+ private void startPlayback() {
+ Song song = mSongList.get(mCurrentSongIndex);
+ MediaPlayerGlue.MetaData metaData = new MediaPlayerGlue.MetaData();
+ metaData.setArtist(song.getDescription());
+ metaData.setTitle(song.getTitle());
+ metaData.setCover(getResources().getDrawable(song.getImageResource(getActivity()), null));
+
+ Uri uri = Utils.getResourceUri(getActivity(), song.getFileResource(getActivity()));
+ mGlue.setMetaData(metaData);
+ mGlue.setMediaSource(uri);
+ mGlue.startPlayback();
+ }
+}
diff --git a/samples/SupportLeanbackShowcase/app/src/main/java/android/support/v17/leanback/supportleanbackshowcase/app/media/MusicExampleActivity.java b/samples/SupportLeanbackShowcase/app/src/main/java/android/support/v17/leanback/supportleanbackshowcase/app/media/MusicExampleActivity.java
new file mode 100644
index 000000000..3107ed615
--- /dev/null
+++ b/samples/SupportLeanbackShowcase/app/src/main/java/android/support/v17/leanback/supportleanbackshowcase/app/media/MusicExampleActivity.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2014 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 android.support.v17.leanback.supportleanbackshowcase.app.media;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.support.v17.leanback.supportleanbackshowcase.R;
+
+/**
+ * TODO: Javadoc
+ */
+public class MusicExampleActivity extends Activity {
+
+ @Override public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_music_example);
+ }
+}
diff --git a/samples/SupportLeanbackShowcase/app/src/main/java/android/support/v17/leanback/supportleanbackshowcase/app/media/SongListRow.java b/samples/SupportLeanbackShowcase/app/src/main/java/android/support/v17/leanback/supportleanbackshowcase/app/media/SongListRow.java
new file mode 100644
index 000000000..409613241
--- /dev/null
+++ b/samples/SupportLeanbackShowcase/app/src/main/java/android/support/v17/leanback/supportleanbackshowcase/app/media/SongListRow.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2015 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 android.support.v17.leanback.supportleanbackshowcase.app.media;
+
+import android.support.v17.leanback.widget.Row;
+
+public class SongListRow extends Row {}
diff --git a/samples/SupportLeanbackShowcase/app/src/main/java/android/support/v17/leanback/supportleanbackshowcase/app/media/TrackListHeader.java b/samples/SupportLeanbackShowcase/app/src/main/java/android/support/v17/leanback/supportleanbackshowcase/app/media/TrackListHeader.java
new file mode 100644
index 000000000..ff794a24d
--- /dev/null
+++ b/samples/SupportLeanbackShowcase/app/src/main/java/android/support/v17/leanback/supportleanbackshowcase/app/media/TrackListHeader.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2015 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 android.support.v17.leanback.supportleanbackshowcase.app.media;
+
+import android.content.Context;
+import android.support.v17.leanback.supportleanbackshowcase.R;
+import android.support.v17.leanback.widget.Row;
+import android.support.v17.leanback.widget.RowPresenter;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+public class TrackListHeader extends Row {
+
+ public static class Presenter extends RowPresenter {
+
+ private final Context mContext;
+
+ public Presenter(Context context) {
+ mContext = context;
+ setHeaderPresenter(null);
+ }
+
+ @Override public boolean isUsingDefaultSelectEffect() {
+ return false;
+ }
+
+ @Override protected ViewHolder createRowViewHolder(ViewGroup parent) {
+ View view = LayoutInflater.from(mContext).inflate(R.layout.row_track_list_header, parent, false);
+ view.setFocusable(false);
+ view.setFocusableInTouchMode(false);
+ return new ViewHolder(view);
+ }
+ }
+
+
+}
diff --git a/samples/SupportLeanbackShowcase/app/src/main/java/android/support/v17/leanback/supportleanbackshowcase/app/media/VideoConsumptionExampleFragment.java b/samples/SupportLeanbackShowcase/app/src/main/java/android/support/v17/leanback/supportleanbackshowcase/app/media/VideoConsumptionExampleFragment.java
new file mode 100644
index 000000000..9a40635cf
--- /dev/null
+++ b/samples/SupportLeanbackShowcase/app/src/main/java/android/support/v17/leanback/supportleanbackshowcase/app/media/VideoConsumptionExampleFragment.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2015 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 android.support.v17.leanback.supportleanbackshowcase.app.media;
+
+import android.app.Fragment;
+import android.os.Bundle;
+import android.support.v17.leanback.app.PlaybackOverlayFragment;
+import android.support.v17.leanback.supportleanbackshowcase.app.MainFragment;
+import android.support.v17.leanback.widget.Action;
+import android.support.v17.leanback.widget.ArrayObjectAdapter;
+import android.support.v17.leanback.widget.OnItemViewClickedListener;
+import android.support.v17.leanback.widget.PlaybackControlsRow;
+import android.support.v17.leanback.widget.PlaybackControlsRowPresenter;
+import android.support.v17.leanback.widget.Presenter;
+import android.support.v17.leanback.widget.Row;
+import android.support.v17.leanback.widget.RowPresenter;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+
+
+public class VideoConsumptionExampleFragment extends PlaybackOverlayFragment implements
+ OnItemViewClickedListener, MediaPlayerGlue.OnMediaFileFinishedPlayingListener {
+
+ private static final String URL = "http://techslides.com/demos/sample-videos/small.mp4";
+ private ArrayObjectAdapter mRowsAdapter;
+ private MediaPlayerGlue mGlue;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mGlue = new VideoMediaPlayerGlue(getActivity(), this) {
+
+ @Override
+ protected void onRowChanged(PlaybackControlsRow row) {
+ if (mRowsAdapter == null) return;
+ mRowsAdapter.notifyArrayItemRangeChanged(0, 1);
+ }
+ };
+ mGlue.setOnMediaFileFinishedPlayingListener(this);
+ MediaPlayerGlue.MetaData metaData = new MediaPlayerGlue.MetaData();
+ metaData.setArtist("A Googler");
+ metaData.setTitle("Diving with Sharks");
+ mGlue.setMetaData(metaData);
+ mGlue.setMediaSource(URL);
+
+ Fragment videoSurfaceFragment = getFragmentManager()
+ .findFragmentByTag(VideoExampleActivity.VIDEO_SURFACE_FRAGMENT_TAG);
+ SurfaceView surface = (SurfaceView) videoSurfaceFragment.getView();
+ surface.getHolder().addCallback(new SurfaceHolder.Callback() {
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ mGlue.setDisplay(holder);
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ // Nothing to do
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ }
+ });
+
+ setBackgroundType(PlaybackOverlayFragment.BG_LIGHT);
+ addPlaybackControlsRow();
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ mGlue.enableProgressUpdating(mGlue.hasValidMedia() && mGlue.isMediaPlaying());
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ mGlue.enableProgressUpdating(false);
+ mGlue.reset();
+ }
+
+ private void addPlaybackControlsRow() {
+ final PlaybackControlsRowPresenter controlsPresenter = mGlue
+ .createControlsRowAndPresenter();
+ mRowsAdapter = new ArrayObjectAdapter(controlsPresenter);
+ mRowsAdapter.add(mGlue.getControlsRow());
+ setAdapter(mRowsAdapter);
+ setOnItemViewClickedListener(this);
+ }
+
+ @Override
+ public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item,
+ RowPresenter.ViewHolder rowViewHolder, Row row) {
+ if (!(item instanceof Action)) return;
+ mGlue.onActionClicked((Action) item);
+ }
+
+
+ @Override
+ public void onMediaFileFinishedPlaying(MediaPlayerGlue.MetaData metaData) {
+ mGlue.startPlayback();
+ }
+
+}
diff --git a/samples/SupportLeanbackShowcase/app/src/main/java/android/support/v17/leanback/supportleanbackshowcase/app/media/VideoExampleActivity.java b/samples/SupportLeanbackShowcase/app/src/main/java/android/support/v17/leanback/supportleanbackshowcase/app/media/VideoExampleActivity.java
new file mode 100644
index 000000000..63eef8219
--- /dev/null
+++ b/samples/SupportLeanbackShowcase/app/src/main/java/android/support/v17/leanback/supportleanbackshowcase/app/media/VideoExampleActivity.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2014 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 android.support.v17.leanback.supportleanbackshowcase.app.media;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.support.v17.leanback.supportleanbackshowcase.R;
+
+/**
+ * TODO: Javadoc
+ */
+public class VideoExampleActivity extends Activity {
+
+ public static final String VIDEO_SURFACE_FRAGMENT_TAG = "VIDEO_SURFACE";
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_video_example);
+
+
+ getFragmentManager().beginTransaction()
+ .replace(R.id.videoFragment, new VideoSurfaceFragment(), VIDEO_SURFACE_FRAGMENT_TAG)
+ .add(R.id.videoFragment, new VideoConsumptionExampleFragment())
+ .commit();
+ }
+}
diff --git a/samples/SupportLeanbackShowcase/app/src/main/java/android/support/v17/leanback/supportleanbackshowcase/app/media/VideoMediaPlayerGlue.java b/samples/SupportLeanbackShowcase/app/src/main/java/android/support/v17/leanback/supportleanbackshowcase/app/media/VideoMediaPlayerGlue.java
new file mode 100644
index 000000000..52ab09fb9
--- /dev/null
+++ b/samples/SupportLeanbackShowcase/app/src/main/java/android/support/v17/leanback/supportleanbackshowcase/app/media/VideoMediaPlayerGlue.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2015 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 android.support.v17.leanback.supportleanbackshowcase.app.media;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.support.v17.leanback.app.PlaybackOverlayFragment;
+import android.support.v17.leanback.supportleanbackshowcase.app.media.MediaPlayerGlue;
+import android.support.v17.leanback.widget.Action;
+import android.support.v17.leanback.widget.ArrayObjectAdapter;
+import android.support.v17.leanback.widget.PlaybackControlsRow;
+import android.support.v17.leanback.widget.PlaybackControlsRowPresenter;
+
+public abstract class VideoMediaPlayerGlue extends MediaPlayerGlue {
+
+ private final PlaybackControlsRow.ClosedCaptioningAction mClosedCaptioningAction;
+
+ public VideoMediaPlayerGlue(Context context, PlaybackOverlayFragment fragment) {
+ super(context, fragment);
+
+ // Instantiate secondary actions
+ mClosedCaptioningAction = new PlaybackControlsRow.ClosedCaptioningAction(context);
+ setFadingEnabled(true);
+ }
+
+ @Override protected void addSecondaryActions(ArrayObjectAdapter secondaryActionsAdapter) {
+ secondaryActionsAdapter.add(mClosedCaptioningAction);
+ secondaryActionsAdapter.add(mThumbsDownAction);
+ secondaryActionsAdapter.add(mThumbsUpAction);
+ }
+
+ @Override public void onActionClicked(Action action) {
+ super.onActionClicked(action);
+ if (action == mClosedCaptioningAction) {
+ mClosedCaptioningAction.nextIndex();
+ }
+ }
+
+ public void setupControlsRowPresenter(PlaybackControlsRowPresenter presenter) {
+ // TODO: hahnr@ move into resources
+ presenter.setProgressColor(Color.parseColor("#EEFF41"));
+ presenter.setBackgroundColor(Color.parseColor("#007236"));
+ }
+}
diff --git a/samples/SupportLeanbackShowcase/app/src/main/java/android/support/v17/leanback/supportleanbackshowcase/app/media/VideoSurfaceFragment.java b/samples/SupportLeanbackShowcase/app/src/main/java/android/support/v17/leanback/supportleanbackshowcase/app/media/VideoSurfaceFragment.java
new file mode 100644
index 000000000..158a048c4
--- /dev/null
+++ b/samples/SupportLeanbackShowcase/app/src/main/java/android/support/v17/leanback/supportleanbackshowcase/app/media/VideoSurfaceFragment.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2015 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 android.support.v17.leanback.supportleanbackshowcase.app.media;
+
+import android.app.Fragment;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v17.leanback.supportleanbackshowcase.R;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+
+public class VideoSurfaceFragment extends Fragment {
+
+ private static final String TAG = "VideoSurfaceFragment";
+
+ @Override public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ }
+
+ @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ return inflater.inflate(R.layout.video_surface_fragment, null);
+ }
+}