2 * Copyright (C) 2006 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package android.widget;
19 import android.annotation.Nullable;
20 import android.graphics.PorterDuff;
22 import com.android.internal.R;
24 import android.annotation.InterpolatorRes;
25 import android.content.Context;
26 import android.content.res.ColorStateList;
27 import android.content.res.TypedArray;
28 import android.graphics.Bitmap;
29 import android.graphics.BitmapShader;
30 import android.graphics.Canvas;
31 import android.graphics.Rect;
32 import android.graphics.Shader;
33 import android.graphics.drawable.Animatable;
34 import android.graphics.drawable.AnimationDrawable;
35 import android.graphics.drawable.BitmapDrawable;
36 import android.graphics.drawable.ClipDrawable;
37 import android.graphics.drawable.Drawable;
38 import android.graphics.drawable.LayerDrawable;
39 import android.graphics.drawable.ShapeDrawable;
40 import android.graphics.drawable.StateListDrawable;
41 import android.graphics.drawable.shapes.RoundRectShape;
42 import android.graphics.drawable.shapes.Shape;
43 import android.os.Parcel;
44 import android.os.Parcelable;
45 import android.util.AttributeSet;
46 import android.util.Pools.SynchronizedPool;
47 import android.view.Gravity;
48 import android.view.RemotableViewMethod;
49 import android.view.View;
50 import android.view.ViewDebug;
51 import android.view.accessibility.AccessibilityEvent;
52 import android.view.accessibility.AccessibilityManager;
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;
61 import java.util.ArrayList;
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.
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.
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:
86 * public class MyActivity extends Activity {
87 * private static final int PROGRESS = 0x1;
89 * private ProgressBar mProgress;
90 * private int mProgressStatus = 0;
92 * private Handler mHandler = new Handler();
94 * protected void onCreate(Bundle icicle) {
95 * super.onCreate(icicle);
97 * setContentView(R.layout.progressbar_activity);
99 * mProgress = (ProgressBar) findViewById(R.id.progress_bar);
101 * // Start lengthy operation in a background thread
102 * new Thread(new Runnable() {
103 * public void run() {
104 * while (mProgressStatus < 100) {
105 * mProgressStatus = doWork();
107 * // Update the progress bar
108 * mHandler.post(new Runnable() {
109 * public void run() {
110 * mProgress.setProgress(mProgressStatus);
119 * <p>To add a progress bar to a layout file, you can use the {@code <ProgressBar>} 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>
126 * style="@android:style/Widget.ProgressBar.Horizontal"
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
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—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—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>
146 * android:orientation="horizontal"
149 * android:layout_width="wrap_content"
150 * android:layout_height="wrap_content"
151 * style="@android:style/Widget.ProgressBar.Small"
152 * android:layout_marginRight="5dp" />
154 * android:layout_width="wrap_content"
155 * android:layout_height="wrap_content"
156 * android:text="@string/loading" />
157 * </LinearLayout></pre>
159 * <p>Other progress bar styles provided by the system include:</p>
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>
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>
173 * <p><strong>XML attributes</b></strong>
175 * See {@link android.R.styleable#ProgressBar ProgressBar Attributes},
176 * {@link android.R.styleable#View View Attributes}
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
197 public class ProgressBar extends View {
198 private static final int MAX_LEVEL = 10000;
199 private static final int TIMEOUT_SEND_ACCESSIBILITY_EVENT = 200;
206 private int mProgress;
207 private int mSecondaryProgress;
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;
218 private Drawable mIndeterminateDrawable;
219 private Drawable mProgressDrawable;
220 private Drawable mCurrentDrawable;
221 private ProgressTintInfo mProgressTintInfo;
224 private boolean mNoInvalidate;
225 private Interpolator mInterpolator;
226 private RefreshProgressRunnable mRefreshProgressRunnable;
227 private long mUiThreadId;
228 private boolean mShouldStartAnimationDrawable;
230 private boolean mInDrawing;
231 private boolean mAttached;
232 private boolean mRefreshIsPosted;
234 boolean mMirrorForRtl = false;
236 private final ArrayList<RefreshData> mRefreshData = new ArrayList<RefreshData>();
238 private AccessibilityEventSender mAccessibilityEventSender;
241 * Create a new progress bar with range 0...100 and initial progress of 0.
242 * @param context the application environment
244 public ProgressBar(Context context) {
248 public ProgressBar(Context context, AttributeSet attrs) {
249 this(context, attrs, com.android.internal.R.attr.progressBarStyle);
252 public ProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
253 this(context, attrs, defStyleAttr, 0);
256 public ProgressBar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
257 super(context, attrs, defStyleAttr, defStyleRes);
259 mUiThreadId = Thread.currentThread().getId();
262 final TypedArray a = context.obtainStyledAttributes(
263 attrs, R.styleable.ProgressBar, defStyleAttr, defStyleRes);
265 mNoInvalidate = true;
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);
275 mDuration = a.getInt(R.styleable.ProgressBar_indeterminateDuration, mDuration);
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);
282 mBehavior = a.getInt(R.styleable.ProgressBar_indeterminateBehavior, mBehavior);
284 final int resID = a.getResourceId(
285 com.android.internal.R.styleable.ProgressBar_interpolator,
286 android.R.anim.linear_interpolator); // default to linear interpolator
288 setInterpolator(context, resID);
291 setMax(a.getInt(R.styleable.ProgressBar_max, mMax));
293 setProgress(a.getInt(R.styleable.ProgressBar_progress, mProgress));
295 setSecondaryProgress(
296 a.getInt(R.styleable.ProgressBar_secondaryProgress, mSecondaryProgress));
298 final Drawable indeterminateDrawable = a.getDrawable(
299 R.styleable.ProgressBar_indeterminateDrawable);
300 if (indeterminateDrawable != null) {
301 setIndeterminateDrawableTiled(indeterminateDrawable);
304 mOnlyIndeterminate = a.getBoolean(
305 R.styleable.ProgressBar_indeterminateOnly, mOnlyIndeterminate);
307 mNoInvalidate = false;
309 setIndeterminate(mOnlyIndeterminate || a.getBoolean(
310 R.styleable.ProgressBar_indeterminate, mIndeterminate));
312 mMirrorForRtl = a.getBoolean(R.styleable.ProgressBar_mirrorForRtl, mMirrorForRtl);
314 if (a.hasValue(R.styleable.ProgressBar_progressTintMode)) {
315 if (mProgressTintInfo == null) {
316 mProgressTintInfo = new ProgressTintInfo();
318 mProgressTintInfo.mProgressTintMode = Drawable.parseTintMode(a.getInt(
319 R.styleable.ProgressBar_progressBackgroundTintMode, -1), null);
320 mProgressTintInfo.mHasProgressTintMode = true;
323 if (a.hasValue(R.styleable.ProgressBar_progressTint)) {
324 if (mProgressTintInfo == null) {
325 mProgressTintInfo = new ProgressTintInfo();
327 mProgressTintInfo.mProgressTintList = a.getColorStateList(
328 R.styleable.ProgressBar_progressTint);
329 mProgressTintInfo.mHasProgressTint = true;
332 if (a.hasValue(R.styleable.ProgressBar_progressBackgroundTintMode)) {
333 if (mProgressTintInfo == null) {
334 mProgressTintInfo = new ProgressTintInfo();
336 mProgressTintInfo.mProgressBackgroundTintMode = Drawable.parseTintMode(a.getInt(
337 R.styleable.ProgressBar_progressTintMode, -1), null);
338 mProgressTintInfo.mHasProgressBackgroundTintMode = true;
341 if (a.hasValue(R.styleable.ProgressBar_progressBackgroundTint)) {
342 if (mProgressTintInfo == null) {
343 mProgressTintInfo = new ProgressTintInfo();
345 mProgressTintInfo.mProgressBackgroundTintList = a.getColorStateList(
346 R.styleable.ProgressBar_progressBackgroundTint);
347 mProgressTintInfo.mHasProgressBackgroundTint = true;
350 if (a.hasValue(R.styleable.ProgressBar_secondaryProgressTintMode)) {
351 if (mProgressTintInfo == null) {
352 mProgressTintInfo = new ProgressTintInfo();
354 mProgressTintInfo.mSecondaryProgressTintMode = Drawable.parseTintMode(
355 a.getInt(R.styleable.ProgressBar_secondaryProgressTintMode, -1), null);
356 mProgressTintInfo.mHasSecondaryProgressTintMode = true;
359 if (a.hasValue(R.styleable.ProgressBar_secondaryProgressTint)) {
360 if (mProgressTintInfo == null) {
361 mProgressTintInfo = new ProgressTintInfo();
363 mProgressTintInfo.mSecondaryProgressTintList = a.getColorStateList(
364 R.styleable.ProgressBar_secondaryProgressTint);
365 mProgressTintInfo.mHasSecondaryProgressTint = true;
368 if (a.hasValue(R.styleable.ProgressBar_indeterminateTint)) {
369 if (mProgressTintInfo == null) {
370 mProgressTintInfo = new ProgressTintInfo();
372 mProgressTintInfo.mIndeterminateTintMode = Drawable.parseTintMode(a.getInt(
373 R.styleable.ProgressBar_indeterminateTintMode, -1), null);
374 mProgressTintInfo.mHasIndeterminateTintMode = true;
377 if (a.hasValue(R.styleable.ProgressBar_indeterminateTint)) {
378 if (mProgressTintInfo == null) {
379 mProgressTintInfo = new ProgressTintInfo();
381 mProgressTintInfo.mIndeterminateTintList = a.getColorStateList(
382 R.styleable.ProgressBar_indeterminateTint);
383 mProgressTintInfo.mHasIndeterminateTint = true;
388 applyProgressTints();
389 applyIndeterminateTint();
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);
398 * Converts a drawable to a tiled version of itself. It will recursively
399 * traverse layer and state list drawables.
401 private Drawable tileify(Drawable drawable, boolean clip) {
402 // TODO: This is a terrible idea that potentially destroys any drawable
403 // that extends any of these classes. We *really* need to remove this.
405 if (drawable instanceof LayerDrawable) {
406 final LayerDrawable orig = (LayerDrawable) drawable;
407 final int N = orig.getNumberOfLayers();
408 final Drawable[] outDrawables = new Drawable[N];
410 for (int i = 0; i < N; i++) {
411 final int id = orig.getId(i);
412 outDrawables[i] = tileify(orig.getDrawable(i),
413 (id == R.id.progress || id == R.id.secondaryProgress));
416 final LayerDrawable clone = new LayerDrawable(outDrawables);
417 for (int i = 0; i < N; i++) {
418 clone.setId(i, orig.getId(i));
419 clone.setLayerGravity(i, orig.getLayerGravity(i));
420 clone.setLayerWidth(i, orig.getLayerWidth(i));
421 clone.setLayerHeight(i, orig.getLayerHeight(i));
422 clone.setLayerInsetLeft(i, orig.getLayerInsetLeft(i));
423 clone.setLayerInsetRight(i, orig.getLayerInsetRight(i));
424 clone.setLayerInsetTop(i, orig.getLayerInsetTop(i));
425 clone.setLayerInsetBottom(i, orig.getLayerInsetBottom(i));
426 clone.setLayerInsetStart(i, orig.getLayerInsetStart(i));
427 clone.setLayerInsetEnd(i, orig.getLayerInsetEnd(i));
433 if (drawable instanceof StateListDrawable) {
434 final StateListDrawable in = (StateListDrawable) drawable;
435 final StateListDrawable out = new StateListDrawable();
436 final int N = in.getStateCount();
437 for (int i = 0; i < N; i++) {
438 out.addState(in.getStateSet(i), tileify(in.getStateDrawable(i), clip));
444 if (drawable instanceof BitmapDrawable) {
445 final BitmapDrawable bitmap = (BitmapDrawable) drawable;
446 final Bitmap tileBitmap = bitmap.getBitmap();
447 if (mSampleTile == null) {
448 mSampleTile = tileBitmap;
451 final ShapeDrawable shapeDrawable = new ShapeDrawable(getDrawableShape());
452 final BitmapShader bitmapShader = new BitmapShader(tileBitmap,
453 Shader.TileMode.REPEAT, Shader.TileMode.CLAMP);
454 shapeDrawable.getPaint().setShader(bitmapShader);
456 // Ensure the tint and filter are propagated in the correct order.
457 shapeDrawable.setTintList(bitmap.getTint());
458 shapeDrawable.setTintMode(bitmap.getTintMode());
459 shapeDrawable.setColorFilter(bitmap.getColorFilter());
461 return clip ? new ClipDrawable(
462 shapeDrawable, Gravity.LEFT, ClipDrawable.HORIZONTAL) : shapeDrawable;
468 Shape getDrawableShape() {
469 final float[] roundedCorners = new float[] { 5, 5, 5, 5, 5, 5, 5, 5 };
470 return new RoundRectShape(roundedCorners, null, null);
474 * Convert a AnimationDrawable for use as a barberpole animation.
475 * Each frame of the animation is wrapped in a ClipDrawable and
476 * given a tiling BitmapShader.
478 private Drawable tileifyIndeterminate(Drawable drawable) {
479 if (drawable instanceof AnimationDrawable) {
480 AnimationDrawable background = (AnimationDrawable) drawable;
481 final int N = background.getNumberOfFrames();
482 AnimationDrawable newBg = new AnimationDrawable();
483 newBg.setOneShot(background.isOneShot());
485 for (int i = 0; i < N; i++) {
486 Drawable frame = tileify(background.getFrame(i), true);
487 frame.setLevel(10000);
488 newBg.addFrame(frame, background.getDuration(i));
490 newBg.setLevel(10000);
498 * Initialize the progress bar's default values:
501 * <li>progress = 0</li>
503 * <li>animation duration = 4000 ms</li>
504 * <li>indeterminate = false</li>
505 * <li>behavior = repeat</li>
508 private void initProgressBar() {
511 mSecondaryProgress = 0;
512 mIndeterminate = false;
513 mOnlyIndeterminate = false;
515 mBehavior = AlphaAnimation.RESTART;
523 * <p>Indicate whether this progress bar is in indeterminate mode.</p>
525 * @return true if the progress bar is in indeterminate mode
527 @ViewDebug.ExportedProperty(category = "progress")
528 public synchronized boolean isIndeterminate() {
529 return mIndeterminate;
533 * <p>Change the indeterminate mode for this progress bar. In indeterminate
534 * mode, the progress is ignored and the progress bar shows an infinite
535 * animation instead.</p>
537 * If this progress bar's style only supports indeterminate mode (such as the circular
538 * progress bars), then this will be ignored.
540 * @param indeterminate true to enable the indeterminate mode
542 @android.view.RemotableViewMethod
543 public synchronized void setIndeterminate(boolean indeterminate) {
544 if ((!mOnlyIndeterminate || !mIndeterminate) && indeterminate != mIndeterminate) {
545 mIndeterminate = indeterminate;
548 // swap between indeterminate and regular backgrounds
549 mCurrentDrawable = mIndeterminateDrawable;
552 mCurrentDrawable = mProgressDrawable;
559 * <p>Get the drawable used to draw the progress bar in
560 * indeterminate mode.</p>
562 * @return a {@link android.graphics.drawable.Drawable} instance
564 * @see #setIndeterminateDrawable(android.graphics.drawable.Drawable)
565 * @see #setIndeterminate(boolean)
567 public Drawable getIndeterminateDrawable() {
568 return mIndeterminateDrawable;
572 * Define the drawable used to draw the progress bar in indeterminate mode.
574 * @param d the new drawable
575 * @see #getIndeterminateDrawable()
576 * @see #setIndeterminate(boolean)
578 public void setIndeterminateDrawable(Drawable d) {
579 if (mIndeterminateDrawable != d) {
580 if (mIndeterminateDrawable != null) {
581 mIndeterminateDrawable.setCallback(null);
582 unscheduleDrawable(mIndeterminateDrawable);
585 mIndeterminateDrawable = d;
589 d.setLayoutDirection(getLayoutDirection());
590 if (d.isStateful()) {
591 d.setState(getDrawableState());
593 applyIndeterminateTint();
596 if (mIndeterminate) {
597 mCurrentDrawable = d;
604 * Applies a tint to the indeterminate drawable. Does not modify the
605 * current tint mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
607 * Subsequent calls to {@link #setIndeterminateDrawable(Drawable)} will
608 * automatically mutate the drawable and apply the specified tint and
610 * {@link Drawable#setTintList(ColorStateList)}.
612 * @param tint the tint to apply, may be {@code null} to clear tint
614 * @attr ref android.R.styleable#ProgressBar_indeterminateTint
615 * @see #getIndeterminateTintList()
616 * @see Drawable#setTintList(ColorStateList)
619 public void setIndeterminateTintList(@Nullable ColorStateList tint) {
620 if (mProgressTintInfo == null) {
621 mProgressTintInfo = new ProgressTintInfo();
623 mProgressTintInfo.mIndeterminateTintList = tint;
624 mProgressTintInfo.mHasIndeterminateTint = true;
626 applyIndeterminateTint();
630 * @return the tint applied to the indeterminate drawable
631 * @attr ref android.R.styleable#ProgressBar_indeterminateTint
632 * @see #setIndeterminateTintList(ColorStateList)
635 public ColorStateList getIndeterminateTintList() {
636 return mProgressTintInfo != null ? mProgressTintInfo.mIndeterminateTintList : null;
640 * Specifies the blending mode used to apply the tint specified by
641 * {@link #setIndeterminateTintList(ColorStateList)} to the indeterminate
642 * drawable. The default mode is {@link PorterDuff.Mode#SRC_IN}.
644 * @param tintMode the blending mode used to apply the tint, may be
645 * {@code null} to clear tint
646 * @attr ref android.R.styleable#ProgressBar_indeterminateTintMode
647 * @see #setIndeterminateTintList(ColorStateList)
648 * @see Drawable#setTintMode(PorterDuff.Mode)
650 public void setIndeterminateTintMode(@Nullable PorterDuff.Mode tintMode) {
651 if (mProgressTintInfo == null) {
652 mProgressTintInfo = new ProgressTintInfo();
654 mProgressTintInfo.mIndeterminateTintMode = tintMode;
655 mProgressTintInfo.mHasIndeterminateTintMode = true;
657 applyIndeterminateTint();
661 * Returns the blending mode used to apply the tint to the indeterminate
662 * drawable, if specified.
664 * @return the blending mode used to apply the tint to the indeterminate
666 * @attr ref android.R.styleable#ProgressBar_indeterminateTintMode
667 * @see #setIndeterminateTintMode(PorterDuff.Mode)
670 public PorterDuff.Mode getIndeterminateTintMode() {
671 return mProgressTintInfo != null ? mProgressTintInfo.mIndeterminateTintMode : null;
674 private void applyIndeterminateTint() {
675 if (mIndeterminateDrawable != null && mProgressTintInfo != null) {
676 final ProgressTintInfo tintInfo = mProgressTintInfo;
677 if (tintInfo.mHasIndeterminateTint || tintInfo.mHasIndeterminateTintMode) {
678 mIndeterminateDrawable = mIndeterminateDrawable.mutate();
680 if (tintInfo.mHasIndeterminateTint) {
681 mIndeterminateDrawable.setTintList(tintInfo.mIndeterminateTintList);
684 if (tintInfo.mHasIndeterminateTintMode) {
685 mIndeterminateDrawable.setTintMode(tintInfo.mIndeterminateTintMode);
688 // The drawable (or one of its children) may not have been
689 // stateful before applying the tint, so let's try again.
690 if (mIndeterminateDrawable.isStateful()) {
691 mIndeterminateDrawable.setState(getDrawableState());
698 * Define the tileable drawable used to draw the progress bar in
699 * indeterminate mode.
701 * If the drawable is a BitmapDrawable or contains BitmapDrawables, a
702 * tiled copy will be generated for display as a progress bar.
704 * @param d the new drawable
705 * @see #getIndeterminateDrawable()
706 * @see #setIndeterminate(boolean)
708 public void setIndeterminateDrawableTiled(Drawable d) {
710 d = tileifyIndeterminate(d);
713 setIndeterminateDrawable(d);
717 * <p>Get the drawable used to draw the progress bar in
720 * @return a {@link android.graphics.drawable.Drawable} instance
722 * @see #setProgressDrawable(android.graphics.drawable.Drawable)
723 * @see #setIndeterminate(boolean)
725 public Drawable getProgressDrawable() {
726 return mProgressDrawable;
730 * Define the drawable used to draw the progress bar in progress mode.
732 * @param d the new drawable
733 * @see #getProgressDrawable()
734 * @see #setIndeterminate(boolean)
736 public void setProgressDrawable(Drawable d) {
737 if (mProgressDrawable != d) {
738 if (mProgressDrawable != null) {
739 mProgressDrawable.setCallback(null);
740 unscheduleDrawable(mProgressDrawable);
743 mProgressDrawable = d;
747 d.setLayoutDirection(getLayoutDirection());
748 if (d.isStateful()) {
749 d.setState(getDrawableState());
752 // Make sure the ProgressBar is always tall enough
753 int drawableHeight = d.getMinimumHeight();
754 if (mMaxHeight < drawableHeight) {
755 mMaxHeight = drawableHeight;
759 applyProgressTints();
762 if (!mIndeterminate) {
763 mCurrentDrawable = d;
767 updateDrawableBounds(getWidth(), getHeight());
768 updateDrawableState();
770 doRefreshProgress(R.id.progress, mProgress, false, false);
771 doRefreshProgress(R.id.secondaryProgress, mSecondaryProgress, false, false);
776 * Applies the progress tints in order of increasing specificity.
778 private void applyProgressTints() {
779 if (mProgressDrawable != null && mProgressTintInfo != null) {
780 applyPrimaryProgressTint();
781 applyProgressBackgroundTint();
782 applySecondaryProgressTint();
787 * Should only be called if we've already verified that mProgressDrawable
788 * and mProgressTintInfo are non-null.
790 private void applyPrimaryProgressTint() {
791 if (mProgressTintInfo.mHasProgressTint
792 || mProgressTintInfo.mHasProgressTintMode) {
793 final Drawable target = getTintTarget(R.id.progress, true);
794 if (target != null) {
795 if (mProgressTintInfo.mHasProgressTint) {
796 target.setTintList(mProgressTintInfo.mProgressTintList);
798 if (mProgressTintInfo.mHasProgressTintMode) {
799 target.setTintMode(mProgressTintInfo.mProgressTintMode);
802 // The drawable (or one of its children) may not have been
803 // stateful before applying the tint, so let's try again.
804 if (target.isStateful()) {
805 target.setState(getDrawableState());
812 * Should only be called if we've already verified that mProgressDrawable
813 * and mProgressTintInfo are non-null.
815 private void applyProgressBackgroundTint() {
816 if (mProgressTintInfo.mHasProgressBackgroundTint
817 || mProgressTintInfo.mHasProgressBackgroundTintMode) {
818 final Drawable target = getTintTarget(R.id.background, false);
819 if (target != null) {
820 if (mProgressTintInfo.mHasProgressBackgroundTint) {
821 target.setTintList(mProgressTintInfo.mProgressBackgroundTintList);
823 if (mProgressTintInfo.mHasProgressBackgroundTintMode) {
824 target.setTintMode(mProgressTintInfo.mProgressBackgroundTintMode);
827 // The drawable (or one of its children) may not have been
828 // stateful before applying the tint, so let's try again.
829 if (target.isStateful()) {
830 target.setState(getDrawableState());
837 * Should only be called if we've already verified that mProgressDrawable
838 * and mProgressTintInfo are non-null.
840 private void applySecondaryProgressTint() {
841 if (mProgressTintInfo.mHasSecondaryProgressTint
842 || mProgressTintInfo.mHasSecondaryProgressTintMode) {
843 final Drawable target = getTintTarget(R.id.secondaryProgress, false);
844 if (target != null) {
845 if (mProgressTintInfo.mHasSecondaryProgressTint) {
846 target.setTintList(mProgressTintInfo.mSecondaryProgressTintList);
848 if (mProgressTintInfo.mHasSecondaryProgressTintMode) {
849 target.setTintMode(mProgressTintInfo.mSecondaryProgressTintMode);
852 // The drawable (or one of its children) may not have been
853 // stateful before applying the tint, so let's try again.
854 if (target.isStateful()) {
855 target.setState(getDrawableState());
862 * Applies a tint to the progress indicator, if one exists, or to the
863 * entire progress drawable otherwise. Does not modify the current tint
864 * mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
866 * The progress indicator should be specified as a layer with
867 * id {@link android.R.id#progress} in a {@link LayerDrawable}
868 * used as the progress drawable.
870 * Subsequent calls to {@link #setProgressDrawable(Drawable)} will
871 * automatically mutate the drawable and apply the specified tint and
873 * {@link Drawable#setTintList(ColorStateList)}.
875 * @param tint the tint to apply, may be {@code null} to clear tint
877 * @attr ref android.R.styleable#ProgressBar_progressTint
878 * @see #getProgressTintList()
879 * @see Drawable#setTintList(ColorStateList)
882 public void setProgressTintList(@Nullable ColorStateList tint) {
883 if (mProgressTintInfo == null) {
884 mProgressTintInfo = new ProgressTintInfo();
886 mProgressTintInfo.mProgressTintList = tint;
887 mProgressTintInfo.mHasProgressTint = true;
889 if (mProgressDrawable != null) {
890 applyPrimaryProgressTint();
895 * Returns the tint applied to the progress drawable, if specified.
897 * @return the tint applied to the progress drawable
898 * @attr ref android.R.styleable#ProgressBar_progressTint
899 * @see #setProgressTintList(ColorStateList)
902 public ColorStateList getProgressTintList() {
903 return mProgressTintInfo != null ? mProgressTintInfo.mProgressTintList : null;
907 * Specifies the blending mode used to apply the tint specified by
908 * {@link #setProgressTintList(ColorStateList)}} to the progress
909 * indicator. The default mode is {@link PorterDuff.Mode#SRC_IN}.
911 * @param tintMode the blending mode used to apply the tint, may be
912 * {@code null} to clear tint
913 * @attr ref android.R.styleable#ProgressBar_progressTintMode
914 * @see #getProgressTintMode()
915 * @see Drawable#setTintMode(PorterDuff.Mode)
917 public void setProgressTintMode(@Nullable PorterDuff.Mode tintMode) {
918 if (mProgressTintInfo == null) {
919 mProgressTintInfo = new ProgressTintInfo();
921 mProgressTintInfo.mProgressTintMode = tintMode;
922 mProgressTintInfo.mHasProgressTintMode = true;
924 if (mProgressDrawable != null) {
925 applyPrimaryProgressTint();
930 * Returns the blending mode used to apply the tint to the progress
931 * drawable, if specified.
933 * @return the blending mode used to apply the tint to the progress
935 * @attr ref android.R.styleable#ProgressBar_progressTintMode
936 * @see #setProgressTintMode(PorterDuff.Mode)
939 public PorterDuff.Mode getProgressTintMode() {
940 return mProgressTintInfo != null ? mProgressTintInfo.mProgressTintMode : null;
944 * Applies a tint to the progress background, if one exists. Does not
945 * modify the current tint mode, which is
946 * {@link PorterDuff.Mode#SRC_ATOP} by default.
948 * The progress background must be specified as a layer with
949 * id {@link android.R.id#background} in a {@link LayerDrawable}
950 * used as the progress drawable.
952 * Subsequent calls to {@link #setProgressDrawable(Drawable)} where the
953 * drawable contains a progress background will automatically mutate the
954 * drawable and apply the specified tint and tint mode using
955 * {@link Drawable#setTintList(ColorStateList)}.
957 * @param tint the tint to apply, may be {@code null} to clear tint
959 * @attr ref android.R.styleable#ProgressBar_progressBackgroundTint
960 * @see #getProgressBackgroundTintList()
961 * @see Drawable#setTintList(ColorStateList)
964 public void setProgressBackgroundTintList(@Nullable ColorStateList tint) {
965 if (mProgressTintInfo == null) {
966 mProgressTintInfo = new ProgressTintInfo();
968 mProgressTintInfo.mProgressBackgroundTintList = tint;
969 mProgressTintInfo.mHasProgressBackgroundTint = true;
971 if (mProgressDrawable != null) {
972 applyProgressBackgroundTint();
977 * Returns the tint applied to the progress background, if specified.
979 * @return the tint applied to the progress background
980 * @attr ref android.R.styleable#ProgressBar_progressBackgroundTint
981 * @see #setProgressBackgroundTintList(ColorStateList)
984 public ColorStateList getProgressBackgroundTintList() {
985 return mProgressTintInfo != null ? mProgressTintInfo.mProgressBackgroundTintList : null;
989 * Specifies the blending mode used to apply the tint specified by
990 * {@link #setProgressBackgroundTintList(ColorStateList)}} to the progress
991 * background. The default mode is {@link PorterDuff.Mode#SRC_IN}.
993 * @param tintMode the blending mode used to apply the tint, may be
994 * {@code null} to clear tint
995 * @attr ref android.R.styleable#ProgressBar_progressBackgroundTintMode
996 * @see #setProgressBackgroundTintList(ColorStateList)
997 * @see Drawable#setTintMode(PorterDuff.Mode)
999 public void setProgressBackgroundTintMode(@Nullable PorterDuff.Mode tintMode) {
1000 if (mProgressTintInfo == null) {
1001 mProgressTintInfo = new ProgressTintInfo();
1003 mProgressTintInfo.mProgressBackgroundTintMode = tintMode;
1004 mProgressTintInfo.mHasProgressBackgroundTintMode = true;
1006 if (mProgressDrawable != null) {
1007 applyProgressBackgroundTint();
1012 * @return the blending mode used to apply the tint to the progress
1014 * @attr ref android.R.styleable#ProgressBar_progressBackgroundTintMode
1015 * @see #setProgressBackgroundTintMode(PorterDuff.Mode)
1018 public PorterDuff.Mode getProgressBackgroundTintMode() {
1019 return mProgressTintInfo != null ? mProgressTintInfo.mProgressBackgroundTintMode : null;
1023 * Applies a tint to the secondary progress indicator, if one exists.
1024 * Does not modify the current tint mode, which is
1025 * {@link PorterDuff.Mode#SRC_ATOP} by default.
1027 * The secondary progress indicator must be specified as a layer with
1028 * id {@link android.R.id#secondaryProgress} in a {@link LayerDrawable}
1029 * used as the progress drawable.
1031 * Subsequent calls to {@link #setProgressDrawable(Drawable)} where the
1032 * drawable contains a secondary progress indicator will automatically
1033 * mutate the drawable and apply the specified tint and tint mode using
1034 * {@link Drawable#setTintList(ColorStateList)}.
1036 * @param tint the tint to apply, may be {@code null} to clear tint
1038 * @attr ref android.R.styleable#ProgressBar_secondaryProgressTint
1039 * @see #getSecondaryProgressTintList()
1040 * @see Drawable#setTintList(ColorStateList)
1042 public void setSecondaryProgressTintList(@Nullable ColorStateList tint) {
1043 if (mProgressTintInfo == null) {
1044 mProgressTintInfo = new ProgressTintInfo();
1046 mProgressTintInfo.mSecondaryProgressTintList = tint;
1047 mProgressTintInfo.mHasSecondaryProgressTint = true;
1049 if (mProgressDrawable != null) {
1050 applySecondaryProgressTint();
1055 * Returns the tint applied to the secondary progress drawable, if
1058 * @return the tint applied to the secondary progress drawable
1059 * @attr ref android.R.styleable#ProgressBar_secondaryProgressTint
1060 * @see #setSecondaryProgressTintList(ColorStateList)
1063 public ColorStateList getSecondaryProgressTintList() {
1064 return mProgressTintInfo != null ? mProgressTintInfo.mSecondaryProgressTintList : null;
1068 * Specifies the blending mode used to apply the tint specified by
1069 * {@link #setSecondaryProgressTintList(ColorStateList)}} to the secondary
1070 * progress indicator. The default mode is
1071 * {@link PorterDuff.Mode#SRC_ATOP}.
1073 * @param tintMode the blending mode used to apply the tint, may be
1074 * {@code null} to clear tint
1075 * @attr ref android.R.styleable#ProgressBar_secondaryProgressTintMode
1076 * @see #setSecondaryProgressTintList(ColorStateList)
1077 * @see Drawable#setTintMode(PorterDuff.Mode)
1079 public void setSecondaryProgressTintMode(@Nullable PorterDuff.Mode tintMode) {
1080 if (mProgressTintInfo == null) {
1081 mProgressTintInfo = new ProgressTintInfo();
1083 mProgressTintInfo.mSecondaryProgressTintMode = tintMode;
1084 mProgressTintInfo.mHasSecondaryProgressTintMode = true;
1086 if (mProgressDrawable != null) {
1087 applySecondaryProgressTint();
1092 * Returns the blending mode used to apply the tint to the secondary
1093 * progress drawable, if specified.
1095 * @return the blending mode used to apply the tint to the secondary
1097 * @attr ref android.R.styleable#ProgressBar_secondaryProgressTintMode
1098 * @see #setSecondaryProgressTintMode(PorterDuff.Mode)
1101 public PorterDuff.Mode getSecondaryProgressTintMode() {
1102 return mProgressTintInfo != null ? mProgressTintInfo.mSecondaryProgressTintMode : null;
1106 * Returns the drawable to which a tint or tint mode should be applied.
1108 * @param layerId id of the layer to modify
1109 * @param shouldFallback whether the base drawable should be returned
1110 * if the id does not exist
1111 * @return the drawable to modify
1114 private Drawable getTintTarget(int layerId, boolean shouldFallback) {
1115 Drawable layer = null;
1117 final Drawable d = mProgressDrawable;
1119 mProgressDrawable = d.mutate();
1121 if (d instanceof LayerDrawable) {
1122 layer = ((LayerDrawable) d).findDrawableByLayerId(layerId);
1125 if (shouldFallback && layer == null) {
1134 * Define the tileable drawable used to draw the progress bar in
1137 * If the drawable is a BitmapDrawable or contains BitmapDrawables, a
1138 * tiled copy will be generated for display as a progress bar.
1140 * @param d the new drawable
1141 * @see #getProgressDrawable()
1142 * @see #setIndeterminate(boolean)
1144 public void setProgressDrawableTiled(Drawable d) {
1146 d = tileify(d, false);
1149 setProgressDrawable(d);
1153 * @return The drawable currently used to draw the progress bar
1155 Drawable getCurrentDrawable() {
1156 return mCurrentDrawable;
1160 protected boolean verifyDrawable(Drawable who) {
1161 return who == mProgressDrawable || who == mIndeterminateDrawable
1162 || super.verifyDrawable(who);
1166 public void jumpDrawablesToCurrentState() {
1167 super.jumpDrawablesToCurrentState();
1168 if (mProgressDrawable != null) mProgressDrawable.jumpToCurrentState();
1169 if (mIndeterminateDrawable != null) mIndeterminateDrawable.jumpToCurrentState();
1176 public void onResolveDrawables(int layoutDirection) {
1177 final Drawable d = mCurrentDrawable;
1179 d.setLayoutDirection(layoutDirection);
1181 if (mIndeterminateDrawable != null) {
1182 mIndeterminateDrawable.setLayoutDirection(layoutDirection);
1184 if (mProgressDrawable != null) {
1185 mProgressDrawable.setLayoutDirection(layoutDirection);
1190 public void postInvalidate() {
1191 if (!mNoInvalidate) {
1192 super.postInvalidate();
1196 private class RefreshProgressRunnable implements Runnable {
1198 synchronized (ProgressBar.this) {
1199 final int count = mRefreshData.size();
1200 for (int i = 0; i < count; i++) {
1201 final RefreshData rd = mRefreshData.get(i);
1202 doRefreshProgress(rd.id, rd.progress, rd.fromUser, true);
1205 mRefreshData.clear();
1206 mRefreshIsPosted = false;
1211 private static class RefreshData {
1212 private static final int POOL_MAX = 24;
1213 private static final SynchronizedPool<RefreshData> sPool =
1214 new SynchronizedPool<RefreshData>(POOL_MAX);
1217 public int progress;
1218 public boolean fromUser;
1220 public static RefreshData obtain(int id, int progress, boolean fromUser) {
1221 RefreshData rd = sPool.acquire();
1223 rd = new RefreshData();
1226 rd.progress = progress;
1227 rd.fromUser = fromUser;
1231 public void recycle() {
1232 sPool.release(this);
1236 private synchronized void doRefreshProgress(int id, int progress, boolean fromUser,
1237 boolean callBackToApp) {
1238 float scale = mMax > 0 ? (float) progress / (float) mMax : 0;
1239 final Drawable d = mCurrentDrawable;
1241 Drawable progressDrawable = null;
1243 if (d instanceof LayerDrawable) {
1244 progressDrawable = ((LayerDrawable) d).findDrawableByLayerId(id);
1245 if (progressDrawable != null && canResolveLayoutDirection()) {
1246 progressDrawable.setLayoutDirection(getLayoutDirection());
1250 final int level = (int) (scale * MAX_LEVEL);
1251 (progressDrawable != null ? progressDrawable : d).setLevel(level);
1256 if (callBackToApp && id == R.id.progress) {
1257 onProgressRefresh(scale, fromUser, progress);
1261 void onProgressRefresh(float scale, boolean fromUser, int progress) {
1262 if (AccessibilityManager.getInstance(mContext).isEnabled()) {
1263 scheduleAccessibilityEventSender();
1267 private synchronized void refreshProgress(int id, int progress, boolean fromUser) {
1268 if (mUiThreadId == Thread.currentThread().getId()) {
1269 doRefreshProgress(id, progress, fromUser, true);
1271 if (mRefreshProgressRunnable == null) {
1272 mRefreshProgressRunnable = new RefreshProgressRunnable();
1275 final RefreshData rd = RefreshData.obtain(id, progress, fromUser);
1276 mRefreshData.add(rd);
1277 if (mAttached && !mRefreshIsPosted) {
1278 post(mRefreshProgressRunnable);
1279 mRefreshIsPosted = true;
1285 * <p>Set the current progress to the specified value. Does not do anything
1286 * if the progress bar is in indeterminate mode.</p>
1288 * @param progress the new progress, between 0 and {@link #getMax()}
1290 * @see #setIndeterminate(boolean)
1291 * @see #isIndeterminate()
1292 * @see #getProgress()
1293 * @see #incrementProgressBy(int)
1295 @android.view.RemotableViewMethod
1296 public synchronized void setProgress(int progress) {
1297 setProgress(progress, false);
1300 @android.view.RemotableViewMethod
1301 synchronized void setProgress(int progress, boolean fromUser) {
1302 if (mIndeterminate) {
1310 if (progress > mMax) {
1314 if (progress != mProgress) {
1315 mProgress = progress;
1316 refreshProgress(R.id.progress, mProgress, fromUser);
1322 * Set the current secondary progress to the specified value. Does not do
1323 * anything if the progress bar is in indeterminate mode.
1326 * @param secondaryProgress the new secondary progress, between 0 and {@link #getMax()}
1327 * @see #setIndeterminate(boolean)
1328 * @see #isIndeterminate()
1329 * @see #getSecondaryProgress()
1330 * @see #incrementSecondaryProgressBy(int)
1332 @android.view.RemotableViewMethod
1333 public synchronized void setSecondaryProgress(int secondaryProgress) {
1334 if (mIndeterminate) {
1338 if (secondaryProgress < 0) {
1339 secondaryProgress = 0;
1342 if (secondaryProgress > mMax) {
1343 secondaryProgress = mMax;
1346 if (secondaryProgress != mSecondaryProgress) {
1347 mSecondaryProgress = secondaryProgress;
1348 refreshProgress(R.id.secondaryProgress, mSecondaryProgress, false);
1353 * <p>Get the progress bar's current level of progress. Return 0 when the
1354 * progress bar is in indeterminate mode.</p>
1356 * @return the current progress, between 0 and {@link #getMax()}
1358 * @see #setIndeterminate(boolean)
1359 * @see #isIndeterminate()
1360 * @see #setProgress(int)
1364 @ViewDebug.ExportedProperty(category = "progress")
1365 public synchronized int getProgress() {
1366 return mIndeterminate ? 0 : mProgress;
1370 * <p>Get the progress bar's current level of secondary progress. Return 0 when the
1371 * progress bar is in indeterminate mode.</p>
1373 * @return the current secondary progress, between 0 and {@link #getMax()}
1375 * @see #setIndeterminate(boolean)
1376 * @see #isIndeterminate()
1377 * @see #setSecondaryProgress(int)
1381 @ViewDebug.ExportedProperty(category = "progress")
1382 public synchronized int getSecondaryProgress() {
1383 return mIndeterminate ? 0 : mSecondaryProgress;
1387 * <p>Return the upper limit of this progress bar's range.</p>
1389 * @return a positive integer
1392 * @see #getProgress()
1393 * @see #getSecondaryProgress()
1395 @ViewDebug.ExportedProperty(category = "progress")
1396 public synchronized int getMax() {
1401 * <p>Set the range of the progress bar to 0...<tt>max</tt>.</p>
1403 * @param max the upper range of this progress bar
1406 * @see #setProgress(int)
1407 * @see #setSecondaryProgress(int)
1409 @android.view.RemotableViewMethod
1410 public synchronized void setMax(int max) {
1418 if (mProgress > max) {
1421 refreshProgress(R.id.progress, mProgress, false);
1426 * <p>Increase the progress bar's progress by the specified amount.</p>
1428 * @param diff the amount by which the progress must be increased
1430 * @see #setProgress(int)
1432 public synchronized final void incrementProgressBy(int diff) {
1433 setProgress(mProgress + diff);
1437 * <p>Increase the progress bar's secondary progress by the specified amount.</p>
1439 * @param diff the amount by which the secondary progress must be increased
1441 * @see #setSecondaryProgress(int)
1443 public synchronized final void incrementSecondaryProgressBy(int diff) {
1444 setSecondaryProgress(mSecondaryProgress + diff);
1448 * <p>Start the indeterminate progress animation.</p>
1450 void startAnimation() {
1451 if (getVisibility() != VISIBLE) {
1455 if (mIndeterminateDrawable instanceof Animatable) {
1456 mShouldStartAnimationDrawable = true;
1457 mHasAnimation = false;
1459 mHasAnimation = true;
1461 if (mInterpolator == null) {
1462 mInterpolator = new LinearInterpolator();
1465 if (mTransformation == null) {
1466 mTransformation = new Transformation();
1468 mTransformation.clear();
1471 if (mAnimation == null) {
1472 mAnimation = new AlphaAnimation(0.0f, 1.0f);
1477 mAnimation.setRepeatMode(mBehavior);
1478 mAnimation.setRepeatCount(Animation.INFINITE);
1479 mAnimation.setDuration(mDuration);
1480 mAnimation.setInterpolator(mInterpolator);
1481 mAnimation.setStartTime(Animation.START_ON_FIRST_FRAME);
1487 * <p>Stop the indeterminate progress animation.</p>
1489 void stopAnimation() {
1490 mHasAnimation = false;
1491 if (mIndeterminateDrawable instanceof Animatable) {
1492 ((Animatable) mIndeterminateDrawable).stop();
1493 mShouldStartAnimationDrawable = false;
1499 * Sets the acceleration curve for the indeterminate animation.
1500 * The interpolator is loaded as a resource from the specified context.
1502 * @param context The application environment
1503 * @param resID The resource identifier of the interpolator to load
1505 public void setInterpolator(Context context, @InterpolatorRes int resID) {
1506 setInterpolator(AnimationUtils.loadInterpolator(context, resID));
1510 * Sets the acceleration curve for the indeterminate animation.
1511 * Defaults to a linear interpolation.
1513 * @param interpolator The interpolator which defines the acceleration curve
1515 public void setInterpolator(Interpolator interpolator) {
1516 mInterpolator = interpolator;
1520 * Gets the acceleration curve type for the indeterminate animation.
1522 * @return the {@link Interpolator} associated to this animation
1524 public Interpolator getInterpolator() {
1525 return mInterpolator;
1529 @RemotableViewMethod
1530 public void setVisibility(int v) {
1531 if (getVisibility() != v) {
1532 super.setVisibility(v);
1534 if (mIndeterminate) {
1535 // let's be nice with the UI thread
1536 if (v == GONE || v == INVISIBLE) {
1546 protected void onVisibilityChanged(View changedView, int visibility) {
1547 super.onVisibilityChanged(changedView, visibility);
1549 if (mIndeterminate) {
1550 // let's be nice with the UI thread
1551 if (visibility == GONE || visibility == INVISIBLE) {
1560 public void invalidateDrawable(Drawable dr) {
1562 if (verifyDrawable(dr)) {
1563 final Rect dirty = dr.getBounds();
1564 final int scrollX = mScrollX + mPaddingLeft;
1565 final int scrollY = mScrollY + mPaddingTop;
1567 invalidate(dirty.left + scrollX, dirty.top + scrollY,
1568 dirty.right + scrollX, dirty.bottom + scrollY);
1570 super.invalidateDrawable(dr);
1576 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
1577 updateDrawableBounds(w, h);
1580 private void updateDrawableBounds(int w, int h) {
1581 // onDraw will translate the canvas so we draw starting at 0,0.
1582 // Subtract out padding for the purposes of the calculations below.
1583 w -= mPaddingRight + mPaddingLeft;
1584 h -= mPaddingTop + mPaddingBottom;
1591 if (mIndeterminateDrawable != null) {
1592 // Aspect ratio logic does not apply to AnimationDrawables
1593 if (mOnlyIndeterminate && !(mIndeterminateDrawable instanceof AnimationDrawable)) {
1594 // Maintain aspect ratio. Certain kinds of animated drawables
1595 // get very confused otherwise.
1596 final int intrinsicWidth = mIndeterminateDrawable.getIntrinsicWidth();
1597 final int intrinsicHeight = mIndeterminateDrawable.getIntrinsicHeight();
1598 final float intrinsicAspect = (float) intrinsicWidth / intrinsicHeight;
1599 final float boundAspect = (float) w / h;
1600 if (intrinsicAspect != boundAspect) {
1601 if (boundAspect > intrinsicAspect) {
1602 // New width is larger. Make it smaller to match height.
1603 final int width = (int) (h * intrinsicAspect);
1604 left = (w - width) / 2;
1605 right = left + width;
1607 // New height is larger. Make it smaller to match width.
1608 final int height = (int) (w * (1 / intrinsicAspect));
1609 top = (h - height) / 2;
1610 bottom = top + height;
1614 if (isLayoutRtl() && mMirrorForRtl) {
1615 int tempLeft = left;
1617 right = w - tempLeft;
1619 mIndeterminateDrawable.setBounds(left, top, right, bottom);
1622 if (mProgressDrawable != null) {
1623 mProgressDrawable.setBounds(0, 0, right, bottom);
1628 protected synchronized void onDraw(Canvas canvas) {
1629 super.onDraw(canvas);
1635 * Draws the progress bar track.
1637 void drawTrack(Canvas canvas) {
1638 final Drawable d = mCurrentDrawable;
1640 // Translate canvas so a indeterminate circular progress bar with padding
1641 // rotates properly in its animation
1642 final int saveCount = canvas.save();
1644 if (isLayoutRtl() && mMirrorForRtl) {
1645 canvas.translate(getWidth() - mPaddingRight, mPaddingTop);
1646 canvas.scale(-1.0f, 1.0f);
1648 canvas.translate(mPaddingLeft, mPaddingTop);
1651 final long time = getDrawingTime();
1652 if (mHasAnimation) {
1653 mAnimation.getTransformation(time, mTransformation);
1654 final float scale = mTransformation.getAlpha();
1657 d.setLevel((int) (scale * MAX_LEVEL));
1661 postInvalidateOnAnimation();
1665 canvas.restoreToCount(saveCount);
1667 if (mShouldStartAnimationDrawable && d instanceof Animatable) {
1668 ((Animatable) d).start();
1669 mShouldStartAnimationDrawable = false;
1675 protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
1679 final Drawable d = mCurrentDrawable;
1681 dw = Math.max(mMinWidth, Math.min(mMaxWidth, d.getIntrinsicWidth()));
1682 dh = Math.max(mMinHeight, Math.min(mMaxHeight, d.getIntrinsicHeight()));
1685 updateDrawableState();
1687 dw += mPaddingLeft + mPaddingRight;
1688 dh += mPaddingTop + mPaddingBottom;
1690 final int measuredWidth = resolveSizeAndState(dw, widthMeasureSpec, 0);
1691 final int measuredHeight = resolveSizeAndState(dh, heightMeasureSpec, 0);
1692 setMeasuredDimension(measuredWidth, measuredHeight);
1696 protected void drawableStateChanged() {
1697 super.drawableStateChanged();
1698 updateDrawableState();
1701 private void updateDrawableState() {
1702 final int[] state = getDrawableState();
1704 if (mProgressDrawable != null && mProgressDrawable.isStateful()) {
1705 mProgressDrawable.setState(state);
1708 if (mIndeterminateDrawable != null && mIndeterminateDrawable.isStateful()) {
1709 mIndeterminateDrawable.setState(state);
1714 public void drawableHotspotChanged(float x, float y) {
1715 super.drawableHotspotChanged(x, y);
1717 if (mProgressDrawable != null) {
1718 mProgressDrawable.setHotspot(x, y);
1721 if (mIndeterminateDrawable != null) {
1722 mIndeterminateDrawable.setHotspot(x, y);
1726 static class SavedState extends BaseSavedState {
1728 int secondaryProgress;
1731 * Constructor called from {@link ProgressBar#onSaveInstanceState()}
1733 SavedState(Parcelable superState) {
1738 * Constructor called from {@link #CREATOR}
1740 private SavedState(Parcel in) {
1742 progress = in.readInt();
1743 secondaryProgress = in.readInt();
1747 public void writeToParcel(Parcel out, int flags) {
1748 super.writeToParcel(out, flags);
1749 out.writeInt(progress);
1750 out.writeInt(secondaryProgress);
1753 public static final Parcelable.Creator<SavedState> CREATOR
1754 = new Parcelable.Creator<SavedState>() {
1755 public SavedState createFromParcel(Parcel in) {
1756 return new SavedState(in);
1759 public SavedState[] newArray(int size) {
1760 return new SavedState[size];
1766 public Parcelable onSaveInstanceState() {
1767 // Force our ancestor class to save its state
1768 Parcelable superState = super.onSaveInstanceState();
1769 SavedState ss = new SavedState(superState);
1771 ss.progress = mProgress;
1772 ss.secondaryProgress = mSecondaryProgress;
1778 public void onRestoreInstanceState(Parcelable state) {
1779 SavedState ss = (SavedState) state;
1780 super.onRestoreInstanceState(ss.getSuperState());
1782 setProgress(ss.progress);
1783 setSecondaryProgress(ss.secondaryProgress);
1787 protected void onAttachedToWindow() {
1788 super.onAttachedToWindow();
1789 if (mIndeterminate) {
1792 if (mRefreshData != null) {
1793 synchronized (this) {
1794 final int count = mRefreshData.size();
1795 for (int i = 0; i < count; i++) {
1796 final RefreshData rd = mRefreshData.get(i);
1797 doRefreshProgress(rd.id, rd.progress, rd.fromUser, true);
1800 mRefreshData.clear();
1807 protected void onDetachedFromWindow() {
1808 if (mIndeterminate) {
1811 if (mRefreshProgressRunnable != null) {
1812 removeCallbacks(mRefreshProgressRunnable);
1814 if (mRefreshProgressRunnable != null && mRefreshIsPosted) {
1815 removeCallbacks(mRefreshProgressRunnable);
1817 if (mAccessibilityEventSender != null) {
1818 removeCallbacks(mAccessibilityEventSender);
1820 // This should come after stopAnimation(), otherwise an invalidate message remains in the
1821 // queue, which can prevent the entire view hierarchy from being GC'ed during a rotation
1822 super.onDetachedFromWindow();
1827 public CharSequence getAccessibilityClassName() {
1828 return ProgressBar.class.getName();
1833 public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
1834 super.onInitializeAccessibilityEventInternal(event);
1835 event.setItemCount(mMax);
1836 event.setCurrentItemIndex(mProgress);
1840 * Schedule a command for sending an accessibility event.
1842 * Note: A command is used to ensure that accessibility events
1843 * are sent at most one in a given time frame to save
1844 * system resources while the progress changes quickly.
1846 private void scheduleAccessibilityEventSender() {
1847 if (mAccessibilityEventSender == null) {
1848 mAccessibilityEventSender = new AccessibilityEventSender();
1850 removeCallbacks(mAccessibilityEventSender);
1852 postDelayed(mAccessibilityEventSender, TIMEOUT_SEND_ACCESSIBILITY_EVENT);
1856 * Command for sending an accessibility event.
1858 private class AccessibilityEventSender implements Runnable {
1860 sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
1864 private static class ProgressTintInfo {
1865 ColorStateList mIndeterminateTintList;
1866 PorterDuff.Mode mIndeterminateTintMode;
1867 boolean mHasIndeterminateTint;
1868 boolean mHasIndeterminateTintMode;
1870 ColorStateList mProgressTintList;
1871 PorterDuff.Mode mProgressTintMode;
1872 boolean mHasProgressTint;
1873 boolean mHasProgressTintMode;
1875 ColorStateList mProgressBackgroundTintList;
1876 PorterDuff.Mode mProgressBackgroundTintMode;
1877 boolean mHasProgressBackgroundTint;
1878 boolean mHasProgressBackgroundTintMode;
1880 ColorStateList mSecondaryProgressTintList;
1881 PorterDuff.Mode mSecondaryProgressTintMode;
1882 boolean mHasSecondaryProgressTint;
1883 boolean mHasSecondaryProgressTintMode;