13bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu/*
23bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu * Copyright (C) 2017 The Android Open Source Project
33bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu *
43bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu * Licensed under the Apache License, Version 2.0 (the "License");
53bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu * you may not use this file except in compliance with the License.
63bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu * You may obtain a copy of the License at
73bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu *
83bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu *      http://www.apache.org/licenses/LICENSE-2.0
93bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu *
103bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu * Unless required by applicable law or agreed to in writing, software
113bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu * distributed under the License is distributed on an "AS IS" BASIS,
123bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu * See the License for the specific language governing permissions and
143bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu * limitations under the License.
153bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu */
163bcad88cbf4488e747d84893c35f2351b8f84afeDake Gupackage android.support.v17.leanback.widget;
173bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
183bcad88cbf4488e747d84893c35f2351b8f84afeDake Guimport android.content.Context;
193bcad88cbf4488e747d84893c35f2351b8f84afeDake Guimport android.graphics.Bitmap;
203bcad88cbf4488e747d84893c35f2351b8f84afeDake Guimport android.graphics.Color;
213bcad88cbf4488e747d84893c35f2351b8f84afeDake Guimport android.os.Build;
223bcad88cbf4488e747d84893c35f2351b8f84afeDake Guimport android.support.annotation.ColorInt;
233bcad88cbf4488e747d84893c35f2351b8f84afeDake Guimport android.support.v17.leanback.R;
243bcad88cbf4488e747d84893c35f2351b8f84afeDake Guimport android.support.v17.leanback.widget.ControlBarPresenter.OnControlClickedListener;
253bcad88cbf4488e747d84893c35f2351b8f84afeDake Guimport android.support.v17.leanback.widget.ControlBarPresenter.OnControlSelectedListener;
263bcad88cbf4488e747d84893c35f2351b8f84afeDake Guimport android.util.TypedValue;
273bcad88cbf4488e747d84893c35f2351b8f84afeDake Guimport android.view.KeyEvent;
283bcad88cbf4488e747d84893c35f2351b8f84afeDake Guimport android.view.LayoutInflater;
293bcad88cbf4488e747d84893c35f2351b8f84afeDake Guimport android.view.View;
303bcad88cbf4488e747d84893c35f2351b8f84afeDake Guimport android.view.ViewGroup;
313bcad88cbf4488e747d84893c35f2351b8f84afeDake Guimport android.widget.ImageView;
323bcad88cbf4488e747d84893c35f2351b8f84afeDake Guimport android.widget.TextView;
333bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
343bcad88cbf4488e747d84893c35f2351b8f84afeDake Guimport java.util.Arrays;
353bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
363bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu/**
373bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu * A PlaybackTransportRowPresenter renders a {@link PlaybackControlsRow} to display a
383bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu * series of playback control buttons. Typically this row will be the first row in a fragment
393bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu * such as the {@link android.support.v17.leanback.app.PlaybackSupportFragment}.
403bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu *
413bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu * <p>The detailed description is rendered using a {@link Presenter} passed in
423bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu * {@link #setDescriptionPresenter(Presenter)}.  This can be an instance of
433bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu * {@link AbstractDetailsDescriptionPresenter}.  The application can access the
443bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu * detailed description ViewHolder from {@link ViewHolder#getDescriptionViewHolder()}.
453bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu * </p>
463bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu */
473bcad88cbf4488e747d84893c35f2351b8f84afeDake Gupublic class PlaybackTransportRowPresenter extends PlaybackRowPresenter {
483bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
493bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    static class BoundData extends PlaybackControlsPresenter.BoundData {
503bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        ViewHolder mRowViewHolder;
513bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    }
523bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
533bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    /**
543bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu     * A ViewHolder for the PlaybackControlsRow supporting seek UI.
553bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu     */
563bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    public class ViewHolder extends PlaybackRowPresenter.ViewHolder implements PlaybackSeekUi {
573bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        final Presenter.ViewHolder mDescriptionViewHolder;
583bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        final ImageView mImageView;
593bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        final ViewGroup mDescriptionDock;
603bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        final ViewGroup mControlsDock;
613bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        final ViewGroup mSecondaryControlsDock;
623bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        final TextView mTotalTime;
633bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        final TextView mCurrentTime;
643bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        final SeekBar mProgressBar;
653bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        final ThumbsBar mThumbsBar;
663bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        long mTotalTimeInMs = Long.MIN_VALUE;
673bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        long mCurrentTimeInMs = Long.MIN_VALUE;
683bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        long mSecondaryProgressInMs;
693bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        final StringBuilder mTempBuilder = new StringBuilder();
703bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        ControlBarPresenter.ViewHolder mControlsVh;
713bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        ControlBarPresenter.ViewHolder mSecondaryControlsVh;
723bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        BoundData mControlsBoundData = new BoundData();
733bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        BoundData mSecondaryBoundData = new BoundData();
743bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        Presenter.ViewHolder mSelectedViewHolder;
753bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        Object mSelectedItem;
763bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        PlaybackControlsRow.PlayPauseAction mPlayPauseAction;
773bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        int mThumbHeroIndex = -1;
783bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
793bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        Client mSeekClient;
803bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        boolean mInSeek;
813bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        PlaybackSeekDataProvider mSeekDataProvider;
823bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        long[] mPositions;
833bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        int mPositionsLength;
843bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
853bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        final PlaybackControlsRow.OnPlaybackProgressCallback mListener =
863bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                new PlaybackControlsRow.OnPlaybackProgressCallback() {
873bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            @Override
883bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            public void onCurrentPositionChanged(PlaybackControlsRow row, long ms) {
893bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                setCurrentPosition(ms);
903bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            }
913bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
923bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            @Override
933bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            public void onDurationChanged(PlaybackControlsRow row, long ms) {
943bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                setTotalTime(ms);
953bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            }
963bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
973bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            @Override
983bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            public void onBufferedPositionChanged(PlaybackControlsRow row, long ms) {
993bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                setBufferedPosition(ms);
1003bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            }
1013bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        };
1023bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
1033bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        void updateProgressInSeek(boolean forward) {
1043bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            long newPos;
1053bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            long pos = mCurrentTimeInMs;
1063bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            if (mPositionsLength > 0) {
1073bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                int index = Arrays.binarySearch(mPositions, 0, mPositionsLength, pos);
1083bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                int thumbHeroIndex;
1093bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                if (forward) {
1103bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                    if (index >= 0) {
1113bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                        // found it, seek to neighbour key position at higher side
1123bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                        if (index < mPositionsLength - 1) {
1133bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                            newPos = mPositions[index + 1];
1143bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                            thumbHeroIndex = index + 1;
1153bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                        } else {
1163bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                            newPos = mTotalTimeInMs;
1173bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                            thumbHeroIndex = index;
1183bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                        }
1193bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                    } else {
1203bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                        // not found, seek to neighbour key position at higher side.
1213bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                        int insertIndex = -1 - index;
1223bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                        if (insertIndex <= mPositionsLength - 1) {
1233bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                            newPos = mPositions[insertIndex];
1243bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                            thumbHeroIndex = insertIndex;
1253bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                        } else {
1263bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                            newPos = mTotalTimeInMs;
1273bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                            thumbHeroIndex = insertIndex > 0 ? insertIndex - 1 : 0;
1283bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                        }
1293bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                    }
1303bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                } else {
1313bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                    if (index >= 0) {
1323bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                        // found it, seek to neighbour key position at lower side.
1333bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                        if (index > 0) {
1343bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                            newPos = mPositions[index - 1];
1353bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                            thumbHeroIndex = index - 1;
1363bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                        } else {
1373bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                            newPos = 0;
1383bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                            thumbHeroIndex = 0;
1393bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                        }
1403bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                    } else {
1413bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                        // not found, seek to neighbour key position at lower side.
1423bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                        int insertIndex = -1 - index;
1433bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                        if (insertIndex > 0) {
1443bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                            newPos = mPositions[insertIndex - 1];
1453bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                            thumbHeroIndex = insertIndex - 1;
1463bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                        } else {
1473bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                            newPos = 0;
1483bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                            thumbHeroIndex = 0;
1493bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                        }
1503bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                    }
1513bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                }
1523bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                updateThumbsInSeek(thumbHeroIndex, forward);
1533bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            } else {
1543bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                long interval = (long) (mTotalTimeInMs * getDefaultSeekIncrement());
1553bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                newPos = pos + (forward ? interval : -interval);
1563bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                if (newPos > mTotalTimeInMs) {
1573bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                    newPos = mTotalTimeInMs;
1583bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                } else if (newPos < 0) {
1593bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                    newPos = 0;
1603bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                }
1613bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            }
1623bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            double ratio = (double) newPos / mTotalTimeInMs;     // Range: [0, 1]
1633bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            mProgressBar.setProgress((int) (ratio * Integer.MAX_VALUE)); // Could safely cast to int
1643bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            mSeekClient.onSeekPositionChanged(newPos);
1653bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        }
1663bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
1673bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        void updateThumbsInSeek(int thumbHeroIndex, boolean forward) {
1683bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            if (mThumbHeroIndex == thumbHeroIndex) {
1693bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                return;
1703bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            }
1713bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
1723bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            final int totalNum = mThumbsBar.getChildCount();
1733bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            if (totalNum < 0 || (totalNum & 1) == 0) {
1743bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                throw new RuntimeException();
1753bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            }
1763bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            final int heroChildIndex = totalNum / 2;
1773bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            final int start = Math.max(thumbHeroIndex - (totalNum / 2), 0);
1783bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            final int end = Math.min(thumbHeroIndex + (totalNum / 2), mPositionsLength - 1);
1793bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            final int newRequestStart;
1803bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            final int newRequestEnd;
1813bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
1823bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            if (mThumbHeroIndex < 0) {
1833bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                // first time
1843bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                newRequestStart = start;
1853bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                newRequestEnd = end;
1863bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            } else {
1873bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                forward = thumbHeroIndex > mThumbHeroIndex;
1883bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                final int oldStart = Math.max(mThumbHeroIndex - (totalNum / 2), 0);
1893bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                final int oldEnd = Math.min(mThumbHeroIndex + (totalNum / 2),
1903bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                        mPositionsLength - 1);
1913bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                if (forward) {
1923bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                    newRequestStart = Math.max(oldEnd + 1, start);
1933bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                    newRequestEnd = end;
1943bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                    // overlapping area directly assign bitmap from previous result
1953bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                    for (int i = start; i <= newRequestStart - 1; i++) {
1963bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                        mThumbsBar.setThumbBitmap(heroChildIndex + (i - thumbHeroIndex),
1973bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                                mThumbsBar.getThumbBitmap(heroChildIndex + (i - mThumbHeroIndex)));
1983bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                    }
1993bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                } else {
2003bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                    newRequestEnd = Math.min(oldStart - 1, end);
2013bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                    newRequestStart = start;
2023bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                    // overlapping area directly assign bitmap from previous result in backward
2033bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                    for (int i = end; i >= newRequestEnd + 1; i--) {
2043bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                        mThumbsBar.setThumbBitmap(heroChildIndex + (i - thumbHeroIndex),
2053bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                                mThumbsBar.getThumbBitmap(heroChildIndex + (i - mThumbHeroIndex)));
2063bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                    }
2073bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                }
2083bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            }
2093bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            // processing new requests with mThumbHeroIndex updated
2103bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            mThumbHeroIndex = thumbHeroIndex;
2113bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            if (forward) {
2123bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                for (int i = newRequestStart; i <= newRequestEnd; i++) {
2133bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                    mSeekDataProvider.getThumbnail(i, mThumbResult);
2143bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                }
2153bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            } else {
2163bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                for (int i = newRequestEnd; i >= newRequestStart; i--) {
2173bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                    mSeekDataProvider.getThumbnail(i, mThumbResult);
2183bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                }
2193bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            }
2203bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            // set thumb bitmaps outside (start , end) to null
2213bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            for (int childIndex = 0; childIndex < heroChildIndex - mThumbHeroIndex + start;
2223bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                    childIndex++) {
2233bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                mThumbsBar.setThumbBitmap(childIndex, null);
2243bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            }
2253bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            for (int childIndex = heroChildIndex + end - mThumbHeroIndex + 1;
2263bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                    childIndex < totalNum; childIndex++) {
2273bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                mThumbsBar.setThumbBitmap(childIndex, null);
2283bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            }
2293bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        }
2303bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
2313bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        PlaybackSeekDataProvider.ResultCallback mThumbResult =
2323bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                new PlaybackSeekDataProvider.ResultCallback() {
2333bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                    @Override
2343bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                    public void onThumbnailLoaded(Bitmap bitmap, int index) {
2353bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                        int childIndex = index - (mThumbHeroIndex - mThumbsBar.getChildCount() / 2);
2363bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                        if (childIndex < 0 || childIndex >= mThumbsBar.getChildCount()) {
2373bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                            return;
2383bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                        }
2393bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                        mThumbsBar.setThumbBitmap(childIndex, bitmap);
2403bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                    }
2413bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        };
2423bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
2433bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        boolean onForward() {
2443bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            if (!startSeek()) {
2453bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                return false;
2463bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            }
2473bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            updateProgressInSeek(true);
2483bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            return true;
2493bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        }
2503bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
2513bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        boolean onBackward() {
2523bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            if (!startSeek()) {
2533bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                return false;
2543bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            }
2553bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            updateProgressInSeek(false);
2563bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            return true;
2573bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        }
2583bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        /**
2593bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu         * Constructor of ViewHolder of PlaybackTransportRowPresenter
2603bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu         * @param rootView Root view of the ViewHolder.
2613bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu         * @param descriptionPresenter The presenter that will be used to create description
2623bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu         *                             ViewHolder. The description view will be added into tree.
2633bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu         */
2643bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        public ViewHolder(View rootView, Presenter descriptionPresenter) {
2653bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            super(rootView);
2663bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            mImageView = (ImageView) rootView.findViewById(R.id.image);
2673bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            mDescriptionDock = (ViewGroup) rootView.findViewById(R.id.description_dock);
2683bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            mCurrentTime = (TextView) rootView.findViewById(R.id.current_time);
2693bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            mTotalTime = (TextView) rootView.findViewById(R.id.total_time);
2703bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            mProgressBar = (SeekBar) rootView.findViewById(R.id.playback_progress);
2713bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            mProgressBar.setOnClickListener(new View.OnClickListener() {
2723bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                @Override
2733bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                public void onClick(View view) {
2743bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                    onProgressBarClicked(ViewHolder.this);
2753bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                }
2763bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            });
2773bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            mProgressBar.setOnKeyListener(new View.OnKeyListener() {
2783bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
2793bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                @Override
2803bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                public boolean onKey(View view, int keyCode, KeyEvent keyEvent) {
2813bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                    // when in seek only allow this keys
2823bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                    switch (keyCode) {
2833bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                        case KeyEvent.KEYCODE_DPAD_UP:
2843bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                        case KeyEvent.KEYCODE_DPAD_DOWN:
2853bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                            // eat DPAD UP/DOWN in seek mode
2863bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                            return mInSeek;
2873bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                        case KeyEvent.KEYCODE_DPAD_LEFT:
2883bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                        case KeyEvent.KEYCODE_MINUS:
2893bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                        case KeyEvent.KEYCODE_MEDIA_REWIND:
2903bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                            if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
2913bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                                onBackward();
2923bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                            }
2933bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                            return true;
2943bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                        case KeyEvent.KEYCODE_DPAD_RIGHT:
2953bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                        case KeyEvent.KEYCODE_PLUS:
2963bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                        case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
2973bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                            if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
2983bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                                onForward();
2993bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                            }
3003bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                            return true;
3013bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                        case KeyEvent.KEYCODE_DPAD_CENTER:
3023bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                        case KeyEvent.KEYCODE_ENTER:
3033bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                            if (!mInSeek) {
3043bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                                return false;
3053bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                            }
3063bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                            if (keyEvent.getAction() == KeyEvent.ACTION_UP) {
3073bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                                stopSeek(false);
3083bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                            }
3093bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                            return true;
3103bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                        case KeyEvent.KEYCODE_BACK:
3113bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                        case KeyEvent.KEYCODE_ESCAPE:
3123bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                            if (!mInSeek) {
3133bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                                return false;
3143bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                            }
3153bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                            if (keyEvent.getAction() == KeyEvent.ACTION_UP) {
3163bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                                // SeekBar does not support cancel in accessibility mode, so always
3173bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                                // "confirm" if accessibility is on.
3183bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                                stopSeek(Build.VERSION.SDK_INT >= 21
3193bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                                        ? !mProgressBar.isAccessibilityFocused() : true);
3203bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                            }
3213bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                            return true;
3223bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                    }
3233bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                    return false;
3243bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                }
3253bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            });
3263bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            mProgressBar.setAccessibilitySeekListener(new SeekBar.AccessibilitySeekListener() {
3273bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                @Override
3283bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                public boolean onAccessibilitySeekForward() {
3293bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                    return onForward();
3303bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                }
3313bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
3323bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                @Override
3333bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                public boolean onAccessibilitySeekBackward() {
3343bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                    return onBackward();
3353bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                }
3363bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            });
3373bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            mProgressBar.setMax(Integer.MAX_VALUE); //current progress will be a fraction of this
3383bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            mControlsDock = (ViewGroup) rootView.findViewById(R.id.controls_dock);
3393bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            mSecondaryControlsDock =
3403bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                    (ViewGroup) rootView.findViewById(R.id.secondary_controls_dock);
3413bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            mDescriptionViewHolder = descriptionPresenter == null ? null :
3423bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                    descriptionPresenter.onCreateViewHolder(mDescriptionDock);
3433bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            if (mDescriptionViewHolder != null) {
3443bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                mDescriptionDock.addView(mDescriptionViewHolder.view);
3453bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            }
3463bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            mThumbsBar = (ThumbsBar) rootView.findViewById(R.id.thumbs_row);
3473bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        }
3483bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
3493bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        /**
3503bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu         * @return The ViewHolder for description.
3513bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu         */
3523bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        public final Presenter.ViewHolder getDescriptionViewHolder() {
3533bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            return mDescriptionViewHolder;
3543bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        }
3553bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
3563bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        @Override
3573bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        public void setPlaybackSeekUiClient(Client client) {
3583bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            mSeekClient = client;
3593bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        }
3603bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
3613bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        boolean startSeek() {
3623bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            if (mInSeek) {
3633bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                return true;
3643bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            }
3653bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            if (mSeekClient == null || !mSeekClient.isSeekEnabled()
3663bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                    || mTotalTimeInMs <= 0) {
3673bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                return false;
3683bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            }
3693bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            mInSeek = true;
3703bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            mSeekClient.onSeekStarted();
3713bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            mSeekDataProvider = mSeekClient.getPlaybackSeekDataProvider();
3723bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            mPositions = mSeekDataProvider != null ? mSeekDataProvider.getSeekPositions() : null;
3733bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            if (mPositions != null) {
3743bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                int pos = Arrays.binarySearch(mPositions, mTotalTimeInMs);
3753bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                if (pos >= 0) {
3763bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                    mPositionsLength = pos + 1;
3773bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                } else {
3783bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                    mPositionsLength = -1 - pos;
3793bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                }
3803bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            } else {
3813bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                mPositionsLength = 0;
3823bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            }
3833bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            mControlsVh.view.setVisibility(View.INVISIBLE);
3843bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            mSecondaryControlsVh.view.setVisibility(View.INVISIBLE);
3853bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            mDescriptionViewHolder.view.setVisibility(View.INVISIBLE);
3863bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            mThumbsBar.setVisibility(View.VISIBLE);
3873bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            return true;
3883bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        }
3893bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
3903bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        void stopSeek(boolean cancelled) {
3913bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            if (!mInSeek) {
3923bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                return;
3933bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            }
3943bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            mInSeek = false;
3953bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            mSeekClient.onSeekFinished(cancelled);
3963bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            if (mSeekDataProvider != null) {
3973bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                mSeekDataProvider.reset();
3983bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            }
3993bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            mThumbHeroIndex = -1;
4003bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            mThumbsBar.clearThumbBitmaps();
4013bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            mSeekDataProvider = null;
4023bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            mPositions = null;
4033bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            mPositionsLength = 0;
4043bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            mControlsVh.view.setVisibility(View.VISIBLE);
4053bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            mSecondaryControlsVh.view.setVisibility(View.VISIBLE);
4063bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            mDescriptionViewHolder.view.setVisibility(View.VISIBLE);
4073bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            mThumbsBar.setVisibility(View.INVISIBLE);
4083bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        }
4093bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
4103bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        void dispatchItemSelection() {
4113bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            if (!isSelected()) {
4123bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                return;
4133bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            }
4143bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            if (mSelectedViewHolder == null) {
4153bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                if (getOnItemViewSelectedListener() != null) {
4163bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                    getOnItemViewSelectedListener().onItemSelected(null, null,
4173bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                            ViewHolder.this, getRow());
4183bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                }
4193bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            } else {
4203bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                if (getOnItemViewSelectedListener() != null) {
4213bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                    getOnItemViewSelectedListener().onItemSelected(mSelectedViewHolder,
4223bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                            mSelectedItem, ViewHolder.this, getRow());
4233bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                }
4243bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            }
4253bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        };
4263bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
4273bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        Presenter getPresenter(boolean primary) {
4283bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            ObjectAdapter adapter = primary
4293bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                    ? ((PlaybackControlsRow) getRow()).getPrimaryActionsAdapter()
4303bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                    : ((PlaybackControlsRow) getRow()).getSecondaryActionsAdapter();
4313bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            if (adapter == null) {
4323bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                return null;
4333bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            }
4343bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            if (adapter.getPresenterSelector() instanceof ControlButtonPresenterSelector) {
4353bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                ControlButtonPresenterSelector selector =
4363bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                        (ControlButtonPresenterSelector) adapter.getPresenterSelector();
4373bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                return selector.getSecondaryPresenter();
4383bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            }
4393bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            return adapter.getPresenter(adapter.size() > 0 ? adapter.get(0) : null);
4403bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        }
4413bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
4423bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        /**
4433bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu         * Returns the TextView that showing total time label. This method might be used in
4443bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu         * {@link #onSetDurationLabel}.
4453bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu         * @return The TextView that showing total time label.
4463bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu         */
4473bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        public final TextView getDurationView() {
4483bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            return mTotalTime;
4493bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        }
4503bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
4513bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        /**
4523bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu         * Called to update total time label. Default implementation updates the TextView
4533bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu         * {@link #getDurationView()}. Subclass might override.
4543bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu         * @param totalTimeMs Total duration of the media in milliseconds.
4553bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu         */
4563bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        protected void onSetDurationLabel(long totalTimeMs) {
4573bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            if (mTotalTime != null) {
4583bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                formatTime(totalTimeMs, mTempBuilder);
4593bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                mTotalTime.setText(mTempBuilder.toString());
4603bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            }
4613bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        }
4623bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
4633bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        void setTotalTime(long totalTimeMs) {
4643bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            if (mTotalTimeInMs != totalTimeMs) {
4653bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                mTotalTimeInMs = totalTimeMs;
4663bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                onSetDurationLabel(totalTimeMs);
4673bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            }
4683bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        }
4693bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
4703bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        /**
4713bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu         * Returns the TextView that showing current position label. This method might be used in
4723bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu         * {@link #onSetCurrentPositionLabel}.
4733bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu         * @return The TextView that showing current position label.
4743bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu         */
4753bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        public final TextView getCurrentPositionView() {
4763bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            return mCurrentTime;
4773bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        }
4783bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
4793bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        /**
4803bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu         * Called to update current time label. Default implementation updates the TextView
4813bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu         * {@link #getCurrentPositionView}. Subclass might override.
4823bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu         * @param currentTimeMs Current playback position in milliseconds.
4833bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu         */
4843bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        protected void onSetCurrentPositionLabel(long currentTimeMs) {
4853bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            if (mCurrentTime != null) {
4863bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                formatTime(currentTimeMs, mTempBuilder);
4873bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                mCurrentTime.setText(mTempBuilder.toString());
4883bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            }
4893bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        }
4903bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
4913bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        void setCurrentPosition(long currentTimeMs) {
4923bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            if (currentTimeMs != mCurrentTimeInMs) {
4933bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                mCurrentTimeInMs = currentTimeMs;
4943bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                onSetCurrentPositionLabel(currentTimeMs);
4953bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            }
4963bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            if (!mInSeek) {
4973bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                int progressRatio = 0;
4983bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                if (mTotalTimeInMs > 0) {
4993bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                    // Use ratio to represent current progres
5003bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                    double ratio = (double) mCurrentTimeInMs / mTotalTimeInMs;     // Range: [0, 1]
5013bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                    progressRatio = (int) (ratio * Integer.MAX_VALUE);  // Could safely cast to int
5023bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                }
5033bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                mProgressBar.setProgress((int) progressRatio);
5043bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            }
5053bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        }
5063bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
5073bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        void setBufferedPosition(long progressMs) {
5083bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            mSecondaryProgressInMs = progressMs;
5093bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            // Solve the progress bar by using ratio
5103bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            double ratio = (double) progressMs / mTotalTimeInMs;           // Range: [0, 1]
5113bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            double progressRatio = ratio * Integer.MAX_VALUE;   // Could safely cast to int
5123bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            mProgressBar.setSecondaryProgress((int) progressRatio);
5133bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        }
5143bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    }
5153bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
5163bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    static void formatTime(long ms, StringBuilder sb) {
5173bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        sb.setLength(0);
5183bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        if (ms < 0) {
5193bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            sb.append("--");
5203bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            return;
5213bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        }
5223bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        long seconds = ms / 1000;
5233bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        long minutes = seconds / 60;
5243bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        long hours = minutes / 60;
5253bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        seconds -= minutes * 60;
5263bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        minutes -= hours * 60;
5273bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
5283bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        if (hours > 0) {
5293bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            sb.append(hours).append(':');
5303bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            if (minutes < 10) {
5313bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                sb.append('0');
5323bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            }
5333bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        }
5343bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        sb.append(minutes).append(':');
5353bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        if (seconds < 10) {
5363bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            sb.append('0');
5373bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        }
5383bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        sb.append(seconds);
5393bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    }
5403bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
5413bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    float mDefaultSeekIncrement = 0.01f;
5423bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    int mProgressColor = Color.TRANSPARENT;
5433bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    boolean mProgressColorSet;
5443bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    Presenter mDescriptionPresenter;
5453bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    ControlBarPresenter mPlaybackControlsPresenter;
5463bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    ControlBarPresenter mSecondaryControlsPresenter;
5473bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    OnActionClickedListener mOnActionClickedListener;
5483bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
5493bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    private final OnControlSelectedListener mOnControlSelectedListener =
5503bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            new OnControlSelectedListener() {
5513bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        @Override
5523bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        public void onControlSelected(Presenter.ViewHolder itemViewHolder, Object item,
5533bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                ControlBarPresenter.BoundData data) {
5543bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            ViewHolder vh = ((BoundData) data).mRowViewHolder;
5553bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            if (vh.mSelectedViewHolder != itemViewHolder || vh.mSelectedItem != item) {
5563bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                vh.mSelectedViewHolder = itemViewHolder;
5573bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                vh.mSelectedItem = item;
5583bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                vh.dispatchItemSelection();
5593bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            }
5603bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        }
5613bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    };
5623bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
5633bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    private final OnControlClickedListener mOnControlClickedListener =
5643bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            new OnControlClickedListener() {
5653bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        @Override
5663bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        public void onControlClicked(Presenter.ViewHolder itemViewHolder, Object item,
5673bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                ControlBarPresenter.BoundData data) {
5683bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            ViewHolder vh = ((BoundData) data).mRowViewHolder;
5693bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            if (vh.getOnItemViewClickedListener() != null) {
5703bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                vh.getOnItemViewClickedListener().onItemClicked(itemViewHolder, item,
5713bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                        vh, vh.getRow());
5723bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            }
5733bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            if (mOnActionClickedListener != null && item instanceof Action) {
5743bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                mOnActionClickedListener.onActionClicked((Action) item);
5753bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            }
5763bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        }
5773bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    };
5783bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
5793bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    public PlaybackTransportRowPresenter() {
5803bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        setHeaderPresenter(null);
5813bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        setSelectEffectEnabled(false);
5823bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
5833bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        mPlaybackControlsPresenter = new ControlBarPresenter(R.layout.lb_control_bar);
5843bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        mPlaybackControlsPresenter.setDefaultFocusToMiddle(false);
5853bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        mSecondaryControlsPresenter = new ControlBarPresenter(R.layout.lb_control_bar);
5863bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        mSecondaryControlsPresenter.setDefaultFocusToMiddle(false);
5873bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
5883bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        mPlaybackControlsPresenter.setOnControlSelectedListener(mOnControlSelectedListener);
5893bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        mSecondaryControlsPresenter.setOnControlSelectedListener(mOnControlSelectedListener);
5903bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        mPlaybackControlsPresenter.setOnControlClickedListener(mOnControlClickedListener);
5913bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        mSecondaryControlsPresenter.setOnControlClickedListener(mOnControlClickedListener);
5923bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    }
5933bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
5943bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    /**
5953bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu     * @param descriptionPresenter Presenter for displaying item details.
5963bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu     */
5973bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    public void setDescriptionPresenter(Presenter descriptionPresenter) {
5983bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        mDescriptionPresenter = descriptionPresenter;
5993bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    }
6003bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
6013bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    /**
6023bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu     * Sets the listener for {@link Action} click events.
6033bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu     */
6043bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    public void setOnActionClickedListener(OnActionClickedListener listener) {
6053bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        mOnActionClickedListener = listener;
6063bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    }
6073bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
6083bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    /**
6093bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu     * Returns the listener for {@link Action} click events.
6103bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu     */
6113bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    public OnActionClickedListener getOnActionClickedListener() {
6123bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        return mOnActionClickedListener;
6133bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    }
6143bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
6153bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    /**
6163bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu     * Sets the primary color for the progress bar.  If not set, a default from
6173bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu     * the theme will be used.
6183bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu     */
6193bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    public void setProgressColor(@ColorInt int color) {
6203bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        mProgressColor = color;
6213bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        mProgressColorSet = true;
6223bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    }
6233bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
6243bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    /**
6253bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu     * Returns the primary color for the progress bar.  If no color was set, transparent
6263bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu     * is returned.
6273bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu     */
6283bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    @ColorInt
6293bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    public int getProgressColor() {
6303bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        return mProgressColor;
6313bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    }
6323bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
6333bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    @Override
6343bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    public void onReappear(RowPresenter.ViewHolder rowViewHolder) {
6353bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        ViewHolder vh = (ViewHolder) rowViewHolder;
6363bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        if (vh.view.hasFocus()) {
6373bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            vh.mProgressBar.requestFocus();
6383bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        }
6393bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    }
6403bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
6413bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    private int getDefaultProgressColor(Context context) {
6423bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        TypedValue outValue = new TypedValue();
6433bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        if (context.getTheme()
6443bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                .resolveAttribute(R.attr.playbackProgressPrimaryColor, outValue, true)) {
6453bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            return context.getResources().getColor(outValue.resourceId);
6463bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        }
6473bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        return context.getResources().getColor(R.color.lb_playback_progress_color_no_theme);
6483bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    }
6493bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
6503bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    @Override
6513bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    protected RowPresenter.ViewHolder createRowViewHolder(ViewGroup parent) {
6523bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        View v = LayoutInflater.from(parent.getContext()).inflate(
6533bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                R.layout.lb_playback_transport_controls_row, parent, false);
6543bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        ViewHolder vh = new ViewHolder(v, mDescriptionPresenter);
6553bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        initRow(vh);
6563bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        return vh;
6573bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    }
6583bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
6593bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    private void initRow(final ViewHolder vh) {
6603bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        vh.mControlsVh = (ControlBarPresenter.ViewHolder) mPlaybackControlsPresenter
6613bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                .onCreateViewHolder(vh.mControlsDock);
6623bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        vh.mProgressBar.setProgressColor(mProgressColorSet ? mProgressColor
6633bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                : getDefaultProgressColor(vh.mControlsDock.getContext()));
6643bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        vh.mControlsDock.addView(vh.mControlsVh.view);
6653bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
6663bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        vh.mSecondaryControlsVh = (ControlBarPresenter.ViewHolder) mSecondaryControlsPresenter
6673bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                .onCreateViewHolder(vh.mSecondaryControlsDock);
6683bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        vh.mSecondaryControlsDock.addView(vh.mSecondaryControlsVh.view);
6693bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        ((PlaybackTransportRowView) vh.view).setOnUnhandledKeyListener(
6703bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                new PlaybackTransportRowView.OnUnhandledKeyListener() {
6713bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                @Override
6723bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                public boolean onUnhandledKey(KeyEvent event) {
6733bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                    if (vh.getOnKeyListener() != null) {
6743bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                        if (vh.getOnKeyListener().onKey(vh.view, event.getKeyCode(), event)) {
6753bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                            return true;
6763bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                        }
6773bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                    }
6783bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                    return false;
6793bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                }
6803bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            });
6813bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    }
6823bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
6833bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    @Override
6843bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    protected void onBindRowViewHolder(RowPresenter.ViewHolder holder, Object item) {
6853bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        super.onBindRowViewHolder(holder, item);
6863bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
6873bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        ViewHolder vh = (ViewHolder) holder;
6883bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        PlaybackControlsRow row = (PlaybackControlsRow) vh.getRow();
6893bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
6903bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        if (row.getItem() == null) {
6913bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            vh.mDescriptionDock.setVisibility(View.GONE);
6923bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        } else {
6933bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            vh.mDescriptionDock.setVisibility(View.VISIBLE);
6943bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            if (vh.mDescriptionViewHolder != null) {
6953bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                mDescriptionPresenter.onBindViewHolder(vh.mDescriptionViewHolder, row.getItem());
6963bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            }
6973bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        }
6983bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
6993bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        if (row.getImageDrawable() == null) {
7003bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            vh.mImageView.setVisibility(View.GONE);
7013bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        } else {
7023bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            vh.mImageView.setVisibility(View.VISIBLE);
7033bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        }
7043bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        vh.mImageView.setImageDrawable(row.getImageDrawable());
7053bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
7063bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        vh.mControlsBoundData.adapter = row.getPrimaryActionsAdapter();
7073bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        vh.mControlsBoundData.presenter = vh.getPresenter(true);
7083bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        vh.mControlsBoundData.mRowViewHolder = vh;
7093bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        mPlaybackControlsPresenter.onBindViewHolder(vh.mControlsVh, vh.mControlsBoundData);
7103bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
7113bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        vh.mSecondaryBoundData.adapter = row.getSecondaryActionsAdapter();
7123bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        vh.mSecondaryBoundData.presenter = vh.getPresenter(false);
7133bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        vh.mSecondaryBoundData.mRowViewHolder = vh;
7143bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        mSecondaryControlsPresenter.onBindViewHolder(vh.mSecondaryControlsVh,
7153bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                vh.mSecondaryBoundData);
7163bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
7173bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        vh.setTotalTime(row.getDuration());
7183bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        vh.setCurrentPosition(row.getCurrentPosition());
7193bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        vh.setBufferedPosition(row.getBufferedPosition());
7203bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        row.setOnPlaybackProgressChangedListener(vh.mListener);
7213bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    }
7223bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
7233bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    @Override
7243bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    protected void onUnbindRowViewHolder(RowPresenter.ViewHolder holder) {
7253bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        ViewHolder vh = (ViewHolder) holder;
7263bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        PlaybackControlsRow row = (PlaybackControlsRow) vh.getRow();
7273bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
7283bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        if (vh.mDescriptionViewHolder != null) {
7293bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            mDescriptionPresenter.onUnbindViewHolder(vh.mDescriptionViewHolder);
7303bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        }
7313bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        mPlaybackControlsPresenter.onUnbindViewHolder(vh.mControlsVh);
7323bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        mSecondaryControlsPresenter.onUnbindViewHolder(vh.mSecondaryControlsVh);
7333bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        row.setOnPlaybackProgressChangedListener(null);
7343bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
7353bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        super.onUnbindRowViewHolder(holder);
7363bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    }
7373bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
7383bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    /**
7393bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu     * Client of progress bar is clicked, default implementation delegate click to
7403bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu     * PlayPauseAction.
7413bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu     *
7423bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu     * @param vh ViewHolder of PlaybackTransportRowPresenter
7433bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu     */
7443bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    protected void onProgressBarClicked(ViewHolder vh) {
7453bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        if (vh != null) {
7463bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            if (vh.mPlayPauseAction == null) {
7473bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                vh.mPlayPauseAction = new PlaybackControlsRow.PlayPauseAction(vh.view.getContext());
7483bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            }
7493bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            if (vh.getOnItemViewClickedListener() != null) {
7503bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                vh.getOnItemViewClickedListener().onItemClicked(vh, vh.mPlayPauseAction,
7513bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                        vh, vh.getRow());
7523bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            }
7533bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            if (mOnActionClickedListener != null) {
7543bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                mOnActionClickedListener.onActionClicked(vh.mPlayPauseAction);
7553bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            }
7563bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        }
7573bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    }
7583bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
7593bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    /**
7603bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu     * Set default seek increment if {@link PlaybackSeekDataProvider} is null.
7613bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu     * @param ratio float value between 0(inclusive) and 1(inclusive).
7623bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu     */
7633bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    public void setDefaultSeekIncrement(float ratio) {
7643bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        mDefaultSeekIncrement = ratio;
7653bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    }
7663bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
7673bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    /**
7683bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu     * Get default seek increment if {@link PlaybackSeekDataProvider} is null.
7693bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu     * @return float value between 0(inclusive) and 1(inclusive).
7703bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu     */
7713bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    public float getDefaultSeekIncrement() {
7723bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        return mDefaultSeekIncrement;
7733bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    }
7743bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
7753bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    @Override
7763bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    protected void onRowViewSelected(RowPresenter.ViewHolder vh, boolean selected) {
7773bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        super.onRowViewSelected(vh, selected);
7783bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        if (selected) {
7793bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            ((ViewHolder) vh).dispatchItemSelection();
7803bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        }
7813bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    }
7823bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
7833bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    @Override
7843bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    protected void onRowViewAttachedToWindow(RowPresenter.ViewHolder vh) {
7853bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        super.onRowViewAttachedToWindow(vh);
7863bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        if (mDescriptionPresenter != null) {
7873bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            mDescriptionPresenter.onViewAttachedToWindow(
7883bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                    ((ViewHolder) vh).mDescriptionViewHolder);
7893bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        }
7903bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    }
7913bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
7923bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    @Override
7933bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    protected void onRowViewDetachedFromWindow(RowPresenter.ViewHolder vh) {
7943bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        super.onRowViewDetachedFromWindow(vh);
7953bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        if (mDescriptionPresenter != null) {
7963bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu            mDescriptionPresenter.onViewDetachedFromWindow(
7973bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu                    ((ViewHolder) vh).mDescriptionViewHolder);
7983bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu        }
7993bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu    }
8003bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu
8013bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu}
802