summaryrefslogtreecommitdiff
path: root/core/java
diff options
context:
space:
mode:
Diffstat (limited to 'core/java')
-rw-r--r--core/java/com/android/internal/graphics/drawable/AnimationScaleListDrawable.java254
1 files changed, 254 insertions, 0 deletions
diff --git a/core/java/com/android/internal/graphics/drawable/AnimationScaleListDrawable.java b/core/java/com/android/internal/graphics/drawable/AnimationScaleListDrawable.java
new file mode 100644
index 000000000000..11338991c052
--- /dev/null
+++ b/core/java/com/android/internal/graphics/drawable/AnimationScaleListDrawable.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2016 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 com.android.internal.graphics.drawable;
+
+import android.animation.ValueAnimator;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.res.Resources;
+import android.content.res.Resources.Theme;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Animatable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.DrawableContainer;
+import android.util.AttributeSet;
+
+import com.android.internal.R;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+/**
+ * An internal DrawableContainer class, used to draw different things depending on animation scale.
+ * i.e: animation scale can be 0 in battery saver mode.
+ * This class contains 2 drawable, one is animatable, the other is static. When animation scale is
+ * not 0, the animatable drawable will the drawn. Otherwise, the static drawable will be drawn.
+ * <p>This class implements Animatable since ProgressBar can pick this up similarly as an
+ * AnimatedVectorDrawable.
+ * <p>It can be defined in an XML file with the {@code <AnimationScaleListDrawable>}
+ * element.
+ */
+public class AnimationScaleListDrawable extends DrawableContainer implements Animatable {
+ private static final String TAG = "AnimationScaleListDrawable";
+ private AnimatedScaleListState mAnimatedScaleListState;
+ private boolean mMutated;
+
+ public AnimationScaleListDrawable() {
+ this(null, null);
+ }
+
+ private AnimationScaleListDrawable(@Nullable AnimatedScaleListState state,
+ @Nullable Resources res) {
+ // Every scale list drawable has its own constant state.
+ final AnimatedScaleListState newState = new AnimatedScaleListState(state, this, res);
+ setConstantState(newState);
+ onStateChange(getState());
+ }
+
+ /**
+ * Set the current drawable according to the animation scale. If scale is 0, then pick the
+ * static drawable, otherwise, pick the animatable drawable.
+ */
+ @Override
+ protected boolean onStateChange(int[] stateSet) {
+ final boolean changed = super.onStateChange(stateSet);
+ int idx = mAnimatedScaleListState.getCurrentDrawableIndexBasedOnScale();
+ return selectDrawable(idx) || changed;
+ }
+
+
+ @Override
+ public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
+ @NonNull AttributeSet attrs, @Nullable Theme theme)
+ throws XmlPullParserException, IOException {
+ final TypedArray a = obtainAttributes(r, theme, attrs,
+ R.styleable.AnimationScaleListDrawable);
+ updateDensity(r);
+ a.recycle();
+
+ inflateChildElements(r, parser, attrs, theme);
+
+ onStateChange(getState());
+ }
+
+ /**
+ * Inflates child elements from XML.
+ */
+ private void inflateChildElements(@NonNull Resources r, @NonNull XmlPullParser parser,
+ @NonNull AttributeSet attrs, @Nullable Theme theme)
+ throws XmlPullParserException, IOException {
+ final AnimatedScaleListState state = mAnimatedScaleListState;
+ final int innerDepth = parser.getDepth() + 1;
+ int type;
+ int depth;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && ((depth = parser.getDepth()) >= innerDepth
+ || type != XmlPullParser.END_TAG)) {
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+
+ if (depth > innerDepth || !parser.getName().equals("item")) {
+ continue;
+ }
+
+ // Either pick up the android:drawable attribute.
+ final TypedArray a = obtainAttributes(r, theme, attrs,
+ R.styleable.AnimationScaleListDrawableItem);
+ Drawable dr = a.getDrawable(R.styleable.AnimationScaleListDrawableItem_drawable);
+ a.recycle();
+
+ // Or parse the child element under <item>.
+ if (dr == null) {
+ while ((type = parser.next()) == XmlPullParser.TEXT) {
+ }
+ if (type != XmlPullParser.START_TAG) {
+ throw new XmlPullParserException(
+ parser.getPositionDescription()
+ + ": <item> tag requires a 'drawable' attribute or "
+ + "child tag defining a drawable");
+ }
+ dr = Drawable.createFromXmlInner(r, parser, attrs, theme);
+ }
+
+ state.addDrawable(dr);
+ }
+ }
+
+ @Override
+ public Drawable mutate() {
+ if (!mMutated && super.mutate() == this) {
+ mAnimatedScaleListState.mutate();
+ mMutated = true;
+ }
+ return this;
+ }
+
+ @Override
+ public void clearMutated() {
+ super.clearMutated();
+ mMutated = false;
+ }
+
+ @Override
+ public void start() {
+ Drawable dr = getCurrent();
+ if (dr != null && dr instanceof Animatable) {
+ ((Animatable) dr).start();
+ }
+ }
+
+ @Override
+ public void stop() {
+ Drawable dr = getCurrent();
+ if (dr != null && dr instanceof Animatable) {
+ ((Animatable) dr).stop();
+ }
+ }
+
+ @Override
+ public boolean isRunning() {
+ boolean result = false;
+ Drawable dr = getCurrent();
+ if (dr != null && dr instanceof Animatable) {
+ result = ((Animatable) dr).isRunning();
+ }
+ return result;
+ }
+
+ static class AnimatedScaleListState extends DrawableContainerState {
+ int[] mThemeAttrs = null;
+ // The index of the last static drawable.
+ int mStaticDrawableIndex = -1;
+ // The index of the last animatable drawable.
+ int mAnimatableDrawableIndex = -1;
+
+ AnimatedScaleListState(AnimatedScaleListState orig, AnimationScaleListDrawable owner,
+ Resources res) {
+ super(orig, owner, res);
+
+ if (orig != null) {
+ // Perform a shallow copy and rely on mutate() to deep-copy.
+ mThemeAttrs = orig.mThemeAttrs;
+
+ mStaticDrawableIndex = orig.mStaticDrawableIndex;
+ mAnimatableDrawableIndex = orig.mAnimatableDrawableIndex;
+ }
+
+ }
+
+ void mutate() {
+ mThemeAttrs = mThemeAttrs != null ? mThemeAttrs.clone() : null;
+ }
+
+ /**
+ * Add the drawable into the container.
+ * This class only keep track one animatable drawable, and one static. If there are multiple
+ * defined in the XML, then pick the last one.
+ */
+ int addDrawable(Drawable drawable) {
+ final int pos = addChild(drawable);
+ if (drawable instanceof Animatable) {
+ mAnimatableDrawableIndex = pos;
+ } else {
+ mStaticDrawableIndex = pos;
+ }
+ return pos;
+ }
+
+ @Override
+ public Drawable newDrawable() {
+ return new AnimationScaleListDrawable(this, null);
+ }
+
+ @Override
+ public Drawable newDrawable(Resources res) {
+ return new AnimationScaleListDrawable(this, res);
+ }
+
+ @Override
+ public boolean canApplyTheme() {
+ return mThemeAttrs != null || super.canApplyTheme();
+ }
+
+ public int getCurrentDrawableIndexBasedOnScale() {
+ if (ValueAnimator.getDurationScale() == 0) {
+ return mStaticDrawableIndex;
+ }
+ return mAnimatableDrawableIndex;
+ }
+ }
+
+ @Override
+ public void applyTheme(@NonNull Theme theme) {
+ super.applyTheme(theme);
+
+ onStateChange(getState());
+ }
+
+ @Override
+ protected void setConstantState(@NonNull DrawableContainerState state) {
+ super.setConstantState(state);
+
+ if (state instanceof AnimatedScaleListState) {
+ mAnimatedScaleListState = (AnimatedScaleListState) state;
+ }
+ }
+}
+