1eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stoutpackage android.support.v17.leanback.app;
2eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
3eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stoutimport android.content.Context;
4eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stoutimport android.graphics.drawable.Drawable;
5eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stoutimport android.os.Handler;
6eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stoutimport android.os.Message;
7eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stoutimport android.support.v17.leanback.widget.AbstractDetailsDescriptionPresenter;
8eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stoutimport android.support.v17.leanback.widget.Action;
9eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stoutimport android.support.v17.leanback.widget.ControlButtonPresenterSelector;
1060bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stoutimport android.support.v17.leanback.widget.OnActionClickedListener;
11eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stoutimport android.support.v17.leanback.widget.OnItemViewClickedListener;
12eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stoutimport android.support.v17.leanback.widget.PlaybackControlsRow;
13eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stoutimport android.support.v17.leanback.widget.PlaybackControlsRowPresenter;
14eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stoutimport android.support.v17.leanback.widget.Presenter;
15eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stoutimport android.support.v17.leanback.widget.PresenterSelector;
16eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stoutimport android.support.v17.leanback.widget.Row;
17eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stoutimport android.support.v17.leanback.widget.RowPresenter;
18eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stoutimport android.support.v17.leanback.widget.SparseArrayObjectAdapter;
19eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stoutimport android.util.Log;
20eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stoutimport android.view.InputEvent;
21eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stoutimport android.view.KeyEvent;
2260bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stoutimport android.view.View;
23eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
24eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
25eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout/**
26eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout * A helper class for managing a {@link android.support.v17.leanback.widget.PlaybackControlsRow} and
27eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout * {@link PlaybackOverlayFragment} that implements a recommended approach to handling standard
28eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout * playback control actions such as play/pause, fast forward/rewind at progressive speed levels,
29eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout * and skip to next/previous.  This helper class is a glue layer in that it manages the
30eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout * configuration of and interaction between the leanback UI components by defining a functional
31eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout * interface to the media player.
32eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout *
33eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout * <p>You can instantiate a concrete subclass such as {@link MediaControllerGlue} or you must
34eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout * subclass this abstract helper.  To create a subclass you must implement all of the
35eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout * abstract methods and the subclass must invoke {@link #onMetadataChanged()} and
36eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout * {@link #onStateChanged()} appropriately.
37eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout * </p>
38eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout *
39eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout * <p>To use an instance of the glue layer, first construct an instance.  Constructor parameters
4060bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout * inform the glue what speed levels are supported for fast forward/rewind.  Providing a
4160bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout * {@link android.support.v17.leanback.app.PlaybackOverlayFragment} is optional.
4260bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout * </p>
4360bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout *
4460bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout * <p>If you have your own controls row you must pass it to {@link #setControlsRow}.
4560bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout * The row will be updated by the glue layer based on the media metadata and playback state.
4660bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout * Alternatively, you may call {@link #createControlsRowAndPresenter()} which will set a controls
4760bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout * row and return a row presenter you can use to present the row.
48eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout * </p>
49eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout *
50eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout * <p>The helper sets a {@link android.support.v17.leanback.widget.SparseArrayObjectAdapter}
51eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout * on the controls row as the primary actions adapter, and adds actions to it.  You can provide
52eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout * additional actions by overriding {@link #createPrimaryActionsAdapter}.  This helper does not
53eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout * deal in secondary actions so those you may add separately.
54eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout * </p>
55eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout *
5660bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout * <p>Provide a click listener on your fragment and if an action is clicked, call
5760bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout * {@link #onActionClicked}.  There is no need to call {@link #setOnItemViewClickedListener}
5860bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout * but if you do a click listener will be installed on the fragment and recognized action clicks
5960bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout * will be handled.  Your listener will be called only for unhandled actions.
6060bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout * </p>
6160bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout *
6260bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout * <p>The helper implements a key event handler.  If you pass a
6360bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout * {@link android.support.v17.leanback.app.PlaybackOverlayFragment} the fragment's input event
6460bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout * handler will be set.  Otherwise, you should set the glue object as key event handler to the
6560bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout * ViewHolder when bound by your row presenter; see
6660bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout * {@link RowPresenter.ViewHolder#setOnKeyListener(android.view.View.OnKeyListener)}.
67eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout * </p>
68eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout *
69eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout * <p>To update the controls row progress during playback, override {@link #enableProgressUpdating}
70eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout * to manage the lifecycle of a periodic callback to {@link #updateProgress()}.
71eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout * {@link #getUpdatePeriod()} provides a recommended update period.
72eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout * </p>
73eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout *
74eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout */
7560bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stoutpublic abstract class PlaybackControlGlue implements OnActionClickedListener, View.OnKeyListener {
76eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    /**
77ce4c2014042fe6e4723bab30741039848adcf4beDake Gu     * The adapter key for the first custom control on the left side
78eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * of the predefined primary controls.
79eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     */
80eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    public static final int ACTION_CUSTOM_LEFT_FIRST = 0x1;
81eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
82eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    /**
83eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * The adapter key for the skip to previous control.
84eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     */
85eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    public static final int ACTION_SKIP_TO_PREVIOUS = 0x10;
86eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
87eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    /**
88eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * The adapter key for the rewind control.
89eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     */
90eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    public static final int ACTION_REWIND = 0x20;
91eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
92eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    /**
93eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * The adapter key for the play/pause control.
94eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     */
95eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    public static final int ACTION_PLAY_PAUSE = 0x40;
96eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
97eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    /**
98eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * The adapter key for the fast forward control.
99eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     */
100eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    public static final int ACTION_FAST_FORWARD = 0x80;
101eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
102eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    /**
103eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * The adapter key for the skip to next control.
104eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     */
105eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    public static final int ACTION_SKIP_TO_NEXT = 0x100;
106eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
107eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    /**
108eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * The adapter key for the first custom control on the right side
109eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * of the predefined primary controls.
110eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     */
111eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    public static final int ACTION_CUSTOM_RIGHT_FIRST = 0x1000;
112eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
113eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    /**
114eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * Invalid playback speed.
115eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     */
116eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    public static final int PLAYBACK_SPEED_INVALID = -1;
117eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
118eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    /**
119eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * Speed representing playback state that is paused.
120eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     */
121eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    public static final int PLAYBACK_SPEED_PAUSED = 0;
122eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
123eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    /**
124eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * Speed representing playback state that is playing normally.
125eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     */
126eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    public static final int PLAYBACK_SPEED_NORMAL = 1;
127eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
128eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    /**
129eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * The initial (level 0) fast forward playback speed.
130eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * The negative of this value is for rewind at the same speed.
131eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     */
132eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    public static final int PLAYBACK_SPEED_FAST_L0 = 10;
133eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
134eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    /**
135eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * The level 1 fast forward playback speed.
136eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * The negative of this value is for rewind at the same speed.
137eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     */
138eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    public static final int PLAYBACK_SPEED_FAST_L1 = 11;
139eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
140eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    /**
141eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * The level 2 fast forward playback speed.
142eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * The negative of this value is for rewind at the same speed.
143eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     */
144eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    public static final int PLAYBACK_SPEED_FAST_L2 = 12;
145eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
146eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    /**
147eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * The level 3 fast forward playback speed.
148eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * The negative of this value is for rewind at the same speed.
149eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     */
150eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    public static final int PLAYBACK_SPEED_FAST_L3 = 13;
151eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
152eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    /**
153eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * The level 4 fast forward playback speed.
154eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * The negative of this value is for rewind at the same speed.
155eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     */
156eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    public static final int PLAYBACK_SPEED_FAST_L4 = 14;
157eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
158eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    private static final String TAG = "PlaybackControlGlue";
159eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    private static final boolean DEBUG = false;
160eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
161eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    private static final int MSG_UPDATE_PLAYBACK_STATE = 100;
162eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    private static final int UPDATE_PLAYBACK_STATE_DELAY_MS = 2000;
163eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    private static final int NUMBER_OF_SEEK_SPEEDS = PLAYBACK_SPEED_FAST_L4 -
164eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            PLAYBACK_SPEED_FAST_L0 + 1;
165eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
166eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    private final PlaybackOverlayFragment mFragment;
167eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    private final Context mContext;
168eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    private final int[] mFastForwardSpeeds;
169eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    private final int[] mRewindSpeeds;
170eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    private PlaybackControlsRow mControlsRow;
171eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    private SparseArrayObjectAdapter mPrimaryActionsAdapter;
172eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    private PlaybackControlsRow.PlayPauseAction mPlayPauseAction;
173eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    private PlaybackControlsRow.SkipNextAction mSkipNextAction;
174eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    private PlaybackControlsRow.SkipPreviousAction mSkipPreviousAction;
175eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    private PlaybackControlsRow.FastForwardAction mFastForwardAction;
176eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    private PlaybackControlsRow.RewindAction mRewindAction;
177eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    private OnItemViewClickedListener mExternalOnItemViewClickedListener;
178eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    private int mPlaybackSpeed = PLAYBACK_SPEED_NORMAL;
179f47fb1e34efd538c322f7539893272ba847cdbdcCraig Stout    private boolean mFadeWhenPlaying = true;
180eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
181eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    private final Handler mHandler = new Handler() {
182eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        @Override
183eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        public void handleMessage(Message msg) {
184eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            if (msg.what == MSG_UPDATE_PLAYBACK_STATE) {
185eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                updatePlaybackState();
186eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            }
187eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        }
188eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    };
189eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
190eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    private final OnItemViewClickedListener mOnItemViewClickedListener =
191eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            new OnItemViewClickedListener() {
192eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        @Override
193eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        public void onItemClicked(Presenter.ViewHolder viewHolder, Object object,
194eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                                  RowPresenter.ViewHolder viewHolder2, Row row) {
195eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            if (DEBUG) Log.v(TAG, "onItemClicked " + object);
196eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            boolean handled = false;
197eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            if (object instanceof Action) {
19860bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout                handled = dispatchAction((Action) object, null);
199eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            }
200eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            if (!handled && mExternalOnItemViewClickedListener != null) {
201eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                mExternalOnItemViewClickedListener.onItemClicked(viewHolder, object,
202eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                        viewHolder2, row);
203eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            }
204eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        }
205eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    };
206eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
20760bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout    /**
20860bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout     * Constructor for the glue.
20960bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout     *
21060bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout     * @param context
21160bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout     * @param seekSpeeds Array of seek speeds for fast forward and rewind.
21260bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout     */
21360bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout    public PlaybackControlGlue(Context context, int[] seekSpeeds) {
21460bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout        this(context, null, seekSpeeds, seekSpeeds);
21560bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout    }
216eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
217eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    /**
218eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * Constructor for the glue.
219eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     *
22060bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout     * @param context
22160bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout     * @param fastForwardSpeeds Array of seek speeds for fast forward.
22260bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout     * @param rewindSpeeds Array of seek speeds for rewind.
22360bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout     */
22460bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout    public PlaybackControlGlue(Context context,
22560bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout                               int[] fastForwardSpeeds,
22660bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout                               int[] rewindSpeeds) {
22760bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout        this(context, null, fastForwardSpeeds, rewindSpeeds);
22860bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout    }
22960bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout
23060bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout    /**
23160bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout     * Constructor for the glue.
232eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     *
233eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * @param context
23460bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout     * @param fragment Optional; if using a {@link PlaybackOverlayFragment}, pass it in.
235eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * @param seekSpeeds Array of seek speeds for fast forward and rewind.
236eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     */
237eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    public PlaybackControlGlue(Context context,
238eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                               PlaybackOverlayFragment fragment,
239eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                               int[] seekSpeeds) {
240eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        this(context, fragment, seekSpeeds, seekSpeeds);
241eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    }
242eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
243eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    /**
244eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * Constructor for the glue.
245eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     *
246eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * @param context
24760bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout     * @param fragment Optional; if using a {@link PlaybackOverlayFragment}, pass it in.
248eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * @param fastForwardSpeeds Array of seek speeds for fast forward.
249eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * @param rewindSpeeds Array of seek speeds for rewind.
250eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     */
251eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    public PlaybackControlGlue(Context context,
252eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                               PlaybackOverlayFragment fragment,
253eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                               int[] fastForwardSpeeds,
254eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                               int[] rewindSpeeds) {
255eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        mContext = context;
256eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        mFragment = fragment;
25760bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout        if (fragment != null) {
25860bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout            attachToFragment();
259eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        }
260eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        if (fastForwardSpeeds.length == 0 || fastForwardSpeeds.length > NUMBER_OF_SEEK_SPEEDS) {
261eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            throw new IllegalStateException("invalid fastForwardSpeeds array size");
262eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        }
263eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        mFastForwardSpeeds = fastForwardSpeeds;
264eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        if (rewindSpeeds.length == 0 || rewindSpeeds.length > NUMBER_OF_SEEK_SPEEDS) {
265eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            throw new IllegalStateException("invalid rewindSpeeds array size");
266eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        }
267eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        mRewindSpeeds = rewindSpeeds;
268eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    }
269eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
27060bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout    private final PlaybackOverlayFragment.InputEventHandler mOnInputEventHandler =
27160bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout            new PlaybackOverlayFragment.InputEventHandler() {
27260bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout        @Override
27360bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout        public boolean handleInputEvent(InputEvent event) {
27460bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout            if (event instanceof KeyEvent) {
27560bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout                KeyEvent keyEvent = (KeyEvent) event;
27660bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout                return onKey(null, keyEvent.getKeyCode(), keyEvent);
27760bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout            }
27860bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout            return false;
27960bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout        }
28060bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout    };
28160bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout
28260bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout    private void attachToFragment() {
28360bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout        mFragment.setInputEventHandler(mOnInputEventHandler);
28460bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout    }
28560bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout
286eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    /**
287eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * Helper method for instantiating a
288eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * {@link android.support.v17.leanback.widget.PlaybackControlsRow} and corresponding
289eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * {@link android.support.v17.leanback.widget.PlaybackControlsRowPresenter}.
290eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     */
291eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    public PlaybackControlsRowPresenter createControlsRowAndPresenter() {
292eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        PlaybackControlsRow controlsRow = new PlaybackControlsRow(this);
293eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        setControlsRow(controlsRow);
294eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
29560bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout        AbstractDetailsDescriptionPresenter detailsPresenter =
29660bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout                new AbstractDetailsDescriptionPresenter() {
297eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            @Override
298eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            protected void onBindDescription(AbstractDetailsDescriptionPresenter.ViewHolder
299eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                                                     viewHolder, Object object) {
300eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                PlaybackControlGlue glue = (PlaybackControlGlue) object;
301eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                if (glue.hasValidMedia()) {
302eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                    viewHolder.getTitle().setText(glue.getMediaTitle());
303eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                    viewHolder.getSubtitle().setText(glue.getMediaSubtitle());
304eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                } else {
305eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                    viewHolder.getTitle().setText("");
306eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                    viewHolder.getSubtitle().setText("");
307eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                }
308eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            }
30960bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout        };
31060bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout        return new PlaybackControlsRowPresenter(detailsPresenter) {
31160bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout            @Override
31260bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout            protected void onBindRowViewHolder(RowPresenter.ViewHolder vh, Object item) {
31360bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout                super.onBindRowViewHolder(vh, item);
31460bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout                vh.setOnKeyListener(PlaybackControlGlue.this);
31560bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout            }
31660bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout            @Override
31760bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout            protected void onUnbindRowViewHolder(RowPresenter.ViewHolder vh) {
31860bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout                super.onUnbindRowViewHolder(vh);
31960bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout                vh.setOnKeyListener(null);
32060bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout            }
32160bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout        };
322eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    }
323eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
324eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    /**
325eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * Returns the fragment.
326eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     */
327eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    public PlaybackOverlayFragment getFragment() {
328eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        return mFragment;
329eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    }
330eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
331eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    /**
332eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * Returns the context.
333eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     */
334eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    public Context getContext() {
335eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        return mContext;
336eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    }
337eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
338eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    /**
339eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * Returns the fast forward speeds.
340eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     */
341eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    public int[] getFastForwardSpeeds() {
342eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        return mFastForwardSpeeds;
343eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    }
344eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
345eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    /**
346eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * Returns the rewind speeds.
347eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     */
348eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    public int[] getRewindSpeeds() {
349eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        return mRewindSpeeds;
350eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    }
351eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
352eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    /**
353c92c356de1a687598a031f3452053dc6c9af9b77Craig Stout     * Sets the controls to fade after a timeout when media is playing.
354c92c356de1a687598a031f3452053dc6c9af9b77Craig Stout     */
355c92c356de1a687598a031f3452053dc6c9af9b77Craig Stout    public void setFadingEnabled(boolean enable) {
356c92c356de1a687598a031f3452053dc6c9af9b77Craig Stout        mFadeWhenPlaying = enable;
35760bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout        if (!mFadeWhenPlaying && mFragment != null) {
358c92c356de1a687598a031f3452053dc6c9af9b77Craig Stout            mFragment.setFadingEnabled(false);
359c92c356de1a687598a031f3452053dc6c9af9b77Craig Stout        }
360c92c356de1a687598a031f3452053dc6c9af9b77Craig Stout    }
361c92c356de1a687598a031f3452053dc6c9af9b77Craig Stout
362c92c356de1a687598a031f3452053dc6c9af9b77Craig Stout    /**
363c92c356de1a687598a031f3452053dc6c9af9b77Craig Stout     * Returns true if controls are set to fade when media is playing.
364c92c356de1a687598a031f3452053dc6c9af9b77Craig Stout     */
365c92c356de1a687598a031f3452053dc6c9af9b77Craig Stout    public boolean isFadingEnabled() {
366c92c356de1a687598a031f3452053dc6c9af9b77Craig Stout        return mFadeWhenPlaying;
367c92c356de1a687598a031f3452053dc6c9af9b77Craig Stout    }
368c92c356de1a687598a031f3452053dc6c9af9b77Craig Stout
369c92c356de1a687598a031f3452053dc6c9af9b77Craig Stout    /**
370eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * Set the {@link OnItemViewClickedListener} to be called if the click event
371eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * is not handled internally.
372eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * @param listener
37360bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout     * @deprecated Don't call this.  Instead set the listener on the fragment yourself,
37460bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout     * and call {@link #onActionClicked} to handle clicks.
375eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     */
376d805095048f6be52cddbd572ee343c4639ba8187Alan Viverette    @Deprecated
377eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    public void setOnItemViewClickedListener(OnItemViewClickedListener listener) {
378eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        mExternalOnItemViewClickedListener = listener;
37960bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout        if (mFragment != null) {
38060bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout            mFragment.setOnItemViewClickedListener(mOnItemViewClickedListener);
38160bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout        }
382eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    }
383eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
384eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    /**
385eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * Returns the {@link OnItemViewClickedListener}.
386eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     */
387eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    public OnItemViewClickedListener getOnItemViewClickedListener() {
388eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        return mExternalOnItemViewClickedListener;
389eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    }
390eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
391eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    /**
392eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * Sets the controls row to be managed by the glue layer.
393eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * The primary actions and playback state related aspects of the row
394eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * are updated by the glue.
395eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     */
396eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    public void setControlsRow(PlaybackControlsRow controlsRow) {
397eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        mControlsRow = controlsRow;
398eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        mPrimaryActionsAdapter = createPrimaryActionsAdapter(
399eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                new ControlButtonPresenterSelector());
400eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        mControlsRow.setPrimaryActionsAdapter(mPrimaryActionsAdapter);
401eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        updateControlsRow();
402eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    }
403eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
404eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    /**
405eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * Returns the playback controls row managed by the glue layer.
406eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     */
407eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    public PlaybackControlsRow getControlsRow() {
408eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        return mControlsRow;
409eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    }
410eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
411eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    /**
412eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * Override this to start/stop a runnable to call {@link #updateProgress} at
413eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * an interval such as {@link #getUpdatePeriod}.
414eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     */
415eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    public void enableProgressUpdating(boolean enable) {
416eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    }
417eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
418eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    /**
419eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * Returns the time period in milliseconds that should be used
420eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * to update the progress.  See {@link #updateProgress()}.
421eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     */
422eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    public int getUpdatePeriod() {
423eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        // TODO: calculate a better update period based on total duration and screen size
424eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        return 500;
425eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    }
426eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
427eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    /**
428eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * Updates the progress bar based on the current media playback position.
429eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     */
430eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    public void updateProgress() {
431eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        int position = getCurrentPosition();
432eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        if (DEBUG) Log.v(TAG, "updateProgress " + position);
433eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        mControlsRow.setCurrentTime(position);
434eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    }
435eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
43660bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout    /**
43760bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout     * Handles action clicks.  A subclass may override this add support for additional actions.
43860bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout     */
43960bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout    @Override
44060bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout    public void onActionClicked(Action action) {
44160bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout        dispatchAction(action, null);
44260bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout    }
44360bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout
44460bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout    /**
44560bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout     * Handles key events and returns true if handled.  A subclass may override this to provide
44660bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout     * additional support.
44760bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout     */
44860bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout    @Override
44960bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout    public boolean onKey(View v, int keyCode, KeyEvent event) {
45060bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout        switch (keyCode) {
45160bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout            case KeyEvent.KEYCODE_DPAD_UP:
45260bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout            case KeyEvent.KEYCODE_DPAD_DOWN:
45360bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout            case KeyEvent.KEYCODE_DPAD_RIGHT:
45460bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout            case KeyEvent.KEYCODE_DPAD_LEFT:
45560bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout            case KeyEvent.KEYCODE_BACK:
456c89266e21f0269aecd5df2dc2a39f6f0cf11a58eCraig Stout            case KeyEvent.KEYCODE_ESCAPE:
45760bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout                boolean abortSeek = mPlaybackSpeed >= PLAYBACK_SPEED_FAST_L0 ||
45860bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout                        mPlaybackSpeed <= -PLAYBACK_SPEED_FAST_L0;
45960bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout                if (abortSeek) {
46060bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout                    mPlaybackSpeed = PLAYBACK_SPEED_NORMAL;
46160bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout                    startPlayback(mPlaybackSpeed);
46260bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout                    updatePlaybackStatusAfterUserAction();
463c89266e21f0269aecd5df2dc2a39f6f0cf11a58eCraig Stout                    return keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE;
46460bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout                }
46560bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout                return false;
46660bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout        }
46760bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout        Action action = mControlsRow.getActionForKeyCode(mPrimaryActionsAdapter, keyCode);
46860bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout        if (action != null) {
46960bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout            if (action == mPrimaryActionsAdapter.lookup(ACTION_PLAY_PAUSE) ||
47060bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout                    action == mPrimaryActionsAdapter.lookup(ACTION_REWIND) ||
47160bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout                    action == mPrimaryActionsAdapter.lookup(ACTION_FAST_FORWARD) ||
47260bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout                    action == mPrimaryActionsAdapter.lookup(ACTION_SKIP_TO_PREVIOUS) ||
47360bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout                    action == mPrimaryActionsAdapter.lookup(ACTION_SKIP_TO_NEXT)) {
47460bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout                if (((KeyEvent) event).getAction() == KeyEvent.ACTION_DOWN) {
47560bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout                    dispatchAction(action, (KeyEvent) event);
47660bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout                }
47760bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout                return true;
47860bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout            }
47960bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout        }
48060bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout        return false;
48160bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout    }
48260bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout
48360bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout    /**
48460bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout     * Called when the given action is invoked, either by click or keyevent.
48560bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout     */
48660bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout    private boolean dispatchAction(Action action, KeyEvent keyEvent) {
487eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        boolean handled = false;
488eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        if (action == mPlayPauseAction) {
48960bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout            boolean canPlay = keyEvent == null ||
49060bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout                    keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE ||
49160bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout                    keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PLAY;
49260bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout            boolean canPause = keyEvent == null ||
49360bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout                    keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE ||
49460bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout                    keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PAUSE;
495eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            if (mPlaybackSpeed != PLAYBACK_SPEED_NORMAL) {
49660bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout                if (canPlay) {
49760bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout                    mPlaybackSpeed = PLAYBACK_SPEED_NORMAL;
49860bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout                    startPlayback(mPlaybackSpeed);
49960bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout                }
50060bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout            } else if (canPause) {
501eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                mPlaybackSpeed = PLAYBACK_SPEED_PAUSED;
502eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                pausePlayback();
503eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            }
504eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            updatePlaybackStatusAfterUserAction();
505eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            handled = true;
506eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        } else if (action == mSkipNextAction) {
507eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            skipToNext();
508eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            handled = true;
509eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        } else if (action == mSkipPreviousAction) {
510eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            skipToPrevious();
511eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            handled = true;
512eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        } else if (action == mFastForwardAction) {
513eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            if (mPlaybackSpeed < getMaxForwardSpeedId()) {
514eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                switch (mPlaybackSpeed) {
515eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                    case PLAYBACK_SPEED_FAST_L0:
516eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                    case PLAYBACK_SPEED_FAST_L1:
517eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                    case PLAYBACK_SPEED_FAST_L2:
518eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                    case PLAYBACK_SPEED_FAST_L3:
519eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                        mPlaybackSpeed++;
520eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                        break;
52160bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout                    default:
52260bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout                        mPlaybackSpeed = PLAYBACK_SPEED_FAST_L0;
52360bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout                        break;
524eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                }
525eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                startPlayback(mPlaybackSpeed);
526eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                updatePlaybackStatusAfterUserAction();
527eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            }
528eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            handled = true;
529eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        } else if (action == mRewindAction) {
530eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            if (mPlaybackSpeed > -getMaxRewindSpeedId()) {
531eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                switch (mPlaybackSpeed) {
532eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                    case -PLAYBACK_SPEED_FAST_L0:
533eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                    case -PLAYBACK_SPEED_FAST_L1:
534eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                    case -PLAYBACK_SPEED_FAST_L2:
535eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                    case -PLAYBACK_SPEED_FAST_L3:
536eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                        mPlaybackSpeed--;
537eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                        break;
53860bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout                    default:
53960bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout                        mPlaybackSpeed = -PLAYBACK_SPEED_FAST_L0;
54060bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout                        break;
541eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                }
542eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                startPlayback(mPlaybackSpeed);
543eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                updatePlaybackStatusAfterUserAction();
544eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            }
545eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            handled = true;
546eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        }
547eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        return handled;
548eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    }
549eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
550eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    private int getMaxForwardSpeedId() {
551eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        return PLAYBACK_SPEED_FAST_L0 + (mFastForwardSpeeds.length - 1);
552eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    }
553eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
554eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    private int getMaxRewindSpeedId() {
555eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        return PLAYBACK_SPEED_FAST_L0 + (mRewindSpeeds.length - 1);
556eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    }
557eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
558eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    private void updateControlsRow() {
559eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        updateRowMetadata();
560eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        mHandler.removeMessages(MSG_UPDATE_PLAYBACK_STATE);
561eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        updatePlaybackState();
562eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    }
563eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
564eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    private void updatePlaybackStatusAfterUserAction() {
565eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        updatePlaybackState(mPlaybackSpeed);
566eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        // Sync playback state after a delay
567eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        mHandler.removeMessages(MSG_UPDATE_PLAYBACK_STATE);
568eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        mHandler.sendEmptyMessageDelayed(MSG_UPDATE_PLAYBACK_STATE,
569eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                UPDATE_PLAYBACK_STATE_DELAY_MS);
570eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    }
571eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
572eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    private void updateRowMetadata() {
573eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        if (mControlsRow == null) {
574eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            return;
575eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        }
576eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
577eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        if (DEBUG) Log.v(TAG, "updateRowMetadata hasValidMedia " + hasValidMedia());
578eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
579eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        if (!hasValidMedia()) {
580eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            mControlsRow.setImageDrawable(null);
581eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            mControlsRow.setTotalTime(0);
582eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            mControlsRow.setCurrentTime(0);
583eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        } else {
584eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            mControlsRow.setImageDrawable(getMediaArt());
585eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            mControlsRow.setTotalTime(getMediaDuration());
586eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            mControlsRow.setCurrentTime(getCurrentPosition());
587eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        }
588eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
589eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        onRowChanged(mControlsRow);
590eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    }
591eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
592eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    private void updatePlaybackState() {
593eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        if (hasValidMedia()) {
594eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            mPlaybackSpeed = getCurrentSpeedId();
595eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            updatePlaybackState(mPlaybackSpeed);
596eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        }
597eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    }
598eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
599eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    private void updatePlaybackState(int playbackSpeed) {
600eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        if (mControlsRow == null) {
601eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            return;
602eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        }
603eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
604eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        final long actions = getSupportedActions();
605eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        if ((actions & ACTION_SKIP_TO_PREVIOUS) != 0) {
606eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            if (mSkipPreviousAction == null) {
607eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                mSkipPreviousAction = new PlaybackControlsRow.SkipPreviousAction(mContext);
608eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            }
609eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            mPrimaryActionsAdapter.set(ACTION_SKIP_TO_PREVIOUS, mSkipPreviousAction);
610eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        } else {
611eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            mPrimaryActionsAdapter.clear(ACTION_SKIP_TO_PREVIOUS);
612eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            mSkipPreviousAction = null;
613eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        }
614eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        if ((actions & ACTION_REWIND) != 0) {
615eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            if (mRewindAction == null) {
616eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                mRewindAction = new PlaybackControlsRow.RewindAction(mContext,
617eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                        mRewindSpeeds.length);
618eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            }
619eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            mPrimaryActionsAdapter.set(ACTION_REWIND, mRewindAction);
620eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        } else {
621eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            mPrimaryActionsAdapter.clear(ACTION_REWIND);
622eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            mRewindAction = null;
623eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        }
624eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        if ((actions & ACTION_PLAY_PAUSE) != 0) {
625eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            if (mPlayPauseAction == null) {
626eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                mPlayPauseAction = new PlaybackControlsRow.PlayPauseAction(mContext);
627eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            }
628eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            mPrimaryActionsAdapter.set(ACTION_PLAY_PAUSE, mPlayPauseAction);
629eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        } else {
630eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            mPrimaryActionsAdapter.clear(ACTION_PLAY_PAUSE);
631eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            mPlayPauseAction = null;
632eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        }
633eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        if ((actions & ACTION_FAST_FORWARD) != 0) {
634eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            if (mFastForwardAction == null) {
635eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                mFastForwardAction = new PlaybackControlsRow.FastForwardAction(mContext,
636eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                        mFastForwardSpeeds.length);
637eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            }
638eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            mPrimaryActionsAdapter.set(ACTION_FAST_FORWARD, mFastForwardAction);
639eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        } else {
640eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            mPrimaryActionsAdapter.clear(ACTION_FAST_FORWARD);
641eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            mFastForwardAction = null;
642eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        }
643eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        if ((actions & ACTION_SKIP_TO_NEXT) != 0) {
644eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            if (mSkipNextAction == null) {
645eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                mSkipNextAction = new PlaybackControlsRow.SkipNextAction(mContext);
646eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            }
647eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            mPrimaryActionsAdapter.set(ACTION_SKIP_TO_NEXT, mSkipNextAction);
648eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        } else {
649eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            mPrimaryActionsAdapter.clear(ACTION_SKIP_TO_NEXT);
650eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            mSkipNextAction = null;
651eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        }
652eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
653eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        if (mFastForwardAction != null) {
654eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            int index = 0;
655eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            if (playbackSpeed >= PLAYBACK_SPEED_FAST_L0) {
656eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                index = playbackSpeed - PLAYBACK_SPEED_FAST_L0;
657eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                if (playbackSpeed < getMaxForwardSpeedId()) {
658eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                    index++;
659eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                }
660eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            }
661eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            if (mFastForwardAction.getIndex() != index) {
662eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                mFastForwardAction.setIndex(index);
663eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                notifyItemChanged(mPrimaryActionsAdapter, mFastForwardAction);
664eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            }
665eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        }
666eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        if (mRewindAction != null) {
667eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            int index = 0;
668eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            if (playbackSpeed <= -PLAYBACK_SPEED_FAST_L0) {
669eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                index = -playbackSpeed - PLAYBACK_SPEED_FAST_L0;
670eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                if (-playbackSpeed < getMaxRewindSpeedId()) {
671eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                    index++;
672eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                }
673eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            }
674eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            if (mRewindAction.getIndex() != index) {
675eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                mRewindAction.setIndex(index);
676eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                notifyItemChanged(mPrimaryActionsAdapter, mRewindAction);
677eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            }
678eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        }
679eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
680eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        if (playbackSpeed == PLAYBACK_SPEED_PAUSED) {
681eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            updateProgress();
682eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            enableProgressUpdating(false);
683eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        } else {
684eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            enableProgressUpdating(true);
685eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        }
686eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
68760bb6af2e336072921f5d3c3861e86b3cc6241b3Craig Stout        if (mFadeWhenPlaying && mFragment != null) {
688eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            mFragment.setFadingEnabled(playbackSpeed == PLAYBACK_SPEED_NORMAL);
689eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        }
690eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
691eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        if (mPlayPauseAction != null) {
692eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            int index = playbackSpeed == PLAYBACK_SPEED_PAUSED ?
693eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                    PlaybackControlsRow.PlayPauseAction.PLAY :
694eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                    PlaybackControlsRow.PlayPauseAction.PAUSE;
695eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            if (mPlayPauseAction.getIndex() != index) {
696eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                mPlayPauseAction.setIndex(index);
697eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                notifyItemChanged(mPrimaryActionsAdapter, mPlayPauseAction);
698eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            }
699eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        }
700eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    }
701eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
702eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    private static void notifyItemChanged(SparseArrayObjectAdapter adapter, Object object) {
703eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        int index = adapter.indexOf(object);
704eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        if (index >= 0) {
705eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            adapter.notifyArrayItemRangeChanged(index, 1);
706eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        }
707eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    }
708eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
709eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    private static String getSpeedString(int speed) {
710eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        switch (speed) {
711eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            case PLAYBACK_SPEED_INVALID:
712eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                return "PLAYBACK_SPEED_INVALID";
713eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            case PLAYBACK_SPEED_PAUSED:
714eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                return "PLAYBACK_SPEED_PAUSED";
715eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            case PLAYBACK_SPEED_NORMAL:
716eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                return "PLAYBACK_SPEED_NORMAL";
717eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            case PLAYBACK_SPEED_FAST_L0:
718eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                return "PLAYBACK_SPEED_FAST_L0";
719eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            case PLAYBACK_SPEED_FAST_L1:
720eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                return "PLAYBACK_SPEED_FAST_L1";
721eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            case PLAYBACK_SPEED_FAST_L2:
722eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                return "PLAYBACK_SPEED_FAST_L2";
723eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            case PLAYBACK_SPEED_FAST_L3:
724eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                return "PLAYBACK_SPEED_FAST_L3";
725eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            case PLAYBACK_SPEED_FAST_L4:
726eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                return "PLAYBACK_SPEED_FAST_L4";
727eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            case -PLAYBACK_SPEED_FAST_L0:
728eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                return "-PLAYBACK_SPEED_FAST_L0";
729eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            case -PLAYBACK_SPEED_FAST_L1:
730eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                return "-PLAYBACK_SPEED_FAST_L1";
731eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            case -PLAYBACK_SPEED_FAST_L2:
732eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                return "-PLAYBACK_SPEED_FAST_L2";
733eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            case -PLAYBACK_SPEED_FAST_L3:
734eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                return "-PLAYBACK_SPEED_FAST_L3";
735eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            case -PLAYBACK_SPEED_FAST_L4:
736eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                return "-PLAYBACK_SPEED_FAST_L4";
737eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        }
738eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        return null;
739eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    }
740eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
741eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    /**
742eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * Returns true if there is a valid media item.
743eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     */
744eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    public abstract boolean hasValidMedia();
745eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
746eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    /**
747eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * Returns true if media is currently playing.
748eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     */
749eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    public abstract boolean isMediaPlaying();
750eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
751eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    /**
752eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * Returns the title of the media item.
753eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     */
754eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    public abstract CharSequence getMediaTitle();
755eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
756eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    /**
757eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * Returns the subtitle of the media item.
758eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     */
759eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    public abstract CharSequence getMediaSubtitle();
760eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
761eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    /**
762eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * Returns the duration of the media item in milliseconds.
763eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     */
764eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    public abstract int getMediaDuration();
765eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
766eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    /**
767eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * Returns a bitmap of the art for the media item.
768eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     */
769eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    public abstract Drawable getMediaArt();
770eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
771eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    /**
772eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * Returns a bitmask of actions supported by the media player.
773eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     */
774eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    public abstract long getSupportedActions();
775eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
776eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    /**
777eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * Returns the current playback speed.  When playing normally,
778eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * {@link #PLAYBACK_SPEED_NORMAL} should be returned.
779eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     */
780eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    public abstract int getCurrentSpeedId();
781eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
782eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    /**
783eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * Returns the current position of the media item in milliseconds.
784eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     */
785eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    public abstract int getCurrentPosition();
786eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
787eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    /**
788eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * Start playback at the given speed.
789eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * @param speed The desired playback speed.  For normal playback this will be
790eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     *              {@link #PLAYBACK_SPEED_NORMAL}; higher positive values for fast forward,
791eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     *              and negative values for rewind.
792eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     */
793eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    protected abstract void startPlayback(int speed);
794eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
795eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    /**
796eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * Pause playback.
797eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     */
798eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    protected abstract void pausePlayback();
799eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
800eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    /**
801eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * Skip to the next track.
802eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     */
803eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    protected abstract void skipToNext();
804eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
805eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    /**
806eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * Skip to the previous track.
807eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     */
808eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    protected abstract void skipToPrevious();
809eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
810eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    /**
811eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * Invoked when the playback controls row has changed.  The adapter containing this row
812eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * should be notified.
813eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     */
814eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    protected abstract void onRowChanged(PlaybackControlsRow row);
815eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
816eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    /**
817eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * Creates the primary action adapter.  May be overridden to add additional primary
818eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * actions to the adapter.
819eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     */
820eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    protected SparseArrayObjectAdapter createPrimaryActionsAdapter(
821eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            PresenterSelector presenterSelector) {
822eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        return new SparseArrayObjectAdapter(presenterSelector);
823eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    }
824eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
825eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    /**
826eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * Must be called appropriately by a subclass when the playback state has changed.
827eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     */
828eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    protected void onStateChanged() {
829eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        if (DEBUG) Log.v(TAG, "onStateChanged");
830eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        // If a pending control button update is present, delay
831eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        // the update until the state settles.
832eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        if (!hasValidMedia()) {
833eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            return;
834eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        }
835eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        if (mHandler.hasMessages(MSG_UPDATE_PLAYBACK_STATE)) {
836eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            mHandler.removeMessages(MSG_UPDATE_PLAYBACK_STATE);
837eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            if (getCurrentSpeedId() != mPlaybackSpeed) {
838eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                if (DEBUG) Log.v(TAG, "Status expectation mismatch, delaying update");
839eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                mHandler.sendEmptyMessageDelayed(MSG_UPDATE_PLAYBACK_STATE,
840eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                        UPDATE_PLAYBACK_STATE_DELAY_MS);
841eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            } else {
842eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                if (DEBUG) Log.v(TAG, "Update state matches expectation");
843eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout                updatePlaybackState();
844eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            }
845eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        } else {
846eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout            updatePlaybackState();
847eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        }
848eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    }
849eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout
850eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    /**
851eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     * Must be called appropriately by a subclass when the metadata state has changed.
852eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout     */
853eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    protected void onMetadataChanged() {
854eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        if (DEBUG) Log.v(TAG, "onMetadataChanged");
855eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout        updateRowMetadata();
856eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout    }
857eb66dab544c4c1eabe4d469b7cea348d4b01e664Craig Stout}
858