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