Loading graphics/java/android/graphics/drawable/Drawable.java +0 −3 Original line number Diff line number Diff line Loading @@ -1019,9 +1019,6 @@ public abstract class Drawable { drawable = new StateListDrawable(); } else if (name.equals("animated-selector")) { drawable = new AnimatedStateListDrawable(); } else if (name.equals("material-progress")) { // TODO: Replace this with something less ridiculous. drawable = new MaterialProgressDrawable(); } else if (name.equals("level-list")) { drawable = new LevelListDrawable(); } else if (name.equals("layer-list")) { Loading graphics/java/android/graphics/drawable/MaterialProgressDrawable.javadeleted 100644 → 0 +0 −548 Original line number Diff line number Diff line /* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.graphics.drawable; import android.animation.Animator; import android.animation.ObjectAnimator; import android.animation.TimeInterpolator; import android.content.res.ColorStateList; import android.content.res.Resources; import android.content.res.Resources.Theme; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorFilter; import android.graphics.Paint; import android.graphics.PorterDuffColorFilter; import android.graphics.Paint.Cap; import android.graphics.Paint.Style; import android.graphics.PorterDuff.Mode; import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.RectF; import android.util.AttributeSet; import android.view.animation.AccelerateDecelerateInterpolator; import android.view.animation.LinearInterpolator; import com.android.internal.R; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.util.ArrayList; /** * Fancy progress indicator for Material theme. * * TODO: Replace this class with something less ridiculous. */ class MaterialProgressDrawable extends Drawable implements Animatable { private static final TimeInterpolator LINEAR_INTERPOLATOR = new LinearInterpolator(); private static final TimeInterpolator END_CURVE_INTERPOLATOR = new EndCurveInterpolator(); private static final TimeInterpolator START_CURVE_INTERPOLATOR = new StartCurveInterpolator(); /** The duration of a single progress spin in milliseconds. */ private static final int ANIMATION_DURATION = 1000 * 80 / 60; /** The number of points in the progress "star". */ private static final int NUM_POINTS = 5; /** The list of animators operating on this drawable. */ private final ArrayList<Animator> mAnimators = new ArrayList<Animator>(); /** The indicator ring, used to manage animation state. */ private final Ring mRing; private MaterialProgressState mState; private PorterDuffColorFilter mTintFilter; /** Canvas rotation in degrees. */ private float mRotation; private boolean mMutated; public MaterialProgressDrawable() { this(new MaterialProgressState(null), null); } private MaterialProgressDrawable(MaterialProgressState state, Theme theme) { mState = state; if (theme != null && state.canApplyTheme()) { applyTheme(theme); } mRing = new Ring(mCallback); mMutated = false; initializeFromState(); setupAnimators(); } private void initializeFromState() { final MaterialProgressState state = mState; final Ring ring = mRing; ring.setStrokeWidth(state.mStrokeWidth); final int color = state.mColor.getColorForState(getState(), Color.TRANSPARENT); ring.setColor(color); final float minEdge = Math.min(state.mWidth, state.mHeight); if (state.mInnerRadius <= 0 || minEdge < 0) { ring.setInsets((int) Math.ceil(state.mStrokeWidth / 2.0f)); } else { float insets = minEdge / 2.0f - state.mInnerRadius; ring.setInsets(insets); } mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode); } @Override public Drawable mutate() { if (!mMutated && super.mutate() == this) { mState = new MaterialProgressState(mState); mMutated = true; } return this; } @Override protected boolean onStateChange(int[] stateSet) { boolean changed = super.onStateChange(stateSet); final MaterialProgressState state = mState; final int color = state.mColor.getColorForState(stateSet, Color.TRANSPARENT); if (color != mRing.getColor()) { mRing.setColor(color); changed = true; } if (state.mTint != null && state.mTintMode != null) { mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode); changed = true; } return changed; } @Override public boolean isStateful() { return super.isStateful() || mState.mColor.isStateful(); } @Override public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme) throws XmlPullParserException, IOException { final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.MaterialProgressDrawable); super.inflateWithAttributes(r, parser, a, R.styleable.MaterialProgressDrawable_visible); updateStateFromTypedArray(a); a.recycle(); initializeFromState(); } @Override public void applyTheme(Theme t) { final TypedArray a = t.resolveAttributes(mState.mThemeAttrs, R.styleable.MaterialProgressDrawable); updateStateFromTypedArray(a); a.recycle(); } private void updateStateFromTypedArray(TypedArray a) { final MaterialProgressState state = mState; state.mThemeAttrs = a.extractThemeAttrs(); state.mWidth = a.getDimensionPixelSize( R.styleable.MaterialProgressDrawable_width, state.mWidth); state.mHeight = a.getDimensionPixelSize( R.styleable.MaterialProgressDrawable_height, state.mHeight); state.mInnerRadius = a.getDimension( R.styleable.MaterialProgressDrawable_innerRadius, state.mInnerRadius); state.mStrokeWidth = a.getDimension( R.styleable.MaterialProgressDrawable_thickness, state.mStrokeWidth); if (a.hasValue(R.styleable.MaterialProgressDrawable_color)) { state.mColor = a.getColorStateList(R.styleable.MaterialProgressDrawable_color); } } @Override public boolean setVisible(boolean visible, boolean restart) { boolean changed = super.setVisible(visible, restart); if (visible) { if (changed || restart) { start(); } } else { stop(); } return changed; } @Override public int getIntrinsicHeight() { return mState.mHeight; } @Override public int getIntrinsicWidth() { return mState.mWidth; } @Override public void draw(Canvas c) { final Rect bounds = getBounds(); final int saveCount = c.save(); c.rotate(mRotation, bounds.exactCenterX(), bounds.exactCenterY()); mRing.draw(c, bounds); c.restoreToCount(saveCount); } @Override public void setAlpha(int alpha) { mRing.setAlpha(alpha); } @Override public int getAlpha() { return mRing.getAlpha(); } @Override public void setColorFilter(ColorFilter colorFilter) { mRing.setColorFilter(colorFilter); } @Override public ColorFilter getColorFilter() { return mRing.getColorFilter(); } @Override public void setTint(ColorStateList tint, Mode tintMode) { if (mState.mTint != tint || mState.mTintMode != tintMode) { mState.mTint = tint; mState.mTintMode = tintMode; mTintFilter = updateTintFilter(mTintFilter, tint, tintMode); invalidateSelf(); } } @SuppressWarnings("unused") private void setRotation(float rotation) { mRotation = rotation; invalidateSelf(); } @SuppressWarnings("unused") private float getRotation() { return mRotation; } @Override public int getOpacity() { return PixelFormat.TRANSLUCENT; } @Override public boolean isRunning() { final ArrayList<Animator> animators = mAnimators; final int N = animators.size(); for (int i = 0; i < N; i++) { final Animator animator = animators.get(i); if (animator.isRunning()) { return true; } } return false; } @Override public void start() { final ArrayList<Animator> animators = mAnimators; final int N = animators.size(); for (int i = 0; i < N; i++) { final Animator animator = animators.get(i); if (animator.isPaused()) { animator.resume(); } else if (!animator.isRunning()){ animator.start(); } } } @Override public void stop() { final ArrayList<Animator> animators = mAnimators; final int N = animators.size(); for (int i = 0; i < N; i++) { final Animator animator = animators.get(i); animator.pause(); } } private void setupAnimators() { final Ring ring = mRing; final ObjectAnimator endTrim = ObjectAnimator.ofFloat(ring, "endTrim", 0, 0.75f); endTrim.setDuration(ANIMATION_DURATION); endTrim.setInterpolator(START_CURVE_INTERPOLATOR); endTrim.setRepeatCount(ObjectAnimator.INFINITE); endTrim.setRepeatMode(ObjectAnimator.RESTART); final ObjectAnimator startTrim = ObjectAnimator.ofFloat(ring, "startTrim", 0.0f, 0.75f); startTrim.setDuration(ANIMATION_DURATION); startTrim.setInterpolator(END_CURVE_INTERPOLATOR); startTrim.setRepeatCount(ObjectAnimator.INFINITE); startTrim.setRepeatMode(ObjectAnimator.RESTART); final ObjectAnimator rotation = ObjectAnimator.ofFloat(ring, "rotation", 0.0f, 0.25f); rotation.setDuration(ANIMATION_DURATION); rotation.setInterpolator(LINEAR_INTERPOLATOR); rotation.setRepeatCount(ObjectAnimator.INFINITE); rotation.setRepeatMode(ObjectAnimator.RESTART); final ObjectAnimator groupRotation = ObjectAnimator.ofFloat(this, "rotation", 0.0f, 360.0f); groupRotation.setDuration(NUM_POINTS * ANIMATION_DURATION); groupRotation.setInterpolator(LINEAR_INTERPOLATOR); groupRotation.setRepeatCount(ObjectAnimator.INFINITE); groupRotation.setRepeatMode(ObjectAnimator.RESTART); mAnimators.add(endTrim); mAnimators.add(startTrim); mAnimators.add(rotation); mAnimators.add(groupRotation); } private final Callback mCallback = new Callback() { @Override public void invalidateDrawable(Drawable d) { invalidateSelf(); } @Override public void scheduleDrawable(Drawable d, Runnable what, long when) { scheduleSelf(what, when); } @Override public void unscheduleDrawable(Drawable d, Runnable what) { unscheduleSelf(what); } }; private static class MaterialProgressState extends ConstantState { private int[] mThemeAttrs = null; private float mStrokeWidth = 5.0f; private float mInnerRadius = -1.0f; private int mWidth = -1; private int mHeight = -1; private ColorStateList mColor = ColorStateList.valueOf(Color.TRANSPARENT); private ColorStateList mTint = null; private Mode mTintMode = null; public MaterialProgressState(MaterialProgressState orig) { if (orig != null) { mThemeAttrs = orig.mThemeAttrs; mStrokeWidth = orig.mStrokeWidth; mInnerRadius = orig.mInnerRadius; mWidth = orig.mWidth; mHeight = orig.mHeight; mColor = orig.mColor; mTint = orig.mTint; mTintMode = orig.mTintMode; } } @Override public boolean canApplyTheme() { return mThemeAttrs != null; } @Override public Drawable newDrawable() { return newDrawable(null, null); } @Override public Drawable newDrawable(Resources res) { return newDrawable(res, null); } @Override public Drawable newDrawable(Resources res, Theme theme) { return new MaterialProgressDrawable(this, theme); } @Override public int getChangingConfigurations() { return 0; } } private static class Ring { private final RectF mTempBounds = new RectF(); private final Paint mPaint = new Paint(); private final Callback mCallback; private float mStartTrim = 0.0f; private float mEndTrim = 0.0f; private float mRotation = 0.0f; private float mStrokeWidth = 5.0f; private float mStrokeInset = 2.5f; private int mAlpha = 0xFF; private int mColor = Color.BLACK; public Ring(Callback callback) { mCallback = callback; mPaint.setStrokeCap(Cap.ROUND); mPaint.setAntiAlias(true); mPaint.setStyle(Style.STROKE); } public void draw(Canvas c, Rect bounds) { final RectF arcBounds = mTempBounds; arcBounds.set(bounds); arcBounds.inset(mStrokeInset, mStrokeInset); final float startAngle = (mStartTrim + mRotation) * 360; final float endAngle = (mEndTrim + mRotation) * 360; float sweepAngle = endAngle - startAngle; // Ensure the sweep angle isn't too small to draw. final float diameter = Math.min(arcBounds.width(), arcBounds.height()); final float minAngle = (float) (360.0 / (diameter * Math.PI)); if (sweepAngle < minAngle && sweepAngle > -minAngle) { sweepAngle = Math.signum(sweepAngle) * minAngle; } c.drawArc(arcBounds, startAngle, sweepAngle, false, mPaint); } public void setColorFilter(ColorFilter filter) { mPaint.setColorFilter(filter); invalidateSelf(); } public ColorFilter getColorFilter() { return mPaint.getColorFilter(); } public void setAlpha(int alpha) { mAlpha = alpha; mPaint.setColor(mColor & 0xFFFFFF | alpha << 24); invalidateSelf(); } public int getAlpha() { return mAlpha; } public void setColor(int color) { mColor = color; mPaint.setColor(color & 0xFFFFFF | mAlpha << 24); invalidateSelf(); } public int getColor() { return mColor; } public void setStrokeWidth(float strokeWidth) { mStrokeWidth = strokeWidth; mPaint.setStrokeWidth(strokeWidth); invalidateSelf(); } @SuppressWarnings("unused") public float getStrokeWidth() { return mStrokeWidth; } @SuppressWarnings("unused") public void setStartTrim(float startTrim) { mStartTrim = startTrim; invalidateSelf(); } @SuppressWarnings("unused") public float getStartTrim() { return mStartTrim; } @SuppressWarnings("unused") public void setEndTrim(float endTrim) { mEndTrim = endTrim; invalidateSelf(); } @SuppressWarnings("unused") public float getEndTrim() { return mEndTrim; } @SuppressWarnings("unused") public void setRotation(float rotation) { mRotation = rotation; invalidateSelf(); } @SuppressWarnings("unused") public float getRotation() { return mRotation; } public void setInsets(float insets) { mStrokeInset = insets; } @SuppressWarnings("unused") public float getInsets() { return mStrokeInset; } private void invalidateSelf() { mCallback.invalidateDrawable(null); } } /** * Squishes the interpolation curve into the second half of the animation. */ private static class EndCurveInterpolator extends AccelerateDecelerateInterpolator { @Override public float getInterpolation(float input) { return super.getInterpolation(Math.max(0, (input - 0.5f) * 2.0f)); } } /** * Squishes the interpolation curve into the first half of the animation. */ private static class StartCurveInterpolator extends AccelerateDecelerateInterpolator { @Override public float getInterpolation(float input) { return super.getInterpolation(Math.min(1, input * 2.0f)); } } } Loading
graphics/java/android/graphics/drawable/Drawable.java +0 −3 Original line number Diff line number Diff line Loading @@ -1019,9 +1019,6 @@ public abstract class Drawable { drawable = new StateListDrawable(); } else if (name.equals("animated-selector")) { drawable = new AnimatedStateListDrawable(); } else if (name.equals("material-progress")) { // TODO: Replace this with something less ridiculous. drawable = new MaterialProgressDrawable(); } else if (name.equals("level-list")) { drawable = new LevelListDrawable(); } else if (name.equals("layer-list")) { Loading
graphics/java/android/graphics/drawable/MaterialProgressDrawable.javadeleted 100644 → 0 +0 −548 Original line number Diff line number Diff line /* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.graphics.drawable; import android.animation.Animator; import android.animation.ObjectAnimator; import android.animation.TimeInterpolator; import android.content.res.ColorStateList; import android.content.res.Resources; import android.content.res.Resources.Theme; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorFilter; import android.graphics.Paint; import android.graphics.PorterDuffColorFilter; import android.graphics.Paint.Cap; import android.graphics.Paint.Style; import android.graphics.PorterDuff.Mode; import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.RectF; import android.util.AttributeSet; import android.view.animation.AccelerateDecelerateInterpolator; import android.view.animation.LinearInterpolator; import com.android.internal.R; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.util.ArrayList; /** * Fancy progress indicator for Material theme. * * TODO: Replace this class with something less ridiculous. */ class MaterialProgressDrawable extends Drawable implements Animatable { private static final TimeInterpolator LINEAR_INTERPOLATOR = new LinearInterpolator(); private static final TimeInterpolator END_CURVE_INTERPOLATOR = new EndCurveInterpolator(); private static final TimeInterpolator START_CURVE_INTERPOLATOR = new StartCurveInterpolator(); /** The duration of a single progress spin in milliseconds. */ private static final int ANIMATION_DURATION = 1000 * 80 / 60; /** The number of points in the progress "star". */ private static final int NUM_POINTS = 5; /** The list of animators operating on this drawable. */ private final ArrayList<Animator> mAnimators = new ArrayList<Animator>(); /** The indicator ring, used to manage animation state. */ private final Ring mRing; private MaterialProgressState mState; private PorterDuffColorFilter mTintFilter; /** Canvas rotation in degrees. */ private float mRotation; private boolean mMutated; public MaterialProgressDrawable() { this(new MaterialProgressState(null), null); } private MaterialProgressDrawable(MaterialProgressState state, Theme theme) { mState = state; if (theme != null && state.canApplyTheme()) { applyTheme(theme); } mRing = new Ring(mCallback); mMutated = false; initializeFromState(); setupAnimators(); } private void initializeFromState() { final MaterialProgressState state = mState; final Ring ring = mRing; ring.setStrokeWidth(state.mStrokeWidth); final int color = state.mColor.getColorForState(getState(), Color.TRANSPARENT); ring.setColor(color); final float minEdge = Math.min(state.mWidth, state.mHeight); if (state.mInnerRadius <= 0 || minEdge < 0) { ring.setInsets((int) Math.ceil(state.mStrokeWidth / 2.0f)); } else { float insets = minEdge / 2.0f - state.mInnerRadius; ring.setInsets(insets); } mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode); } @Override public Drawable mutate() { if (!mMutated && super.mutate() == this) { mState = new MaterialProgressState(mState); mMutated = true; } return this; } @Override protected boolean onStateChange(int[] stateSet) { boolean changed = super.onStateChange(stateSet); final MaterialProgressState state = mState; final int color = state.mColor.getColorForState(stateSet, Color.TRANSPARENT); if (color != mRing.getColor()) { mRing.setColor(color); changed = true; } if (state.mTint != null && state.mTintMode != null) { mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode); changed = true; } return changed; } @Override public boolean isStateful() { return super.isStateful() || mState.mColor.isStateful(); } @Override public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme) throws XmlPullParserException, IOException { final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.MaterialProgressDrawable); super.inflateWithAttributes(r, parser, a, R.styleable.MaterialProgressDrawable_visible); updateStateFromTypedArray(a); a.recycle(); initializeFromState(); } @Override public void applyTheme(Theme t) { final TypedArray a = t.resolveAttributes(mState.mThemeAttrs, R.styleable.MaterialProgressDrawable); updateStateFromTypedArray(a); a.recycle(); } private void updateStateFromTypedArray(TypedArray a) { final MaterialProgressState state = mState; state.mThemeAttrs = a.extractThemeAttrs(); state.mWidth = a.getDimensionPixelSize( R.styleable.MaterialProgressDrawable_width, state.mWidth); state.mHeight = a.getDimensionPixelSize( R.styleable.MaterialProgressDrawable_height, state.mHeight); state.mInnerRadius = a.getDimension( R.styleable.MaterialProgressDrawable_innerRadius, state.mInnerRadius); state.mStrokeWidth = a.getDimension( R.styleable.MaterialProgressDrawable_thickness, state.mStrokeWidth); if (a.hasValue(R.styleable.MaterialProgressDrawable_color)) { state.mColor = a.getColorStateList(R.styleable.MaterialProgressDrawable_color); } } @Override public boolean setVisible(boolean visible, boolean restart) { boolean changed = super.setVisible(visible, restart); if (visible) { if (changed || restart) { start(); } } else { stop(); } return changed; } @Override public int getIntrinsicHeight() { return mState.mHeight; } @Override public int getIntrinsicWidth() { return mState.mWidth; } @Override public void draw(Canvas c) { final Rect bounds = getBounds(); final int saveCount = c.save(); c.rotate(mRotation, bounds.exactCenterX(), bounds.exactCenterY()); mRing.draw(c, bounds); c.restoreToCount(saveCount); } @Override public void setAlpha(int alpha) { mRing.setAlpha(alpha); } @Override public int getAlpha() { return mRing.getAlpha(); } @Override public void setColorFilter(ColorFilter colorFilter) { mRing.setColorFilter(colorFilter); } @Override public ColorFilter getColorFilter() { return mRing.getColorFilter(); } @Override public void setTint(ColorStateList tint, Mode tintMode) { if (mState.mTint != tint || mState.mTintMode != tintMode) { mState.mTint = tint; mState.mTintMode = tintMode; mTintFilter = updateTintFilter(mTintFilter, tint, tintMode); invalidateSelf(); } } @SuppressWarnings("unused") private void setRotation(float rotation) { mRotation = rotation; invalidateSelf(); } @SuppressWarnings("unused") private float getRotation() { return mRotation; } @Override public int getOpacity() { return PixelFormat.TRANSLUCENT; } @Override public boolean isRunning() { final ArrayList<Animator> animators = mAnimators; final int N = animators.size(); for (int i = 0; i < N; i++) { final Animator animator = animators.get(i); if (animator.isRunning()) { return true; } } return false; } @Override public void start() { final ArrayList<Animator> animators = mAnimators; final int N = animators.size(); for (int i = 0; i < N; i++) { final Animator animator = animators.get(i); if (animator.isPaused()) { animator.resume(); } else if (!animator.isRunning()){ animator.start(); } } } @Override public void stop() { final ArrayList<Animator> animators = mAnimators; final int N = animators.size(); for (int i = 0; i < N; i++) { final Animator animator = animators.get(i); animator.pause(); } } private void setupAnimators() { final Ring ring = mRing; final ObjectAnimator endTrim = ObjectAnimator.ofFloat(ring, "endTrim", 0, 0.75f); endTrim.setDuration(ANIMATION_DURATION); endTrim.setInterpolator(START_CURVE_INTERPOLATOR); endTrim.setRepeatCount(ObjectAnimator.INFINITE); endTrim.setRepeatMode(ObjectAnimator.RESTART); final ObjectAnimator startTrim = ObjectAnimator.ofFloat(ring, "startTrim", 0.0f, 0.75f); startTrim.setDuration(ANIMATION_DURATION); startTrim.setInterpolator(END_CURVE_INTERPOLATOR); startTrim.setRepeatCount(ObjectAnimator.INFINITE); startTrim.setRepeatMode(ObjectAnimator.RESTART); final ObjectAnimator rotation = ObjectAnimator.ofFloat(ring, "rotation", 0.0f, 0.25f); rotation.setDuration(ANIMATION_DURATION); rotation.setInterpolator(LINEAR_INTERPOLATOR); rotation.setRepeatCount(ObjectAnimator.INFINITE); rotation.setRepeatMode(ObjectAnimator.RESTART); final ObjectAnimator groupRotation = ObjectAnimator.ofFloat(this, "rotation", 0.0f, 360.0f); groupRotation.setDuration(NUM_POINTS * ANIMATION_DURATION); groupRotation.setInterpolator(LINEAR_INTERPOLATOR); groupRotation.setRepeatCount(ObjectAnimator.INFINITE); groupRotation.setRepeatMode(ObjectAnimator.RESTART); mAnimators.add(endTrim); mAnimators.add(startTrim); mAnimators.add(rotation); mAnimators.add(groupRotation); } private final Callback mCallback = new Callback() { @Override public void invalidateDrawable(Drawable d) { invalidateSelf(); } @Override public void scheduleDrawable(Drawable d, Runnable what, long when) { scheduleSelf(what, when); } @Override public void unscheduleDrawable(Drawable d, Runnable what) { unscheduleSelf(what); } }; private static class MaterialProgressState extends ConstantState { private int[] mThemeAttrs = null; private float mStrokeWidth = 5.0f; private float mInnerRadius = -1.0f; private int mWidth = -1; private int mHeight = -1; private ColorStateList mColor = ColorStateList.valueOf(Color.TRANSPARENT); private ColorStateList mTint = null; private Mode mTintMode = null; public MaterialProgressState(MaterialProgressState orig) { if (orig != null) { mThemeAttrs = orig.mThemeAttrs; mStrokeWidth = orig.mStrokeWidth; mInnerRadius = orig.mInnerRadius; mWidth = orig.mWidth; mHeight = orig.mHeight; mColor = orig.mColor; mTint = orig.mTint; mTintMode = orig.mTintMode; } } @Override public boolean canApplyTheme() { return mThemeAttrs != null; } @Override public Drawable newDrawable() { return newDrawable(null, null); } @Override public Drawable newDrawable(Resources res) { return newDrawable(res, null); } @Override public Drawable newDrawable(Resources res, Theme theme) { return new MaterialProgressDrawable(this, theme); } @Override public int getChangingConfigurations() { return 0; } } private static class Ring { private final RectF mTempBounds = new RectF(); private final Paint mPaint = new Paint(); private final Callback mCallback; private float mStartTrim = 0.0f; private float mEndTrim = 0.0f; private float mRotation = 0.0f; private float mStrokeWidth = 5.0f; private float mStrokeInset = 2.5f; private int mAlpha = 0xFF; private int mColor = Color.BLACK; public Ring(Callback callback) { mCallback = callback; mPaint.setStrokeCap(Cap.ROUND); mPaint.setAntiAlias(true); mPaint.setStyle(Style.STROKE); } public void draw(Canvas c, Rect bounds) { final RectF arcBounds = mTempBounds; arcBounds.set(bounds); arcBounds.inset(mStrokeInset, mStrokeInset); final float startAngle = (mStartTrim + mRotation) * 360; final float endAngle = (mEndTrim + mRotation) * 360; float sweepAngle = endAngle - startAngle; // Ensure the sweep angle isn't too small to draw. final float diameter = Math.min(arcBounds.width(), arcBounds.height()); final float minAngle = (float) (360.0 / (diameter * Math.PI)); if (sweepAngle < minAngle && sweepAngle > -minAngle) { sweepAngle = Math.signum(sweepAngle) * minAngle; } c.drawArc(arcBounds, startAngle, sweepAngle, false, mPaint); } public void setColorFilter(ColorFilter filter) { mPaint.setColorFilter(filter); invalidateSelf(); } public ColorFilter getColorFilter() { return mPaint.getColorFilter(); } public void setAlpha(int alpha) { mAlpha = alpha; mPaint.setColor(mColor & 0xFFFFFF | alpha << 24); invalidateSelf(); } public int getAlpha() { return mAlpha; } public void setColor(int color) { mColor = color; mPaint.setColor(color & 0xFFFFFF | mAlpha << 24); invalidateSelf(); } public int getColor() { return mColor; } public void setStrokeWidth(float strokeWidth) { mStrokeWidth = strokeWidth; mPaint.setStrokeWidth(strokeWidth); invalidateSelf(); } @SuppressWarnings("unused") public float getStrokeWidth() { return mStrokeWidth; } @SuppressWarnings("unused") public void setStartTrim(float startTrim) { mStartTrim = startTrim; invalidateSelf(); } @SuppressWarnings("unused") public float getStartTrim() { return mStartTrim; } @SuppressWarnings("unused") public void setEndTrim(float endTrim) { mEndTrim = endTrim; invalidateSelf(); } @SuppressWarnings("unused") public float getEndTrim() { return mEndTrim; } @SuppressWarnings("unused") public void setRotation(float rotation) { mRotation = rotation; invalidateSelf(); } @SuppressWarnings("unused") public float getRotation() { return mRotation; } public void setInsets(float insets) { mStrokeInset = insets; } @SuppressWarnings("unused") public float getInsets() { return mStrokeInset; } private void invalidateSelf() { mCallback.invalidateDrawable(null); } } /** * Squishes the interpolation curve into the second half of the animation. */ private static class EndCurveInterpolator extends AccelerateDecelerateInterpolator { @Override public float getInterpolation(float input) { return super.getInterpolation(Math.max(0, (input - 0.5f) * 2.0f)); } } /** * Squishes the interpolation curve into the first half of the animation. */ private static class StartCurveInterpolator extends AccelerateDecelerateInterpolator { @Override public float getInterpolation(float input) { return super.getInterpolation(Math.min(1, input * 2.0f)); } } }