1 /* 2 * Copyright (C) 2006 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.widget; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.graphics.PorterDuff; 22 23 import com.android.internal.R; 24 25 import android.annotation.InterpolatorRes; 26 import android.content.Context; 27 import android.content.res.ColorStateList; 28 import android.content.res.TypedArray; 29 import android.graphics.Bitmap; 30 import android.graphics.BitmapShader; 31 import android.graphics.Canvas; 32 import android.graphics.Rect; 33 import android.graphics.Shader; 34 import android.graphics.drawable.Animatable; 35 import android.graphics.drawable.AnimationDrawable; 36 import android.graphics.drawable.BitmapDrawable; 37 import android.graphics.drawable.ClipDrawable; 38 import android.graphics.drawable.Drawable; 39 import android.graphics.drawable.LayerDrawable; 40 import android.graphics.drawable.ShapeDrawable; 41 import android.graphics.drawable.StateListDrawable; 42 import android.graphics.drawable.shapes.RoundRectShape; 43 import android.graphics.drawable.shapes.Shape; 44 import android.os.Parcel; 45 import android.os.Parcelable; 46 import android.util.AttributeSet; 47 import android.util.MathUtils; 48 import android.util.Pools.SynchronizedPool; 49 import android.view.Gravity; 50 import android.view.RemotableViewMethod; 51 import android.view.View; 52 import android.view.ViewDebug; 53 import android.view.ViewHierarchyEncoder; 54 import android.view.accessibility.AccessibilityEvent; 55 import android.view.accessibility.AccessibilityManager; 56 import android.view.animation.AlphaAnimation; 57 import android.view.animation.Animation; 58 import android.view.animation.AnimationUtils; 59 import android.view.animation.Interpolator; 60 import android.view.animation.LinearInterpolator; 61 import android.view.animation.Transformation; 62 import android.widget.RemoteViews.RemoteView; 63 64 import java.util.ArrayList; 65 66 /** 67 * <p> 68 * Visual indicator of progress in some operation. Displays a bar to the user 69 * representing how far the operation has progressed; the application can 70 * change the amount of progress (modifying the length of the bar) as it moves 71 * forward. There is also a secondary progress displayable on a progress bar 72 * which is useful for displaying intermediate progress, such as the buffer 73 * level during a streaming playback progress bar. 74 * </p> 75 * 76 * <p> 77 * A progress bar can also be made indeterminate. In indeterminate mode, the 78 * progress bar shows a cyclic animation without an indication of progress. This mode is used by 79 * applications when the length of the task is unknown. The indeterminate progress bar can be either 80 * a spinning wheel or a horizontal bar. 81 * </p> 82 * 83 * <p>The following code example shows how a progress bar can be used from 84 * a worker thread to update the user interface to notify the user of progress: 85 * </p> 86 * 87 * <pre> 88 * public class MyActivity extends Activity { 89 * private static final int PROGRESS = 0x1; 90 * 91 * private ProgressBar mProgress; 92 * private int mProgressStatus = 0; 93 * 94 * private Handler mHandler = new Handler(); 95 * 96 * protected void onCreate(Bundle icicle) { 97 * super.onCreate(icicle); 98 * 99 * setContentView(R.layout.progressbar_activity); 100 * 101 * mProgress = (ProgressBar) findViewById(R.id.progress_bar); 102 * 103 * // Start lengthy operation in a background thread 104 * new Thread(new Runnable() { 105 * public void run() { 106 * while (mProgressStatus < 100) { 107 * mProgressStatus = doWork(); 108 * 109 * // Update the progress bar 110 * mHandler.post(new Runnable() { 111 * public void run() { 112 * mProgress.setProgress(mProgressStatus); 113 * } 114 * }); 115 * } 116 * } 117 * }).start(); 118 * } 119 * }</pre> 120 * 121 * <p>To add a progress bar to a layout file, you can use the {@code <ProgressBar>} element. 122 * By default, the progress bar is a spinning wheel (an indeterminate indicator). To change to a 123 * horizontal progress bar, apply the {@link android.R.style#Widget_ProgressBar_Horizontal 124 * Widget.ProgressBar.Horizontal} style, like so:</p> 125 * 126 * <pre> 127 * <ProgressBar 128 * style="@android:style/Widget.ProgressBar.Horizontal" 129 * ... /></pre> 130 * 131 * <p>If you will use the progress bar to show real progress, you must use the horizontal bar. You 132 * can then increment the progress with {@link #incrementProgressBy incrementProgressBy()} or 133 * {@link #setProgress setProgress()}. By default, the progress bar is full when it reaches 100. If 134 * necessary, you can adjust the maximum value (the value for a full bar) using the {@link 135 * android.R.styleable#ProgressBar_max android:max} attribute. Other attributes available are listed 136 * below.</p> 137 * 138 * <p>Another common style to apply to the progress bar is {@link 139 * android.R.style#Widget_ProgressBar_Small Widget.ProgressBar.Small}, which shows a smaller 140 * version of the spinning wheel—useful when waiting for content to load. 141 * For example, you can insert this kind of progress bar into your default layout for 142 * a view that will be populated by some content fetched from the Internet—the spinning wheel 143 * appears immediately and when your application receives the content, it replaces the progress bar 144 * with the loaded content. For example:</p> 145 * 146 * <pre> 147 * <LinearLayout 148 * android:orientation="horizontal" 149 * ... > 150 * <ProgressBar 151 * android:layout_width="wrap_content" 152 * android:layout_height="wrap_content" 153 * style="@android:style/Widget.ProgressBar.Small" 154 * android:layout_marginRight="5dp" /> 155 * <TextView 156 * android:layout_width="wrap_content" 157 * android:layout_height="wrap_content" 158 * android:text="@string/loading" /> 159 * </LinearLayout></pre> 160 * 161 * <p>Other progress bar styles provided by the system include:</p> 162 * <ul> 163 * <li>{@link android.R.style#Widget_ProgressBar_Horizontal Widget.ProgressBar.Horizontal}</li> 164 * <li>{@link android.R.style#Widget_ProgressBar_Small Widget.ProgressBar.Small}</li> 165 * <li>{@link android.R.style#Widget_ProgressBar_Large Widget.ProgressBar.Large}</li> 166 * <li>{@link android.R.style#Widget_ProgressBar_Inverse Widget.ProgressBar.Inverse}</li> 167 * <li>{@link android.R.style#Widget_ProgressBar_Small_Inverse 168 * Widget.ProgressBar.Small.Inverse}</li> 169 * <li>{@link android.R.style#Widget_ProgressBar_Large_Inverse 170 * Widget.ProgressBar.Large.Inverse}</li> 171 * </ul> 172 * <p>The "inverse" styles provide an inverse color scheme for the spinner, which may be necessary 173 * if your application uses a light colored theme (a white background).</p> 174 * 175 * <p><strong>XML attributes</b></strong> 176 * <p> 177 * See {@link android.R.styleable#ProgressBar ProgressBar Attributes}, 178 * {@link android.R.styleable#View View Attributes} 179 * </p> 180 * 181 * @attr ref android.R.styleable#ProgressBar_animationResolution 182 * @attr ref android.R.styleable#ProgressBar_indeterminate 183 * @attr ref android.R.styleable#ProgressBar_indeterminateBehavior 184 * @attr ref android.R.styleable#ProgressBar_indeterminateDrawable 185 * @attr ref android.R.styleable#ProgressBar_indeterminateDuration 186 * @attr ref android.R.styleable#ProgressBar_indeterminateOnly 187 * @attr ref android.R.styleable#ProgressBar_interpolator 188 * @attr ref android.R.styleable#ProgressBar_max 189 * @attr ref android.R.styleable#ProgressBar_maxHeight 190 * @attr ref android.R.styleable#ProgressBar_maxWidth 191 * @attr ref android.R.styleable#ProgressBar_minHeight 192 * @attr ref android.R.styleable#ProgressBar_minWidth 193 * @attr ref android.R.styleable#ProgressBar_mirrorForRtl 194 * @attr ref android.R.styleable#ProgressBar_progress 195 * @attr ref android.R.styleable#ProgressBar_progressDrawable 196 * @attr ref android.R.styleable#ProgressBar_secondaryProgress 197 */ 198 @RemoteView 199 public class ProgressBar extends View { 200 private static final int MAX_LEVEL = 10000; 201 private static final int TIMEOUT_SEND_ACCESSIBILITY_EVENT = 200; 202 203 int mMinWidth; 204 int mMaxWidth; 205 int mMinHeight; 206 int mMaxHeight; 207 208 private int mProgress; 209 private int mSecondaryProgress; 210 private int mMax; 211 212 private int mBehavior; 213 private int mDuration; 214 private boolean mIndeterminate; 215 private boolean mOnlyIndeterminate; 216 private Transformation mTransformation; 217 private AlphaAnimation mAnimation; 218 private boolean mHasAnimation; 219 220 private Drawable mIndeterminateDrawable; 221 private Drawable mProgressDrawable; 222 private Drawable mCurrentDrawable; 223 private ProgressTintInfo mProgressTintInfo; 224 225 Bitmap mSampleTile; 226 private boolean mNoInvalidate; 227 private Interpolator mInterpolator; 228 private RefreshProgressRunnable mRefreshProgressRunnable; 229 private long mUiThreadId; 230 private boolean mShouldStartAnimationDrawable; 231 232 private boolean mInDrawing; 233 private boolean mAttached; 234 private boolean mRefreshIsPosted; 235 236 boolean mMirrorForRtl = false; 237 238 private final ArrayList<RefreshData> mRefreshData = new ArrayList<RefreshData>(); 239 240 private AccessibilityEventSender mAccessibilityEventSender; 241 242 /** 243 * Create a new progress bar with range 0...100 and initial progress of 0. 244 * @param context the application environment 245 */ ProgressBar(Context context)246 public ProgressBar(Context context) { 247 this(context, null); 248 } 249 ProgressBar(Context context, AttributeSet attrs)250 public ProgressBar(Context context, AttributeSet attrs) { 251 this(context, attrs, com.android.internal.R.attr.progressBarStyle); 252 } 253 ProgressBar(Context context, AttributeSet attrs, int defStyleAttr)254 public ProgressBar(Context context, AttributeSet attrs, int defStyleAttr) { 255 this(context, attrs, defStyleAttr, 0); 256 } 257 ProgressBar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)258 public ProgressBar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 259 super(context, attrs, defStyleAttr, defStyleRes); 260 261 mUiThreadId = Thread.currentThread().getId(); 262 initProgressBar(); 263 264 final TypedArray a = context.obtainStyledAttributes( 265 attrs, R.styleable.ProgressBar, defStyleAttr, defStyleRes); 266 267 mNoInvalidate = true; 268 269 final Drawable progressDrawable = a.getDrawable(R.styleable.ProgressBar_progressDrawable); 270 if (progressDrawable != null) { 271 // Calling setProgressDrawable can set mMaxHeight, so make sure the 272 // corresponding XML attribute for mMaxHeight is read after calling 273 // this method. 274 if (needsTileify(progressDrawable)) { 275 setProgressDrawableTiled(progressDrawable); 276 } else { 277 setProgressDrawable(progressDrawable); 278 } 279 } 280 281 282 mDuration = a.getInt(R.styleable.ProgressBar_indeterminateDuration, mDuration); 283 284 mMinWidth = a.getDimensionPixelSize(R.styleable.ProgressBar_minWidth, mMinWidth); 285 mMaxWidth = a.getDimensionPixelSize(R.styleable.ProgressBar_maxWidth, mMaxWidth); 286 mMinHeight = a.getDimensionPixelSize(R.styleable.ProgressBar_minHeight, mMinHeight); 287 mMaxHeight = a.getDimensionPixelSize(R.styleable.ProgressBar_maxHeight, mMaxHeight); 288 289 mBehavior = a.getInt(R.styleable.ProgressBar_indeterminateBehavior, mBehavior); 290 291 final int resID = a.getResourceId( 292 com.android.internal.R.styleable.ProgressBar_interpolator, 293 android.R.anim.linear_interpolator); // default to linear interpolator 294 if (resID > 0) { 295 setInterpolator(context, resID); 296 } 297 298 setMax(a.getInt(R.styleable.ProgressBar_max, mMax)); 299 300 setProgress(a.getInt(R.styleable.ProgressBar_progress, mProgress)); 301 302 setSecondaryProgress(a.getInt( 303 R.styleable.ProgressBar_secondaryProgress, mSecondaryProgress)); 304 305 final Drawable indeterminateDrawable = a.getDrawable( 306 R.styleable.ProgressBar_indeterminateDrawable); 307 if (indeterminateDrawable != null) { 308 if (needsTileify(indeterminateDrawable)) { 309 setIndeterminateDrawableTiled(indeterminateDrawable); 310 } else { 311 setIndeterminateDrawable(indeterminateDrawable); 312 } 313 } 314 315 mOnlyIndeterminate = a.getBoolean( 316 R.styleable.ProgressBar_indeterminateOnly, mOnlyIndeterminate); 317 318 mNoInvalidate = false; 319 320 setIndeterminate(mOnlyIndeterminate || a.getBoolean( 321 R.styleable.ProgressBar_indeterminate, mIndeterminate)); 322 323 mMirrorForRtl = a.getBoolean(R.styleable.ProgressBar_mirrorForRtl, mMirrorForRtl); 324 325 if (a.hasValue(R.styleable.ProgressBar_progressTintMode)) { 326 if (mProgressTintInfo == null) { 327 mProgressTintInfo = new ProgressTintInfo(); 328 } 329 mProgressTintInfo.mProgressTintMode = Drawable.parseTintMode(a.getInt( 330 R.styleable.ProgressBar_progressTintMode, -1), null); 331 mProgressTintInfo.mHasProgressTintMode = true; 332 } 333 334 if (a.hasValue(R.styleable.ProgressBar_progressTint)) { 335 if (mProgressTintInfo == null) { 336 mProgressTintInfo = new ProgressTintInfo(); 337 } 338 mProgressTintInfo.mProgressTintList = a.getColorStateList( 339 R.styleable.ProgressBar_progressTint); 340 mProgressTintInfo.mHasProgressTint = true; 341 } 342 343 if (a.hasValue(R.styleable.ProgressBar_progressBackgroundTintMode)) { 344 if (mProgressTintInfo == null) { 345 mProgressTintInfo = new ProgressTintInfo(); 346 } 347 mProgressTintInfo.mProgressBackgroundTintMode = Drawable.parseTintMode(a.getInt( 348 R.styleable.ProgressBar_progressBackgroundTintMode, -1), null); 349 mProgressTintInfo.mHasProgressBackgroundTintMode = true; 350 } 351 352 if (a.hasValue(R.styleable.ProgressBar_progressBackgroundTint)) { 353 if (mProgressTintInfo == null) { 354 mProgressTintInfo = new ProgressTintInfo(); 355 } 356 mProgressTintInfo.mProgressBackgroundTintList = a.getColorStateList( 357 R.styleable.ProgressBar_progressBackgroundTint); 358 mProgressTintInfo.mHasProgressBackgroundTint = true; 359 } 360 361 if (a.hasValue(R.styleable.ProgressBar_secondaryProgressTintMode)) { 362 if (mProgressTintInfo == null) { 363 mProgressTintInfo = new ProgressTintInfo(); 364 } 365 mProgressTintInfo.mSecondaryProgressTintMode = Drawable.parseTintMode( 366 a.getInt(R.styleable.ProgressBar_secondaryProgressTintMode, -1), null); 367 mProgressTintInfo.mHasSecondaryProgressTintMode = true; 368 } 369 370 if (a.hasValue(R.styleable.ProgressBar_secondaryProgressTint)) { 371 if (mProgressTintInfo == null) { 372 mProgressTintInfo = new ProgressTintInfo(); 373 } 374 mProgressTintInfo.mSecondaryProgressTintList = a.getColorStateList( 375 R.styleable.ProgressBar_secondaryProgressTint); 376 mProgressTintInfo.mHasSecondaryProgressTint = true; 377 } 378 379 if (a.hasValue(R.styleable.ProgressBar_indeterminateTintMode)) { 380 if (mProgressTintInfo == null) { 381 mProgressTintInfo = new ProgressTintInfo(); 382 } 383 mProgressTintInfo.mIndeterminateTintMode = Drawable.parseTintMode(a.getInt( 384 R.styleable.ProgressBar_indeterminateTintMode, -1), null); 385 mProgressTintInfo.mHasIndeterminateTintMode = true; 386 } 387 388 if (a.hasValue(R.styleable.ProgressBar_indeterminateTint)) { 389 if (mProgressTintInfo == null) { 390 mProgressTintInfo = new ProgressTintInfo(); 391 } 392 mProgressTintInfo.mIndeterminateTintList = a.getColorStateList( 393 R.styleable.ProgressBar_indeterminateTint); 394 mProgressTintInfo.mHasIndeterminateTint = true; 395 } 396 397 a.recycle(); 398 399 applyProgressTints(); 400 applyIndeterminateTint(); 401 402 // If not explicitly specified this view is important for accessibility. 403 if (getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) { 404 setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); 405 } 406 } 407 408 /** 409 * Returns {@code true} if the target drawable needs to be tileified. 410 * 411 * @param dr the drawable to check 412 * @return {@code true} if the target drawable needs to be tileified, 413 * {@code false} otherwise 414 */ needsTileify(Drawable dr)415 private static boolean needsTileify(Drawable dr) { 416 if (dr instanceof LayerDrawable) { 417 final LayerDrawable orig = (LayerDrawable) dr; 418 final int N = orig.getNumberOfLayers(); 419 for (int i = 0; i < N; i++) { 420 if (needsTileify(orig.getDrawable(i))) { 421 return true; 422 } 423 } 424 return false; 425 } 426 427 if (dr instanceof StateListDrawable) { 428 final StateListDrawable in = (StateListDrawable) dr; 429 final int N = in.getStateCount(); 430 for (int i = 0; i < N; i++) { 431 if (needsTileify(in.getStateDrawable(i))) { 432 return true; 433 } 434 } 435 return false; 436 } 437 438 // If there's a bitmap that's not wrapped with a ClipDrawable or 439 // ScaleDrawable, we'll need to wrap it and apply tiling. 440 if (dr instanceof BitmapDrawable) { 441 return true; 442 } 443 444 return false; 445 } 446 447 /** 448 * Converts a drawable to a tiled version of itself. It will recursively 449 * traverse layer and state list drawables. 450 */ tileify(Drawable drawable, boolean clip)451 private Drawable tileify(Drawable drawable, boolean clip) { 452 // TODO: This is a terrible idea that potentially destroys any drawable 453 // that extends any of these classes. We *really* need to remove this. 454 455 if (drawable instanceof LayerDrawable) { 456 final LayerDrawable orig = (LayerDrawable) drawable; 457 final int N = orig.getNumberOfLayers(); 458 final Drawable[] outDrawables = new Drawable[N]; 459 460 for (int i = 0; i < N; i++) { 461 final int id = orig.getId(i); 462 outDrawables[i] = tileify(orig.getDrawable(i), 463 (id == R.id.progress || id == R.id.secondaryProgress)); 464 } 465 466 final LayerDrawable clone = new LayerDrawable(outDrawables); 467 for (int i = 0; i < N; i++) { 468 clone.setId(i, orig.getId(i)); 469 clone.setLayerGravity(i, orig.getLayerGravity(i)); 470 clone.setLayerWidth(i, orig.getLayerWidth(i)); 471 clone.setLayerHeight(i, orig.getLayerHeight(i)); 472 clone.setLayerInsetLeft(i, orig.getLayerInsetLeft(i)); 473 clone.setLayerInsetRight(i, orig.getLayerInsetRight(i)); 474 clone.setLayerInsetTop(i, orig.getLayerInsetTop(i)); 475 clone.setLayerInsetBottom(i, orig.getLayerInsetBottom(i)); 476 clone.setLayerInsetStart(i, orig.getLayerInsetStart(i)); 477 clone.setLayerInsetEnd(i, orig.getLayerInsetEnd(i)); 478 } 479 480 return clone; 481 } 482 483 if (drawable instanceof StateListDrawable) { 484 final StateListDrawable in = (StateListDrawable) drawable; 485 final StateListDrawable out = new StateListDrawable(); 486 final int N = in.getStateCount(); 487 for (int i = 0; i < N; i++) { 488 out.addState(in.getStateSet(i), tileify(in.getStateDrawable(i), clip)); 489 } 490 491 return out; 492 } 493 494 if (drawable instanceof BitmapDrawable) { 495 final BitmapDrawable bitmap = (BitmapDrawable) drawable; 496 final Bitmap tileBitmap = bitmap.getBitmap(); 497 if (mSampleTile == null) { 498 mSampleTile = tileBitmap; 499 } 500 501 final BitmapDrawable clone = (BitmapDrawable) bitmap.getConstantState().newDrawable(); 502 clone.setTileModeXY(Shader.TileMode.REPEAT, Shader.TileMode.CLAMP); 503 504 if (clip) { 505 return new ClipDrawable(clone, Gravity.LEFT, ClipDrawable.HORIZONTAL); 506 } else { 507 return clone; 508 } 509 } 510 511 return drawable; 512 } 513 getDrawableShape()514 Shape getDrawableShape() { 515 final float[] roundedCorners = new float[] { 5, 5, 5, 5, 5, 5, 5, 5 }; 516 return new RoundRectShape(roundedCorners, null, null); 517 } 518 519 /** 520 * Convert a AnimationDrawable for use as a barberpole animation. 521 * Each frame of the animation is wrapped in a ClipDrawable and 522 * given a tiling BitmapShader. 523 */ tileifyIndeterminate(Drawable drawable)524 private Drawable tileifyIndeterminate(Drawable drawable) { 525 if (drawable instanceof AnimationDrawable) { 526 AnimationDrawable background = (AnimationDrawable) drawable; 527 final int N = background.getNumberOfFrames(); 528 AnimationDrawable newBg = new AnimationDrawable(); 529 newBg.setOneShot(background.isOneShot()); 530 531 for (int i = 0; i < N; i++) { 532 Drawable frame = tileify(background.getFrame(i), true); 533 frame.setLevel(10000); 534 newBg.addFrame(frame, background.getDuration(i)); 535 } 536 newBg.setLevel(10000); 537 drawable = newBg; 538 } 539 return drawable; 540 } 541 542 /** 543 * <p> 544 * Initialize the progress bar's default values: 545 * </p> 546 * <ul> 547 * <li>progress = 0</li> 548 * <li>max = 100</li> 549 * <li>animation duration = 4000 ms</li> 550 * <li>indeterminate = false</li> 551 * <li>behavior = repeat</li> 552 * </ul> 553 */ initProgressBar()554 private void initProgressBar() { 555 mMax = 100; 556 mProgress = 0; 557 mSecondaryProgress = 0; 558 mIndeterminate = false; 559 mOnlyIndeterminate = false; 560 mDuration = 4000; 561 mBehavior = AlphaAnimation.RESTART; 562 mMinWidth = 24; 563 mMaxWidth = 48; 564 mMinHeight = 24; 565 mMaxHeight = 48; 566 } 567 568 /** 569 * <p>Indicate whether this progress bar is in indeterminate mode.</p> 570 * 571 * @return true if the progress bar is in indeterminate mode 572 */ 573 @ViewDebug.ExportedProperty(category = "progress") isIndeterminate()574 public synchronized boolean isIndeterminate() { 575 return mIndeterminate; 576 } 577 578 /** 579 * <p>Change the indeterminate mode for this progress bar. In indeterminate 580 * mode, the progress is ignored and the progress bar shows an infinite 581 * animation instead.</p> 582 * 583 * If this progress bar's style only supports indeterminate mode (such as the circular 584 * progress bars), then this will be ignored. 585 * 586 * @param indeterminate true to enable the indeterminate mode 587 */ 588 @android.view.RemotableViewMethod setIndeterminate(boolean indeterminate)589 public synchronized void setIndeterminate(boolean indeterminate) { 590 if ((!mOnlyIndeterminate || !mIndeterminate) && indeterminate != mIndeterminate) { 591 mIndeterminate = indeterminate; 592 593 if (indeterminate) { 594 // swap between indeterminate and regular backgrounds 595 mCurrentDrawable = mIndeterminateDrawable; 596 startAnimation(); 597 } else { 598 mCurrentDrawable = mProgressDrawable; 599 stopAnimation(); 600 } 601 } 602 } 603 604 /** 605 * <p>Get the drawable used to draw the progress bar in 606 * indeterminate mode.</p> 607 * 608 * @return a {@link android.graphics.drawable.Drawable} instance 609 * 610 * @see #setIndeterminateDrawable(android.graphics.drawable.Drawable) 611 * @see #setIndeterminate(boolean) 612 */ getIndeterminateDrawable()613 public Drawable getIndeterminateDrawable() { 614 return mIndeterminateDrawable; 615 } 616 617 /** 618 * Define the drawable used to draw the progress bar in indeterminate mode. 619 * 620 * @param d the new drawable 621 * @see #getIndeterminateDrawable() 622 * @see #setIndeterminate(boolean) 623 */ setIndeterminateDrawable(Drawable d)624 public void setIndeterminateDrawable(Drawable d) { 625 if (mIndeterminateDrawable != d) { 626 if (mIndeterminateDrawable != null) { 627 mIndeterminateDrawable.setCallback(null); 628 unscheduleDrawable(mIndeterminateDrawable); 629 } 630 631 mIndeterminateDrawable = d; 632 633 if (d != null) { 634 d.setCallback(this); 635 d.setLayoutDirection(getLayoutDirection()); 636 if (d.isStateful()) { 637 d.setState(getDrawableState()); 638 } 639 applyIndeterminateTint(); 640 } 641 642 if (mIndeterminate) { 643 mCurrentDrawable = d; 644 postInvalidate(); 645 } 646 } 647 } 648 649 /** 650 * Applies a tint to the indeterminate drawable. Does not modify the 651 * current tint mode, which is {@link PorterDuff.Mode#SRC_IN} by default. 652 * <p> 653 * Subsequent calls to {@link #setIndeterminateDrawable(Drawable)} will 654 * automatically mutate the drawable and apply the specified tint and 655 * tint mode using 656 * {@link Drawable#setTintList(ColorStateList)}. 657 * 658 * @param tint the tint to apply, may be {@code null} to clear tint 659 * 660 * @attr ref android.R.styleable#ProgressBar_indeterminateTint 661 * @see #getIndeterminateTintList() 662 * @see Drawable#setTintList(ColorStateList) 663 */ 664 @RemotableViewMethod setIndeterminateTintList(@ullable ColorStateList tint)665 public void setIndeterminateTintList(@Nullable ColorStateList tint) { 666 if (mProgressTintInfo == null) { 667 mProgressTintInfo = new ProgressTintInfo(); 668 } 669 mProgressTintInfo.mIndeterminateTintList = tint; 670 mProgressTintInfo.mHasIndeterminateTint = true; 671 672 applyIndeterminateTint(); 673 } 674 675 /** 676 * @return the tint applied to the indeterminate drawable 677 * @attr ref android.R.styleable#ProgressBar_indeterminateTint 678 * @see #setIndeterminateTintList(ColorStateList) 679 */ 680 @Nullable getIndeterminateTintList()681 public ColorStateList getIndeterminateTintList() { 682 return mProgressTintInfo != null ? mProgressTintInfo.mIndeterminateTintList : null; 683 } 684 685 /** 686 * Specifies the blending mode used to apply the tint specified by 687 * {@link #setIndeterminateTintList(ColorStateList)} to the indeterminate 688 * drawable. The default mode is {@link PorterDuff.Mode#SRC_IN}. 689 * 690 * @param tintMode the blending mode used to apply the tint, may be 691 * {@code null} to clear tint 692 * @attr ref android.R.styleable#ProgressBar_indeterminateTintMode 693 * @see #setIndeterminateTintList(ColorStateList) 694 * @see Drawable#setTintMode(PorterDuff.Mode) 695 */ setIndeterminateTintMode(@ullable PorterDuff.Mode tintMode)696 public void setIndeterminateTintMode(@Nullable PorterDuff.Mode tintMode) { 697 if (mProgressTintInfo == null) { 698 mProgressTintInfo = new ProgressTintInfo(); 699 } 700 mProgressTintInfo.mIndeterminateTintMode = tintMode; 701 mProgressTintInfo.mHasIndeterminateTintMode = true; 702 703 applyIndeterminateTint(); 704 } 705 706 /** 707 * Returns the blending mode used to apply the tint to the indeterminate 708 * drawable, if specified. 709 * 710 * @return the blending mode used to apply the tint to the indeterminate 711 * drawable 712 * @attr ref android.R.styleable#ProgressBar_indeterminateTintMode 713 * @see #setIndeterminateTintMode(PorterDuff.Mode) 714 */ 715 @Nullable getIndeterminateTintMode()716 public PorterDuff.Mode getIndeterminateTintMode() { 717 return mProgressTintInfo != null ? mProgressTintInfo.mIndeterminateTintMode : null; 718 } 719 applyIndeterminateTint()720 private void applyIndeterminateTint() { 721 if (mIndeterminateDrawable != null && mProgressTintInfo != null) { 722 final ProgressTintInfo tintInfo = mProgressTintInfo; 723 if (tintInfo.mHasIndeterminateTint || tintInfo.mHasIndeterminateTintMode) { 724 mIndeterminateDrawable = mIndeterminateDrawable.mutate(); 725 726 if (tintInfo.mHasIndeterminateTint) { 727 mIndeterminateDrawable.setTintList(tintInfo.mIndeterminateTintList); 728 } 729 730 if (tintInfo.mHasIndeterminateTintMode) { 731 mIndeterminateDrawable.setTintMode(tintInfo.mIndeterminateTintMode); 732 } 733 734 // The drawable (or one of its children) may not have been 735 // stateful before applying the tint, so let's try again. 736 if (mIndeterminateDrawable.isStateful()) { 737 mIndeterminateDrawable.setState(getDrawableState()); 738 } 739 } 740 } 741 } 742 743 /** 744 * Define the tileable drawable used to draw the progress bar in 745 * indeterminate mode. 746 * <p> 747 * If the drawable is a BitmapDrawable or contains BitmapDrawables, a 748 * tiled copy will be generated for display as a progress bar. 749 * 750 * @param d the new drawable 751 * @see #getIndeterminateDrawable() 752 * @see #setIndeterminate(boolean) 753 */ setIndeterminateDrawableTiled(Drawable d)754 public void setIndeterminateDrawableTiled(Drawable d) { 755 if (d != null) { 756 d = tileifyIndeterminate(d); 757 } 758 759 setIndeterminateDrawable(d); 760 } 761 762 /** 763 * <p>Get the drawable used to draw the progress bar in 764 * progress mode.</p> 765 * 766 * @return a {@link android.graphics.drawable.Drawable} instance 767 * 768 * @see #setProgressDrawable(android.graphics.drawable.Drawable) 769 * @see #setIndeterminate(boolean) 770 */ getProgressDrawable()771 public Drawable getProgressDrawable() { 772 return mProgressDrawable; 773 } 774 775 /** 776 * Define the drawable used to draw the progress bar in progress mode. 777 * 778 * @param d the new drawable 779 * @see #getProgressDrawable() 780 * @see #setIndeterminate(boolean) 781 */ setProgressDrawable(Drawable d)782 public void setProgressDrawable(Drawable d) { 783 if (mProgressDrawable != d) { 784 if (mProgressDrawable != null) { 785 mProgressDrawable.setCallback(null); 786 unscheduleDrawable(mProgressDrawable); 787 } 788 789 mProgressDrawable = d; 790 791 if (d != null) { 792 d.setCallback(this); 793 d.setLayoutDirection(getLayoutDirection()); 794 if (d.isStateful()) { 795 d.setState(getDrawableState()); 796 } 797 798 // Make sure the ProgressBar is always tall enough 799 int drawableHeight = d.getMinimumHeight(); 800 if (mMaxHeight < drawableHeight) { 801 mMaxHeight = drawableHeight; 802 requestLayout(); 803 } 804 805 applyProgressTints(); 806 } 807 808 if (!mIndeterminate) { 809 mCurrentDrawable = d; 810 postInvalidate(); 811 } 812 813 updateDrawableBounds(getWidth(), getHeight()); 814 updateDrawableState(); 815 816 doRefreshProgress(R.id.progress, mProgress, false, false); 817 doRefreshProgress(R.id.secondaryProgress, mSecondaryProgress, false, false); 818 } 819 } 820 821 /** 822 * Applies the progress tints in order of increasing specificity. 823 */ applyProgressTints()824 private void applyProgressTints() { 825 if (mProgressDrawable != null && mProgressTintInfo != null) { 826 applyPrimaryProgressTint(); 827 applyProgressBackgroundTint(); 828 applySecondaryProgressTint(); 829 } 830 } 831 832 /** 833 * Should only be called if we've already verified that mProgressDrawable 834 * and mProgressTintInfo are non-null. 835 */ applyPrimaryProgressTint()836 private void applyPrimaryProgressTint() { 837 if (mProgressTintInfo.mHasProgressTint 838 || mProgressTintInfo.mHasProgressTintMode) { 839 final Drawable target = getTintTarget(R.id.progress, true); 840 if (target != null) { 841 if (mProgressTintInfo.mHasProgressTint) { 842 target.setTintList(mProgressTintInfo.mProgressTintList); 843 } 844 if (mProgressTintInfo.mHasProgressTintMode) { 845 target.setTintMode(mProgressTintInfo.mProgressTintMode); 846 } 847 848 // The drawable (or one of its children) may not have been 849 // stateful before applying the tint, so let's try again. 850 if (target.isStateful()) { 851 target.setState(getDrawableState()); 852 } 853 } 854 } 855 } 856 857 /** 858 * Should only be called if we've already verified that mProgressDrawable 859 * and mProgressTintInfo are non-null. 860 */ applyProgressBackgroundTint()861 private void applyProgressBackgroundTint() { 862 if (mProgressTintInfo.mHasProgressBackgroundTint 863 || mProgressTintInfo.mHasProgressBackgroundTintMode) { 864 final Drawable target = getTintTarget(R.id.background, false); 865 if (target != null) { 866 if (mProgressTintInfo.mHasProgressBackgroundTint) { 867 target.setTintList(mProgressTintInfo.mProgressBackgroundTintList); 868 } 869 if (mProgressTintInfo.mHasProgressBackgroundTintMode) { 870 target.setTintMode(mProgressTintInfo.mProgressBackgroundTintMode); 871 } 872 873 // The drawable (or one of its children) may not have been 874 // stateful before applying the tint, so let's try again. 875 if (target.isStateful()) { 876 target.setState(getDrawableState()); 877 } 878 } 879 } 880 } 881 882 /** 883 * Should only be called if we've already verified that mProgressDrawable 884 * and mProgressTintInfo are non-null. 885 */ applySecondaryProgressTint()886 private void applySecondaryProgressTint() { 887 if (mProgressTintInfo.mHasSecondaryProgressTint 888 || mProgressTintInfo.mHasSecondaryProgressTintMode) { 889 final Drawable target = getTintTarget(R.id.secondaryProgress, false); 890 if (target != null) { 891 if (mProgressTintInfo.mHasSecondaryProgressTint) { 892 target.setTintList(mProgressTintInfo.mSecondaryProgressTintList); 893 } 894 if (mProgressTintInfo.mHasSecondaryProgressTintMode) { 895 target.setTintMode(mProgressTintInfo.mSecondaryProgressTintMode); 896 } 897 898 // The drawable (or one of its children) may not have been 899 // stateful before applying the tint, so let's try again. 900 if (target.isStateful()) { 901 target.setState(getDrawableState()); 902 } 903 } 904 } 905 } 906 907 /** 908 * Applies a tint to the progress indicator, if one exists, or to the 909 * entire progress drawable otherwise. Does not modify the current tint 910 * mode, which is {@link PorterDuff.Mode#SRC_IN} by default. 911 * <p> 912 * The progress indicator should be specified as a layer with 913 * id {@link android.R.id#progress} in a {@link LayerDrawable} 914 * used as the progress drawable. 915 * <p> 916 * Subsequent calls to {@link #setProgressDrawable(Drawable)} will 917 * automatically mutate the drawable and apply the specified tint and 918 * tint mode using 919 * {@link Drawable#setTintList(ColorStateList)}. 920 * 921 * @param tint the tint to apply, may be {@code null} to clear tint 922 * 923 * @attr ref android.R.styleable#ProgressBar_progressTint 924 * @see #getProgressTintList() 925 * @see Drawable#setTintList(ColorStateList) 926 */ 927 @RemotableViewMethod setProgressTintList(@ullable ColorStateList tint)928 public void setProgressTintList(@Nullable ColorStateList tint) { 929 if (mProgressTintInfo == null) { 930 mProgressTintInfo = new ProgressTintInfo(); 931 } 932 mProgressTintInfo.mProgressTintList = tint; 933 mProgressTintInfo.mHasProgressTint = true; 934 935 if (mProgressDrawable != null) { 936 applyPrimaryProgressTint(); 937 } 938 } 939 940 /** 941 * Returns the tint applied to the progress drawable, if specified. 942 * 943 * @return the tint applied to the progress drawable 944 * @attr ref android.R.styleable#ProgressBar_progressTint 945 * @see #setProgressTintList(ColorStateList) 946 */ 947 @Nullable getProgressTintList()948 public ColorStateList getProgressTintList() { 949 return mProgressTintInfo != null ? mProgressTintInfo.mProgressTintList : null; 950 } 951 952 /** 953 * Specifies the blending mode used to apply the tint specified by 954 * {@link #setProgressTintList(ColorStateList)}} to the progress 955 * indicator. The default mode is {@link PorterDuff.Mode#SRC_IN}. 956 * 957 * @param tintMode the blending mode used to apply the tint, may be 958 * {@code null} to clear tint 959 * @attr ref android.R.styleable#ProgressBar_progressTintMode 960 * @see #getProgressTintMode() 961 * @see Drawable#setTintMode(PorterDuff.Mode) 962 */ setProgressTintMode(@ullable PorterDuff.Mode tintMode)963 public void setProgressTintMode(@Nullable PorterDuff.Mode tintMode) { 964 if (mProgressTintInfo == null) { 965 mProgressTintInfo = new ProgressTintInfo(); 966 } 967 mProgressTintInfo.mProgressTintMode = tintMode; 968 mProgressTintInfo.mHasProgressTintMode = true; 969 970 if (mProgressDrawable != null) { 971 applyPrimaryProgressTint(); 972 } 973 } 974 975 /** 976 * Returns the blending mode used to apply the tint to the progress 977 * drawable, if specified. 978 * 979 * @return the blending mode used to apply the tint to the progress 980 * drawable 981 * @attr ref android.R.styleable#ProgressBar_progressTintMode 982 * @see #setProgressTintMode(PorterDuff.Mode) 983 */ 984 @Nullable getProgressTintMode()985 public PorterDuff.Mode getProgressTintMode() { 986 return mProgressTintInfo != null ? mProgressTintInfo.mProgressTintMode : null; 987 } 988 989 /** 990 * Applies a tint to the progress background, if one exists. Does not 991 * modify the current tint mode, which is 992 * {@link PorterDuff.Mode#SRC_ATOP} by default. 993 * <p> 994 * The progress background must be specified as a layer with 995 * id {@link android.R.id#background} in a {@link LayerDrawable} 996 * used as the progress drawable. 997 * <p> 998 * Subsequent calls to {@link #setProgressDrawable(Drawable)} where the 999 * drawable contains a progress background will automatically mutate the 1000 * drawable and apply the specified tint and tint mode using 1001 * {@link Drawable#setTintList(ColorStateList)}. 1002 * 1003 * @param tint the tint to apply, may be {@code null} to clear tint 1004 * 1005 * @attr ref android.R.styleable#ProgressBar_progressBackgroundTint 1006 * @see #getProgressBackgroundTintList() 1007 * @see Drawable#setTintList(ColorStateList) 1008 */ 1009 @RemotableViewMethod setProgressBackgroundTintList(@ullable ColorStateList tint)1010 public void setProgressBackgroundTintList(@Nullable ColorStateList tint) { 1011 if (mProgressTintInfo == null) { 1012 mProgressTintInfo = new ProgressTintInfo(); 1013 } 1014 mProgressTintInfo.mProgressBackgroundTintList = tint; 1015 mProgressTintInfo.mHasProgressBackgroundTint = true; 1016 1017 if (mProgressDrawable != null) { 1018 applyProgressBackgroundTint(); 1019 } 1020 } 1021 1022 /** 1023 * Returns the tint applied to the progress background, if specified. 1024 * 1025 * @return the tint applied to the progress background 1026 * @attr ref android.R.styleable#ProgressBar_progressBackgroundTint 1027 * @see #setProgressBackgroundTintList(ColorStateList) 1028 */ 1029 @Nullable getProgressBackgroundTintList()1030 public ColorStateList getProgressBackgroundTintList() { 1031 return mProgressTintInfo != null ? mProgressTintInfo.mProgressBackgroundTintList : null; 1032 } 1033 1034 /** 1035 * Specifies the blending mode used to apply the tint specified by 1036 * {@link #setProgressBackgroundTintList(ColorStateList)}} to the progress 1037 * background. The default mode is {@link PorterDuff.Mode#SRC_IN}. 1038 * 1039 * @param tintMode the blending mode used to apply the tint, may be 1040 * {@code null} to clear tint 1041 * @attr ref android.R.styleable#ProgressBar_progressBackgroundTintMode 1042 * @see #setProgressBackgroundTintList(ColorStateList) 1043 * @see Drawable#setTintMode(PorterDuff.Mode) 1044 */ setProgressBackgroundTintMode(@ullable PorterDuff.Mode tintMode)1045 public void setProgressBackgroundTintMode(@Nullable PorterDuff.Mode tintMode) { 1046 if (mProgressTintInfo == null) { 1047 mProgressTintInfo = new ProgressTintInfo(); 1048 } 1049 mProgressTintInfo.mProgressBackgroundTintMode = tintMode; 1050 mProgressTintInfo.mHasProgressBackgroundTintMode = true; 1051 1052 if (mProgressDrawable != null) { 1053 applyProgressBackgroundTint(); 1054 } 1055 } 1056 1057 /** 1058 * @return the blending mode used to apply the tint to the progress 1059 * background 1060 * @attr ref android.R.styleable#ProgressBar_progressBackgroundTintMode 1061 * @see #setProgressBackgroundTintMode(PorterDuff.Mode) 1062 */ 1063 @Nullable getProgressBackgroundTintMode()1064 public PorterDuff.Mode getProgressBackgroundTintMode() { 1065 return mProgressTintInfo != null ? mProgressTintInfo.mProgressBackgroundTintMode : null; 1066 } 1067 1068 /** 1069 * Applies a tint to the secondary progress indicator, if one exists. 1070 * Does not modify the current tint mode, which is 1071 * {@link PorterDuff.Mode#SRC_ATOP} by default. 1072 * <p> 1073 * The secondary progress indicator must be specified as a layer with 1074 * id {@link android.R.id#secondaryProgress} in a {@link LayerDrawable} 1075 * used as the progress drawable. 1076 * <p> 1077 * Subsequent calls to {@link #setProgressDrawable(Drawable)} where the 1078 * drawable contains a secondary progress indicator will automatically 1079 * mutate the drawable and apply the specified tint and tint mode using 1080 * {@link Drawable#setTintList(ColorStateList)}. 1081 * 1082 * @param tint the tint to apply, may be {@code null} to clear tint 1083 * 1084 * @attr ref android.R.styleable#ProgressBar_secondaryProgressTint 1085 * @see #getSecondaryProgressTintList() 1086 * @see Drawable#setTintList(ColorStateList) 1087 */ setSecondaryProgressTintList(@ullable ColorStateList tint)1088 public void setSecondaryProgressTintList(@Nullable ColorStateList tint) { 1089 if (mProgressTintInfo == null) { 1090 mProgressTintInfo = new ProgressTintInfo(); 1091 } 1092 mProgressTintInfo.mSecondaryProgressTintList = tint; 1093 mProgressTintInfo.mHasSecondaryProgressTint = true; 1094 1095 if (mProgressDrawable != null) { 1096 applySecondaryProgressTint(); 1097 } 1098 } 1099 1100 /** 1101 * Returns the tint applied to the secondary progress drawable, if 1102 * specified. 1103 * 1104 * @return the tint applied to the secondary progress drawable 1105 * @attr ref android.R.styleable#ProgressBar_secondaryProgressTint 1106 * @see #setSecondaryProgressTintList(ColorStateList) 1107 */ 1108 @Nullable getSecondaryProgressTintList()1109 public ColorStateList getSecondaryProgressTintList() { 1110 return mProgressTintInfo != null ? mProgressTintInfo.mSecondaryProgressTintList : null; 1111 } 1112 1113 /** 1114 * Specifies the blending mode used to apply the tint specified by 1115 * {@link #setSecondaryProgressTintList(ColorStateList)}} to the secondary 1116 * progress indicator. The default mode is 1117 * {@link PorterDuff.Mode#SRC_ATOP}. 1118 * 1119 * @param tintMode the blending mode used to apply the tint, may be 1120 * {@code null} to clear tint 1121 * @attr ref android.R.styleable#ProgressBar_secondaryProgressTintMode 1122 * @see #setSecondaryProgressTintList(ColorStateList) 1123 * @see Drawable#setTintMode(PorterDuff.Mode) 1124 */ setSecondaryProgressTintMode(@ullable PorterDuff.Mode tintMode)1125 public void setSecondaryProgressTintMode(@Nullable PorterDuff.Mode tintMode) { 1126 if (mProgressTintInfo == null) { 1127 mProgressTintInfo = new ProgressTintInfo(); 1128 } 1129 mProgressTintInfo.mSecondaryProgressTintMode = tintMode; 1130 mProgressTintInfo.mHasSecondaryProgressTintMode = true; 1131 1132 if (mProgressDrawable != null) { 1133 applySecondaryProgressTint(); 1134 } 1135 } 1136 1137 /** 1138 * Returns the blending mode used to apply the tint to the secondary 1139 * progress drawable, if specified. 1140 * 1141 * @return the blending mode used to apply the tint to the secondary 1142 * progress drawable 1143 * @attr ref android.R.styleable#ProgressBar_secondaryProgressTintMode 1144 * @see #setSecondaryProgressTintMode(PorterDuff.Mode) 1145 */ 1146 @Nullable getSecondaryProgressTintMode()1147 public PorterDuff.Mode getSecondaryProgressTintMode() { 1148 return mProgressTintInfo != null ? mProgressTintInfo.mSecondaryProgressTintMode : null; 1149 } 1150 1151 /** 1152 * Returns the drawable to which a tint or tint mode should be applied. 1153 * 1154 * @param layerId id of the layer to modify 1155 * @param shouldFallback whether the base drawable should be returned 1156 * if the id does not exist 1157 * @return the drawable to modify 1158 */ 1159 @Nullable getTintTarget(int layerId, boolean shouldFallback)1160 private Drawable getTintTarget(int layerId, boolean shouldFallback) { 1161 Drawable layer = null; 1162 1163 final Drawable d = mProgressDrawable; 1164 if (d != null) { 1165 mProgressDrawable = d.mutate(); 1166 1167 if (d instanceof LayerDrawable) { 1168 layer = ((LayerDrawable) d).findDrawableByLayerId(layerId); 1169 } 1170 1171 if (shouldFallback && layer == null) { 1172 layer = d; 1173 } 1174 } 1175 1176 return layer; 1177 } 1178 1179 /** 1180 * Define the tileable drawable used to draw the progress bar in 1181 * progress mode. 1182 * <p> 1183 * If the drawable is a BitmapDrawable or contains BitmapDrawables, a 1184 * tiled copy will be generated for display as a progress bar. 1185 * 1186 * @param d the new drawable 1187 * @see #getProgressDrawable() 1188 * @see #setIndeterminate(boolean) 1189 */ setProgressDrawableTiled(Drawable d)1190 public void setProgressDrawableTiled(Drawable d) { 1191 if (d != null) { 1192 d = tileify(d, false); 1193 } 1194 1195 setProgressDrawable(d); 1196 } 1197 1198 /** 1199 * @return The drawable currently used to draw the progress bar 1200 */ getCurrentDrawable()1201 Drawable getCurrentDrawable() { 1202 return mCurrentDrawable; 1203 } 1204 1205 @Override verifyDrawable(Drawable who)1206 protected boolean verifyDrawable(Drawable who) { 1207 return who == mProgressDrawable || who == mIndeterminateDrawable 1208 || super.verifyDrawable(who); 1209 } 1210 1211 @Override jumpDrawablesToCurrentState()1212 public void jumpDrawablesToCurrentState() { 1213 super.jumpDrawablesToCurrentState(); 1214 if (mProgressDrawable != null) mProgressDrawable.jumpToCurrentState(); 1215 if (mIndeterminateDrawable != null) mIndeterminateDrawable.jumpToCurrentState(); 1216 } 1217 1218 /** 1219 * @hide 1220 */ 1221 @Override onResolveDrawables(int layoutDirection)1222 public void onResolveDrawables(int layoutDirection) { 1223 final Drawable d = mCurrentDrawable; 1224 if (d != null) { 1225 d.setLayoutDirection(layoutDirection); 1226 } 1227 if (mIndeterminateDrawable != null) { 1228 mIndeterminateDrawable.setLayoutDirection(layoutDirection); 1229 } 1230 if (mProgressDrawable != null) { 1231 mProgressDrawable.setLayoutDirection(layoutDirection); 1232 } 1233 } 1234 1235 @Override postInvalidate()1236 public void postInvalidate() { 1237 if (!mNoInvalidate) { 1238 super.postInvalidate(); 1239 } 1240 } 1241 1242 private class RefreshProgressRunnable implements Runnable { run()1243 public void run() { 1244 synchronized (ProgressBar.this) { 1245 final int count = mRefreshData.size(); 1246 for (int i = 0; i < count; i++) { 1247 final RefreshData rd = mRefreshData.get(i); 1248 doRefreshProgress(rd.id, rd.progress, rd.fromUser, true); 1249 rd.recycle(); 1250 } 1251 mRefreshData.clear(); 1252 mRefreshIsPosted = false; 1253 } 1254 } 1255 } 1256 1257 private static class RefreshData { 1258 private static final int POOL_MAX = 24; 1259 private static final SynchronizedPool<RefreshData> sPool = 1260 new SynchronizedPool<RefreshData>(POOL_MAX); 1261 1262 public int id; 1263 public int progress; 1264 public boolean fromUser; 1265 obtain(int id, int progress, boolean fromUser)1266 public static RefreshData obtain(int id, int progress, boolean fromUser) { 1267 RefreshData rd = sPool.acquire(); 1268 if (rd == null) { 1269 rd = new RefreshData(); 1270 } 1271 rd.id = id; 1272 rd.progress = progress; 1273 rd.fromUser = fromUser; 1274 return rd; 1275 } 1276 recycle()1277 public void recycle() { 1278 sPool.release(this); 1279 } 1280 } 1281 doRefreshProgress(int id, int progress, boolean fromUser, boolean callBackToApp)1282 private synchronized void doRefreshProgress(int id, int progress, boolean fromUser, 1283 boolean callBackToApp) { 1284 float scale = mMax > 0 ? (float) progress / (float) mMax : 0; 1285 final Drawable d = mCurrentDrawable; 1286 if (d != null) { 1287 Drawable progressDrawable = null; 1288 1289 if (d instanceof LayerDrawable) { 1290 progressDrawable = ((LayerDrawable) d).findDrawableByLayerId(id); 1291 if (progressDrawable != null && canResolveLayoutDirection()) { 1292 progressDrawable.setLayoutDirection(getLayoutDirection()); 1293 } 1294 } 1295 1296 final int level = (int) (scale * MAX_LEVEL); 1297 (progressDrawable != null ? progressDrawable : d).setLevel(level); 1298 } else { 1299 invalidate(); 1300 } 1301 1302 if (callBackToApp && id == R.id.progress) { 1303 onProgressRefresh(scale, fromUser, progress); 1304 } 1305 } 1306 onProgressRefresh(float scale, boolean fromUser, int progress)1307 void onProgressRefresh(float scale, boolean fromUser, int progress) { 1308 if (AccessibilityManager.getInstance(mContext).isEnabled()) { 1309 scheduleAccessibilityEventSender(); 1310 } 1311 } 1312 refreshProgress(int id, int progress, boolean fromUser)1313 private synchronized void refreshProgress(int id, int progress, boolean fromUser) { 1314 if (mUiThreadId == Thread.currentThread().getId()) { 1315 doRefreshProgress(id, progress, fromUser, true); 1316 } else { 1317 if (mRefreshProgressRunnable == null) { 1318 mRefreshProgressRunnable = new RefreshProgressRunnable(); 1319 } 1320 1321 final RefreshData rd = RefreshData.obtain(id, progress, fromUser); 1322 mRefreshData.add(rd); 1323 if (mAttached && !mRefreshIsPosted) { 1324 post(mRefreshProgressRunnable); 1325 mRefreshIsPosted = true; 1326 } 1327 } 1328 } 1329 1330 /** 1331 * <p>Set the current progress to the specified value. Does not do anything 1332 * if the progress bar is in indeterminate mode.</p> 1333 * 1334 * @param progress the new progress, between 0 and {@link #getMax()} 1335 * 1336 * @see #setIndeterminate(boolean) 1337 * @see #isIndeterminate() 1338 * @see #getProgress() 1339 * @see #incrementProgressBy(int) 1340 */ 1341 @android.view.RemotableViewMethod setProgress(int progress)1342 public synchronized void setProgress(int progress) { 1343 setProgress(progress, false); 1344 } 1345 1346 @android.view.RemotableViewMethod setProgress(int progress, boolean fromUser)1347 synchronized boolean setProgress(int progress, boolean fromUser) { 1348 if (mIndeterminate) { 1349 // Not applicable. 1350 return false; 1351 } 1352 1353 progress = MathUtils.constrain(progress, 0, mMax); 1354 1355 if (progress == mProgress) { 1356 // No change from current. 1357 return false; 1358 } 1359 1360 mProgress = progress; 1361 refreshProgress(R.id.progress, mProgress, fromUser); 1362 return true; 1363 } 1364 1365 /** 1366 * <p> 1367 * Set the current secondary progress to the specified value. Does not do 1368 * anything if the progress bar is in indeterminate mode. 1369 * </p> 1370 * 1371 * @param secondaryProgress the new secondary progress, between 0 and {@link #getMax()} 1372 * @see #setIndeterminate(boolean) 1373 * @see #isIndeterminate() 1374 * @see #getSecondaryProgress() 1375 * @see #incrementSecondaryProgressBy(int) 1376 */ 1377 @android.view.RemotableViewMethod setSecondaryProgress(int secondaryProgress)1378 public synchronized void setSecondaryProgress(int secondaryProgress) { 1379 if (mIndeterminate) { 1380 return; 1381 } 1382 1383 if (secondaryProgress < 0) { 1384 secondaryProgress = 0; 1385 } 1386 1387 if (secondaryProgress > mMax) { 1388 secondaryProgress = mMax; 1389 } 1390 1391 if (secondaryProgress != mSecondaryProgress) { 1392 mSecondaryProgress = secondaryProgress; 1393 refreshProgress(R.id.secondaryProgress, mSecondaryProgress, false); 1394 } 1395 } 1396 1397 /** 1398 * <p>Get the progress bar's current level of progress. Return 0 when the 1399 * progress bar is in indeterminate mode.</p> 1400 * 1401 * @return the current progress, between 0 and {@link #getMax()} 1402 * 1403 * @see #setIndeterminate(boolean) 1404 * @see #isIndeterminate() 1405 * @see #setProgress(int) 1406 * @see #setMax(int) 1407 * @see #getMax() 1408 */ 1409 @ViewDebug.ExportedProperty(category = "progress") getProgress()1410 public synchronized int getProgress() { 1411 return mIndeterminate ? 0 : mProgress; 1412 } 1413 1414 /** 1415 * <p>Get the progress bar's current level of secondary progress. Return 0 when the 1416 * progress bar is in indeterminate mode.</p> 1417 * 1418 * @return the current secondary progress, between 0 and {@link #getMax()} 1419 * 1420 * @see #setIndeterminate(boolean) 1421 * @see #isIndeterminate() 1422 * @see #setSecondaryProgress(int) 1423 * @see #setMax(int) 1424 * @see #getMax() 1425 */ 1426 @ViewDebug.ExportedProperty(category = "progress") getSecondaryProgress()1427 public synchronized int getSecondaryProgress() { 1428 return mIndeterminate ? 0 : mSecondaryProgress; 1429 } 1430 1431 /** 1432 * <p>Return the upper limit of this progress bar's range.</p> 1433 * 1434 * @return a positive integer 1435 * 1436 * @see #setMax(int) 1437 * @see #getProgress() 1438 * @see #getSecondaryProgress() 1439 */ 1440 @ViewDebug.ExportedProperty(category = "progress") getMax()1441 public synchronized int getMax() { 1442 return mMax; 1443 } 1444 1445 /** 1446 * <p>Set the range of the progress bar to 0...<tt>max</tt>.</p> 1447 * 1448 * @param max the upper range of this progress bar 1449 * 1450 * @see #getMax() 1451 * @see #setProgress(int) 1452 * @see #setSecondaryProgress(int) 1453 */ 1454 @android.view.RemotableViewMethod setMax(int max)1455 public synchronized void setMax(int max) { 1456 if (max < 0) { 1457 max = 0; 1458 } 1459 if (max != mMax) { 1460 mMax = max; 1461 postInvalidate(); 1462 1463 if (mProgress > max) { 1464 mProgress = max; 1465 } 1466 refreshProgress(R.id.progress, mProgress, false); 1467 } 1468 } 1469 1470 /** 1471 * <p>Increase the progress bar's progress by the specified amount.</p> 1472 * 1473 * @param diff the amount by which the progress must be increased 1474 * 1475 * @see #setProgress(int) 1476 */ incrementProgressBy(int diff)1477 public synchronized final void incrementProgressBy(int diff) { 1478 setProgress(mProgress + diff); 1479 } 1480 1481 /** 1482 * <p>Increase the progress bar's secondary progress by the specified amount.</p> 1483 * 1484 * @param diff the amount by which the secondary progress must be increased 1485 * 1486 * @see #setSecondaryProgress(int) 1487 */ incrementSecondaryProgressBy(int diff)1488 public synchronized final void incrementSecondaryProgressBy(int diff) { 1489 setSecondaryProgress(mSecondaryProgress + diff); 1490 } 1491 1492 /** 1493 * <p>Start the indeterminate progress animation.</p> 1494 */ startAnimation()1495 void startAnimation() { 1496 if (getVisibility() != VISIBLE) { 1497 return; 1498 } 1499 1500 if (mIndeterminateDrawable instanceof Animatable) { 1501 mShouldStartAnimationDrawable = true; 1502 mHasAnimation = false; 1503 } else { 1504 mHasAnimation = true; 1505 1506 if (mInterpolator == null) { 1507 mInterpolator = new LinearInterpolator(); 1508 } 1509 1510 if (mTransformation == null) { 1511 mTransformation = new Transformation(); 1512 } else { 1513 mTransformation.clear(); 1514 } 1515 1516 if (mAnimation == null) { 1517 mAnimation = new AlphaAnimation(0.0f, 1.0f); 1518 } else { 1519 mAnimation.reset(); 1520 } 1521 1522 mAnimation.setRepeatMode(mBehavior); 1523 mAnimation.setRepeatCount(Animation.INFINITE); 1524 mAnimation.setDuration(mDuration); 1525 mAnimation.setInterpolator(mInterpolator); 1526 mAnimation.setStartTime(Animation.START_ON_FIRST_FRAME); 1527 } 1528 postInvalidate(); 1529 } 1530 1531 /** 1532 * <p>Stop the indeterminate progress animation.</p> 1533 */ stopAnimation()1534 void stopAnimation() { 1535 mHasAnimation = false; 1536 if (mIndeterminateDrawable instanceof Animatable) { 1537 ((Animatable) mIndeterminateDrawable).stop(); 1538 mShouldStartAnimationDrawable = false; 1539 } 1540 postInvalidate(); 1541 } 1542 1543 /** 1544 * Sets the acceleration curve for the indeterminate animation. 1545 * The interpolator is loaded as a resource from the specified context. 1546 * 1547 * @param context The application environment 1548 * @param resID The resource identifier of the interpolator to load 1549 */ setInterpolator(Context context, @InterpolatorRes int resID)1550 public void setInterpolator(Context context, @InterpolatorRes int resID) { 1551 setInterpolator(AnimationUtils.loadInterpolator(context, resID)); 1552 } 1553 1554 /** 1555 * Sets the acceleration curve for the indeterminate animation. 1556 * Defaults to a linear interpolation. 1557 * 1558 * @param interpolator The interpolator which defines the acceleration curve 1559 */ setInterpolator(Interpolator interpolator)1560 public void setInterpolator(Interpolator interpolator) { 1561 mInterpolator = interpolator; 1562 } 1563 1564 /** 1565 * Gets the acceleration curve type for the indeterminate animation. 1566 * 1567 * @return the {@link Interpolator} associated to this animation 1568 */ getInterpolator()1569 public Interpolator getInterpolator() { 1570 return mInterpolator; 1571 } 1572 1573 @Override 1574 @RemotableViewMethod setVisibility(int v)1575 public void setVisibility(int v) { 1576 if (getVisibility() != v) { 1577 super.setVisibility(v); 1578 1579 if (mIndeterminate) { 1580 // let's be nice with the UI thread 1581 if (v == GONE || v == INVISIBLE) { 1582 stopAnimation(); 1583 } else { 1584 startAnimation(); 1585 } 1586 } 1587 } 1588 } 1589 1590 @Override onVisibilityChanged(View changedView, int visibility)1591 protected void onVisibilityChanged(View changedView, int visibility) { 1592 super.onVisibilityChanged(changedView, visibility); 1593 1594 if (mIndeterminate) { 1595 // let's be nice with the UI thread 1596 if (visibility == GONE || visibility == INVISIBLE) { 1597 stopAnimation(); 1598 } else { 1599 startAnimation(); 1600 } 1601 } 1602 } 1603 1604 @Override invalidateDrawable(Drawable dr)1605 public void invalidateDrawable(Drawable dr) { 1606 if (!mInDrawing) { 1607 if (verifyDrawable(dr)) { 1608 final Rect dirty = dr.getBounds(); 1609 final int scrollX = mScrollX + mPaddingLeft; 1610 final int scrollY = mScrollY + mPaddingTop; 1611 1612 invalidate(dirty.left + scrollX, dirty.top + scrollY, 1613 dirty.right + scrollX, dirty.bottom + scrollY); 1614 } else { 1615 super.invalidateDrawable(dr); 1616 } 1617 } 1618 } 1619 1620 @Override onSizeChanged(int w, int h, int oldw, int oldh)1621 protected void onSizeChanged(int w, int h, int oldw, int oldh) { 1622 updateDrawableBounds(w, h); 1623 } 1624 updateDrawableBounds(int w, int h)1625 private void updateDrawableBounds(int w, int h) { 1626 // onDraw will translate the canvas so we draw starting at 0,0. 1627 // Subtract out padding for the purposes of the calculations below. 1628 w -= mPaddingRight + mPaddingLeft; 1629 h -= mPaddingTop + mPaddingBottom; 1630 1631 int right = w; 1632 int bottom = h; 1633 int top = 0; 1634 int left = 0; 1635 1636 if (mIndeterminateDrawable != null) { 1637 // Aspect ratio logic does not apply to AnimationDrawables 1638 if (mOnlyIndeterminate && !(mIndeterminateDrawable instanceof AnimationDrawable)) { 1639 // Maintain aspect ratio. Certain kinds of animated drawables 1640 // get very confused otherwise. 1641 final int intrinsicWidth = mIndeterminateDrawable.getIntrinsicWidth(); 1642 final int intrinsicHeight = mIndeterminateDrawable.getIntrinsicHeight(); 1643 final float intrinsicAspect = (float) intrinsicWidth / intrinsicHeight; 1644 final float boundAspect = (float) w / h; 1645 if (intrinsicAspect != boundAspect) { 1646 if (boundAspect > intrinsicAspect) { 1647 // New width is larger. Make it smaller to match height. 1648 final int width = (int) (h * intrinsicAspect); 1649 left = (w - width) / 2; 1650 right = left + width; 1651 } else { 1652 // New height is larger. Make it smaller to match width. 1653 final int height = (int) (w * (1 / intrinsicAspect)); 1654 top = (h - height) / 2; 1655 bottom = top + height; 1656 } 1657 } 1658 } 1659 if (isLayoutRtl() && mMirrorForRtl) { 1660 int tempLeft = left; 1661 left = w - right; 1662 right = w - tempLeft; 1663 } 1664 mIndeterminateDrawable.setBounds(left, top, right, bottom); 1665 } 1666 1667 if (mProgressDrawable != null) { 1668 mProgressDrawable.setBounds(0, 0, right, bottom); 1669 } 1670 } 1671 1672 @Override onDraw(Canvas canvas)1673 protected synchronized void onDraw(Canvas canvas) { 1674 super.onDraw(canvas); 1675 1676 drawTrack(canvas); 1677 } 1678 1679 /** 1680 * Draws the progress bar track. 1681 */ drawTrack(Canvas canvas)1682 void drawTrack(Canvas canvas) { 1683 final Drawable d = mCurrentDrawable; 1684 if (d != null) { 1685 // Translate canvas so a indeterminate circular progress bar with padding 1686 // rotates properly in its animation 1687 final int saveCount = canvas.save(); 1688 1689 if (isLayoutRtl() && mMirrorForRtl) { 1690 canvas.translate(getWidth() - mPaddingRight, mPaddingTop); 1691 canvas.scale(-1.0f, 1.0f); 1692 } else { 1693 canvas.translate(mPaddingLeft, mPaddingTop); 1694 } 1695 1696 final long time = getDrawingTime(); 1697 if (mHasAnimation) { 1698 mAnimation.getTransformation(time, mTransformation); 1699 final float scale = mTransformation.getAlpha(); 1700 try { 1701 mInDrawing = true; 1702 d.setLevel((int) (scale * MAX_LEVEL)); 1703 } finally { 1704 mInDrawing = false; 1705 } 1706 postInvalidateOnAnimation(); 1707 } 1708 1709 d.draw(canvas); 1710 canvas.restoreToCount(saveCount); 1711 1712 if (mShouldStartAnimationDrawable && d instanceof Animatable) { 1713 ((Animatable) d).start(); 1714 mShouldStartAnimationDrawable = false; 1715 } 1716 } 1717 } 1718 1719 @Override onMeasure(int widthMeasureSpec, int heightMeasureSpec)1720 protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 1721 int dw = 0; 1722 int dh = 0; 1723 1724 final Drawable d = mCurrentDrawable; 1725 if (d != null) { 1726 dw = Math.max(mMinWidth, Math.min(mMaxWidth, d.getIntrinsicWidth())); 1727 dh = Math.max(mMinHeight, Math.min(mMaxHeight, d.getIntrinsicHeight())); 1728 } 1729 1730 updateDrawableState(); 1731 1732 dw += mPaddingLeft + mPaddingRight; 1733 dh += mPaddingTop + mPaddingBottom; 1734 1735 final int measuredWidth = resolveSizeAndState(dw, widthMeasureSpec, 0); 1736 final int measuredHeight = resolveSizeAndState(dh, heightMeasureSpec, 0); 1737 setMeasuredDimension(measuredWidth, measuredHeight); 1738 } 1739 1740 @Override drawableStateChanged()1741 protected void drawableStateChanged() { 1742 super.drawableStateChanged(); 1743 updateDrawableState(); 1744 } 1745 updateDrawableState()1746 private void updateDrawableState() { 1747 final int[] state = getDrawableState(); 1748 1749 if (mProgressDrawable != null && mProgressDrawable.isStateful()) { 1750 mProgressDrawable.setState(state); 1751 } 1752 1753 if (mIndeterminateDrawable != null && mIndeterminateDrawable.isStateful()) { 1754 mIndeterminateDrawable.setState(state); 1755 } 1756 } 1757 1758 @Override drawableHotspotChanged(float x, float y)1759 public void drawableHotspotChanged(float x, float y) { 1760 super.drawableHotspotChanged(x, y); 1761 1762 if (mProgressDrawable != null) { 1763 mProgressDrawable.setHotspot(x, y); 1764 } 1765 1766 if (mIndeterminateDrawable != null) { 1767 mIndeterminateDrawable.setHotspot(x, y); 1768 } 1769 } 1770 1771 static class SavedState extends BaseSavedState { 1772 int progress; 1773 int secondaryProgress; 1774 1775 /** 1776 * Constructor called from {@link ProgressBar#onSaveInstanceState()} 1777 */ SavedState(Parcelable superState)1778 SavedState(Parcelable superState) { 1779 super(superState); 1780 } 1781 1782 /** 1783 * Constructor called from {@link #CREATOR} 1784 */ SavedState(Parcel in)1785 private SavedState(Parcel in) { 1786 super(in); 1787 progress = in.readInt(); 1788 secondaryProgress = in.readInt(); 1789 } 1790 1791 @Override writeToParcel(Parcel out, int flags)1792 public void writeToParcel(Parcel out, int flags) { 1793 super.writeToParcel(out, flags); 1794 out.writeInt(progress); 1795 out.writeInt(secondaryProgress); 1796 } 1797 1798 public static final Parcelable.Creator<SavedState> CREATOR 1799 = new Parcelable.Creator<SavedState>() { 1800 public SavedState createFromParcel(Parcel in) { 1801 return new SavedState(in); 1802 } 1803 1804 public SavedState[] newArray(int size) { 1805 return new SavedState[size]; 1806 } 1807 }; 1808 } 1809 1810 @Override onSaveInstanceState()1811 public Parcelable onSaveInstanceState() { 1812 // Force our ancestor class to save its state 1813 Parcelable superState = super.onSaveInstanceState(); 1814 SavedState ss = new SavedState(superState); 1815 1816 ss.progress = mProgress; 1817 ss.secondaryProgress = mSecondaryProgress; 1818 1819 return ss; 1820 } 1821 1822 @Override onRestoreInstanceState(Parcelable state)1823 public void onRestoreInstanceState(Parcelable state) { 1824 SavedState ss = (SavedState) state; 1825 super.onRestoreInstanceState(ss.getSuperState()); 1826 1827 setProgress(ss.progress); 1828 setSecondaryProgress(ss.secondaryProgress); 1829 } 1830 1831 @Override onAttachedToWindow()1832 protected void onAttachedToWindow() { 1833 super.onAttachedToWindow(); 1834 if (mIndeterminate) { 1835 startAnimation(); 1836 } 1837 if (mRefreshData != null) { 1838 synchronized (this) { 1839 final int count = mRefreshData.size(); 1840 for (int i = 0; i < count; i++) { 1841 final RefreshData rd = mRefreshData.get(i); 1842 doRefreshProgress(rd.id, rd.progress, rd.fromUser, true); 1843 rd.recycle(); 1844 } 1845 mRefreshData.clear(); 1846 } 1847 } 1848 mAttached = true; 1849 } 1850 1851 @Override onDetachedFromWindow()1852 protected void onDetachedFromWindow() { 1853 if (mIndeterminate) { 1854 stopAnimation(); 1855 } 1856 if (mRefreshProgressRunnable != null) { 1857 removeCallbacks(mRefreshProgressRunnable); 1858 mRefreshIsPosted = false; 1859 } 1860 if (mAccessibilityEventSender != null) { 1861 removeCallbacks(mAccessibilityEventSender); 1862 } 1863 // This should come after stopAnimation(), otherwise an invalidate message remains in the 1864 // queue, which can prevent the entire view hierarchy from being GC'ed during a rotation 1865 super.onDetachedFromWindow(); 1866 mAttached = false; 1867 } 1868 1869 @Override getAccessibilityClassName()1870 public CharSequence getAccessibilityClassName() { 1871 return ProgressBar.class.getName(); 1872 } 1873 1874 /** @hide */ 1875 @Override onInitializeAccessibilityEventInternal(AccessibilityEvent event)1876 public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) { 1877 super.onInitializeAccessibilityEventInternal(event); 1878 event.setItemCount(mMax); 1879 event.setCurrentItemIndex(mProgress); 1880 } 1881 1882 /** 1883 * Schedule a command for sending an accessibility event. 1884 * </br> 1885 * Note: A command is used to ensure that accessibility events 1886 * are sent at most one in a given time frame to save 1887 * system resources while the progress changes quickly. 1888 */ scheduleAccessibilityEventSender()1889 private void scheduleAccessibilityEventSender() { 1890 if (mAccessibilityEventSender == null) { 1891 mAccessibilityEventSender = new AccessibilityEventSender(); 1892 } else { 1893 removeCallbacks(mAccessibilityEventSender); 1894 } 1895 postDelayed(mAccessibilityEventSender, TIMEOUT_SEND_ACCESSIBILITY_EVENT); 1896 } 1897 1898 /** @hide */ 1899 @Override encodeProperties(@onNull ViewHierarchyEncoder stream)1900 protected void encodeProperties(@NonNull ViewHierarchyEncoder stream) { 1901 super.encodeProperties(stream); 1902 1903 stream.addProperty("progress:max", getMax()); 1904 stream.addProperty("progress:progress", getProgress()); 1905 stream.addProperty("progress:secondaryProgress", getSecondaryProgress()); 1906 stream.addProperty("progress:indeterminate", isIndeterminate()); 1907 } 1908 1909 /** 1910 * Command for sending an accessibility event. 1911 */ 1912 private class AccessibilityEventSender implements Runnable { run()1913 public void run() { 1914 sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED); 1915 } 1916 } 1917 1918 private static class ProgressTintInfo { 1919 ColorStateList mIndeterminateTintList; 1920 PorterDuff.Mode mIndeterminateTintMode; 1921 boolean mHasIndeterminateTint; 1922 boolean mHasIndeterminateTintMode; 1923 1924 ColorStateList mProgressTintList; 1925 PorterDuff.Mode mProgressTintMode; 1926 boolean mHasProgressTint; 1927 boolean mHasProgressTintMode; 1928 1929 ColorStateList mProgressBackgroundTintList; 1930 PorterDuff.Mode mProgressBackgroundTintMode; 1931 boolean mHasProgressBackgroundTint; 1932 boolean mHasProgressBackgroundTintMode; 1933 1934 ColorStateList mSecondaryProgressTintList; 1935 PorterDuff.Mode mSecondaryProgressTintMode; 1936 boolean mHasSecondaryProgressTint; 1937 boolean mHasSecondaryProgressTintMode; 1938 } 1939 } 1940