PlaybackOverlayActivity & PlaybackOverlayFragment
– Android TV application hands on tutorial 7

PlaybackOverview1-2015-07-14-200711

PlaybackOverlayActivity & PlaybackOverlayFragment – Theory

I will explain about only UI part in this chapter, video control is explained in next chapter.

We will implement UI for handling video contents.

PlaybackOverlayActivity & PlaybackOverlayFragment – Implementation

Creating PlaybackOverlayActivity & PlaybackOverlayFragment is the same way as in introduced previously.

PlaybackOverlayActivity

New → Activity → BlankActivity

Activity Name: PlaybackOverlayActivity
Layout Name: activity_playback_overlay
 …

This PlaybackOverlayActivity will refer activity_playback_overlay.xml file in res/layout folder. It is constructed in 2 layer – VideoView in the back and PlaybackOverlayFragment in the front. VideoView is the view which we will play video contents, and PlaybackOverlayFragment will show the UI for controlling video, which we will focus on in this chapter. Implementat activity_playback_overlay.xml as follows.

<?xml version="1.0" encoding="utf-8"?>

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent" android:layout_height="match_parent">

    <VideoView android:id="@+id/videoView" android:layout_width="match_parent"
        android:layout_alignParentRight="true" android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true" android:layout_alignParentBottom="true"
        android:layout_height="match_parent" android:layout_gravity="center"
        android:layout_centerInParent="true"></VideoView>

    <fragment android:id="@+id/playback_controls_fragment"
        android:name="com.corochann.androidtvapptutorial.PlaybackOverlayFragment"
        android:layout_width="match_parent" android:layout_height="match_parent" />

</FrameLayout>

We don’t need any modification for PlaybackOverlayActivity for now.

PlaybackOverlayFragment

New -> Java Class -> Name: PlaybackOverlayFragment

This PlaybackOverlayFragment is subclass of android.support.v17.leanback.app.PlaybackOverlayFragment which provides us component to make video control UI.

It works very similar to VideoDetailsFragment, so you just need to call setAdapter(adapter), after setting rows in adapter. For PlaybackOverlayFragment, we need to set instance of “PlaybackControlsRow”, which shows the video control UI, to first row element of adapter.

PlaybackOverview1

So let’s study “PlaybackControlsRow” and its Presenter, “PlaybackControlsRowPresenter”. We need to specify following in each instance.

  • PlaybackControlsRow
    • PrimaryActionsAdapter      – It owns icons for main row
    • SecondaryActionsAdapter – It owns icons for sub rowNote that PlaybackControlsRow class provides us many useful default icons for video control. We only need to instantiate its inner class.
  • PlaybackControlsRowPresenter
    • DescriptionPresenter – It is Presenter for displaying item details on the top of PrimaryActions bar. 
PlaybackOverview2

Above photo explains internal construction of PlaybackControlsRow – PlaybackControlsRowpresenter. Constructor of PlaybackControlsRowPresenter takes argument of  DescriptionPresenter object, which determines how to show video details. This time, we will reuse DetailsDescriptionPresenter, which we made in previous chapter to show item details in DetailsFragment. PlaybackControlsRow have 2 rows inside to set the actions, PrimaryActionsAdapter and SecondaryActionsAdapter. We can set “actions” icon in these actionsadapters. See above photos for the available actions and these icons.

Sample implementation of PlaybackOverlayFragment is following.

package com.corochann.androidtvapptutorial;

import android.content.Context;
import android.os.Bundle;
import android.support.v17.leanback.widget.ArrayObjectAdapter;
import android.support.v17.leanback.widget.ClassPresenterSelector;
import android.support.v17.leanback.widget.ControlButtonPresenterSelector;
import android.support.v17.leanback.widget.HeaderItem;
import android.support.v17.leanback.widget.ListRow;
import android.support.v17.leanback.widget.ListRowPresenter;
import android.support.v17.leanback.widget.PlaybackControlsRow;
import android.support.v17.leanback.widget.PlaybackControlsRowPresenter;
import android.util.Log;

/**
 * Created by corochann on 7/7/2015.
 */
public class PlaybackOverlayFragment extends android.support.v17.leanback.app.PlaybackOverlayFragment {

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

    private Movie mSelectedMovie;
    private PlaybackControlsRow mPlaybackControlsRow;
    private ArrayObjectAdapter mPrimaryActionAdapter;
    private ArrayObjectAdapter mSecondaryActionAdapter;

    private PlaybackControlsRow.PlayPauseAction mPlayPauseAction;
    private PlaybackControlsRow.RepeatAction mRepeatAction;
    private PlaybackControlsRow.ThumbsUpAction mThumbsUpAction;
    private PlaybackControlsRow.ThumbsDownAction mThumbsDownAction;
    private PlaybackControlsRow.ShuffleAction mShuffleAction;
    private PlaybackControlsRow.SkipNextAction mSkipNextAction;
    private PlaybackControlsRow.SkipPreviousAction mSkipPreviousAction;
    private PlaybackControlsRow.FastForwardAction mFastForwardAction;
    private PlaybackControlsRow.RewindAction mRewindAction;
    private PlaybackControlsRow.HighQualityAction mHighQualityAction;
    private PlaybackControlsRow.ClosedCaptioningAction mClosedCaptioningAction;
    private PlaybackControlsRow.MoreActions mMoreActions;


    @Override
    public void onCreate(Bundle savedInstanceState) {
        Log.i(TAG, "onCreate");
        super.onCreate(savedInstanceState);

        mSelectedMovie = (Movie) getActivity().getIntent().getSerializableExtra(DetailsActivity.MOVIE);

        setBackgroundType(PlaybackOverlayFragment.BG_LIGHT);
        setFadingEnabled(true);

        setUpRows();
    }

    private ArrayObjectAdapter mRowsAdapter;

    private void setUpRows() {
        ClassPresenterSelector ps = new ClassPresenterSelector();

        PlaybackControlsRowPresenter playbackControlsRowPresenter;
        playbackControlsRowPresenter = new PlaybackControlsRowPresenter(new DetailsDescriptionPresenter());

        ps.addClassPresenter(PlaybackControlsRow.class, playbackControlsRowPresenter);
        ps.addClassPresenter(ListRow.class, new ListRowPresenter());
        mRowsAdapter = new ArrayObjectAdapter(ps);

        /*
         * Add PlaybackControlsRow to mRowsAdapter, which makes video control UI.
         * PlaybackControlsRow is supposed to be first Row of mRowsAdapter.
         */
        addPlaybackControlsRow();
        /* add ListRow to second row of mRowsAdapter */
        addOtherRows();

        setAdapter(mRowsAdapter);

    }

    private void addPlaybackControlsRow() {
        mPlaybackControlsRow = new PlaybackControlsRow(mSelectedMovie);
        mRowsAdapter.add(mPlaybackControlsRow);

        ControlButtonPresenterSelector presenterSelector = new ControlButtonPresenterSelector();
        mPrimaryActionsAdapter = new ArrayObjectAdapter(presenterSelector);
        mSecondaryActionsAdapter = new ArrayObjectAdapter(presenterSelector);
        mPlaybackControlsRow.setPrimaryActionsAdapter(mPrimaryActionsAdapter);
        mPlaybackControlsRow.setSecondaryActionsAdapter(mSecondaryActionsAdapter);
        
        Activity activity = getActivity();
        mPlayPauseAction = new PlaybackControlsRow.PlayPauseAction(activity);
        mRepeatAction = new PlaybackControlsRow.RepeatAction(activity);
        mThumbsUpAction = new PlaybackControlsRow.ThumbsUpAction(activity);
        mThumbsDownAction = new PlaybackControlsRow.ThumbsDownAction(activity);
        mShuffleAction = new PlaybackControlsRow.ShuffleAction(activity);
        mSkipNextAction = new PlaybackControlsRow.SkipNextAction(activity);
        mSkipPreviousAction = new PlaybackControlsRow.SkipPreviousAction(activity);
        mFastForwardAction = new PlaybackControlsRow.FastForwardAction(activity);
        mRewindAction = new PlaybackControlsRow.RewindAction(activity);
        mHighQualityAction = new PlaybackControlsRow.HighQualityAction(activity);
        mClosedCaptioningAction = new PlaybackControlsRow.ClosedCaptioningAction(activity);
        mMoreActions = new PlaybackControlsRow.MoreActions(activity);

        /* PrimaryAction setting */
        mPrimaryActionsAdapter.add(mSkipPreviousAction);
        mPrimaryActionsAdapter.add(mRewindAction);
        mPrimaryActionsAdapter.add(mPlayPauseAction);
        mPrimaryActionsAdapter.add(mFastForwardAction);
        mPrimaryActionsAdapter.add(mSkipNextAction);

        /* SecondaryAction setting */
        mSecondaryActionsAdapter.add(mThumbsUpAction);
        mSecondaryActionsAdapter.add(mThumbsDownAction);
        mSecondaryActionsAdapter.add(mRepeatAction);
        mSecondaryActionsAdapter.add(mShuffleAction);
        mSecondaryActionsAdapter.add(mHighQualityAction);
        mSecondaryActionsAdapter.add(mClosedCaptioningAction);
        mSecondaryActionsAdapter.add(mMoreActions);
    }

    private void addOtherRows() {
        ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(new CardPresenter());
        Movie movie = new Movie();
        movie.setTitle("Title");
        movie.setStudio("studio");
        movie.setDescription("description");
        movie.setCardImageUrl("http://heimkehrend.raindrop.jp/kl-hacker/wp-content/uploads/2014/08/DSC02580.jpg");
        listRowAdapter.add(movie);
        listRowAdapter.add(movie);

        HeaderItem header = new HeaderItem(0, "OtherRows");
        mRowsAdapter.add(new ListRow(header, listRowAdapter));
    }

}

To launch PlaybackOverlayAcitivity from DetailsActivity, let’s implement setOnActionClickedListener.

        @Override
        protected void onPostExecute(DetailsOverviewRow row) {
            /* 1st row: DetailsOverviewRow */

              /* action setting*/
            SparseArrayObjectAdapter sparseArrayObjectAdapter = new SparseArrayObjectAdapter();
            sparseArrayObjectAdapter.set(0, new Action(ACTION_PLAY_VIDEO, "Play Video"));
            sparseArrayObjectAdapter.set(1, new Action(1, "Action 2", "label"));
            sparseArrayObjectAdapter.set(2, new Action(2, "Action 3", "label"));

            row.setActionsAdapter(sparseArrayObjectAdapter);

            mFwdorPresenter.setOnActionClickedListener(new OnActionClickedListener() {
                @Override
                public void onActionClicked(Action action) {
                    if (action.getId() == ACTION_PLAY_VIDEO) {
                        Intent intent = new Intent(getActivity(), PlaybackOverlayActivity.class);
                        intent.putExtra("Movie", mSelectedMovie);
                        intent.putExtra("shouldStart", true);
                        startActivity(intent);
                    }
                }
            });
    <string name="movie">Movie</string>
    <string name="should_start">shouldStart</string>

Build and Run

PlaybackOverview1-2015-07-14-200711

Background VideoView is not implemented yet and black. But we can see Video control UI is already done!

Source code is on github.

I will continue to implement video controls using this UI in next chapter.

Leave a Comment

Your email address will not be published. Required fields are marked *