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

Commit 2a0d4a1f authored by Alan Viverette's avatar Alan Viverette Committed by Android (Google) Code Review
Browse files

Merge "Update AnimatedStateListDrawable to work with Animatable drawables"

parents 034b20c1 f456b1f0
Loading
Loading
Loading
Loading
+1 −1
Original line number Original line Diff line number Diff line
@@ -11287,7 +11287,7 @@ package android.graphics.drawable {
  public class AnimatedStateListDrawable extends android.graphics.drawable.StateListDrawable {
  public class AnimatedStateListDrawable extends android.graphics.drawable.StateListDrawable {
    ctor public AnimatedStateListDrawable();
    ctor public AnimatedStateListDrawable();
    method public void addState(int[], android.graphics.drawable.Drawable, int);
    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 {
  public class AnimatedVectorDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Animatable {
+1 −1
Original line number Original line Diff line number Diff line
@@ -17,7 +17,7 @@
package android.graphics.drawable;
package android.graphics.drawable;


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


    @Override
    @Override
    public boolean setVisible(boolean visible, boolean restart) {
    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);
        final boolean changed = super.setVisible(visible, restart);

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

        return changed;
        return changed;
    }
    }


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

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


    /**
    /**
     * Adds a new transition between keyframes.
     * Adds a new transition between keyframes.
     *
     *
     * @param fromId Unique identifier of the starting keyframe
     * @param fromId Unique identifier of the starting keyframe
     * @param toId Unique identifier of the ending 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
     * @param reversible Whether the transition can be reversed
     */
     */
    public void addTransition(int fromId, int toId, AnimationDrawable anim, boolean reversible) {
    public void addTransition(int fromId, int toId, @NonNull Drawable transition,
        mState.addTransition(fromId, toId, anim, reversible);
            boolean reversible) {
        if (transition == null) {
            throw new IllegalArgumentException("Transition drawable must not be null");
        }

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


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


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


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


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

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


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


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

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


        final Drawable d = getCurrent();
        final Drawable d = getCurrent();
        if (!(d instanceof AnimationDrawable)) {
        if (d instanceof AnimationDrawable) {
            // Transition isn't an animation.
            // 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;
            return false;
        }
        }


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

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

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


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


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


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


    @Override
    @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 {
            throws XmlPullParserException, IOException {
        final TypedArray a = r.obtainAttributes(attrs, R.styleable.AnimatedStateListDrawable);
        final TypedArray a = r.obtainAttributes(attrs, R.styleable.AnimatedStateListDrawable);


@@ -260,7 +299,8 @@ public class AnimatedStateListDrawable extends StateListDrawable {
        onStateChange(getState());
        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 {
            throws XmlPullParserException, IOException {
        int drawableRes = 0;
        int drawableRes = 0;
        int fromId = 0;
        int fromId = 0;
@@ -304,19 +344,11 @@ public class AnimatedStateListDrawable extends StateListDrawable {
            dr = Drawable.createFromXmlInner(r, parser, attrs, theme);
            dr = Drawable.createFromXmlInner(r, parser, attrs, theme);
        }
        }


        final AnimationDrawable anim;
        return mState.addTransition(fromId, toId, dr, reversible);
        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);
    }
    }


    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 {
            throws XmlPullParserException, IOException {
        int drawableRes = 0;
        int drawableRes = 0;
        int keyframeId = 0;
        int keyframeId = 0;
@@ -390,8 +422,8 @@ public class AnimatedStateListDrawable extends StateListDrawable {
        final LongSparseLongArray mTransitions;
        final LongSparseLongArray mTransitions;
        final SparseIntArray mStateIds;
        final SparseIntArray mStateIds;


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


            if (orig != null) {
            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 int pos = super.addChild(anim);
            final long keyFromTo = generateTransitionKey(fromId, toId);
            final long keyFromTo = generateTransitionKey(fromId, toId);
            mTransitions.append(keyFromTo, pos);
            mTransitions.append(keyFromTo, pos);
@@ -416,13 +448,13 @@ public class AnimatedStateListDrawable extends StateListDrawable {
            return addChild(anim);
            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);
            final int index = super.addStateSet(stateSet, drawable);
            mStateIds.put(index, id);
            mStateIds.put(index, id);
            return index;
            return index;
        }
        }


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


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


        mState = state;
        mState = state;
    }
    }


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


        final AnimatedStateListState newState = new AnimatedStateListState(state, this, res);
        final AnimatedStateListState newState = new AnimatedStateListState(state, this, res);
+9 −0
Original line number Original line Diff line number Diff line
@@ -70,6 +70,15 @@
                <category android:name="com.android.test.dynamic.TEST" />
                <category android:name="com.android.test.dynamic.TEST" />
            </intent-filter>
            </intent-filter>
        </activity>
        </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
        <activity
            android:name="VectorDrawable01"
            android:name="VectorDrawable01"
            android:label="VectorTest1" >
            android:label="VectorTest1" >
+47 −0
Original line number Original line 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