Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0c03664f authored by Teng-Hui Zhu's avatar Teng-Hui Zhu
Browse files

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
parent f4c5b344
Loading
Loading
Loading
Loading
+254 −0
Original line number Diff line number Diff line
/*
 * 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;
        }
    }
}
+0 −3
Original line number Diff line number Diff line
@@ -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>
+26 −0
Original line number Diff line number Diff line
<?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>
+27 −0
Original line number Diff line number Diff line
<?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>
 No newline at end of file
+5 −13
Original line number Diff line number Diff line
@@ -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>
Loading