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

Commit f456b1f0 authored by Alan Viverette's avatar Alan Viverette
Browse files

Update AnimatedStateListDrawable to work with Animatable drawables

BUG: 16016730
Change-Id: I6d02a1235c0aecd7e62f12226f3689372d043ddd
parent 40d43b27
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -11284,7 +11284,7 @@ package android.graphics.drawable {
  public class AnimatedStateListDrawable extends android.graphics.drawable.StateListDrawable {
    ctor public AnimatedStateListDrawable();
    method public void addState(int[], android.graphics.drawable.Drawable, int);
    method public void addTransition(int, int, android.graphics.drawable.AnimationDrawable, boolean);
    method public void addTransition(int, int, android.graphics.drawable.Drawable, boolean);
  }
  public class AnimatedVectorDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Animatable {
+1 −1
Original line number Diff line number Diff line
@@ -17,7 +17,7 @@
package android.graphics.drawable;

/**
 * Interface that drawables suporting animations should implement.
 * Interface that drawables supporting animations should implement.
 */
public interface Animatable {
    /**
+77 −45
Original line number Diff line number Diff line
@@ -20,6 +20,8 @@ import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
@@ -82,17 +84,23 @@ public class AnimatedStateListDrawable extends StateListDrawable {

    @Override
    public boolean setVisible(boolean visible, boolean restart) {
        // If we're relying on an Animatable transition, the super method
        // will handle visibility changes.
        final boolean changed = super.setVisible(visible, restart);

        if (mAnim != null) {
            if (visible) {
                if (changed || restart) {
                    // TODO: Should this support restart?
                    mAnim.end();
                if (restart) {
                    mAnim.cancel();
                    mAnim.start();
                } else if (changed && mAnim.isPaused()) {
                    mAnim.resume();
                }
            } else {
                mAnim.end();
            } else if (mAnim.isRunning()) {
                mAnim.pause();
            }
        }

        return changed;
    }

@@ -100,26 +108,33 @@ public class AnimatedStateListDrawable extends StateListDrawable {
     * Add a new drawable to the set of keyframes.
     *
     * @param stateSet An array of resource IDs to associate with the keyframe
     * @param drawable The drawable to show when in the specified state
     * @param drawable The drawable to show when in the specified state, may not be null
     * @param id The unique identifier for the keyframe
     */
    public void addState(int[] stateSet, Drawable drawable, int id) {
        if (drawable != null) {
    public void addState(@NonNull int[] stateSet, @NonNull Drawable drawable, int id) {
        if (drawable == null) {
            throw new IllegalArgumentException("Drawable must not be null");
        }

        mState.addStateSet(stateSet, drawable, id);
        onStateChange(getState());
    }
    }

    /**
     * Adds a new transition between keyframes.
     *
     * @param fromId Unique identifier of the starting keyframe
     * @param toId Unique identifier of the ending keyframe
     * @param anim An AnimationDrawable to use as a transition
     * @param transition An animatable drawable to use as a transition, may not be null
     * @param reversible Whether the transition can be reversed
     */
    public void addTransition(int fromId, int toId, AnimationDrawable anim, boolean reversible) {
        mState.addTransition(fromId, toId, anim, reversible);
    public void addTransition(int fromId, int toId, @NonNull Drawable transition,
            boolean reversible) {
        if (transition == null) {
            throw new IllegalArgumentException("Transition drawable must not be null");
        }

        mState.addTransition(fromId, toId, transition, reversible);
    }

    @Override
@@ -131,13 +146,16 @@ public class AnimatedStateListDrawable extends StateListDrawable {
    protected boolean onStateChange(int[] stateSet) {
        final int keyframeIndex = mState.indexOfKeyframe(stateSet);
        if (keyframeIndex == getCurrentIndex()) {
            // No transition needed.
            return false;
        }

        // Attempt to find a valid transition to the keyframe.
        if (selectTransition(keyframeIndex)) {
            return true;
        }

        // No valid transition, attempt to jump directly to the keyframe.
        if (selectDrawable(keyframeIndex)) {
            return true;
        }
@@ -146,10 +164,14 @@ public class AnimatedStateListDrawable extends StateListDrawable {
    }

    private boolean selectTransition(int toIndex) {
        if (mAnim != null) {
        if (toIndex == mAnimToIndex) {
            // Already animating to that keyframe.
            return true;
        }

        if (mAnim != null) {
            if (toIndex == mAnimToIndex) {
                return true;
            } else if (toIndex == mAnimFromIndex) {
                // Reverse the current animation.
                mAnim.reverse();
@@ -159,9 +181,14 @@ public class AnimatedStateListDrawable extends StateListDrawable {
            }

            // Changing animation, end the current animation.
            mAnim.end();
            mAnim.cancel();
            mAnim = null;
        }

        // Reset state.
        mAnimFromIndex = -1;
        mAnimToIndex = -1;

        final AnimatedStateListState state = mState;
        final int fromIndex = getCurrentIndex();
        final int fromId = state.getKeyframeIdAt(fromIndex);
@@ -179,42 +206,54 @@ public class AnimatedStateListDrawable extends StateListDrawable {
        }

        final Drawable d = getCurrent();
        if (!(d instanceof AnimationDrawable)) {
            // Transition isn't an animation.
        if (d instanceof AnimationDrawable) {
            // We can support reverse() here.
            final boolean reversed = mState.isTransitionReversed(fromId, toId);
            mAnim = getAnimationDrawableAnimator((AnimationDrawable) d, reversed);
            mAnim.start();
        } else if (d instanceof Animatable) {
            // Let the transition animate itself.
            ((Animatable) d).start();
        } else {
            // We don't know how to animate this transition.
            return false;
        }

        final AnimationDrawable ad = (AnimationDrawable) d;
        final boolean reversed = mState.isTransitionReversed(fromId, toId);
        mAnimFromIndex = fromIndex;
        mAnimToIndex = toIndex;
        return true;
    }

    private ObjectAnimator getAnimationDrawableAnimator(@NonNull AnimationDrawable ad,
            boolean reversed) {
        final int frameCount = ad.getNumberOfFrames();
        final int fromFrame = reversed ? frameCount - 1 : 0;
        final int toFrame = reversed ? 0 : frameCount - 1;

        final FrameInterpolator interp = new FrameInterpolator(ad, reversed);
        final ObjectAnimator anim = ObjectAnimator.ofInt(ad, "currentIndex", fromFrame, toFrame);
        anim.setAutoCancel(true);
        anim.setDuration(interp.getTotalDuration());
        anim.addListener(mAnimListener);
        anim.setInterpolator(interp);
        anim.start();

        mAnim = anim;
        mAnimFromIndex = fromIndex;
        mAnimToIndex = toIndex;
        return true;
        return anim;
    }

    @Override
    public void jumpToCurrentState() {
        // If we're relying on an Animatable transition, the super method
        // will handle jumping it to the current state.
        super.jumpToCurrentState();

        if (mAnim != null) {
            mAnim.end();
            mAnim = null;
        }
    }

    @Override
    public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
    public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
            @NonNull AttributeSet attrs, @Nullable Theme theme)
            throws XmlPullParserException, IOException {
        final TypedArray a = r.obtainAttributes(attrs, R.styleable.AnimatedStateListDrawable);

@@ -260,7 +299,8 @@ public class AnimatedStateListDrawable extends StateListDrawable {
        onStateChange(getState());
    }

    private int parseTransition(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
    private int parseTransition(@NonNull Resources r, @NonNull XmlPullParser parser,
            @NonNull AttributeSet attrs, @Nullable Theme theme)
            throws XmlPullParserException, IOException {
        int drawableRes = 0;
        int fromId = 0;
@@ -304,19 +344,11 @@ public class AnimatedStateListDrawable extends StateListDrawable {
            dr = Drawable.createFromXmlInner(r, parser, attrs, theme);
        }

        final AnimationDrawable anim;
        if (dr instanceof AnimationDrawable) {
            anim = (AnimationDrawable) dr;
        } else {
            throw new XmlPullParserException(parser.getPositionDescription()
                    + ": <transition> tag requires a 'drawable' attribute or "
                    + "child tag defining a drawable of type <animation>");
        }

        return mState.addTransition(fromId, toId, anim, reversible);
        return mState.addTransition(fromId, toId, dr, reversible);
    }

    private int parseItem(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
    private int parseItem(@NonNull Resources r, @NonNull XmlPullParser parser,
            @NonNull AttributeSet attrs, @Nullable Theme theme)
            throws XmlPullParserException, IOException {
        int drawableRes = 0;
        int keyframeId = 0;
@@ -390,8 +422,8 @@ public class AnimatedStateListDrawable extends StateListDrawable {
        final LongSparseLongArray mTransitions;
        final SparseIntArray mStateIds;

        AnimatedStateListState(AnimatedStateListState orig, AnimatedStateListDrawable owner,
                Resources res) {
        AnimatedStateListState(@Nullable AnimatedStateListState orig,
                @NonNull AnimatedStateListDrawable owner, @Nullable Resources res) {
            super(orig, owner, res);

            if (orig != null) {
@@ -403,7 +435,7 @@ public class AnimatedStateListDrawable extends StateListDrawable {
            }
        }

        int addTransition(int fromId, int toId, AnimationDrawable anim, boolean reversible) {
        int addTransition(int fromId, int toId, @NonNull Drawable anim, boolean reversible) {
            final int pos = super.addChild(anim);
            final long keyFromTo = generateTransitionKey(fromId, toId);
            mTransitions.append(keyFromTo, pos);
@@ -416,13 +448,13 @@ public class AnimatedStateListDrawable extends StateListDrawable {
            return addChild(anim);
        }

        int addStateSet(int[] stateSet, Drawable drawable, int id) {
        int addStateSet(@NonNull int[] stateSet, @NonNull Drawable drawable, int id) {
            final int index = super.addStateSet(stateSet, drawable);
            mStateIds.put(index, id);
            return index;
        }

        int indexOfKeyframe(int[] stateSet) {
        int indexOfKeyframe(@NonNull int[] stateSet) {
            final int index = super.indexOfStateSet(stateSet);
            if (index >= 0) {
                return index;
@@ -460,13 +492,13 @@ public class AnimatedStateListDrawable extends StateListDrawable {
        }
    }

    void setConstantState(AnimatedStateListState state) {
    void setConstantState(@NonNull AnimatedStateListState state) {
        super.setConstantState(state);

        mState = state;
    }

    private AnimatedStateListDrawable(AnimatedStateListState state, Resources res) {
    private AnimatedStateListDrawable(@Nullable AnimatedStateListState state, @Nullable Resources res) {
        super(null);

        final AnimatedStateListState newState = new AnimatedStateListState(state, this, res);
+9 −0
Original line number Diff line number Diff line
@@ -70,6 +70,15 @@
                <category android:name="com.android.test.dynamic.TEST" />
            </intent-filter>
        </activity>
        <activity
            android:name="AnimatedStateVectorDrawableTest"
            android:label="AnimatedStateList and AnimatedVectorDrawable" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="com.android.test.dynamic.TEST" />
            </intent-filter>
        </activity>
        <activity
            android:name="VectorDrawable01"
            android:label="VectorTest1" >
+47 −0
Original line number Diff line number Diff line
<!--
 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.
-->

<animated-selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/on" android:state_checked="true"
        android:drawable="@drawable/vector_drawable12" />
    <item android:id="@+id/off"
        android:drawable="@drawable/vector_drawable12" />
    <transition android:fromId="@+id/off" android:toId="@+id/on">
        <animated-vector android:drawable="@drawable/vector_drawable12">
            <target
                android:name="pie1"
                android:animation="@anim/trim_path_animation01" />
            <target
                android:name="v"
                android:animation="@anim/trim_path_animation02" />
            <target
                android:name="v"
                android:animation="@anim/trim_path_animation05" />
            <target
                android:name="rotationGroup"
                android:animation="@anim/trim_path_animation03" />
            <target
                android:name="rotationGroup3"
                android:animation="@anim/trim_path_animation03" />
            <target
                android:name="rotationGroupBlue"
                android:animation="@anim/trim_path_animation03" />
            <target
                android:name="rotationGroup"
                android:animation="@anim/trim_path_animation04" />
        </animated-vector>
    </transition>
</animated-selector>
Loading