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