AnimatedStateListDrawable.java revision 5b84eace6cb79c42fe43480f08c68b7dea4e074a
1289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza/*
2289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza * Copyright (C) 2014 The Android Open Source Project
3289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza *
4289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza * Licensed under the Apache License, Version 2.0 (the "License");
5289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza * you may not use this file except in compliance with the License.
6289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza * You may obtain a copy of the License at
7289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza *
8289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza *      http://www.apache.org/licenses/LICENSE-2.0
9289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza *
10289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza * Unless required by applicable law or agreed to in writing, software
11289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza * distributed under the License is distributed on an "AS IS" BASIS,
12289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza * See the License for the specific language governing permissions and
14289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza * limitations under the License.
15289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza */
16289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
173e96f1982fda358424b0b75f394cbf7c1794a072Dan Stozapackage android.graphics.drawable;
183e96f1982fda358424b0b75f394cbf7c1794a072Dan Stoza
193e96f1982fda358424b0b75f394cbf7c1794a072Dan Stozaimport android.animation.ObjectAnimator;
203e96f1982fda358424b0b75f394cbf7c1794a072Dan Stozaimport android.animation.TimeInterpolator;
21289ade165e60b5f71734d30e535f16eb1f4313adDan Stozaimport android.annotation.NonNull;
22289ade165e60b5f71734d30e535f16eb1f4313adDan Stozaimport android.annotation.Nullable;
23289ade165e60b5f71734d30e535f16eb1f4313adDan Stozaimport android.content.res.Resources;
24289ade165e60b5f71734d30e535f16eb1f4313adDan Stozaimport android.content.res.Resources.Theme;
25289ade165e60b5f71734d30e535f16eb1f4313adDan Stozaimport android.content.res.TypedArray;
26289ade165e60b5f71734d30e535f16eb1f4313adDan Stozaimport android.util.AttributeSet;
27289ade165e60b5f71734d30e535f16eb1f4313adDan Stozaimport android.util.Log;
28289ade165e60b5f71734d30e535f16eb1f4313adDan Stozaimport android.util.LongSparseLongArray;
29289ade165e60b5f71734d30e535f16eb1f4313adDan Stozaimport android.util.SparseIntArray;
30289ade165e60b5f71734d30e535f16eb1f4313adDan Stozaimport android.util.StateSet;
31289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
32289ade165e60b5f71734d30e535f16eb1f4313adDan Stozaimport com.android.internal.R;
33289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
34289ade165e60b5f71734d30e535f16eb1f4313adDan Stozaimport org.xmlpull.v1.XmlPullParser;
35289ade165e60b5f71734d30e535f16eb1f4313adDan Stozaimport org.xmlpull.v1.XmlPullParserException;
36289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
37289ade165e60b5f71734d30e535f16eb1f4313adDan Stozaimport java.io.IOException;
38289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
39289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza/**
40289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza * Drawable containing a set of Drawable keyframes where the currently displayed
41289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza * keyframe is chosen based on the current state set. Animations between
42289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza * keyframes may optionally be defined using transition elements.
43289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza * <p>
44289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza * This drawable can be defined in an XML file with the <code>
45289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza * &lt;animated-selector></code> element. Each keyframe Drawable is defined in a
46289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza * nested <code>&lt;item></code> element. Transitions are defined in a nested
47289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza * <code>&lt;transition></code> element.
48289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza *
49289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza * @attr ref android.R.styleable#DrawableStates_state_focused
50289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza * @attr ref android.R.styleable#DrawableStates_state_window_focused
513e96f1982fda358424b0b75f394cbf7c1794a072Dan Stoza * @attr ref android.R.styleable#DrawableStates_state_enabled
52289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza * @attr ref android.R.styleable#DrawableStates_state_checkable
533e96f1982fda358424b0b75f394cbf7c1794a072Dan Stoza * @attr ref android.R.styleable#DrawableStates_state_checked
54289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza * @attr ref android.R.styleable#DrawableStates_state_selected
55289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza * @attr ref android.R.styleable#DrawableStates_state_activated
56289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza * @attr ref android.R.styleable#DrawableStates_state_active
57289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza * @attr ref android.R.styleable#DrawableStates_state_single
58289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza * @attr ref android.R.styleable#DrawableStates_state_first
59289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza * @attr ref android.R.styleable#DrawableStates_state_middle
60289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza * @attr ref android.R.styleable#DrawableStates_state_last
61289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza * @attr ref android.R.styleable#DrawableStates_state_pressed
62289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza */
63289ade165e60b5f71734d30e535f16eb1f4313adDan Stozapublic class AnimatedStateListDrawable extends StateListDrawable {
64289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    private static final String LOGTAG = AnimatedStateListDrawable.class.getSimpleName();
65289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
66289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    private static final String ELEMENT_TRANSITION = "transition";
67289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    private static final String ELEMENT_ITEM = "item";
68289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
69289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    private AnimatedStateListState mState;
70289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
71289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    /** The currently running transition, if any. */
72289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    private Transition mTransition;
73289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
74289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    /** Index to be set after the transition ends. */
75289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    private int mTransitionToIndex = -1;
76289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
77289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    /** Index away from which we are transitioning. */
78289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    private int mTransitionFromIndex = -1;
793e96f1982fda358424b0b75f394cbf7c1794a072Dan Stoza
80289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    private boolean mMutated;
813e96f1982fda358424b0b75f394cbf7c1794a072Dan Stoza
82289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    public AnimatedStateListDrawable() {
83289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        this(null, null);
84289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    }
85289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
863e96f1982fda358424b0b75f394cbf7c1794a072Dan Stoza    @Override
87289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    public boolean setVisible(boolean visible, boolean restart) {
88289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        final boolean changed = super.setVisible(visible, restart);
89289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
90289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        if (mTransition != null && (changed || restart)) {
91289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            if (visible) {
92289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                mTransition.start();
93289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            } else {
94289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                // Ensure we're showing the correct state when visible.
95289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                jumpToCurrentState();
96289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            }
97289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        }
98289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
99289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        return changed;
100289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    }
101289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
102289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    /**
103289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza     * Add a new drawable to the set of keyframes.
104289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza     *
105289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza     * @param stateSet An array of resource IDs to associate with the keyframe
106289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza     * @param drawable The drawable to show when in the specified state, may not be null
107289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza     * @param id The unique identifier for the keyframe
108289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza     */
109289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    public void addState(@NonNull int[] stateSet, @NonNull Drawable drawable, int id) {
110289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        if (drawable == null) {
111289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            throw new IllegalArgumentException("Drawable must not be null");
112289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        }
113289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
114289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        mState.addStateSet(stateSet, drawable, id);
115289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        onStateChange(getState());
116289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    }
117289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
118289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    /**
119289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza     * Adds a new transition between keyframes.
120289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza     *
121289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza     * @param fromId Unique identifier of the starting keyframe
122289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza     * @param toId Unique identifier of the ending keyframe
123289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza     * @param transition An {@link Animatable} drawable to use as a transition, may not be null
124289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza     * @param reversible Whether the transition can be reversed
125289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza     */
126289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    public <T extends Drawable & Animatable> void addTransition(int fromId, int toId,
127289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            @NonNull T transition, boolean reversible) {
128289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        if (transition == null) {
129289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            throw new IllegalArgumentException("Transition drawable must not be null");
130289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        }
131289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
132289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        mState.addTransition(fromId, toId, transition, reversible);
133289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    }
134289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
135289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    @Override
136289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    public boolean isStateful() {
137289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        return true;
138289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    }
139289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
140289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    @Override
141289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    protected boolean onStateChange(int[] stateSet) {
142289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        // If we're not already at the target index, either attempt to find a
143289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        // valid transition to it or jump directly there.
144289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        final int targetIndex = mState.indexOfKeyframe(stateSet);
145289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        boolean changed = targetIndex != getCurrentIndex()
146289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                && (selectTransition(targetIndex) || selectDrawable(targetIndex));
147289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
148289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        // We need to propagate the state change to the current drawable, but
149289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        // we can't call StateListDrawable.onStateChange() without changing the
150289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        // current drawable.
151289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        final Drawable current = getCurrent();
152289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        if (current != null) {
153289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            changed |= current.setState(stateSet);
154289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        }
155289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
156289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        return changed;
157289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    }
158289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
159289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    private boolean selectTransition(int toIndex) {
160289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        final int fromIndex;
161289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        final Transition currentTransition = mTransition;
162289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        if (currentTransition != null) {
163289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            if (toIndex == mTransitionToIndex) {
164289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                // Already animating to that keyframe.
165289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                return true;
166289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            } else if (toIndex == mTransitionFromIndex && currentTransition.canReverse()) {
167289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                // Reverse the current animation.
168289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                currentTransition.reverse();
169289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                mTransitionToIndex = mTransitionFromIndex;
170289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                mTransitionFromIndex = toIndex;
171289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                return true;
172289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            }
173289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
174289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            // Start the next transition from the end of the current one.
175289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            fromIndex = mTransitionToIndex;
1763e96f1982fda358424b0b75f394cbf7c1794a072Dan Stoza
177289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            // Changing animation, end the current animation.
178289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            currentTransition.stop();
179289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        } else {
180289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            fromIndex = getCurrentIndex();
181289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        }
182289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
183289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        // Reset state.
184289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        mTransition = null;
185289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        mTransitionFromIndex = -1;
186289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        mTransitionToIndex = -1;
187289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
188289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        final AnimatedStateListState state = mState;
189289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        final int fromId = state.getKeyframeIdAt(fromIndex);
190289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        final int toId = state.getKeyframeIdAt(toIndex);
191289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        if (toId == 0 || fromId == 0) {
192289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            // Missing a keyframe ID.
193289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            return false;
194289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        }
195289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
196289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        final int transitionIndex = state.indexOfTransition(fromId, toId);
197289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        if (transitionIndex < 0) {
198289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            // Couldn't select a transition.
199289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            return false;
200289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        }
201289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
202289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        boolean hasReversibleFlag = state.transitionHasReversibleFlag(fromId, toId);
203289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
204289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        // This may fail if we're already on the transition, but that's okay!
205289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        selectDrawable(transitionIndex);
206289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
207289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        final Transition transition;
208289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        final Drawable d = getCurrent();
209289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        if (d instanceof AnimationDrawable) {
210289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            final boolean reversed = state.isTransitionReversed(fromId, toId);
211289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
212289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            transition = new AnimationDrawableTransition((AnimationDrawable) d,
213289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                    reversed, hasReversibleFlag);
214289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        } else if (d instanceof AnimatedVectorDrawable) {
215289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            final boolean reversed = state.isTransitionReversed(fromId, toId);
216289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
217289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            transition = new AnimatedVectorDrawableTransition((AnimatedVectorDrawable) d,
218289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                    reversed, hasReversibleFlag);
219289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        } else if (d instanceof Animatable) {
220289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            transition = new AnimatableTransition((Animatable) d);
221289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        } else {
222289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            // We don't know how to animate this transition.
223289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            return false;
224289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        }
225289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
226289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        transition.start();
227289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
228289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        mTransition = transition;
229289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        mTransitionFromIndex = fromIndex;
230289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        mTransitionToIndex = toIndex;
231289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        return true;
232289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    }
233289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
234289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    private static abstract class Transition {
235289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        public abstract void start();
236289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        public abstract void stop();
237289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
238289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        public void reverse() {
239289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            // Not supported by default.
240289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        }
241289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
242289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        public boolean canReverse() {
243289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            return false;
244289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        }
245289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    }
246289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
247289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    private static class AnimatableTransition  extends Transition {
248289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        private final Animatable mA;
249289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
250289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        public AnimatableTransition(Animatable a) {
251289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            mA = a;
252289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        }
253289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
254289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        @Override
255289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        public void start() {
256289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            mA.start();
257289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        }
258289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
259289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        @Override
260289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        public void stop() {
261289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            mA.stop();
262289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        }
263289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    }
264289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
265289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
266289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    private static class AnimationDrawableTransition  extends Transition {
267289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        private final ObjectAnimator mAnim;
268289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
269289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        // Even AnimationDrawable is always reversible technically, but
270289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        // we should obey the XML's android:reversible flag.
271289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        private final boolean mHasReversibleFlag;
272289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
273289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        public AnimationDrawableTransition(AnimationDrawable ad,
274289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                boolean reversed, boolean hasReversibleFlag) {
275289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            final int frameCount = ad.getNumberOfFrames();
276289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            final int fromFrame = reversed ? frameCount - 1 : 0;
277289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            final int toFrame = reversed ? 0 : frameCount - 1;
278289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            final FrameInterpolator interp = new FrameInterpolator(ad, reversed);
279289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            final ObjectAnimator anim = ObjectAnimator.ofInt(ad, "currentIndex", fromFrame, toFrame);
280289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            anim.setAutoCancel(true);
281289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            anim.setDuration(interp.getTotalDuration());
282289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            anim.setInterpolator(interp);
283289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            mHasReversibleFlag = hasReversibleFlag;
284289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            mAnim = anim;
285289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        }
286289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
287289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        @Override
288289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        public boolean canReverse() {
289289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            return mHasReversibleFlag;
290289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        }
291289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
292289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        @Override
293289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        public void start() {
294289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            mAnim.start();
295289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        }
296289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
297289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        @Override
298289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        public void reverse() {
299289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            mAnim.reverse();
300289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        }
301289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
302289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        @Override
303289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        public void stop() {
304289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            mAnim.cancel();
305289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        }
306289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    }
307289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
308289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    private static class AnimatedVectorDrawableTransition  extends Transition {
309289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        private final AnimatedVectorDrawable mAvd;
310289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
311289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        // mReversed is indicating the current transition's direction.
312289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        private final boolean mReversed;
313289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
314289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        // mHasReversibleFlag is indicating whether the whole transition has
315289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        // reversible flag set to true.
316289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        // If mHasReversibleFlag is false, then mReversed is always false.
317289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        private final boolean mHasReversibleFlag;
318289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
319289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        public AnimatedVectorDrawableTransition(AnimatedVectorDrawable avd,
320289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                boolean reversed, boolean hasReversibleFlag) {
321289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            mAvd = avd;
322289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            mReversed = reversed;
323289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            mHasReversibleFlag = hasReversibleFlag;
324289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        }
325289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
326289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        @Override
327289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        public boolean canReverse() {
328289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            // When the transition's XML says it is not reversible, then we obey
329289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            // it, even if the AVD itself is reversible.
330289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            // This will help the single direction transition.
331289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            return mAvd.canReverse() && mHasReversibleFlag;
332289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        }
333289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
334289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        @Override
335289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        public void start() {
336289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            if (mReversed) {
337289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                reverse();
338289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            } else {
339289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                mAvd.start();
340289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            }
341289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        }
342289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
343289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        @Override
344289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        public void reverse() {
345289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            if (canReverse()) {
346289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                mAvd.reverse();
347289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            } else {
348289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                Log.w(LOGTAG, "Can't reverse, either the reversible is set to false,"
349289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                        + " or the AnimatedVectorDrawable can't reverse");
350289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            }
351289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        }
352289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
353289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        @Override
354289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        public void stop() {
355289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            mAvd.stop();
356289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        }
357289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    }
358289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
359289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
360289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    @Override
361289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    public void jumpToCurrentState() {
362289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        super.jumpToCurrentState();
363289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
364289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        if (mTransition != null) {
365289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            mTransition.stop();
366289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            mTransition = null;
367289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
368289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            selectDrawable(mTransitionToIndex);
369289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            mTransitionToIndex = -1;
370289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            mTransitionFromIndex = -1;
371289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        }
372289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    }
373289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
374289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    @Override
375289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
376289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            @NonNull AttributeSet attrs, @Nullable Theme theme)
377289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            throws XmlPullParserException, IOException {
378289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        final TypedArray a = obtainAttributes(
379289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                r, theme, attrs, R.styleable.AnimatedStateListDrawable);
380289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        super.inflateWithAttributes(r, parser, a, R.styleable.AnimatedStateListDrawable_visible);
381289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        updateStateFromTypedArray(a);
382289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        a.recycle();
383289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
384289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        inflateChildElements(r, parser, attrs, theme);
385289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
386289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        init();
387289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    }
388289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
389289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    @Override
390289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    public void applyTheme(@Nullable Theme theme) {
391289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        super.applyTheme(theme);
392289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
393289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        final AnimatedStateListState state = mState;
394289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        if (state == null || state.mAnimThemeAttrs == null) {
395289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            return;
396289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        }
397289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
398289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        final TypedArray a = theme.resolveAttributes(
399289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                state.mAnimThemeAttrs, R.styleable.AnimatedRotateDrawable);
400289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        updateStateFromTypedArray(a);
401289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        a.recycle();
402289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
403289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        init();
404289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    }
405289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
406289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    private void updateStateFromTypedArray(TypedArray a) {
407289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        final AnimatedStateListState state = mState;
408289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
409289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        // Account for any configuration changes.
410289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        state.mChangingConfigurations |= a.getChangingConfigurations();
411289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
412289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        // Extract the theme attributes, if any.
413289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        state.mAnimThemeAttrs = a.extractThemeAttrs();
414289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
415289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        state.setVariablePadding(a.getBoolean(
416289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                R.styleable.AnimatedStateListDrawable_variablePadding, state.mVariablePadding));
417289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        state.setConstantSize(a.getBoolean(
418289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                R.styleable.AnimatedStateListDrawable_constantSize, state.mConstantSize));
419289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        state.setEnterFadeDuration(a.getInt(
420289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                R.styleable.AnimatedStateListDrawable_enterFadeDuration, state.mEnterFadeDuration));
421289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        state.setExitFadeDuration(a.getInt(
422289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                R.styleable.AnimatedStateListDrawable_exitFadeDuration, state.mExitFadeDuration));
423289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
424289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        setDither(a.getBoolean(
425289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                R.styleable.AnimatedStateListDrawable_dither, state.mDither));
426289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        setAutoMirrored(a.getBoolean(
427289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                R.styleable.AnimatedStateListDrawable_autoMirrored, state.mAutoMirrored));
428289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    }
429289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
430289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    private void init() {
431289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        onStateChange(getState());
432289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    }
433289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
434289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    private void inflateChildElements(Resources r, XmlPullParser parser, AttributeSet attrs,
435289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            Theme theme) throws XmlPullParserException, IOException {
436289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        int type;
437289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
438289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        final int innerDepth = parser.getDepth() + 1;
439289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        int depth;
440289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
441289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                && ((depth = parser.getDepth()) >= innerDepth
442289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                || type != XmlPullParser.END_TAG)) {
443289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            if (type != XmlPullParser.START_TAG) {
444289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                continue;
445289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            }
446289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
447289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            if (depth > innerDepth) {
448289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                continue;
449289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            }
450289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
451289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            if (parser.getName().equals(ELEMENT_ITEM)) {
452289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                parseItem(r, parser, attrs, theme);
453289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            } else if (parser.getName().equals(ELEMENT_TRANSITION)) {
454289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                parseTransition(r, parser, attrs, theme);
455289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            }
456289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        }
457289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    }
458289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
459289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    private int parseTransition(@NonNull Resources r, @NonNull XmlPullParser parser,
460289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            @NonNull AttributeSet attrs, @Nullable Theme theme)
461289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            throws XmlPullParserException, IOException {
462289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        // This allows state list drawable item elements to be themed at
463289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        // inflation time but does NOT make them work for Zygote preload.
464289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        final TypedArray a = obtainAttributes(r, theme, attrs,
465289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                R.styleable.AnimatedStateListDrawableTransition);
466289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        final int fromId = a.getResourceId(
467289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                R.styleable.AnimatedStateListDrawableTransition_fromId, 0);
468289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        final int toId = a.getResourceId(
469289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                R.styleable.AnimatedStateListDrawableTransition_toId, 0);
470289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        final boolean reversible = a.getBoolean(
471289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                R.styleable.AnimatedStateListDrawableTransition_reversible, false);
472289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        Drawable dr = a.getDrawable(
473289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                R.styleable.AnimatedStateListDrawableTransition_drawable);
474289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        a.recycle();
475289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
476289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        // Loading child elements modifies the state of the AttributeSet's
477289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        // underlying parser, so it needs to happen after obtaining
478289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        // attributes and extracting states.
479289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        if (dr == null) {
480289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            int type;
481289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            while ((type = parser.next()) == XmlPullParser.TEXT) {
482289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            }
483289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            if (type != XmlPullParser.START_TAG) {
484289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                throw new XmlPullParserException(
485289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                        parser.getPositionDescription()
486289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                                + ": <transition> tag requires a 'drawable' attribute or "
487289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                                + "child tag defining a drawable");
488289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            }
489289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            dr = Drawable.createFromXmlInner(r, parser, attrs, theme);
490289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        }
491289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
492289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        return mState.addTransition(fromId, toId, dr, reversible);
493289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    }
494289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
495289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    private int parseItem(@NonNull Resources r, @NonNull XmlPullParser parser,
496289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            @NonNull AttributeSet attrs, @Nullable Theme theme)
497289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            throws XmlPullParserException, IOException {
498289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        // This allows state list drawable item elements to be themed at
499289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        // inflation time but does NOT make them work for Zygote preload.
500289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        final TypedArray a = obtainAttributes(r, theme, attrs,
5013e96f1982fda358424b0b75f394cbf7c1794a072Dan Stoza                R.styleable.AnimatedStateListDrawableItem);
502289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        final int keyframeId = a.getResourceId(R.styleable.AnimatedStateListDrawableItem_id, 0);
5033e96f1982fda358424b0b75f394cbf7c1794a072Dan Stoza        Drawable dr = a.getDrawable(R.styleable.AnimatedStateListDrawableItem_drawable);
504289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        a.recycle();
505289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
506289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        final int[] states = extractStateSet(attrs);
507289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
508289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        // Loading child elements modifies the state of the AttributeSet's
509289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        // underlying parser, so it needs to happen after obtaining
510289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        // attributes and extracting states.
511289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        if (dr == null) {
512289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            int type;
513289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            while ((type = parser.next()) == XmlPullParser.TEXT) {
514289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            }
515289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            if (type != XmlPullParser.START_TAG) {
516289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                throw new XmlPullParserException(
517289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                        parser.getPositionDescription()
518289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                                + ": <item> tag requires a 'drawable' attribute or "
519289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                                + "child tag defining a drawable");
520289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            }
521289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            dr = Drawable.createFromXmlInner(r, parser, attrs, theme);
522289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        }
523289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
524289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        return mState.addStateSet(states, dr, keyframeId);
525289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    }
526289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
527289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    @Override
528289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    public Drawable mutate() {
529289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        if (!mMutated && super.mutate() == this) {
530289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            final AnimatedStateListState newState = new AnimatedStateListState(mState, this, null);
531289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            setConstantState(newState);
532289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            mMutated = true;
533289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        }
534289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
535289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        return this;
536289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    }
537289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
538289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    /**
539289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza     * @hide
540289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza     */
541289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    public void clearMutated() {
542289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        super.clearMutated();
543289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        mMutated = false;
544289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    }
545289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
546289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    static class AnimatedStateListState extends StateListState {
547289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        // REVERSED_BIT is indicating the current transition's direction.
548289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        private static final long REVERSED_BIT = 0x100000000l;
549289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
550289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        // REVERSIBLE_FLAG_BIT is indicating whether the whole transition has
551289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        // reversible flag set to true.
552289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        private static final long REVERSIBLE_FLAG_BIT = 0x200000000l;
553289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
554289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        int[] mAnimThemeAttrs;
555289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
556289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        final LongSparseLongArray mTransitions;
557289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        final SparseIntArray mStateIds;
558289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
559289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        AnimatedStateListState(@Nullable AnimatedStateListState orig,
560289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                @NonNull AnimatedStateListDrawable owner, @Nullable Resources res) {
561289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            super(orig, owner, res);
562289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
563289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            if (orig != null) {
564289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                mAnimThemeAttrs = orig.mAnimThemeAttrs;
565289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                mTransitions = orig.mTransitions.clone();
566289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                mStateIds = orig.mStateIds.clone();
567289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            } else {
568289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                mTransitions = new LongSparseLongArray();
569289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                mStateIds = new SparseIntArray();
570289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            }
571289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        }
572289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
573289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        int addTransition(int fromId, int toId, @NonNull Drawable anim, boolean reversible) {
574289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            final int pos = super.addChild(anim);
575289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            final long keyFromTo = generateTransitionKey(fromId, toId);
576289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            long reversibleBit = 0;
577289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            if (reversible) {
578289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                reversibleBit = REVERSIBLE_FLAG_BIT;
579289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            }
580289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            mTransitions.append(keyFromTo, pos | reversibleBit);
581289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
582289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            if (reversible) {
583289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                final long keyToFrom = generateTransitionKey(toId, fromId);
584289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                mTransitions.append(keyToFrom, pos | REVERSED_BIT | reversibleBit);
585289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            }
586289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
587289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            return addChild(anim);
588289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        }
589289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
590289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        int addStateSet(@NonNull int[] stateSet, @NonNull Drawable drawable, int id) {
591289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            final int index = super.addStateSet(stateSet, drawable);
592289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            mStateIds.put(index, id);
593289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            return index;
594289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        }
595289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
596289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        int indexOfKeyframe(@NonNull int[] stateSet) {
597289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            final int index = super.indexOfStateSet(stateSet);
598289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            if (index >= 0) {
599289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                return index;
600289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            }
601289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
602289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            return super.indexOfStateSet(StateSet.WILD_CARD);
603289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        }
604289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
605289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        int getKeyframeIdAt(int index) {
606289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            return index < 0 ? 0 : mStateIds.get(index, 0);
607289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        }
608289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
609289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        int indexOfTransition(int fromId, int toId) {
610289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            final long keyFromTo = generateTransitionKey(fromId, toId);
611289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            return (int) mTransitions.get(keyFromTo, -1);
612289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        }
613289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
614289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        boolean isTransitionReversed(int fromId, int toId) {
615289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            final long keyFromTo = generateTransitionKey(fromId, toId);
616289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            return (mTransitions.get(keyFromTo, -1) & REVERSED_BIT) != 0;
617289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        }
618289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
619289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        boolean transitionHasReversibleFlag(int fromId, int toId) {
620289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            final long keyFromTo = generateTransitionKey(fromId, toId);
621289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            return (mTransitions.get(keyFromTo, -1) & REVERSIBLE_FLAG_BIT) != 0;
622289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        }
623289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
624289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        @Override
625289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        public boolean canApplyTheme() {
626289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            return mAnimThemeAttrs != null || super.canApplyTheme();
627289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        }
628289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
629289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        @Override
630289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        public Drawable newDrawable() {
631289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            return new AnimatedStateListDrawable(this, null);
632289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        }
633289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
634289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        @Override
635289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        public Drawable newDrawable(Resources res) {
636289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            return new AnimatedStateListDrawable(this, res);
637289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        }
638289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
639289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        private static long generateTransitionKey(int fromId, int toId) {
640289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            return (long) fromId << 32 | toId;
641289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        }
642289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    }
643289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
644289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    void setConstantState(@NonNull AnimatedStateListState state) {
645289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        super.setConstantState(state);
646289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
647289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        mState = state;
648289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    }
649289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
650289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    private AnimatedStateListDrawable(@Nullable AnimatedStateListState state, @Nullable Resources res) {
651289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        super(null);
652289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
653289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        final AnimatedStateListState newState = new AnimatedStateListState(state, this, res);
654289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        setConstantState(newState);
655289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        onStateChange(getState());
656289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        jumpToCurrentState();
657289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    }
658289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
659289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    /**
660289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza     * Interpolates between frames with respect to their individual durations.
661289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza     */
662289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza    private static class FrameInterpolator implements TimeInterpolator {
663289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        private int[] mFrameTimes;
664289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        private int mFrames;
665289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        private int mTotalDuration;
666289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
667289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        public FrameInterpolator(AnimationDrawable d, boolean reversed) {
668289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            updateFrames(d, reversed);
669289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        }
670289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
671289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        public int updateFrames(AnimationDrawable d, boolean reversed) {
672289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            final int N = d.getNumberOfFrames();
673289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            mFrames = N;
674289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
675289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            if (mFrameTimes == null || mFrameTimes.length < N) {
676289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                mFrameTimes = new int[N];
677399184a4cd728ea1421fb0bc1722274a29e38f4aJesse Hall            }
678289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
679289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            final int[] frameTimes = mFrameTimes;
680289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            int totalDuration = 0;
681289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            for (int i = 0; i < N; i++) {
682289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                final int duration = d.getDuration(reversed ? N - i - 1 : i);
683289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                frameTimes[i] = duration;
684289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                totalDuration += duration;
685289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            }
686289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
687289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            mTotalDuration = totalDuration;
688289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            return totalDuration;
689289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        }
690289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
691289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        public int getTotalDuration() {
692289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            return mTotalDuration;
693289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        }
694289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
695289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        @Override
696289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza        public float getInterpolation(float input) {
697289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            final int elapsed = (int) (input * mTotalDuration + 0.5f);
698289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            final int N = mFrames;
699289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            final int[] frameTimes = mFrameTimes;
700289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
701399184a4cd728ea1421fb0bc1722274a29e38f4aJesse Hall            // Find the current frame and remaining time within that frame.
702399184a4cd728ea1421fb0bc1722274a29e38f4aJesse Hall            int remaining = elapsed;
703399184a4cd728ea1421fb0bc1722274a29e38f4aJesse Hall            int i = 0;
704399184a4cd728ea1421fb0bc1722274a29e38f4aJesse Hall            while (i < N && remaining >= frameTimes[i]) {
705399184a4cd728ea1421fb0bc1722274a29e38f4aJesse Hall                remaining -= frameTimes[i];
706399184a4cd728ea1421fb0bc1722274a29e38f4aJesse Hall                i++;
707289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            }
708289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
709289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            // Remaining time is relative of total duration.
710289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            final float frameElapsed;
711289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            if (i < N) {
712289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                frameElapsed = remaining / (float) mTotalDuration;
713289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            } else {
714289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza                frameElapsed = 0;
715289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza            }
716289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza
717            return i / (float) N + frameElapsed;
718        }
719    }
720}
721