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 private String[] mLabels; 52 private String[] mLabels2; 53 54 /** 55 * Constructor 56 * @param id The id of the Action. 57 */ 58 public MultiAction(int id) { 59 super(id); 60 } 61 62 /** 63 * Sets the array of drawables. The size of the array defines the range 64 * of valid indices for this action. 65 */ 66 public void setDrawables(Drawable[] drawables) { 67 mDrawables = drawables; 68 setIndex(0); 69 } 70 71 /** 72 * Sets the array of strings used as labels. The size of the array defines the range 73 * of valid indices for this action. The labels are used to define the accessibility 74 * content description unless secondary labels are provided. 75 */ 76 public void setLabels(String[] labels) { 77 mLabels = labels; 78 setIndex(0); 79 } 80 81 /** 82 * Sets the array of strings used as secondary labels. These labels are used 83 * in place of the primary labels for accessibility content description only. 84 */ 85 public void setSecondaryLabels(String[] labels) { 86 mLabels2 = labels; 87 setIndex(0); 88 } 89 90 /** 91 * Returns the number of actions. 92 */ 93 public int getActionCount() { 94 if (mDrawables != null) { 95 return mDrawables.length; 96 } 97 if (mLabels != null) { 98 return mLabels.length; 99 } 100 return 0; 101 } 102 103 /** 104 * Returns the drawable at the given index. 105 */ 106 public Drawable getDrawable(int index) { 107 return mDrawables == null ? null : mDrawables[index]; 108 } 109 110 /** 111 * Returns the label at the given index. 112 */ 113 public String getLabel(int index) { 114 return mLabels == null ? null : mLabels[index]; 115 } 116 117 /** 118 * Returns the secondary label at the given index. 119 */ 120 public String getSecondaryLabel(int index) { 121 return mLabels2 == null ? null : mLabels2[index]; 122 } 123 124 /** 125 * Increments the index, wrapping to zero once the end is reached. 126 */ 127 public void nextIndex() { 128 setIndex(mIndex < getActionCount() - 1 ? mIndex + 1 : 0); 129 } 130 131 /** 132 * Sets the current index. 133 */ 134 public void setIndex(int index) { 135 mIndex = index; 136 if (mDrawables != null) { 137 setIcon(mDrawables[mIndex]); 138 } 139 if (mLabels != null) { 140 setLabel1(mLabels[mIndex]); 141 } 142 if (mLabels2 != null) { 143 setLabel2(mLabels2[mIndex]); 144 } 145 } 146 147 /** 148 * Gets the current index. 149 */ 150 public int getIndex() { 151 return mIndex; 152 } 153 } 154 155 /** 156 * An action displaying icons for play and pause. 157 */ 158 public static class PlayPauseAction extends MultiAction { 159 /** 160 * Action index for the play icon. 161 */ 162 public static int PLAY = 0; 163 164 /** 165 * Action index for the pause icon. 166 */ 167 public static int PAUSE = 1; 168 169 /** 170 * Constructor 171 * @param context Context used for loading resources. 172 */ 173 public PlayPauseAction(Context context) { 174 super(R.id.lb_control_play_pause); 175 Drawable[] drawables = new Drawable[2]; 176 drawables[PLAY] = getStyledDrawable(context, 177 R.styleable.lbPlaybackControlsActionIcons_play); 178 drawables[PAUSE] = getStyledDrawable(context, 179 R.styleable.lbPlaybackControlsActionIcons_pause); 180 setDrawables(drawables); 181 182 String[] labels = new String[drawables.length]; 183 labels[PLAY] = context.getString(R.string.lb_playback_controls_play); 184 labels[PAUSE] = context.getString(R.string.lb_playback_controls_pause); 185 setLabels(labels); 186 } 187 } 188 189 /** 190 * An action displaying an icon for fast forward. 191 */ 192 public static class FastForwardAction extends MultiAction { 193 /** 194 * Constructor 195 * @param context Context used for loading resources. 196 */ 197 public FastForwardAction(Context context) { 198 this(context, 1); 199 } 200 201 /** 202 * Constructor 203 * @param context Context used for loading resources. 204 * @param numSpeeds Number of supported fast forward speeds. 205 */ 206 public FastForwardAction(Context context, int numSpeeds) { 207 super(R.id.lb_control_fast_forward); 208 209 if (numSpeeds < 1) { 210 throw new IllegalArgumentException("numSpeeds must be > 0"); 211 } 212 Drawable[] drawables = new Drawable[numSpeeds]; 213 drawables[0] = getStyledDrawable(context, 214 R.styleable.lbPlaybackControlsActionIcons_fast_forward); 215 setDrawables(drawables); 216 217 String[] labels = new String[getActionCount()]; 218 labels[0] = context.getString(R.string.lb_playback_controls_fast_forward); 219 220 String[] labels2 = new String[getActionCount()]; 221 labels2[0] = labels[0]; 222 223 for (int i = 1; i < numSpeeds; i++) { 224 int multiplier = i + 1; 225 labels[i] = context.getResources().getString( 226 R.string.lb_control_display_fast_forward_multiplier, multiplier); 227 labels2[i] = context.getResources().getString( 228 R.string.lb_playback_controls_fast_forward_multiplier, multiplier); 229 } 230 setLabels(labels); 231 setSecondaryLabels(labels2); 232 } 233 } 234 235 /** 236 * An action displaying an icon for rewind. 237 */ 238 public static class RewindAction extends MultiAction { 239 /** 240 * Constructor 241 * @param context Context used for loading resources. 242 */ 243 public RewindAction(Context context) { 244 this(context, 1); 245 } 246 247 /** 248 * Constructor 249 * @param context Context used for loading resources. 250 * @param numSpeeds Number of supported fast forward speeds. 251 */ 252 public RewindAction(Context context, int numSpeeds) { 253 super(R.id.lb_control_fast_rewind); 254 255 if (numSpeeds < 1) { 256 throw new IllegalArgumentException("numSpeeds must be > 0"); 257 } 258 Drawable[] drawables = new Drawable[numSpeeds]; 259 drawables[0] = getStyledDrawable(context, 260 R.styleable.lbPlaybackControlsActionIcons_rewind); 261 setDrawables(drawables); 262 263 String[] labels = new String[getActionCount()]; 264 labels[0] = context.getString(R.string.lb_playback_controls_rewind); 265 266 String[] labels2 = new String[getActionCount()]; 267 labels2[0] = labels[0]; 268 269 for (int i = 1; i < numSpeeds; i++) { 270 int multiplier = i + 1; 271 labels[i] = labels[i] = context.getResources().getString( 272 R.string.lb_control_display_rewind_multiplier, multiplier); 273 labels2[i] = context.getResources().getString( 274 R.string.lb_playback_controls_rewind_multiplier, multiplier); 275 } 276 setLabels(labels); 277 setSecondaryLabels(labels2); 278 } 279 } 280 281 /** 282 * An action displaying an icon for skip next. 283 */ 284 public static class SkipNextAction extends Action { 285 /** 286 * Constructor 287 * @param context Context used for loading resources. 288 */ 289 public SkipNextAction(Context context) { 290 super(R.id.lb_control_skip_next); 291 setIcon(getStyledDrawable(context, 292 R.styleable.lbPlaybackControlsActionIcons_skip_next)); 293 setLabel1(context.getString(R.string.lb_playback_controls_skip_next)); 294 } 295 } 296 297 /** 298 * An action displaying an icon for skip previous. 299 */ 300 public static class SkipPreviousAction extends Action { 301 /** 302 * Constructor 303 * @param context Context used for loading resources. 304 */ 305 public SkipPreviousAction(Context context) { 306 super(R.id.lb_control_skip_previous); 307 setIcon(getStyledDrawable(context, 308 R.styleable.lbPlaybackControlsActionIcons_skip_previous)); 309 setLabel1(context.getString(R.string.lb_playback_controls_skip_previous)); 310 } 311 } 312 313 /** 314 * An action displaying an icon for "more actions". 315 */ 316 public static class MoreActions extends Action { 317 /** 318 * Constructor 319 * @param context Context used for loading resources. 320 */ 321 public MoreActions(Context context) { 322 super(R.id.lb_control_more_actions); 323 setIcon(context.getResources().getDrawable(R.drawable.lb_ic_more)); 324 setLabel1(context.getString(R.string.lb_playback_controls_more_actions)); 325 } 326 } 327 328 /** 329 * A base class for displaying a thumbs action. 330 */ 331 public static abstract class ThumbsAction extends MultiAction { 332 /** 333 * Action index for the solid thumb icon. 334 */ 335 public static int SOLID = 0; 336 337 /** 338 * Action index for the outline thumb icon. 339 */ 340 public static int OUTLINE = 1; 341 342 /** 343 * Constructor 344 * @param context Context used for loading resources. 345 */ 346 public ThumbsAction(int id, Context context, int solidIconIndex, int outlineIconIndex) { 347 super(id); 348 Drawable[] drawables = new Drawable[2]; 349 drawables[SOLID] = getStyledDrawable(context, solidIconIndex); 350 drawables[OUTLINE] = getStyledDrawable(context, outlineIconIndex); 351 setDrawables(drawables); 352 } 353 } 354 355 /** 356 * An action displaying an icon for thumbs up. 357 */ 358 public static class ThumbsUpAction extends ThumbsAction { 359 public ThumbsUpAction(Context context) { 360 super(R.id.lb_control_thumbs_up, context, 361 R.styleable.lbPlaybackControlsActionIcons_thumb_up, 362 R.styleable.lbPlaybackControlsActionIcons_thumb_up_outline); 363 String[] labels = new String[getActionCount()]; 364 labels[SOLID] = context.getString(R.string.lb_playback_controls_thumb_up); 365 labels[OUTLINE] = context.getString(R.string.lb_playback_controls_thumb_up_outline); 366 setLabels(labels); 367 } 368 } 369 370 /** 371 * An action displaying an icon for thumbs down. 372 */ 373 public static class ThumbsDownAction extends ThumbsAction { 374 public ThumbsDownAction(Context context) { 375 super(R.id.lb_control_thumbs_down, context, 376 R.styleable.lbPlaybackControlsActionIcons_thumb_down, 377 R.styleable.lbPlaybackControlsActionIcons_thumb_down_outline); 378 String[] labels = new String[getActionCount()]; 379 labels[SOLID] = context.getString(R.string.lb_playback_controls_thumb_down); 380 labels[OUTLINE] = context.getString(R.string.lb_playback_controls_thumb_down_outline); 381 setLabels(labels); 382 } 383 } 384 385 /** 386 * An action for displaying three repeat states: none, one, or all. 387 */ 388 public static class RepeatAction extends MultiAction { 389 /** 390 * Action index for the repeat-none icon. 391 */ 392 public static int NONE = 0; 393 394 /** 395 * Action index for the repeat-all icon. 396 */ 397 public static int ALL = 1; 398 399 /** 400 * Action index for the repeat-one icon. 401 */ 402 public static int ONE = 2; 403 404 /** 405 * Constructor 406 * @param context Context used for loading resources. 407 */ 408 public RepeatAction(Context context) { 409 this(context, getColorFromTheme(context, 410 R.attr.playbackControlsIconHighlightColor)); 411 } 412 413 /** 414 * Constructor 415 * @param context Context used for loading resources 416 * @param highlightColor Color to display the repeat-all and repeat0one icons. 417 */ 418 public RepeatAction(Context context, int highlightColor) { 419 this(context, highlightColor, highlightColor); 420 } 421 422 /** 423 * Constructor 424 * @param context Context used for loading resources 425 * @param repeatAllColor Color to display the repeat-all icon. 426 * @param repeatOneColor Color to display the repeat-one icon. 427 */ 428 public RepeatAction(Context context, int repeatAllColor, int repeatOneColor) { 429 super(R.id.lb_control_repeat); 430 Drawable[] drawables = new Drawable[3]; 431 BitmapDrawable repeatDrawable = (BitmapDrawable) getStyledDrawable(context, 432 R.styleable.lbPlaybackControlsActionIcons_repeat); 433 BitmapDrawable repeatOneDrawable = (BitmapDrawable) getStyledDrawable(context, 434 R.styleable.lbPlaybackControlsActionIcons_repeat_one); 435 drawables[NONE] = repeatDrawable; 436 drawables[ALL] = new BitmapDrawable(context.getResources(), 437 createBitmap(repeatDrawable.getBitmap(), repeatAllColor)); 438 drawables[ONE] = new BitmapDrawable(context.getResources(), 439 createBitmap(repeatOneDrawable.getBitmap(), repeatOneColor)); 440 setDrawables(drawables); 441 442 String[] labels = new String[drawables.length]; 443 // Note, labels denote the action taken when clicked 444 labels[NONE] = context.getString(R.string.lb_playback_controls_repeat_all); 445 labels[ALL] = context.getString(R.string.lb_playback_controls_repeat_one); 446 labels[ONE] = context.getString(R.string.lb_playback_controls_repeat_none); 447 setLabels(labels); 448 } 449 } 450 451 /** 452 * An action for displaying a shuffle icon. 453 */ 454 public static class ShuffleAction extends MultiAction { 455 public static int OFF = 0; 456 public static int ON = 1; 457 458 /** 459 * Constructor 460 * @param context Context used for loading resources. 461 */ 462 public ShuffleAction(Context context) { 463 this(context, getColorFromTheme(context, 464 R.attr.playbackControlsIconHighlightColor)); 465 } 466 467 /** 468 * Constructor 469 * @param context Context used for loading resources. 470 * @param highlightColor Color for the highlighted icon state. 471 */ 472 public ShuffleAction(Context context, int highlightColor) { 473 super(R.id.lb_control_shuffle); 474 BitmapDrawable uncoloredDrawable = (BitmapDrawable) getStyledDrawable(context, 475 R.styleable.lbPlaybackControlsActionIcons_shuffle); 476 Drawable[] drawables = new Drawable[2]; 477 drawables[OFF] = uncoloredDrawable; 478 drawables[ON] = new BitmapDrawable(context.getResources(), 479 createBitmap(uncoloredDrawable.getBitmap(), highlightColor)); 480 setDrawables(drawables); 481 482 String[] labels = new String[drawables.length]; 483 labels[OFF] = context.getString(R.string.lb_playback_controls_shuffle_enable); 484 labels[ON] = context.getString(R.string.lb_playback_controls_shuffle_disable); 485 setLabels(labels); 486 } 487 } 488 489 /** 490 * An action for displaying a HQ (High Quality) icon. 491 */ 492 public static class HighQualityAction extends MultiAction { 493 public static int OFF = 0; 494 public static int ON = 1; 495 496 /** 497 * Constructor 498 * @param context Context used for loading resources. 499 */ 500 public HighQualityAction(Context context) { 501 this(context, getColorFromTheme(context, 502 R.attr.playbackControlsIconHighlightColor)); 503 } 504 505 /** 506 * Constructor 507 * @param context Context used for loading resources. 508 * @param highlightColor Color for the highlighted icon state. 509 */ 510 public HighQualityAction(Context context, int highlightColor) { 511 super(R.id.lb_control_high_quality); 512 BitmapDrawable uncoloredDrawable = (BitmapDrawable) getStyledDrawable(context, 513 R.styleable.lbPlaybackControlsActionIcons_high_quality); 514 Drawable[] drawables = new Drawable[2]; 515 drawables[OFF] = uncoloredDrawable; 516 drawables[ON] = new BitmapDrawable(context.getResources(), 517 createBitmap(uncoloredDrawable.getBitmap(), highlightColor)); 518 setDrawables(drawables); 519 520 String[] labels = new String[drawables.length]; 521 labels[OFF] = context.getString(R.string.lb_playback_controls_high_quality_enable); 522 labels[ON] = context.getString(R.string.lb_playback_controls_high_quality_disable); 523 setLabels(labels); 524 } 525 } 526 527 /** 528 * An action for displaying a CC (Closed Captioning) icon. 529 */ 530 public static class ClosedCaptioningAction extends MultiAction { 531 public static int OFF = 0; 532 public static int ON = 1; 533 534 /** 535 * Constructor 536 * @param context Context used for loading resources. 537 */ 538 public ClosedCaptioningAction(Context context) { 539 this(context, getColorFromTheme(context, 540 R.attr.playbackControlsIconHighlightColor)); 541 } 542 543 /** 544 * Constructor 545 * @param context Context used for loading resources. 546 * @param highlightColor Color for the highlighted icon state. 547 */ 548 public ClosedCaptioningAction(Context context, int highlightColor) { 549 super(R.id.lb_control_high_quality); 550 BitmapDrawable uncoloredDrawable = (BitmapDrawable) getStyledDrawable(context, 551 R.styleable.lbPlaybackControlsActionIcons_closed_captioning); 552 Drawable[] drawables = new Drawable[2]; 553 drawables[OFF] = uncoloredDrawable; 554 drawables[ON] = new BitmapDrawable(context.getResources(), 555 createBitmap(uncoloredDrawable.getBitmap(), highlightColor)); 556 setDrawables(drawables); 557 558 String[] labels = new String[drawables.length]; 559 labels[OFF] = context.getString(R.string.lb_playback_controls_closed_captioning_enable); 560 labels[ON] = context.getString(R.string.lb_playback_controls_closed_captioning_disable); 561 setLabels(labels); 562 } 563 } 564 565 private static Bitmap createBitmap(Bitmap bitmap, int color) { 566 Bitmap dst = bitmap.copy(bitmap.getConfig(), true); 567 Canvas canvas = new Canvas(dst); 568 Paint paint = new Paint(); 569 paint.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_ATOP)); 570 canvas.drawBitmap(bitmap, 0, 0, paint); 571 return dst; 572 } 573 574 private static int getColorFromTheme(Context context, int attributeResId) { 575 TypedValue outValue = new TypedValue(); 576 context.getTheme().resolveAttribute(attributeResId, outValue, true); 577 return outValue.data; 578 } 579 580 private static Drawable getStyledDrawable(Context context, int index) { 581 TypedValue outValue = new TypedValue(); 582 context.getTheme().resolveAttribute( 583 R.attr.playbackControlsActionIcons, outValue, false); 584 TypedArray array = context.getTheme().obtainStyledAttributes(outValue.data, 585 R.styleable.lbPlaybackControlsActionIcons); 586 Drawable drawable = array.getDrawable(index); 587 array.recycle(); 588 return drawable; 589 } 590 591 private Object mItem; 592 private Drawable mImageDrawable; 593 private ObjectAdapter mPrimaryActionsAdapter; 594 private ObjectAdapter mSecondaryActionsAdapter; 595 private int mTotalTimeMs; 596 private int mCurrentTimeMs; 597 private int mBufferedProgressMs; 598 private OnPlaybackStateChangedListener mListener; 599 600 /** 601 * Constructor for a PlaybackControlsRow that displays some details from 602 * the given item. 603 * 604 * @param item The main item for the row. 605 */ 606 public PlaybackControlsRow(Object item) { 607 mItem = item; 608 } 609 610 /** 611 * Constructor for a PlaybackControlsRow that has no item details. 612 */ 613 public PlaybackControlsRow() { 614 } 615 616 /** 617 * Gets the main item for the details page. 618 */ 619 public final Object getItem() { 620 return mItem; 621 } 622 623 /** 624 * Sets a {link @Drawable} image for this row. 625 * 626 * @param drawable The drawable to set. 627 */ 628 public final void setImageDrawable(Drawable drawable) { 629 mImageDrawable = drawable; 630 } 631 632 /** 633 * Sets a {@link Bitmap} for this row. 634 * 635 * @param context The context to retrieve display metrics from. 636 * @param bm The bitmap to set. 637 */ 638 public final void setImageBitmap(Context context, Bitmap bm) { 639 mImageDrawable = new BitmapDrawable(context.getResources(), bm); 640 } 641 642 /** 643 * Gets the image {@link Drawable} of this row. 644 * 645 * @return The overview's image drawable, or null if no drawable has been 646 * assigned. 647 */ 648 public final Drawable getImageDrawable() { 649 return mImageDrawable; 650 } 651 652 /** 653 * Sets the primary actions {@link ObjectAdapter}. 654 */ 655 public final void setPrimaryActionsAdapter(ObjectAdapter adapter) { 656 mPrimaryActionsAdapter = adapter; 657 } 658 659 /** 660 * Sets the secondary actions {@link ObjectAdapter}. 661 */ 662 public final void setSecondaryActionsAdapter(ObjectAdapter adapter) { 663 mSecondaryActionsAdapter = adapter; 664 } 665 666 /** 667 * Returns the primary actions {@link ObjectAdapter}. 668 */ 669 public final ObjectAdapter getPrimaryActionsAdapter() { 670 return mPrimaryActionsAdapter; 671 } 672 673 /** 674 * Returns the secondary actions {@link ObjectAdapter}. 675 */ 676 public final ObjectAdapter getSecondaryActionsAdapter() { 677 return mSecondaryActionsAdapter; 678 } 679 680 /** 681 * Sets the total time in milliseconds for the playback controls row. 682 */ 683 public void setTotalTime(int ms) { 684 mTotalTimeMs = ms; 685 } 686 687 /** 688 * Returns the total time in milliseconds for the playback controls row. 689 */ 690 public int getTotalTime() { 691 return mTotalTimeMs; 692 } 693 694 /** 695 * Sets the current time in milliseconds for the playback controls row. 696 * If this row is bound to a view, the view will automatically 697 * be updated to reflect the new value. 698 */ 699 public void setCurrentTime(int ms) { 700 if (mCurrentTimeMs != ms) { 701 mCurrentTimeMs = ms; 702 currentTimeChanged(); 703 } 704 } 705 706 /** 707 * Returns the current time in milliseconds for the playback controls row. 708 */ 709 public int getCurrentTime() { 710 return mCurrentTimeMs; 711 } 712 713 /** 714 * Sets the buffered progress for the playback controls row. 715 * If this row is bound to a view, the view will automatically 716 * be updated to reflect the new value. 717 */ 718 public void setBufferedProgress(int ms) { 719 if (mBufferedProgressMs != ms) { 720 mBufferedProgressMs = ms; 721 bufferedProgressChanged(); 722 } 723 } 724 725 /** 726 * Returns the buffered progress for the playback controls row. 727 */ 728 public int getBufferedProgress() { 729 return mBufferedProgressMs; 730 } 731 732 interface OnPlaybackStateChangedListener { 733 public void onCurrentTimeChanged(int currentTimeMs); 734 public void onBufferedProgressChanged(int bufferedProgressMs); 735 } 736 737 /** 738 * Sets a listener to be called when the playback state changes. 739 */ 740 public void setOnPlaybackStateChangedListener(OnPlaybackStateChangedListener listener) { 741 mListener = listener; 742 } 743 744 /** 745 * Returns the playback state listener. 746 */ 747 public OnPlaybackStateChangedListener getOnPlaybackStateChangedListener() { 748 return mListener; 749 } 750 751 private void currentTimeChanged() { 752 if (mListener != null) { 753 mListener.onCurrentTimeChanged(mCurrentTimeMs); 754 } 755 } 756 757 private void bufferedProgressChanged() { 758 if (mListener != null) { 759 mListener.onBufferedProgressChanged(mBufferedProgressMs); 760 } 761 } 762} 763