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 * <animated-selector></code> element. Each keyframe Drawable is defined in a 46289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza * nested <code><item></code> element. Transitions are defined in a nested 47289ade165e60b5f71734d30e535f16eb1f4313adDan Stoza * <code><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