Copyright notice: This article is the original translation of the blogger, please indicate the source.

Recommendation: welcome attention I create Android TV Jane books project, regularly to share some AndroidTv related content: www.jianshu.com/c/37efc6e97…


### Video control minimize video streaming transmission in VideoView.

  • I cited Google’s latest Android TV app. The AOSP sample application implementation is uncertain.

For video controls, we explain the following.

  1. UI Update section of Action (this chapter)
  2. Video Control Section (This chapter)
  3. MediaSession, implemented through MediaController’s TransportControls (next chapter) (When the user presses the video control button on the TV remote control, MediaSession can handle the action. It allows other activities to inherit video control. Especially LeanbackLauncher, a home display that can play videos in the background.

4. Set MediaMetadata to MediaSession (next chapter) (” Now Playing Card “will appear at the top of the recommended line.)

In this chapter, the implementation of video control is introduced. Because Google’s sample application implements all of 1 through 4, the source code is a bit long and difficult to understand for beginners. I only did a minimum of 1 ~ 2 implementations. I’ll explain these implementations for each section in this chapter, so download and reference the source code on Github first (I refactor the prepared movie content using the MovieProvider class). The techniques in this chapter are common in Android.

In the next chapter I will describe the implementation of MediaSession. We can play the video background in the LeanbackLauncher by using MediaSession to pass the VideoView control to the LeanbackLauncher. # # # VideoView processing

PlaybackOverlayActivity needs to have the VideoView field variable “mVideoView” to control the video.

public class PlaybackOverlayActivity extends Activity {

    private static final String TAG = PlaybackOverlayActivity.class.getSimpleName();

    private VideoView mVideoView;

    private LeanbackPlaybackState mPlaybackState = LeanbackPlaybackState.IDLE;

    private int mPosition = 0;
    private long mStartTimeMillis;
    private long mDuration = -1;

    /*
     * List of various states that we can be in
     */
    public enum LeanbackPlaybackState {
        PLAYING, PAUSED, IDLE
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_playback_overlay);

        loadViews();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        stopPlayback();
        mVideoView.suspend();
        mVideoView.setVideoURI(null);
    }

    private void loadViews() {
        mVideoView = (VideoView) findViewById(R.id.videoView);
        mVideoView.setFocusable(false);
        mVideoView.setFocusableInTouchMode(false);

        Movie movie = (Movie) getIntent().getSerializableExtra(DetailsActivity.MOVIE);
        setVideoPath(movie.getVideoUrl());

    }

    public void setVideoPath(String videoUrl) {
        setPosition(0);
        mVideoView.setVideoPath(videoUrl);
        mStartTimeMillis = 0;
        mDuration = Utils.getDuration(videoUrl);
    }

    private void stopPlayback() {
        if (mVideoView != null) {
            mVideoView.stopPlayback();
        }
    }
Copy the code

# # # realize setOnActionClickedListener and onActionClicked callback

To assign each video control button action, we use setOnActionClickedListener, it is a kind of method of PlaybackControlsRowPresenter.

    private void setUpRows() {... /* add ListRow to second row of mRowsAdapter */ addOtherRows(); /* onClick */ playbackControlsRowPresenter.setOnActionClickedListener(newOnActionClickedListener() {
            public void onActionClicked(Action action) {
                if (action.getId() == mPlayPauseAction.getId()) {
                    /* PlayPause action */
                    togglePlayback(mPlayPauseAction.getIndex() == PlaybackControlsRow.PlayPauseAction.PLAY);
                } else if (action.getId() == mSkipNextAction.getId()) {
                    /* SkipNext action */
                    next(mCurrentPlaybackState == PlaybackState.STATE_PLAYING);
                } else if (action.getId() == mSkipPreviousAction.getId()) {
                    /* SkipPrevious action */
                    prev(mCurrentPlaybackState == PlaybackState.STATE_PLAYING);
                } else if (action.getId() == mFastForwardAction.getId()) {
                    /* FastForward action  */
                    fastForward();
                } else if (action.getId() == mRewindAction.getId()) {
                    /* Rewind action */
                    rewind();
                }
                if(action instanceof PlaybackControlsRow.MultiAction) { /* Following action is subclass of MultiAction * - PlayPauseAction  * - FastForwardAction * - RewindAction * - ThumbsAction * - RepeatAction * - ShuffleAction * - HighQualityAction * - ClosedCaptioningAction */ notifyChanged(action); }}});setAdapter(mRowsAdapter);

    }
Copy the code

From here you can explain the implementation of each action. Note that it’s important to distinguish between the “UI update section” and the “video control section,” as the video control section moves to MediaSession in the next chapter.

In the source code, I am PlaybackOverlayFragment. In Java implements “UI update part”, and “Video control part” is in PlaybackOverlayActivity. In Java implementation. ###PlayPauseAction

PlaybackOverlayFragment.java – PlayPauseAction

    private void togglePlayback(boolean playPause) {
        /* Video control part */
        ((PlaybackOverlayActivity) getActivity()).playPause(playPause);

        /* UI control part */
        playbackStateChanged();
    }
Copy the code

The video control section handles playing/pausing the video in VideoView. PlaybackOverlayActivity. Java – PlayPauseAction video control part

    public void playPause(boolean doPlay) {
        if (mPlaybackState == LeanbackPlaybackState.IDLE) {
            /* Callbacks for mVideoView */
            setupCallbacks();
        }

        if (doPlay && mPlaybackState ! = LeanbackPlaybackState.PLAYING) { mPlaybackState = LeanbackPlaybackState.PLAYING;if (mPosition > 0) {
                mVideoView.seekTo(mPosition);
            }
            mVideoView.start();
            mStartTimeMillis = System.currentTimeMillis();
        } else {
            mPlaybackState = LeanbackPlaybackState.PAUSED;
            int timeElapsedSinceStart = (int) (System.currentTimeMillis() - mStartTimeMillis);
            setPosition(mPosition + timeElapsedSinceStart);
            mVideoView.pause();
        }
    }

    private void setupCallbacks() {

        mVideoView.setOnErrorListener(new MediaPlayer.OnErrorListener() {

            @Override
            public boolean onError(MediaPlayer mp, int what, int extra) {
                mVideoView.stopPlayback();
                mPlaybackState = LeanbackPlaybackState.IDLE;
                return false; }}); mVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
            @Override
            public void onPrepared(MediaPlayer mp) {
                if(mPlaybackState == LeanbackPlaybackState.PLAYING) { mVideoView.start(); }}}); mVideoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { @Override public void onCompletion(MediaPlayer mp) { mPlaybackState = LeanbackPlaybackState.IDLE; }}); }Copy the code

The UI controls section will handle

  • Toggle play/pause ICONS
  • Updates the current time of the video
    public void playbackStateChanged() {

        if(mCurrentPlaybackState ! = PlaybackState.STATE_PLAYING) { mCurrentPlaybackState = PlaybackState.STATE_PLAYING; startProgressAutomation();setFadingEnabled(true);
            mPlayPauseAction.setIndex(PlaybackControlsRow.PlayPauseAction.PAUSE);
            mPlayPauseAction.setIcon(mPlayPauseAction.getDrawable(PlaybackControlsRow.PlayPauseAction.PAUSE));
            notifyChanged(mPlayPauseAction);
        } else if(mCurrentPlaybackState ! = PlaybackState.STATE_PAUSED) { mCurrentPlaybackState = PlaybackState.STATE_PAUSED; stopProgressAutomation(); //setFadingEnabled(false); // if set to false, PlaybackcontrolsRow will always be on the screen
            mPlayPauseAction.setIndex(PlaybackControlsRow.PlayPauseAction.PLAY);
            mPlayPauseAction.setIcon(mPlayPauseAction.getDrawable(PlaybackControlsRow.PlayPauseAction.PLAY));
            notifyChanged(mPlayPauseAction);
        }

        int currentTime = ((PlaybackOverlayActivity) getActivity()).getPosition();
        mPlaybackControlsRow.setCurrentTime(currentTime);
        mPlaybackControlsRow.setBufferedProgress(currentTime + SIMULATED_BUFFERED_TIME);

    }

    private void startProgressAutomation() {
        if (mRunnable == null) {
            mRunnable = new Runnable() {
                @Override
                public void run() {
                    int updatePeriod = getUpdatePeriod();
                    int currentTime = mPlaybackControlsRow.getCurrentTime() + updatePeriod;
                    int totalTime = mPlaybackControlsRow.getTotalTime();
                    mPlaybackControlsRow.setCurrentTime(currentTime);
                    mPlaybackControlsRow.setBufferedProgress(currentTime + SIMULATED_BUFFERED_TIME);

                    if (totalTime > 0 && totalTime <= currentTime) {
                        stopProgressAutomation();
                        //next(true);
                    } else{ mHandler.postDelayed(this, updatePeriod); }}}; mHandler.postDelayed(mRunnable, getUpdatePeriod()); } } private voidstopProgressAutomation() {
        if(mHandler ! = null && mRunnable ! = null) { mHandler.removeCallbacks(mRunnable); mRunnable = null; }}Copy the code

### Rewind & fast forward

PlaybackOverlayFragment.java – rewind/fastForward

    private void fastForward() {
        /* Video control part */
        ((PlaybackOverlayActivity) getActivity()).fastForward();

        /* UI part */
        int currentTime = ((PlaybackOverlayActivity) getActivity()).getPosition();
        mPlaybackControlsRow.setCurrentTime(currentTime);
        mPlaybackControlsRow.setBufferedProgress(currentTime + SIMULATED_BUFFERED_TIME);
    }

    private void rewind() {
        /* Video control part */
        ((PlaybackOverlayActivity) getActivity()).rewind();

        /* UI part */
        int currentTime = ((PlaybackOverlayActivity) getActivity()).getPosition();
        mPlaybackControlsRow.setCurrentTime(currentTime);
        mPlaybackControlsRow.setBufferedProgress(currentTime + SIMULATED_BUFFERED_TIME);
    }
Copy the code

Here, the rewind and fast-forward video controls are implemented as simply as fast forward/forward the current position for 10 seconds.

PlaybackOverlayActivity. Java – rewind/fastForward Video control section

public void fastForward() {
        if(mDuration ! = -1) { // Fast forward 10 seconds.setPosition(mVideoView.getCurrentPosition() + (10 * 1000));
            mVideoView.seekTo(mPosition);
        }
    }

    public void rewind() {
        // rewind 10 seconds
        setPosition(mVideoView.getCurrentPosition() - (10 * 1000));
        mVideoView.seekTo(mPosition);
    }
Copy the code

The UI control section is updating the current time of the video. ###SkipPrevious & SkipNext

PlaybackOverlayFragment.java – skipPrevious/skipNext

    private void next(boolean autoPlay) {
        /* Video control part */
        if (++mCurrentItem >= mItems.size()) { // Current Item is set to next here
            mCurrentItem = 0;
        }

        if (autoPlay) {
            mCurrentPlaybackState = PlaybackState.STATE_PAUSED;
        }

        Movie movie = mItems.get(mCurrentItem);
        if(movie ! = null) { ((PlaybackOverlayActivity) getActivity()).setVideoPath(movie.getVideoUrl()); ((PlaybackOverlayActivity) getActivity()).setPlaybackState(PlaybackOverlayActivity.LeanbackPlaybackState.PAUSED); ((PlaybackOverlayActivity) getActivity()).playPause(autoPlay); } /* UI part */ playbackStateChanged(); updatePlaybackRow(mCurrentItem); } private void prev(boolean autoPlay) { /* Video control part */if (--mCurrentItem < 0) { // Current Item is set to previous here
            mCurrentItem = mItems.size() - 1;
        }
        if (autoPlay) {
            mCurrentPlaybackState = PlaybackState.STATE_PAUSED;
        }

        Movie movie = mItems.get(mCurrentItem);
        if(movie ! = null) { ((PlaybackOverlayActivity) getActivity()).setVideoPath(movie.getVideoUrl()); ((PlaybackOverlayActivity) getActivity()).setPlaybackState(PlaybackOverlayActivity.LeanbackPlaybackState.PAUSED); ((PlaybackOverlayActivity) getActivity()).playPause(autoPlay); } /* UI part */ playbackStateChanged(); updatePlaybackRow(mCurrentItem); }Copy the code

For the video control section, in addition to the first line, there are two other features that do the same thing. MCurrentItem is set to previous/next, then use setVideoPath and Play/Pause to set the correct video path based on the current play/pause state using playPause.

The UI controls section, the first line calls the playbackStateChanged () method, but you only need to control startProgressAutomation/stopProgressAutomation to update the current time state of the video. The updateplaybackRow method updates the DetailsDescription of the video content.

PlaybackOverlayFragment.java – updatePlaybackRow

private void updatePlaybackRow(int index) {
        Log.d(TAG, "updatePlaybackRow");
        if(mPlaybackControlsRow.getItem() ! = null) { Movie item = (Movie) mPlaybackControlsRow.getItem(); item.setTitle(mItems.get(mCurrentItem).getTitle()); item.setStudio(mItems.get(mCurrentItem).getStudio()); mRowsAdapter.notifyArrayItemRangeChanged(0, 1); /* total time is necessary to show video playing time progress bar */ int duration = (int) Utils.getDuration(mItems.get(mCurrentItem).getVideoUrl()); Log.i(TAG,"videoUrl: " + mItems.get(mCurrentItem).getVideoUrl());
            Log.i(TAG, "duration = " + duration);
            mPlaybackControlsRow.setTotalTime(duration);
            mPlaybackControlsRow.setCurrentTime(0);
            mPlaybackControlsRow.setBufferedProgress(0);
        }
        if(SHOW_IMAGE) { mPlaybackControlsRowTarget = new PicassoPlaybackControlsRowTarget(mPlaybackControlsRow); updateVideoImage(mItems.get(mCurrentItem).getCardImageURI()); }}Copy the code

###ThumbUp & ThumbDown & Repeat & Shuffle & HighQuality & ClosedCaptioning & MoreActions You can change this by setting the index of the operation. Implemented under the onActionClicked method to check the behavior of each index setting.

PlaybackOverlayFragment.java – onActionClicked

 /* Change icon */
if (action instanceof PlaybackControlsRow.ThumbsUpAction ||
    action instanceof PlaybackControlsRow.ThumbsDownAction ||
    action instanceof PlaybackControlsRow.RepeatAction ||
    action instanceof PlaybackControlsRow.ShuffleAction ||
    action instanceof PlaybackControlsRow.HighQualityAction ||
    action instanceof PlaybackControlsRow.ClosedCaptioningAction) {
                        ((PlaybackControlsRow.MultiAction) action).nextIndex();
}
Copy the code

The code has been uploaded to Github. In the next chapter, we will implement MediaSession. Pay attention to wechat public number, regularly recommend mobile development related articles for you.