summaryrefslogtreecommitdiffstats
path: root/graphics
diff options
context:
space:
mode:
authorAlan Viverette <alanv@google.com>2014-06-04 18:44:09 +0000
committerAndroid Git Automerger <android-git-automerger@android.com>2014-06-04 18:44:09 +0000
commit3b1e08fc9fc8c8eb557a260ae9b8e809864b471a (patch)
tree5692cfe79285ac1b5dfa944625e68c4ef98041f7 /graphics
parent3decd141a51849fa7f84cb4e5a28fa047ff9be37 (diff)
parentbb938107b7ef1f6660b9f9cbafacd9d0a427bedc (diff)
downloadframeworks_base-3b1e08fc9fc8c8eb557a260ae9b8e809864b471a.zip
frameworks_base-3b1e08fc9fc8c8eb557a260ae9b8e809864b471a.tar.gz
frameworks_base-3b1e08fc9fc8c8eb557a260ae9b8e809864b471a.tar.bz2
am 62ea9554: Merge "Temporary drawable for Quantum progress indicator" into lmp-preview-dev
* commit '62ea9554753820df25343e52e0dcab090e5755b5': Temporary drawable for Quantum progress indicator
Diffstat (limited to 'graphics')
-rw-r--r--graphics/java/android/graphics/drawable/Drawable.java3
-rw-r--r--graphics/java/android/graphics/drawable/QuantumProgressDrawable.java491
2 files changed, 494 insertions, 0 deletions
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index 76dd1c8..3a32e80 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -1023,6 +1023,9 @@ public abstract class Drawable {
drawable = new StateListDrawable();
} else if (name.equals("animated-selector")) {
drawable = new AnimatedStateListDrawable();
+ } else if (name.equals("quantum-progress")) {
+ // TODO: Replace this with something less ridiculous.
+ drawable = new QuantumProgressDrawable();
} else if (name.equals("level-list")) {
drawable = new LevelListDrawable();
} else if (name.equals("layer-list")) {
diff --git a/graphics/java/android/graphics/drawable/QuantumProgressDrawable.java b/graphics/java/android/graphics/drawable/QuantumProgressDrawable.java
new file mode 100644
index 0000000..d756eb1
--- /dev/null
+++ b/graphics/java/android/graphics/drawable/QuantumProgressDrawable.java
@@ -0,0 +1,491 @@
+/*
+ * 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.Paint.Cap;
+import android.graphics.Paint.Style;
+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 Quantum theme.
+ *
+ * TODO: Replace this class with something less ridiculous.
+ */
+class QuantumProgressDrawable 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 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 QuantumProgressState mState;
+
+ private boolean mMutated;
+
+ public QuantumProgressDrawable() {
+ this(new QuantumProgressState(null), null);
+ }
+
+ private QuantumProgressDrawable(QuantumProgressState state, Theme theme) {
+ mState = state;
+ if (theme != null && state.canApplyTheme()) {
+ applyTheme(theme);
+ }
+
+ mRing = new Ring(mCallback);
+ mMutated = false;
+
+ initializeFromState();
+ setupAnimators();
+ }
+
+ private void initializeFromState() {
+ final QuantumProgressState 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);
+ }
+ }
+
+ @Override
+ public Drawable mutate() {
+ if (!mMutated && super.mutate() == this) {
+ mState = new QuantumProgressState(mState);
+ mMutated = true;
+ }
+ return this;
+ }
+
+ @Override
+ protected boolean onStateChange(int[] state) {
+ boolean changed = super.onStateChange(state);
+
+ final int color = mState.mColor.getColorForState(state, Color.TRANSPARENT);
+ if (color != mRing.getColor()) {
+ mRing.setColor(color);
+ 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.QuantumProgressDrawable);
+ super.inflateWithAttributes(r, parser, a, R.styleable.QuantumProgressDrawable_visible);
+ updateStateFromTypedArray(a);
+ a.recycle();
+
+ initializeFromState();
+ }
+
+ @Override
+ public void applyTheme(Theme t) {
+ final TypedArray a = t.resolveAttributes(mState.mThemeAttrs,
+ R.styleable.QuantumProgressDrawable);
+ updateStateFromTypedArray(a);
+ a.recycle();
+ }
+
+ private void updateStateFromTypedArray(TypedArray a) {
+ final QuantumProgressState state = mState;
+ state.mThemeAttrs = a.extractThemeAttrs();
+ state.mWidth = a.getDimensionPixelSize(
+ R.styleable.QuantumProgressDrawable_width, state.mWidth);
+ state.mHeight = a.getDimensionPixelSize(
+ R.styleable.QuantumProgressDrawable_height, state.mHeight);
+ state.mInnerRadius = a.getDimension(
+ R.styleable.QuantumProgressDrawable_innerRadius, state.mInnerRadius);
+ state.mStrokeWidth = a.getDimension(
+ R.styleable.QuantumProgressDrawable_thickness, state.mStrokeWidth);
+
+ if (a.hasValue(R.styleable.QuantumProgressDrawable_color)) {
+ state.mColor = a.getColorStateList(R.styleable.QuantumProgressDrawable_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) {
+ mRing.draw(c, getBounds());
+ }
+
+ @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 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(1000 * 80 / 60);
+ 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(1000 * 80 / 60);
+ 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(1000 * 80 / 60);
+ rotation.setInterpolator(LINEAR_INTERPOLATOR);
+ rotation.setRepeatCount(ObjectAnimator.INFINITE);
+ rotation.setRepeatMode(ObjectAnimator.RESTART);
+
+ mAnimators.add(endTrim);
+ mAnimators.add(startTrim);
+ mAnimators.add(rotation);
+ }
+
+ 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 QuantumProgressState 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);
+
+ public QuantumProgressState(QuantumProgressState orig) {
+ if (orig != null) {
+ mThemeAttrs = orig.mThemeAttrs;
+ mStrokeWidth = orig.mStrokeWidth;
+ mInnerRadius = orig.mInnerRadius;
+ mWidth = orig.mWidth;
+ mHeight = orig.mHeight;
+ mColor = orig.mColor;
+ }
+ }
+
+ @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 QuantumProgressDrawable(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));
+ }
+ }
+}