• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
14 package android.support.v17.leanback.widget;
15 
16 import android.content.Context;
17 import android.content.res.TypedArray;
18 import android.graphics.Bitmap;
19 import android.graphics.Canvas;
20 import android.graphics.Paint;
21 import android.graphics.PorterDuff;
22 import android.graphics.PorterDuffColorFilter;
23 import android.graphics.drawable.BitmapDrawable;
24 import android.graphics.drawable.Drawable;
25 import android.support.v17.leanback.R;
26 import android.support.v17.leanback.util.MathUtil;
27 import android.util.TypedValue;
28 import android.view.KeyEvent;
29 
30 /**
31  * A {@link 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  * <p>
37  * Controls are specified via an {@link ObjectAdapter} containing one or more
38  * {@link Action}s.
39  * </p>
40  * <p>
41  * Adapters should have their {@link PresenterSelector} set to an instance of
42  * {@link ControlButtonPresenterSelector}.
43  * </p>
44  */
45 public class PlaybackControlsRow extends Row {
46 
47     /**
48      * Listener for progress or duration change.
49      */
50     public static class OnPlaybackProgressCallback {
51         /**
52          * Called when {@link PlaybackControlsRow#getCurrentPosition()} changed.
53          * @param row The PlaybackControlsRow that current time changed.
54          * @param currentTimeMs Current time in milliseconds.
55          */
onCurrentPositionChanged(PlaybackControlsRow row, long currentTimeMs)56         public void onCurrentPositionChanged(PlaybackControlsRow row, long currentTimeMs) {
57         }
58 
59         /**
60          * Called when {@link PlaybackControlsRow#getDuration()} changed.
61          * @param row The PlaybackControlsRow that total time changed.
62          * @param totalTime Total time in milliseconds.
63          */
onDurationChanged(PlaybackControlsRow row, long totalTime)64         public void onDurationChanged(PlaybackControlsRow row, long totalTime) {
65         }
66 
67         /**
68          * Called when {@link PlaybackControlsRow#getBufferedPosition()} changed.
69          * @param row The PlaybackControlsRow that buffered progress changed.
70          * @param bufferedProgressMs Buffered time in milliseconds.
71          */
onBufferedPositionChanged(PlaybackControlsRow row, long bufferedProgressMs)72         public void onBufferedPositionChanged(PlaybackControlsRow row, long bufferedProgressMs) {
73         }
74     }
75 
76     /**
77      * Base class for an action comprised of a series of icons.
78      */
79     public static abstract class MultiAction extends Action {
80         private int mIndex;
81         private Drawable[] mDrawables;
82         private String[] mLabels;
83         private String[] mLabels2;
84 
85         /**
86          * Constructor
87          * @param id The id of the Action.
88          */
MultiAction(int id)89         public MultiAction(int id) {
90             super(id);
91         }
92 
93         /**
94          * Sets the array of drawables.  The size of the array defines the range
95          * of valid indices for this action.
96          */
setDrawables(Drawable[] drawables)97         public void setDrawables(Drawable[] drawables) {
98             mDrawables = drawables;
99             setIndex(0);
100         }
101 
102         /**
103          * Sets the array of strings used as labels.  The size of the array defines the range
104          * of valid indices for this action.  The labels are used to define the accessibility
105          * content description unless secondary labels are provided.
106          */
setLabels(String[] labels)107         public void setLabels(String[] labels) {
108             mLabels = labels;
109             setIndex(0);
110         }
111 
112         /**
113          * Sets the array of strings used as secondary labels.  These labels are used
114          * in place of the primary labels for accessibility content description only.
115          */
setSecondaryLabels(String[] labels)116         public void setSecondaryLabels(String[] labels) {
117             mLabels2 = labels;
118             setIndex(0);
119         }
120 
121         /**
122          * Returns the number of actions.
123          */
getActionCount()124         public int getActionCount() {
125             if (mDrawables != null) {
126                 return mDrawables.length;
127             }
128             if (mLabels != null) {
129                 return mLabels.length;
130             }
131             return 0;
132         }
133 
134         /**
135          * Returns the drawable at the given index.
136          */
getDrawable(int index)137         public Drawable getDrawable(int index) {
138             return mDrawables == null ? null : mDrawables[index];
139         }
140 
141         /**
142          * Returns the label at the given index.
143          */
getLabel(int index)144         public String getLabel(int index) {
145             return mLabels == null ? null : mLabels[index];
146         }
147 
148         /**
149          * Returns the secondary label at the given index.
150          */
getSecondaryLabel(int index)151         public String getSecondaryLabel(int index) {
152             return mLabels2 == null ? null : mLabels2[index];
153         }
154 
155         /**
156          * Increments the index, wrapping to zero once the end is reached.
157          */
nextIndex()158         public void nextIndex() {
159             setIndex(mIndex < getActionCount() - 1 ? mIndex + 1 : 0);
160         }
161 
162         /**
163          * Sets the current index.
164          */
165         public void setIndex(int index) {
166             mIndex = index;
167             if (mDrawables != null) {
168                 setIcon(mDrawables[mIndex]);
169             }
170             if (mLabels != null) {
171                 setLabel1(mLabels[mIndex]);
172             }
173             if (mLabels2 != null) {
174                 setLabel2(mLabels2[mIndex]);
175             }
176         }
177 
178         /**
179          * Returns the current index.
180          */
181         public int getIndex() {
182             return mIndex;
183         }
184     }
185 
186     /**
187      * An action displaying icons for play and pause.
188      */
189     public static class PlayPauseAction extends MultiAction {
190         /**
191          * Action index for the play icon.
192          * @deprecated Use {@link #INDEX_PLAY}
193          */
194         @Deprecated
195         public static int PLAY = 0;
196 
197         /**
198          * Action index for the pause icon.
199          * @deprecated Use {@link #INDEX_PAUSE}
200          */
201         @Deprecated
202         public static int PAUSE = 1;
203 
204         /**
205          * Action index for the play icon.
206          */
207         public static final int INDEX_PLAY = 0;
208 
209         /**
210          * Action index for the pause icon.
211          */
212         public static final int INDEX_PAUSE = 1;
213 
214         /**
215          * Constructor
216          * @param context Context used for loading resources.
217          */
218         public PlayPauseAction(Context context) {
219             super(R.id.lb_control_play_pause);
220             Drawable[] drawables = new Drawable[2];
221             drawables[INDEX_PLAY] = getStyledDrawable(context,
222                     R.styleable.lbPlaybackControlsActionIcons_play);
223             drawables[INDEX_PAUSE] = getStyledDrawable(context,
224                     R.styleable.lbPlaybackControlsActionIcons_pause);
225             setDrawables(drawables);
226 
227             String[] labels = new String[drawables.length];
228             labels[INDEX_PLAY] = context.getString(R.string.lb_playback_controls_play);
229             labels[INDEX_PAUSE] = context.getString(R.string.lb_playback_controls_pause);
230             setLabels(labels);
231             addKeyCode(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
232             addKeyCode(KeyEvent.KEYCODE_MEDIA_PLAY);
233             addKeyCode(KeyEvent.KEYCODE_MEDIA_PAUSE);
234         }
235     }
236 
237     /**
238      * An action displaying an icon for fast forward.
239      */
240     public static class FastForwardAction extends MultiAction {
241         /**
242          * Constructor
243          * @param context Context used for loading resources.
244          */
245         public FastForwardAction(Context context) {
246             this(context, 1);
247         }
248 
249         /**
250          * Constructor
251          * @param context Context used for loading resources.
252          * @param numSpeeds Number of supported fast forward speeds.
253          */
254         public FastForwardAction(Context context, int numSpeeds) {
255             super(R.id.lb_control_fast_forward);
256 
257             if (numSpeeds < 1) {
258                 throw new IllegalArgumentException("numSpeeds must be > 0");
259             }
260             Drawable[] drawables = new Drawable[numSpeeds + 1];
261             drawables[0] = getStyledDrawable(context,
262                     R.styleable.lbPlaybackControlsActionIcons_fast_forward);
263             setDrawables(drawables);
264 
265             String[] labels = new String[getActionCount()];
266             labels[0] = context.getString(R.string.lb_playback_controls_fast_forward);
267 
268             String[] labels2 = new String[getActionCount()];
269             labels2[0] = labels[0];
270 
271             for (int i = 1; i <= numSpeeds; i++) {
272                 int multiplier = i + 1;
273                 labels[i] = context.getResources().getString(
274                         R.string.lb_control_display_fast_forward_multiplier, multiplier);
275                 labels2[i] = context.getResources().getString(
276                         R.string.lb_playback_controls_fast_forward_multiplier, multiplier);
277             }
278             setLabels(labels);
279             setSecondaryLabels(labels2);
280             addKeyCode(KeyEvent.KEYCODE_MEDIA_FAST_FORWARD);
281         }
282     }
283 
284     /**
285      * An action displaying an icon for rewind.
286      */
287     public static class RewindAction extends MultiAction {
288         /**
289          * Constructor
290          * @param context Context used for loading resources.
291          */
292         public RewindAction(Context context) {
293             this(context, 1);
294         }
295 
296         /**
297          * Constructor
298          * @param context Context used for loading resources.
299          * @param numSpeeds Number of supported fast forward speeds.
300          */
301         public RewindAction(Context context, int numSpeeds) {
302             super(R.id.lb_control_fast_rewind);
303 
304             if (numSpeeds < 1) {
305                 throw new IllegalArgumentException("numSpeeds must be > 0");
306             }
307             Drawable[] drawables = new Drawable[numSpeeds + 1];
308             drawables[0] = getStyledDrawable(context,
309                     R.styleable.lbPlaybackControlsActionIcons_rewind);
310             setDrawables(drawables);
311 
312             String[] labels = new String[getActionCount()];
313             labels[0] = context.getString(R.string.lb_playback_controls_rewind);
314 
315             String[] labels2 = new String[getActionCount()];
316             labels2[0] = labels[0];
317 
318             for (int i = 1; i <= numSpeeds; i++) {
319                 int multiplier = i + 1;
320                 labels[i] = labels[i] = context.getResources().getString(
321                         R.string.lb_control_display_rewind_multiplier, multiplier);
322                 labels2[i] = context.getResources().getString(
323                         R.string.lb_playback_controls_rewind_multiplier, multiplier);
324             }
325             setLabels(labels);
326             setSecondaryLabels(labels2);
327             addKeyCode(KeyEvent.KEYCODE_MEDIA_REWIND);
328         }
329     }
330 
331     /**
332      * An action displaying an icon for skip next.
333      */
334     public static class SkipNextAction extends Action {
335         /**
336          * Constructor
337          * @param context Context used for loading resources.
338          */
339         public SkipNextAction(Context context) {
340             super(R.id.lb_control_skip_next);
341             setIcon(getStyledDrawable(context,
342                     R.styleable.lbPlaybackControlsActionIcons_skip_next));
343             setLabel1(context.getString(R.string.lb_playback_controls_skip_next));
344             addKeyCode(KeyEvent.KEYCODE_MEDIA_NEXT);
345         }
346     }
347 
348     /**
349      * An action displaying an icon for skip previous.
350      */
351     public static class SkipPreviousAction extends Action {
352         /**
353          * Constructor
354          * @param context Context used for loading resources.
355          */
356         public SkipPreviousAction(Context context) {
357             super(R.id.lb_control_skip_previous);
358             setIcon(getStyledDrawable(context,
359                     R.styleable.lbPlaybackControlsActionIcons_skip_previous));
360             setLabel1(context.getString(R.string.lb_playback_controls_skip_previous));
361             addKeyCode(KeyEvent.KEYCODE_MEDIA_PREVIOUS);
362         }
363     }
364 
365     /**
366      * An action displaying an icon for picture-in-picture.
367      */
368     public static class PictureInPictureAction extends Action {
369         /**
370          * Constructor
371          * @param context Context used for loading resources.
372          */
373         public PictureInPictureAction(Context context) {
374             super(R.id.lb_control_picture_in_picture);
375             setIcon(getStyledDrawable(context,
376                     R.styleable.lbPlaybackControlsActionIcons_picture_in_picture));
377             setLabel1(context.getString(R.string.lb_playback_controls_picture_in_picture));
378             addKeyCode(KeyEvent.KEYCODE_WINDOW);
379         }
380     }
381 
382     /**
383      * An action displaying an icon for "more actions".
384      */
385     public static class MoreActions extends Action {
386         /**
387          * Constructor
388          * @param context Context used for loading resources.
389          */
390         public MoreActions(Context context) {
391             super(R.id.lb_control_more_actions);
392             setIcon(context.getResources().getDrawable(R.drawable.lb_ic_more));
393             setLabel1(context.getString(R.string.lb_playback_controls_more_actions));
394         }
395     }
396 
397     /**
398      * A base class for displaying a thumbs action.
399      */
400     public static abstract class ThumbsAction extends MultiAction {
401         /**
402          * Action index for the solid thumb icon.
403          * @deprecated Use {@link #INDEX_SOLID}
404          */
405         @Deprecated
406         public static int SOLID = 0;
407 
408         /**
409          * Action index for the outline thumb icon.
410          * @deprecated Use {@link #INDEX_OUTLINE}
411          */
412         @Deprecated
413         public static int OUTLINE = 1;
414 
415         /**
416          * Action index for the solid thumb icon.
417          */
418         public static final int INDEX_SOLID = 0;
419 
420         /**
421          * Action index for the outline thumb icon.
422          */
423         public static final int INDEX_OUTLINE = 1;
424 
425         /**
426          * Constructor
427          * @param context Context used for loading resources.
428          */
429         public ThumbsAction(int id, Context context, int solidIconIndex, int outlineIconIndex) {
430             super(id);
431             Drawable[] drawables = new Drawable[2];
432             drawables[INDEX_SOLID] = getStyledDrawable(context, solidIconIndex);
433             drawables[INDEX_OUTLINE] = getStyledDrawable(context, outlineIconIndex);
434             setDrawables(drawables);
435         }
436     }
437 
438     /**
439      * An action displaying an icon for thumbs up.
440      */
441     public static class ThumbsUpAction extends ThumbsAction {
442         public ThumbsUpAction(Context context) {
443             super(R.id.lb_control_thumbs_up, context,
444                     R.styleable.lbPlaybackControlsActionIcons_thumb_up,
445                     R.styleable.lbPlaybackControlsActionIcons_thumb_up_outline);
446             String[] labels = new String[getActionCount()];
447             labels[INDEX_SOLID] = context.getString(R.string.lb_playback_controls_thumb_up);
448             labels[INDEX_OUTLINE] = context.getString(
449                     R.string.lb_playback_controls_thumb_up_outline);
450             setLabels(labels);
451         }
452     }
453 
454     /**
455      * An action displaying an icon for thumbs down.
456      */
457     public static class ThumbsDownAction extends ThumbsAction {
458         public ThumbsDownAction(Context context) {
459             super(R.id.lb_control_thumbs_down, context,
460                     R.styleable.lbPlaybackControlsActionIcons_thumb_down,
461                     R.styleable.lbPlaybackControlsActionIcons_thumb_down_outline);
462             String[] labels = new String[getActionCount()];
463             labels[INDEX_SOLID] = context.getString(R.string.lb_playback_controls_thumb_down);
464             labels[INDEX_OUTLINE] = context.getString(
465                     R.string.lb_playback_controls_thumb_down_outline);
466             setLabels(labels);
467         }
468     }
469 
470     /**
471      * An action for displaying three repeat states: none, one, or all.
472      */
473     public static class RepeatAction extends MultiAction {
474         /**
475          * Action index for the repeat-none icon.
476          * @deprecated Use {@link #INDEX_NONE}
477          */
478         @Deprecated
479         public static int NONE = 0;
480 
481         /**
482          * Action index for the repeat-all icon.
483          * @deprecated Use {@link #INDEX_ALL}
484          */
485         @Deprecated
486         public static int ALL = 1;
487 
488         /**
489          * Action index for the repeat-one icon.
490          * @deprecated Use {@link #INDEX_ONE}
491          */
492         @Deprecated
493         public static int ONE = 2;
494 
495         /**
496          * Action index for the repeat-none icon.
497          */
498         public static final int INDEX_NONE = 0;
499 
500         /**
501          * Action index for the repeat-all icon.
502          */
503         public static final int INDEX_ALL = 1;
504 
505         /**
506          * Action index for the repeat-one icon.
507          */
508         public static final int INDEX_ONE = 2;
509 
510         /**
511          * Constructor
512          * @param context Context used for loading resources.
513          */
514         public RepeatAction(Context context) {
515             this(context, getIconHighlightColor(context));
516         }
517 
518         /**
519          * Constructor
520          * @param context Context used for loading resources
521          * @param highlightColor Color to display the repeat-all and repeat0one icons.
522          */
523         public RepeatAction(Context context, int highlightColor) {
524             this(context, highlightColor, highlightColor);
525         }
526 
527         /**
528          * Constructor
529          * @param context Context used for loading resources
530          * @param repeatAllColor Color to display the repeat-all icon.
531          * @param repeatOneColor Color to display the repeat-one icon.
532          */
533         public RepeatAction(Context context, int repeatAllColor, int repeatOneColor) {
534             super(R.id.lb_control_repeat);
535             Drawable[] drawables = new Drawable[3];
536             BitmapDrawable repeatDrawable = (BitmapDrawable) getStyledDrawable(context,
537                     R.styleable.lbPlaybackControlsActionIcons_repeat);
538             BitmapDrawable repeatOneDrawable = (BitmapDrawable) getStyledDrawable(context,
539                     R.styleable.lbPlaybackControlsActionIcons_repeat_one);
540             drawables[INDEX_NONE] = repeatDrawable;
541             drawables[INDEX_ALL] = repeatDrawable == null ? null
542                     : new BitmapDrawable(context.getResources(),
543                             createBitmap(repeatDrawable.getBitmap(), repeatAllColor));
544             drawables[INDEX_ONE] = repeatOneDrawable == null ? null
545                     : new BitmapDrawable(context.getResources(),
546                             createBitmap(repeatOneDrawable.getBitmap(), repeatOneColor));
547             setDrawables(drawables);
548 
549             String[] labels = new String[drawables.length];
550             // Note, labels denote the action taken when clicked
551             labels[INDEX_NONE] = context.getString(R.string.lb_playback_controls_repeat_all);
552             labels[INDEX_ALL] = context.getString(R.string.lb_playback_controls_repeat_one);
553             labels[INDEX_ONE] = context.getString(R.string.lb_playback_controls_repeat_none);
554             setLabels(labels);
555         }
556     }
557 
558     /**
559      * An action for displaying a shuffle icon.
560      */
561     public static class ShuffleAction extends MultiAction {
562         /**
563          * Action index for shuffle is off.
564          * @deprecated Use {@link #INDEX_OFF}
565          */
566         @Deprecated
567         public static int OFF = 0;
568 
569         /**
570          * Action index for shuffle is on.
571          * @deprecated Use {@link #INDEX_ON}
572          */
573         @Deprecated
574         public static int ON = 1;
575 
576         /**
577          * Action index for shuffle is off
578          */
579         public static final int INDEX_OFF = 0;
580 
581         /**
582          * Action index for shuffle is on.
583          */
584         public static final int INDEX_ON = 1;
585 
586         /**
587          * Constructor
588          * @param context Context used for loading resources.
589          */
590         public ShuffleAction(Context context) {
591             this(context, getIconHighlightColor(context));
592         }
593 
594         /**
595          * Constructor
596          * @param context Context used for loading resources.
597          * @param highlightColor Color for the highlighted icon state.
598          */
599         public ShuffleAction(Context context, int highlightColor) {
600             super(R.id.lb_control_shuffle);
601             BitmapDrawable uncoloredDrawable = (BitmapDrawable) getStyledDrawable(context,
602                     R.styleable.lbPlaybackControlsActionIcons_shuffle);
603             Drawable[] drawables = new Drawable[2];
604             drawables[INDEX_OFF] = uncoloredDrawable;
605             drawables[INDEX_ON] = new BitmapDrawable(context.getResources(),
606                     createBitmap(uncoloredDrawable.getBitmap(), highlightColor));
607             setDrawables(drawables);
608 
609             String[] labels = new String[drawables.length];
610             labels[INDEX_OFF] = context.getString(R.string.lb_playback_controls_shuffle_enable);
611             labels[INDEX_ON] = context.getString(R.string.lb_playback_controls_shuffle_disable);
612             setLabels(labels);
613         }
614     }
615 
616     /**
617      * An action for displaying a HQ (High Quality) icon.
618      */
619     public static class HighQualityAction extends MultiAction {
620         /**
621          * Action index for high quality is off.
622          * @deprecated Use {@link #INDEX_OFF}
623          */
624         @Deprecated
625         public static int OFF = 0;
626 
627         /**
628          * Action index for high quality is on.
629          * @deprecated Use {@link #INDEX_ON}
630          */
631         @Deprecated
632         public static int ON = 1;
633 
634         /**
635          * Action index for high quality is off.
636          */
637         public static final int INDEX_OFF = 0;
638 
639         /**
640          * Action index for high quality is on.
641          */
642         public static final int INDEX_ON = 1;
643 
644         /**
645          * Constructor
646          * @param context Context used for loading resources.
647          */
648         public HighQualityAction(Context context) {
649             this(context, getIconHighlightColor(context));
650         }
651 
652         /**
653          * Constructor
654          * @param context Context used for loading resources.
655          * @param highlightColor Color for the highlighted icon state.
656          */
657         public HighQualityAction(Context context, int highlightColor) {
658             super(R.id.lb_control_high_quality);
659             BitmapDrawable uncoloredDrawable = (BitmapDrawable) getStyledDrawable(context,
660                     R.styleable.lbPlaybackControlsActionIcons_high_quality);
661             Drawable[] drawables = new Drawable[2];
662             drawables[INDEX_OFF] = uncoloredDrawable;
663             drawables[INDEX_ON] = new BitmapDrawable(context.getResources(),
664                     createBitmap(uncoloredDrawable.getBitmap(), highlightColor));
665             setDrawables(drawables);
666 
667             String[] labels = new String[drawables.length];
668             labels[INDEX_OFF] = context.getString(
669                     R.string.lb_playback_controls_high_quality_enable);
670             labels[INDEX_ON] = context.getString(
671                     R.string.lb_playback_controls_high_quality_disable);
672             setLabels(labels);
673         }
674     }
675 
676     /**
677      * An action for displaying a CC (Closed Captioning) icon.
678      */
679     public static class ClosedCaptioningAction extends MultiAction {
680         /**
681          * Action index for closed caption is off.
682          * @deprecated Use {@link #INDEX_OFF}
683          */
684         @Deprecated
685         public static int OFF = 0;
686 
687         /**
688          * Action index for closed caption is on.
689          * @deprecated Use {@link #INDEX_ON}
690          */
691         @Deprecated
692         public static int ON = 1;
693 
694         /**
695          * Action index for closed caption is off.
696          */
697         public static final int INDEX_OFF = 0;
698 
699         /**
700          * Action index for closed caption is on.
701          */
702         public static final int INDEX_ON = 1;
703 
704 
705         /**
706          * Constructor
707          * @param context Context used for loading resources.
708          */
709         public ClosedCaptioningAction(Context context) {
710             this(context, getIconHighlightColor(context));
711         }
712 
713         /**
714          * Constructor
715          * @param context Context used for loading resources.
716          * @param highlightColor Color for the highlighted icon state.
717          */
718         public ClosedCaptioningAction(Context context, int highlightColor) {
719             super(R.id.lb_control_closed_captioning);
720             BitmapDrawable uncoloredDrawable = (BitmapDrawable) getStyledDrawable(context,
721                     R.styleable.lbPlaybackControlsActionIcons_closed_captioning);
722             Drawable[] drawables = new Drawable[2];
723             drawables[INDEX_OFF] = uncoloredDrawable;
724             drawables[INDEX_ON] = new BitmapDrawable(context.getResources(),
725                     createBitmap(uncoloredDrawable.getBitmap(), highlightColor));
726             setDrawables(drawables);
727 
728             String[] labels = new String[drawables.length];
729             labels[INDEX_OFF] = context.getString(
730                     R.string.lb_playback_controls_closed_captioning_enable);
731             labels[INDEX_ON] = context.getString(
732                     R.string.lb_playback_controls_closed_captioning_disable);
733             setLabels(labels);
734         }
735     }
736 
737     static Bitmap createBitmap(Bitmap bitmap, int color) {
738         Bitmap dst = bitmap.copy(bitmap.getConfig(), true);
739         Canvas canvas = new Canvas(dst);
740         Paint paint = new Paint();
741         paint.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_ATOP));
742         canvas.drawBitmap(bitmap, 0, 0, paint);
743         return dst;
744     }
745 
746     static int getIconHighlightColor(Context context) {
747         TypedValue outValue = new TypedValue();
748         if (context.getTheme().resolveAttribute(R.attr.playbackControlsIconHighlightColor,
749                 outValue, true)) {
750             return outValue.data;
751         }
752         return context.getResources().getColor(R.color.lb_playback_icon_highlight_no_theme);
753     }
754 
755     static Drawable getStyledDrawable(Context context, int index) {
756         TypedValue outValue = new TypedValue();
757         if (!context.getTheme().resolveAttribute(
758                 R.attr.playbackControlsActionIcons, outValue, false)) {
759             return null;
760         }
761         TypedArray array = context.getTheme().obtainStyledAttributes(outValue.data,
762                 R.styleable.lbPlaybackControlsActionIcons);
763         Drawable drawable = array.getDrawable(index);
764         array.recycle();
765         return drawable;
766     }
767 
768     private Object mItem;
769     private Drawable mImageDrawable;
770     private ObjectAdapter mPrimaryActionsAdapter;
771     private ObjectAdapter mSecondaryActionsAdapter;
772     private long mTotalTimeMs;
773     private long mCurrentTimeMs;
774     private long mBufferedProgressMs;
775     private OnPlaybackProgressCallback mListener;
776 
777     /**
778      * Constructor for a PlaybackControlsRow that displays some details from
779      * the given item.
780      *
781      * @param item The main item for the row.
782      */
783     public PlaybackControlsRow(Object item) {
784         mItem = item;
785     }
786 
787     /**
788      * Constructor for a PlaybackControlsRow that has no item details.
789      */
790     public PlaybackControlsRow() {
791     }
792 
793     /**
794      * Returns the main item for the details page.
795      */
796     public final Object getItem() {
797         return mItem;
798     }
799 
800     /**
801      * Sets a {link @Drawable} image for this row.
802      * <p>If set after the row has been bound to a view, the adapter must be notified that
803      * this row has changed.</p>
804      *
805      * @param drawable The drawable to set.
806      */
807     public final void setImageDrawable(Drawable drawable) {
808         mImageDrawable = drawable;
809     }
810 
811     /**
812      * Sets a {@link Bitmap} for this row.
813      * <p>If set after the row has been bound to a view, the adapter must be notified that
814      * this row has changed.</p>
815      *
816      * @param context The context to retrieve display metrics from.
817      * @param bm The bitmap to set.
818      */
819     public final void setImageBitmap(Context context, Bitmap bm) {
820         mImageDrawable = new BitmapDrawable(context.getResources(), bm);
821     }
822 
823     /**
824      * Returns the image {@link Drawable} of this row.
825      *
826      * @return The overview's image drawable, or null if no drawable has been
827      *         assigned.
828      */
829     public final Drawable getImageDrawable() {
830         return mImageDrawable;
831     }
832 
833     /**
834      * Sets the primary actions {@link ObjectAdapter}.
835      * <p>If set after the row has been bound to a view, the adapter must be notified that
836      * this row has changed.</p>
837      */
838     public final void setPrimaryActionsAdapter(ObjectAdapter adapter) {
839         mPrimaryActionsAdapter = adapter;
840     }
841 
842     /**
843      * Sets the secondary actions {@link ObjectAdapter}.
844      * <p>If set after the row has been bound to a view, the adapter must be notified that
845      * this row has changed.</p>
846      */
847     public final void setSecondaryActionsAdapter(ObjectAdapter adapter) {
848         mSecondaryActionsAdapter = adapter;
849     }
850 
851     /**
852      * Returns the primary actions {@link ObjectAdapter}.
853      */
854     public final ObjectAdapter getPrimaryActionsAdapter() {
855         return mPrimaryActionsAdapter;
856     }
857 
858     /**
859      * Returns the secondary actions {@link ObjectAdapter}.
860      */
861     public final ObjectAdapter getSecondaryActionsAdapter() {
862         return mSecondaryActionsAdapter;
863     }
864 
865     /**
866      * Sets the total time in milliseconds for the playback controls row.
867      * <p>If set after the row has been bound to a view, the adapter must be notified that
868      * this row has changed.</p>
869      * @deprecated Use {@link #setDuration(long)}
870      */
871     @Deprecated
872     public void setTotalTime(int ms) {
873         setDuration((long) ms);
874     }
875 
876     /**
877      * Sets the total time in milliseconds (long type) for the playback controls row.
878      * @param ms Total time in milliseconds of long type.
879      * @deprecated Use {@link #setDuration(long)}
880      */
881     @Deprecated
882     public void setTotalTimeLong(long ms) {
883         setDuration(ms);
884     }
885 
886     /**
887      * Sets the total time in milliseconds (long type) for the playback controls row.
888      * If this row is bound to a view, the view will automatically
889      * be updated to reflect the new value.
890      * @param ms Total time in milliseconds of long type.
891      */
892     public void setDuration(long ms) {
893         if (mTotalTimeMs != ms) {
894             mTotalTimeMs = ms;
895             if (mListener != null) {
896                 mListener.onDurationChanged(this, mTotalTimeMs);
897             }
898         }
899     }
900 
901     /**
902      * Returns the total time in milliseconds for the playback controls row.
903      * @throws ArithmeticException If total time in milliseconds overflows int.
904      * @deprecated use {@link #getDuration()}
905      */
906     @Deprecated
907     public int getTotalTime() {
908         return MathUtil.safeLongToInt(getTotalTimeLong());
909     }
910 
911     /**
912      * Returns the total time in milliseconds of long type for the playback controls row.
913      * @deprecated use {@link #getDuration()}
914      */
915     @Deprecated
916     public long getTotalTimeLong() {
917         return mTotalTimeMs;
918     }
919 
920     /**
921      * Returns duration in milliseconds.
922      * @return Duration in milliseconds.
923      */
924     public long getDuration() {
925         return mTotalTimeMs;
926     }
927 
928     /**
929      * Sets the current time in milliseconds for the playback controls row.
930      * If this row is bound to a view, the view will automatically
931      * be updated to reflect the new value.
932      * @deprecated use {@link #setCurrentPosition(long)}
933      */
934     @Deprecated
935     public void setCurrentTime(int ms) {
936         setCurrentTimeLong((long) ms);
937     }
938 
939     /**
940      * Sets the current time in milliseconds for playback controls row in long type.
941      * @param ms Current time in milliseconds of long type.
942      * @deprecated use {@link #setCurrentPosition(long)}
943      */
944     @Deprecated
945     public void setCurrentTimeLong(long ms) {
946         setCurrentPosition(ms);
947     }
948 
949     /**
950      * Sets the current time in milliseconds for the playback controls row.
951      * If this row is bound to a view, the view will automatically
952      * be updated to reflect the new value.
953      * @param ms Current time in milliseconds of long type.
954      */
955     public void setCurrentPosition(long ms) {
956         if (mCurrentTimeMs != ms) {
957             mCurrentTimeMs = ms;
958             if (mListener != null) {
959                 mListener.onCurrentPositionChanged(this, mCurrentTimeMs);
960             }
961         }
962     }
963 
964     /**
965      * Returns the current time in milliseconds for the playback controls row.
966      * @throws ArithmeticException If current time in milliseconds overflows int.
967      * @deprecated Use {@link #getCurrentPosition()}
968      */
969     @Deprecated
970     public int getCurrentTime() {
971         return MathUtil.safeLongToInt(getCurrentTimeLong());
972     }
973 
974     /**
975      * Returns the current time in milliseconds of long type for playback controls row.
976      * @deprecated Use {@link #getCurrentPosition()}
977      */
978     @Deprecated
979     public long getCurrentTimeLong() {
980         return mCurrentTimeMs;
981     }
982 
983     /**
984      * Returns the current time in milliseconds of long type for playback controls row.
985      */
986     public long getCurrentPosition() {
987         return mCurrentTimeMs;
988     }
989 
990     /**
991      * Sets the buffered progress for the playback controls row.
992      * If this row is bound to a view, the view will automatically
993      * be updated to reflect the new value.
994      * @deprecated Use {@link #setBufferedPosition(long)}
995      */
996     @Deprecated
997     public void setBufferedProgress(int ms) {
998         setBufferedPosition((long) ms);
999     }
1000 
1001     /**
1002      * Sets the buffered progress for the playback controls row.
1003      * @param ms Buffered progress in milliseconds of long type.
1004      * @deprecated Use {@link #setBufferedPosition(long)}
1005      */
1006     @Deprecated
1007     public void setBufferedProgressLong(long ms) {
1008         setBufferedPosition(ms);
1009     }
1010 
1011     /**
1012      * Sets the buffered progress for the playback controls row.
1013      * @param ms Buffered progress in milliseconds of long type.
1014      */
1015     public void setBufferedPosition(long ms) {
1016         if (mBufferedProgressMs != ms) {
1017             mBufferedProgressMs = ms;
1018             if (mListener != null) {
1019                 mListener.onBufferedPositionChanged(this, mBufferedProgressMs);
1020             }
1021         }
1022     }
1023     /**
1024      * Returns the buffered progress for the playback controls row.
1025      * @throws ArithmeticException If buffered progress in milliseconds overflows int.
1026      * @deprecated Use {@link #getBufferedPosition()}
1027      */
1028     @Deprecated
1029     public int getBufferedProgress() {
1030         return MathUtil.safeLongToInt(getBufferedPosition());
1031     }
1032 
1033     /**
1034      * Returns the buffered progress of long type for the playback controls row.
1035      * @deprecated Use {@link #getBufferedPosition()}
1036      */
1037     @Deprecated
1038     public long getBufferedProgressLong() {
1039         return mBufferedProgressMs;
1040     }
1041 
1042     /**
1043      * Returns the buffered progress of long type for the playback controls row.
1044      */
1045     public long getBufferedPosition() {
1046         return mBufferedProgressMs;
1047     }
1048 
1049     /**
1050      * Returns the Action associated with the given keycode, or null if no associated action exists.
1051      * Searches the primary adapter first, then the secondary adapter.
1052      */
1053     public Action getActionForKeyCode(int keyCode) {
1054         Action action = getActionForKeyCode(getPrimaryActionsAdapter(), keyCode);
1055         if (action != null) {
1056             return action;
1057         }
1058         return getActionForKeyCode(getSecondaryActionsAdapter(), keyCode);
1059     }
1060 
1061     /**
1062      * Returns the Action associated with the given keycode, or null if no associated action exists.
1063      */
1064     public Action getActionForKeyCode(ObjectAdapter adapter, int keyCode) {
1065         if (adapter != mPrimaryActionsAdapter && adapter != mSecondaryActionsAdapter) {
1066             throw new IllegalArgumentException("Invalid adapter");
1067         }
1068         for (int i = 0; i < adapter.size(); i++) {
1069             Action action = (Action) adapter.get(i);
1070             if (action.respondsToKeyCode(keyCode)) {
1071                 return action;
1072             }
1073         }
1074         return null;
1075     }
1076 
1077     /**
1078      * Sets a listener to be called when the playback state changes.
1079      */
1080     public void setOnPlaybackProgressChangedListener(OnPlaybackProgressCallback listener) {
1081         mListener = listener;
1082     }
1083 }
1084