From 0c03664fa6acbe5c3fd11d54ab9a6792f43dda07 Mon Sep 17 00:00:00 2001 From: Teng-Hui Zhu Date: Fri, 19 Aug 2016 10:06:25 -0700 Subject: [PATCH] Add an new internal DrawableContainer to handle animation scale == 0 case. This will help ProgressBar to show something meaningful when animation scale is 0. b/30877925 Change-Id: Ieb2e78712999d2e3f3a2a234bc605b4821ae41c0 --- .../drawable/AnimationScaleListDrawable.java | 254 ++++++++++++++++++ core/res/res/drawable/ic_refresh.xml | 3 - ...ress_indeterminate_anim_large_material.xml | 26 ++ ...ess_indeterminate_anim_medium_material.xml | 27 ++ .../res/drawable/progress_large_material.xml | 18 +- .../res/drawable/progress_medium_material.xml | 17 +- .../res/drawable/progress_small_material.xml | 27 +- .../res/drawable/progress_static_material.xml | 25 ++ core/res/res/values/attrs.xml | 15 ++ .../android/graphics/drawable/Drawable.java | 5 +- .../graphics/drawable/DrawableContainer.java | 10 +- 11 files changed, 381 insertions(+), 46 deletions(-) create mode 100644 core/java/com/android/internal/graphics/drawable/AnimationScaleListDrawable.java create mode 100644 core/res/res/drawable/progress_indeterminate_anim_large_material.xml create mode 100644 core/res/res/drawable/progress_indeterminate_anim_medium_material.xml create mode 100644 core/res/res/drawable/progress_static_material.xml 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 0000000000000..11338991c0521 --- /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. + *

This class implements Animatable since ProgressBar can pick this up similarly as an + * AnimatedVectorDrawable. + *

It can be defined in an XML file with the {@code } + * 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 . + if (dr == null) { + while ((type = parser.next()) == XmlPullParser.TEXT) { + } + if (type != XmlPullParser.START_TAG) { + throw new XmlPullParserException( + parser.getPositionDescription() + + ": 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; + } + } +} + diff --git a/core/res/res/drawable/ic_refresh.xml b/core/res/res/drawable/ic_refresh.xml index 1f671684861fb..1297407fbb8b8 100644 --- a/core/res/res/drawable/ic_refresh.xml +++ b/core/res/res/drawable/ic_refresh.xml @@ -21,7 +21,4 @@ Copyright (C) 2016 The Android Open Source Project - diff --git a/core/res/res/drawable/progress_indeterminate_anim_large_material.xml b/core/res/res/drawable/progress_indeterminate_anim_large_material.xml new file mode 100644 index 0000000000000..560ec5af508f4 --- /dev/null +++ b/core/res/res/drawable/progress_indeterminate_anim_large_material.xml @@ -0,0 +1,26 @@ + + + + + + + + diff --git a/core/res/res/drawable/progress_indeterminate_anim_medium_material.xml b/core/res/res/drawable/progress_indeterminate_anim_medium_material.xml new file mode 100644 index 0000000000000..fbea22f443e1a --- /dev/null +++ b/core/res/res/drawable/progress_indeterminate_anim_medium_material.xml @@ -0,0 +1,27 @@ + + + + + + + + + \ No newline at end of file diff --git a/core/res/res/drawable/progress_large_material.xml b/core/res/res/drawable/progress_large_material.xml index 526f9141d716d..ee82e35d00f5f 100644 --- a/core/res/res/drawable/progress_large_material.xml +++ b/core/res/res/drawable/progress_large_material.xml @@ -13,16 +13,8 @@ See the License for the specific language governing permissions and limitations under the License. --> - - - - - - - - + + + + diff --git a/core/res/res/drawable/progress_medium_material.xml b/core/res/res/drawable/progress_medium_material.xml index cc35816c9c3c6..5c9260069e5d0 100644 --- a/core/res/res/drawable/progress_medium_material.xml +++ b/core/res/res/drawable/progress_medium_material.xml @@ -13,15 +13,8 @@ See the License for the specific language governing permissions and limitations under the License. --> - - - - - - - \ No newline at end of file + + + + diff --git a/core/res/res/drawable/progress_small_material.xml b/core/res/res/drawable/progress_small_material.xml index c6e43801cdf76..cec9d9565183d 100644 --- a/core/res/res/drawable/progress_small_material.xml +++ b/core/res/res/drawable/progress_small_material.xml @@ -13,16 +13,17 @@ See the License for the specific language governing permissions and limitations under the License. --> - - - - - - - - + + + + + + + + + diff --git a/core/res/res/drawable/progress_static_material.xml b/core/res/res/drawable/progress_static_material.xml new file mode 100644 index 0000000000000..b078fa93a1557 --- /dev/null +++ b/core/res/res/drawable/progress_static_material.xml @@ -0,0 +1,25 @@ + + + + diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 0c085646e01fb..f273d272e41b6 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -5312,6 +5312,21 @@ i + + + + + + + + + + + diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java index 7f3a4373639b7..3aca867c03470 100644 --- a/graphics/java/android/graphics/drawable/Drawable.java +++ b/graphics/java/android/graphics/drawable/Drawable.java @@ -1422,9 +1422,10 @@ public abstract class Drawable { /** * Obtains styled attributes from the theme, if available, or unstyled * resources if the theme is null. + * @hide */ - static @NonNull TypedArray obtainAttributes(@NonNull Resources res, @Nullable Theme theme, - @NonNull AttributeSet set, @NonNull int[] attrs) { + protected static @NonNull TypedArray obtainAttributes(@NonNull Resources res, + @Nullable Theme theme, @NonNull AttributeSet set, @NonNull int[] attrs) { if (theme == null) { return res.obtainAttributes(set, attrs); } diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java index cc7f5c7cf753b..c7a3c75f3545a 100644 --- a/graphics/java/android/graphics/drawable/DrawableContainer.java +++ b/graphics/java/android/graphics/drawable/DrawableContainer.java @@ -27,8 +27,8 @@ import android.graphics.ColorFilter; import android.graphics.Insets; import android.graphics.Outline; import android.graphics.PixelFormat; -import android.graphics.Rect; import android.graphics.PorterDuff.Mode; +import android.graphics.Rect; import android.os.SystemClock; import android.util.DisplayMetrics; import android.util.LayoutDirection; @@ -601,8 +601,9 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { * during inflation. * * @param res the resources used to inflate density-dependent values + * @hide */ - final void updateDensity(Resources res) { + protected final void updateDensity(Resources res) { mDrawableContainerState.updateDensity(res); } @@ -711,7 +712,10 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { boolean mHasTintList; boolean mHasTintMode; - DrawableContainerState(DrawableContainerState orig, DrawableContainer owner, + /** + * @hide + */ + protected DrawableContainerState(DrawableContainerState orig, DrawableContainer owner, Resources res) { mOwner = owner; mSourceRes = res != null ? res : (orig != null ? orig.mSourceRes : null);