PlaybackControlsRow.java revision 386b06a44a1f46327f045dbb3ae1bebadbcbfed7
1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 * in compliance with the License. You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software distributed under the License
10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 * or implied. See the License for the specific language governing permissions and limitations under
12 * the License.
13 */
14package android.support.v17.leanback.widget;
15
16import android.support.v17.leanback.R;
17import android.util.TypedValue;
18import android.content.Context;
19import android.content.res.TypedArray;
20import android.graphics.Bitmap;
21import android.graphics.BitmapFactory;
22import android.graphics.Canvas;
23import android.graphics.Color;
24import android.graphics.Paint;
25import android.graphics.PorterDuff;
26import android.graphics.PorterDuffColorFilter;
27import android.graphics.drawable.BitmapDrawable;
28import android.graphics.drawable.Drawable;
29
30/**
31 * A row of playback controls to be displayed by a {@link PlaybackControlsRowPresenter}.
32 *
33 * This row consists of some optional item detail, a series of primary actions,
34 * and an optional series of secondary actions.
35 *
36 * Controls are specified via an {@link ObjectAdapter} containing one or more
37 * {@link Action}s.
38 *
39 * Adapters should have their {@link PresenterSelector} set to an instance of
40 * {@link ControlButtonPresenterSelector}.
41 *
42 */
43public class PlaybackControlsRow extends Row {
44
45    /**
46     * Base class for an action comprised of a series of icons.
47     */
48    public static abstract class MultiAction extends Action {
49        private int mIndex;
50        private Drawable[] mDrawables;
51
52        /**
53         * Constructor
54         * @param id The id of the Action.
55         */
56        public MultiAction(int id) {
57            super(id);
58        }
59
60        /**
61         * Sets the array of drawables.  The size of the array defines the range
62         * of valid indices for this action.
63         */
64        public void setDrawables(Drawable[] drawables) {
65            mDrawables = drawables;
66            setIndex(0);
67        }
68
69        /**
70         * Returns the number of drawables.
71         */
72        public int getNumberOfDrawables() {
73            return mDrawables.length;
74        }
75
76        /**
77         * Returns the drawable at the given index.
78         */
79        public Drawable getDrawable(int index) {
80            return mDrawables[index];
81        }
82
83        /**
84         * Increments the index, wrapping to zero once the end is reached.
85         */
86        public void nextIndex() {
87            setIndex(mIndex < mDrawables.length - 1 ? mIndex + 1 : 0);
88        }
89
90        /**
91         * Sets the current index.
92         */
93        public void setIndex(int index) {
94            mIndex = index;
95            setIcon(mDrawables[mIndex]);
96        }
97
98        /**
99         * Gets the current index.
100         */
101        public int getIndex() {
102            return mIndex;
103        }
104    }
105
106    /**
107     * An action displaying icons for play and pause.
108     */
109    public static class PlayPauseAction extends MultiAction {
110        /**
111         * Action index for the play icon.
112         */
113        public static int PLAY = 0;
114
115        /**
116         * Action index for the pause icon.
117         */
118        public static int PAUSE = 1;
119
120        /**
121         * Constructor
122         * @param context Context used for loading resources.
123         */
124        public PlayPauseAction(Context context) {
125            super(R.id.lb_control_play_pause);
126            Drawable[] drawables = new Drawable[2];
127            drawables[PLAY] = getStyledDrawable(context,
128                    R.styleable.lbPlaybackControlsActionIcons_play);
129            drawables[PAUSE] = getStyledDrawable(context,
130                    R.styleable.lbPlaybackControlsActionIcons_pause);
131            setDrawables(drawables);
132        }
133    }
134
135    /**
136     * An action displaying an icon for fast forward.
137     */
138    public static class FastForwardAction extends Action {
139        /**
140         * Constructor
141         * @param context Context used for loading resources.
142         */
143        public FastForwardAction(Context context) {
144            super(R.id.lb_control_fast_forward);
145            setIcon(getStyledDrawable(context,
146                    R.styleable.lbPlaybackControlsActionIcons_fast_forward));
147        }
148    }
149
150    /**
151     * An action displaying an icon for rewind.
152     */
153    public static class RewindAction extends Action {
154        /**
155         * Constructor
156         * @param context Context used for loading resources.
157         */
158        public RewindAction(Context context) {
159            super(R.id.lb_control_fast_rewind);
160            setIcon(getStyledDrawable(context,
161                    R.styleable.lbPlaybackControlsActionIcons_rewind));
162        }
163    }
164
165    /**
166     * An action displaying an icon for skip next.
167     */
168    public static class SkipNextAction extends Action {
169        /**
170         * Constructor
171         * @param context Context used for loading resources.
172         */
173        public SkipNextAction(Context context) {
174            super(R.id.lb_control_skip_next);
175            setIcon(getStyledDrawable(context,
176                    R.styleable.lbPlaybackControlsActionIcons_skip_next));
177        }
178    }
179
180    /**
181     * An action displaying an icon for skip previous.
182     */
183    public static class SkipPreviousAction extends Action {
184        /**
185         * Constructor
186         * @param context Context used for loading resources.
187         */
188        public SkipPreviousAction(Context context) {
189            super(R.id.lb_control_skip_previous);
190            setIcon(getStyledDrawable(context,
191                    R.styleable.lbPlaybackControlsActionIcons_skip_previous));
192        }
193    }
194
195    /**
196     * An action displaying an icon for "more actions".
197     */
198    public static class MoreActions extends Action {
199        /**
200         * Constructor
201         * @param context Context used for loading resources.
202         */
203        public MoreActions(Context context) {
204            super(R.id.lb_control_more_actions);
205            setIcon(context.getResources().getDrawable(R.drawable.lb_ic_more));
206        }
207    }
208
209    /**
210     * A base class for displaying a thumbs action.
211     */
212    public static abstract class ThumbsAction extends MultiAction {
213        /**
214         * Action index for the solid thumb icon.
215         */
216        public static int SOLID = 0;
217
218        /**
219         * Action index for the outline thumb icon.
220         */
221        public static int OUTLINE = 1;
222
223        /**
224         * Constructor
225         * @param context Context used for loading resources.
226         */
227        public ThumbsAction(int id, Context context, int solidIconIndex, int outlineIconIndex) {
228            super(id);
229            Drawable[] drawables = new Drawable[2];
230            drawables[SOLID] = getStyledDrawable(context, solidIconIndex);
231            drawables[OUTLINE] = getStyledDrawable(context, outlineIconIndex);
232            setDrawables(drawables);
233        }
234    }
235
236    /**
237     * An action displaying an icon for thumbs up.
238     */
239    public static class ThumbsUpAction extends ThumbsAction {
240        public ThumbsUpAction(Context context) {
241            super(R.id.lb_control_thumbs_up, context,
242                    R.styleable.lbPlaybackControlsActionIcons_thumb_up,
243                    R.styleable.lbPlaybackControlsActionIcons_thumb_up_outline);
244        }
245    }
246
247    /**
248     * An action displaying an icon for thumbs down.
249     */
250    public static class ThumbsDownAction extends ThumbsAction {
251        public ThumbsDownAction(Context context) {
252            super(R.id.lb_control_thumbs_down, context,
253                    R.styleable.lbPlaybackControlsActionIcons_thumb_down,
254                    R.styleable.lbPlaybackControlsActionIcons_thumb_down_outline);
255        }
256    }
257
258     /**
259     * An action for displaying three repeat states: none, one, or all.
260     */
261    public static class RepeatAction extends MultiAction {
262        /**
263         * Action index for the repeat-none icon.
264         */
265        public static int NONE = 0;
266
267        /**
268         * Action index for the repeat-all icon.
269         */
270        public static int ALL = 1;
271
272        /**
273         * Action index for the repeat-one icon.
274         */
275        public static int ONE = 2;
276
277        /**
278         * Constructor
279         * @param context Context used for loading resources.
280         */
281        public RepeatAction(Context context) {
282            this(context, getColorFromTheme(context,
283                    R.attr.playbackControlsIconHighlightColor));
284        }
285
286        /**
287         * Constructor
288         * @param context Context used for loading resources
289         * @param highlightColor Color to display the repeat-all and repeat0one icons.
290         */
291        public RepeatAction(Context context, int highlightColor) {
292            this(context, highlightColor, highlightColor);
293        }
294
295        /**
296         * Constructor
297         * @param context Context used for loading resources
298         * @param repeatAllColor Color to display the repeat-all icon.
299         * @param repeatOneColor Color to display the repeat-one icon.
300         */
301        public RepeatAction(Context context, int repeatAllColor, int repeatOneColor) {
302            super(R.id.lb_control_repeat);
303            Drawable[] drawables = new Drawable[3];
304            BitmapDrawable repeatDrawable = (BitmapDrawable) getStyledDrawable(context,
305                    R.styleable.lbPlaybackControlsActionIcons_repeat);
306            BitmapDrawable repeatOneDrawable = (BitmapDrawable) getStyledDrawable(context,
307                    R.styleable.lbPlaybackControlsActionIcons_repeat_one);
308            drawables[NONE] = repeatDrawable;
309            drawables[ALL] = new BitmapDrawable(context.getResources(),
310                    createBitmap(repeatDrawable.getBitmap(), repeatAllColor));
311            drawables[ONE] = new BitmapDrawable(context.getResources(),
312                    createBitmap(repeatOneDrawable.getBitmap(), repeatOneColor));
313            setDrawables(drawables);
314        }
315    }
316
317    /**
318     * An action for displaying a shuffle icon.
319     */
320    public static class ShuffleAction extends MultiAction {
321        public static int OFF = 0;
322        public static int ON = 1;
323
324        /**
325         * Constructor
326         * @param context Context used for loading resources.
327         */
328        public ShuffleAction(Context context) {
329            this(context, getColorFromTheme(context,
330                    R.attr.playbackControlsIconHighlightColor));
331        }
332
333        /**
334         * Constructor
335         * @param context Context used for loading resources.
336         * @param highlightColor Color for the highlighted icon state.
337         */
338        public ShuffleAction(Context context, int highlightColor) {
339            super(R.id.lb_control_shuffle);
340            BitmapDrawable uncoloredDrawable = (BitmapDrawable) getStyledDrawable(context,
341                    R.styleable.lbPlaybackControlsActionIcons_shuffle);
342            Drawable[] drawables = new Drawable[2];
343            drawables[OFF] = uncoloredDrawable;
344            drawables[ON] = new BitmapDrawable(context.getResources(),
345                    createBitmap(uncoloredDrawable.getBitmap(), highlightColor));
346            setDrawables(drawables);
347        }
348    }
349
350    /**
351     * An action for displaying a HQ (High Quality) icon.
352     */
353    public static class HighQualityAction extends MultiAction {
354        public static int OFF = 0;
355        public static int ON = 1;
356
357        /**
358         * Constructor
359         * @param context Context used for loading resources.
360         */
361        public HighQualityAction(Context context) {
362            this(context, getColorFromTheme(context,
363                    R.attr.playbackControlsIconHighlightColor));
364        }
365
366        /**
367         * Constructor
368         * @param context Context used for loading resources.
369         * @param highlightColor Color for the highlighted icon state.
370         */
371        public HighQualityAction(Context context, int highlightColor) {
372            super(R.id.lb_control_high_quality);
373            BitmapDrawable uncoloredDrawable = (BitmapDrawable) getStyledDrawable(context,
374                    R.styleable.lbPlaybackControlsActionIcons_high_quality);
375            Drawable[] drawables = new Drawable[2];
376            drawables[OFF] = uncoloredDrawable;
377            drawables[ON] = new BitmapDrawable(context.getResources(),
378                    createBitmap(uncoloredDrawable.getBitmap(), highlightColor));
379            setDrawables(drawables);
380        }
381    }
382
383    /**
384     * An action for displaying a CC (Closed Captioning) icon.
385     */
386    public static class ClosedCaptioningAction extends MultiAction {
387        public static int OFF = 0;
388        public static int ON = 1;
389
390        /**
391         * Constructor
392         * @param context Context used for loading resources.
393         */
394        public ClosedCaptioningAction(Context context) {
395            this(context, getColorFromTheme(context,
396                    R.attr.playbackControlsIconHighlightColor));
397        }
398
399        /**
400         * Constructor
401         * @param context Context used for loading resources.
402         * @param highlightColor Color for the highlighted icon state.
403         */
404        public ClosedCaptioningAction(Context context, int highlightColor) {
405            super(R.id.lb_control_high_quality);
406            BitmapDrawable uncoloredDrawable = (BitmapDrawable) getStyledDrawable(context,
407                    R.styleable.lbPlaybackControlsActionIcons_closed_captioning);
408            Drawable[] drawables = new Drawable[2];
409            drawables[OFF] = uncoloredDrawable;
410            drawables[ON] = new BitmapDrawable(context.getResources(),
411                    createBitmap(uncoloredDrawable.getBitmap(), highlightColor));
412            setDrawables(drawables);
413        }
414    }
415
416    private static Bitmap createBitmap(Bitmap bitmap, int color) {
417        Bitmap dst = bitmap.copy(bitmap.getConfig(), true);
418        Canvas canvas = new Canvas(dst);
419        Paint paint = new Paint();
420        paint.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_ATOP));
421        canvas.drawBitmap(bitmap, 0, 0, paint);
422        return dst;
423    }
424
425    private static int getColorFromTheme(Context context, int attributeResId) {
426        TypedValue outValue = new TypedValue();
427        context.getTheme().resolveAttribute(attributeResId, outValue, true);
428        return outValue.data;
429    }
430
431    private static Drawable getStyledDrawable(Context context, int index) {
432        TypedValue outValue = new TypedValue();
433        context.getTheme().resolveAttribute(
434                R.attr.playbackControlsActionIcons, outValue, false);
435        TypedArray array = context.getTheme().obtainStyledAttributes(outValue.data,
436                R.styleable.lbPlaybackControlsActionIcons);
437        Drawable drawable = array.getDrawable(index);
438        array.recycle();
439        return drawable;
440    }
441
442    private Object mItem;
443    private Drawable mImageDrawable;
444    private ObjectAdapter mPrimaryActionsAdapter;
445    private ObjectAdapter mSecondaryActionsAdapter;
446    private int mTotalTimeMs;
447    private int mCurrentTimeMs;
448    private int mBufferedProgressMs;
449    private OnPlaybackStateChangedListener mListener;
450
451    /**
452     * Constructor for a PlaybackControlsRow that displays some details from
453     * the given item.
454     *
455     * @param item The main item for the row.
456     */
457    public PlaybackControlsRow(Object item) {
458        mItem = item;
459    }
460
461    /**
462     * Constructor for a PlaybackControlsRow that has no item details.
463     */
464    public PlaybackControlsRow() {
465    }
466
467    /**
468     * Gets the main item for the details page.
469     */
470    public final Object getItem() {
471        return mItem;
472    }
473
474    /**
475     * Sets a {link @Drawable} image for this row.
476     *
477     * @param drawable The drawable to set.
478     */
479    public final void setImageDrawable(Drawable drawable) {
480        mImageDrawable = drawable;
481    }
482
483    /**
484     * Sets a {@link Bitmap} for this row.
485     *
486     * @param context The context to retrieve display metrics from.
487     * @param bm The bitmap to set.
488     */
489    public final void setImageBitmap(Context context, Bitmap bm) {
490        mImageDrawable = new BitmapDrawable(context.getResources(), bm);
491    }
492
493    /**
494     * Gets the image {@link Drawable} of this row.
495     *
496     * @return The overview's image drawable, or null if no drawable has been
497     *         assigned.
498     */
499    public final Drawable getImageDrawable() {
500        return mImageDrawable;
501    }
502
503    /**
504     * Sets the primary actions {@link ObjectAdapter}.
505     */
506    public final void setPrimaryActionsAdapter(ObjectAdapter adapter) {
507        mPrimaryActionsAdapter = adapter;
508    }
509
510    /**
511     * Sets the secondary actions {@link ObjectAdapter}.
512     */
513    public final void setSecondaryActionsAdapter(ObjectAdapter adapter) {
514        mSecondaryActionsAdapter = adapter;
515    }
516
517    /**
518     * Returns the primary actions {@link ObjectAdapter}.
519     */
520    public final ObjectAdapter getPrimaryActionsAdapter() {
521        return mPrimaryActionsAdapter;
522    }
523
524    /**
525     * Returns the secondary actions {@link ObjectAdapter}.
526     */
527    public final ObjectAdapter getSecondaryActionsAdapter() {
528        return mSecondaryActionsAdapter;
529    }
530
531    /**
532     * Sets the total time in milliseconds for the playback controls row.
533     */
534    public void setTotalTime(int ms) {
535        mTotalTimeMs = ms;
536    }
537
538    /**
539     * Returns the total time in milliseconds for the playback controls row.
540     */
541    public int getTotalTime() {
542        return mTotalTimeMs;
543    }
544
545    /**
546     * Sets the current time in milliseconds for the playback controls row.
547     */
548    public void setCurrentTime(int ms) {
549        if (mCurrentTimeMs != ms) {
550            mCurrentTimeMs = ms;
551            currentTimeChanged();
552        }
553    }
554
555    /**
556     * Returns the current time in milliseconds for the playback controls row.
557     */
558    public int getCurrentTime() {
559        return mCurrentTimeMs;
560    }
561
562    /**
563     * Sets the buffered progress for the playback controls row.
564     */
565    public void setBufferedProgress(int ms) {
566        if (mBufferedProgressMs != ms) {
567            mBufferedProgressMs = ms;
568            bufferedProgressChanged();
569        }
570    }
571
572    /**
573     * Returns the buffered progress for the playback controls row.
574     */
575    public int getBufferedProgress() {
576        return mBufferedProgressMs;
577    }
578
579    interface OnPlaybackStateChangedListener {
580        public void onCurrentTimeChanged(int currentTimeMs);
581        public void onBufferedProgressChanged(int bufferedProgressMs);
582    }
583
584    /**
585     * Sets a listener to be called when the playback state changes.
586     */
587    public void setOnPlaybackStateChangedListener(OnPlaybackStateChangedListener listener) {
588        mListener = listener;
589    }
590
591    /**
592     * Returns the playback state listener.
593     */
594    public OnPlaybackStateChangedListener getOnPlaybackStateChangedListener() {
595        return mListener;
596    }
597
598    private void currentTimeChanged() {
599        if (mListener != null) {
600            mListener.onCurrentTimeChanged(mCurrentTimeMs);
601        }
602    }
603
604    private void bufferedProgressChanged() {
605        if (mListener != null) {
606            mListener.onBufferedProgressChanged(mBufferedProgressMs);
607        }
608    }
609}
610