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