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