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.animation.ObjectAnimator; 20 import android.annotation.InterpolatorRes; 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.annotation.Px; 24 import android.annotation.UnsupportedAppUsage; 25 import android.content.Context; 26 import android.content.res.ColorStateList; 27 import android.content.res.TypedArray; 28 import android.graphics.BlendMode; 29 import android.graphics.Canvas; 30 import android.graphics.PorterDuff; 31 import android.graphics.Rect; 32 import android.graphics.Shader; 33 import android.graphics.drawable.Animatable; 34 import android.graphics.drawable.AnimationDrawable; 35 import android.graphics.drawable.BitmapDrawable; 36 import android.graphics.drawable.ClipDrawable; 37 import android.graphics.drawable.Drawable; 38 import android.graphics.drawable.LayerDrawable; 39 import android.graphics.drawable.StateListDrawable; 40 import android.graphics.drawable.shapes.RoundRectShape; 41 import android.graphics.drawable.shapes.Shape; 42 import android.os.Build; 43 import android.os.Parcel; 44 import android.os.Parcelable; 45 import android.util.AttributeSet; 46 import android.util.FloatProperty; 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.accessibility.AccessibilityNodeInfo; 57 import android.view.animation.AlphaAnimation; 58 import android.view.animation.Animation; 59 import android.view.animation.AnimationUtils; 60 import android.view.animation.DecelerateInterpolator; 61 import android.view.animation.Interpolator; 62 import android.view.animation.LinearInterpolator; 63 import android.view.animation.Transformation; 64 import android.view.inspector.InspectableProperty; 65 import android.widget.RemoteViews.RemoteView; 66 67 import com.android.internal.R; 68 69 import java.util.ArrayList; 70 71 /** 72 * <p> 73 * A user interface element that indicates the progress of an operation. 74 * Progress bar supports two modes to represent progress: determinate, and indeterminate. For 75 * a visual overview of the difference between determinate and indeterminate progress modes, see 76 * <a href="https://material.io/guidelines/components/progress-activity.html#progress-activity-types-of-indicators"> 77 * Progress & activity</a>. 78 * Display progress bars to a user in a non-interruptive way. 79 * Show the progress bar in your app's user interface or in a notification 80 * instead of within a dialog. 81 * </p> 82 * <h3>Indeterminate Progress</h3> 83 * <p> 84 * Use indeterminate mode for the progress bar when you do not know how long an 85 * operation will take. 86 * Indeterminate mode is the default for progress bar and shows a cyclic animation without a 87 * specific amount of progress indicated. 88 * The following example shows an indeterminate progress bar: 89 * <pre> 90 * <ProgressBar 91 * android:id="@+id/indeterminateBar" 92 * android:layout_width="wrap_content" 93 * android:layout_height="wrap_content" 94 * /> 95 * </pre> 96 * </p> 97 * <h3>Determinate Progress</h3> 98 * <p> 99 * Use determinate mode for the progress bar when you want to show that a specific quantity of 100 * progress has occurred. 101 * For example, the percent remaining of a file being retrieved, the amount records in 102 * a batch written to database, or the percent remaining of an audio file that is playing. 103 * <p> 104 * <p> 105 * To indicate determinate progress, you set the style of the progress bar to 106 * {@link android.R.style#Widget_ProgressBar_Horizontal} and set the amount of progress. 107 * The following example shows a determinate progress bar that is 25% complete: 108 * <pre> 109 * <ProgressBar 110 * android:id="@+id/determinateBar" 111 * style="@android:style/Widget.ProgressBar.Horizontal" 112 * android:layout_width="wrap_content" 113 * android:layout_height="wrap_content" 114 * android:progress="25"/> 115 * </pre> 116 * You can update the percentage of progress displayed by using the 117 * {@link #setProgress(int)} method, or by calling 118 * {@link #incrementProgressBy(int)} to increase the current progress completed 119 * by a specified amount. 120 * By default, the progress bar is full when the progress value reaches 100. 121 * You can adjust this default by setting the 122 * {@link android.R.styleable#ProgressBar_max android:max} attribute. 123 * </p> 124 * <p>Other progress bar styles provided by the system include:</p> 125 * <ul> 126 * <li>{@link android.R.style#Widget_ProgressBar_Horizontal Widget.ProgressBar.Horizontal}</li> 127 * <li>{@link android.R.style#Widget_ProgressBar_Small Widget.ProgressBar.Small}</li> 128 * <li>{@link android.R.style#Widget_ProgressBar_Large Widget.ProgressBar.Large}</li> 129 * <li>{@link android.R.style#Widget_ProgressBar_Inverse Widget.ProgressBar.Inverse}</li> 130 * <li>{@link android.R.style#Widget_ProgressBar_Small_Inverse 131 * Widget.ProgressBar.Small.Inverse}</li> 132 * <li>{@link android.R.style#Widget_ProgressBar_Large_Inverse 133 * Widget.ProgressBar.Large.Inverse}</li> 134 * </ul> 135 * <p>The "inverse" styles provide an inverse color scheme for the spinner, which may be necessary 136 * if your application uses a light colored theme (a white background).</p> 137 * 138 * <p><strong>XML attributes</b></strong> 139 * <p> 140 * See {@link android.R.styleable#ProgressBar ProgressBar Attributes}, 141 * {@link android.R.styleable#View View Attributes} 142 * </p> 143 * 144 * @attr ref android.R.styleable#ProgressBar_animationResolution 145 * @attr ref android.R.styleable#ProgressBar_indeterminate 146 * @attr ref android.R.styleable#ProgressBar_indeterminateBehavior 147 * @attr ref android.R.styleable#ProgressBar_indeterminateDrawable 148 * @attr ref android.R.styleable#ProgressBar_indeterminateDuration 149 * @attr ref android.R.styleable#ProgressBar_indeterminateOnly 150 * @attr ref android.R.styleable#ProgressBar_interpolator 151 * @attr ref android.R.styleable#ProgressBar_min 152 * @attr ref android.R.styleable#ProgressBar_max 153 * @attr ref android.R.styleable#ProgressBar_maxHeight 154 * @attr ref android.R.styleable#ProgressBar_maxWidth 155 * @attr ref android.R.styleable#ProgressBar_minHeight 156 * @attr ref android.R.styleable#ProgressBar_minWidth 157 * @attr ref android.R.styleable#ProgressBar_mirrorForRtl 158 * @attr ref android.R.styleable#ProgressBar_progress 159 * @attr ref android.R.styleable#ProgressBar_progressDrawable 160 * @attr ref android.R.styleable#ProgressBar_secondaryProgress 161 */ 162 @RemoteView 163 public class ProgressBar extends View { 164 165 private static final int MAX_LEVEL = 10000; 166 private static final int TIMEOUT_SEND_ACCESSIBILITY_EVENT = 200; 167 168 /** Interpolator used for smooth progress animations. */ 169 private static final DecelerateInterpolator PROGRESS_ANIM_INTERPOLATOR = 170 new DecelerateInterpolator(); 171 172 /** Duration of smooth progress animations. */ 173 private static final int PROGRESS_ANIM_DURATION = 80; 174 175 /** 176 * Outside the framework, please use {@link ProgressBar#getMinWidth()} and 177 * {@link ProgressBar#setMinWidth(int)} instead of accessing these directly. 178 */ 179 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) 180 int mMinWidth; 181 int mMaxWidth; 182 /** 183 * Outside the framework, please use {@link ProgressBar#getMinHeight()} and 184 * {@link ProgressBar#setMinHeight(int)} instead of accessing these directly. 185 */ 186 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) 187 int mMinHeight; 188 /** 189 * Outside the framework, please use {@link ProgressBar#getMaxHeight()} ()} and 190 * {@link ProgressBar#setMaxHeight(int)} (int)} instead of accessing these directly. 191 */ 192 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) 193 int mMaxHeight; 194 195 private int mProgress; 196 private int mSecondaryProgress; 197 private int mMin; 198 private boolean mMinInitialized; 199 private int mMax; 200 private boolean mMaxInitialized; 201 202 private int mBehavior; 203 // Better to define a Drawable that implements Animatable if you want to modify animation 204 // characteristics programatically. 205 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124052713) 206 private int mDuration; 207 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) 208 private boolean mIndeterminate; 209 @UnsupportedAppUsage(trackingBug = 124049927) 210 private boolean mOnlyIndeterminate; 211 private Transformation mTransformation; 212 private AlphaAnimation mAnimation; 213 private boolean mHasAnimation; 214 215 private Drawable mIndeterminateDrawable; 216 private Drawable mProgressDrawable; 217 /** 218 * Outside the framework, instead of accessing this directly, please use 219 * {@link #getCurrentDrawable()}, {@link #setProgressDrawable(Drawable)}, 220 * {@link #setIndeterminateDrawable(Drawable)} and their tiled versions. 221 */ 222 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) 223 private Drawable mCurrentDrawable; 224 private ProgressTintInfo mProgressTintInfo; 225 226 int mSampleWidth = 0; 227 private boolean mNoInvalidate; 228 private Interpolator mInterpolator; 229 private RefreshProgressRunnable mRefreshProgressRunnable; 230 private long mUiThreadId; 231 private boolean mShouldStartAnimationDrawable; 232 233 private boolean mInDrawing; 234 private boolean mAttached; 235 private boolean mRefreshIsPosted; 236 237 /** Value used to track progress animation, in the range [0...1]. */ 238 private float mVisualProgress; 239 240 @UnsupportedAppUsage 241 boolean mMirrorForRtl = false; 242 243 private boolean mAggregatedIsVisible; 244 245 private final ArrayList<RefreshData> mRefreshData = new ArrayList<RefreshData>(); 246 247 private AccessibilityEventSender mAccessibilityEventSender; 248 249 /** 250 * Create a new progress bar with range 0...100 and initial progress of 0. 251 * @param context the application environment 252 */ ProgressBar(Context context)253 public ProgressBar(Context context) { 254 this(context, null); 255 } 256 ProgressBar(Context context, AttributeSet attrs)257 public ProgressBar(Context context, AttributeSet attrs) { 258 this(context, attrs, com.android.internal.R.attr.progressBarStyle); 259 } 260 ProgressBar(Context context, AttributeSet attrs, int defStyleAttr)261 public ProgressBar(Context context, AttributeSet attrs, int defStyleAttr) { 262 this(context, attrs, defStyleAttr, 0); 263 } 264 ProgressBar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)265 public ProgressBar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 266 super(context, attrs, defStyleAttr, defStyleRes); 267 268 mUiThreadId = Thread.currentThread().getId(); 269 initProgressBar(); 270 271 final TypedArray a = context.obtainStyledAttributes( 272 attrs, R.styleable.ProgressBar, defStyleAttr, defStyleRes); 273 saveAttributeDataForStyleable(context, R.styleable.ProgressBar, 274 attrs, a, defStyleAttr, defStyleRes); 275 276 mNoInvalidate = true; 277 278 final Drawable progressDrawable = a.getDrawable(R.styleable.ProgressBar_progressDrawable); 279 if (progressDrawable != null) { 280 // Calling setProgressDrawable can set mMaxHeight, so make sure the 281 // corresponding XML attribute for mMaxHeight is read after calling 282 // this method. 283 if (needsTileify(progressDrawable)) { 284 setProgressDrawableTiled(progressDrawable); 285 } else { 286 setProgressDrawable(progressDrawable); 287 } 288 } 289 290 mDuration = a.getInt(R.styleable.ProgressBar_indeterminateDuration, mDuration); 291 292 mMinWidth = a.getDimensionPixelSize(R.styleable.ProgressBar_minWidth, mMinWidth); 293 mMaxWidth = a.getDimensionPixelSize(R.styleable.ProgressBar_maxWidth, mMaxWidth); 294 mMinHeight = a.getDimensionPixelSize(R.styleable.ProgressBar_minHeight, mMinHeight); 295 mMaxHeight = a.getDimensionPixelSize(R.styleable.ProgressBar_maxHeight, mMaxHeight); 296 297 mBehavior = a.getInt(R.styleable.ProgressBar_indeterminateBehavior, mBehavior); 298 299 final int resID = a.getResourceId( 300 com.android.internal.R.styleable.ProgressBar_interpolator, 301 android.R.anim.linear_interpolator); // default to linear interpolator 302 if (resID > 0) { 303 setInterpolator(context, resID); 304 } 305 306 setMin(a.getInt(R.styleable.ProgressBar_min, mMin)); 307 setMax(a.getInt(R.styleable.ProgressBar_max, mMax)); 308 309 setProgress(a.getInt(R.styleable.ProgressBar_progress, mProgress)); 310 311 setSecondaryProgress(a.getInt( 312 R.styleable.ProgressBar_secondaryProgress, mSecondaryProgress)); 313 314 final Drawable indeterminateDrawable = a.getDrawable( 315 R.styleable.ProgressBar_indeterminateDrawable); 316 if (indeterminateDrawable != null) { 317 if (needsTileify(indeterminateDrawable)) { 318 setIndeterminateDrawableTiled(indeterminateDrawable); 319 } else { 320 setIndeterminateDrawable(indeterminateDrawable); 321 } 322 } 323 324 mOnlyIndeterminate = a.getBoolean( 325 R.styleable.ProgressBar_indeterminateOnly, mOnlyIndeterminate); 326 327 mNoInvalidate = false; 328 329 setIndeterminate(mOnlyIndeterminate || a.getBoolean( 330 R.styleable.ProgressBar_indeterminate, mIndeterminate)); 331 332 mMirrorForRtl = a.getBoolean(R.styleable.ProgressBar_mirrorForRtl, mMirrorForRtl); 333 334 if (a.hasValue(R.styleable.ProgressBar_progressTintMode)) { 335 if (mProgressTintInfo == null) { 336 mProgressTintInfo = new ProgressTintInfo(); 337 } 338 mProgressTintInfo.mProgressBlendMode = Drawable.parseBlendMode(a.getInt( 339 R.styleable.ProgressBar_progressTintMode, -1), null); 340 mProgressTintInfo.mHasProgressTintMode = true; 341 } 342 343 if (a.hasValue(R.styleable.ProgressBar_progressTint)) { 344 if (mProgressTintInfo == null) { 345 mProgressTintInfo = new ProgressTintInfo(); 346 } 347 mProgressTintInfo.mProgressTintList = a.getColorStateList( 348 R.styleable.ProgressBar_progressTint); 349 mProgressTintInfo.mHasProgressTint = true; 350 } 351 352 if (a.hasValue(R.styleable.ProgressBar_progressBackgroundTintMode)) { 353 if (mProgressTintInfo == null) { 354 mProgressTintInfo = new ProgressTintInfo(); 355 } 356 mProgressTintInfo.mProgressBackgroundBlendMode = Drawable.parseBlendMode(a.getInt( 357 R.styleable.ProgressBar_progressBackgroundTintMode, -1), null); 358 mProgressTintInfo.mHasProgressBackgroundTintMode = true; 359 } 360 361 if (a.hasValue(R.styleable.ProgressBar_progressBackgroundTint)) { 362 if (mProgressTintInfo == null) { 363 mProgressTintInfo = new ProgressTintInfo(); 364 } 365 mProgressTintInfo.mProgressBackgroundTintList = a.getColorStateList( 366 R.styleable.ProgressBar_progressBackgroundTint); 367 mProgressTintInfo.mHasProgressBackgroundTint = true; 368 } 369 370 if (a.hasValue(R.styleable.ProgressBar_secondaryProgressTintMode)) { 371 if (mProgressTintInfo == null) { 372 mProgressTintInfo = new ProgressTintInfo(); 373 } 374 mProgressTintInfo.mSecondaryProgressBlendMode = Drawable.parseBlendMode( 375 a.getInt(R.styleable.ProgressBar_secondaryProgressTintMode, -1), null); 376 mProgressTintInfo.mHasSecondaryProgressTintMode = true; 377 } 378 379 if (a.hasValue(R.styleable.ProgressBar_secondaryProgressTint)) { 380 if (mProgressTintInfo == null) { 381 mProgressTintInfo = new ProgressTintInfo(); 382 } 383 mProgressTintInfo.mSecondaryProgressTintList = a.getColorStateList( 384 R.styleable.ProgressBar_secondaryProgressTint); 385 mProgressTintInfo.mHasSecondaryProgressTint = true; 386 } 387 388 if (a.hasValue(R.styleable.ProgressBar_indeterminateTintMode)) { 389 if (mProgressTintInfo == null) { 390 mProgressTintInfo = new ProgressTintInfo(); 391 } 392 mProgressTintInfo.mIndeterminateBlendMode = Drawable.parseBlendMode(a.getInt( 393 R.styleable.ProgressBar_indeterminateTintMode, -1), null); 394 mProgressTintInfo.mHasIndeterminateTintMode = true; 395 } 396 397 if (a.hasValue(R.styleable.ProgressBar_indeterminateTint)) { 398 if (mProgressTintInfo == null) { 399 mProgressTintInfo = new ProgressTintInfo(); 400 } 401 mProgressTintInfo.mIndeterminateTintList = a.getColorStateList( 402 R.styleable.ProgressBar_indeterminateTint); 403 mProgressTintInfo.mHasIndeterminateTint = true; 404 } 405 406 a.recycle(); 407 408 applyProgressTints(); 409 applyIndeterminateTint(); 410 411 // If not explicitly specified this view is important for accessibility. 412 if (getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) { 413 setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); 414 } 415 } 416 417 /** 418 * Sets the minimum width the progress bar can have. 419 * @param minWidth the minimum width to be set, in pixels 420 * @attr ref android.R.styleable#ProgressBar_minWidth 421 */ setMinWidth(@x int minWidth)422 public void setMinWidth(@Px int minWidth) { 423 mMinWidth = minWidth; 424 requestLayout(); 425 } 426 427 /** 428 * @return the minimum width the progress bar can have, in pixels 429 */ getMinWidth()430 @Px public int getMinWidth() { 431 return mMinWidth; 432 } 433 434 /** 435 * Sets the maximum width the progress bar can have. 436 * @param maxWidth the maximum width to be set, in pixels 437 * @attr ref android.R.styleable#ProgressBar_maxWidth 438 */ setMaxWidth(@x int maxWidth)439 public void setMaxWidth(@Px int maxWidth) { 440 mMaxWidth = maxWidth; 441 requestLayout(); 442 } 443 444 /** 445 * @return the maximum width the progress bar can have, in pixels 446 */ getMaxWidth()447 @Px public int getMaxWidth() { 448 return mMaxWidth; 449 } 450 451 /** 452 * Sets the minimum height the progress bar can have. 453 * @param minHeight the minimum height to be set, in pixels 454 * @attr ref android.R.styleable#ProgressBar_minHeight 455 */ setMinHeight(@x int minHeight)456 public void setMinHeight(@Px int minHeight) { 457 mMinHeight = minHeight; 458 requestLayout(); 459 } 460 461 /** 462 * @return the minimum height the progress bar can have, in pixels 463 */ getMinHeight()464 @Px public int getMinHeight() { 465 return mMinHeight; 466 } 467 468 /** 469 * Sets the maximum height the progress bar can have. 470 * @param maxHeight the maximum height to be set, in pixels 471 * @attr ref android.R.styleable#ProgressBar_maxHeight 472 */ setMaxHeight(@x int maxHeight)473 public void setMaxHeight(@Px int maxHeight) { 474 mMaxHeight = maxHeight; 475 requestLayout(); 476 } 477 478 /** 479 * @return the maximum height the progress bar can have, in pixels 480 */ getMaxHeight()481 @Px public int getMaxHeight() { 482 return mMaxHeight; 483 } 484 485 /** 486 * Returns {@code true} if the target drawable needs to be tileified. 487 * 488 * @param dr the drawable to check 489 * @return {@code true} if the target drawable needs to be tileified, 490 * {@code false} otherwise 491 */ needsTileify(Drawable dr)492 private static boolean needsTileify(Drawable dr) { 493 if (dr instanceof LayerDrawable) { 494 final LayerDrawable orig = (LayerDrawable) dr; 495 final int N = orig.getNumberOfLayers(); 496 for (int i = 0; i < N; i++) { 497 if (needsTileify(orig.getDrawable(i))) { 498 return true; 499 } 500 } 501 return false; 502 } 503 504 if (dr instanceof StateListDrawable) { 505 final StateListDrawable in = (StateListDrawable) dr; 506 final int N = in.getStateCount(); 507 for (int i = 0; i < N; i++) { 508 if (needsTileify(in.getStateDrawable(i))) { 509 return true; 510 } 511 } 512 return false; 513 } 514 515 // If there's a bitmap that's not wrapped with a ClipDrawable or 516 // ScaleDrawable, we'll need to wrap it and apply tiling. 517 if (dr instanceof BitmapDrawable) { 518 return true; 519 } 520 521 return false; 522 } 523 524 /** 525 * Converts a drawable to a tiled version of itself. It will recursively 526 * traverse layer and state list drawables. 527 */ 528 @UnsupportedAppUsage tileify(Drawable drawable, boolean clip)529 private Drawable tileify(Drawable drawable, boolean clip) { 530 // TODO: This is a terrible idea that potentially destroys any drawable 531 // that extends any of these classes. We *really* need to remove this. 532 533 if (drawable instanceof LayerDrawable) { 534 final LayerDrawable orig = (LayerDrawable) drawable; 535 final int N = orig.getNumberOfLayers(); 536 final Drawable[] outDrawables = new Drawable[N]; 537 538 for (int i = 0; i < N; i++) { 539 final int id = orig.getId(i); 540 outDrawables[i] = tileify(orig.getDrawable(i), 541 (id == R.id.progress || id == R.id.secondaryProgress)); 542 } 543 544 final LayerDrawable clone = new LayerDrawable(outDrawables); 545 for (int i = 0; i < N; i++) { 546 clone.setId(i, orig.getId(i)); 547 clone.setLayerGravity(i, orig.getLayerGravity(i)); 548 clone.setLayerWidth(i, orig.getLayerWidth(i)); 549 clone.setLayerHeight(i, orig.getLayerHeight(i)); 550 clone.setLayerInsetLeft(i, orig.getLayerInsetLeft(i)); 551 clone.setLayerInsetRight(i, orig.getLayerInsetRight(i)); 552 clone.setLayerInsetTop(i, orig.getLayerInsetTop(i)); 553 clone.setLayerInsetBottom(i, orig.getLayerInsetBottom(i)); 554 clone.setLayerInsetStart(i, orig.getLayerInsetStart(i)); 555 clone.setLayerInsetEnd(i, orig.getLayerInsetEnd(i)); 556 } 557 558 return clone; 559 } 560 561 if (drawable instanceof StateListDrawable) { 562 final StateListDrawable in = (StateListDrawable) drawable; 563 final StateListDrawable out = new StateListDrawable(); 564 final int N = in.getStateCount(); 565 for (int i = 0; i < N; i++) { 566 out.addState(in.getStateSet(i), tileify(in.getStateDrawable(i), clip)); 567 } 568 569 return out; 570 } 571 572 if (drawable instanceof BitmapDrawable) { 573 final Drawable.ConstantState cs = drawable.getConstantState(); 574 final BitmapDrawable clone = (BitmapDrawable) cs.newDrawable(getResources()); 575 clone.setTileModeXY(Shader.TileMode.REPEAT, Shader.TileMode.CLAMP); 576 577 if (mSampleWidth <= 0) { 578 mSampleWidth = clone.getIntrinsicWidth(); 579 } 580 581 if (clip) { 582 return new ClipDrawable(clone, Gravity.LEFT, ClipDrawable.HORIZONTAL); 583 } else { 584 return clone; 585 } 586 } 587 588 return drawable; 589 } 590 getDrawableShape()591 Shape getDrawableShape() { 592 final float[] roundedCorners = new float[] { 5, 5, 5, 5, 5, 5, 5, 5 }; 593 return new RoundRectShape(roundedCorners, null, null); 594 } 595 596 /** 597 * Convert a AnimationDrawable for use as a barberpole animation. 598 * Each frame of the animation is wrapped in a ClipDrawable and 599 * given a tiling BitmapShader. 600 */ tileifyIndeterminate(Drawable drawable)601 private Drawable tileifyIndeterminate(Drawable drawable) { 602 if (drawable instanceof AnimationDrawable) { 603 AnimationDrawable background = (AnimationDrawable) drawable; 604 final int N = background.getNumberOfFrames(); 605 AnimationDrawable newBg = new AnimationDrawable(); 606 newBg.setOneShot(background.isOneShot()); 607 608 for (int i = 0; i < N; i++) { 609 Drawable frame = tileify(background.getFrame(i), true); 610 frame.setLevel(10000); 611 newBg.addFrame(frame, background.getDuration(i)); 612 } 613 newBg.setLevel(10000); 614 drawable = newBg; 615 } 616 return drawable; 617 } 618 619 /** 620 * <p> 621 * Initialize the progress bar's default values: 622 * </p> 623 * <ul> 624 * <li>progress = 0</li> 625 * <li>max = 100</li> 626 * <li>animation duration = 4000 ms</li> 627 * <li>indeterminate = false</li> 628 * <li>behavior = repeat</li> 629 * </ul> 630 */ initProgressBar()631 private void initProgressBar() { 632 mMin = 0; 633 mMax = 100; 634 mProgress = 0; 635 mSecondaryProgress = 0; 636 mIndeterminate = false; 637 mOnlyIndeterminate = false; 638 mDuration = 4000; 639 mBehavior = AlphaAnimation.RESTART; 640 mMinWidth = 24; 641 mMaxWidth = 48; 642 mMinHeight = 24; 643 mMaxHeight = 48; 644 } 645 646 /** 647 * <p>Indicate whether this progress bar is in indeterminate mode.</p> 648 * 649 * @return true if the progress bar is in indeterminate mode 650 */ 651 @InspectableProperty 652 @ViewDebug.ExportedProperty(category = "progress") isIndeterminate()653 public synchronized boolean isIndeterminate() { 654 return mIndeterminate; 655 } 656 657 /** 658 * <p>Change the indeterminate mode for this progress bar. In indeterminate 659 * mode, the progress is ignored and the progress bar shows an infinite 660 * animation instead.</p> 661 * 662 * If this progress bar's style only supports indeterminate mode (such as the circular 663 * progress bars), then this will be ignored. 664 * 665 * @param indeterminate true to enable the indeterminate mode 666 */ 667 @android.view.RemotableViewMethod setIndeterminate(boolean indeterminate)668 public synchronized void setIndeterminate(boolean indeterminate) { 669 if ((!mOnlyIndeterminate || !mIndeterminate) && indeterminate != mIndeterminate) { 670 mIndeterminate = indeterminate; 671 672 if (indeterminate) { 673 // swap between indeterminate and regular backgrounds 674 swapCurrentDrawable(mIndeterminateDrawable); 675 startAnimation(); 676 } else { 677 swapCurrentDrawable(mProgressDrawable); 678 stopAnimation(); 679 } 680 } 681 } 682 swapCurrentDrawable(Drawable newDrawable)683 private void swapCurrentDrawable(Drawable newDrawable) { 684 final Drawable oldDrawable = mCurrentDrawable; 685 mCurrentDrawable = newDrawable; 686 687 if (oldDrawable != mCurrentDrawable) { 688 if (oldDrawable != null) { 689 oldDrawable.setVisible(false, false); 690 } 691 if (mCurrentDrawable != null) { 692 mCurrentDrawable.setVisible(getWindowVisibility() == VISIBLE && isShown(), false); 693 } 694 } 695 } 696 697 /** 698 * <p>Get the drawable used to draw the progress bar in 699 * indeterminate mode.</p> 700 * 701 * @return a {@link android.graphics.drawable.Drawable} instance 702 * 703 * @see #setIndeterminateDrawable(android.graphics.drawable.Drawable) 704 * @see #setIndeterminate(boolean) 705 */ 706 @InspectableProperty getIndeterminateDrawable()707 public Drawable getIndeterminateDrawable() { 708 return mIndeterminateDrawable; 709 } 710 711 /** 712 * Define the drawable used to draw the progress bar in indeterminate mode. 713 * 714 * <p>For the Drawable to animate, it must implement {@link Animatable}, or override 715 * {@link Drawable#onLevelChange(int)}. A Drawable that implements Animatable will be animated 716 * via that interface and therefore provides the greatest amount of customization. A Drawable 717 * that only overrides onLevelChange(int) is animated directly by ProgressBar and only the 718 * animation {@link android.R.styleable#ProgressBar_indeterminateDuration duration}, 719 * {@link android.R.styleable#ProgressBar_indeterminateBehavior repeating behavior}, and 720 * {@link #setInterpolator(Interpolator) interpolator} can be modified, and only before the 721 * indeterminate animation begins. 722 * 723 * @param d the new drawable 724 * @attr ref android.R.styleable#ProgressBar_indeterminateDrawable 725 * @see #getIndeterminateDrawable() 726 * @see #setIndeterminate(boolean) 727 */ setIndeterminateDrawable(Drawable d)728 public void setIndeterminateDrawable(Drawable d) { 729 if (mIndeterminateDrawable != d) { 730 if (mIndeterminateDrawable != null) { 731 mIndeterminateDrawable.setCallback(null); 732 unscheduleDrawable(mIndeterminateDrawable); 733 } 734 735 mIndeterminateDrawable = d; 736 737 if (d != null) { 738 d.setCallback(this); 739 d.setLayoutDirection(getLayoutDirection()); 740 if (d.isStateful()) { 741 d.setState(getDrawableState()); 742 } 743 applyIndeterminateTint(); 744 } 745 746 if (mIndeterminate) { 747 swapCurrentDrawable(d); 748 postInvalidate(); 749 } 750 } 751 } 752 753 /** 754 * Applies a tint to the indeterminate drawable. Does not modify the 755 * current tint mode, which is {@link PorterDuff.Mode#SRC_IN} by default. 756 * <p> 757 * Subsequent calls to {@link #setIndeterminateDrawable(Drawable)} will 758 * automatically mutate the drawable and apply the specified tint and 759 * tint mode using 760 * {@link Drawable#setTintList(ColorStateList)}. 761 * 762 * @param tint the tint to apply, may be {@code null} to clear tint 763 * 764 * @attr ref android.R.styleable#ProgressBar_indeterminateTint 765 * @see #getIndeterminateTintList() 766 * @see Drawable#setTintList(ColorStateList) 767 */ 768 @RemotableViewMethod setIndeterminateTintList(@ullable ColorStateList tint)769 public void setIndeterminateTintList(@Nullable ColorStateList tint) { 770 if (mProgressTintInfo == null) { 771 mProgressTintInfo = new ProgressTintInfo(); 772 } 773 mProgressTintInfo.mIndeterminateTintList = tint; 774 mProgressTintInfo.mHasIndeterminateTint = true; 775 776 applyIndeterminateTint(); 777 } 778 779 /** 780 * @return the tint applied to the indeterminate drawable 781 * @attr ref android.R.styleable#ProgressBar_indeterminateTint 782 * @see #setIndeterminateTintList(ColorStateList) 783 */ 784 @InspectableProperty(name = "indeterminateTint") 785 @Nullable getIndeterminateTintList()786 public ColorStateList getIndeterminateTintList() { 787 return mProgressTintInfo != null ? mProgressTintInfo.mIndeterminateTintList : null; 788 } 789 790 /** 791 * Specifies the blending mode used to apply the tint specified by 792 * {@link #setIndeterminateTintList(ColorStateList)} to the indeterminate 793 * drawable. The default mode is {@link PorterDuff.Mode#SRC_IN}. 794 * 795 * @param tintMode the blending mode used to apply the tint, may be 796 * {@code null} to clear tint 797 * @attr ref android.R.styleable#ProgressBar_indeterminateTintMode 798 * @see #setIndeterminateTintList(ColorStateList) 799 * @see Drawable#setTintMode(PorterDuff.Mode) 800 * 801 */ setIndeterminateTintMode(@ullable PorterDuff.Mode tintMode)802 public void setIndeterminateTintMode(@Nullable PorterDuff.Mode tintMode) { 803 setIndeterminateTintBlendMode(tintMode != null 804 ? BlendMode.fromValue(tintMode.nativeInt) : null); 805 } 806 807 /** 808 * Specifies the blending mode used to apply the tint specified by 809 * {@link #setIndeterminateTintList(ColorStateList)} to the indeterminate 810 * drawable. The default mode is {@link PorterDuff.Mode#SRC_IN}. 811 * 812 * @param blendMode the blending mode used to apply the tint, may be 813 * {@code null} to clear tint 814 * @attr ref android.R.styleable#ProgressBar_indeterminateTintMode 815 * @see #setIndeterminateTintList(ColorStateList) 816 * @see Drawable#setTintBlendMode(BlendMode) 817 */ setIndeterminateTintBlendMode(@ullable BlendMode blendMode)818 public void setIndeterminateTintBlendMode(@Nullable BlendMode blendMode) { 819 if (mProgressTintInfo == null) { 820 mProgressTintInfo = new ProgressTintInfo(); 821 } 822 mProgressTintInfo.mIndeterminateBlendMode = blendMode; 823 mProgressTintInfo.mHasIndeterminateTintMode = true; 824 825 applyIndeterminateTint(); 826 } 827 828 /** 829 * Returns the blending mode used to apply the tint to the indeterminate 830 * drawable, if specified. 831 * 832 * @return the blending mode used to apply the tint to the indeterminate 833 * drawable 834 * @attr ref android.R.styleable#ProgressBar_indeterminateTintMode 835 * @see #setIndeterminateTintMode(PorterDuff.Mode) 836 */ 837 @InspectableProperty 838 @Nullable getIndeterminateTintMode()839 public PorterDuff.Mode getIndeterminateTintMode() { 840 BlendMode mode = getIndeterminateTintBlendMode(); 841 return mode != null ? BlendMode.blendModeToPorterDuffMode(mode) : null; 842 } 843 844 /** 845 * Returns the blending mode used to apply the tint to the indeterminate 846 * drawable, if specified. 847 * 848 * @return the blending mode used to apply the tint to the indeterminate 849 * drawable 850 * @attr ref android.R.styleable#ProgressBar_indeterminateTintMode 851 * @see #setIndeterminateTintBlendMode(BlendMode) 852 */ 853 @InspectableProperty(attributeId = R.styleable.ProgressBar_indeterminateTintMode) 854 @Nullable getIndeterminateTintBlendMode()855 public BlendMode getIndeterminateTintBlendMode() { 856 return mProgressTintInfo != null ? mProgressTintInfo.mIndeterminateBlendMode : null; 857 } 858 applyIndeterminateTint()859 private void applyIndeterminateTint() { 860 if (mIndeterminateDrawable != null && mProgressTintInfo != null) { 861 final ProgressTintInfo tintInfo = mProgressTintInfo; 862 if (tintInfo.mHasIndeterminateTint || tintInfo.mHasIndeterminateTintMode) { 863 mIndeterminateDrawable = mIndeterminateDrawable.mutate(); 864 865 if (tintInfo.mHasIndeterminateTint) { 866 mIndeterminateDrawable.setTintList(tintInfo.mIndeterminateTintList); 867 } 868 869 if (tintInfo.mHasIndeterminateTintMode) { 870 mIndeterminateDrawable.setTintBlendMode(tintInfo.mIndeterminateBlendMode); 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 (mIndeterminateDrawable.isStateful()) { 876 mIndeterminateDrawable.setState(getDrawableState()); 877 } 878 } 879 } 880 } 881 882 /** 883 * Define the tileable drawable used to draw the progress bar in 884 * indeterminate mode. 885 * <p> 886 * If the drawable is a BitmapDrawable or contains BitmapDrawables, a 887 * tiled copy will be generated for display as a progress bar. 888 * 889 * @param d the new drawable 890 * @see #getIndeterminateDrawable() 891 * @see #setIndeterminate(boolean) 892 */ setIndeterminateDrawableTiled(Drawable d)893 public void setIndeterminateDrawableTiled(Drawable d) { 894 if (d != null) { 895 d = tileifyIndeterminate(d); 896 } 897 898 setIndeterminateDrawable(d); 899 } 900 901 /** 902 * <p>Get the drawable used to draw the progress bar in 903 * progress mode.</p> 904 * 905 * @return a {@link android.graphics.drawable.Drawable} instance 906 * 907 * @see #setProgressDrawable(android.graphics.drawable.Drawable) 908 * @see #setIndeterminate(boolean) 909 */ 910 @InspectableProperty getProgressDrawable()911 public Drawable getProgressDrawable() { 912 return mProgressDrawable; 913 } 914 915 /** 916 * Define the drawable used to draw the progress bar in progress mode. 917 * 918 * @param d the new drawable 919 * @see #getProgressDrawable() 920 * @see #setIndeterminate(boolean) 921 */ setProgressDrawable(Drawable d)922 public void setProgressDrawable(Drawable d) { 923 if (mProgressDrawable != d) { 924 if (mProgressDrawable != null) { 925 mProgressDrawable.setCallback(null); 926 unscheduleDrawable(mProgressDrawable); 927 } 928 929 mProgressDrawable = d; 930 931 if (d != null) { 932 d.setCallback(this); 933 d.setLayoutDirection(getLayoutDirection()); 934 if (d.isStateful()) { 935 d.setState(getDrawableState()); 936 } 937 938 // Make sure the ProgressBar is always tall enough 939 int drawableHeight = d.getMinimumHeight(); 940 if (mMaxHeight < drawableHeight) { 941 mMaxHeight = drawableHeight; 942 requestLayout(); 943 } 944 945 applyProgressTints(); 946 } 947 948 if (!mIndeterminate) { 949 swapCurrentDrawable(d); 950 postInvalidate(); 951 } 952 953 updateDrawableBounds(getWidth(), getHeight()); 954 updateDrawableState(); 955 956 doRefreshProgress(R.id.progress, mProgress, false, false, false); 957 doRefreshProgress(R.id.secondaryProgress, mSecondaryProgress, false, false, false); 958 } 959 } 960 961 /** 962 * @hide 963 */ 964 @InspectableProperty getMirrorForRtl()965 public boolean getMirrorForRtl() { 966 return mMirrorForRtl; 967 } 968 969 /** 970 * Applies the progress tints in order of increasing specificity. 971 */ applyProgressTints()972 private void applyProgressTints() { 973 if (mProgressDrawable != null && mProgressTintInfo != null) { 974 applyPrimaryProgressTint(); 975 applyProgressBackgroundTint(); 976 applySecondaryProgressTint(); 977 } 978 } 979 980 /** 981 * Should only be called if we've already verified that mProgressDrawable 982 * and mProgressTintInfo are non-null. 983 */ applyPrimaryProgressTint()984 private void applyPrimaryProgressTint() { 985 if (mProgressTintInfo.mHasProgressTint 986 || mProgressTintInfo.mHasProgressTintMode) { 987 final Drawable target = getTintTarget(R.id.progress, true); 988 if (target != null) { 989 if (mProgressTintInfo.mHasProgressTint) { 990 target.setTintList(mProgressTintInfo.mProgressTintList); 991 } 992 if (mProgressTintInfo.mHasProgressTintMode) { 993 target.setTintBlendMode(mProgressTintInfo.mProgressBlendMode); 994 } 995 996 // The drawable (or one of its children) may not have been 997 // stateful before applying the tint, so let's try again. 998 if (target.isStateful()) { 999 target.setState(getDrawableState()); 1000 } 1001 } 1002 } 1003 } 1004 1005 /** 1006 * Should only be called if we've already verified that mProgressDrawable 1007 * and mProgressTintInfo are non-null. 1008 */ applyProgressBackgroundTint()1009 private void applyProgressBackgroundTint() { 1010 if (mProgressTintInfo.mHasProgressBackgroundTint 1011 || mProgressTintInfo.mHasProgressBackgroundTintMode) { 1012 final Drawable target = getTintTarget(R.id.background, false); 1013 if (target != null) { 1014 if (mProgressTintInfo.mHasProgressBackgroundTint) { 1015 target.setTintList(mProgressTintInfo.mProgressBackgroundTintList); 1016 } 1017 if (mProgressTintInfo.mHasProgressBackgroundTintMode) { 1018 target.setTintBlendMode(mProgressTintInfo.mProgressBackgroundBlendMode); 1019 } 1020 1021 // The drawable (or one of its children) may not have been 1022 // stateful before applying the tint, so let's try again. 1023 if (target.isStateful()) { 1024 target.setState(getDrawableState()); 1025 } 1026 } 1027 } 1028 } 1029 1030 /** 1031 * Should only be called if we've already verified that mProgressDrawable 1032 * and mProgressTintInfo are non-null. 1033 */ applySecondaryProgressTint()1034 private void applySecondaryProgressTint() { 1035 if (mProgressTintInfo.mHasSecondaryProgressTint 1036 || mProgressTintInfo.mHasSecondaryProgressTintMode) { 1037 final Drawable target = getTintTarget(R.id.secondaryProgress, false); 1038 if (target != null) { 1039 if (mProgressTintInfo.mHasSecondaryProgressTint) { 1040 target.setTintList(mProgressTintInfo.mSecondaryProgressTintList); 1041 } 1042 if (mProgressTintInfo.mHasSecondaryProgressTintMode) { 1043 target.setTintBlendMode(mProgressTintInfo.mSecondaryProgressBlendMode); 1044 } 1045 1046 // The drawable (or one of its children) may not have been 1047 // stateful before applying the tint, so let's try again. 1048 if (target.isStateful()) { 1049 target.setState(getDrawableState()); 1050 } 1051 } 1052 } 1053 } 1054 1055 /** 1056 * Applies a tint to the progress indicator, if one exists, or to the 1057 * entire progress drawable otherwise. Does not modify the current tint 1058 * mode, which is {@link PorterDuff.Mode#SRC_IN} by default. 1059 * <p> 1060 * The progress indicator should be specified as a layer with 1061 * id {@link android.R.id#progress} in a {@link LayerDrawable} 1062 * used as the progress drawable. 1063 * <p> 1064 * Subsequent calls to {@link #setProgressDrawable(Drawable)} will 1065 * automatically mutate the drawable and apply the specified tint and 1066 * tint mode using 1067 * {@link Drawable#setTintList(ColorStateList)}. 1068 * 1069 * @param tint the tint to apply, may be {@code null} to clear tint 1070 * 1071 * @attr ref android.R.styleable#ProgressBar_progressTint 1072 * @see #getProgressTintList() 1073 * @see Drawable#setTintList(ColorStateList) 1074 */ 1075 @RemotableViewMethod setProgressTintList(@ullable ColorStateList tint)1076 public void setProgressTintList(@Nullable ColorStateList tint) { 1077 if (mProgressTintInfo == null) { 1078 mProgressTintInfo = new ProgressTintInfo(); 1079 } 1080 mProgressTintInfo.mProgressTintList = tint; 1081 mProgressTintInfo.mHasProgressTint = true; 1082 1083 if (mProgressDrawable != null) { 1084 applyPrimaryProgressTint(); 1085 } 1086 } 1087 1088 /** 1089 * Returns the tint applied to the progress drawable, if specified. 1090 * 1091 * @return the tint applied to the progress drawable 1092 * @attr ref android.R.styleable#ProgressBar_progressTint 1093 * @see #setProgressTintList(ColorStateList) 1094 */ 1095 @InspectableProperty(name = "progressTint") 1096 @Nullable getProgressTintList()1097 public ColorStateList getProgressTintList() { 1098 return mProgressTintInfo != null ? mProgressTintInfo.mProgressTintList : null; 1099 } 1100 1101 /** 1102 * Specifies the blending mode used to apply the tint specified by 1103 * {@link #setProgressTintList(ColorStateList)}} to the progress 1104 * indicator. The default mode is {@link PorterDuff.Mode#SRC_IN}. 1105 * 1106 * @param tintMode the blending mode used to apply the tint, may be 1107 * {@code null} to clear tint 1108 * @attr ref android.R.styleable#ProgressBar_progressTintMode 1109 * @see #getProgressTintMode() 1110 * @see Drawable#setTintMode(PorterDuff.Mode) 1111 */ setProgressTintMode(@ullable PorterDuff.Mode tintMode)1112 public void setProgressTintMode(@Nullable PorterDuff.Mode tintMode) { 1113 setProgressTintBlendMode(tintMode != null ? BlendMode.fromValue(tintMode.nativeInt) : null); 1114 } 1115 1116 /** 1117 * Specifies the blending mode used to apply the tint specified by 1118 * {@link #setProgressTintList(ColorStateList)}} to the progress 1119 * indicator. The default mode is {@link PorterDuff.Mode#SRC_IN}. 1120 * 1121 * @param blendMode the blending mode used to apply the tint, may be 1122 * {@code null} to clear tint 1123 * @attr ref android.R.styleable#ProgressBar_progressTintMode 1124 * @see #getProgressTintMode() 1125 * @see Drawable#setTintBlendMode(BlendMode) 1126 */ setProgressTintBlendMode(@ullable BlendMode blendMode)1127 public void setProgressTintBlendMode(@Nullable BlendMode blendMode) { 1128 if (mProgressTintInfo == null) { 1129 mProgressTintInfo = new ProgressTintInfo(); 1130 } 1131 mProgressTintInfo.mProgressBlendMode = blendMode; 1132 mProgressTintInfo.mHasProgressTintMode = true; 1133 1134 if (mProgressDrawable != null) { 1135 applyPrimaryProgressTint(); 1136 } 1137 } 1138 1139 /** 1140 * Returns the blending mode used to apply the tint to the progress 1141 * drawable, if specified. 1142 * 1143 * @return the blending mode used to apply the tint to the progress 1144 * drawable 1145 * @attr ref android.R.styleable#ProgressBar_progressTintMode 1146 * @see #setProgressTintMode(PorterDuff.Mode) 1147 */ 1148 @InspectableProperty 1149 @Nullable getProgressTintMode()1150 public PorterDuff.Mode getProgressTintMode() { 1151 BlendMode mode = getProgressTintBlendMode(); 1152 return mode != null ? BlendMode.blendModeToPorterDuffMode(mode) : null; 1153 } 1154 1155 /** 1156 * Returns the blending mode used to apply the tint to the progress 1157 * drawable, if specified. 1158 * 1159 * @return the blending mode used to apply the tint to the progress 1160 * drawable 1161 * @attr ref android.R.styleable#ProgressBar_progressTintMode 1162 * @see #setProgressTintBlendMode(BlendMode) 1163 */ 1164 @InspectableProperty(attributeId = android.R.styleable.ProgressBar_progressTintMode) 1165 @Nullable getProgressTintBlendMode()1166 public BlendMode getProgressTintBlendMode() { 1167 return mProgressTintInfo != null ? mProgressTintInfo.mProgressBlendMode : null; 1168 } 1169 1170 /** 1171 * Applies a tint to the progress background, if one exists. Does not 1172 * modify the current tint mode, which is 1173 * {@link PorterDuff.Mode#SRC_ATOP} by default. 1174 * <p> 1175 * The progress background must be specified as a layer with 1176 * id {@link android.R.id#background} in a {@link LayerDrawable} 1177 * used as the progress drawable. 1178 * <p> 1179 * Subsequent calls to {@link #setProgressDrawable(Drawable)} where the 1180 * drawable contains a progress background will automatically mutate the 1181 * drawable and apply the specified tint and tint mode using 1182 * {@link Drawable#setTintList(ColorStateList)}. 1183 * 1184 * @param tint the tint to apply, may be {@code null} to clear tint 1185 * 1186 * @attr ref android.R.styleable#ProgressBar_progressBackgroundTint 1187 * @see #getProgressBackgroundTintList() 1188 * @see Drawable#setTintList(ColorStateList) 1189 */ 1190 @RemotableViewMethod setProgressBackgroundTintList(@ullable ColorStateList tint)1191 public void setProgressBackgroundTintList(@Nullable ColorStateList tint) { 1192 if (mProgressTintInfo == null) { 1193 mProgressTintInfo = new ProgressTintInfo(); 1194 } 1195 mProgressTintInfo.mProgressBackgroundTintList = tint; 1196 mProgressTintInfo.mHasProgressBackgroundTint = true; 1197 1198 if (mProgressDrawable != null) { 1199 applyProgressBackgroundTint(); 1200 } 1201 } 1202 1203 /** 1204 * Returns the tint applied to the progress background, if specified. 1205 * 1206 * @return the tint applied to the progress background 1207 * @attr ref android.R.styleable#ProgressBar_progressBackgroundTint 1208 * @see #setProgressBackgroundTintList(ColorStateList) 1209 */ 1210 @InspectableProperty(name = "progressBackgroundTint") 1211 @Nullable getProgressBackgroundTintList()1212 public ColorStateList getProgressBackgroundTintList() { 1213 return mProgressTintInfo != null ? mProgressTintInfo.mProgressBackgroundTintList : null; 1214 } 1215 1216 /** 1217 * Specifies the blending mode used to apply the tint specified by 1218 * {@link #setProgressBackgroundTintList(ColorStateList)}} to the progress 1219 * background. The default mode is {@link PorterDuff.Mode#SRC_IN}. 1220 * 1221 * @param tintMode the blending mode used to apply the tint, may be 1222 * {@code null} to clear tint 1223 * @attr ref android.R.styleable#ProgressBar_progressBackgroundTintMode 1224 * @see #setProgressBackgroundTintList(ColorStateList) 1225 * @see Drawable#setTintMode(PorterDuff.Mode) 1226 */ setProgressBackgroundTintMode(@ullable PorterDuff.Mode tintMode)1227 public void setProgressBackgroundTintMode(@Nullable PorterDuff.Mode tintMode) { 1228 setProgressBackgroundTintBlendMode(tintMode != null 1229 ? BlendMode.fromValue(tintMode.nativeInt) : null); 1230 } 1231 1232 /** 1233 * Specifies the blending mode used to apply the tint specified by 1234 * {@link #setProgressBackgroundTintList(ColorStateList)}} to the progress 1235 * background. The default mode is {@link BlendMode#SRC_IN}. 1236 * 1237 * @param blendMode the blending mode used to apply the tint, may be 1238 * {@code null} to clear tint 1239 * @attr ref android.R.styleable#ProgressBar_progressBackgroundTintMode 1240 * @see #setProgressBackgroundTintList(ColorStateList) 1241 * @see Drawable#setTintBlendMode(BlendMode) 1242 */ setProgressBackgroundTintBlendMode(@ullable BlendMode blendMode)1243 public void setProgressBackgroundTintBlendMode(@Nullable BlendMode blendMode) { 1244 if (mProgressTintInfo == null) { 1245 mProgressTintInfo = new ProgressTintInfo(); 1246 } 1247 mProgressTintInfo.mProgressBackgroundBlendMode = blendMode; 1248 mProgressTintInfo.mHasProgressBackgroundTintMode = true; 1249 1250 if (mProgressDrawable != null) { 1251 applyProgressBackgroundTint(); 1252 } 1253 } 1254 1255 /** 1256 * @return the blending mode used to apply the tint to the progress 1257 * background 1258 * @attr ref android.R.styleable#ProgressBar_progressBackgroundTintMode 1259 * @see #setProgressBackgroundTintMode(PorterDuff.Mode) 1260 */ 1261 @InspectableProperty 1262 @Nullable getProgressBackgroundTintMode()1263 public PorterDuff.Mode getProgressBackgroundTintMode() { 1264 BlendMode mode = getProgressBackgroundTintBlendMode(); 1265 return mode != null ? BlendMode.blendModeToPorterDuffMode(mode) : null; 1266 } 1267 1268 /** 1269 * @return the blending mode used to apply the tint to the progress 1270 * background 1271 * @attr ref android.R.styleable#ProgressBar_progressBackgroundTintMode 1272 * @see #setProgressBackgroundTintBlendMode(BlendMode) 1273 */ 1274 @InspectableProperty(attributeId = R.styleable.ProgressBar_progressBackgroundTintMode) 1275 @Nullable getProgressBackgroundTintBlendMode()1276 public BlendMode getProgressBackgroundTintBlendMode() { 1277 return mProgressTintInfo != null ? mProgressTintInfo.mProgressBackgroundBlendMode : null; 1278 } 1279 1280 /** 1281 * Applies a tint to the secondary progress indicator, if one exists. 1282 * Does not modify the current tint mode, which is 1283 * {@link PorterDuff.Mode#SRC_ATOP} by default. 1284 * <p> 1285 * The secondary progress indicator must be specified as a layer with 1286 * id {@link android.R.id#secondaryProgress} in a {@link LayerDrawable} 1287 * used as the progress drawable. 1288 * <p> 1289 * Subsequent calls to {@link #setProgressDrawable(Drawable)} where the 1290 * drawable contains a secondary progress indicator will automatically 1291 * mutate the drawable and apply the specified tint and tint mode using 1292 * {@link Drawable#setTintList(ColorStateList)}. 1293 * 1294 * @param tint the tint to apply, may be {@code null} to clear tint 1295 * 1296 * @attr ref android.R.styleable#ProgressBar_secondaryProgressTint 1297 * @see #getSecondaryProgressTintList() 1298 * @see Drawable#setTintList(ColorStateList) 1299 */ setSecondaryProgressTintList(@ullable ColorStateList tint)1300 public void setSecondaryProgressTintList(@Nullable ColorStateList tint) { 1301 if (mProgressTintInfo == null) { 1302 mProgressTintInfo = new ProgressTintInfo(); 1303 } 1304 mProgressTintInfo.mSecondaryProgressTintList = tint; 1305 mProgressTintInfo.mHasSecondaryProgressTint = true; 1306 1307 if (mProgressDrawable != null) { 1308 applySecondaryProgressTint(); 1309 } 1310 } 1311 1312 /** 1313 * Returns the tint applied to the secondary progress drawable, if 1314 * specified. 1315 * 1316 * @return the tint applied to the secondary progress drawable 1317 * @attr ref android.R.styleable#ProgressBar_secondaryProgressTint 1318 * @see #setSecondaryProgressTintList(ColorStateList) 1319 */ 1320 @InspectableProperty(name = "secondaryProgressTint") 1321 @Nullable getSecondaryProgressTintList()1322 public ColorStateList getSecondaryProgressTintList() { 1323 return mProgressTintInfo != null ? mProgressTintInfo.mSecondaryProgressTintList : null; 1324 } 1325 1326 /** 1327 * Specifies the blending mode used to apply the tint specified by 1328 * {@link #setSecondaryProgressTintList(ColorStateList)}} to the secondary 1329 * progress indicator. The default mode is 1330 * {@link PorterDuff.Mode#SRC_ATOP}. 1331 * 1332 * @param tintMode the blending mode used to apply the tint, may be 1333 * {@code null} to clear tint 1334 * @attr ref android.R.styleable#ProgressBar_secondaryProgressTintMode 1335 * @see #setSecondaryProgressTintList(ColorStateList) 1336 * @see Drawable#setTintMode(PorterDuff.Mode) 1337 */ setSecondaryProgressTintMode(@ullable PorterDuff.Mode tintMode)1338 public void setSecondaryProgressTintMode(@Nullable PorterDuff.Mode tintMode) { 1339 setSecondaryProgressTintBlendMode(tintMode != null 1340 ? BlendMode.fromValue(tintMode.nativeInt) : null); 1341 } 1342 1343 /** 1344 * Specifies the blending mode used to apply the tint specified by 1345 * {@link #setSecondaryProgressTintList(ColorStateList)}} to the secondary 1346 * progress indicator. The default mode is 1347 * {@link PorterDuff.Mode#SRC_ATOP}. 1348 * 1349 * @param blendMode the blending mode used to apply the tint, may be 1350 * {@code null} to clear tint 1351 * @attr ref android.R.styleable#ProgressBar_secondaryProgressTintMode 1352 * @see #setSecondaryProgressTintList(ColorStateList) 1353 * @see Drawable#setTintBlendMode(BlendMode) 1354 */ setSecondaryProgressTintBlendMode(@ullable BlendMode blendMode)1355 public void setSecondaryProgressTintBlendMode(@Nullable BlendMode blendMode) { 1356 if (mProgressTintInfo == null) { 1357 mProgressTintInfo = new ProgressTintInfo(); 1358 } 1359 mProgressTintInfo.mSecondaryProgressBlendMode = blendMode; 1360 mProgressTintInfo.mHasSecondaryProgressTintMode = true; 1361 1362 if (mProgressDrawable != null) { 1363 applySecondaryProgressTint(); 1364 } 1365 } 1366 1367 /** 1368 * Returns the blending mode used to apply the tint to the secondary 1369 * progress drawable, if specified. 1370 * 1371 * @return the blending mode used to apply the tint to the secondary 1372 * progress drawable 1373 * @attr ref android.R.styleable#ProgressBar_secondaryProgressTintMode 1374 * @see #setSecondaryProgressTintMode(PorterDuff.Mode) 1375 */ 1376 @InspectableProperty 1377 @Nullable getSecondaryProgressTintMode()1378 public PorterDuff.Mode getSecondaryProgressTintMode() { 1379 BlendMode mode = getSecondaryProgressTintBlendMode(); 1380 return mode != null ? BlendMode.blendModeToPorterDuffMode(mode) : null; 1381 } 1382 1383 /** 1384 * Returns the blending mode used to apply the tint to the secondary 1385 * progress drawable, if specified. 1386 * 1387 * @return the blending mode used to apply the tint to the secondary 1388 * progress drawable 1389 * @attr ref android.R.styleable#ProgressBar_secondaryProgressTintMode 1390 * @see #setSecondaryProgressTintBlendMode(BlendMode) 1391 */ 1392 @InspectableProperty(attributeId = android.R.styleable.ProgressBar_secondaryProgressTintMode) 1393 @Nullable getSecondaryProgressTintBlendMode()1394 public BlendMode getSecondaryProgressTintBlendMode() { 1395 return mProgressTintInfo != null ? mProgressTintInfo.mSecondaryProgressBlendMode : null; 1396 } 1397 1398 /** 1399 * Returns the drawable to which a tint or tint mode should be applied. 1400 * 1401 * @param layerId id of the layer to modify 1402 * @param shouldFallback whether the base drawable should be returned 1403 * if the id does not exist 1404 * @return the drawable to modify 1405 */ 1406 @Nullable getTintTarget(int layerId, boolean shouldFallback)1407 private Drawable getTintTarget(int layerId, boolean shouldFallback) { 1408 Drawable layer = null; 1409 1410 final Drawable d = mProgressDrawable; 1411 if (d != null) { 1412 mProgressDrawable = d.mutate(); 1413 1414 if (d instanceof LayerDrawable) { 1415 layer = ((LayerDrawable) d).findDrawableByLayerId(layerId); 1416 } 1417 1418 if (shouldFallback && layer == null) { 1419 layer = d; 1420 } 1421 } 1422 1423 return layer; 1424 } 1425 1426 /** 1427 * Define the tileable drawable used to draw the progress bar in 1428 * progress mode. 1429 * <p> 1430 * If the drawable is a BitmapDrawable or contains BitmapDrawables, a 1431 * tiled copy will be generated for display as a progress bar. 1432 * 1433 * @param d the new drawable 1434 * @see #getProgressDrawable() 1435 * @see #setIndeterminate(boolean) 1436 */ setProgressDrawableTiled(Drawable d)1437 public void setProgressDrawableTiled(Drawable d) { 1438 if (d != null) { 1439 d = tileify(d, false); 1440 } 1441 1442 setProgressDrawable(d); 1443 } 1444 1445 /** 1446 * Returns the drawable currently used to draw the progress bar. This will be 1447 * either {@link #getProgressDrawable()} or {@link #getIndeterminateDrawable()} 1448 * depending on whether the progress bar is in determinate or indeterminate mode. 1449 * 1450 * @return the drawable currently used to draw the progress bar 1451 */ 1452 @Nullable getCurrentDrawable()1453 public Drawable getCurrentDrawable() { 1454 return mCurrentDrawable; 1455 } 1456 1457 @Override verifyDrawable(@onNull Drawable who)1458 protected boolean verifyDrawable(@NonNull Drawable who) { 1459 return who == mProgressDrawable || who == mIndeterminateDrawable 1460 || super.verifyDrawable(who); 1461 } 1462 1463 @Override jumpDrawablesToCurrentState()1464 public void jumpDrawablesToCurrentState() { 1465 super.jumpDrawablesToCurrentState(); 1466 if (mProgressDrawable != null) mProgressDrawable.jumpToCurrentState(); 1467 if (mIndeterminateDrawable != null) mIndeterminateDrawable.jumpToCurrentState(); 1468 } 1469 1470 /** 1471 * @hide 1472 */ 1473 @Override onResolveDrawables(int layoutDirection)1474 public void onResolveDrawables(int layoutDirection) { 1475 final Drawable d = mCurrentDrawable; 1476 if (d != null) { 1477 d.setLayoutDirection(layoutDirection); 1478 } 1479 if (mIndeterminateDrawable != null) { 1480 mIndeterminateDrawable.setLayoutDirection(layoutDirection); 1481 } 1482 if (mProgressDrawable != null) { 1483 mProgressDrawable.setLayoutDirection(layoutDirection); 1484 } 1485 } 1486 1487 @Override postInvalidate()1488 public void postInvalidate() { 1489 if (!mNoInvalidate) { 1490 super.postInvalidate(); 1491 } 1492 } 1493 1494 private class RefreshProgressRunnable implements Runnable { run()1495 public void run() { 1496 synchronized (ProgressBar.this) { 1497 final int count = mRefreshData.size(); 1498 for (int i = 0; i < count; i++) { 1499 final RefreshData rd = mRefreshData.get(i); 1500 doRefreshProgress(rd.id, rd.progress, rd.fromUser, true, rd.animate); 1501 rd.recycle(); 1502 } 1503 mRefreshData.clear(); 1504 mRefreshIsPosted = false; 1505 } 1506 } 1507 } 1508 1509 private static class RefreshData { 1510 private static final int POOL_MAX = 24; 1511 private static final SynchronizedPool<RefreshData> sPool = 1512 new SynchronizedPool<RefreshData>(POOL_MAX); 1513 1514 public int id; 1515 public int progress; 1516 public boolean fromUser; 1517 public boolean animate; 1518 obtain(int id, int progress, boolean fromUser, boolean animate)1519 public static RefreshData obtain(int id, int progress, boolean fromUser, boolean animate) { 1520 RefreshData rd = sPool.acquire(); 1521 if (rd == null) { 1522 rd = new RefreshData(); 1523 } 1524 rd.id = id; 1525 rd.progress = progress; 1526 rd.fromUser = fromUser; 1527 rd.animate = animate; 1528 return rd; 1529 } 1530 recycle()1531 public void recycle() { 1532 sPool.release(this); 1533 } 1534 } 1535 doRefreshProgress(int id, int progress, boolean fromUser, boolean callBackToApp, boolean animate)1536 private synchronized void doRefreshProgress(int id, int progress, boolean fromUser, 1537 boolean callBackToApp, boolean animate) { 1538 int range = mMax - mMin; 1539 final float scale = range > 0 ? (progress - mMin) / (float) range : 0; 1540 final boolean isPrimary = id == R.id.progress; 1541 1542 if (isPrimary && animate) { 1543 final ObjectAnimator animator = ObjectAnimator.ofFloat(this, VISUAL_PROGRESS, scale); 1544 animator.setAutoCancel(true); 1545 animator.setDuration(PROGRESS_ANIM_DURATION); 1546 animator.setInterpolator(PROGRESS_ANIM_INTERPOLATOR); 1547 animator.start(); 1548 } else { 1549 setVisualProgress(id, scale); 1550 } 1551 1552 if (isPrimary && callBackToApp) { 1553 onProgressRefresh(scale, fromUser, progress); 1554 } 1555 } 1556 onProgressRefresh(float scale, boolean fromUser, int progress)1557 void onProgressRefresh(float scale, boolean fromUser, int progress) { 1558 if (AccessibilityManager.getInstance(mContext).isEnabled()) { 1559 scheduleAccessibilityEventSender(); 1560 } 1561 } 1562 1563 /** 1564 * Sets the visual state of a progress indicator. 1565 * 1566 * @param id the identifier of the progress indicator 1567 * @param progress the visual progress in the range [0...1] 1568 */ setVisualProgress(int id, float progress)1569 private void setVisualProgress(int id, float progress) { 1570 mVisualProgress = progress; 1571 1572 Drawable d = mCurrentDrawable; 1573 1574 if (d instanceof LayerDrawable) { 1575 d = ((LayerDrawable) d).findDrawableByLayerId(id); 1576 if (d == null) { 1577 // If we can't find the requested layer, fall back to setting 1578 // the level of the entire drawable. This will break if 1579 // progress is set on multiple elements, but the theme-default 1580 // drawable will always have all layer IDs present. 1581 d = mCurrentDrawable; 1582 } 1583 } 1584 1585 if (d != null) { 1586 final int level = (int) (progress * MAX_LEVEL); 1587 d.setLevel(level); 1588 } else { 1589 invalidate(); 1590 } 1591 1592 onVisualProgressChanged(id, progress); 1593 } 1594 1595 /** 1596 * Called when the visual state of a progress indicator changes. 1597 * 1598 * @param id the identifier of the progress indicator 1599 * @param progress the visual progress in the range [0...1] 1600 */ onVisualProgressChanged(int id, float progress)1601 void onVisualProgressChanged(int id, float progress) { 1602 // Stub method. 1603 } 1604 1605 @UnsupportedAppUsage refreshProgress(int id, int progress, boolean fromUser, boolean animate)1606 private synchronized void refreshProgress(int id, int progress, boolean fromUser, 1607 boolean animate) { 1608 if (mUiThreadId == Thread.currentThread().getId()) { 1609 doRefreshProgress(id, progress, fromUser, true, animate); 1610 } else { 1611 if (mRefreshProgressRunnable == null) { 1612 mRefreshProgressRunnable = new RefreshProgressRunnable(); 1613 } 1614 1615 final RefreshData rd = RefreshData.obtain(id, progress, fromUser, animate); 1616 mRefreshData.add(rd); 1617 if (mAttached && !mRefreshIsPosted) { 1618 post(mRefreshProgressRunnable); 1619 mRefreshIsPosted = true; 1620 } 1621 } 1622 } 1623 1624 /** 1625 * Sets the current progress to the specified value. Does not do anything 1626 * if the progress bar is in indeterminate mode. 1627 * <p> 1628 * This method will immediately update the visual position of the progress 1629 * indicator. To animate the visual position to the target value, use 1630 * {@link #setProgress(int, boolean)}}. 1631 * 1632 * @param progress the new progress, between {@link #getMin()} and {@link #getMax()} 1633 * 1634 * @see #setIndeterminate(boolean) 1635 * @see #isIndeterminate() 1636 * @see #getProgress() 1637 * @see #incrementProgressBy(int) 1638 */ 1639 @android.view.RemotableViewMethod setProgress(int progress)1640 public synchronized void setProgress(int progress) { 1641 setProgressInternal(progress, false, false); 1642 } 1643 1644 /** 1645 * Sets the current progress to the specified value, optionally animating 1646 * the visual position between the current and target values. 1647 * <p> 1648 * Animation does not affect the result of {@link #getProgress()}, which 1649 * will return the target value immediately after this method is called. 1650 * 1651 * @param progress the new progress value, between {@link #getMin()} and {@link #getMax()} 1652 * @param animate {@code true} to animate between the current and target 1653 * values or {@code false} to not animate 1654 */ setProgress(int progress, boolean animate)1655 public void setProgress(int progress, boolean animate) { 1656 setProgressInternal(progress, false, animate); 1657 } 1658 1659 @android.view.RemotableViewMethod 1660 @UnsupportedAppUsage setProgressInternal(int progress, boolean fromUser, boolean animate)1661 synchronized boolean setProgressInternal(int progress, boolean fromUser, boolean animate) { 1662 if (mIndeterminate) { 1663 // Not applicable. 1664 return false; 1665 } 1666 1667 progress = MathUtils.constrain(progress, mMin, mMax); 1668 1669 if (progress == mProgress) { 1670 // No change from current. 1671 return false; 1672 } 1673 1674 mProgress = progress; 1675 refreshProgress(R.id.progress, mProgress, fromUser, animate); 1676 return true; 1677 } 1678 1679 /** 1680 * <p> 1681 * Set the current secondary progress to the specified value. Does not do 1682 * anything if the progress bar is in indeterminate mode. 1683 * </p> 1684 * 1685 * @param secondaryProgress the new secondary progress, between {@link #getMin()} and 1686 * {@link #getMax()} 1687 * @see #setIndeterminate(boolean) 1688 * @see #isIndeterminate() 1689 * @see #getSecondaryProgress() 1690 * @see #incrementSecondaryProgressBy(int) 1691 */ 1692 @android.view.RemotableViewMethod setSecondaryProgress(int secondaryProgress)1693 public synchronized void setSecondaryProgress(int secondaryProgress) { 1694 if (mIndeterminate) { 1695 return; 1696 } 1697 1698 if (secondaryProgress < mMin) { 1699 secondaryProgress = mMin; 1700 } 1701 1702 if (secondaryProgress > mMax) { 1703 secondaryProgress = mMax; 1704 } 1705 1706 if (secondaryProgress != mSecondaryProgress) { 1707 mSecondaryProgress = secondaryProgress; 1708 refreshProgress(R.id.secondaryProgress, mSecondaryProgress, false, false); 1709 } 1710 } 1711 1712 /** 1713 * <p>Get the progress bar's current level of progress. Return 0 when the 1714 * progress bar is in indeterminate mode.</p> 1715 * 1716 * @return the current progress, between {@link #getMin()} and {@link #getMax()} 1717 * 1718 * @see #setIndeterminate(boolean) 1719 * @see #isIndeterminate() 1720 * @see #setProgress(int) 1721 * @see #setMax(int) 1722 * @see #getMax() 1723 */ 1724 @ViewDebug.ExportedProperty(category = "progress") 1725 @InspectableProperty getProgress()1726 public synchronized int getProgress() { 1727 return mIndeterminate ? 0 : mProgress; 1728 } 1729 1730 /** 1731 * <p>Get the progress bar's current level of secondary progress. Return 0 when the 1732 * progress bar is in indeterminate mode.</p> 1733 * 1734 * @return the current secondary progress, between {@link #getMin()} and {@link #getMax()} 1735 * 1736 * @see #setIndeterminate(boolean) 1737 * @see #isIndeterminate() 1738 * @see #setSecondaryProgress(int) 1739 * @see #setMax(int) 1740 * @see #getMax() 1741 */ 1742 @ViewDebug.ExportedProperty(category = "progress") 1743 @InspectableProperty getSecondaryProgress()1744 public synchronized int getSecondaryProgress() { 1745 return mIndeterminate ? 0 : mSecondaryProgress; 1746 } 1747 1748 /** 1749 * <p>Return the lower limit of this progress bar's range.</p> 1750 * 1751 * @return a positive integer 1752 * 1753 * @see #setMin(int) 1754 * @see #getProgress() 1755 * @see #getSecondaryProgress() 1756 */ 1757 @ViewDebug.ExportedProperty(category = "progress") 1758 @InspectableProperty getMin()1759 public synchronized int getMin() { 1760 return mMin; 1761 } 1762 1763 /** 1764 * <p>Return the upper limit of this progress bar's range.</p> 1765 * 1766 * @return a positive integer 1767 * 1768 * @see #setMax(int) 1769 * @see #getProgress() 1770 * @see #getSecondaryProgress() 1771 */ 1772 @ViewDebug.ExportedProperty(category = "progress") 1773 @InspectableProperty getMax()1774 public synchronized int getMax() { 1775 return mMax; 1776 } 1777 1778 /** 1779 * <p>Set the lower range of the progress bar to <tt>min</tt>.</p> 1780 * 1781 * @param min the lower range of this progress bar 1782 * 1783 * @see #getMin() 1784 * @see #setProgress(int) 1785 * @see #setSecondaryProgress(int) 1786 */ 1787 @android.view.RemotableViewMethod setMin(int min)1788 public synchronized void setMin(int min) { 1789 if (mMaxInitialized) { 1790 if (min > mMax) { 1791 min = mMax; 1792 } 1793 } 1794 mMinInitialized = true; 1795 if (mMaxInitialized && min != mMin) { 1796 mMin = min; 1797 postInvalidate(); 1798 1799 if (mProgress < min) { 1800 mProgress = min; 1801 } 1802 refreshProgress(R.id.progress, mProgress, false, false); 1803 } else { 1804 mMin = min; 1805 } 1806 } 1807 1808 /** 1809 * <p>Set the upper range of the progress bar <tt>max</tt>.</p> 1810 * 1811 * @param max the upper range of this progress bar 1812 * 1813 * @see #getMax() 1814 * @see #setProgress(int) 1815 * @see #setSecondaryProgress(int) 1816 */ 1817 @android.view.RemotableViewMethod setMax(int max)1818 public synchronized void setMax(int max) { 1819 if (mMinInitialized) { 1820 if (max < mMin) { 1821 max = mMin; 1822 } 1823 } 1824 mMaxInitialized = true; 1825 if (mMinInitialized && max != mMax) { 1826 mMax = max; 1827 postInvalidate(); 1828 1829 if (mProgress > max) { 1830 mProgress = max; 1831 } 1832 refreshProgress(R.id.progress, mProgress, false, false); 1833 } else { 1834 mMax = max; 1835 } 1836 } 1837 1838 /** 1839 * <p>Increase the progress bar's progress by the specified amount.</p> 1840 * 1841 * @param diff the amount by which the progress must be increased 1842 * 1843 * @see #setProgress(int) 1844 */ incrementProgressBy(int diff)1845 public synchronized final void incrementProgressBy(int diff) { 1846 setProgress(mProgress + diff); 1847 } 1848 1849 /** 1850 * <p>Increase the progress bar's secondary progress by the specified amount.</p> 1851 * 1852 * @param diff the amount by which the secondary progress must be increased 1853 * 1854 * @see #setSecondaryProgress(int) 1855 */ incrementSecondaryProgressBy(int diff)1856 public synchronized final void incrementSecondaryProgressBy(int diff) { 1857 setSecondaryProgress(mSecondaryProgress + diff); 1858 } 1859 1860 /** 1861 * <p>Start the indeterminate progress animation.</p> 1862 */ 1863 @UnsupportedAppUsage startAnimation()1864 void startAnimation() { 1865 if (getVisibility() != VISIBLE || getWindowVisibility() != VISIBLE) { 1866 return; 1867 } 1868 1869 if (mIndeterminateDrawable instanceof Animatable) { 1870 mShouldStartAnimationDrawable = true; 1871 mHasAnimation = false; 1872 } else { 1873 mHasAnimation = true; 1874 1875 if (mInterpolator == null) { 1876 mInterpolator = new LinearInterpolator(); 1877 } 1878 1879 if (mTransformation == null) { 1880 mTransformation = new Transformation(); 1881 } else { 1882 mTransformation.clear(); 1883 } 1884 1885 if (mAnimation == null) { 1886 mAnimation = new AlphaAnimation(0.0f, 1.0f); 1887 } else { 1888 mAnimation.reset(); 1889 } 1890 1891 mAnimation.setRepeatMode(mBehavior); 1892 mAnimation.setRepeatCount(Animation.INFINITE); 1893 mAnimation.setDuration(mDuration); 1894 mAnimation.setInterpolator(mInterpolator); 1895 mAnimation.setStartTime(Animation.START_ON_FIRST_FRAME); 1896 } 1897 postInvalidate(); 1898 } 1899 1900 /** 1901 * <p>Stop the indeterminate progress animation.</p> 1902 */ 1903 @UnsupportedAppUsage stopAnimation()1904 void stopAnimation() { 1905 mHasAnimation = false; 1906 if (mIndeterminateDrawable instanceof Animatable) { 1907 ((Animatable) mIndeterminateDrawable).stop(); 1908 mShouldStartAnimationDrawable = false; 1909 } 1910 postInvalidate(); 1911 } 1912 1913 /** 1914 * Sets the acceleration curve for the indeterminate animation. 1915 * 1916 * <p>The interpolator is loaded as a resource from the specified context. Defaults to a linear 1917 * interpolation. 1918 * 1919 * <p>The interpolator only affects the indeterminate animation if the 1920 * {@link #setIndeterminateDrawable(Drawable) supplied indeterminate drawable} does not 1921 * implement {@link Animatable}. 1922 * 1923 * <p>This call must be made before the indeterminate animation starts for it to have an affect. 1924 * 1925 * @param context The application environment 1926 * @param resID The resource identifier of the interpolator to load 1927 * @attr ref android.R.styleable#ProgressBar_interpolator 1928 * @see #setInterpolator(Interpolator) 1929 * @see #getInterpolator() 1930 */ setInterpolator(Context context, @InterpolatorRes int resID)1931 public void setInterpolator(Context context, @InterpolatorRes int resID) { 1932 setInterpolator(AnimationUtils.loadInterpolator(context, resID)); 1933 } 1934 1935 /** 1936 * Sets the acceleration curve for the indeterminate animation. 1937 * Defaults to a linear interpolation. 1938 * 1939 * <p>The interpolator only affects the indeterminate animation if the 1940 * {@link #setIndeterminateDrawable(Drawable) supplied indeterminate drawable} does not 1941 * implement {@link Animatable}. 1942 * 1943 * <p>This call must be made before the indeterminate animation starts for it to have 1944 * an affect. 1945 * 1946 * @param interpolator The interpolator which defines the acceleration curve 1947 * @attr ref android.R.styleable#ProgressBar_interpolator 1948 * @see #setInterpolator(Context, int) 1949 * @see #getInterpolator() 1950 */ setInterpolator(Interpolator interpolator)1951 public void setInterpolator(Interpolator interpolator) { 1952 mInterpolator = interpolator; 1953 } 1954 1955 /** 1956 * Gets the acceleration curve type for the indeterminate animation. 1957 * 1958 * @return the {@link Interpolator} associated to this animation 1959 * @attr ref android.R.styleable#ProgressBar_interpolator 1960 * @see #setInterpolator(Context, int) 1961 * @see #setInterpolator(Interpolator) 1962 */ 1963 @InspectableProperty getInterpolator()1964 public Interpolator getInterpolator() { 1965 return mInterpolator; 1966 } 1967 1968 @Override onVisibilityAggregated(boolean isVisible)1969 public void onVisibilityAggregated(boolean isVisible) { 1970 super.onVisibilityAggregated(isVisible); 1971 1972 if (isVisible != mAggregatedIsVisible) { 1973 mAggregatedIsVisible = isVisible; 1974 1975 if (mIndeterminate) { 1976 // let's be nice with the UI thread 1977 if (isVisible) { 1978 startAnimation(); 1979 } else { 1980 stopAnimation(); 1981 } 1982 } 1983 1984 if (mCurrentDrawable != null) { 1985 mCurrentDrawable.setVisible(isVisible, false); 1986 } 1987 } 1988 } 1989 1990 @Override invalidateDrawable(@onNull Drawable dr)1991 public void invalidateDrawable(@NonNull Drawable dr) { 1992 if (!mInDrawing) { 1993 if (verifyDrawable(dr)) { 1994 final Rect dirty = dr.getBounds(); 1995 final int scrollX = mScrollX + mPaddingLeft; 1996 final int scrollY = mScrollY + mPaddingTop; 1997 1998 invalidate(dirty.left + scrollX, dirty.top + scrollY, 1999 dirty.right + scrollX, dirty.bottom + scrollY); 2000 } else { 2001 super.invalidateDrawable(dr); 2002 } 2003 } 2004 } 2005 2006 @Override onSizeChanged(int w, int h, int oldw, int oldh)2007 protected void onSizeChanged(int w, int h, int oldw, int oldh) { 2008 updateDrawableBounds(w, h); 2009 } 2010 updateDrawableBounds(int w, int h)2011 private void updateDrawableBounds(int w, int h) { 2012 // onDraw will translate the canvas so we draw starting at 0,0. 2013 // Subtract out padding for the purposes of the calculations below. 2014 w -= mPaddingRight + mPaddingLeft; 2015 h -= mPaddingTop + mPaddingBottom; 2016 2017 int right = w; 2018 int bottom = h; 2019 int top = 0; 2020 int left = 0; 2021 2022 if (mIndeterminateDrawable != null) { 2023 // Aspect ratio logic does not apply to AnimationDrawables 2024 if (mOnlyIndeterminate && !(mIndeterminateDrawable instanceof AnimationDrawable)) { 2025 // Maintain aspect ratio. Certain kinds of animated drawables 2026 // get very confused otherwise. 2027 final int intrinsicWidth = mIndeterminateDrawable.getIntrinsicWidth(); 2028 final int intrinsicHeight = mIndeterminateDrawable.getIntrinsicHeight(); 2029 final float intrinsicAspect = (float) intrinsicWidth / intrinsicHeight; 2030 final float boundAspect = (float) w / h; 2031 if (intrinsicAspect != boundAspect) { 2032 if (boundAspect > intrinsicAspect) { 2033 // New width is larger. Make it smaller to match height. 2034 final int width = (int) (h * intrinsicAspect); 2035 left = (w - width) / 2; 2036 right = left + width; 2037 } else { 2038 // New height is larger. Make it smaller to match width. 2039 final int height = (int) (w * (1 / intrinsicAspect)); 2040 top = (h - height) / 2; 2041 bottom = top + height; 2042 } 2043 } 2044 } 2045 if (isLayoutRtl() && mMirrorForRtl) { 2046 int tempLeft = left; 2047 left = w - right; 2048 right = w - tempLeft; 2049 } 2050 mIndeterminateDrawable.setBounds(left, top, right, bottom); 2051 } 2052 2053 if (mProgressDrawable != null) { 2054 mProgressDrawable.setBounds(0, 0, right, bottom); 2055 } 2056 } 2057 2058 @Override onDraw(Canvas canvas)2059 protected synchronized void onDraw(Canvas canvas) { 2060 super.onDraw(canvas); 2061 2062 drawTrack(canvas); 2063 } 2064 2065 /** 2066 * Draws the progress bar track. 2067 */ drawTrack(Canvas canvas)2068 void drawTrack(Canvas canvas) { 2069 final Drawable d = mCurrentDrawable; 2070 if (d != null) { 2071 // Translate canvas so a indeterminate circular progress bar with padding 2072 // rotates properly in its animation 2073 final int saveCount = canvas.save(); 2074 2075 if (isLayoutRtl() && mMirrorForRtl) { 2076 canvas.translate(getWidth() - mPaddingRight, mPaddingTop); 2077 canvas.scale(-1.0f, 1.0f); 2078 } else { 2079 canvas.translate(mPaddingLeft, mPaddingTop); 2080 } 2081 2082 final long time = getDrawingTime(); 2083 if (mHasAnimation) { 2084 mAnimation.getTransformation(time, mTransformation); 2085 final float scale = mTransformation.getAlpha(); 2086 try { 2087 mInDrawing = true; 2088 d.setLevel((int) (scale * MAX_LEVEL)); 2089 } finally { 2090 mInDrawing = false; 2091 } 2092 postInvalidateOnAnimation(); 2093 } 2094 2095 d.draw(canvas); 2096 canvas.restoreToCount(saveCount); 2097 2098 if (mShouldStartAnimationDrawable && d instanceof Animatable) { 2099 ((Animatable) d).start(); 2100 mShouldStartAnimationDrawable = false; 2101 } 2102 } 2103 } 2104 2105 @Override onMeasure(int widthMeasureSpec, int heightMeasureSpec)2106 protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 2107 int dw = 0; 2108 int dh = 0; 2109 2110 final Drawable d = mCurrentDrawable; 2111 if (d != null) { 2112 dw = Math.max(mMinWidth, Math.min(mMaxWidth, d.getIntrinsicWidth())); 2113 dh = Math.max(mMinHeight, Math.min(mMaxHeight, d.getIntrinsicHeight())); 2114 } 2115 2116 updateDrawableState(); 2117 2118 dw += mPaddingLeft + mPaddingRight; 2119 dh += mPaddingTop + mPaddingBottom; 2120 2121 final int measuredWidth = resolveSizeAndState(dw, widthMeasureSpec, 0); 2122 final int measuredHeight = resolveSizeAndState(dh, heightMeasureSpec, 0); 2123 setMeasuredDimension(measuredWidth, measuredHeight); 2124 } 2125 2126 @Override drawableStateChanged()2127 protected void drawableStateChanged() { 2128 super.drawableStateChanged(); 2129 updateDrawableState(); 2130 } 2131 updateDrawableState()2132 private void updateDrawableState() { 2133 final int[] state = getDrawableState(); 2134 boolean changed = false; 2135 2136 final Drawable progressDrawable = mProgressDrawable; 2137 if (progressDrawable != null && progressDrawable.isStateful()) { 2138 changed |= progressDrawable.setState(state); 2139 } 2140 2141 final Drawable indeterminateDrawable = mIndeterminateDrawable; 2142 if (indeterminateDrawable != null && indeterminateDrawable.isStateful()) { 2143 changed |= indeterminateDrawable.setState(state); 2144 } 2145 2146 if (changed) { 2147 invalidate(); 2148 } 2149 } 2150 2151 @Override drawableHotspotChanged(float x, float y)2152 public void drawableHotspotChanged(float x, float y) { 2153 super.drawableHotspotChanged(x, y); 2154 2155 if (mProgressDrawable != null) { 2156 mProgressDrawable.setHotspot(x, y); 2157 } 2158 2159 if (mIndeterminateDrawable != null) { 2160 mIndeterminateDrawable.setHotspot(x, y); 2161 } 2162 } 2163 2164 static class SavedState extends BaseSavedState { 2165 int progress; 2166 int secondaryProgress; 2167 2168 /** 2169 * Constructor called from {@link ProgressBar#onSaveInstanceState()} 2170 */ SavedState(Parcelable superState)2171 SavedState(Parcelable superState) { 2172 super(superState); 2173 } 2174 2175 /** 2176 * Constructor called from {@link #CREATOR} 2177 */ SavedState(Parcel in)2178 private SavedState(Parcel in) { 2179 super(in); 2180 progress = in.readInt(); 2181 secondaryProgress = in.readInt(); 2182 } 2183 2184 @Override writeToParcel(Parcel out, int flags)2185 public void writeToParcel(Parcel out, int flags) { 2186 super.writeToParcel(out, flags); 2187 out.writeInt(progress); 2188 out.writeInt(secondaryProgress); 2189 } 2190 2191 public static final @android.annotation.NonNull Parcelable.Creator<SavedState> CREATOR 2192 = new Parcelable.Creator<SavedState>() { 2193 public SavedState createFromParcel(Parcel in) { 2194 return new SavedState(in); 2195 } 2196 2197 public SavedState[] newArray(int size) { 2198 return new SavedState[size]; 2199 } 2200 }; 2201 } 2202 2203 @Override onSaveInstanceState()2204 public Parcelable onSaveInstanceState() { 2205 // Force our ancestor class to save its state 2206 Parcelable superState = super.onSaveInstanceState(); 2207 SavedState ss = new SavedState(superState); 2208 2209 ss.progress = mProgress; 2210 ss.secondaryProgress = mSecondaryProgress; 2211 2212 return ss; 2213 } 2214 2215 @Override onRestoreInstanceState(Parcelable state)2216 public void onRestoreInstanceState(Parcelable state) { 2217 SavedState ss = (SavedState) state; 2218 super.onRestoreInstanceState(ss.getSuperState()); 2219 2220 setProgress(ss.progress); 2221 setSecondaryProgress(ss.secondaryProgress); 2222 } 2223 2224 @Override onAttachedToWindow()2225 protected void onAttachedToWindow() { 2226 super.onAttachedToWindow(); 2227 if (mIndeterminate) { 2228 startAnimation(); 2229 } 2230 if (mRefreshData != null) { 2231 synchronized (this) { 2232 final int count = mRefreshData.size(); 2233 for (int i = 0; i < count; i++) { 2234 final RefreshData rd = mRefreshData.get(i); 2235 doRefreshProgress(rd.id, rd.progress, rd.fromUser, true, rd.animate); 2236 rd.recycle(); 2237 } 2238 mRefreshData.clear(); 2239 } 2240 } 2241 mAttached = true; 2242 } 2243 2244 @Override onDetachedFromWindow()2245 protected void onDetachedFromWindow() { 2246 if (mIndeterminate) { 2247 stopAnimation(); 2248 } 2249 if (mRefreshProgressRunnable != null) { 2250 removeCallbacks(mRefreshProgressRunnable); 2251 mRefreshIsPosted = false; 2252 } 2253 if (mAccessibilityEventSender != null) { 2254 removeCallbacks(mAccessibilityEventSender); 2255 } 2256 // This should come after stopAnimation(), otherwise an invalidate message remains in the 2257 // queue, which can prevent the entire view hierarchy from being GC'ed during a rotation 2258 super.onDetachedFromWindow(); 2259 mAttached = false; 2260 } 2261 2262 @Override getAccessibilityClassName()2263 public CharSequence getAccessibilityClassName() { 2264 return ProgressBar.class.getName(); 2265 } 2266 2267 /** @hide */ 2268 @Override onInitializeAccessibilityEventInternal(AccessibilityEvent event)2269 public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) { 2270 super.onInitializeAccessibilityEventInternal(event); 2271 event.setItemCount(mMax - mMin); 2272 event.setCurrentItemIndex(mProgress); 2273 } 2274 2275 /** @hide */ 2276 @Override onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info)2277 public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) { 2278 super.onInitializeAccessibilityNodeInfoInternal(info); 2279 2280 if (!isIndeterminate()) { 2281 AccessibilityNodeInfo.RangeInfo rangeInfo = AccessibilityNodeInfo.RangeInfo.obtain( 2282 AccessibilityNodeInfo.RangeInfo.RANGE_TYPE_INT, getMin(), getMax(), 2283 getProgress()); 2284 info.setRangeInfo(rangeInfo); 2285 } 2286 } 2287 2288 /** 2289 * Schedule a command for sending an accessibility event. 2290 * </br> 2291 * Note: A command is used to ensure that accessibility events 2292 * are sent at most one in a given time frame to save 2293 * system resources while the progress changes quickly. 2294 */ scheduleAccessibilityEventSender()2295 private void scheduleAccessibilityEventSender() { 2296 if (mAccessibilityEventSender == null) { 2297 mAccessibilityEventSender = new AccessibilityEventSender(); 2298 } else { 2299 removeCallbacks(mAccessibilityEventSender); 2300 } 2301 postDelayed(mAccessibilityEventSender, TIMEOUT_SEND_ACCESSIBILITY_EVENT); 2302 } 2303 2304 /** @hide */ 2305 @Override encodeProperties(@onNull ViewHierarchyEncoder stream)2306 protected void encodeProperties(@NonNull ViewHierarchyEncoder stream) { 2307 super.encodeProperties(stream); 2308 2309 stream.addProperty("progress:max", getMax()); 2310 stream.addProperty("progress:progress", getProgress()); 2311 stream.addProperty("progress:secondaryProgress", getSecondaryProgress()); 2312 stream.addProperty("progress:indeterminate", isIndeterminate()); 2313 } 2314 2315 /** 2316 * Returns whether the ProgressBar is animating or not. This is essentially the same 2317 * as whether the ProgressBar is {@link #isIndeterminate() indeterminate} and visible, 2318 * as indeterminate ProgressBars are always animating, and non-indeterminate 2319 * ProgressBars are not animating. 2320 * 2321 * @return true if the ProgressBar is animating, false otherwise. 2322 */ isAnimating()2323 public boolean isAnimating() { 2324 return isIndeterminate() && getWindowVisibility() == VISIBLE && isShown(); 2325 } 2326 2327 /** 2328 * Command for sending an accessibility event. 2329 */ 2330 private class AccessibilityEventSender implements Runnable { run()2331 public void run() { 2332 sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED); 2333 } 2334 } 2335 2336 private static class ProgressTintInfo { 2337 ColorStateList mIndeterminateTintList; 2338 BlendMode mIndeterminateBlendMode; 2339 boolean mHasIndeterminateTint; 2340 boolean mHasIndeterminateTintMode; 2341 2342 ColorStateList mProgressTintList; 2343 BlendMode mProgressBlendMode; 2344 boolean mHasProgressTint; 2345 boolean mHasProgressTintMode; 2346 2347 ColorStateList mProgressBackgroundTintList; 2348 BlendMode mProgressBackgroundBlendMode; 2349 boolean mHasProgressBackgroundTint; 2350 boolean mHasProgressBackgroundTintMode; 2351 2352 ColorStateList mSecondaryProgressTintList; 2353 BlendMode mSecondaryProgressBlendMode; 2354 boolean mHasSecondaryProgressTint; 2355 boolean mHasSecondaryProgressTintMode; 2356 } 2357 2358 /** 2359 * Property wrapper around the visual state of the {@code progress} functionality 2360 * handled by the {@link ProgressBar#setProgress(int, boolean)} method. This does 2361 * not correspond directly to the actual progress -- only the visual state. 2362 */ 2363 private final FloatProperty<ProgressBar> VISUAL_PROGRESS = 2364 new FloatProperty<ProgressBar>("visual_progress") { 2365 @Override 2366 public void setValue(ProgressBar object, float value) { 2367 object.setVisualProgress(R.id.progress, value); 2368 object.mVisualProgress = value; 2369 } 2370 2371 @Override 2372 public Float get(ProgressBar object) { 2373 return object.mVisualProgress; 2374 } 2375 }; 2376 } 2377