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