Create PlaybackController

This commit is contained in:
Eric Reed committed 2014-12-09 16:01:12 -05:00
1 parent 66560d9809
commit fb57d300e2
5 files changed
+177 -174

No files matched your search

@@ -126,7 +126,8 @@ public class BaseItemDetailsFragment extends DetailsFragment {
public void onActionClicked(Action action) {
if (action.getId() == ACTION_PLAY) {
Intent intent = new Intent(getActivity(), PlaybackOverlayActivity.class);
intent.putExtra("BaseItemDto", TvApp.getApplication().getSerializer().SerializeToString(mBaseItem));
String[] items = new String[] {TvApp.getApplication().getSerializer().SerializeToString(mBaseItem)};
intent.putExtra("Items", items);
intent.putExtra(getResources().getString(R.string.should_start), true);
startActivity(intent);
} else {
@@ -0,0 +1,131 @@
package tv.mediabrowser.mediabrowsertv;
import android.media.MediaPlayer;
import android.widget.VideoView;
import java.util.List;
import mediabrowser.model.dto.BaseItemDto;
/**
* Created by Eric on 12/9/2014.
*/
public class PlaybackController {
List<BaseItemDto> mItems;
VideoView mVideoView;
int mCurrentIndex = 0;
private int mCurrentPosition = 0;
private PlaybackState mPlaybackState = PlaybackState.IDLE;
private TvApp mApplication;
public PlaybackController(List<BaseItemDto> items) {
mItems = items;
mApplication = TvApp.getApplication();
}
public void init(VideoView view) {
mVideoView = view;
setupCallbacks();
}
public BaseItemDto getCurrentlyPlayingItem() {
return mItems.get(mCurrentIndex);
}
/**
* Implementation of OnPlayPauseClickedListener
*/
public void onFragmentPlayPause(int position, Boolean playPause) {
Utils.Play(getCurrentlyPlayingItem(), mVideoView);
if (position == 0 || mPlaybackState == PlaybackState.IDLE) {
setupCallbacks();
mPlaybackState = PlaybackState.IDLE;
}
if (playPause && mPlaybackState != PlaybackState.PLAYING) {
mPlaybackState = PlaybackState.PLAYING;
if (position > 0) {
mVideoView.seekTo(position);
mVideoView.start();
}
} else {
mPlaybackState = PlaybackState.PAUSED;
mVideoView.pause();
}
}
public void play() {
}
public void next() {
}
public void prev() {
}
private void setupCallbacks() {
mVideoView.setOnErrorListener(new MediaPlayer.OnErrorListener() {
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
String msg = "";
if (extra == MediaPlayer.MEDIA_ERROR_TIMED_OUT) {
msg = mApplication.getString(R.string.video_error_media_load_timeout);
} else if (what == MediaPlayer.MEDIA_ERROR_SERVER_DIED) {
msg = mApplication.getString(R.string.video_error_server_inaccessible);
} else {
msg = mApplication.getString(R.string.video_error_unknown_error);
}
mVideoView.stopPlayback();
mPlaybackState = PlaybackState.IDLE;
return false;
}
});
mVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
if (mPlaybackState == PlaybackState.PLAYING) {
mVideoView.start();
}
}
});
mVideoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
mPlaybackState = PlaybackState.IDLE;
Long mbPos = (long) mVideoView.getCurrentPosition() * 10000;
Utils.Stop(TvApp.getApplication().getCurrentPlayingItem(), mbPos);
mVideoView.suspend();
}
});
}
public int getmCurrentPosition() {
return mCurrentPosition;
}
public void setmCurrentPosition(int mCurrentPosition) {
this.mCurrentPosition = mCurrentPosition;
}
/*
* List of various states that we can be in
*/
public static enum PlaybackState {
PLAYING, PAUSED, BUFFERING, IDLE;
}
}
@@ -14,26 +14,15 @@
package tv.mediabrowser.mediabrowsertv;
import android.app.Activity;
import android.media.MediaPlayer;
import android.media.session.MediaController;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.Log;
import android.widget.FrameLayout;
import android.widget.VideoView;
import mediabrowser.apiinteraction.ApiClient;
import mediabrowser.apiinteraction.EmptyResponse;
import mediabrowser.model.dlna.StreamBuilder;
import mediabrowser.model.dlna.VideoOptions;
import mediabrowser.model.dto.BaseItemDto;
import mediabrowser.model.session.PlaybackStopInfo;
/**
* PlaybackOverlayActivity for video playback that loads PlaybackOverlayFragment
*/
public class PlaybackOverlayActivity extends Activity implements
PlaybackOverlayFragment.OnPlayPauseClickedListener {
public class PlaybackOverlayActivity extends Activity {
private static final String TAG = "PlaybackOverlayActivity";
private static final double MEDIA_HEIGHT = 0.95;
@@ -43,10 +32,8 @@ public class PlaybackOverlayActivity extends Activity implements
private static final double MEDIA_BOTTOM_MARGIN = 0.025;
private static final double MEDIA_LEFT_MARGIN = 0.025;
private ApiClient mApiClient;
private VideoView mVideoView;
private PlaybackState mPlaybackState = PlaybackState.IDLE;
private TvApp mApplication;
/**
* Called when the activity is first created.
@@ -54,45 +41,22 @@ public class PlaybackOverlayActivity extends Activity implements
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mApiClient = TvApp.getApplication().getApiClient();
setContentView(R.layout.playback_controls);
mApplication = TvApp.getApplication();
loadViews();
setupCallbacks();
//overScan();
}
@Override
public void onDestroy() {
super.onDestroy();
Utils.Stop(TvApp.getApplication().getCurrentPlayingItem(), mVideoView.getCurrentPosition());
mVideoView.suspend();
}
/**
* Implementation of OnPlayPauseClickedListener
*/
public void onFragmentPlayPause(BaseItemDto movie, int position, Boolean playPause) {
Utils.Play(movie, mVideoView);
if (position == 0 || mPlaybackState == PlaybackState.IDLE) {
setupCallbacks();
mPlaybackState = PlaybackState.IDLE;
}
if (playPause && mPlaybackState != PlaybackState.PLAYING) {
mPlaybackState = PlaybackState.PLAYING;
if (position > 0) {
mVideoView.seekTo(position);
mVideoView.start();
}
} else {
mPlaybackState = PlaybackState.PAUSED;
mVideoView.pause();
}
Utils.Stop(mApplication.getPlaybackController().getCurrentlyPlayingItem(), mApplication.getPlaybackController().getmCurrentPosition() * 10000);
}
private void loadViews() {
mVideoView = (VideoView) findViewById(R.id.videoView);
mApplication.getPlaybackController().init(mVideoView);
}
private void overScan() {
@@ -108,55 +72,4 @@ public class PlaybackOverlayActivity extends Activity implements
lp.setMargins(marginLeft, marginTop, marginRight, marginBottom);
mVideoView.setLayoutParams(lp);
}
private void setupCallbacks() {
mVideoView.setOnErrorListener(new MediaPlayer.OnErrorListener() {
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
String msg = "";
if (extra == MediaPlayer.MEDIA_ERROR_TIMED_OUT) {
msg = getString(R.string.video_error_media_load_timeout);
} else if (what == MediaPlayer.MEDIA_ERROR_SERVER_DIED) {
msg = getString(R.string.video_error_server_inaccessible);
} else {
msg = getString(R.string.video_error_unknown_error);
}
mVideoView.stopPlayback();
mPlaybackState = PlaybackState.IDLE;
return false;
}
});
mVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
if (mPlaybackState == PlaybackState.PLAYING) {
mVideoView.start();
}
}
});
mVideoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
mPlaybackState = PlaybackState.IDLE;
Long mbPos = new Long(mVideoView.getCurrentPosition()) * 10000;
Utils.Stop(TvApp.getApplication().getCurrentPlayingItem(), mbPos);
mVideoView.suspend();
}
});
}
/*
* List of various states that we can be in
*/
public static enum PlaybackState {
PLAYING, PAUSED, BUFFERING, IDLE;
}
}
@@ -18,8 +18,6 @@ import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.media.MediaMetadataRetriever;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.support.v17.leanback.widget.AbstractDetailsDescriptionPresenter;
@@ -49,15 +47,15 @@ import android.support.v17.leanback.widget.Row;
import android.support.v17.leanback.widget.RowPresenter;
import android.util.Log;
import android.widget.Toast;
import android.widget.VideoView;
import com.squareup.picasso.Picasso;
import com.squareup.picasso.Target;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import mediabrowser.apiinteraction.android.GsonJsonSerializer;
import mediabrowser.model.dto.BaseItemDto;
/*
@@ -69,7 +67,6 @@ public class PlaybackOverlayFragment extends android.support.v17.leanback.app.Pl
private static Context sContext;
private static final boolean SHOW_DETAIL = true;
private static final boolean HIDE_MORE_ACTIONS = false;
private static final int PRIMARY_CONTROLS = 5;
private static final boolean SHOW_IMAGE = PRIMARY_CONTROLS <= 5;
private static final int BACKGROUND_TYPE = PlaybackOverlayFragment.BG_LIGHT;
@@ -92,20 +89,13 @@ public class PlaybackOverlayFragment extends android.support.v17.leanback.app.Pl
private SkipNextAction mSkipNextAction;
private SkipPreviousAction mSkipPreviousAction;
private PlaybackControlsRow mPlaybackControlsRow;
private ArrayList<BaseItemDto> mItems = new ArrayList<>();
private int mCurrentItem;
private Handler mHandler;
private Runnable mRunnable;
private BaseItemDto mSelectedMovie;
private TvApp mApplication;
private PicassoPlaybackControlsRowTarget mPlaybackControlsRowTarget;
OnPlayPauseClickedListener mCallback;
// Container Activity must implement this interface
public interface OnPlayPauseClickedListener {
public void onFragmentPlayPause(BaseItemDto movie, int position, Boolean playPause);
}
private PlaybackController mPlaybackController;
private List<BaseItemDto> mItemsToPlay = new ArrayList<>();
private VideoView mVideoView;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -114,15 +104,17 @@ public class PlaybackOverlayFragment extends android.support.v17.leanback.app.Pl
sContext = getActivity();
mApplication = TvApp.getApplication();
mItems = new ArrayList<>();
mSelectedMovie = mApplication.getSerializer().DeserializeFromString(getActivity()
.getIntent().getStringExtra("BaseItemDto"), BaseItemDto.class);
GsonJsonSerializer serializer = mApplication.getSerializer();
//TODO get additional items to play from intent
mItems.add(mSelectedMovie);
mCurrentItem = 0;
String[] passedItems = getActivity().getIntent().getStringArrayExtra("Items");
if (passedItems != null) {
for (String json : passedItems) {
mItemsToPlay.add((BaseItemDto) serializer.DeserializeFromString(json, BaseItemDto.class));
}
}
//ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(new CardPresenter());
mApplication.setPlaybackController(new PlaybackController(mItemsToPlay));
mPlaybackController = mApplication.getPlaybackController();
mHandler = new Handler();
@@ -147,23 +139,10 @@ public class PlaybackOverlayFragment extends android.support.v17.leanback.app.Pl
});
if (getActivity().getIntent().getBooleanExtra("ShouldStart", false)) {
mCallback.onFragmentPlayPause(mSelectedMovie, 0, true);
mPlaybackController.onFragmentPlayPause(0, true);
}
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// This makes sure that the container activity has implemented
// the callback interface. If not, it throws an exception
try {
mCallback = (OnPlayPauseClickedListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnPlayPauseClickedListener");
}
}
private void setupRows() {
@@ -182,18 +161,16 @@ public class PlaybackOverlayFragment extends android.support.v17.leanback.app.Pl
if (mPlayPauseAction.getIndex() == PlayPauseAction.PLAY) {
startProgressAutomation();
setFadingEnabled(true);
mCallback.onFragmentPlayPause(mItems.get(mCurrentItem),
mPlaybackControlsRow.getCurrentTime(), true);
mPlaybackController.onFragmentPlayPause(mPlaybackControlsRow.getCurrentTime(), true);
} else {
stopProgressAutomation();
setFadingEnabled(false);
mCallback.onFragmentPlayPause(mItems.get(mCurrentItem),
mPlaybackControlsRow.getCurrentTime(), false);
mPlaybackController.onFragmentPlayPause(mPlaybackControlsRow.getCurrentTime(), false);
}
} else if (action.getId() == mSkipNextAction.getId()) {
next();
mPlaybackController.next();
} else if (action.getId() == mSkipPreviousAction.getId()) {
prev();
mPlaybackController.prev();
} else if (action.getId() == mFastForwardAction.getId()) {
Toast.makeText(getActivity(), "TODO: Fast Forward", Toast.LENGTH_SHORT).show();
} else if (action.getId() == mRewindAction.getId()) {
@@ -205,20 +182,20 @@ public class PlaybackOverlayFragment extends android.support.v17.leanback.app.Pl
}
}
});
playbackControlsRowPresenter.setSecondaryActionsHidden(HIDE_MORE_ACTIONS);
playbackControlsRowPresenter.setSecondaryActionsHidden(false);
ps.addClassPresenter(PlaybackControlsRow.class, playbackControlsRowPresenter);
ps.addClassPresenter(ListRow.class, new ListRowPresenter());
mRowsAdapter = new ArrayObjectAdapter(ps);
addPlaybackControlsRow();
if (mItems.size() > 1) addOtherRows(); // only show queue if more than one item
if (mItemsToPlay.size() > 1) addQueueRow(); // only show queue if more than one item
setAdapter(mRowsAdapter);
}
private int getDuration() {
BaseItemDto movie = mItems.get(mCurrentItem);
BaseItemDto movie = mPlaybackController.getCurrentlyPlayingItem();
if (movie != null) {
Long mbRuntime = movie.getRunTimeTicks();
@@ -230,13 +207,13 @@ public class PlaybackOverlayFragment extends android.support.v17.leanback.app.Pl
private void addPlaybackControlsRow() {
if (SHOW_DETAIL) {
mPlaybackControlsRow = new PlaybackControlsRow(mSelectedMovie);
mPlaybackControlsRow = new PlaybackControlsRow(mPlaybackController.getCurrentlyPlayingItem());
} else {
mPlaybackControlsRow = new PlaybackControlsRow();
}
mRowsAdapter.add(mPlaybackControlsRow);
updatePlaybackRow(mCurrentItem);
updatePlaybackRow();
ControlButtonPresenterSelector presenterSelector = new ControlButtonPresenterSelector();
mPrimaryActionsAdapter = new ArrayObjectAdapter(presenterSelector);
@@ -293,15 +270,10 @@ public class PlaybackOverlayFragment extends android.support.v17.leanback.app.Pl
}
}
private void updatePlaybackRow(int index) {
if (mPlaybackControlsRow.getItem() != null) {
BaseItemDto item = (BaseItemDto) mPlaybackControlsRow.getItem();
// item.setTitle(mItems.get(mCurrentItem).getTitle());
// item.setStudio(mItems.get(mCurrentItem).getStudio());
}
private void updatePlaybackRow() {
if (SHOW_IMAGE) {
mPlaybackControlsRowTarget = new PicassoPlaybackControlsRowTarget(mPlaybackControlsRow);
updateVideoImage(Utils.getPrimaryImageUrl(mItems.get(mCurrentItem), mApplication.getApiClient()));
updateVideoImage(Utils.getPrimaryImageUrl(mPlaybackController.getCurrentlyPlayingItem(), mApplication.getApiClient()));
}
mRowsAdapter.notifyArrayItemRangeChanged(0, 1);
mPlaybackControlsRow.setTotalTime(getDuration());
@@ -309,9 +281,9 @@ public class PlaybackOverlayFragment extends android.support.v17.leanback.app.Pl
mPlaybackControlsRow.setBufferedProgress(0);
}
private void addOtherRows() {
private void addQueueRow() {
ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(new CardPresenter());
for (BaseItemDto movie : mItems) {
for (BaseItemDto movie : mItemsToPlay) {
listRowAdapter.add(movie);
}
HeaderItem header = new HeaderItem(0, getString(R.string.current_queue), null);
@@ -334,10 +306,11 @@ public class PlaybackOverlayFragment extends android.support.v17.leanback.app.Pl
int currentTime = mPlaybackControlsRow.getCurrentTime() + updatePeriod;
int totalTime = mPlaybackControlsRow.getTotalTime();
mPlaybackControlsRow.setCurrentTime(currentTime);
mPlaybackController.setmCurrentPosition(currentTime);
mPlaybackControlsRow.setBufferedProgress(currentTime + SIMULATED_BUFFERED_TIME);
if (totalTime > 0 && totalTime <= currentTime) {
next();
mPlaybackController.next();
}
mHandler.postDelayed(this, updatePeriod);
}
@@ -345,30 +318,6 @@ public class PlaybackOverlayFragment extends android.support.v17.leanback.app.Pl
mHandler.postDelayed(mRunnable, getUpdatePeriod());
}
private void next() {
if (++mCurrentItem >= mItems.size()) {
mCurrentItem = 0;
}
if (mPlayPauseAction.getIndex() == PlayPauseAction.PLAY) {
mCallback.onFragmentPlayPause(mItems.get(mCurrentItem), 0, false);
} else {
mCallback.onFragmentPlayPause(mItems.get(mCurrentItem), 0, true);
}
updatePlaybackRow(mCurrentItem);
}
private void prev() {
if (--mCurrentItem < 0) {
mCurrentItem = mItems.size() - 1;
}
if (mPlayPauseAction.getIndex() == PlayPauseAction.PLAY) {
mCallback.onFragmentPlayPause(mItems.get(mCurrentItem), 0, false);
} else {
mCallback.onFragmentPlayPause(mItems.get(mCurrentItem), 0, true);
}
updatePlaybackRow(mCurrentItem);
}
private void stopProgressAutomation() {
if (mHandler != null && mRunnable != null) {
@@ -24,6 +24,7 @@ public class TvApp extends Application {
private static TvApp app;
private UserDto currentUser;
private BaseItemDto currentPlayingItem;
private PlaybackController playbackController;
@Override
public void onCreate() {
@@ -93,4 +94,12 @@ public class TvApp extends Application {
public void setCurrentPlayingItem(BaseItemDto currentPlayingItem) {
this.currentPlayingItem = currentPlayingItem;
}
public PlaybackController getPlaybackController() {
return playbackController;
}
public void setPlaybackController(PlaybackController playbackController) {
this.playbackController = playbackController;
}
}