PlaybackControlsRow.java revision cf992de2d34abb8228dc6cb39fffe97346823a37
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        @Override
135        public String toString() {
136            return "PlayPauseAction";
137        }
138    }
139
140    /**
141     * An action displaying an icon for fast forward.
142     */
143    public static class FastForwardAction extends Action {
144        /**
145         * Constructor
146         * @param context Context used for loading resources.
147         */
148        public FastForwardAction(Context context) {
149            super(R.id.lb_control_fast_forward);
150            setIcon(getStyledDrawable(context,
151                    R.styleable.lbPlaybackControlsActionIcons_fast_forward));
152        }
153
154        @Override
155        public String toString() {
156            return "FastForwardAction";
157        }
158    }
159
160    /**
161     * An action displaying an icon for rewind.
162     */
163    public static class RewindAction extends Action {
164        /**
165         * Constructor
166         * @param context Context used for loading resources.
167         */
168        public RewindAction(Context context) {
169            super(R.id.lb_control_fast_rewind);
170            setIcon(getStyledDrawable(context,
171                    R.styleable.lbPlaybackControlsActionIcons_rewind));
172        }
173
174        @Override
175        public String toString() {
176            return "RewindAction";
177        }
178    }
179
180    /**
181     * An action displaying an icon for skip next.
182     */
183    public static class SkipNextAction extends Action {
184        /**
185         * Constructor
186         * @param context Context used for loading resources.
187         */
188        public SkipNextAction(Context context) {
189            super(R.id.lb_control_skip_next);
190            setIcon(getStyledDrawable(context,
191                    R.styleable.lbPlaybackControlsActionIcons_skip_next));
192        }
193
194        @Override
195        public String toString() {
196            return "SkipNextAction";
197        }
198    }
199
200    /**
201     * An action displaying an icon for skip previous.
202     */
203    public static class SkipPreviousAction extends Action {
204        /**
205         * Constructor
206         * @param context Context used for loading resources.
207         */
208        public SkipPreviousAction(Context context) {
209            super(R.id.lb_control_skip_previous);
210            setIcon(getStyledDrawable(context,
211                    R.styleable.lbPlaybackControlsActionIcons_skip_previous));
212        }
213
214        @Override
215        public String toString() {
216            return "SkipPreviousAction";
217        }
218    }
219
220    /**
221     * An action displaying an icon for "more actions".
222     */
223    public static class MoreActions extends Action {
224        /**
225         * Constructor
226         * @param context Context used for loading resources.
227         */
228        public MoreActions(Context context) {
229            super(R.id.lb_control_more_actions);
230            setIcon(context.getResources().getDrawable(R.drawable.lb_ic_more));
231        }
232
233        @Override
234        public String toString() {
235            return "MoreActions";
236        }
237    }
238
239    /**
240     * A base class for displaying a thumbs action.
241     */
242    public static abstract class ThumbsAction extends MultiAction {
243        /**
244         * Action index for the solid thumb icon.
245         */
246        public static int SOLID = 0;
247
248        /**
249         * Action index for the outline thumb icon.
250         */
251        public static int OUTLINE = 1;
252
253        /**
254         * Constructor
255         * @param context Context used for loading resources.
256         */
257        public ThumbsAction(int id, Context context, int solidIconIndex, int outlineIconIndex) {
258            super(id);
259            Drawable[] drawables = new Drawable[2];
260            drawables[SOLID] = getStyledDrawable(context, solidIconIndex);
261            drawables[OUTLINE] = getStyledDrawable(context, outlineIconIndex);
262            setDrawables(drawables);
263        }
264    }
265
266    /**
267     * An action displaying an icon for thumbs up.
268     */
269    public static class ThumbsUpAction extends ThumbsAction {
270        public ThumbsUpAction(Context context) {
271            super(R.id.lb_control_thumbs_up, context,
272                    R.styleable.lbPlaybackControlsActionIcons_thumb_up,
273                    R.styleable.lbPlaybackControlsActionIcons_thumb_up_outline);
274        }
275
276        @Override
277        public String toString() {
278            return "ThumbsUpAction";
279        }
280    }
281
282    /**
283     * An action displaying an icon for thumbs down.
284     */
285    public static class ThumbsDownAction extends ThumbsAction {
286        public ThumbsDownAction(Context context) {
287            super(R.id.lb_control_thumbs_down, context,
288                    R.styleable.lbPlaybackControlsActionIcons_thumb_down,
289                    R.styleable.lbPlaybackControlsActionIcons_thumb_down_outline);
290        }
291
292        @Override
293        public String toString() {
294            return "ThumbsDownAction";
295        }
296    }
297
298    /**
299     * An action for displaying three repeat states: none, one, or all.
300     */
301    public static class RepeatAction extends MultiAction {
302        /**
303         * Action index for the repeat-none icon.
304         */
305        public static int NONE = 0;
306
307        /**
308         * Action index for the repeat-all icon.
309         */
310        public static int ALL = 1;
311
312        /**
313         * Action index for the repeat-one icon.
314         */
315        public static int ONE = 2;
316
317        /**
318         * Constructor
319         * @param context Context used for loading resources.
320         */
321        public RepeatAction(Context context) {
322            this(context, getColorFromTheme(context,
323                    R.attr.playbackControlsIconHighlightColor));
324        }
325
326        /**
327         * Constructor
328         * @param context Context used for loading resources
329         * @param highlightColor Color to display the repeat-all and repeat0one icons.
330         */
331        public RepeatAction(Context context, int highlightColor) {
332            this(context, highlightColor, highlightColor);
333        }
334
335        /**
336         * Constructor
337         * @param context Context used for loading resources
338         * @param repeatAllColor Color to display the repeat-all icon.
339         * @param repeatOneColor Color to display the repeat-one icon.
340         */
341        public RepeatAction(Context context, int repeatAllColor, int repeatOneColor) {
342            super(R.id.lb_control_repeat);
343            Drawable[] drawables = new Drawable[3];
344            BitmapDrawable repeatDrawable = (BitmapDrawable) getStyledDrawable(context,
345                    R.styleable.lbPlaybackControlsActionIcons_repeat);
346            BitmapDrawable repeatOneDrawable = (BitmapDrawable) getStyledDrawable(context,
347                    R.styleable.lbPlaybackControlsActionIcons_repeat_one);
348            drawables[NONE] = repeatDrawable;
349            drawables[ALL] = new BitmapDrawable(context.getResources(),
350                    createBitmap(repeatDrawable.getBitmap(), repeatAllColor));
351            drawables[ONE] = new BitmapDrawable(context.getResources(),
352                    createBitmap(repeatOneDrawable.getBitmap(), repeatOneColor));
353            setDrawables(drawables);
354        }
355
356        @Override
357        public String toString() {
358            return "RepeatAction";
359        }
360    }
361
362    /**
363     * An action for displaying a shuffle icon.
364     */
365    public static class ShuffleAction extends MultiAction {
366        public static int OFF = 0;
367        public static int ON = 1;
368
369        /**
370         * Constructor
371         * @param context Context used for loading resources.
372         */
373        public ShuffleAction(Context context) {
374            this(context, getColorFromTheme(context,
375                    R.attr.playbackControlsIconHighlightColor));
376        }
377
378        /**
379         * Constructor
380         * @param context Context used for loading resources.
381         * @param highlightColor Color for the highlighted icon state.
382         */
383        public ShuffleAction(Context context, int highlightColor) {
384            super(R.id.lb_control_shuffle);
385            BitmapDrawable uncoloredDrawable = (BitmapDrawable) getStyledDrawable(context,
386                    R.styleable.lbPlaybackControlsActionIcons_shuffle);
387            Drawable[] drawables = new Drawable[2];
388            drawables[OFF] = uncoloredDrawable;
389            drawables[ON] = new BitmapDrawable(context.getResources(),
390                    createBitmap(uncoloredDrawable.getBitmap(), highlightColor));
391            setDrawables(drawables);
392        }
393
394        @Override
395        public String toString() {
396            return "ShuffleAction";
397        }
398    }
399
400    /**
401     * An action for displaying a HQ (High Quality) icon.
402     */
403    public static class HighQualityAction extends MultiAction {
404        public static int OFF = 0;
405        public static int ON = 1;
406
407        /**
408         * Constructor
409         * @param context Context used for loading resources.
410         */
411        public HighQualityAction(Context context) {
412            this(context, getColorFromTheme(context,
413                    R.attr.playbackControlsIconHighlightColor));
414        }
415
416        /**
417         * Constructor
418         * @param context Context used for loading resources.
419         * @param highlightColor Color for the highlighted icon state.
420         */
421        public HighQualityAction(Context context, int highlightColor) {
422            super(R.id.lb_control_high_quality);
423            BitmapDrawable uncoloredDrawable = (BitmapDrawable) getStyledDrawable(context,
424                    R.styleable.lbPlaybackControlsActionIcons_high_quality);
425            Drawable[] drawables = new Drawable[2];
426            drawables[OFF] = uncoloredDrawable;
427            drawables[ON] = new BitmapDrawable(context.getResources(),
428                    createBitmap(uncoloredDrawable.getBitmap(), highlightColor));
429            setDrawables(drawables);
430        }
431
432        @Override
433        public String toString() {
434            return "HighQualityAction";
435        }
436    }
437
438    /**
439     * An action for displaying a CC (Closed Captioning) icon.
440     */
441    public static class ClosedCaptioningAction extends MultiAction {
442        public static int OFF = 0;
443        public static int ON = 1;
444
445        /**
446         * Constructor
447         * @param context Context used for loading resources.
448         */
449        public ClosedCaptioningAction(Context context) {
450            this(context, getColorFromTheme(context,
451                    R.attr.playbackControlsIconHighlightColor));
452        }
453
454        /**
455         * Constructor
456         * @param context Context used for loading resources.
457         * @param highlightColor Color for the highlighted icon state.
458         */
459        public ClosedCaptioningAction(Context context, int highlightColor) {
460            super(R.id.lb_control_high_quality);
461            BitmapDrawable uncoloredDrawable = (BitmapDrawable) getStyledDrawable(context,
462                    R.styleable.lbPlaybackControlsActionIcons_closed_captioning);
463            Drawable[] drawables = new Drawable[2];
464            drawables[OFF] = uncoloredDrawable;
465            drawables[ON] = new BitmapDrawable(context.getResources(),
466                    createBitmap(uncoloredDrawable.getBitmap(), highlightColor));
467            setDrawables(drawables);
468        }
469
470        @Override
471        public String toString() {
472            return "ClosedCaptioningAction";
473        }
474    }
475
476    private static Bitmap createBitmap(Bitmap bitmap, int color) {
477        Bitmap dst = bitmap.copy(bitmap.getConfig(), true);
478        Canvas canvas = new Canvas(dst);
479        Paint paint = new Paint();
480        paint.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_ATOP));
481        canvas.drawBitmap(bitmap, 0, 0, paint);
482        return dst;
483    }
484
485    private static int getColorFromTheme(Context context, int attributeResId) {
486        TypedValue outValue = new TypedValue();
487        context.getTheme().resolveAttribute(attributeResId, outValue, true);
488        return outValue.data;
489    }
490
491    private static Drawable getStyledDrawable(Context context, int index) {
492        TypedValue outValue = new TypedValue();
493        context.getTheme().resolveAttribute(
494                R.attr.playbackControlsActionIcons, outValue, false);
495        TypedArray array = context.getTheme().obtainStyledAttributes(outValue.data,
496                R.styleable.lbPlaybackControlsActionIcons);
497        Drawable drawable = array.getDrawable(index);
498        array.recycle();
499        return drawable;
500    }
501
502    private Object mItem;
503    private Drawable mImageDrawable;
504    private ObjectAdapter mPrimaryActionsAdapter;
505    private ObjectAdapter mSecondaryActionsAdapter;
506    private int mTotalTimeMs;
507    private int mCurrentTimeMs;
508    private int mBufferedProgressMs;
509    private OnPlaybackStateChangedListener mListener;
510
511    /**
512     * Constructor for a PlaybackControlsRow that displays some details from
513     * the given item.
514     *
515     * @param item The main item for the row.
516     */
517    public PlaybackControlsRow(Object item) {
518        mItem = item;
519    }
520
521    /**
522     * Constructor for a PlaybackControlsRow that has no item details.
523     */
524    public PlaybackControlsRow() {
525    }
526
527    /**
528     * Gets the main item for the details page.
529     */
530    public final Object getItem() {
531        return mItem;
532    }
533
534    /**
535     * Sets a {link @Drawable} image for this row.
536     *
537     * @param drawable The drawable to set.
538     */
539    public final void setImageDrawable(Drawable drawable) {
540        mImageDrawable = drawable;
541    }
542
543    /**
544     * Sets a {@link Bitmap} for this row.
545     *
546     * @param context The context to retrieve display metrics from.
547     * @param bm The bitmap to set.
548     */
549    public final void setImageBitmap(Context context, Bitmap bm) {
550        mImageDrawable = new BitmapDrawable(context.getResources(), bm);
551    }
552
553    /**
554     * Gets the image {@link Drawable} of this row.
555     *
556     * @return The overview's image drawable, or null if no drawable has been
557     *         assigned.
558     */
559    public final Drawable getImageDrawable() {
560        return mImageDrawable;
561    }
562
563    /**
564     * Sets the primary actions {@link ObjectAdapter}.
565     */
566    public final void setPrimaryActionsAdapter(ObjectAdapter adapter) {
567        mPrimaryActionsAdapter = adapter;
568    }
569
570    /**
571     * Sets the secondary actions {@link ObjectAdapter}.
572     */
573    public final void setSecondaryActionsAdapter(ObjectAdapter adapter) {
574        mSecondaryActionsAdapter = adapter;
575    }
576
577    /**
578     * Returns the primary actions {@link ObjectAdapter}.
579     */
580    public final ObjectAdapter getPrimaryActionsAdapter() {
581        return mPrimaryActionsAdapter;
582    }
583
584    /**
585     * Returns the secondary actions {@link ObjectAdapter}.
586     */
587    public final ObjectAdapter getSecondaryActionsAdapter() {
588        return mSecondaryActionsAdapter;
589    }
590
591    /**
592     * Sets the total time in milliseconds for the playback controls row.
593     */
594    public void setTotalTime(int ms) {
595        mTotalTimeMs = ms;
596    }
597
598    /**
599     * Returns the total time in milliseconds for the playback controls row.
600     */
601    public int getTotalTime() {
602        return mTotalTimeMs;
603    }
604
605    /**
606     * Sets the current time in milliseconds for the playback controls row.
607     */
608    public void setCurrentTime(int ms) {
609        if (mCurrentTimeMs != ms) {
610            mCurrentTimeMs = ms;
611            currentTimeChanged();
612        }
613    }
614
615    /**
616     * Returns the current time in milliseconds for the playback controls row.
617     */
618    public int getCurrentTime() {
619        return mCurrentTimeMs;
620    }
621
622    /**
623     * Sets the buffered progress for the playback controls row.
624     */
625    public void setBufferedProgress(int ms) {
626        if (mBufferedProgressMs != ms) {
627            mBufferedProgressMs = ms;
628            bufferedProgressChanged();
629        }
630    }
631
632    /**
633     * Returns the buffered progress for the playback controls row.
634     */
635    public int getBufferedProgress() {
636        return mBufferedProgressMs;
637    }
638
639    interface OnPlaybackStateChangedListener {
640        public void onCurrentTimeChanged(int currentTimeMs);
641        public void onBufferedProgressChanged(int bufferedProgressMs);
642    }
643
644    /**
645     * Sets a listener to be called when the playback state changes.
646     */
647    public void setOnPlaybackStateChangedListener(OnPlaybackStateChangedListener listener) {
648        mListener = listener;
649    }
650
651    /**
652     * Returns the playback state listener.
653     */
654    public OnPlaybackStateChangedListener getOnPlaybackStateChangedListener() {
655        return mListener;
656    }
657
658    private void currentTimeChanged() {
659        if (mListener != null) {
660            mListener.onCurrentTimeChanged(mCurrentTimeMs);
661        }
662    }
663
664    private void bufferedProgressChanged() {
665        if (mListener != null) {
666            mListener.onBufferedProgressChanged(mBufferedProgressMs);
667        }
668    }
669}
670