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
);