Pass the new progress when updating a ProgressBar

There was a race condition where the update for a progress change
from the user could pass the wrong progress value causing apps to
treat a non-user update as a user update.

bug:18515012
Change-Id: Ia62a1d07cd15f99effbf644642307c71049748f2
This commit is contained in:
RoboErik
2015-02-11 13:52:05 -08:00
parent 7d85bc4c43
commit 5b07143e36
4 changed files with 105 additions and 104 deletions

View File

@@ -381,8 +381,8 @@ public abstract class AbsSeekBar extends ProgressBar {
}
@Override
void onProgressRefresh(float scale, boolean fromUser) {
super.onProgressRefresh(scale, fromUser);
void onProgressRefresh(float scale, boolean fromUser, int progress) {
super.onProgressRefresh(scale, fromUser, progress);
final Drawable thumb = mThumb;
if (thumb != null) {

View File

@@ -18,6 +18,7 @@ package android.widget;
import android.annotation.Nullable;
import android.graphics.PorterDuff;
import com.android.internal.R;
import android.content.Context;
@@ -64,8 +65,8 @@ import java.util.ArrayList;
/**
* <p>
* Visual indicator of progress in some operation. Displays a bar to the user
* representing how far the operation has progressed; the application can
* change the amount of progress (modifying the length of the bar) as it moves
* representing how far the operation has progressed; the application can
* change the amount of progress (modifying the length of the bar) as it moves
* forward. There is also a secondary progress displayable on a progress bar
* which is useful for displaying intermediate progress, such as the buffer
* level during a streaming playback progress bar.
@@ -81,7 +82,7 @@ import java.util.ArrayList;
* <p>The following code example shows how a progress bar can be used from
* a worker thread to update the user interface to notify the user of progress:
* </p>
*
*
* <pre>
* public class MyActivity extends Activity {
* private static final int PROGRESS = 0x1;
@@ -169,13 +170,13 @@ import java.util.ArrayList;
* </ul>
* <p>The "inverse" styles provide an inverse color scheme for the spinner, which may be necessary
* if your application uses a light colored theme (a white background).</p>
*
* <p><strong>XML attributes</b></strong>
* <p>
* See {@link android.R.styleable#ProgressBar ProgressBar Attributes},
*
* <p><strong>XML attributes</b></strong>
* <p>
* See {@link android.R.styleable#ProgressBar ProgressBar Attributes},
* {@link android.R.styleable#View View Attributes}
* </p>
*
*
* @attr ref android.R.styleable#ProgressBar_animationResolution
* @attr ref android.R.styleable#ProgressBar_indeterminate
* @attr ref android.R.styleable#ProgressBar_indeterminateBehavior
@@ -244,7 +245,7 @@ public class ProgressBar extends View {
public ProgressBar(Context context) {
this(context, null);
}
public ProgressBar(Context context, AttributeSet attrs) {
this(context, attrs, com.android.internal.R.attr.progressBarStyle);
}
@@ -261,9 +262,9 @@ public class ProgressBar extends View {
final TypedArray a = context.obtainStyledAttributes(
attrs, R.styleable.ProgressBar, defStyleAttr, defStyleRes);
mNoInvalidate = true;
final Drawable progressDrawable = a.getDrawable(R.styleable.ProgressBar_progressDrawable);
if (progressDrawable != null) {
// Calling this method can set mMaxHeight, make sure the corresponding
@@ -282,11 +283,11 @@ public class ProgressBar extends View {
mBehavior = a.getInt(R.styleable.ProgressBar_indeterminateBehavior, mBehavior);
final int resID = a.getResourceId(
com.android.internal.R.styleable.ProgressBar_interpolator,
com.android.internal.R.styleable.ProgressBar_interpolator,
android.R.anim.linear_interpolator); // default to linear interpolator
if (resID > 0) {
setInterpolator(context, resID);
}
}
setMax(a.getInt(R.styleable.ProgressBar_max, mMax));
@@ -399,12 +400,12 @@ public class ProgressBar extends View {
* traverse layer and state list drawables.
*/
private Drawable tileify(Drawable drawable, boolean clip) {
if (drawable instanceof LayerDrawable) {
LayerDrawable background = (LayerDrawable) drawable;
final int N = background.getNumberOfLayers();
Drawable[] outDrawables = new Drawable[N];
for (int i = 0; i < N; i++) {
int id = background.getId(i);
outDrawables[i] = tileify(background.getDrawable(i),
@@ -412,13 +413,13 @@ public class ProgressBar extends View {
}
LayerDrawable newBg = new LayerDrawable(outDrawables);
for (int i = 0; i < N; i++) {
newBg.setId(i, background.getId(i));
}
return newBg;
} else if (drawable instanceof StateListDrawable) {
StateListDrawable in = (StateListDrawable) drawable;
StateListDrawable out = new StateListDrawable();
@@ -427,7 +428,7 @@ public class ProgressBar extends View {
out.addState(in.getStateSet(i), tileify(in.getStateDrawable(i), clip));
}
return out;
} else if (drawable instanceof BitmapDrawable) {
final BitmapDrawable bitmap = (BitmapDrawable) drawable;
final Bitmap tileBitmap = bitmap.getBitmap();
@@ -448,7 +449,7 @@ public class ProgressBar extends View {
return clip ? new ClipDrawable(
shapeDrawable, Gravity.LEFT, ClipDrawable.HORIZONTAL) : shapeDrawable;
}
return drawable;
}
@@ -456,7 +457,7 @@ public class ProgressBar extends View {
final float[] roundedCorners = new float[] { 5, 5, 5, 5, 5, 5, 5, 5 };
return new RoundRectShape(roundedCorners, null, null);
}
/**
* Convert a AnimationDrawable for use as a barberpole animation.
* Each frame of the animation is wrapped in a ClipDrawable and
@@ -468,7 +469,7 @@ public class ProgressBar extends View {
final int N = background.getNumberOfFrames();
AnimationDrawable newBg = new AnimationDrawable();
newBg.setOneShot(background.isOneShot());
for (int i = 0; i < N; i++) {
Drawable frame = tileify(background.getFrame(i), true);
frame.setLevel(10000);
@@ -479,7 +480,7 @@ public class ProgressBar extends View {
}
return drawable;
}
/**
* <p>
* Initialize the progress bar's default values:
@@ -520,7 +521,7 @@ public class ProgressBar extends View {
* <p>Change the indeterminate mode for this progress bar. In indeterminate
* mode, the progress is ignored and the progress bar shows an infinite
* animation instead.</p>
*
*
* If this progress bar's style only supports indeterminate mode (such as the circular
* progress bars), then this will be ignored.
*
@@ -699,7 +700,7 @@ public class ProgressBar extends View {
setIndeterminateDrawable(d);
}
/**
* <p>Get the drawable used to draw the progress bar in
* progress mode.</p>
@@ -1135,7 +1136,7 @@ public class ProgressBar extends View {
setProgressDrawable(d);
}
/**
* @return The drawable currently used to draw the progress bar
*/
@@ -1214,7 +1215,7 @@ public class ProgressBar extends View {
rd.fromUser = fromUser;
return rd;
}
public void recycle() {
sPool.release(this);
}
@@ -1257,13 +1258,13 @@ public class ProgressBar extends View {
} else {
invalidate();
}
if (callBackToApp && id == R.id.progress) {
onProgressRefresh(scale, fromUser);
onProgressRefresh(scale, fromUser, progress);
}
}
void onProgressRefresh(float scale, boolean fromUser) {
void onProgressRefresh(float scale, boolean fromUser, int progress) {
if (AccessibilityManager.getInstance(mContext).isEnabled()) {
scheduleAccessibilityEventSender();
}
@@ -1285,7 +1286,7 @@ public class ProgressBar extends View {
}
}
}
/**
* <p>Set the current progress to the specified value. Does not do anything
* if the progress bar is in indeterminate mode.</p>
@@ -1295,13 +1296,13 @@ public class ProgressBar extends View {
* @see #setIndeterminate(boolean)
* @see #isIndeterminate()
* @see #getProgress()
* @see #incrementProgressBy(int)
* @see #incrementProgressBy(int)
*/
@android.view.RemotableViewMethod
public synchronized void setProgress(int progress) {
setProgress(progress, false);
}
@android.view.RemotableViewMethod
synchronized void setProgress(int progress, boolean fromUser) {
if (mIndeterminate) {
@@ -1327,7 +1328,7 @@ public class ProgressBar extends View {
* Set the current secondary progress to the specified value. Does not do
* anything if the progress bar is in indeterminate mode.
* </p>
*
*
* @param secondaryProgress the new secondary progress, between 0 and {@link #getMax()}
* @see #setIndeterminate(boolean)
* @see #isIndeterminate()
@@ -1408,8 +1409,8 @@ public class ProgressBar extends View {
* @param max the upper range of this progress bar
*
* @see #getMax()
* @see #setProgress(int)
* @see #setSecondaryProgress(int)
* @see #setProgress(int)
* @see #setSecondaryProgress(int)
*/
@android.view.RemotableViewMethod
public synchronized void setMax(int max) {
@@ -1426,13 +1427,13 @@ public class ProgressBar extends View {
refreshProgress(R.id.progress, mProgress, false);
}
}
/**
* <p>Increase the progress bar's progress by the specified amount.</p>
*
* @param diff the amount by which the progress must be increased
*
* @see #setProgress(int)
* @see #setProgress(int)
*/
public synchronized final void incrementProgressBy(int diff) {
setProgress(mProgress + diff);
@@ -1443,7 +1444,7 @@ public class ProgressBar extends View {
*
* @param diff the amount by which the secondary progress must be increased
*
* @see #setSecondaryProgress(int)
* @see #setSecondaryProgress(int)
*/
public synchronized final void incrementSecondaryProgressBy(int diff) {
setSecondaryProgress(mSecondaryProgress + diff);
@@ -1466,13 +1467,13 @@ public class ProgressBar extends View {
if (mInterpolator == null) {
mInterpolator = new LinearInterpolator();
}
if (mTransformation == null) {
mTransformation = new Transformation();
} else {
mTransformation.clear();
}
if (mAnimation == null) {
mAnimation = new AlphaAnimation(0.0f, 1.0f);
} else {
@@ -1623,7 +1624,7 @@ public class ProgressBar extends View {
}
mIndeterminateDrawable.setBounds(left, top, right, bottom);
}
if (mProgressDrawable != null) {
mProgressDrawable.setBounds(0, 0, right, bottom);
}
@@ -1693,20 +1694,20 @@ public class ProgressBar extends View {
setMeasuredDimension(resolveSizeAndState(dw, widthMeasureSpec, 0),
resolveSizeAndState(dh, heightMeasureSpec, 0));
}
@Override
protected void drawableStateChanged() {
super.drawableStateChanged();
updateDrawableState();
}
private void updateDrawableState() {
int[] state = getDrawableState();
if (mProgressDrawable != null && mProgressDrawable.isStateful()) {
mProgressDrawable.setState(state);
}
if (mIndeterminateDrawable != null && mIndeterminateDrawable.isStateful()) {
mIndeterminateDrawable.setState(state);
}
@@ -1728,14 +1729,14 @@ public class ProgressBar extends View {
static class SavedState extends BaseSavedState {
int progress;
int secondaryProgress;
/**
* Constructor called from {@link ProgressBar#onSaveInstanceState()}
*/
SavedState(Parcelable superState) {
super(superState);
}
/**
* Constructor called from {@link #CREATOR}
*/
@@ -1769,10 +1770,10 @@ public class ProgressBar extends View {
// Force our ancestor class to save its state
Parcelable superState = super.onSaveInstanceState();
SavedState ss = new SavedState(superState);
ss.progress = mProgress;
ss.secondaryProgress = mSecondaryProgress;
return ss;
}
@@ -1780,7 +1781,7 @@ public class ProgressBar extends View {
public void onRestoreInstanceState(Parcelable state) {
SavedState ss = (SavedState) state;
super.onRestoreInstanceState(ss.getSuperState());
setProgress(ss.progress);
setSecondaryProgress(ss.secondaryProgress);
}

View File

@@ -43,7 +43,7 @@ import com.android.internal.R;
* <p>
* The secondary progress should not be modified by the client as it is used
* internally as the background for a fractionally filled star.
*
*
* @attr ref android.R.styleable#RatingBar_numStars
* @attr ref android.R.styleable#RatingBar_rating
* @attr ref android.R.styleable#RatingBar_stepSize
@@ -58,14 +58,14 @@ public class RatingBar extends AbsSeekBar {
* programmatically.
*/
public interface OnRatingBarChangeListener {
/**
* Notification that the rating has changed. Clients can use the
* fromUser parameter to distinguish user-initiated changes from those
* that occurred programmatically. This will not be called continuously
* while the user is dragging, only when the user finalizes a rating by
* lifting the touch.
*
*
* @param ratingBar The RatingBar whose rating has changed.
* @param rating The current rating. This will be in the range
* 0..numStars.
@@ -79,9 +79,9 @@ public class RatingBar extends AbsSeekBar {
private int mNumStars = 5;
private int mProgressOnStartTracking;
private OnRatingBarChangeListener mOnRatingBarChangeListener;
public RatingBar(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
@@ -98,19 +98,19 @@ public class RatingBar extends AbsSeekBar {
a.recycle();
if (numStars > 0 && numStars != mNumStars) {
setNumStars(numStars);
setNumStars(numStars);
}
if (stepSize >= 0) {
setStepSize(stepSize);
} else {
setStepSize(0.5f);
}
if (rating >= 0) {
setRating(rating);
}
// A touch inside a star fill up to that fractional area (slightly more
// than 1 so boundaries round up).
mTouchProgressOffset = 1.1f;
@@ -123,16 +123,16 @@ public class RatingBar extends AbsSeekBar {
public RatingBar(Context context) {
this(context, null);
}
/**
* Sets the listener to be called when the rating changes.
*
*
* @param listener The listener.
*/
public void setOnRatingBarChangeListener(OnRatingBarChangeListener listener) {
mOnRatingBarChangeListener = listener;
}
/**
* @return The listener (may be null) that is listening for rating change
* events.
@@ -144,7 +144,7 @@ public class RatingBar extends AbsSeekBar {
/**
* Whether this rating bar should only be an indicator (thus non-changeable
* by the user).
*
*
* @param isIndicator Whether it should be an indicator.
*
* @attr ref android.R.styleable#RatingBar_isIndicator
@@ -153,7 +153,7 @@ public class RatingBar extends AbsSeekBar {
mIsUserSeekable = !isIndicator;
setFocusable(!isIndicator);
}
/**
* @return Whether this rating bar is only an indicator.
*
@@ -162,21 +162,21 @@ public class RatingBar extends AbsSeekBar {
public boolean isIndicator() {
return !mIsUserSeekable;
}
/**
* Sets the number of stars to show. In order for these to be shown
* properly, it is recommended the layout width of this widget be wrap
* content.
*
*
* @param numStars The number of stars.
*/
public void setNumStars(final int numStars) {
if (numStars <= 0) {
return;
}
mNumStars = numStars;
// This causes the width to change, so re-layout
requestLayout();
}
@@ -188,10 +188,10 @@ public class RatingBar extends AbsSeekBar {
public int getNumStars() {
return mNumStars;
}
/**
* Sets the rating (the number of stars filled).
*
*
* @param rating The rating to set.
*/
public void setRating(float rating) {
@@ -200,16 +200,16 @@ public class RatingBar extends AbsSeekBar {
/**
* Gets the current rating (number of stars filled).
*
*
* @return The current rating.
*/
public float getRating() {
return getProgress() / getProgressPerStar();
return getProgress() / getProgressPerStar();
}
/**
* Sets the step size (granularity) of this rating bar.
*
*
* @param stepSize The step size of this rating bar. For example, if
* half-star granularity is wanted, this would be 0.5.
*/
@@ -217,7 +217,7 @@ public class RatingBar extends AbsSeekBar {
if (stepSize <= 0) {
return;
}
final float newMax = mNumStars / stepSize;
final int newProgress = (int) (newMax / getMax() * getProgress());
setMax((int) newMax);
@@ -226,13 +226,13 @@ public class RatingBar extends AbsSeekBar {
/**
* Gets the step size of this rating bar.
*
*
* @return The step size.
*/
public float getStepSize() {
return (float) getNumStars() / getMax();
}
/**
* @return The amount of progress that fits into a star
*/
@@ -251,12 +251,12 @@ public class RatingBar extends AbsSeekBar {
}
@Override
void onProgressRefresh(float scale, boolean fromUser) {
super.onProgressRefresh(scale, fromUser);
void onProgressRefresh(float scale, boolean fromUser, int progress) {
super.onProgressRefresh(scale, fromUser, progress);
// Keep secondary progress in sync with primary
updateSecondaryProgress(getProgress());
updateSecondaryProgress(progress);
if (!fromUser) {
// Callback for non-user rating changes
dispatchRatingChange(false);
@@ -267,7 +267,7 @@ public class RatingBar extends AbsSeekBar {
* The secondary progress is used to differentiate the background of a
* partially filled star. This method keeps the secondary progress in sync
* with the progress.
*
*
* @param progress The primary progress level.
*/
private void updateSecondaryProgress(int progress) {
@@ -278,11 +278,11 @@ public class RatingBar extends AbsSeekBar {
setSecondaryProgress(secondaryProgress);
}
}
@Override
protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (mSampleTile != null) {
// TODO: Once ProgressBar's TODOs are gone, this can be done more
// cleanly than mSampleTile
@@ -295,7 +295,7 @@ public class RatingBar extends AbsSeekBar {
@Override
void onStartTrackingTouch() {
mProgressOnStartTracking = getProgress();
super.onStartTrackingTouch();
}
@@ -327,7 +327,7 @@ public class RatingBar extends AbsSeekBar {
if (max <= 0) {
return;
}
super.setMax(max);
}

View File

@@ -26,7 +26,7 @@ import android.view.accessibility.AccessibilityNodeInfo;
/**
* A SeekBar is an extension of ProgressBar that adds a draggable thumb. The user can touch
* the thumb and drag left or right to set the current progress level or use the arrow keys.
* Placing focusable widgets to the left or right of a SeekBar is discouraged.
* Placing focusable widgets to the left or right of a SeekBar is discouraged.
* <p>
* Clients of the SeekBar can attach a {@link SeekBar.OnSeekBarChangeListener} to
* be notified of the user's actions.
@@ -42,39 +42,39 @@ public class SeekBar extends AbsSeekBar {
* programmatically.
*/
public interface OnSeekBarChangeListener {
/**
* Notification that the progress level has changed. Clients can use the fromUser parameter
* to distinguish user-initiated changes from those that occurred programmatically.
*
*
* @param seekBar The SeekBar whose progress has changed
* @param progress The current progress level. This will be in the range 0..max where max
* was set by {@link ProgressBar#setMax(int)}. (The default value for max is 100.)
* @param fromUser True if the progress change was initiated by the user.
*/
void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser);
/**
* Notification that the user has started a touch gesture. Clients may want to use this
* to disable advancing the seekbar.
* to disable advancing the seekbar.
* @param seekBar The SeekBar in which the touch gesture began
*/
void onStartTrackingTouch(SeekBar seekBar);
/**
* Notification that the user has finished a touch gesture. Clients may want to use this
* to re-enable advancing the seekbar.
* to re-enable advancing the seekbar.
* @param seekBar The SeekBar in which the touch gesture began
*/
void onStopTrackingTouch(SeekBar seekBar);
}
private OnSeekBarChangeListener mOnSeekBarChangeListener;
public SeekBar(Context context) {
this(context, null);
}
public SeekBar(Context context, AttributeSet attrs) {
this(context, attrs, com.android.internal.R.attr.seekBarStyle);
}
@@ -88,26 +88,26 @@ public class SeekBar extends AbsSeekBar {
}
@Override
void onProgressRefresh(float scale, boolean fromUser) {
super.onProgressRefresh(scale, fromUser);
void onProgressRefresh(float scale, boolean fromUser, int progress) {
super.onProgressRefresh(scale, fromUser, progress);
if (mOnSeekBarChangeListener != null) {
mOnSeekBarChangeListener.onProgressChanged(this, getProgress(), fromUser);
mOnSeekBarChangeListener.onProgressChanged(this, progress, fromUser);
}
}
/**
* Sets a listener to receive notifications of changes to the SeekBar's progress level. Also
* provides notifications of when the user starts and stops a touch gesture within the SeekBar.
*
*
* @param l The seek bar notification listener
*
*
* @see SeekBar.OnSeekBarChangeListener
*/
public void setOnSeekBarChangeListener(OnSeekBarChangeListener l) {
mOnSeekBarChangeListener = l;
}
@Override
void onStartTrackingTouch() {
super.onStartTrackingTouch();
@@ -115,7 +115,7 @@ public class SeekBar extends AbsSeekBar {
mOnSeekBarChangeListener.onStartTrackingTouch(this);
}
}
@Override
void onStopTrackingTouch() {
super.onStopTrackingTouch();