Home | History | Annotate | Download | only in widget
      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      */
    244     public ProgressBar(Context context) {
    245         this(context, null);
    246     }
    247 
    248     public ProgressBar(Context context, AttributeSet attrs) {
    249         this(context, attrs, com.android.internal.R.attr.progressBarStyle);
    250     }
    251 
    252     public ProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
    253         this(context, attrs, defStyleAttr, 0);
    254     }
    255 
    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      */
    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 
    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      */
    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      */
    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")
    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
    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      */
    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      */
    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
    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
    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      */
    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
    657     public PorterDuff.Mode getIndeterminateTintMode() {
    658         return mProgressTintInfo != null ? mProgressTintInfo.mIndeterminateTintMode : null;
    659     }
    660 
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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
    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
    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      */
    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
    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
    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
    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      */
    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
   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      */
   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
   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      */
   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
   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
   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      */
   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      */
   1142     Drawable getCurrentDrawable() {
   1143         return mCurrentDrawable;
   1144     }
   1145 
   1146     @Override
   1147     protected boolean verifyDrawable(Drawable who) {
   1148         return who == mProgressDrawable || who == mIndeterminateDrawable
   1149                 || super.verifyDrawable(who);
   1150     }
   1151 
   1152     @Override
   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
   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
   1177     public void postInvalidate() {
   1178         if (!mNoInvalidate) {
   1179             super.postInvalidate();
   1180         }
   1181     }
   1182 
   1183     private class RefreshProgressRunnable implements Runnable {
   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 
   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 
   1218         public void recycle() {
   1219             sPool.release(this);
   1220         }
   1221     }
   1222 
   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 
   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 
   1266     void onProgressRefresh(float scale, boolean fromUser) {
   1267         if (AccessibilityManager.getInstance(mContext).isEnabled()) {
   1268             scheduleAccessibilityEventSender();
   1269         }
   1270     }
   1271 
   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
   1301     public synchronized void setProgress(int progress) {
   1302         setProgress(progress, false);
   1303     }
   1304 
   1305     @android.view.RemotableViewMethod
   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
   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")
   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")
   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")
   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
   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      */
   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      */
   1448     public synchronized final void incrementSecondaryProgressBy(int diff) {
   1449         setSecondaryProgress(mSecondaryProgress + diff);
   1450     }
   1451 
   1452     /**
   1453      * <p>Start the indeterminate progress animation.</p>
   1454      */
   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      */
   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      */
   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      */
   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      */
   1529     public Interpolator getInterpolator() {
   1530         return mInterpolator;
   1531     }
   1532 
   1533     @Override
   1534     @RemotableViewMethod
   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
   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
   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
   1581     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
   1582         updateDrawableBounds(w, h);
   1583     }
   1584 
   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
   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      */
   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
   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
   1698     protected void drawableStateChanged() {
   1699         super.drawableStateChanged();
   1700         updateDrawableState();
   1701     }
   1702 
   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
   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          */
   1735         SavedState(Parcelable superState) {
   1736             super(superState);
   1737         }
   1738 
   1739         /**
   1740          * Constructor called from {@link #CREATOR}
   1741          */
   1742         private SavedState(Parcel in) {
   1743             super(in);
   1744             progress = in.readInt();
   1745             secondaryProgress = in.readInt();
   1746         }
   1747 
   1748         @Override
   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
   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
   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
   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
   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
   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
   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      */
   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 {
   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