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
This commit is contained in:
Teng-Hui Zhu
2016-08-19 10:06:25 -07:00
parent f4c5b344ae
commit 0c03664fa6
11 changed files with 381 additions and 46 deletions

View File

@@ -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;
}
}
}

View File

@@ -21,7 +21,4 @@ Copyright (C) 2016 The Android Open Source Project
<path
android:fillColor="#FF000000"
android:pathData="M17.65,6.35C16.2,4.9 14.21,4.0 12.0,4.0c-4.42,0.0 -7.99,3.58 -7.99,8.0s3.57,8.0 7.99,8.0c3.73,0.0 6.84,-2.55 7.73,-6.0l-2.08,0.0c-0.82,2.33 -3.04,4.0 -5.65,4.0 -3.31,0.0 -6.0,-2.69 -6.0,-6.0s2.69,-6.0 6.0,-6.0c1.66,0.0 3.1,0.69 4.22,1.78L13.0,11.0l7.0,0.0L20.0,4.0l-2.35,2.35z"/>
<path
android:pathData="M0 0h24v24H0z"
android:fillColor="#00000000"/>
</vector>

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/vector_drawable_progress_bar_large" >
<target
android:name="progressBar"
android:animation="@anim/progress_indeterminate_material" />
<target
android:name="root"
android:animation="@anim/progress_indeterminate_rotation_material" />
</animated-vector>

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/vector_drawable_progress_bar_medium" >
<target
android:name="progressBar"
android:animation="@anim/progress_indeterminate_material" />
<target
android:name="root"
android:animation="@anim/progress_indeterminate_rotation_material" />
</animated-vector>

View File

@@ -13,16 +13,8 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/vector_drawable_progress_bar_large" >
<target
android:name="progressBar"
android:animation="@anim/progress_indeterminate_material" />
<target
android:name="root"
android:animation="@anim/progress_indeterminate_rotation_material" />
</animated-vector>
<com.android.internal.graphics.drawable.AnimationScaleListDrawable
xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/progress_static_material" />
<item android:drawable="@drawable/progress_indeterminate_anim_large_material" />
</com.android.internal.graphics.drawable.AnimationScaleListDrawable>

View File

@@ -13,15 +13,8 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/vector_drawable_progress_bar_medium" >
<target
android:name="progressBar"
android:animation="@anim/progress_indeterminate_material" />
<target
android:name="root"
android:animation="@anim/progress_indeterminate_rotation_material" />
</animated-vector>
<com.android.internal.graphics.drawable.AnimationScaleListDrawable
xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/progress_static_material" />
<item android:drawable="@drawable/progress_indeterminate_anim_medium_material" />
</com.android.internal.graphics.drawable.AnimationScaleListDrawable>

View File

@@ -13,16 +13,17 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/vector_drawable_progress_bar_small" >
<target
android:name="progressBar"
android:animation="@anim/progress_indeterminate_material" />
<target
android:name="root"
android:animation="@anim/progress_indeterminate_rotation_material" />
</animated-vector>
<com.android.internal.graphics.drawable.AnimationScaleListDrawable
xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/progress_static_material" />
<item>
<animated-vector android:drawable="@drawable/vector_drawable_progress_bar_small" >
<target
android:name="progressBar"
android:animation="@anim/progress_indeterminate_material" />
<target
android:name="root"
android:animation="@anim/progress_indeterminate_rotation_material" />
</animated-vector>
</item>
</com.android.internal.graphics.drawable.AnimationScaleListDrawable>

View File

@@ -0,0 +1,25 @@
<!--
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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24.0dp"
android:height="24.0dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
>
<path
android:fillColor="?attr/colorControlActivated"
android:pathData="M17.65,6.35C16.2,4.9 14.21,4.0 12.0,4.0c-4.42,0.0 -7.99,3.58 -7.99,8.0s3.57,8.0 7.99,8.0c3.73,0.0 6.84,-2.55 7.73,-6.0l-2.08,0.0c-0.82,2.33 -3.04,4.0 -5.65,4.0 -3.31,0.0 -6.0,-2.69 -6.0,-6.0s2.69,-6.0 6.0,-6.0c1.66,0.0 3.1,0.69 4.22,1.78L13.0,11.0l7.0,0.0L20.0,4.0l-2.35,2.35z"/>
</vector>

View File

@@ -5312,6 +5312,21 @@ i
<attr name="alpha" />
</declare-styleable>
<!-- Drawable used to render according to the animation scale. Esp. when it is 0 due to battery
saver mode. It should contain one animatable drawable and one static drawable.
@hide -->
<declare-styleable name="AnimationScaleListDrawable">
</declare-styleable>
<!-- Attributes that can be assigned to a AnimationScaleListDrawable item.
@hide -->
<declare-styleable name="AnimationScaleListDrawableItem">
<!-- Reference to a drawable resource to use for the state. If not
given, the drawable must be defined by the first child tag. -->
<attr name="drawable" />
</declare-styleable>
<!-- Drawable used to render a geometric shape, with a gradient or a solid color. -->
<declare-styleable name="GradientDrawable">
<!-- Indicates whether the drawable should intially be visible. -->

View File

@@ -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);
}

View File

@@ -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);