org.opensilk.video.tv.ui.playback.PlaybackControlsFragment.java Source code

Java tutorial

Introduction

Here is the source code for org.opensilk.video.tv.ui.playback.PlaybackControlsFragment.java

Source

/*
 * Copyright (c) 2016 OpenSilk Productions LLC.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.opensilk.video.tv.ui.playback;

import android.app.Activity;
import android.content.Intent;
import android.media.MediaDescription;
import android.media.MediaMetadata;
import android.media.browse.MediaBrowser;
import android.media.session.MediaController;
import android.media.session.MediaSession;
import android.media.session.PlaybackState;
import android.os.Bundle;
import android.os.Handler;
import android.os.ResultReceiver;
import android.support.annotation.NonNull;
import android.support.v17.leanback.app.PlaybackOverlayFragment;
import android.support.v17.leanback.widget.ArrayObjectAdapter;
import android.support.v17.leanback.widget.ClassPresenterSelector;
import android.support.v17.leanback.widget.HeaderItem;
import android.support.v17.leanback.widget.ImageCardView;
import android.support.v17.leanback.widget.ListRow;
import android.support.v17.leanback.widget.ListRowPresenter;
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.support.v17.leanback.widget.SparseArrayObjectAdapter;
import android.support.v17.leanback.widget.VerticalGridView;
import android.view.InputEvent;
import android.view.KeyEvent;
import android.view.MotionEvent;

import org.apache.commons.lang3.StringUtils;
import org.opensilk.common.core.dagger2.DaggerService;
import org.opensilk.common.core.util.BundleHelper;
import org.opensilk.video.data.VideoDescInfo;
import org.opensilk.video.playback.PlaybackService;
import org.opensilk.video.tv.ui.details.DetailsActivity;

import java.util.List;

import javax.inject.Inject;
import javax.inject.Named;

import rx.Observable;
import rx.Subscription;
import rx.subjects.Subject;
import rx.subscriptions.CompositeSubscription;
import timber.log.Timber;

/**
 * Created by drew on 3/20/16.
 */
public class PlaybackControlsFragment extends PlaybackOverlayFragment {

    final CompositeSubscription mSubscriptions = new CompositeSubscription();

    @Inject
    @Named("itemOverview")
    Observable<VideoDescInfo> mOverviewObservable;
    @Inject
    @Named("itemChangeSubject")
    Subject<MediaDescription, MediaDescription> mOverviewSubject;
    @Inject
    @Named("itemChange")
    Observable<MediaBrowser.MediaItem> mMediaItemObservable;

    final Handler mMainHandler = new Handler();

    PlaybackControlsComponent mComponent;
    MediaController mMediaController;
    SparseArrayObjectAdapter mAdapter;
    PlaybackControlsGlue mControlsRowGlue;
    ListRow mQueueRow;
    PlaybackOverviewRow mOverviewRow;
    ListRow mSubtitleRow;

    long mLastActiveItemId = MediaSession.QueueItem.UNKNOWN_ID;

    final static int CONTROLS_ROW = 1;
    final static int OVERVIEW_ROW = 2;
    final static int SUBTITLE_ROW = 3;
    final static int QUEUE_ROW = 4;

    final MediaControllerCallback mMediaControllerCallback = new MediaControllerCallback();
    final VerticalGridView.OnTouchInterceptListener mOnTouchInterceptListener = new VerticalGridView.OnTouchInterceptListener() {
        public boolean onInterceptTouchEvent(MotionEvent event) {
            return onInterceptInputEvent(event);
        }
    };

    final VerticalGridView.OnKeyInterceptListener mOnKeyInterceptListener = new VerticalGridView.OnKeyInterceptListener() {
        public boolean onInterceptKeyEvent(KeyEvent event) {
            return onInterceptInputEvent(event);
        }
    };

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        PlaybackActivityComponent activityComponent = DaggerService.getDaggerComponent(getActivity());
        mComponent = activityComponent
                .newPlaybackControlsComponent(new PlaybackControlsModule(getActivity(), this));
        mComponent.inject(this);

        setBackgroundType(BG_NONE);

        //init mediaController
        mMediaController = getActivity().getMediaController();
        mMediaController.registerCallback(mMediaControllerCallback);

        //init primary adapter
        ClassPresenterSelector presenterSelector = new ClassPresenterSelector();
        mAdapter = new SparseArrayObjectAdapter(presenterSelector);

        //init controls row
        mControlsRowGlue = new PlaybackControlsGlue(getContext(), this, mMediaController);

        MediaMetadata mediaMetadata = mMediaController.getMetadata();
        if (mediaMetadata != null) {
            mControlsRowGlue.onMetadataChanged(mediaMetadata);
        }

        PlaybackState playbackState = mMediaController.getPlaybackState();
        if (playbackState != null) {
            mControlsRowGlue.onStateChanged(playbackState);
        }

        PlaybackControlsRowPresenter controlsRowPresenter = mControlsRowGlue.createControlsRowAndPresenter();
        PlaybackControlsRow controlsRow = mControlsRowGlue.getControlsRow();
        //        SparseArrayObjectAdapter secondaryActionsAdapter =
        //                new SparseArrayObjectAdapter(new ControlButtonPresenterSelector());
        //        secondaryActionsAdapter.set(5, new PlaybackControlsRow.ClosedCaptioningAction(getContext()));
        //        controlsRow.setSecondaryActionsAdapter(secondaryActionsAdapter);
        presenterSelector.addClassPresenter(PlaybackControlsRow.class, controlsRowPresenter);
        mAdapter.set(CONTROLS_ROW, controlsRow);

        //init overview row
        mOverviewRow = new PlaybackOverviewRow(new HeaderItem("Overview"));
        PlaybackOverviewRowPresenter overviewRowPresenter = new PlaybackOverviewRowPresenter();
        if (playbackState != null) {
            updateOverview(playbackState.getActiveQueueItemId());
        }
        presenterSelector.addClassPresenter(PlaybackOverviewRow.class, overviewRowPresenter);
        mAdapter.set(OVERVIEW_ROW, mOverviewRow);
        Subscription overviewSub = mOverviewObservable.subscribe(info -> {
            mOverviewRow.setItem(info);
        }, e -> {
            Timber.w("videoDescription");
        });
        mSubscriptions.add(overviewSub);

        //init queue row
        QueueAdapter queueRowAdapter = new QueueAdapter();
        List<MediaSession.QueueItem> queue = mMediaController.getQueue();
        if (queue != null) {
            queueRowAdapter.swap(queue);
        }
        CharSequence queueTitle = mMediaController.getQueueTitle();
        if (StringUtils.isEmpty(queueTitle)) {
            queueTitle = "Queue";
        }
        mQueueRow = new ListRow(new HeaderItem(queueTitle.toString()), queueRowAdapter);
        presenterSelector.addClassPresenter(ListRow.class, new ListRowPresenter());
        mAdapter.set(QUEUE_ROW, mQueueRow);

        //init subtitle row
        SubtitleAdapter subtitleAdapter = new SubtitleAdapter();
        mSubtitleRow = new ListRow(new HeaderItem("Subtitles"), subtitleAdapter);
        //not adding yet

        //set adapter
        setAdapter(mAdapter);
        setOnItemViewClickedListener(new ItemClickListener());

        //on new items update the activity result
        Subscription resultSub = mMediaItemObservable.subscribe(item -> {
            Activity activity = getActivity();
            if (activity != null) {
                Intent result = new Intent().putExtra(DetailsActivity.MEDIA_ITEM, item);
                activity.setResult(Activity.RESULT_FIRST_USER + 12, result);
            }
        }, e -> {
            Timber.w(e, "setResult()");
        });
        mSubscriptions.add(resultSub);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        mSubscriptions.clear();
        mMediaController.unregisterCallback(mMediaControllerCallback);
        mMediaController = null;
        mAdapter = null;
        mControlsRowGlue.enableProgressUpdating(false);
        mControlsRowGlue = null;
        mOverviewObservable = null;
        mOverviewSubject = null;
        mComponent = null;
    }

    @Override
    public void onResume() {
        super.onResume();
        //replace supers listeners with ours
        getVerticalGridView().setOnKeyInterceptListener(mOnKeyInterceptListener);
        getVerticalGridView().setOnTouchInterceptListener(mOnTouchInterceptListener);
    }

    @Override
    protected boolean onInterceptInputEvent(InputEvent event) {
        boolean hidden = areControlsHidden();
        Timber.d("onInterceptInputEvent(%s) hidden = %s", event, hidden);
        boolean consumeEvent = false;
        boolean handled = false;
        int keyCode = KeyEvent.KEYCODE_UNKNOWN;
        if (event instanceof KeyEvent) {
            keyCode = ((KeyEvent) event).getKeyCode();
        }
        switch (keyCode) {
        //            case KeyEvent.KEYCODE_DPAD_CENTER:
        //            case KeyEvent.KEYCODE_DPAD_DOWN:
        //            case KeyEvent.KEYCODE_DPAD_UP:
        case KeyEvent.KEYCODE_DPAD_LEFT:
        case KeyEvent.KEYCODE_DPAD_RIGHT:
            if (hidden) {
                if (((KeyEvent) event).getAction() == KeyEvent.ACTION_DOWN) {
                    int delta = keyCode == KeyEvent.KEYCODE_DPAD_LEFT ? -10000 : 15000;
                    consumeEvent = seekDelta(delta);
                }
                handled = true;
            }
            break;
        }
        return handled ? consumeEvent : super.onInterceptInputEvent(event);
    }

    private boolean seekDelta(int delta) {
        if (mMediaController != null) {
            mMediaController.getTransportControls().sendCustomAction(PlaybackService.ACTION.SEEK_DELTA,
                    BundleHelper.b().putInt(delta).get());
            return true;
        }
        return false;
    }

    public void updateRow(int row) {
        if (mAdapter != null) {
            mAdapter.notifyArrayItemRangeChanged(mAdapter.indexOf(row), 1);
        }
    }

    void updateOverview(long itemId) {
        if (mMediaController == null || itemId == mLastActiveItemId) {
            return;
        }
        List<MediaSession.QueueItem> q = mMediaController.getQueue();
        if (q == null) {
            return;
        }
        for (MediaSession.QueueItem i : q) {
            if (i.getQueueId() == itemId) {
                MediaDescription d = i.getDescription();
                mOverviewSubject.onNext(d);
                break;
            }
        }
    }

    void updateSubtitles() {
        if (mMediaController == null || mAdapter == null || mSubtitleRow == null) {
            return;
        }
        mAdapter.clear(SUBTITLE_ROW);
        final SubtitleAdapter subtitleAdapter = (SubtitleAdapter) mSubtitleRow.getAdapter();
        subtitleAdapter.clear();
        mMediaController.sendCommand(PlaybackService.CMD.GET_SPU_TRACKS, null, new ResultReceiver(mMainHandler) {
            @Override
            protected void onReceiveResult(int resultCode, Bundle resultData) {
                switch (resultCode) {
                case 0:
                    break;
                case 1: {
                    if (mAdapter.indexOf(SUBTITLE_ROW) < 0) {
                        mAdapter.set(SUBTITLE_ROW, mSubtitleRow);
                    }
                    subtitleAdapter.add(resultData);
                    break;
                }
                }
            }
        });
    }

    class MediaControllerCallback extends MediaController.Callback {

        @Override
        public void onPlaybackStateChanged(@NonNull PlaybackState state) {
            Timber.d("onPlaybackStateChanged(%s)", state);
            int nextState = state.getState();
            if (nextState == PlaybackState.STATE_STOPPED) {
                getActivity().finish();
                return;
            }
            if (mControlsRowGlue != null) {
                mControlsRowGlue.onStateChanged(state);
            }
            if (mOverviewRow != null) {
                updateOverview(state.getActiveQueueItemId());
            }
            if (nextState == PlaybackState.STATE_PLAYING) {
                updateSubtitles();
            }
        }

        @Override
        public void onMetadataChanged(MediaMetadata metadata) {
            Timber.d("onMetadataChanged(%s)", metadata);
            if (mControlsRowGlue != null) {
                mControlsRowGlue.onMetadataChanged(metadata);
            }
        }

        @Override
        public void onQueueChanged(List<MediaSession.QueueItem> queue) {
            Timber.d("onQueueChanged(%s)", queue);
            if (mQueueRow != null) {
                ArrayObjectAdapter adapter = (ArrayObjectAdapter) mQueueRow.getAdapter();
                if (queue == null || queue.isEmpty()) {
                    adapter.clear();
                } else {
                    adapter.swap(queue);
                }
            }
        }

        @Override
        public void onQueueTitleChanged(CharSequence title) {
            Timber.d("onQueueTitleChanged(%s)", title);
            if (mQueueRow != null) {
                mQueueRow.setHeaderItem(new HeaderItem(title.toString()));
                updateRow(QUEUE_ROW);
            }
        }
    }

    class ItemClickListener implements OnItemViewClickedListener {
        @Override
        public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item,
                RowPresenter.ViewHolder rowViewHolder, Row row) {
            if ((item instanceof MediaSession.QueueItem)) {
                MediaSession.QueueItem queueItem = (MediaSession.QueueItem) item;
                if (mMediaController != null) {
                    mMediaController.getTransportControls().skipToQueueItem(queueItem.getQueueId());
                }
            } else if (item instanceof Bundle) {
                Bundle bundle = (Bundle) item;
                if ("spu_track".equals(BundleHelper.getTag(bundle))) {
                    if (mMediaController != null) {
                        mMediaController.getTransportControls()
                                .sendCustomAction(PlaybackService.ACTION.SET_SPU_TRACK, bundle);
                    }
                }
            }
        }
    }
}