package com.actionbarsherlock.internal.widget;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.Shader;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ClipDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.RoundRectShape;
import android.graphics.drawable.shapes.Shape;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.view.ViewDebug;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
import android.view.animation.Transformation;
import android.widget.RemoteViews.RemoteView;
@RemoteView
private static final int MAX_LEVEL = 10000;
private static final int ANIMATION_RESOLUTION = 200;
private static final int TIMEOUT_SEND_ACCESSIBILITY_EVENT = 200;
private static final int[] ProgressBar = new int[] {
android.R.attr.maxWidth,
android.R.attr.maxHeight,
android.R.attr.max,
android.R.attr.progress,
android.R.attr.secondaryProgress,
android.R.attr.indeterminate,
android.R.attr.indeterminateOnly,
android.R.attr.indeterminateDrawable,
android.R.attr.progressDrawable,
android.R.attr.indeterminateDuration,
android.R.attr.indeterminateBehavior,
android.R.attr.minWidth,
android.R.attr.minHeight,
android.R.attr.interpolator,
android.R.attr.animationResolution,
};
private static final int ProgressBar_maxWidth = 0;
private static final int ProgressBar_maxHeight = 1;
private static final int ProgressBar_max = 2;
private static final int ProgressBar_progress = 3;
private static final int ProgressBar_secondaryProgress = 4;
private static final int ProgressBar_indeterminate = 5;
private static final int ProgressBar_indeterminateOnly = 6;
private static final int ProgressBar_indeterminateDrawable = 7;
private static final int ProgressBar_progressDrawable = 8;
private static final int ProgressBar_indeterminateDuration = 9;
private static final int ProgressBar_indeterminateBehavior = 10;
private static final int ProgressBar_minWidth = 11;
private static final int ProgressBar_minHeight = 12;
private static final int ProgressBar_interpolator = 13;
private static final int ProgressBar_animationResolution = 14;
int mMinWidth;
int mMaxWidth;
int mMinHeight;
int mMaxHeight;
private int mProgress;
private int mSecondaryProgress;
private int mMax;
private int mBehavior;
private int mDuration;
private boolean mIndeterminate;
private boolean mOnlyIndeterminate;
private Transformation mTransformation;
private AlphaAnimation mAnimation;
private Drawable mIndeterminateDrawable;
private Drawable mProgressDrawable;
private Drawable mCurrentDrawable;
Bitmap mSampleTile;
private boolean mNoInvalidate;
private Interpolator mInterpolator;
private RefreshProgressRunnable mRefreshProgressRunnable;
private long mUiThreadId;
private boolean mShouldStartAnimationDrawable;
private long mLastDrawTime;
private boolean mInDrawing;
private int mAnimationResolution;
private AccessibilityManager mAccessibilityManager;
private AccessibilityEventSender mAccessibilityEventSender;
this(context, null);
}
this(context, attrs, android.R.attr.progressBarStyle);
}
public IcsProgressBar(Context context, AttributeSet attrs,
int defStyle) {
this(context, attrs, defStyle, 0);
}
public IcsProgressBar(Context context, AttributeSet attrs,
int defStyle,
int styleRes) {
super(context, attrs, defStyle);
mUiThreadId = Thread.currentThread().getId();
initProgressBar();
TypedArray a =
context.obtainStyledAttributes(attrs, ProgressBar, defStyle, styleRes);
mNoInvalidate = true;
Drawable drawable = a.getDrawable(ProgressBar_progressDrawable);
if (drawable != null) {
drawable = tileify(drawable, false);
setProgressDrawable(drawable);
}
mDuration = a.getInt(ProgressBar_indeterminateDuration, mDuration);
mMinWidth = a.getDimensionPixelSize(ProgressBar_minWidth, mMinWidth);
mMaxWidth = a.getDimensionPixelSize(ProgressBar_maxWidth, mMaxWidth);
mMinHeight = a.getDimensionPixelSize(ProgressBar_minHeight, mMinHeight);
mMaxHeight = a.getDimensionPixelSize(ProgressBar_maxHeight, mMaxHeight);
mBehavior = a.getInt(ProgressBar_indeterminateBehavior, mBehavior);
final int resID = a.getResourceId(
ProgressBar_interpolator,
android.R.anim.linear_interpolator);
if (resID > 0) {
setInterpolator(context, resID);
}
setMax(a.getInt(ProgressBar_max, mMax));
setProgress(a.getInt(ProgressBar_progress, mProgress));
setSecondaryProgress(
a.getInt(ProgressBar_secondaryProgress, mSecondaryProgress));
drawable = a.getDrawable(ProgressBar_indeterminateDrawable);
if (drawable != null) {
drawable = tileifyIndeterminate(drawable);
setIndeterminateDrawable(drawable);
}
mOnlyIndeterminate = a.getBoolean(
ProgressBar_indeterminateOnly, mOnlyIndeterminate);
mNoInvalidate = false;
setIndeterminate(mOnlyIndeterminate || a.getBoolean(
ProgressBar_indeterminate, mIndeterminate));
mAnimationResolution = a.getInteger(ProgressBar_animationResolution,
ANIMATION_RESOLUTION);
a.recycle();
mAccessibilityManager = (AccessibilityManager)context.getSystemService(Context.ACCESSIBILITY_SERVICE);
}
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),
(id == android.R.id.progress || id == android.R.id.secondaryProgress));
}
LayerDrawable newBg = new LayerDrawable(outDrawables);
for (int i = 0; i < N; i++) {
newBg.setId(i, background.getId(i));
}
return newBg;
} else if (drawable instanceof BitmapDrawable) {
final Bitmap tileBitmap = ((BitmapDrawable) drawable).getBitmap();
if (mSampleTile == null) {
mSampleTile = tileBitmap;
}
final ShapeDrawable shapeDrawable = new ShapeDrawable(getDrawableShape());
final BitmapShader bitmapShader = new BitmapShader(tileBitmap,
Shader.TileMode.REPEAT, Shader.TileMode.CLAMP);
shapeDrawable.getPaint().setShader(bitmapShader);
return (clip) ? new ClipDrawable(shapeDrawable, Gravity.LEFT,
ClipDrawable.HORIZONTAL) : shapeDrawable;
}
return drawable;
}
final float[] roundedCorners = new float[] { 5, 5, 5, 5, 5, 5, 5, 5 };
return new RoundRectShape(roundedCorners, null, null);
}
if (drawable instanceof AnimationDrawable) {
AnimationDrawable background = (AnimationDrawable) drawable;
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);
newBg.addFrame(frame, background.getDuration(i));
}
newBg.setLevel(10000);
drawable = newBg;
}
return drawable;
}
mMax = 100;
mProgress = 0;
mSecondaryProgress = 0;
mIndeterminate = false;
mOnlyIndeterminate = false;
mDuration = 4000;
mBehavior = AlphaAnimation.RESTART;
mMinWidth = 24;
mMaxWidth = 48;
mMinHeight = 24;
mMaxHeight = 48;
}
@ViewDebug.ExportedProperty(category = "progress")
return mIndeterminate;
}
if ((!mOnlyIndeterminate || !mIndeterminate) && indeterminate != mIndeterminate) {
mIndeterminate = indeterminate;
if (indeterminate) {
mCurrentDrawable = mIndeterminateDrawable;
startAnimation();
} else {
mCurrentDrawable = mProgressDrawable;
stopAnimation();
}
}
}
return mIndeterminateDrawable;
}
if (d != null) {
d.setCallback(this);
}
mIndeterminateDrawable = d;
if (mIndeterminate) {
mCurrentDrawable = d;
postInvalidate();
}
}
return mProgressDrawable;
}
boolean needUpdate;
if (mProgressDrawable != null && d != mProgressDrawable) {
mProgressDrawable.setCallback(null);
needUpdate = true;
} else {
needUpdate = false;
}
if (d != null) {
d.setCallback(this);
int drawableHeight = d.getMinimumHeight();
if (mMaxHeight < drawableHeight) {
mMaxHeight = drawableHeight;
requestLayout();
}
}
mProgressDrawable = d;
if (!mIndeterminate) {
mCurrentDrawable = d;
postInvalidate();
}
if (needUpdate) {
updateDrawableBounds(getWidth(), getHeight());
updateDrawableState();
doRefreshProgress(android.R.id.progress, mProgress, false, false);
doRefreshProgress(android.R.id.secondaryProgress, mSecondaryProgress, false, false);
}
}
return mCurrentDrawable;
}
@Override
return who == mProgressDrawable || who == mIndeterminateDrawable
|| super.verifyDrawable(who);
}
@Override
super.jumpDrawablesToCurrentState();
if (mProgressDrawable != null) mProgressDrawable.jumpToCurrentState();
if (mIndeterminateDrawable != null) mIndeterminateDrawable.jumpToCurrentState();
}
@Override
if (!mNoInvalidate) {
super.postInvalidate();
}
}
private int mId;
private int mProgress;
private boolean mFromUser;
mId = id;
mProgress = progress;
mFromUser = fromUser;
}
doRefreshProgress(mId, mProgress, mFromUser, true);
mRefreshProgressRunnable = this;
}
public void setup(
int id,
int progress,
boolean fromUser) {
mId = id;
mProgress = progress;
mFromUser = fromUser;
}
}
boolean callBackToApp) {
float scale = mMax > 0 ? (float) progress / (float) mMax : 0;
final Drawable d = mCurrentDrawable;
if (d != null) {
Drawable progressDrawable = null;
if (d instanceof LayerDrawable) {
progressDrawable = ((LayerDrawable) d).findDrawableByLayerId(id);
}
final int level = (int) (scale * MAX_LEVEL);
(progressDrawable != null ? progressDrawable : d).setLevel(level);
} else {
invalidate();
}
if (callBackToApp && id == android.R.id.progress) {
onProgressRefresh(scale, fromUser);
}
}
if (mAccessibilityManager.isEnabled()) {
scheduleAccessibilityEventSender();
}
}
private synchronized void refreshProgress(
int id,
int progress,
boolean fromUser) {
if (mUiThreadId == Thread.currentThread().getId()) {
doRefreshProgress(id, progress, fromUser, true);
} else {
RefreshProgressRunnable r;
if (mRefreshProgressRunnable != null) {
r = mRefreshProgressRunnable;
mRefreshProgressRunnable = null;
r.setup(id, progress, fromUser);
} else {
r = new RefreshProgressRunnable(id, progress, fromUser);
}
post(r);
}
}
setProgress(progress, false);
}
synchronized void setProgress(
int progress,
boolean fromUser) {
if (mIndeterminate) {
return;
}
if (progress < 0) {
progress = 0;
}
if (progress > mMax) {
progress = mMax;
}
if (progress != mProgress) {
mProgress = progress;
refreshProgress(android.R.id.progress, mProgress, fromUser);
}
}
if (mIndeterminate) {
return;
}
if (secondaryProgress < 0) {
secondaryProgress = 0;
}
if (secondaryProgress > mMax) {
secondaryProgress = mMax;
}
if (secondaryProgress != mSecondaryProgress) {
mSecondaryProgress = secondaryProgress;
refreshProgress(android.R.id.secondaryProgress, mSecondaryProgress, false);
}
}
@ViewDebug.ExportedProperty(category = "progress")
return mIndeterminate ? 0 : mProgress;
}
@ViewDebug.ExportedProperty(category = "progress")
return mIndeterminate ? 0 : mSecondaryProgress;
}
@ViewDebug.ExportedProperty(category = "progress")
public synchronized int getMax() {
return mMax;
}
public synchronized void setMax(
int max) {
if (max < 0) {
max = 0;
}
if (max != mMax) {
mMax = max;
postInvalidate();
if (mProgress > max) {
mProgress = max;
}
refreshProgress(android.R.id.progress, mProgress, false);
}
}
setProgress(mProgress + diff);
}
setSecondaryProgress(mSecondaryProgress + diff);
}
if (getVisibility() != VISIBLE) {
return;
}
if (mIndeterminateDrawable instanceof Animatable) {
mShouldStartAnimationDrawable = true;
mAnimation = null;
} else {
if (mInterpolator == null) {
mInterpolator = new LinearInterpolator();
}
mTransformation = new Transformation();
mAnimation = new AlphaAnimation(0.0f, 1.0f);
mAnimation.setRepeatMode(mBehavior);
mAnimation.setRepeatCount(Animation.INFINITE);
mAnimation.setDuration(mDuration);
mAnimation.setInterpolator(mInterpolator);
mAnimation.setStartTime(Animation.START_ON_FIRST_FRAME);
}
postInvalidate();
}
mAnimation = null;
mTransformation = null;
if (mIndeterminateDrawable instanceof Animatable) {
((Animatable) mIndeterminateDrawable).stop();
mShouldStartAnimationDrawable = false;
}
postInvalidate();
}
setInterpolator(AnimationUtils.loadInterpolator(context, resID));
}
mInterpolator = interpolator;
}
return mInterpolator;
}
@Override
if (getVisibility() != v) {
super.setVisibility(v);
if (mIndeterminate) {
if (v == GONE || v == INVISIBLE) {
stopAnimation();
} else {
startAnimation();
}
}
}
}
@Override
super.onVisibilityChanged(changedView, visibility);
if (mIndeterminate) {
if (visibility == GONE || visibility == INVISIBLE) {
stopAnimation();
} else {
startAnimation();
}
}
}
@Override
if (!mInDrawing) {
if (verifyDrawable(dr)) {
final Rect dirty = dr.getBounds();
final int scrollX = getScrollX() + getPaddingLeft();
final int scrollY = getScrollY() + getPaddingTop();
invalidate(dirty.left + scrollX, dirty.top + scrollY,
dirty.right + scrollX, dirty.bottom + scrollY);
} else {
super.invalidateDrawable(dr);
}
}
}
@Override
updateDrawableBounds(w, h);
}
int right = w - getPaddingRight() - getPaddingLeft();
int bottom = h - getPaddingBottom() - getPaddingTop();
int top = 0;
int left = 0;
if (mIndeterminateDrawable != null) {
if (mOnlyIndeterminate && !(mIndeterminateDrawable instanceof AnimationDrawable)) {
final int intrinsicWidth = mIndeterminateDrawable.getIntrinsicWidth();
final int intrinsicHeight = mIndeterminateDrawable.getIntrinsicHeight();
final float intrinsicAspect = (float) intrinsicWidth / intrinsicHeight;
final float boundAspect = (float) w / h;
if (intrinsicAspect != boundAspect) {
if (boundAspect > intrinsicAspect) {
final int width = (int) (h * intrinsicAspect);
left = (w - width) / 2;
right = left + width;
} else {
final int height = (int) (w * (1 / intrinsicAspect));
top = (h - height) / 2;
bottom = top + height;
}
}
}
mIndeterminateDrawable.setBounds(left, top, right, bottom);
}
if (mProgressDrawable != null) {
mProgressDrawable.setBounds(0, 0, right, bottom);
}
}
@Override
protected synchronized void onDraw(Canvas canvas) {
super.onDraw(canvas);
Drawable d = mCurrentDrawable;
if (d != null) {
canvas.save();
canvas.translate(getPaddingLeft(), getPaddingTop());
long time = getDrawingTime();
if (mAnimation != null) {
mAnimation.getTransformation(time, mTransformation);
float scale = mTransformation.getAlpha();
try {
mInDrawing = true;
d.setLevel((int) (scale * MAX_LEVEL));
} finally {
mInDrawing = false;
}
if (SystemClock.uptimeMillis() - mLastDrawTime >= mAnimationResolution) {
mLastDrawTime = SystemClock.uptimeMillis();
postInvalidateDelayed(mAnimationResolution);
}
}
d.draw(canvas);
canvas.restore();
if (mShouldStartAnimationDrawable && d instanceof Animatable) {
((Animatable) d).start();
mShouldStartAnimationDrawable = false;
}
}
}
@Override
protected synchronized void onMeasure(
int widthMeasureSpec,
int heightMeasureSpec) {
Drawable d = mCurrentDrawable;
int dw = 0;
int dh = 0;
if (d != null) {
dw = Math.max(mMinWidth, Math.min(mMaxWidth, d.getIntrinsicWidth()));
dh = Math.max(mMinHeight, Math.min(mMaxHeight, d.getIntrinsicHeight()));
}
updateDrawableState();
dw += getPaddingLeft() + getPaddingRight();
dh += getPaddingTop() + getPaddingBottom();
setMeasuredDimension(IcsView.resolveSizeAndState(dw, widthMeasureSpec, 0),
IcsView.resolveSizeAndState(dh, heightMeasureSpec, 0));
}
@Override
super.drawableStateChanged();
updateDrawableState();
}
int[] state = getDrawableState();
if (mProgressDrawable != null && mProgressDrawable.isStateful()) {
mProgressDrawable.setState(state);
}
if (mIndeterminateDrawable != null && mIndeterminateDrawable.isStateful()) {
mIndeterminateDrawable.setState(state);
}
}
int progress;
int secondaryProgress;
super(superState);
}
super(in);
progress = in.readInt();
secondaryProgress = in.readInt();
}
@Override
super.writeToParcel(out, flags);
out.writeInt(progress);
out.writeInt(secondaryProgress);
}
public static final Parcelable.Creator<SavedState> CREATOR
= new Parcelable.Creator<SavedState>() {
return new SavedState(in);
}
public SavedState[]
newArray(
int size) {
return new SavedState[size];
}
};
}
@Override
Parcelable superState = super.onSaveInstanceState();
SavedState ss = new SavedState(superState);
ss.progress = mProgress;
ss.secondaryProgress = mSecondaryProgress;
return ss;
}
@Override
SavedState ss = (SavedState) state;
super.onRestoreInstanceState(ss.getSuperState());
setProgress(ss.progress);
setSecondaryProgress(ss.secondaryProgress);
}
@Override
super.onAttachedToWindow();
if (mIndeterminate) {
startAnimation();
}
}
@Override
if (mIndeterminate) {
stopAnimation();
}
if(mRefreshProgressRunnable != null) {
removeCallbacks(mRefreshProgressRunnable);
}
if (mAccessibilityEventSender != null) {
removeCallbacks(mAccessibilityEventSender);
}
super.onDetachedFromWindow();
}
@Override
super.onInitializeAccessibilityEvent(event);
event.setItemCount(mMax);
event.setCurrentItemIndex(mProgress);
}
if (mAccessibilityEventSender == null) {
mAccessibilityEventSender = new AccessibilityEventSender();
} else {
removeCallbacks(mAccessibilityEventSender);
}
postDelayed(mAccessibilityEventSender, TIMEOUT_SEND_ACCESSIBILITY_EVENT);
}
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
}
}
}