blob: af9e2f0bc853e04b92df48f330ec9a23e9e0245c [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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
17package android.widget;
18
Svetoslav Ganov6518ad72011-03-18 16:19:55 -070019import com.android.internal.R;
20
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080021import android.content.Context;
22import android.content.res.TypedArray;
23import android.graphics.Bitmap;
24import android.graphics.BitmapShader;
25import android.graphics.Canvas;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080026import android.graphics.Rect;
Steve Howardb25ffff2010-08-20 17:39:26 -070027import android.graphics.Shader;
28import android.graphics.drawable.Animatable;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080029import android.graphics.drawable.AnimationDrawable;
30import android.graphics.drawable.BitmapDrawable;
31import android.graphics.drawable.ClipDrawable;
32import android.graphics.drawable.Drawable;
33import android.graphics.drawable.LayerDrawable;
34import android.graphics.drawable.ShapeDrawable;
35import android.graphics.drawable.StateListDrawable;
36import android.graphics.drawable.shapes.RoundRectShape;
37import android.graphics.drawable.shapes.Shape;
Steve Howardb25ffff2010-08-20 17:39:26 -070038import android.os.Parcel;
39import android.os.Parcelable;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040import android.util.AttributeSet;
Svetoslav Ganovabae2a12012-11-27 16:59:37 -080041import android.util.Pools.SynchronizedPool;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042import android.view.Gravity;
Steve Howardb25ffff2010-08-20 17:39:26 -070043import android.view.RemotableViewMethod;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044import android.view.View;
Steve Zeigler7a367882010-02-23 16:39:08 -080045import android.view.ViewDebug;
Svetoslav Ganov6518ad72011-03-18 16:19:55 -070046import android.view.accessibility.AccessibilityEvent;
47import android.view.accessibility.AccessibilityManager;
Svetoslav Ganov8a78fd42012-01-17 14:36:46 -080048import android.view.accessibility.AccessibilityNodeInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049import android.view.animation.AlphaAnimation;
50import android.view.animation.Animation;
51import android.view.animation.AnimationUtils;
52import android.view.animation.Interpolator;
53import android.view.animation.LinearInterpolator;
54import android.view.animation.Transformation;
55import android.widget.RemoteViews.RemoteView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056
Adam Powella0506632012-04-10 17:19:20 -070057import java.util.ArrayList;
58
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080059
60/**
61 * <p>
62 * Visual indicator of progress in some operation. Displays a bar to the user
63 * representing how far the operation has progressed; the application can
64 * change the amount of progress (modifying the length of the bar) as it moves
65 * forward. There is also a secondary progress displayable on a progress bar
66 * which is useful for displaying intermediate progress, such as the buffer
67 * level during a streaming playback progress bar.
68 * </p>
69 *
70 * <p>
71 * A progress bar can also be made indeterminate. In indeterminate mode, the
Scott Main42f139c2011-04-29 10:31:10 -070072 * progress bar shows a cyclic animation without an indication of progress. This mode is used by
73 * applications when the length of the task is unknown. The indeterminate progress bar can be either
74 * a spinning wheel or a horizontal bar.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075 * </p>
76 *
77 * <p>The following code example shows how a progress bar can be used from
78 * a worker thread to update the user interface to notify the user of progress:
79 * </p>
80 *
Scott Main42f139c2011-04-29 10:31:10 -070081 * <pre>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080082 * public class MyActivity extends Activity {
83 * private static final int PROGRESS = 0x1;
84 *
85 * private ProgressBar mProgress;
86 * private int mProgressStatus = 0;
87 *
88 * private Handler mHandler = new Handler();
89 *
90 * protected void onCreate(Bundle icicle) {
91 * super.onCreate(icicle);
92 *
93 * setContentView(R.layout.progressbar_activity);
94 *
95 * mProgress = (ProgressBar) findViewById(R.id.progress_bar);
96 *
97 * // Start lengthy operation in a background thread
98 * new Thread(new Runnable() {
99 * public void run() {
Scott Main42f139c2011-04-29 10:31:10 -0700100 * while (mProgressStatus &lt; 100) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800101 * mProgressStatus = doWork();
102 *
103 * // Update the progress bar
104 * mHandler.post(new Runnable() {
105 * public void run() {
106 * mProgress.setProgress(mProgressStatus);
107 * }
108 * });
109 * }
110 * }
111 * }).start();
112 * }
Scott Main42f139c2011-04-29 10:31:10 -0700113 * }</pre>
114 *
115 * <p>To add a progress bar to a layout file, you can use the {@code &lt;ProgressBar&gt;} element.
116 * By default, the progress bar is a spinning wheel (an indeterminate indicator). To change to a
117 * horizontal progress bar, apply the {@link android.R.style#Widget_ProgressBar_Horizontal
118 * Widget.ProgressBar.Horizontal} style, like so:</p>
119 *
120 * <pre>
121 * &lt;ProgressBar
122 * style="@android:style/Widget.ProgressBar.Horizontal"
123 * ... /&gt;</pre>
124 *
125 * <p>If you will use the progress bar to show real progress, you must use the horizontal bar. You
126 * can then increment the progress with {@link #incrementProgressBy incrementProgressBy()} or
127 * {@link #setProgress setProgress()}. By default, the progress bar is full when it reaches 100. If
128 * necessary, you can adjust the maximum value (the value for a full bar) using the {@link
129 * android.R.styleable#ProgressBar_max android:max} attribute. Other attributes available are listed
130 * below.</p>
131 *
132 * <p>Another common style to apply to the progress bar is {@link
133 * android.R.style#Widget_ProgressBar_Small Widget.ProgressBar.Small}, which shows a smaller
134 * version of the spinning wheel&mdash;useful when waiting for content to load.
135 * For example, you can insert this kind of progress bar into your default layout for
136 * a view that will be populated by some content fetched from the Internet&mdash;the spinning wheel
137 * appears immediately and when your application receives the content, it replaces the progress bar
138 * with the loaded content. For example:</p>
139 *
140 * <pre>
141 * &lt;LinearLayout
142 * android:orientation="horizontal"
143 * ... &gt;
144 * &lt;ProgressBar
145 * android:layout_width="wrap_content"
146 * android:layout_height="wrap_content"
147 * style="@android:style/Widget.ProgressBar.Small"
148 * android:layout_marginRight="5dp" /&gt;
149 * &lt;TextView
150 * android:layout_width="wrap_content"
151 * android:layout_height="wrap_content"
152 * android:text="@string/loading" /&gt;
153 * &lt;/LinearLayout&gt;</pre>
154 *
155 * <p>Other progress bar styles provided by the system include:</p>
156 * <ul>
157 * <li>{@link android.R.style#Widget_ProgressBar_Horizontal Widget.ProgressBar.Horizontal}</li>
158 * <li>{@link android.R.style#Widget_ProgressBar_Small Widget.ProgressBar.Small}</li>
159 * <li>{@link android.R.style#Widget_ProgressBar_Large Widget.ProgressBar.Large}</li>
160 * <li>{@link android.R.style#Widget_ProgressBar_Inverse Widget.ProgressBar.Inverse}</li>
161 * <li>{@link android.R.style#Widget_ProgressBar_Small_Inverse
162 * Widget.ProgressBar.Small.Inverse}</li>
163 * <li>{@link android.R.style#Widget_ProgressBar_Large_Inverse
164 * Widget.ProgressBar.Large.Inverse}</li>
165 * </ul>
166 * <p>The "inverse" styles provide an inverse color scheme for the spinner, which may be necessary
167 * if your application uses a light colored theme (a white background).</p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800168 *
169 * <p><strong>XML attributes</b></strong>
170 * <p>
171 * See {@link android.R.styleable#ProgressBar ProgressBar Attributes},
172 * {@link android.R.styleable#View View Attributes}
173 * </p>
174 *
Scott Main42f139c2011-04-29 10:31:10 -0700175 * @attr ref android.R.styleable#ProgressBar_animationResolution
176 * @attr ref android.R.styleable#ProgressBar_indeterminate
177 * @attr ref android.R.styleable#ProgressBar_indeterminateBehavior
178 * @attr ref android.R.styleable#ProgressBar_indeterminateDrawable
179 * @attr ref android.R.styleable#ProgressBar_indeterminateDuration
180 * @attr ref android.R.styleable#ProgressBar_indeterminateOnly
181 * @attr ref android.R.styleable#ProgressBar_interpolator
182 * @attr ref android.R.styleable#ProgressBar_max
183 * @attr ref android.R.styleable#ProgressBar_maxHeight
184 * @attr ref android.R.styleable#ProgressBar_maxWidth
185 * @attr ref android.R.styleable#ProgressBar_minHeight
186 * @attr ref android.R.styleable#ProgressBar_minWidth
Scott Mainb40c1fd2013-04-23 13:30:06 -0700187 * @attr ref android.R.styleable#ProgressBar_mirrorForRtl
Scott Main42f139c2011-04-29 10:31:10 -0700188 * @attr ref android.R.styleable#ProgressBar_progress
189 * @attr ref android.R.styleable#ProgressBar_progressDrawable
190 * @attr ref android.R.styleable#ProgressBar_secondaryProgress
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800191 */
192@RemoteView
193public class ProgressBar extends View {
194 private static final int MAX_LEVEL = 10000;
Svetoslav Ganov6518ad72011-03-18 16:19:55 -0700195 private static final int TIMEOUT_SEND_ACCESSIBILITY_EVENT = 200;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800196
197 int mMinWidth;
198 int mMaxWidth;
199 int mMinHeight;
200 int mMaxHeight;
201
202 private int mProgress;
203 private int mSecondaryProgress;
204 private int mMax;
205
206 private int mBehavior;
207 private int mDuration;
208 private boolean mIndeterminate;
209 private boolean mOnlyIndeterminate;
210 private Transformation mTransformation;
211 private AlphaAnimation mAnimation;
Romain Guyab4c4f4f2012-05-06 13:11:24 -0700212 private boolean mHasAnimation;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800213 private Drawable mIndeterminateDrawable;
214 private Drawable mProgressDrawable;
215 private Drawable mCurrentDrawable;
216 Bitmap mSampleTile;
217 private boolean mNoInvalidate;
218 private Interpolator mInterpolator;
219 private RefreshProgressRunnable mRefreshProgressRunnable;
220 private long mUiThreadId;
221 private boolean mShouldStartAnimationDrawable;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800222
223 private boolean mInDrawing;
Adam Powella0506632012-04-10 17:19:20 -0700224 private boolean mAttached;
225 private boolean mRefreshIsPosted;
226
Fabrice Di Meglio2b378cd2013-01-30 16:39:33 -0800227 boolean mMirrorForRtl = false;
228
Adam Powella0506632012-04-10 17:19:20 -0700229 private final ArrayList<RefreshData> mRefreshData = new ArrayList<RefreshData>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800230
Svetoslav Ganov6518ad72011-03-18 16:19:55 -0700231 private AccessibilityEventSender mAccessibilityEventSender;
232
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800233 /**
234 * Create a new progress bar with range 0...100 and initial progress of 0.
235 * @param context the application environment
236 */
237 public ProgressBar(Context context) {
238 this(context, null);
239 }
240
241 public ProgressBar(Context context, AttributeSet attrs) {
242 this(context, attrs, com.android.internal.R.attr.progressBarStyle);
243 }
244
Alan Viverette617feb92013-09-09 18:09:13 -0700245 public ProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
246 this(context, attrs, defStyleAttr, 0);
Adam Powell6af97e12010-11-11 21:11:53 -0800247 }
248
Alan Viverette617feb92013-09-09 18:09:13 -0700249 public ProgressBar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
250 super(context, attrs, defStyleAttr, defStyleRes);
251
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800252 mUiThreadId = Thread.currentThread().getId();
253 initProgressBar();
254
Alan Viverette617feb92013-09-09 18:09:13 -0700255 final TypedArray a = context.obtainStyledAttributes(
256 attrs, R.styleable.ProgressBar, defStyleAttr, defStyleRes);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800257
258 mNoInvalidate = true;
259
260 Drawable drawable = a.getDrawable(R.styleable.ProgressBar_progressDrawable);
261 if (drawable != null) {
Romain Guy8d21bdb2009-12-30 13:54:04 -0800262 // Calling this method can set mMaxHeight, make sure the corresponding
263 // XML attribute for mMaxHeight is read after calling this method
Alan Viverettee785d022013-09-26 15:21:10 -0700264 setProgressDrawableTiled(drawable);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800265 }
266
267
268 mDuration = a.getInt(R.styleable.ProgressBar_indeterminateDuration, mDuration);
269
270 mMinWidth = a.getDimensionPixelSize(R.styleable.ProgressBar_minWidth, mMinWidth);
271 mMaxWidth = a.getDimensionPixelSize(R.styleable.ProgressBar_maxWidth, mMaxWidth);
272 mMinHeight = a.getDimensionPixelSize(R.styleable.ProgressBar_minHeight, mMinHeight);
273 mMaxHeight = a.getDimensionPixelSize(R.styleable.ProgressBar_maxHeight, mMaxHeight);
274
275 mBehavior = a.getInt(R.styleable.ProgressBar_indeterminateBehavior, mBehavior);
276
Jean-Baptiste Queru72b1f372009-08-31 09:17:57 -0700277 final int resID = a.getResourceId(
278 com.android.internal.R.styleable.ProgressBar_interpolator,
279 android.R.anim.linear_interpolator); // default to linear interpolator
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800280 if (resID > 0) {
281 setInterpolator(context, resID);
Jean-Baptiste Queru72b1f372009-08-31 09:17:57 -0700282 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800283
284 setMax(a.getInt(R.styleable.ProgressBar_max, mMax));
285
286 setProgress(a.getInt(R.styleable.ProgressBar_progress, mProgress));
287
288 setSecondaryProgress(
289 a.getInt(R.styleable.ProgressBar_secondaryProgress, mSecondaryProgress));
290
291 drawable = a.getDrawable(R.styleable.ProgressBar_indeterminateDrawable);
292 if (drawable != null) {
Alan Viverettee785d022013-09-26 15:21:10 -0700293 setIndeterminateDrawableTiled(drawable);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800294 }
295
296 mOnlyIndeterminate = a.getBoolean(
297 R.styleable.ProgressBar_indeterminateOnly, mOnlyIndeterminate);
298
299 mNoInvalidate = false;
300
301 setIndeterminate(mOnlyIndeterminate || a.getBoolean(
302 R.styleable.ProgressBar_indeterminate, mIndeterminate));
303
Fabrice Di Meglio2b378cd2013-01-30 16:39:33 -0800304 mMirrorForRtl = a.getBoolean(R.styleable.ProgressBar_mirrorForRtl, mMirrorForRtl);
305
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800306 a.recycle();
Svetoslav7face752014-01-13 15:25:58 -0800307
308 // If not explicitly specified this view is important for accessibility.
309 if (getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
310 setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
311 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800312 }
313
314 /**
315 * Converts a drawable to a tiled version of itself. It will recursively
316 * traverse layer and state list drawables.
317 */
318 private Drawable tileify(Drawable drawable, boolean clip) {
319
320 if (drawable instanceof LayerDrawable) {
321 LayerDrawable background = (LayerDrawable) drawable;
322 final int N = background.getNumberOfLayers();
323 Drawable[] outDrawables = new Drawable[N];
324
325 for (int i = 0; i < N; i++) {
326 int id = background.getId(i);
327 outDrawables[i] = tileify(background.getDrawable(i),
328 (id == R.id.progress || id == R.id.secondaryProgress));
329 }
330
331 LayerDrawable newBg = new LayerDrawable(outDrawables);
332
333 for (int i = 0; i < N; i++) {
334 newBg.setId(i, background.getId(i));
335 }
336
337 return newBg;
338
339 } else if (drawable instanceof StateListDrawable) {
340 StateListDrawable in = (StateListDrawable) drawable;
341 StateListDrawable out = new StateListDrawable();
342 int numStates = in.getStateCount();
343 for (int i = 0; i < numStates; i++) {
344 out.addState(in.getStateSet(i), tileify(in.getStateDrawable(i), clip));
345 }
346 return out;
347
348 } else if (drawable instanceof BitmapDrawable) {
349 final Bitmap tileBitmap = ((BitmapDrawable) drawable).getBitmap();
350 if (mSampleTile == null) {
351 mSampleTile = tileBitmap;
352 }
353
354 final ShapeDrawable shapeDrawable = new ShapeDrawable(getDrawableShape());
355
356 final BitmapShader bitmapShader = new BitmapShader(tileBitmap,
357 Shader.TileMode.REPEAT, Shader.TileMode.CLAMP);
358 shapeDrawable.getPaint().setShader(bitmapShader);
359
Fabrice Di Meglio7fb98b32012-09-12 20:04:04 -0700360 return (clip) ? new ClipDrawable(shapeDrawable, Gravity.LEFT,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800361 ClipDrawable.HORIZONTAL) : shapeDrawable;
362 }
363
364 return drawable;
365 }
366
367 Shape getDrawableShape() {
368 final float[] roundedCorners = new float[] { 5, 5, 5, 5, 5, 5, 5, 5 };
369 return new RoundRectShape(roundedCorners, null, null);
370 }
371
372 /**
373 * Convert a AnimationDrawable for use as a barberpole animation.
374 * Each frame of the animation is wrapped in a ClipDrawable and
375 * given a tiling BitmapShader.
376 */
377 private Drawable tileifyIndeterminate(Drawable drawable) {
378 if (drawable instanceof AnimationDrawable) {
379 AnimationDrawable background = (AnimationDrawable) drawable;
380 final int N = background.getNumberOfFrames();
381 AnimationDrawable newBg = new AnimationDrawable();
382 newBg.setOneShot(background.isOneShot());
383
384 for (int i = 0; i < N; i++) {
385 Drawable frame = tileify(background.getFrame(i), true);
386 frame.setLevel(10000);
387 newBg.addFrame(frame, background.getDuration(i));
388 }
389 newBg.setLevel(10000);
390 drawable = newBg;
391 }
392 return drawable;
393 }
394
395 /**
396 * <p>
397 * Initialize the progress bar's default values:
398 * </p>
399 * <ul>
400 * <li>progress = 0</li>
401 * <li>max = 100</li>
402 * <li>animation duration = 4000 ms</li>
403 * <li>indeterminate = false</li>
404 * <li>behavior = repeat</li>
405 * </ul>
406 */
407 private void initProgressBar() {
408 mMax = 100;
409 mProgress = 0;
410 mSecondaryProgress = 0;
411 mIndeterminate = false;
412 mOnlyIndeterminate = false;
413 mDuration = 4000;
414 mBehavior = AlphaAnimation.RESTART;
415 mMinWidth = 24;
416 mMaxWidth = 48;
417 mMinHeight = 24;
418 mMaxHeight = 48;
419 }
420
421 /**
422 * <p>Indicate whether this progress bar is in indeterminate mode.</p>
423 *
424 * @return true if the progress bar is in indeterminate mode
425 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -0700426 @ViewDebug.ExportedProperty(category = "progress")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800427 public synchronized boolean isIndeterminate() {
428 return mIndeterminate;
429 }
430
431 /**
432 * <p>Change the indeterminate mode for this progress bar. In indeterminate
433 * mode, the progress is ignored and the progress bar shows an infinite
434 * animation instead.</p>
435 *
436 * If this progress bar's style only supports indeterminate mode (such as the circular
437 * progress bars), then this will be ignored.
438 *
439 * @param indeterminate true to enable the indeterminate mode
440 */
441 @android.view.RemotableViewMethod
442 public synchronized void setIndeterminate(boolean indeterminate) {
443 if ((!mOnlyIndeterminate || !mIndeterminate) && indeterminate != mIndeterminate) {
444 mIndeterminate = indeterminate;
445
446 if (indeterminate) {
447 // swap between indeterminate and regular backgrounds
448 mCurrentDrawable = mIndeterminateDrawable;
449 startAnimation();
450 } else {
451 mCurrentDrawable = mProgressDrawable;
452 stopAnimation();
453 }
454 }
455 }
456
457 /**
458 * <p>Get the drawable used to draw the progress bar in
459 * indeterminate mode.</p>
460 *
461 * @return a {@link android.graphics.drawable.Drawable} instance
462 *
463 * @see #setIndeterminateDrawable(android.graphics.drawable.Drawable)
464 * @see #setIndeterminate(boolean)
465 */
466 public Drawable getIndeterminateDrawable() {
467 return mIndeterminateDrawable;
468 }
469
470 /**
Alan Viverettee785d022013-09-26 15:21:10 -0700471 * Define the drawable used to draw the progress bar in indeterminate mode.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800472 *
473 * @param d the new drawable
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800474 * @see #getIndeterminateDrawable()
475 * @see #setIndeterminate(boolean)
476 */
477 public void setIndeterminateDrawable(Drawable d) {
478 if (d != null) {
479 d.setCallback(this);
480 }
481 mIndeterminateDrawable = d;
Fabrice Di Meglio0af4b8b2012-06-11 18:30:05 -0700482 if (mIndeterminateDrawable != null && canResolveLayoutDirection()) {
Fabrice Di Meglioe56ffdc2012-09-23 14:51:16 -0700483 mIndeterminateDrawable.setLayoutDirection(getLayoutDirection());
Fabrice Di Megliob03b4342012-06-04 12:55:30 -0700484 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800485 if (mIndeterminate) {
486 mCurrentDrawable = d;
487 postInvalidate();
488 }
489 }
Alan Viverettee785d022013-09-26 15:21:10 -0700490
491 /**
492 * Define the tileable drawable used to draw the progress bar in
493 * indeterminate mode.
494 * <p>
495 * If the drawable is a BitmapDrawable or contains BitmapDrawables, a
496 * tiled copy will be generated for display as a progress bar.
497 *
498 * @param d the new drawable
499 * @see #getIndeterminateDrawable()
500 * @see #setIndeterminate(boolean)
501 */
502 public void setIndeterminateDrawableTiled(Drawable d) {
503 if (d != null) {
504 d = tileifyIndeterminate(d);
505 }
506
507 setIndeterminateDrawable(d);
508 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800509
510 /**
511 * <p>Get the drawable used to draw the progress bar in
512 * progress mode.</p>
513 *
514 * @return a {@link android.graphics.drawable.Drawable} instance
515 *
516 * @see #setProgressDrawable(android.graphics.drawable.Drawable)
517 * @see #setIndeterminate(boolean)
518 */
519 public Drawable getProgressDrawable() {
520 return mProgressDrawable;
521 }
522
523 /**
Alan Viverettee785d022013-09-26 15:21:10 -0700524 * Define the drawable used to draw the progress bar in progress mode.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800525 *
526 * @param d the new drawable
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800527 * @see #getProgressDrawable()
528 * @see #setIndeterminate(boolean)
529 */
530 public void setProgressDrawable(Drawable d) {
Joe Onoratoaa072632010-12-08 15:31:28 -0800531 boolean needUpdate;
532 if (mProgressDrawable != null && d != mProgressDrawable) {
533 mProgressDrawable.setCallback(null);
534 needUpdate = true;
535 } else {
536 needUpdate = false;
537 }
538
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800539 if (d != null) {
540 d.setCallback(this);
Fabrice Di Meglio0af4b8b2012-06-11 18:30:05 -0700541 if (canResolveLayoutDirection()) {
Fabrice Di Meglioe56ffdc2012-09-23 14:51:16 -0700542 d.setLayoutDirection(getLayoutDirection());
Fabrice Di Meglio0af4b8b2012-06-11 18:30:05 -0700543 }
NoraBoraa7f7e2a2009-12-25 19:51:34 -0500544
Romain Guy8d21bdb2009-12-30 13:54:04 -0800545 // Make sure the ProgressBar is always tall enough
546 int drawableHeight = d.getMinimumHeight();
NoraBoraa7f7e2a2009-12-25 19:51:34 -0500547 if (mMaxHeight < drawableHeight) {
548 mMaxHeight = drawableHeight;
549 requestLayout();
550 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800551 }
552 mProgressDrawable = d;
553 if (!mIndeterminate) {
554 mCurrentDrawable = d;
555 postInvalidate();
556 }
Joe Onoratoaa072632010-12-08 15:31:28 -0800557
558 if (needUpdate) {
559 updateDrawableBounds(getWidth(), getHeight());
560 updateDrawableState();
561 doRefreshProgress(R.id.progress, mProgress, false, false);
562 doRefreshProgress(R.id.secondaryProgress, mSecondaryProgress, false, false);
563 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800564 }
Alan Viverettee785d022013-09-26 15:21:10 -0700565
566 /**
567 * Define the tileable drawable used to draw the progress bar in
568 * progress mode.
569 * <p>
570 * If the drawable is a BitmapDrawable or contains BitmapDrawables, a
571 * tiled copy will be generated for display as a progress bar.
572 *
573 * @param d the new drawable
574 * @see #getProgressDrawable()
575 * @see #setIndeterminate(boolean)
576 */
577 public void setProgressDrawableTiled(Drawable d) {
578 if (d != null) {
579 d = tileify(d, false);
580 }
581
582 setProgressDrawable(d);
583 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800584
585 /**
586 * @return The drawable currently used to draw the progress bar
587 */
588 Drawable getCurrentDrawable() {
589 return mCurrentDrawable;
590 }
591
592 @Override
593 protected boolean verifyDrawable(Drawable who) {
594 return who == mProgressDrawable || who == mIndeterminateDrawable
595 || super.verifyDrawable(who);
596 }
597
598 @Override
Dianne Hackborne2136772010-11-04 15:08:59 -0700599 public void jumpDrawablesToCurrentState() {
600 super.jumpDrawablesToCurrentState();
601 if (mProgressDrawable != null) mProgressDrawable.jumpToCurrentState();
602 if (mIndeterminateDrawable != null) mIndeterminateDrawable.jumpToCurrentState();
603 }
604
Fabrice Di Meglio4457e852012-09-18 19:23:12 -0700605 /**
606 * @hide
607 */
Dianne Hackborne2136772010-11-04 15:08:59 -0700608 @Override
Fabrice Di Meglio0af4b8b2012-06-11 18:30:05 -0700609 public void onResolveDrawables(int layoutDirection) {
610 final Drawable d = mCurrentDrawable;
611 if (d != null) {
612 d.setLayoutDirection(layoutDirection);
613 }
614 if (mIndeterminateDrawable != null) {
615 mIndeterminateDrawable.setLayoutDirection(layoutDirection);
616 }
617 if (mProgressDrawable != null) {
618 mProgressDrawable.setLayoutDirection(layoutDirection);
619 }
620 }
621
622 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800623 public void postInvalidate() {
624 if (!mNoInvalidate) {
625 super.postInvalidate();
626 }
627 }
628
629 private class RefreshProgressRunnable implements Runnable {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800630 public void run() {
Adam Powella0506632012-04-10 17:19:20 -0700631 synchronized (ProgressBar.this) {
632 final int count = mRefreshData.size();
633 for (int i = 0; i < count; i++) {
634 final RefreshData rd = mRefreshData.get(i);
635 doRefreshProgress(rd.id, rd.progress, rd.fromUser, true);
636 rd.recycle();
637 }
638 mRefreshData.clear();
639 mRefreshIsPosted = false;
640 }
641 }
642 }
643
Svetoslav Ganovabae2a12012-11-27 16:59:37 -0800644 private static class RefreshData {
645 private static final int POOL_MAX = 24;
646 private static final SynchronizedPool<RefreshData> sPool =
647 new SynchronizedPool<RefreshData>(POOL_MAX);
648
Adam Powella0506632012-04-10 17:19:20 -0700649 public int id;
650 public int progress;
651 public boolean fromUser;
Adam Powella0506632012-04-10 17:19:20 -0700652
653 public static RefreshData obtain(int id, int progress, boolean fromUser) {
654 RefreshData rd = sPool.acquire();
Svetoslav Ganovabae2a12012-11-27 16:59:37 -0800655 if (rd == null) {
656 rd = new RefreshData();
657 }
Adam Powella0506632012-04-10 17:19:20 -0700658 rd.id = id;
659 rd.progress = progress;
660 rd.fromUser = fromUser;
661 return rd;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800662 }
663
Adam Powella0506632012-04-10 17:19:20 -0700664 public void recycle() {
665 sPool.release(this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800666 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800667 }
Svetoslav Ganovabae2a12012-11-27 16:59:37 -0800668
Joe Onoratoaa072632010-12-08 15:31:28 -0800669 private synchronized void doRefreshProgress(int id, int progress, boolean fromUser,
670 boolean callBackToApp) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800671 float scale = mMax > 0 ? (float) progress / (float) mMax : 0;
672 final Drawable d = mCurrentDrawable;
673 if (d != null) {
674 Drawable progressDrawable = null;
675
676 if (d instanceof LayerDrawable) {
677 progressDrawable = ((LayerDrawable) d).findDrawableByLayerId(id);
Fabrice Di Meglio0af4b8b2012-06-11 18:30:05 -0700678 if (progressDrawable != null && canResolveLayoutDirection()) {
Fabrice Di Meglioe56ffdc2012-09-23 14:51:16 -0700679 progressDrawable.setLayoutDirection(getLayoutDirection());
Fabrice Di Meglio0af4b8b2012-06-11 18:30:05 -0700680 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800681 }
682
683 final int level = (int) (scale * MAX_LEVEL);
684 (progressDrawable != null ? progressDrawable : d).setLevel(level);
685 } else {
686 invalidate();
687 }
688
Joe Onoratoaa072632010-12-08 15:31:28 -0800689 if (callBackToApp && id == R.id.progress) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800690 onProgressRefresh(scale, fromUser);
691 }
692 }
Svetoslav Ganov6518ad72011-03-18 16:19:55 -0700693
694 void onProgressRefresh(float scale, boolean fromUser) {
695 if (AccessibilityManager.getInstance(mContext).isEnabled()) {
696 scheduleAccessibilityEventSender();
697 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800698 }
699
700 private synchronized void refreshProgress(int id, int progress, boolean fromUser) {
701 if (mUiThreadId == Thread.currentThread().getId()) {
Joe Onoratoaa072632010-12-08 15:31:28 -0800702 doRefreshProgress(id, progress, fromUser, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800703 } else {
Romain Guyab4c4f4f2012-05-06 13:11:24 -0700704 if (mRefreshProgressRunnable == null) {
705 mRefreshProgressRunnable = new RefreshProgressRunnable();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800706 }
Romain Guyab4c4f4f2012-05-06 13:11:24 -0700707
Adam Powella0506632012-04-10 17:19:20 -0700708 final RefreshData rd = RefreshData.obtain(id, progress, fromUser);
709 mRefreshData.add(rd);
710 if (mAttached && !mRefreshIsPosted) {
Romain Guyab4c4f4f2012-05-06 13:11:24 -0700711 post(mRefreshProgressRunnable);
Adam Powella0506632012-04-10 17:19:20 -0700712 mRefreshIsPosted = true;
713 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800714 }
715 }
716
717 /**
718 * <p>Set the current progress to the specified value. Does not do anything
719 * if the progress bar is in indeterminate mode.</p>
720 *
721 * @param progress the new progress, between 0 and {@link #getMax()}
722 *
723 * @see #setIndeterminate(boolean)
724 * @see #isIndeterminate()
725 * @see #getProgress()
726 * @see #incrementProgressBy(int)
727 */
728 @android.view.RemotableViewMethod
729 public synchronized void setProgress(int progress) {
730 setProgress(progress, false);
731 }
732
733 @android.view.RemotableViewMethod
734 synchronized void setProgress(int progress, boolean fromUser) {
735 if (mIndeterminate) {
736 return;
737 }
738
739 if (progress < 0) {
740 progress = 0;
741 }
742
743 if (progress > mMax) {
744 progress = mMax;
745 }
746
747 if (progress != mProgress) {
748 mProgress = progress;
749 refreshProgress(R.id.progress, mProgress, fromUser);
750 }
751 }
752
753 /**
754 * <p>
755 * Set the current secondary progress to the specified value. Does not do
756 * anything if the progress bar is in indeterminate mode.
757 * </p>
758 *
759 * @param secondaryProgress the new secondary progress, between 0 and {@link #getMax()}
760 * @see #setIndeterminate(boolean)
761 * @see #isIndeterminate()
762 * @see #getSecondaryProgress()
763 * @see #incrementSecondaryProgressBy(int)
764 */
765 @android.view.RemotableViewMethod
766 public synchronized void setSecondaryProgress(int secondaryProgress) {
767 if (mIndeterminate) {
768 return;
769 }
770
771 if (secondaryProgress < 0) {
772 secondaryProgress = 0;
773 }
774
775 if (secondaryProgress > mMax) {
776 secondaryProgress = mMax;
777 }
778
779 if (secondaryProgress != mSecondaryProgress) {
780 mSecondaryProgress = secondaryProgress;
781 refreshProgress(R.id.secondaryProgress, mSecondaryProgress, false);
782 }
783 }
784
785 /**
786 * <p>Get the progress bar's current level of progress. Return 0 when the
787 * progress bar is in indeterminate mode.</p>
788 *
789 * @return the current progress, between 0 and {@link #getMax()}
790 *
791 * @see #setIndeterminate(boolean)
792 * @see #isIndeterminate()
793 * @see #setProgress(int)
794 * @see #setMax(int)
795 * @see #getMax()
796 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -0700797 @ViewDebug.ExportedProperty(category = "progress")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800798 public synchronized int getProgress() {
799 return mIndeterminate ? 0 : mProgress;
800 }
801
802 /**
803 * <p>Get the progress bar's current level of secondary progress. Return 0 when the
804 * progress bar is in indeterminate mode.</p>
805 *
806 * @return the current secondary progress, between 0 and {@link #getMax()}
807 *
808 * @see #setIndeterminate(boolean)
809 * @see #isIndeterminate()
810 * @see #setSecondaryProgress(int)
811 * @see #setMax(int)
812 * @see #getMax()
813 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -0700814 @ViewDebug.ExportedProperty(category = "progress")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800815 public synchronized int getSecondaryProgress() {
816 return mIndeterminate ? 0 : mSecondaryProgress;
817 }
818
819 /**
820 * <p>Return the upper limit of this progress bar's range.</p>
821 *
822 * @return a positive integer
823 *
824 * @see #setMax(int)
825 * @see #getProgress()
826 * @see #getSecondaryProgress()
827 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -0700828 @ViewDebug.ExportedProperty(category = "progress")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800829 public synchronized int getMax() {
830 return mMax;
831 }
832
833 /**
834 * <p>Set the range of the progress bar to 0...<tt>max</tt>.</p>
835 *
836 * @param max the upper range of this progress bar
837 *
838 * @see #getMax()
839 * @see #setProgress(int)
840 * @see #setSecondaryProgress(int)
841 */
842 @android.view.RemotableViewMethod
843 public synchronized void setMax(int max) {
844 if (max < 0) {
845 max = 0;
846 }
847 if (max != mMax) {
848 mMax = max;
849 postInvalidate();
850
851 if (mProgress > max) {
852 mProgress = max;
853 }
Michael Krehan58e38222011-02-17 20:04:27 -0800854 refreshProgress(R.id.progress, mProgress, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800855 }
856 }
857
858 /**
859 * <p>Increase the progress bar's progress by the specified amount.</p>
860 *
861 * @param diff the amount by which the progress must be increased
862 *
863 * @see #setProgress(int)
864 */
865 public synchronized final void incrementProgressBy(int diff) {
866 setProgress(mProgress + diff);
867 }
868
869 /**
870 * <p>Increase the progress bar's secondary progress by the specified amount.</p>
871 *
872 * @param diff the amount by which the secondary progress must be increased
873 *
874 * @see #setSecondaryProgress(int)
875 */
876 public synchronized final void incrementSecondaryProgressBy(int diff) {
877 setSecondaryProgress(mSecondaryProgress + diff);
878 }
879
880 /**
881 * <p>Start the indeterminate progress animation.</p>
882 */
883 void startAnimation() {
Romain Guya05e8a52010-02-25 14:32:39 -0800884 if (getVisibility() != VISIBLE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800885 return;
886 }
887
Jean-Baptiste Querucf4550c2009-07-21 11:16:54 -0700888 if (mIndeterminateDrawable instanceof Animatable) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800889 mShouldStartAnimationDrawable = true;
Romain Guyab4c4f4f2012-05-06 13:11:24 -0700890 mHasAnimation = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800891 } else {
Romain Guyab4c4f4f2012-05-06 13:11:24 -0700892 mHasAnimation = true;
893
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800894 if (mInterpolator == null) {
895 mInterpolator = new LinearInterpolator();
896 }
897
Romain Guyab4c4f4f2012-05-06 13:11:24 -0700898 if (mTransformation == null) {
899 mTransformation = new Transformation();
900 } else {
901 mTransformation.clear();
902 }
903
904 if (mAnimation == null) {
905 mAnimation = new AlphaAnimation(0.0f, 1.0f);
906 } else {
907 mAnimation.reset();
908 }
909
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800910 mAnimation.setRepeatMode(mBehavior);
911 mAnimation.setRepeatCount(Animation.INFINITE);
912 mAnimation.setDuration(mDuration);
913 mAnimation.setInterpolator(mInterpolator);
914 mAnimation.setStartTime(Animation.START_ON_FIRST_FRAME);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800915 }
Evan Charlton08e14732010-06-07 10:38:53 -0700916 postInvalidate();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800917 }
918
919 /**
920 * <p>Stop the indeterminate progress animation.</p>
921 */
922 void stopAnimation() {
Romain Guyab4c4f4f2012-05-06 13:11:24 -0700923 mHasAnimation = false;
Jean-Baptiste Querucf4550c2009-07-21 11:16:54 -0700924 if (mIndeterminateDrawable instanceof Animatable) {
925 ((Animatable) mIndeterminateDrawable).stop();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800926 mShouldStartAnimationDrawable = false;
927 }
Evan Charlton08e14732010-06-07 10:38:53 -0700928 postInvalidate();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800929 }
930
931 /**
932 * Sets the acceleration curve for the indeterminate animation.
933 * The interpolator is loaded as a resource from the specified context.
934 *
935 * @param context The application environment
936 * @param resID The resource identifier of the interpolator to load
937 */
938 public void setInterpolator(Context context, int resID) {
939 setInterpolator(AnimationUtils.loadInterpolator(context, resID));
940 }
941
942 /**
943 * Sets the acceleration curve for the indeterminate animation.
944 * Defaults to a linear interpolation.
945 *
946 * @param interpolator The interpolator which defines the acceleration curve
947 */
948 public void setInterpolator(Interpolator interpolator) {
949 mInterpolator = interpolator;
950 }
951
952 /**
953 * Gets the acceleration curve type for the indeterminate animation.
954 *
955 * @return the {@link Interpolator} associated to this animation
956 */
957 public Interpolator getInterpolator() {
958 return mInterpolator;
959 }
960
961 @Override
Steve Howardb25ffff2010-08-20 17:39:26 -0700962 @RemotableViewMethod
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800963 public void setVisibility(int v) {
964 if (getVisibility() != v) {
965 super.setVisibility(v);
966
967 if (mIndeterminate) {
968 // let's be nice with the UI thread
969 if (v == GONE || v == INVISIBLE) {
970 stopAnimation();
Romain Guya05e8a52010-02-25 14:32:39 -0800971 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800972 startAnimation();
973 }
974 }
975 }
976 }
977
978 @Override
Romain Guya05e8a52010-02-25 14:32:39 -0800979 protected void onVisibilityChanged(View changedView, int visibility) {
980 super.onVisibilityChanged(changedView, visibility);
981
982 if (mIndeterminate) {
983 // let's be nice with the UI thread
984 if (visibility == GONE || visibility == INVISIBLE) {
985 stopAnimation();
986 } else {
987 startAnimation();
988 }
989 }
990 }
991
992 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800993 public void invalidateDrawable(Drawable dr) {
994 if (!mInDrawing) {
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700995 if (verifyDrawable(dr)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800996 final Rect dirty = dr.getBounds();
Chris Craik7546a4b2013-10-30 19:49:37 -0700997 final int scrollX = mScrollX + mPaddingLeft;
998 final int scrollY = mScrollY + mPaddingTop;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800999
Chris Craik7546a4b2013-10-30 19:49:37 -07001000 invalidate(dirty.left + scrollX, dirty.top + scrollY,
1001 dirty.right + scrollX, dirty.bottom + scrollY);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001002 } else {
1003 super.invalidateDrawable(dr);
1004 }
1005 }
1006 }
1007
1008 @Override
1009 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
Joe Onoratoaa072632010-12-08 15:31:28 -08001010 updateDrawableBounds(w, h);
1011 }
1012
1013 private void updateDrawableBounds(int w, int h) {
Adam Powell6322af52012-08-08 15:59:12 -07001014 // onDraw will translate the canvas so we draw starting at 0,0.
1015 // Subtract out padding for the purposes of the calculations below.
1016 w -= mPaddingRight + mPaddingLeft;
1017 h -= mPaddingTop + mPaddingBottom;
1018
1019 int right = w;
1020 int bottom = h;
Adam Powella1b92c52011-09-02 15:05:15 -07001021 int top = 0;
1022 int left = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001023
1024 if (mIndeterminateDrawable != null) {
Chet Haasea79803c2011-09-28 17:51:53 -07001025 // Aspect ratio logic does not apply to AnimationDrawables
1026 if (mOnlyIndeterminate && !(mIndeterminateDrawable instanceof AnimationDrawable)) {
Adam Powella1b92c52011-09-02 15:05:15 -07001027 // Maintain aspect ratio. Certain kinds of animated drawables
1028 // get very confused otherwise.
1029 final int intrinsicWidth = mIndeterminateDrawable.getIntrinsicWidth();
1030 final int intrinsicHeight = mIndeterminateDrawable.getIntrinsicHeight();
1031 final float intrinsicAspect = (float) intrinsicWidth / intrinsicHeight;
1032 final float boundAspect = (float) w / h;
1033 if (intrinsicAspect != boundAspect) {
1034 if (boundAspect > intrinsicAspect) {
1035 // New width is larger. Make it smaller to match height.
1036 final int width = (int) (h * intrinsicAspect);
1037 left = (w - width) / 2;
1038 right = left + width;
1039 } else {
1040 // New height is larger. Make it smaller to match width.
1041 final int height = (int) (w * (1 / intrinsicAspect));
1042 top = (h - height) / 2;
1043 bottom = top + height;
1044 }
1045 }
1046 }
Fabrice Di Meglio2b378cd2013-01-30 16:39:33 -08001047 if (isLayoutRtl() && mMirrorForRtl) {
Fabrice Di Meglio7fb98b32012-09-12 20:04:04 -07001048 int tempLeft = left;
1049 left = w - right;
1050 right = w - tempLeft;
1051 }
Adam Powella1b92c52011-09-02 15:05:15 -07001052 mIndeterminateDrawable.setBounds(left, top, right, bottom);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001053 }
1054
1055 if (mProgressDrawable != null) {
1056 mProgressDrawable.setBounds(0, 0, right, bottom);
1057 }
1058 }
1059
1060 @Override
1061 protected synchronized void onDraw(Canvas canvas) {
1062 super.onDraw(canvas);
1063
1064 Drawable d = mCurrentDrawable;
1065 if (d != null) {
1066 // Translate canvas so a indeterminate circular progress bar with padding
1067 // rotates properly in its animation
1068 canvas.save();
Fabrice Di Meglio2b378cd2013-01-30 16:39:33 -08001069 if(isLayoutRtl() && mMirrorForRtl) {
Fabrice Di Meglio7fb98b32012-09-12 20:04:04 -07001070 canvas.translate(getWidth() - mPaddingRight, mPaddingTop);
1071 canvas.scale(-1.0f, 1.0f);
1072 } else {
1073 canvas.translate(mPaddingLeft, mPaddingTop);
1074 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001075 long time = getDrawingTime();
Romain Guyab4c4f4f2012-05-06 13:11:24 -07001076 if (mHasAnimation) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001077 mAnimation.getTransformation(time, mTransformation);
1078 float scale = mTransformation.getAlpha();
1079 try {
1080 mInDrawing = true;
1081 d.setLevel((int) (scale * MAX_LEVEL));
1082 } finally {
1083 mInDrawing = false;
1084 }
Jeff Brown6cb7b462012-03-05 13:21:17 -08001085 postInvalidateOnAnimation();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001086 }
1087 d.draw(canvas);
1088 canvas.restore();
Jean-Baptiste Querucf4550c2009-07-21 11:16:54 -07001089 if (mShouldStartAnimationDrawable && d instanceof Animatable) {
1090 ((Animatable) d).start();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001091 mShouldStartAnimationDrawable = false;
1092 }
1093 }
1094 }
1095
1096 @Override
1097 protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
1098 Drawable d = mCurrentDrawable;
1099
1100 int dw = 0;
1101 int dh = 0;
1102 if (d != null) {
1103 dw = Math.max(mMinWidth, Math.min(mMaxWidth, d.getIntrinsicWidth()));
1104 dh = Math.max(mMinHeight, Math.min(mMaxHeight, d.getIntrinsicHeight()));
1105 }
Joe Onoratoaa072632010-12-08 15:31:28 -08001106 updateDrawableState();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001107 dw += mPaddingLeft + mPaddingRight;
1108 dh += mPaddingTop + mPaddingBottom;
1109
Dianne Hackborn189ee182010-12-02 21:48:53 -08001110 setMeasuredDimension(resolveSizeAndState(dw, widthMeasureSpec, 0),
1111 resolveSizeAndState(dh, heightMeasureSpec, 0));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001112 }
1113
1114 @Override
1115 protected void drawableStateChanged() {
1116 super.drawableStateChanged();
Joe Onoratoaa072632010-12-08 15:31:28 -08001117 updateDrawableState();
1118 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001119
Joe Onoratoaa072632010-12-08 15:31:28 -08001120 private void updateDrawableState() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001121 int[] state = getDrawableState();
1122
1123 if (mProgressDrawable != null && mProgressDrawable.isStateful()) {
1124 mProgressDrawable.setState(state);
1125 }
1126
1127 if (mIndeterminateDrawable != null && mIndeterminateDrawable.isStateful()) {
1128 mIndeterminateDrawable.setState(state);
1129 }
1130 }
1131
1132 static class SavedState extends BaseSavedState {
1133 int progress;
1134 int secondaryProgress;
1135
1136 /**
1137 * Constructor called from {@link ProgressBar#onSaveInstanceState()}
1138 */
1139 SavedState(Parcelable superState) {
1140 super(superState);
1141 }
1142
1143 /**
1144 * Constructor called from {@link #CREATOR}
1145 */
1146 private SavedState(Parcel in) {
1147 super(in);
1148 progress = in.readInt();
1149 secondaryProgress = in.readInt();
1150 }
1151
1152 @Override
1153 public void writeToParcel(Parcel out, int flags) {
1154 super.writeToParcel(out, flags);
1155 out.writeInt(progress);
1156 out.writeInt(secondaryProgress);
1157 }
1158
1159 public static final Parcelable.Creator<SavedState> CREATOR
1160 = new Parcelable.Creator<SavedState>() {
1161 public SavedState createFromParcel(Parcel in) {
1162 return new SavedState(in);
1163 }
1164
1165 public SavedState[] newArray(int size) {
1166 return new SavedState[size];
1167 }
1168 };
1169 }
1170
1171 @Override
1172 public Parcelable onSaveInstanceState() {
1173 // Force our ancestor class to save its state
1174 Parcelable superState = super.onSaveInstanceState();
1175 SavedState ss = new SavedState(superState);
1176
1177 ss.progress = mProgress;
1178 ss.secondaryProgress = mSecondaryProgress;
1179
1180 return ss;
1181 }
1182
1183 @Override
1184 public void onRestoreInstanceState(Parcelable state) {
1185 SavedState ss = (SavedState) state;
1186 super.onRestoreInstanceState(ss.getSuperState());
1187
1188 setProgress(ss.progress);
1189 setSecondaryProgress(ss.secondaryProgress);
1190 }
David Sobreira Marques52a35432010-05-15 16:10:18 -03001191
1192 @Override
1193 protected void onAttachedToWindow() {
1194 super.onAttachedToWindow();
1195 if (mIndeterminate) {
1196 startAnimation();
1197 }
Adam Powella0506632012-04-10 17:19:20 -07001198 if (mRefreshData != null) {
1199 synchronized (this) {
1200 final int count = mRefreshData.size();
1201 for (int i = 0; i < count; i++) {
1202 final RefreshData rd = mRefreshData.get(i);
1203 doRefreshProgress(rd.id, rd.progress, rd.fromUser, true);
1204 rd.recycle();
1205 }
1206 mRefreshData.clear();
1207 }
1208 }
1209 mAttached = true;
David Sobreira Marques52a35432010-05-15 16:10:18 -03001210 }
1211
1212 @Override
1213 protected void onDetachedFromWindow() {
David Sobreira Marques52a35432010-05-15 16:10:18 -03001214 if (mIndeterminate) {
1215 stopAnimation();
1216 }
Adam Powella0506632012-04-10 17:19:20 -07001217 if (mRefreshProgressRunnable != null) {
1218 removeCallbacks(mRefreshProgressRunnable);
1219 }
1220 if (mRefreshProgressRunnable != null && mRefreshIsPosted) {
Svetoslav Ganov6518ad72011-03-18 16:19:55 -07001221 removeCallbacks(mRefreshProgressRunnable);
1222 }
1223 if (mAccessibilityEventSender != null) {
1224 removeCallbacks(mAccessibilityEventSender);
1225 }
Patrick Dubroyec84c3a2011-01-13 17:55:37 -08001226 // This should come after stopAnimation(), otherwise an invalidate message remains in the
1227 // queue, which can prevent the entire view hierarchy from being GC'ed during a rotation
1228 super.onDetachedFromWindow();
Adam Powella0506632012-04-10 17:19:20 -07001229 mAttached = false;
David Sobreira Marques52a35432010-05-15 16:10:18 -03001230 }
Svetoslav Ganov6518ad72011-03-18 16:19:55 -07001231
1232 @Override
Svetoslav Ganov30401322011-05-12 18:53:45 -07001233 public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
1234 super.onInitializeAccessibilityEvent(event);
Svetoslav Ganov8a78fd42012-01-17 14:36:46 -08001235 event.setClassName(ProgressBar.class.getName());
Svetoslav Ganov736c2752011-04-22 18:30:36 -07001236 event.setItemCount(mMax);
1237 event.setCurrentItemIndex(mProgress);
Svetoslav Ganov6518ad72011-03-18 16:19:55 -07001238 }
1239
Svetoslav Ganov8a78fd42012-01-17 14:36:46 -08001240 @Override
1241 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
1242 super.onInitializeAccessibilityNodeInfo(info);
1243 info.setClassName(ProgressBar.class.getName());
1244 }
1245
Svetoslav Ganov6518ad72011-03-18 16:19:55 -07001246 /**
1247 * Schedule a command for sending an accessibility event.
1248 * </br>
1249 * Note: A command is used to ensure that accessibility events
1250 * are sent at most one in a given time frame to save
1251 * system resources while the progress changes quickly.
1252 */
1253 private void scheduleAccessibilityEventSender() {
1254 if (mAccessibilityEventSender == null) {
1255 mAccessibilityEventSender = new AccessibilityEventSender();
1256 } else {
1257 removeCallbacks(mAccessibilityEventSender);
1258 }
1259 postDelayed(mAccessibilityEventSender, TIMEOUT_SEND_ACCESSIBILITY_EVENT);
1260 }
1261
1262 /**
1263 * Command for sending an accessibility event.
1264 */
1265 private class AccessibilityEventSender implements Runnable {
1266 public void run() {
1267 sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
1268 }
1269 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001270}