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