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 com
.actionbarsherlock
.internal
.widget
;
19 import android
.content
.Context
;
20 import android
.content
.res
.TypedArray
;
21 import android
.graphics
.Bitmap
;
22 import android
.graphics
.BitmapShader
;
23 import android
.graphics
.Canvas
;
24 import android
.graphics
.Rect
;
25 import android
.graphics
.Shader
;
26 import android
.graphics
.drawable
.Animatable
;
27 import android
.graphics
.drawable
.AnimationDrawable
;
28 import android
.graphics
.drawable
.BitmapDrawable
;
29 import android
.graphics
.drawable
.ClipDrawable
;
30 import android
.graphics
.drawable
.Drawable
;
31 import android
.graphics
.drawable
.LayerDrawable
;
32 import android
.graphics
.drawable
.ShapeDrawable
;
33 import android
.graphics
.drawable
.shapes
.RoundRectShape
;
34 import android
.graphics
.drawable
.shapes
.Shape
;
35 import android
.os
.Build
;
36 import android
.os
.Parcel
;
37 import android
.os
.Parcelable
;
38 import android
.os
.SystemClock
;
39 import android
.util
.AttributeSet
;
40 import android
.view
.Gravity
;
41 import android
.view
.View
;
42 import android
.view
.ViewDebug
;
43 import android
.view
.accessibility
.AccessibilityEvent
;
44 import android
.view
.accessibility
.AccessibilityManager
;
45 import android
.view
.animation
.AlphaAnimation
;
46 import android
.view
.animation
.Animation
;
47 import android
.view
.animation
.AnimationUtils
;
48 import android
.view
.animation
.Interpolator
;
49 import android
.view
.animation
.LinearInterpolator
;
50 import android
.view
.animation
.Transformation
;
51 import android
.widget
.RemoteViews
.RemoteView
;
56 * Visual indicator of progress in some operation. Displays a bar to the user
57 * representing how far the operation has progressed; the application can
58 * change the amount of progress (modifying the length of the bar) as it moves
59 * forward. There is also a secondary progress displayable on a progress bar
60 * which is useful for displaying intermediate progress, such as the buffer
61 * level during a streaming playback progress bar.
65 * A progress bar can also be made indeterminate. In indeterminate mode, the
66 * progress bar shows a cyclic animation without an indication of progress. This mode is used by
67 * applications when the length of the task is unknown. The indeterminate progress bar can be either
68 * a spinning wheel or a horizontal bar.
71 * <p>The following code example shows how a progress bar can be used from
72 * a worker thread to update the user interface to notify the user of progress:
76 * public class MyActivity extends Activity {
77 * private static final int PROGRESS = 0x1;
79 * private ProgressBar mProgress;
80 * private int mProgressStatus = 0;
82 * private Handler mHandler = new Handler();
84 * protected void onCreate(Bundle icicle) {
85 * super.onCreate(icicle);
87 * setContentView(R.layout.progressbar_activity);
89 * mProgress = (ProgressBar) findViewById(R.id.progress_bar);
91 * // Start lengthy operation in a background thread
92 * new Thread(new Runnable() {
94 * while (mProgressStatus < 100) {
95 * mProgressStatus = doWork();
97 * // Update the progress bar
98 * mHandler.post(new Runnable() {
100 * mProgress.setProgress(mProgressStatus);
109 * <p>To add a progress bar to a layout file, you can use the {@code <ProgressBar>} element.
110 * By default, the progress bar is a spinning wheel (an indeterminate indicator). To change to a
111 * horizontal progress bar, apply the {@link android.R.style#Widget_ProgressBar_Horizontal
112 * Widget.ProgressBar.Horizontal} style, like so:</p>
116 * style="@android:style/Widget.ProgressBar.Horizontal"
119 * <p>If you will use the progress bar to show real progress, you must use the horizontal bar. You
120 * can then increment the progress with {@link #incrementProgressBy incrementProgressBy()} or
121 * {@link #setProgress setProgress()}. By default, the progress bar is full when it reaches 100. If
122 * necessary, you can adjust the maximum value (the value for a full bar) using the {@link
123 * android.R.styleable#ProgressBar_max android:max} attribute. Other attributes available are listed
126 * <p>Another common style to apply to the progress bar is {@link
127 * android.R.style#Widget_ProgressBar_Small Widget.ProgressBar.Small}, which shows a smaller
128 * version of the spinning wheel—useful when waiting for content to load.
129 * For example, you can insert this kind of progress bar into your default layout for
130 * a view that will be populated by some content fetched from the Internet—the spinning wheel
131 * appears immediately and when your application receives the content, it replaces the progress bar
132 * with the loaded content. For example:</p>
136 * android:orientation="horizontal"
139 * android:layout_width="wrap_content"
140 * android:layout_height="wrap_content"
141 * style="@android:style/Widget.ProgressBar.Small"
142 * android:layout_marginRight="5dp" />
144 * android:layout_width="wrap_content"
145 * android:layout_height="wrap_content"
146 * android:text="@string/loading" />
147 * </LinearLayout></pre>
149 * <p>Other progress bar styles provided by the system include:</p>
151 * <li>{@link android.R.style#Widget_ProgressBar_Horizontal Widget.ProgressBar.Horizontal}</li>
152 * <li>{@link android.R.style#Widget_ProgressBar_Small Widget.ProgressBar.Small}</li>
153 * <li>{@link android.R.style#Widget_ProgressBar_Large Widget.ProgressBar.Large}</li>
154 * <li>{@link android.R.style#Widget_ProgressBar_Inverse Widget.ProgressBar.Inverse}</li>
155 * <li>{@link android.R.style#Widget_ProgressBar_Small_Inverse
156 * Widget.ProgressBar.Small.Inverse}</li>
157 * <li>{@link android.R.style#Widget_ProgressBar_Large_Inverse
158 * Widget.ProgressBar.Large.Inverse}</li>
160 * <p>The "inverse" styles provide an inverse color scheme for the spinner, which may be necessary
161 * if your application uses a light colored theme (a white background).</p>
163 * <p><strong>XML attributes</b></strong>
165 * See {@link android.R.styleable#ProgressBar ProgressBar Attributes},
166 * {@link android.R.styleable#View View Attributes}
169 * @attr ref android.R.styleable#ProgressBar_animationResolution
170 * @attr ref android.R.styleable#ProgressBar_indeterminate
171 * @attr ref android.R.styleable#ProgressBar_indeterminateBehavior
172 * @attr ref android.R.styleable#ProgressBar_indeterminateDrawable
173 * @attr ref android.R.styleable#ProgressBar_indeterminateDuration
174 * @attr ref android.R.styleable#ProgressBar_indeterminateOnly
175 * @attr ref android.R.styleable#ProgressBar_interpolator
176 * @attr ref android.R.styleable#ProgressBar_max
177 * @attr ref android.R.styleable#ProgressBar_maxHeight
178 * @attr ref android.R.styleable#ProgressBar_maxWidth
179 * @attr ref android.R.styleable#ProgressBar_minHeight
180 * @attr ref android.R.styleable#ProgressBar_minWidth
181 * @attr ref android.R.styleable#ProgressBar_progress
182 * @attr ref android.R.styleable#ProgressBar_progressDrawable
183 * @attr ref android.R.styleable#ProgressBar_secondaryProgress
186 public class IcsProgressBar
extends View
{
187 private static final boolean IS_HONEYCOMB
= Build
.VERSION
.SDK_INT
>= Build
.VERSION_CODES
.HONEYCOMB
;
188 private static final int MAX_LEVEL
= 10000;
189 private static final int ANIMATION_RESOLUTION
= 200;
190 private static final int TIMEOUT_SEND_ACCESSIBILITY_EVENT
= 200;
192 private static final int[] ProgressBar
= new int[] {
193 android
.R
.attr
.maxWidth
,
194 android
.R
.attr
.maxHeight
,
196 android
.R
.attr
.progress
,
197 android
.R
.attr
.secondaryProgress
,
198 android
.R
.attr
.indeterminate
,
199 android
.R
.attr
.indeterminateOnly
,
200 android
.R
.attr
.indeterminateDrawable
,
201 android
.R
.attr
.progressDrawable
,
202 android
.R
.attr
.indeterminateDuration
,
203 android
.R
.attr
.indeterminateBehavior
,
204 android
.R
.attr
.minWidth
,
205 android
.R
.attr
.minHeight
,
206 android
.R
.attr
.interpolator
,
207 android
.R
.attr
.animationResolution
,
209 private static final int ProgressBar_maxWidth
= 0;
210 private static final int ProgressBar_maxHeight
= 1;
211 private static final int ProgressBar_max
= 2;
212 private static final int ProgressBar_progress
= 3;
213 private static final int ProgressBar_secondaryProgress
= 4;
214 private static final int ProgressBar_indeterminate
= 5;
215 private static final int ProgressBar_indeterminateOnly
= 6;
216 private static final int ProgressBar_indeterminateDrawable
= 7;
217 private static final int ProgressBar_progressDrawable
= 8;
218 private static final int ProgressBar_indeterminateDuration
= 9;
219 private static final int ProgressBar_indeterminateBehavior
= 10;
220 private static final int ProgressBar_minWidth
= 11;
221 private static final int ProgressBar_minHeight
= 12;
222 private static final int ProgressBar_interpolator
= 13;
223 private static final int ProgressBar_animationResolution
= 14;
230 private int mProgress
;
231 private int mSecondaryProgress
;
234 private int mBehavior
;
235 private int mDuration
;
236 private boolean mIndeterminate
;
237 private boolean mOnlyIndeterminate
;
238 private Transformation mTransformation
;
239 private AlphaAnimation mAnimation
;
240 private Drawable mIndeterminateDrawable
;
241 private int mIndeterminateRealLeft
;
242 private int mIndeterminateRealTop
;
243 private Drawable mProgressDrawable
;
244 private Drawable mCurrentDrawable
;
246 private boolean mNoInvalidate
;
247 private Interpolator mInterpolator
;
248 private RefreshProgressRunnable mRefreshProgressRunnable
;
249 private long mUiThreadId
;
250 private boolean mShouldStartAnimationDrawable
;
251 private long mLastDrawTime
;
253 private boolean mInDrawing
;
255 private int mAnimationResolution
;
257 private AccessibilityManager mAccessibilityManager
;
258 private AccessibilityEventSender mAccessibilityEventSender
;
261 * Create a new progress bar with range 0...100 and initial progress of 0.
262 * @param context the application environment
264 public IcsProgressBar(Context context
) {
268 public IcsProgressBar(Context context
, AttributeSet attrs
) {
269 this(context
, attrs
, android
.R
.attr
.progressBarStyle
);
272 public IcsProgressBar(Context context
, AttributeSet attrs
, int defStyle
) {
273 this(context
, attrs
, defStyle
, 0);
279 public IcsProgressBar(Context context
, AttributeSet attrs
, int defStyle
, int styleRes
) {
280 super(context
, attrs
, defStyle
);
281 mUiThreadId
= Thread
.currentThread().getId();
285 context
.obtainStyledAttributes(attrs
, /*R.styleable.*/ProgressBar
, defStyle
, styleRes
);
287 mNoInvalidate
= true
;
289 Drawable drawable
= a
.getDrawable(/*R.styleable.*/ProgressBar_progressDrawable
);
290 if (drawable
!= null
) {
291 drawable
= tileify(drawable
, false
);
292 // Calling this method can set mMaxHeight, make sure the corresponding
293 // XML attribute for mMaxHeight is read after calling this method
294 setProgressDrawable(drawable
);
298 mDuration
= a
.getInt(/*R.styleable.*/ProgressBar_indeterminateDuration
, mDuration
);
300 mMinWidth
= a
.getDimensionPixelSize(/*R.styleable.*/ProgressBar_minWidth
, mMinWidth
);
301 mMaxWidth
= a
.getDimensionPixelSize(/*R.styleable.*/ProgressBar_maxWidth
, mMaxWidth
);
302 mMinHeight
= a
.getDimensionPixelSize(/*R.styleable.*/ProgressBar_minHeight
, mMinHeight
);
303 mMaxHeight
= a
.getDimensionPixelSize(/*R.styleable.*/ProgressBar_maxHeight
, mMaxHeight
);
305 mBehavior
= a
.getInt(/*R.styleable.*/ProgressBar_indeterminateBehavior
, mBehavior
);
307 final int resID
= a
.getResourceId(
308 /*com.android.internal.R.styleable.*/ProgressBar_interpolator
,
309 android
.R
.anim
.linear_interpolator
); // default to linear interpolator
311 setInterpolator(context
, resID
);
314 setMax(a
.getInt(/*R.styleable.*/ProgressBar_max
, mMax
));
316 setProgress(a
.getInt(/*R.styleable.*/ProgressBar_progress
, mProgress
));
318 setSecondaryProgress(
319 a
.getInt(/*R.styleable.*/ProgressBar_secondaryProgress
, mSecondaryProgress
));
321 drawable
= a
.getDrawable(/*R.styleable.*/ProgressBar_indeterminateDrawable
);
322 if (drawable
!= null
) {
323 drawable
= tileifyIndeterminate(drawable
);
324 setIndeterminateDrawable(drawable
);
327 mOnlyIndeterminate
= a
.getBoolean(
328 /*R.styleable.*/ProgressBar_indeterminateOnly
, mOnlyIndeterminate
);
330 mNoInvalidate
= false
;
332 setIndeterminate(mOnlyIndeterminate
|| a
.getBoolean(
333 /*R.styleable.*/ProgressBar_indeterminate
, mIndeterminate
));
335 mAnimationResolution
= a
.getInteger(/*R.styleable.*/ProgressBar_animationResolution
,
336 ANIMATION_RESOLUTION
);
340 mAccessibilityManager
= (AccessibilityManager
)context
.getSystemService(Context
.ACCESSIBILITY_SERVICE
);
344 * Converts a drawable to a tiled version of itself. It will recursively
345 * traverse layer and state list drawables.
347 private Drawable
tileify(Drawable drawable
, boolean clip
) {
349 if (drawable
instanceof LayerDrawable
) {
350 LayerDrawable background
= (LayerDrawable
) drawable
;
351 final int N
= background
.getNumberOfLayers();
352 Drawable
[] outDrawables
= new Drawable
[N
];
354 for (int i
= 0; i
< N
; i
++) {
355 int id
= background
.getId(i
);
356 outDrawables
[i
] = tileify(background
.getDrawable(i
),
357 (id
== android
.R
.id
.progress
|| id
== android
.R
.id
.secondaryProgress
));
360 LayerDrawable newBg
= new LayerDrawable(outDrawables
);
362 for (int i
= 0; i
< N
; i
++) {
363 newBg
.setId(i
, background
.getId(i
));
368 }/* else if (drawable instanceof StateListDrawable) {
369 StateListDrawable in = (StateListDrawable) drawable;
370 StateListDrawable out = new StateListDrawable();
371 int numStates = in.getStateCount();
372 for (int i = 0; i < numStates; i++) {
373 out.addState(in.getStateSet(i), tileify(in.getStateDrawable(i), clip));
377 }*/ else if (drawable
instanceof BitmapDrawable
) {
378 final Bitmap tileBitmap
= ((BitmapDrawable
) drawable
).getBitmap();
379 if (mSampleTile
== null
) {
380 mSampleTile
= tileBitmap
;
383 final ShapeDrawable shapeDrawable
= new ShapeDrawable(getDrawableShape());
385 final BitmapShader bitmapShader
= new BitmapShader(tileBitmap
,
386 Shader
.TileMode
.REPEAT
, Shader
.TileMode
.CLAMP
);
387 shapeDrawable
.getPaint().setShader(bitmapShader
);
389 return (clip
) ?
new ClipDrawable(shapeDrawable
, Gravity
.LEFT
,
390 ClipDrawable
.HORIZONTAL
) : shapeDrawable
;
396 Shape
getDrawableShape() {
397 final float[] roundedCorners
= new float[] { 5, 5, 5, 5, 5, 5, 5, 5 };
398 return new RoundRectShape(roundedCorners
, null
, null
);
402 * Convert a AnimationDrawable for use as a barberpole animation.
403 * Each frame of the animation is wrapped in a ClipDrawable and
404 * given a tiling BitmapShader.
406 private Drawable
tileifyIndeterminate(Drawable drawable
) {
407 if (drawable
instanceof AnimationDrawable
) {
408 AnimationDrawable background
= (AnimationDrawable
) drawable
;
409 final int N
= background
.getNumberOfFrames();
410 AnimationDrawable newBg
= new AnimationDrawable();
411 newBg
.setOneShot(background
.isOneShot());
413 for (int i
= 0; i
< N
; i
++) {
414 Drawable frame
= tileify(background
.getFrame(i
), true
);
415 frame
.setLevel(10000);
416 newBg
.addFrame(frame
, background
.getDuration(i
));
418 newBg
.setLevel(10000);
426 * Initialize the progress bar's default values:
429 * <li>progress = 0</li>
431 * <li>animation duration = 4000 ms</li>
432 * <li>indeterminate = false</li>
433 * <li>behavior = repeat</li>
436 private void initProgressBar() {
439 mSecondaryProgress
= 0;
440 mIndeterminate
= false
;
441 mOnlyIndeterminate
= false
;
443 mBehavior
= AlphaAnimation
.RESTART
;
451 * <p>Indicate whether this progress bar is in indeterminate mode.</p>
453 * @return true if the progress bar is in indeterminate mode
455 @ViewDebug.ExportedProperty(category
= "progress")
456 public synchronized boolean isIndeterminate() {
457 return mIndeterminate
;
461 * <p>Change the indeterminate mode for this progress bar. In indeterminate
462 * mode, the progress is ignored and the progress bar shows an infinite
463 * animation instead.</p>
465 * If this progress bar's style only supports indeterminate mode (such as the circular
466 * progress bars), then this will be ignored.
468 * @param indeterminate true to enable the indeterminate mode
470 public synchronized void setIndeterminate(boolean indeterminate
) {
471 if ((!mOnlyIndeterminate
|| !mIndeterminate
) && indeterminate
!= mIndeterminate
) {
472 mIndeterminate
= indeterminate
;
475 // swap between indeterminate and regular backgrounds
476 mCurrentDrawable
= mIndeterminateDrawable
;
479 mCurrentDrawable
= mProgressDrawable
;
486 * <p>Get the drawable used to draw the progress bar in
487 * indeterminate mode.</p>
489 * @return a {@link android.graphics.drawable.Drawable} instance
491 * @see #setIndeterminateDrawable(android.graphics.drawable.Drawable)
492 * @see #setIndeterminate(boolean)
494 public Drawable
getIndeterminateDrawable() {
495 return mIndeterminateDrawable
;
499 * <p>Define the drawable used to draw the progress bar in
500 * indeterminate mode.</p>
502 * @param d the new drawable
504 * @see #getIndeterminateDrawable()
505 * @see #setIndeterminate(boolean)
507 public void setIndeterminateDrawable(Drawable d
) {
511 mIndeterminateDrawable
= d
;
512 if (mIndeterminate
) {
513 mCurrentDrawable
= d
;
519 * <p>Get the drawable used to draw the progress bar in
522 * @return a {@link android.graphics.drawable.Drawable} instance
524 * @see #setProgressDrawable(android.graphics.drawable.Drawable)
525 * @see #setIndeterminate(boolean)
527 public Drawable
getProgressDrawable() {
528 return mProgressDrawable
;
532 * <p>Define the drawable used to draw the progress bar in
535 * @param d the new drawable
537 * @see #getProgressDrawable()
538 * @see #setIndeterminate(boolean)
540 public void setProgressDrawable(Drawable d
) {
542 if (mProgressDrawable
!= null
&& d
!= mProgressDrawable
) {
543 mProgressDrawable
.setCallback(null
);
552 // Make sure the ProgressBar is always tall enough
553 int drawableHeight
= d
.getMinimumHeight();
554 if (mMaxHeight
< drawableHeight
) {
555 mMaxHeight
= drawableHeight
;
559 mProgressDrawable
= d
;
560 if (!mIndeterminate
) {
561 mCurrentDrawable
= d
;
566 updateDrawableBounds(getWidth(), getHeight());
567 updateDrawableState();
568 doRefreshProgress(android
.R
.id
.progress
, mProgress
, false
, false
);
569 doRefreshProgress(android
.R
.id
.secondaryProgress
, mSecondaryProgress
, false
, false
);
574 * @return The drawable currently used to draw the progress bar
576 Drawable
getCurrentDrawable() {
577 return mCurrentDrawable
;
581 protected boolean verifyDrawable(Drawable who
) {
582 return who
== mProgressDrawable
|| who
== mIndeterminateDrawable
583 || super.verifyDrawable(who
);
587 public void jumpDrawablesToCurrentState() {
588 super.jumpDrawablesToCurrentState();
589 if (mProgressDrawable
!= null
) mProgressDrawable
.jumpToCurrentState();
590 if (mIndeterminateDrawable
!= null
) mIndeterminateDrawable
.jumpToCurrentState();
594 public void postInvalidate() {
595 if (!mNoInvalidate
) {
596 super.postInvalidate();
600 private class RefreshProgressRunnable
implements Runnable
{
603 private int mProgress
;
604 private boolean mFromUser
;
606 RefreshProgressRunnable(int id
, int progress
, boolean fromUser
) {
608 mProgress
= progress
;
609 mFromUser
= fromUser
;
613 doRefreshProgress(mId
, mProgress
, mFromUser
, true
);
614 // Put ourselves back in the cache when we are done
615 mRefreshProgressRunnable
= this;
618 public void setup(int id
, int progress
, boolean fromUser
) {
620 mProgress
= progress
;
621 mFromUser
= fromUser
;
626 private synchronized void doRefreshProgress(int id
, int progress
, boolean fromUser
,
627 boolean callBackToApp
) {
628 float scale
= mMax
> 0 ?
(float) progress
/ (float) mMax
: 0;
629 final Drawable d
= mCurrentDrawable
;
631 Drawable progressDrawable
= null
;
633 if (d
instanceof LayerDrawable
) {
634 progressDrawable
= ((LayerDrawable
) d
).findDrawableByLayerId(id
);
637 final int level
= (int) (scale
* MAX_LEVEL
);
638 (progressDrawable
!= null ? progressDrawable
: d
).setLevel(level
);
643 if (callBackToApp
&& id
== android
.R
.id
.progress
) {
644 onProgressRefresh(scale
, fromUser
);
648 void onProgressRefresh(float scale
, boolean fromUser
) {
649 if (mAccessibilityManager
.isEnabled()) {
650 scheduleAccessibilityEventSender();
654 private synchronized void refreshProgress(int id
, int progress
, boolean fromUser
) {
655 if (mUiThreadId
== Thread
.currentThread().getId()) {
656 doRefreshProgress(id
, progress
, fromUser
, true
);
658 RefreshProgressRunnable r
;
659 if (mRefreshProgressRunnable
!= null
) {
660 // Use cached RefreshProgressRunnable if available
661 r
= mRefreshProgressRunnable
;
663 mRefreshProgressRunnable
= null
;
664 r
.setup(id
, progress
, fromUser
);
667 r
= new RefreshProgressRunnable(id
, progress
, fromUser
);
674 * <p>Set the current progress to the specified value. Does not do anything
675 * if the progress bar is in indeterminate mode.</p>
677 * @param progress the new progress, between 0 and {@link #getMax()}
679 * @see #setIndeterminate(boolean)
680 * @see #isIndeterminate()
681 * @see #getProgress()
682 * @see #incrementProgressBy(int)
684 public synchronized void setProgress(int progress
) {
685 setProgress(progress
, false
);
688 synchronized void setProgress(int progress
, boolean fromUser
) {
689 if (mIndeterminate
) {
697 if (progress
> mMax
) {
701 if (progress
!= mProgress
) {
702 mProgress
= progress
;
703 refreshProgress(android
.R
.id
.progress
, mProgress
, fromUser
);
709 * Set the current secondary progress to the specified value. Does not do
710 * anything if the progress bar is in indeterminate mode.
713 * @param secondaryProgress the new secondary progress, between 0 and {@link #getMax()}
714 * @see #setIndeterminate(boolean)
715 * @see #isIndeterminate()
716 * @see #getSecondaryProgress()
717 * @see #incrementSecondaryProgressBy(int)
719 public synchronized void setSecondaryProgress(int secondaryProgress
) {
720 if (mIndeterminate
) {
724 if (secondaryProgress
< 0) {
725 secondaryProgress
= 0;
728 if (secondaryProgress
> mMax
) {
729 secondaryProgress
= mMax
;
732 if (secondaryProgress
!= mSecondaryProgress
) {
733 mSecondaryProgress
= secondaryProgress
;
734 refreshProgress(android
.R
.id
.secondaryProgress
, mSecondaryProgress
, false
);
739 * <p>Get the progress bar's current level of progress. Return 0 when the
740 * progress bar is in indeterminate mode.</p>
742 * @return the current progress, between 0 and {@link #getMax()}
744 * @see #setIndeterminate(boolean)
745 * @see #isIndeterminate()
746 * @see #setProgress(int)
750 @ViewDebug.ExportedProperty(category
= "progress")
751 public synchronized int getProgress() {
752 return mIndeterminate ?
0 : mProgress
;
756 * <p>Get the progress bar's current level of secondary progress. Return 0 when the
757 * progress bar is in indeterminate mode.</p>
759 * @return the current secondary progress, between 0 and {@link #getMax()}
761 * @see #setIndeterminate(boolean)
762 * @see #isIndeterminate()
763 * @see #setSecondaryProgress(int)
767 @ViewDebug.ExportedProperty(category
= "progress")
768 public synchronized int getSecondaryProgress() {
769 return mIndeterminate ?
0 : mSecondaryProgress
;
773 * <p>Return the upper limit of this progress bar's range.</p>
775 * @return a positive integer
778 * @see #getProgress()
779 * @see #getSecondaryProgress()
781 @ViewDebug.ExportedProperty(category
= "progress")
782 public synchronized int getMax() {
787 * <p>Set the range of the progress bar to 0...<tt>max</tt>.</p>
789 * @param max the upper range of this progress bar
792 * @see #setProgress(int)
793 * @see #setSecondaryProgress(int)
795 public synchronized void setMax(int max
) {
803 if (mProgress
> max
) {
806 refreshProgress(android
.R
.id
.progress
, mProgress
, false
);
811 * <p>Increase the progress bar's progress by the specified amount.</p>
813 * @param diff the amount by which the progress must be increased
815 * @see #setProgress(int)
817 public synchronized final void incrementProgressBy(int diff
) {
818 setProgress(mProgress
+ diff
);
822 * <p>Increase the progress bar's secondary progress by the specified amount.</p>
824 * @param diff the amount by which the secondary progress must be increased
826 * @see #setSecondaryProgress(int)
828 public synchronized final void incrementSecondaryProgressBy(int diff
) {
829 setSecondaryProgress(mSecondaryProgress
+ diff
);
833 * <p>Start the indeterminate progress animation.</p>
835 void startAnimation() {
836 if (getVisibility() != VISIBLE
) {
840 if (mIndeterminateDrawable
instanceof Animatable
) {
841 mShouldStartAnimationDrawable
= true
;
844 if (mInterpolator
== null
) {
845 mInterpolator
= new LinearInterpolator();
848 mTransformation
= new Transformation();
849 mAnimation
= new AlphaAnimation(0.0f
, 1.0f
);
850 mAnimation
.setRepeatMode(mBehavior
);
851 mAnimation
.setRepeatCount(Animation
.INFINITE
);
852 mAnimation
.setDuration(mDuration
);
853 mAnimation
.setInterpolator(mInterpolator
);
854 mAnimation
.setStartTime(Animation
.START_ON_FIRST_FRAME
);
860 * <p>Stop the indeterminate progress animation.</p>
862 void stopAnimation() {
864 mTransformation
= null
;
865 if (mIndeterminateDrawable
instanceof Animatable
) {
866 ((Animatable
) mIndeterminateDrawable
).stop();
867 mShouldStartAnimationDrawable
= false
;
873 * Sets the acceleration curve for the indeterminate animation.
874 * The interpolator is loaded as a resource from the specified context.
876 * @param context The application environment
877 * @param resID The resource identifier of the interpolator to load
879 public void setInterpolator(Context context
, int resID
) {
880 setInterpolator(AnimationUtils
.loadInterpolator(context
, resID
));
884 * Sets the acceleration curve for the indeterminate animation.
885 * Defaults to a linear interpolation.
887 * @param interpolator The interpolator which defines the acceleration curve
889 public void setInterpolator(Interpolator interpolator
) {
890 mInterpolator
= interpolator
;
894 * Gets the acceleration curve type for the indeterminate animation.
896 * @return the {@link Interpolator} associated to this animation
898 public Interpolator
getInterpolator() {
899 return mInterpolator
;
903 public void setVisibility(int v
) {
904 if (getVisibility() != v
) {
905 super.setVisibility(v
);
907 if (mIndeterminate
) {
908 // let's be nice with the UI thread
909 if (v
== GONE
|| v
== INVISIBLE
) {
919 protected void onVisibilityChanged(View changedView
, int visibility
) {
920 super.onVisibilityChanged(changedView
, visibility
);
922 if (mIndeterminate
) {
923 // let's be nice with the UI thread
924 if (visibility
== GONE
|| visibility
== INVISIBLE
) {
933 public void invalidateDrawable(Drawable dr
) {
935 if (verifyDrawable(dr
)) {
936 final Rect dirty
= dr
.getBounds();
937 final int scrollX
= getScrollX() + getPaddingLeft();
938 final int scrollY
= getScrollY() + getPaddingTop();
940 invalidate(dirty
.left
+ scrollX
, dirty
.top
+ scrollY
,
941 dirty
.right
+ scrollX
, dirty
.bottom
+ scrollY
);
943 super.invalidateDrawable(dr
);
952 public int getResolvedLayoutDirection(Drawable who) {
953 return (who == mProgressDrawable || who == mIndeterminateDrawable) ?
954 getResolvedLayoutDirection() : super.getResolvedLayoutDirection(who);
959 protected void onSizeChanged(int w
, int h
, int oldw
, int oldh
) {
960 updateDrawableBounds(w
, h
);
963 private void updateDrawableBounds(int w
, int h
) {
964 // onDraw will translate the canvas so we draw starting at 0,0
965 int right
= w
- getPaddingRight() - getPaddingLeft();
966 int bottom
= h
- getPaddingBottom() - getPaddingTop();
970 if (mIndeterminateDrawable
!= null
) {
971 // Aspect ratio logic does not apply to AnimationDrawables
972 if (mOnlyIndeterminate
&& !(mIndeterminateDrawable
instanceof AnimationDrawable
)) {
973 // Maintain aspect ratio. Certain kinds of animated drawables
974 // get very confused otherwise.
975 final int intrinsicWidth
= mIndeterminateDrawable
.getIntrinsicWidth();
976 final int intrinsicHeight
= mIndeterminateDrawable
.getIntrinsicHeight();
977 final float intrinsicAspect
= (float) intrinsicWidth
/ intrinsicHeight
;
978 final float boundAspect
= (float) w
/ h
;
979 if (intrinsicAspect
!= boundAspect
) {
980 if (boundAspect
> intrinsicAspect
) {
981 // New width is larger. Make it smaller to match height.
982 final int width
= (int) (h
* intrinsicAspect
);
983 left
= (w
- width
) / 2;
984 right
= left
+ width
;
986 // New height is larger. Make it smaller to match width.
987 final int height
= (int) (w
* (1 / intrinsicAspect
));
988 top
= (h
- height
) / 2;
989 bottom
= top
+ height
;
993 mIndeterminateDrawable
.setBounds(0, 0, right
- left
, bottom
- top
);
994 mIndeterminateRealLeft
= left
;
995 mIndeterminateRealTop
= top
;
998 if (mProgressDrawable
!= null
) {
999 mProgressDrawable
.setBounds(0, 0, right
, bottom
);
1004 protected synchronized void onDraw(Canvas canvas
) {
1005 super.onDraw(canvas
);
1007 Drawable d
= mCurrentDrawable
;
1009 // Translate canvas so a indeterminate circular progress bar with padding
1010 // rotates properly in its animation
1012 canvas
.translate(getPaddingLeft() + mIndeterminateRealLeft
, getPaddingTop() + mIndeterminateRealTop
);
1013 long time
= getDrawingTime();
1014 if (mAnimation
!= null
) {
1015 mAnimation
.getTransformation(time
, mTransformation
);
1016 float scale
= mTransformation
.getAlpha();
1019 d
.setLevel((int) (scale
* MAX_LEVEL
));
1023 if (SystemClock
.uptimeMillis() - mLastDrawTime
>= mAnimationResolution
) {
1024 mLastDrawTime
= SystemClock
.uptimeMillis();
1025 postInvalidateDelayed(mAnimationResolution
);
1030 if (mShouldStartAnimationDrawable
&& d
instanceof Animatable
) {
1031 ((Animatable
) d
).start();
1032 mShouldStartAnimationDrawable
= false
;
1038 protected synchronized void onMeasure(int widthMeasureSpec
, int heightMeasureSpec
) {
1039 Drawable d
= mCurrentDrawable
;
1044 dw
= Math
.max(mMinWidth
, Math
.min(mMaxWidth
, d
.getIntrinsicWidth()));
1045 dh
= Math
.max(mMinHeight
, Math
.min(mMaxHeight
, d
.getIntrinsicHeight()));
1047 updateDrawableState();
1048 dw
+= getPaddingLeft() + getPaddingRight();
1049 dh
+= getPaddingTop() + getPaddingBottom();
1052 setMeasuredDimension(View
.resolveSizeAndState(dw
, widthMeasureSpec
, 0),
1053 View
.resolveSizeAndState(dh
, heightMeasureSpec
, 0));
1055 setMeasuredDimension(View
.resolveSize(dw
, widthMeasureSpec
),
1056 View
.resolveSize(dh
, heightMeasureSpec
));
1061 protected void drawableStateChanged() {
1062 super.drawableStateChanged();
1063 updateDrawableState();
1066 private void updateDrawableState() {
1067 int[] state
= getDrawableState();
1069 if (mProgressDrawable
!= null
&& mProgressDrawable
.isStateful()) {
1070 mProgressDrawable
.setState(state
);
1073 if (mIndeterminateDrawable
!= null
&& mIndeterminateDrawable
.isStateful()) {
1074 mIndeterminateDrawable
.setState(state
);
1078 static class SavedState
extends BaseSavedState
{
1080 int secondaryProgress
;
1083 * Constructor called from {@link IcsProgressBar#onSaveInstanceState()}
1085 SavedState(Parcelable superState
) {
1090 * Constructor called from {@link #CREATOR}
1092 private SavedState(Parcel
in) {
1094 progress
= in.readInt();
1095 secondaryProgress
= in.readInt();
1099 public void writeToParcel(Parcel out
, int flags
) {
1100 super.writeToParcel(out
, flags
);
1101 out
.writeInt(progress
);
1102 out
.writeInt(secondaryProgress
);
1105 public static final Parcelable
.Creator
<SavedState
> CREATOR
1106 = new Parcelable
.Creator
<SavedState
>() {
1107 public SavedState
createFromParcel(Parcel
in) {
1108 return new SavedState(in);
1111 public SavedState
[] newArray(int size
) {
1112 return new SavedState
[size
];
1118 public Parcelable
onSaveInstanceState() {
1119 // Force our ancestor class to save its state
1120 Parcelable superState
= super.onSaveInstanceState();
1121 SavedState ss
= new SavedState(superState
);
1123 ss
.progress
= mProgress
;
1124 ss
.secondaryProgress
= mSecondaryProgress
;
1130 public void onRestoreInstanceState(Parcelable state
) {
1131 SavedState ss
= (SavedState
) state
;
1132 super.onRestoreInstanceState(ss
.getSuperState());
1134 setProgress(ss
.progress
);
1135 setSecondaryProgress(ss
.secondaryProgress
);
1139 protected void onAttachedToWindow() {
1140 super.onAttachedToWindow();
1141 if (mIndeterminate
) {
1147 protected void onDetachedFromWindow() {
1148 if (mIndeterminate
) {
1151 if(mRefreshProgressRunnable
!= null
) {
1152 removeCallbacks(mRefreshProgressRunnable
);
1154 if (mAccessibilityEventSender
!= null
) {
1155 removeCallbacks(mAccessibilityEventSender
);
1157 // This should come after stopAnimation(), otherwise an invalidate message remains in the
1158 // queue, which can prevent the entire view hierarchy from being GC'ed during a rotation
1159 super.onDetachedFromWindow();
1163 public void onInitializeAccessibilityEvent(AccessibilityEvent event
) {
1164 super.onInitializeAccessibilityEvent(event
);
1165 event
.setItemCount(mMax
);
1166 event
.setCurrentItemIndex(mProgress
);
1170 * Schedule a command for sending an accessibility event.
1172 * Note: A command is used to ensure that accessibility events
1173 * are sent at most one in a given time frame to save
1174 * system resources while the progress changes quickly.
1176 private void scheduleAccessibilityEventSender() {
1177 if (mAccessibilityEventSender
== null
) {
1178 mAccessibilityEventSender
= new AccessibilityEventSender();
1180 removeCallbacks(mAccessibilityEventSender
);
1182 postDelayed(mAccessibilityEventSender
, TIMEOUT_SEND_ACCESSIBILITY_EVENT
);
1186 * Command for sending an accessibility event.
1188 private class AccessibilityEventSender
implements Runnable
{
1190 sendAccessibilityEvent(AccessibilityEvent
.TYPE_VIEW_SELECTED
);