117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase/* 217fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * Copyright (C) 2010 The Android Open Source Project 317fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * 417fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * Licensed under the Apache License, Version 2.0 (the "License"); 517fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * you may not use this file except in compliance with the License. 617fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * You may obtain a copy of the License at 717fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * 817fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * http://www.apache.org/licenses/LICENSE-2.0 917fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * 1017fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * Unless required by applicable law or agreed to in writing, software 1117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * distributed under the License is distributed on an "AS IS" BASIS, 1217fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1317fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * See the License for the specific language governing permissions and 1417fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * limitations under the License. 1517fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase */ 1617fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase 1717fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haasepackage android.animation; 1817fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase 1958606db8be7e64a4317955b87fba4ee51f353630Doris Liuimport android.app.ActivityThread; 2058606db8be7e64a4317955b87fba4ee51f353630Doris Liuimport android.app.Application; 2158606db8be7e64a4317955b87fba4ee51f353630Doris Liuimport android.os.Build; 2213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liuimport android.os.Looper; 2313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liuimport android.util.AndroidRuntimeException; 24d7444427d9f44b6b7448d4c21edca866132c8b59Doris Liuimport android.util.ArrayMap; 251309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liuimport android.util.Log; 2613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liuimport android.view.animation.Animation; 27d7444427d9f44b6b7448d4c21edca866132c8b59Doris Liu 2817fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haaseimport java.util.ArrayList; 2937a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haaseimport java.util.Collection; 3013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liuimport java.util.Comparator; 3156b2df05f56b2c7b82a50b46341d055f6d70a913Doris Liuimport java.util.HashMap; 3237a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haaseimport java.util.List; 3317fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase 3417fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase/** 35a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * This class plays a set of {@link Animator} objects in the specified order. Animations 3617fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * can be set up to play together, in sequence, or after a specified delay. 3717fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * 38a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * <p>There are two different approaches to adding animations to a <code>AnimatorSet</code>: 39a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * either the {@link AnimatorSet#playTogether(Animator[]) playTogether()} or 40a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * {@link AnimatorSet#playSequentially(Animator[]) playSequentially()} methods can be called to add 41a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * a set of animations all at once, or the {@link AnimatorSet#play(Animator)} can be 42a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * used in conjunction with methods in the {@link AnimatorSet.Builder Builder} 4317fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * class to add animations 4417fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * one by one.</p> 4517fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * 46a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * <p>It is possible to set up a <code>AnimatorSet</code> with circular dependencies between 4717fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * its animations. For example, an animation a1 could be set up to start before animation a2, a2 4817fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * before a3, and a3 before a1. The results of this configuration are undefined, but will typically 4917fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * result in none of the affected animations being played. Because of this (and because 5017fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * circular dependencies do not make logical sense anyway), circular dependencies 5117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * should be avoided, and the dependency flow of animations should only be in one direction. 523aef8e1d1b2f0b87d470bcccf37ba4ebb6560c45Joe Fernandez * 533aef8e1d1b2f0b87d470bcccf37ba4ebb6560c45Joe Fernandez * <div class="special reference"> 543aef8e1d1b2f0b87d470bcccf37ba4ebb6560c45Joe Fernandez * <h3>Developer Guides</h3> 553aef8e1d1b2f0b87d470bcccf37ba4ebb6560c45Joe Fernandez * <p>For more information about animating with {@code AnimatorSet}, read the 563aef8e1d1b2f0b87d470bcccf37ba4ebb6560c45Joe Fernandez * <a href="{@docRoot}guide/topics/graphics/prop-animation.html#choreography">Property 573aef8e1d1b2f0b87d470bcccf37ba4ebb6560c45Joe Fernandez * Animation</a> developer guide.</p> 583aef8e1d1b2f0b87d470bcccf37ba4ebb6560c45Joe Fernandez * </div> 5917fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase */ 6013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liupublic final class AnimatorSet extends Animator implements AnimationHandler.AnimationFrameCallback { 6117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase 621309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu private static final String TAG = "AnimatorSet"; 6317fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase /** 6449afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase * Internal variables 6549afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase * NOTE: This object implements the clone() method, making a deep copy of any referenced 6649afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase * objects. As other non-trivial fields are added to this class, make sure to add logic 6749afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase * to clone() to make deep copies of them. 6849afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase */ 6949afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase 7049afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase /** 713b69b6f0be85d1f97c1e6824cf986777ba4e5d00Chet Haase * Tracks animations currently being played, so that we know what to 72a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * cancel or end when cancel() or end() is called on this AnimatorSet 7317fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase */ 7413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu private ArrayList<Node> mPlayingSet = new ArrayList<Node>(); 7517fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase 7617fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase /** 77a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * Contains all nodes, mapped to their respective Animators. When new 78a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * dependency information is added for an Animator, we want to add it 79a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * to a single node representing that Animator, not create a new Node 8017fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * if one already exists. 8117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase */ 82d7444427d9f44b6b7448d4c21edca866132c8b59Doris Liu private ArrayMap<Animator, Node> mNodeMap = new ArrayMap<Animator, Node>(); 8317fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase 8417fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase /** 8513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu * Contains the start and end events of all the nodes. All these events are sorted in this list. 8613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu */ 8713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu private ArrayList<AnimationEvent> mEvents = new ArrayList<>(); 8813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 8913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu /** 90a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * Set of all nodes created for this AnimatorSet. This list is used upon 91a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * starting the set, and the nodes are placed in sorted order into the 9217fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * sortedNodes collection. 9317fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase */ 9449afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase private ArrayList<Node> mNodes = new ArrayList<Node>(); 9517fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase 9617fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase /** 971309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu * Tracks whether any change has been made to the AnimatorSet, which is then used to 981309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu * determine whether the dependency graph should be re-constructed. 991309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu */ 1001309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu private boolean mDependencyDirty = false; 101010dbaa1236cf2dcdc62c29049468e90188acaaeChet Haase 1028b699792b677bd4dd8442b32641ac09d48fdd79cChet Haase /** 1038b699792b677bd4dd8442b32641ac09d48fdd79cChet Haase * Indicates whether an AnimatorSet has been start()'d, whether or 1048b699792b677bd4dd8442b32641ac09d48fdd79cChet Haase * not there is a nonzero startDelay. 1058b699792b677bd4dd8442b32641ac09d48fdd79cChet Haase */ 1068b699792b677bd4dd8442b32641ac09d48fdd79cChet Haase private boolean mStarted = false; 1078b699792b677bd4dd8442b32641ac09d48fdd79cChet Haase 10821cd1389d2ef218b20994b617c57af120841a57fChet Haase // The amount of time in ms to delay starting the animation after start() is called 10921cd1389d2ef218b20994b617c57af120841a57fChet Haase private long mStartDelay = 0; 11021cd1389d2ef218b20994b617c57af120841a57fChet Haase 111e2ab7ccd385cdb6517955c719e1d2b49771bedb6Chet Haase // Animator used for a nonzero startDelay 1121309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu private ValueAnimator mDelayAnim = ValueAnimator.ofFloat(0f, 1f).setDuration(0); 113e2ab7ccd385cdb6517955c719e1d2b49771bedb6Chet Haase 1141309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu // Root of the dependency tree of all the animators in the set. In this tree, parent-child 1151309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu // relationship captures the order of animation (i.e. parent and child will play sequentially), 1161309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu // and sibling relationship indicates "with" relationship, as sibling animators start at the 1171309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu // same time. 1181309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu private Node mRootNode = new Node(mDelayAnim); 11921cd1389d2ef218b20994b617c57af120841a57fChet Haase 12021cd1389d2ef218b20994b617c57af120841a57fChet Haase // How long the child animations should last in ms. The default value is negative, which 121a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase // simply means that there is no duration set on the AnimatorSet. When a real duration is 12221cd1389d2ef218b20994b617c57af120841a57fChet Haase // set, it is passed along to the child animations. 12321cd1389d2ef218b20994b617c57af120841a57fChet Haase private long mDuration = -1; 12421cd1389d2ef218b20994b617c57af120841a57fChet Haase 125430742f09063574271e6c4091de13b9b9e762514Chet Haase // Records the interpolator for the set. Null value indicates that no interpolator 126430742f09063574271e6c4091de13b9b9e762514Chet Haase // was set on this AnimatorSet, so it should not be passed down to the children. 127430742f09063574271e6c4091de13b9b9e762514Chet Haase private TimeInterpolator mInterpolator = null; 128430742f09063574271e6c4091de13b9b9e762514Chet Haase 1291309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu // The total duration of finishing all the Animators in the set. 1301309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu private long mTotalDuration = 0; 1311309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu 13258606db8be7e64a4317955b87fba4ee51f353630Doris Liu // In pre-N releases, calling end() before start() on an animator set is no-op. But that is not 13358606db8be7e64a4317955b87fba4ee51f353630Doris Liu // consistent with the behavior for other animator types. In order to keep the behavior 13458606db8be7e64a4317955b87fba4ee51f353630Doris Liu // consistent within Animation framework, when end() is called without start(), we will start 13558606db8be7e64a4317955b87fba4ee51f353630Doris Liu // the animator set and immediately end it for N and forward. 13658606db8be7e64a4317955b87fba4ee51f353630Doris Liu private final boolean mShouldIgnoreEndWithoutStart; 13758606db8be7e64a4317955b87fba4ee51f353630Doris Liu 13813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // In pre-O releases, calling start() doesn't reset all the animators values to start values. 13913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // As a result, the start of the animation is inconsistent with what setCurrentPlayTime(0) would 14013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // look like on O. Also it is inconsistent with what reverse() does on O, as reverse would 14113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // advance all the animations to the right beginning values for before starting to reverse. 14213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // From O and forward, we will add an additional step of resetting the animation values (unless 14313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // the animation was previously seeked and therefore doesn't start from the beginning). 14413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu private final boolean mShouldResetValuesAtStart; 14513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 1466d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu // In pre-O releases, end() may never explicitly called on a child animator. As a result, end() 1476d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu // may not even be properly implemented in a lot of cases. After a few apps crashing on this, 1486d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu // it became necessary to use an sdk target guard for calling end(). 1496d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu private final boolean mEndCanBeCalled; 1506d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu 15113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // The time, in milliseconds, when last frame of the animation came in. -1 when the animation is 15213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // not running. 15313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu private long mLastFrameTime = -1; 15413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 1556d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu // The time, in milliseconds, when the first frame of the animation came in. This is the 1566d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu // frame before we start counting down the start delay, if any. 15713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // -1 when the animation is not running. 15813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu private long mFirstFrame = -1; 15913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 16013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // The time, in milliseconds, when the first frame of the animation came in. 16113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // -1 when the animation is not running. 16213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu private int mLastEventId = -1; 16313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 16413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // Indicates whether the animation is reversing. 16513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu private boolean mReversing = false; 16613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 16713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // Indicates whether the animation should register frame callbacks. If false, the animation will 16813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // passively wait for an AnimatorSet to pulse it. 16913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu private boolean mSelfPulse = true; 17013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 17113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // SeekState stores the last seeked play time as well as seek direction. 17213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu private SeekState mSeekState = new SeekState(); 17313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 17413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // Indicates where children animators are all initialized with their start values captured. 17513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu private boolean mChildrenInitialized = false; 17613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 17713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu /** 17813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu * Set on the next frame after pause() is called, used to calculate a new startTime 17913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu * or delayStartTime which allows the animator set to continue from the point at which 18013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu * it was paused. If negative, has not yet been set. 18113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu */ 18213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu private long mPauseTime = -1; 18313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 1846ba5ed322c04f260c51d58f2e49c6189b672f2d3Doris Liu // This is to work around a bug in b/34736819. This needs to be removed once app team 18566c564e390ea71576db7d4c1803b169cc98e987bDoris Liu // fixes their side. 1866ba5ed322c04f260c51d58f2e49c6189b672f2d3Doris Liu private AnimatorListenerAdapter mDummyListener = new AnimatorListenerAdapter() { 1876ba5ed322c04f260c51d58f2e49c6189b672f2d3Doris Liu @Override 1886ba5ed322c04f260c51d58f2e49c6189b672f2d3Doris Liu public void onAnimationEnd(Animator animation) { 1896ba5ed322c04f260c51d58f2e49c6189b672f2d3Doris Liu if (mNodeMap.get(animation) == null) { 1906ba5ed322c04f260c51d58f2e49c6189b672f2d3Doris Liu throw new AndroidRuntimeException("Error: animation ended is not in the node map"); 1916ba5ed322c04f260c51d58f2e49c6189b672f2d3Doris Liu } 1926ba5ed322c04f260c51d58f2e49c6189b672f2d3Doris Liu mNodeMap.get(animation).mEnded = true; 1936ba5ed322c04f260c51d58f2e49c6189b672f2d3Doris Liu 1946ba5ed322c04f260c51d58f2e49c6189b672f2d3Doris Liu } 1956ba5ed322c04f260c51d58f2e49c6189b672f2d3Doris Liu }; 19666c564e390ea71576db7d4c1803b169cc98e987bDoris Liu 1971309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu public AnimatorSet() { 1981309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu super(); 1991309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu mNodeMap.put(mDelayAnim, mRootNode); 2001309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu mNodes.add(mRootNode); 2016d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu boolean isPreO; 20258606db8be7e64a4317955b87fba4ee51f353630Doris Liu // Set the flag to ignore calling end() without start() for pre-N releases 20358606db8be7e64a4317955b87fba4ee51f353630Doris Liu Application app = ActivityThread.currentApplication(); 20458606db8be7e64a4317955b87fba4ee51f353630Doris Liu if (app == null || app.getApplicationInfo() == null) { 20558606db8be7e64a4317955b87fba4ee51f353630Doris Liu mShouldIgnoreEndWithoutStart = true; 2066d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu isPreO = true; 20758606db8be7e64a4317955b87fba4ee51f353630Doris Liu } else { 20813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (app.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) { 20913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu mShouldIgnoreEndWithoutStart = true; 21013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } else { 21113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu mShouldIgnoreEndWithoutStart = false; 21213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 21313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 2146d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu isPreO = app.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.O; 21558606db8be7e64a4317955b87fba4ee51f353630Doris Liu } 2166d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu mShouldResetValuesAtStart = !isPreO; 2176d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu mEndCanBeCalled = !isPreO; 2181309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu } 2191309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu 220010dbaa1236cf2dcdc62c29049468e90188acaaeChet Haase /** 221a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * Sets up this AnimatorSet to play all of the supplied animations at the same time. 222430742f09063574271e6c4091de13b9b9e762514Chet Haase * This is equivalent to calling {@link #play(Animator)} with the first animator in the 223430742f09063574271e6c4091de13b9b9e762514Chet Haase * set and then {@link Builder#with(Animator)} with each of the other animators. Note that 224430742f09063574271e6c4091de13b9b9e762514Chet Haase * an Animator with a {@link Animator#setStartDelay(long) startDelay} will not actually 225430742f09063574271e6c4091de13b9b9e762514Chet Haase * start until that delay elapses, which means that if the first animator in the list 226430742f09063574271e6c4091de13b9b9e762514Chet Haase * supplied to this constructor has a startDelay, none of the other animators will start 227430742f09063574271e6c4091de13b9b9e762514Chet Haase * until that first animator's startDelay has elapsed. 22817fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * 229a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * @param items The animations that will be started simultaneously. 23017fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase */ 231a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase public void playTogether(Animator... items) { 232a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase if (items != null) { 233a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase Builder builder = play(items[0]); 234a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase for (int i = 1; i < items.length; ++i) { 235a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase builder.with(items[i]); 23617fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase } 23717fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase } 23817fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase } 23917fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase 24017fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase /** 24137a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase * Sets up this AnimatorSet to play all of the supplied animations at the same time. 24237a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase * 24337a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase * @param items The animations that will be started simultaneously. 24437a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase */ 24537a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase public void playTogether(Collection<Animator> items) { 24637a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase if (items != null && items.size() > 0) { 24737a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase Builder builder = null; 24837a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase for (Animator anim : items) { 24937a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase if (builder == null) { 25037a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase builder = play(anim); 25137a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase } else { 25237a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase builder.with(anim); 25337a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase } 25437a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase } 25537a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase } 25637a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase } 25737a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase 25837a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase /** 259a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * Sets up this AnimatorSet to play each of the supplied animations when the 26017fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * previous animation ends. 26117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * 26237a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase * @param items The animations that will be started one after another. 26317fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase */ 264a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase public void playSequentially(Animator... items) { 265a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase if (items != null) { 266a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase if (items.length == 1) { 267a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase play(items[0]); 26817fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase } else { 269a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase for (int i = 0; i < items.length - 1; ++i) { 2701309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu play(items[i]).before(items[i + 1]); 27117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase } 27217fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase } 27317fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase } 27417fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase } 27517fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase 27617fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase /** 27737a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase * Sets up this AnimatorSet to play each of the supplied animations when the 27837a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase * previous animation ends. 27937a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase * 28037a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase * @param items The animations that will be started one after another. 28137a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase */ 28237a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase public void playSequentially(List<Animator> items) { 28337a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase if (items != null && items.size() > 0) { 28437a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase if (items.size() == 1) { 28537a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase play(items.get(0)); 28637a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase } else { 28737a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase for (int i = 0; i < items.size() - 1; ++i) { 2881309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu play(items.get(i)).before(items.get(i + 1)); 28937a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase } 29037a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase } 29137a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase } 29237a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase } 29337a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase 29437a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase /** 295a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * Returns the current list of child Animator objects controlled by this 296a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * AnimatorSet. This is a copy of the internal list; modifications to the returned list 297a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * will not affect the AnimatorSet, although changes to the underlying Animator objects 298a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * will affect those objects being managed by the AnimatorSet. 299f54a8d7c479485174941c38f151ea7083c658da3Chet Haase * 300a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * @return ArrayList<Animator> The list of child animations of this AnimatorSet. 301f54a8d7c479485174941c38f151ea7083c658da3Chet Haase */ 302a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase public ArrayList<Animator> getChildAnimations() { 303a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase ArrayList<Animator> childList = new ArrayList<Animator>(); 3041309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu int size = mNodes.size(); 3051309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu for (int i = 0; i < size; i++) { 3061309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu Node node = mNodes.get(i); 307dbf69e4652d439892080dff3353b1cfd5b4bf6bcDoris Liu if (node != mRootNode) { 308dbf69e4652d439892080dff3353b1cfd5b4bf6bcDoris Liu childList.add(node.mAnimation); 309dbf69e4652d439892080dff3353b1cfd5b4bf6bcDoris Liu } 310f54a8d7c479485174941c38f151ea7083c658da3Chet Haase } 311f54a8d7c479485174941c38f151ea7083c658da3Chet Haase return childList; 312f54a8d7c479485174941c38f151ea7083c658da3Chet Haase } 313f54a8d7c479485174941c38f151ea7083c658da3Chet Haase 314f54a8d7c479485174941c38f151ea7083c658da3Chet Haase /** 315811ed1065f39469cf2cf6adba22cab397ed88d5eChet Haase * Sets the target object for all current {@link #getChildAnimations() child animations} 316a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * of this AnimatorSet that take targets ({@link ObjectAnimator} and 317a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * AnimatorSet). 318811ed1065f39469cf2cf6adba22cab397ed88d5eChet Haase * 319811ed1065f39469cf2cf6adba22cab397ed88d5eChet Haase * @param target The object being animated 320811ed1065f39469cf2cf6adba22cab397ed88d5eChet Haase */ 32121cd1389d2ef218b20994b617c57af120841a57fChet Haase @Override 322811ed1065f39469cf2cf6adba22cab397ed88d5eChet Haase public void setTarget(Object target) { 3231309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu int size = mNodes.size(); 3241309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu for (int i = 0; i < size; i++) { 3251309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu Node node = mNodes.get(i); 3261309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu Animator animation = node.mAnimation; 327a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase if (animation instanceof AnimatorSet) { 328a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase ((AnimatorSet)animation).setTarget(target); 329a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase } else if (animation instanceof ObjectAnimator) { 330a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase ((ObjectAnimator)animation).setTarget(target); 331811ed1065f39469cf2cf6adba22cab397ed88d5eChet Haase } 332811ed1065f39469cf2cf6adba22cab397ed88d5eChet Haase } 333811ed1065f39469cf2cf6adba22cab397ed88d5eChet Haase } 334811ed1065f39469cf2cf6adba22cab397ed88d5eChet Haase 335811ed1065f39469cf2cf6adba22cab397ed88d5eChet Haase /** 336d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar * @hide 337d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar */ 338d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar @Override 339d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar public int getChangingConfigurations() { 340d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar int conf = super.getChangingConfigurations(); 341d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar final int nodeCount = mNodes.size(); 342d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar for (int i = 0; i < nodeCount; i ++) { 3431309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu conf |= mNodes.get(i).mAnimation.getChangingConfigurations(); 344d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar } 345d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar return conf; 346d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar } 347d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar 348d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar /** 349e0ee2e9f3102c3c14c873a75a7b04e49787e0fb9Chet Haase * Sets the TimeInterpolator for all current {@link #getChildAnimations() child animations} 350430742f09063574271e6c4091de13b9b9e762514Chet Haase * of this AnimatorSet. The default value is null, which means that no interpolator 351430742f09063574271e6c4091de13b9b9e762514Chet Haase * is set on this AnimatorSet. Setting the interpolator to any non-null value 352430742f09063574271e6c4091de13b9b9e762514Chet Haase * will cause that interpolator to be set on the child animations 353430742f09063574271e6c4091de13b9b9e762514Chet Haase * when the set is started. 35421cd1389d2ef218b20994b617c57af120841a57fChet Haase * 355a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * @param interpolator the interpolator to be used by each child animation of this AnimatorSet 35621cd1389d2ef218b20994b617c57af120841a57fChet Haase */ 35721cd1389d2ef218b20994b617c57af120841a57fChet Haase @Override 358e0ee2e9f3102c3c14c873a75a7b04e49787e0fb9Chet Haase public void setInterpolator(TimeInterpolator interpolator) { 359430742f09063574271e6c4091de13b9b9e762514Chet Haase mInterpolator = interpolator; 360430742f09063574271e6c4091de13b9b9e762514Chet Haase } 361430742f09063574271e6c4091de13b9b9e762514Chet Haase 362430742f09063574271e6c4091de13b9b9e762514Chet Haase @Override 363430742f09063574271e6c4091de13b9b9e762514Chet Haase public TimeInterpolator getInterpolator() { 364430742f09063574271e6c4091de13b9b9e762514Chet Haase return mInterpolator; 36521cd1389d2ef218b20994b617c57af120841a57fChet Haase } 36621cd1389d2ef218b20994b617c57af120841a57fChet Haase 36721cd1389d2ef218b20994b617c57af120841a57fChet Haase /** 36817fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * This method creates a <code>Builder</code> object, which is used to 36917fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * set up playing constraints. This initial <code>play()</code> method 37017fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * tells the <code>Builder</code> the animation that is the dependency for 37117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * the succeeding commands to the <code>Builder</code>. For example, 372a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * calling <code>play(a1).with(a2)</code> sets up the AnimatorSet to play 37317fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * <code>a1</code> and <code>a2</code> at the same time, 374a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * <code>play(a1).before(a2)</code> sets up the AnimatorSet to play 37517fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * <code>a1</code> first, followed by <code>a2</code>, and 376a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * <code>play(a1).after(a2)</code> sets up the AnimatorSet to play 37717fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * <code>a2</code> first, followed by <code>a1</code>. 37817fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * 37917fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * <p>Note that <code>play()</code> is the only way to tell the 38017fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * <code>Builder</code> the animation upon which the dependency is created, 38117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * so successive calls to the various functions in <code>Builder</code> 38217fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * will all refer to the initial parameter supplied in <code>play()</code> 38317fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * as the dependency of the other animations. For example, calling 38417fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * <code>play(a1).before(a2).before(a3)</code> will play both <code>a2</code> 38517fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * and <code>a3</code> when a1 ends; it does not set up a dependency between 38617fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * <code>a2</code> and <code>a3</code>.</p> 38717fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * 38817fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * @param anim The animation that is the dependency used in later calls to the 38917fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * methods in the returned <code>Builder</code> object. A null parameter will result 39017fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * in a null <code>Builder</code> return value. 391a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * @return Builder The object that constructs the AnimatorSet based on the dependencies 39217fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * outlined in the calls to <code>play</code> and the other methods in the 39317fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * <code>Builder</code object. 39417fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase */ 395a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase public Builder play(Animator anim) { 39617fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase if (anim != null) { 39717fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase return new Builder(anim); 39817fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase } 39917fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase return null; 40017fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase } 40117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase 40217fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase /** 40317fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * {@inheritDoc} 40417fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * 4058b699792b677bd4dd8442b32641ac09d48fdd79cChet Haase * <p>Note that canceling a <code>AnimatorSet</code> also cancels all of the animations that it 4068b699792b677bd4dd8442b32641ac09d48fdd79cChet Haase * is responsible for.</p> 40717fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase */ 40817fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase @SuppressWarnings("unchecked") 40917fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase @Override 41017fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase public void cancel() { 41113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (Looper.myLooper() == null) { 41213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu throw new AndroidRuntimeException("Animators may only be run on Looper threads"); 41313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 4148b699792b677bd4dd8442b32641ac09d48fdd79cChet Haase if (isStarted()) { 4157dfacdb1c820f955cb3cd6032ff5fbc2dd7d9df5Chet Haase ArrayList<AnimatorListener> tmpListeners = null; 416e2ab7ccd385cdb6517955c719e1d2b49771bedb6Chet Haase if (mListeners != null) { 4177dfacdb1c820f955cb3cd6032ff5fbc2dd7d9df5Chet Haase tmpListeners = (ArrayList<AnimatorListener>) mListeners.clone(); 4181309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu int size = tmpListeners.size(); 4191309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu for (int i = 0; i < size; i++) { 4201309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu tmpListeners.get(i).onAnimationCancel(this); 421e2ab7ccd385cdb6517955c719e1d2b49771bedb6Chet Haase } 422e2ab7ccd385cdb6517955c719e1d2b49771bedb6Chet Haase } 42313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu ArrayList<Node> playingSet = new ArrayList<>(mPlayingSet); 4241309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu int setSize = playingSet.size(); 4251309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu for (int i = 0; i < setSize; i++) { 42613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu playingSet.get(i).mAnimation.cancel(); 4277dfacdb1c820f955cb3cd6032ff5fbc2dd7d9df5Chet Haase } 42813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu mPlayingSet.clear(); 42913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu endAnimation(); 43017fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase } 43117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase } 43217fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase 4336d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu // Force all the animations to end when the duration scale is 0. 4346d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu private void forceToEnd() { 435ae570c04c0e43e0d66e2c563fe6d8cacb438961bDoris Liu if (mEndCanBeCalled) { 436ae570c04c0e43e0d66e2c563fe6d8cacb438961bDoris Liu end(); 437ae570c04c0e43e0d66e2c563fe6d8cacb438961bDoris Liu return; 438ae570c04c0e43e0d66e2c563fe6d8cacb438961bDoris Liu } 439d7968dc174f60e3b8f5aaddd05703cf6ce9114adDoris Liu 440d7968dc174f60e3b8f5aaddd05703cf6ce9114adDoris Liu // Note: we don't want to combine this case with the end() method below because in 441d7968dc174f60e3b8f5aaddd05703cf6ce9114adDoris Liu // the case of developer calling end(), we still need to make sure end() is explicitly 442d7968dc174f60e3b8f5aaddd05703cf6ce9114adDoris Liu // called on the child animators to maintain the old behavior. 443d7968dc174f60e3b8f5aaddd05703cf6ce9114adDoris Liu if (mReversing) { 444d7968dc174f60e3b8f5aaddd05703cf6ce9114adDoris Liu handleAnimationEvents(mLastEventId, 0, getTotalDuration()); 4456d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu } else { 446d7968dc174f60e3b8f5aaddd05703cf6ce9114adDoris Liu long zeroScalePlayTime = getTotalDuration(); 447d7968dc174f60e3b8f5aaddd05703cf6ce9114adDoris Liu if (zeroScalePlayTime == DURATION_INFINITE) { 448d7968dc174f60e3b8f5aaddd05703cf6ce9114adDoris Liu // Use a large number for the play time. 449d7968dc174f60e3b8f5aaddd05703cf6ce9114adDoris Liu zeroScalePlayTime = Integer.MAX_VALUE; 4506d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu } 451d7968dc174f60e3b8f5aaddd05703cf6ce9114adDoris Liu handleAnimationEvents(mLastEventId, mEvents.size() - 1, zeroScalePlayTime); 4526d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu } 453d7968dc174f60e3b8f5aaddd05703cf6ce9114adDoris Liu mPlayingSet.clear(); 454d7968dc174f60e3b8f5aaddd05703cf6ce9114adDoris Liu endAnimation(); 4556d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu } 4566d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu 45717fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase /** 45817fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * {@inheritDoc} 45917fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * 460a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * <p>Note that ending a <code>AnimatorSet</code> also ends all of the animations that it is 46117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * responsible for.</p> 46217fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase */ 46317fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase @Override 46417fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase public void end() { 46513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (Looper.myLooper() == null) { 46613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu throw new AndroidRuntimeException("Animators may only be run on Looper threads"); 46713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 46858606db8be7e64a4317955b87fba4ee51f353630Doris Liu if (mShouldIgnoreEndWithoutStart && !isStarted()) { 46958606db8be7e64a4317955b87fba4ee51f353630Doris Liu return; 47058606db8be7e64a4317955b87fba4ee51f353630Doris Liu } 4718b699792b677bd4dd8442b32641ac09d48fdd79cChet Haase if (isStarted()) { 47213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // Iterate the animations that haven't finished or haven't started, and end them. 47313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (mReversing) { 47413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // Between start() and first frame, mLastEventId would be unset (i.e. -1) 47513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu mLastEventId = mLastEventId == -1 ? mEvents.size() : mLastEventId; 47688bb31b95467f3591f4911be673b57c6fb316134Doris Liu while (mLastEventId > 0) { 47788bb31b95467f3591f4911be673b57c6fb316134Doris Liu mLastEventId = mLastEventId - 1; 47888bb31b95467f3591f4911be673b57c6fb316134Doris Liu AnimationEvent event = mEvents.get(mLastEventId); 4796d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu Animator anim = event.mNode.mAnimation; 48088bb31b95467f3591f4911be673b57c6fb316134Doris Liu if (mNodeMap.get(anim).mEnded) { 48188bb31b95467f3591f4911be673b57c6fb316134Doris Liu continue; 48288bb31b95467f3591f4911be673b57c6fb316134Doris Liu } 48313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (event.mEvent == AnimationEvent.ANIMATION_END) { 4846d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu anim.reverse(); 4856d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu } else if (event.mEvent == AnimationEvent.ANIMATION_DELAY_ENDED 4866d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu && anim.isStarted()) { 4876d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu // Make sure anim hasn't finished before calling end() so that we don't end 4886d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu // already ended animations, which will cause start and end callbacks to be 4896d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu // triggered again. 4906d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu anim.end(); 49113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 49213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 49313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } else { 49488bb31b95467f3591f4911be673b57c6fb316134Doris Liu while (mLastEventId < mEvents.size() - 1) { 49588bb31b95467f3591f4911be673b57c6fb316134Doris Liu // Avoid potential reentrant loop caused by child animators manipulating 49688bb31b95467f3591f4911be673b57c6fb316134Doris Liu // AnimatorSet's lifecycle (i.e. not a recommended approach). 49788bb31b95467f3591f4911be673b57c6fb316134Doris Liu mLastEventId = mLastEventId + 1; 49888bb31b95467f3591f4911be673b57c6fb316134Doris Liu AnimationEvent event = mEvents.get(mLastEventId); 4996d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu Animator anim = event.mNode.mAnimation; 50088bb31b95467f3591f4911be673b57c6fb316134Doris Liu if (mNodeMap.get(anim).mEnded) { 50188bb31b95467f3591f4911be673b57c6fb316134Doris Liu continue; 50288bb31b95467f3591f4911be673b57c6fb316134Doris Liu } 50313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (event.mEvent == AnimationEvent.ANIMATION_START) { 5046d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu anim.start(); 5056d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu } else if (event.mEvent == AnimationEvent.ANIMATION_END && anim.isStarted()) { 5066d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu // Make sure anim hasn't finished before calling end() so that we don't end 5076d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu // already ended animations, which will cause start and end callbacks to be 5086d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu // triggered again. 5096d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu anim.end(); 5101309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu } 5117dfacdb1c820f955cb3cd6032ff5fbc2dd7d9df5Chet Haase } 51217fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase } 51313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu mPlayingSet.clear(); 51417fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase } 51513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu endAnimation(); 51617fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase } 51717fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase 51817fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase /** 5198b699792b677bd4dd8442b32641ac09d48fdd79cChet Haase * Returns true if any of the child animations of this AnimatorSet have been started and have 520ee684556ceb8b940ca2af06613a59728e9b78507Doris Liu * not yet ended. Child animations will not be started until the AnimatorSet has gone past 521ee684556ceb8b940ca2af06613a59728e9b78507Doris Liu * its initial delay set through {@link #setStartDelay(long)}. 522ee684556ceb8b940ca2af06613a59728e9b78507Doris Liu * 523ee684556ceb8b940ca2af06613a59728e9b78507Doris Liu * @return Whether this AnimatorSet has gone past the initial delay, and at least one child 524ee684556ceb8b940ca2af06613a59728e9b78507Doris Liu * animation has been started and not yet ended. 525673e42fafd4088970ec95e1f13c61dc83132c74eChet Haase */ 526673e42fafd4088970ec95e1f13c61dc83132c74eChet Haase @Override 527673e42fafd4088970ec95e1f13c61dc83132c74eChet Haase public boolean isRunning() { 5286d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu if (mStartDelay == 0) { 52913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu return mStarted; 530673e42fafd4088970ec95e1f13c61dc83132c74eChet Haase } 5316d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu return mLastFrameTime > 0; 5328b699792b677bd4dd8442b32641ac09d48fdd79cChet Haase } 5338b699792b677bd4dd8442b32641ac09d48fdd79cChet Haase 5348b699792b677bd4dd8442b32641ac09d48fdd79cChet Haase @Override 5358b699792b677bd4dd8442b32641ac09d48fdd79cChet Haase public boolean isStarted() { 5368b699792b677bd4dd8442b32641ac09d48fdd79cChet Haase return mStarted; 537673e42fafd4088970ec95e1f13c61dc83132c74eChet Haase } 538673e42fafd4088970ec95e1f13c61dc83132c74eChet Haase 539673e42fafd4088970ec95e1f13c61dc83132c74eChet Haase /** 54021cd1389d2ef218b20994b617c57af120841a57fChet Haase * The amount of time, in milliseconds, to delay starting the animation after 54121cd1389d2ef218b20994b617c57af120841a57fChet Haase * {@link #start()} is called. 54221cd1389d2ef218b20994b617c57af120841a57fChet Haase * 54321cd1389d2ef218b20994b617c57af120841a57fChet Haase * @return the number of milliseconds to delay running the animation 54421cd1389d2ef218b20994b617c57af120841a57fChet Haase */ 54521cd1389d2ef218b20994b617c57af120841a57fChet Haase @Override 54621cd1389d2ef218b20994b617c57af120841a57fChet Haase public long getStartDelay() { 54721cd1389d2ef218b20994b617c57af120841a57fChet Haase return mStartDelay; 54821cd1389d2ef218b20994b617c57af120841a57fChet Haase } 54921cd1389d2ef218b20994b617c57af120841a57fChet Haase 55021cd1389d2ef218b20994b617c57af120841a57fChet Haase /** 55121cd1389d2ef218b20994b617c57af120841a57fChet Haase * The amount of time, in milliseconds, to delay starting the animation after 55261045c518b18a7cee30954fe45f9db8c14e705e1Doris Liu * {@link #start()} is called. Note that the start delay should always be non-negative. Any 55361045c518b18a7cee30954fe45f9db8c14e705e1Doris Liu * negative start delay will be clamped to 0 on N and above. 55461045c518b18a7cee30954fe45f9db8c14e705e1Doris Liu * 55521cd1389d2ef218b20994b617c57af120841a57fChet Haase * @param startDelay The amount of the delay, in milliseconds 55621cd1389d2ef218b20994b617c57af120841a57fChet Haase */ 55721cd1389d2ef218b20994b617c57af120841a57fChet Haase @Override 55821cd1389d2ef218b20994b617c57af120841a57fChet Haase public void setStartDelay(long startDelay) { 55961045c518b18a7cee30954fe45f9db8c14e705e1Doris Liu // Clamp start delay to non-negative range. 56061045c518b18a7cee30954fe45f9db8c14e705e1Doris Liu if (startDelay < 0) { 56161045c518b18a7cee30954fe45f9db8c14e705e1Doris Liu Log.w(TAG, "Start delay should always be non-negative"); 56261045c518b18a7cee30954fe45f9db8c14e705e1Doris Liu startDelay = 0; 5637bc6a3f023ca3e1dde91fc97b6036dee3ba538a2ztenghui } 5641309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu long delta = startDelay - mStartDelay; 56549db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu if (delta == 0) { 56649db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu return; 56749db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu } 56821cd1389d2ef218b20994b617c57af120841a57fChet Haase mStartDelay = startDelay; 5691309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu if (!mDependencyDirty) { 5701309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu // Dependency graph already constructed, update all the nodes' start/end time 5711309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu int size = mNodes.size(); 5721309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu for (int i = 0; i < size; i++) { 5731309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu Node node = mNodes.get(i); 5741309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu if (node == mRootNode) { 5751309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu node.mEndTime = mStartDelay; 5761309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu } else { 5771309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu node.mStartTime = node.mStartTime == DURATION_INFINITE ? 5781309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu DURATION_INFINITE : node.mStartTime + delta; 5791309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu node.mEndTime = node.mEndTime == DURATION_INFINITE ? 5801309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu DURATION_INFINITE : node.mEndTime + delta; 5811309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu } 5821309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu } 58349db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu // Update total duration, if necessary. 58449db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu if (mTotalDuration != DURATION_INFINITE) { 58549db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu mTotalDuration += delta; 58649db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu } 5871309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu } 58821cd1389d2ef218b20994b617c57af120841a57fChet Haase } 58921cd1389d2ef218b20994b617c57af120841a57fChet Haase 59021cd1389d2ef218b20994b617c57af120841a57fChet Haase /** 591a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * Gets the length of each of the child animations of this AnimatorSet. This value may 592a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * be less than 0, which indicates that no duration has been set on this AnimatorSet 59321cd1389d2ef218b20994b617c57af120841a57fChet Haase * and each of the child animations will use their own duration. 59421cd1389d2ef218b20994b617c57af120841a57fChet Haase * 59521cd1389d2ef218b20994b617c57af120841a57fChet Haase * @return The length of the animation, in milliseconds, of each of the child 596a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * animations of this AnimatorSet. 59721cd1389d2ef218b20994b617c57af120841a57fChet Haase */ 59821cd1389d2ef218b20994b617c57af120841a57fChet Haase @Override 59921cd1389d2ef218b20994b617c57af120841a57fChet Haase public long getDuration() { 60021cd1389d2ef218b20994b617c57af120841a57fChet Haase return mDuration; 60121cd1389d2ef218b20994b617c57af120841a57fChet Haase } 60221cd1389d2ef218b20994b617c57af120841a57fChet Haase 60321cd1389d2ef218b20994b617c57af120841a57fChet Haase /** 604a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * Sets the length of each of the current child animations of this AnimatorSet. By default, 605a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * each child animation will use its own duration. If the duration is set on the AnimatorSet, 60621cd1389d2ef218b20994b617c57af120841a57fChet Haase * then each child animation inherits this duration. 60721cd1389d2ef218b20994b617c57af120841a57fChet Haase * 60821cd1389d2ef218b20994b617c57af120841a57fChet Haase * @param duration The length of the animation, in milliseconds, of each of the child 609a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * animations of this AnimatorSet. 61021cd1389d2ef218b20994b617c57af120841a57fChet Haase */ 61121cd1389d2ef218b20994b617c57af120841a57fChet Haase @Override 6122794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase public AnimatorSet setDuration(long duration) { 61321cd1389d2ef218b20994b617c57af120841a57fChet Haase if (duration < 0) { 61421cd1389d2ef218b20994b617c57af120841a57fChet Haase throw new IllegalArgumentException("duration must be a value of zero or greater"); 61521cd1389d2ef218b20994b617c57af120841a57fChet Haase } 6161309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu mDependencyDirty = true; 617c299a3384171e36fc9ab6d1011d8a589a7f344d1Chet Haase // Just record the value for now - it will be used later when the AnimatorSet starts 61821cd1389d2ef218b20994b617c57af120841a57fChet Haase mDuration = duration; 6192794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase return this; 62021cd1389d2ef218b20994b617c57af120841a57fChet Haase } 62121cd1389d2ef218b20994b617c57af120841a57fChet Haase 6222970c499388b4dcd1232cd622a9b80b395eeb2b4Chet Haase @Override 6232970c499388b4dcd1232cd622a9b80b395eeb2b4Chet Haase public void setupStartValues() { 6241309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu int size = mNodes.size(); 6251309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu for (int i = 0; i < size; i++) { 6261309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu Node node = mNodes.get(i); 627458f20ed5a3be51ab1166256dfeece37fa7e3c66Doris Liu if (node != mRootNode) { 628458f20ed5a3be51ab1166256dfeece37fa7e3c66Doris Liu node.mAnimation.setupStartValues(); 629458f20ed5a3be51ab1166256dfeece37fa7e3c66Doris Liu } 6302970c499388b4dcd1232cd622a9b80b395eeb2b4Chet Haase } 6312970c499388b4dcd1232cd622a9b80b395eeb2b4Chet Haase } 6322970c499388b4dcd1232cd622a9b80b395eeb2b4Chet Haase 6332970c499388b4dcd1232cd622a9b80b395eeb2b4Chet Haase @Override 6342970c499388b4dcd1232cd622a9b80b395eeb2b4Chet Haase public void setupEndValues() { 6351309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu int size = mNodes.size(); 6361309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu for (int i = 0; i < size; i++) { 6371309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu Node node = mNodes.get(i); 638458f20ed5a3be51ab1166256dfeece37fa7e3c66Doris Liu if (node != mRootNode) { 639458f20ed5a3be51ab1166256dfeece37fa7e3c66Doris Liu node.mAnimation.setupEndValues(); 640458f20ed5a3be51ab1166256dfeece37fa7e3c66Doris Liu } 6412970c499388b4dcd1232cd622a9b80b395eeb2b4Chet Haase } 6422970c499388b4dcd1232cd622a9b80b395eeb2b4Chet Haase } 6432970c499388b4dcd1232cd622a9b80b395eeb2b4Chet Haase 6448aa1ffb0ed292891030992c65df4e5dc8bd37524Chet Haase @Override 6458aa1ffb0ed292891030992c65df4e5dc8bd37524Chet Haase public void pause() { 64613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (Looper.myLooper() == null) { 64713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu throw new AndroidRuntimeException("Animators may only be run on Looper threads"); 64813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 6498aa1ffb0ed292891030992c65df4e5dc8bd37524Chet Haase boolean previouslyPaused = mPaused; 6508aa1ffb0ed292891030992c65df4e5dc8bd37524Chet Haase super.pause(); 6518aa1ffb0ed292891030992c65df4e5dc8bd37524Chet Haase if (!previouslyPaused && mPaused) { 65213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu mPauseTime = -1; 6538aa1ffb0ed292891030992c65df4e5dc8bd37524Chet Haase } 6548aa1ffb0ed292891030992c65df4e5dc8bd37524Chet Haase } 6558aa1ffb0ed292891030992c65df4e5dc8bd37524Chet Haase 6568aa1ffb0ed292891030992c65df4e5dc8bd37524Chet Haase @Override 6578aa1ffb0ed292891030992c65df4e5dc8bd37524Chet Haase public void resume() { 65813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (Looper.myLooper() == null) { 65913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu throw new AndroidRuntimeException("Animators may only be run on Looper threads"); 66013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 6618aa1ffb0ed292891030992c65df4e5dc8bd37524Chet Haase boolean previouslyPaused = mPaused; 6628aa1ffb0ed292891030992c65df4e5dc8bd37524Chet Haase super.resume(); 6638aa1ffb0ed292891030992c65df4e5dc8bd37524Chet Haase if (previouslyPaused && !mPaused) { 66413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (mPauseTime >= 0) { 66513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu addAnimationCallback(0); 6668aa1ffb0ed292891030992c65df4e5dc8bd37524Chet Haase } 6678aa1ffb0ed292891030992c65df4e5dc8bd37524Chet Haase } 6688aa1ffb0ed292891030992c65df4e5dc8bd37524Chet Haase } 6698aa1ffb0ed292891030992c65df4e5dc8bd37524Chet Haase 67021cd1389d2ef218b20994b617c57af120841a57fChet Haase /** 67117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * {@inheritDoc} 67217fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * 673a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * <p>Starting this <code>AnimatorSet</code> will, in turn, start the animations for which 67417fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * it is responsible. The details of when exactly those animations are started depends on 67517fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * the dependency relationships that have been set up between the animations. 67688bb31b95467f3591f4911be673b57c6fb316134Doris Liu * 67788bb31b95467f3591f4911be673b57c6fb316134Doris Liu * <b>Note:</b> Manipulating AnimatorSet's lifecycle in the child animators' listener callbacks 67888bb31b95467f3591f4911be673b57c6fb316134Doris Liu * will lead to undefined behaviors. Also, AnimatorSet will ignore any seeking in the child 67988bb31b95467f3591f4911be673b57c6fb316134Doris Liu * animators once {@link #start()} is called. 68017fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase */ 68117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase @SuppressWarnings("unchecked") 68217fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase @Override 68317fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase public void start() { 68413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu start(false, true); 68513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 68613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 68713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu @Override 68813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu void startWithoutPulsing(boolean inReverse) { 68913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu start(inReverse, false); 69013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 69113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 69213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu private void initAnimation() { 69313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (mInterpolator != null) { 69413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu for (int i = 0; i < mNodes.size(); i++) { 69513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu Node node = mNodes.get(i); 69613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu node.mAnimation.setInterpolator(mInterpolator); 69713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 69813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 69913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu updateAnimatorsDuration(); 70013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu createDependencyGraph(); 70113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 70213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 70313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu private void start(boolean inReverse, boolean selfPulse) { 70413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (Looper.myLooper() == null) { 70513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu throw new AndroidRuntimeException("Animators may only be run on Looper threads"); 70613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 7078b699792b677bd4dd8442b32641ac09d48fdd79cChet Haase mStarted = true; 70813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu mSelfPulse = selfPulse; 7098aa1ffb0ed292891030992c65df4e5dc8bd37524Chet Haase mPaused = false; 71013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu mPauseTime = -1; 711010dbaa1236cf2dcdc62c29049468e90188acaaeChet Haase 7121309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu int size = mNodes.size(); 7131309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu for (int i = 0; i < size; i++) { 7141309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu Node node = mNodes.get(i); 7151309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu node.mEnded = false; 7161309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu node.mAnimation.setAllowRunningAsynchronously(false); 717f5945a0c8bb868f978d9d0d22043a8b44464a86eJohn Reck } 718f5945a0c8bb868f978d9d0d22043a8b44464a86eJohn Reck 71913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu initAnimation(); 72013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (inReverse && !canReverse()) { 72113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu throw new UnsupportedOperationException("Cannot reverse infinite AnimatorSet"); 722e2ab7ccd385cdb6517955c719e1d2b49771bedb6Chet Haase } 723e2ab7ccd385cdb6517955c719e1d2b49771bedb6Chet Haase 72413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu mReversing = inReverse; 7251309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu 72617fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase // Now that all dependencies are set up, start the animations that should be started. 72721ba77f6b6c037e9507e210e22c18614fa9d2e9bDoris Liu boolean isEmptySet = isEmptySet(this); 72821ba77f6b6c037e9507e210e22c18614fa9d2e9bDoris Liu if (!isEmptySet) { 72913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu startAnimation(); 730f57bfe2fefc87fdb1dcc27b0f4b3a11996c15da2Doris Liu } 731f57bfe2fefc87fdb1dcc27b0f4b3a11996c15da2Doris Liu 73217fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase if (mListeners != null) { 733a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase ArrayList<AnimatorListener> tmpListeners = 734a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase (ArrayList<AnimatorListener>) mListeners.clone(); 7357c608f25d494c8a0a671e7373efbb47ca635367eChet Haase int numListeners = tmpListeners.size(); 7367c608f25d494c8a0a671e7373efbb47ca635367eChet Haase for (int i = 0; i < numListeners; ++i) { 73713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu tmpListeners.get(i).onAnimationStart(this, inReverse); 7388b699792b677bd4dd8442b32641ac09d48fdd79cChet Haase } 7398b699792b677bd4dd8442b32641ac09d48fdd79cChet Haase } 74021ba77f6b6c037e9507e210e22c18614fa9d2e9bDoris Liu if (isEmptySet) { 7416d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu // In the case of empty AnimatorSet, or 0 duration scale, we will trigger the 7426d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu // onAnimationEnd() right away. 743d7968dc174f60e3b8f5aaddd05703cf6ce9114adDoris Liu end(); 74417fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase } 74517fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase } 74617fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase 747f66d2f6f31d6a9bc51c8fa52915906c3594db82cDoris Liu // Returns true if set is empty or contains nothing but animator sets with no start delay. 748f66d2f6f31d6a9bc51c8fa52915906c3594db82cDoris Liu private static boolean isEmptySet(AnimatorSet set) { 749f66d2f6f31d6a9bc51c8fa52915906c3594db82cDoris Liu if (set.getStartDelay() > 0) { 750f66d2f6f31d6a9bc51c8fa52915906c3594db82cDoris Liu return false; 751f66d2f6f31d6a9bc51c8fa52915906c3594db82cDoris Liu } 752f66d2f6f31d6a9bc51c8fa52915906c3594db82cDoris Liu for (int i = 0; i < set.getChildAnimations().size(); i++) { 753f66d2f6f31d6a9bc51c8fa52915906c3594db82cDoris Liu Animator anim = set.getChildAnimations().get(i); 754f66d2f6f31d6a9bc51c8fa52915906c3594db82cDoris Liu if (!(anim instanceof AnimatorSet)) { 755f66d2f6f31d6a9bc51c8fa52915906c3594db82cDoris Liu // Contains non-AnimatorSet, not empty. 756f66d2f6f31d6a9bc51c8fa52915906c3594db82cDoris Liu return false; 757f66d2f6f31d6a9bc51c8fa52915906c3594db82cDoris Liu } else { 758f66d2f6f31d6a9bc51c8fa52915906c3594db82cDoris Liu if (!isEmptySet((AnimatorSet) anim)) { 759f66d2f6f31d6a9bc51c8fa52915906c3594db82cDoris Liu return false; 760f66d2f6f31d6a9bc51c8fa52915906c3594db82cDoris Liu } 761f66d2f6f31d6a9bc51c8fa52915906c3594db82cDoris Liu } 762f66d2f6f31d6a9bc51c8fa52915906c3594db82cDoris Liu } 763f66d2f6f31d6a9bc51c8fa52915906c3594db82cDoris Liu return true; 764f66d2f6f31d6a9bc51c8fa52915906c3594db82cDoris Liu } 765f66d2f6f31d6a9bc51c8fa52915906c3594db82cDoris Liu 7661309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu private void updateAnimatorsDuration() { 7671309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu if (mDuration >= 0) { 7681309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu // If the duration was set on this AnimatorSet, pass it along to all child animations 7691309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu int size = mNodes.size(); 7701309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu for (int i = 0; i < size; i++) { 7711309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu Node node = mNodes.get(i); 7721309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu // TODO: don't set the duration of the timing-only nodes created by AnimatorSet to 7731309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu // insert "play-after" delays 7741309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu node.mAnimation.setDuration(mDuration); 7751309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu } 7761309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu } 7771309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu mDelayAnim.setDuration(mStartDelay); 7781309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu } 7791309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu 78013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu @Override 78113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu void skipToEndValue(boolean inReverse) { 78213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (!isInitialized()) { 78313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu throw new UnsupportedOperationException("Children must be initialized."); 78413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 78513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 78613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // This makes sure the animation events are sorted an up to date. 78713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu initAnimation(); 78813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 78913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // Calling skip to the end in the sequence that they would be called in a forward/reverse 79013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // run, such that the sequential animations modifying the same property would have 79113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // the right value in the end. 79213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (inReverse) { 79313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu for (int i = mEvents.size() - 1; i >= 0; i--) { 79413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (mEvents.get(i).mEvent == AnimationEvent.ANIMATION_DELAY_ENDED) { 79513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu mEvents.get(i).mNode.mAnimation.skipToEndValue(true); 79613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 79713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 79813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } else { 79913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu for (int i = 0; i < mEvents.size(); i++) { 80013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (mEvents.get(i).mEvent == AnimationEvent.ANIMATION_END) { 80113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu mEvents.get(i).mNode.mAnimation.skipToEndValue(false); 80213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 80313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 80413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 80513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 80613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 80713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu /** 80813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu * Internal only. 80913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu * 81013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu * This method sets the animation values based on the play time. It also fast forward or 81113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu * backward all the child animations progress accordingly. 81213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu * 81313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu * This method is also responsible for calling 81413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu * {@link android.view.animation.Animation.AnimationListener#onAnimationRepeat(Animation)}, 81513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu * as needed, based on the last play time and current play time. 81613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu */ 81713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu @Override 81813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu void animateBasedOnPlayTime(long currentPlayTime, long lastPlayTime, boolean inReverse) { 81913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (currentPlayTime < 0 || lastPlayTime < 0) { 82013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu throw new UnsupportedOperationException("Error: Play time should never be negative."); 82113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 82213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // TODO: take into account repeat counts and repeat callback when repeat is implemented. 82313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // Clamp currentPlayTime and lastPlayTime 82413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 82513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // TODO: Make this more efficient 82613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 82713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // Convert the play times to the forward direction. 82813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (inReverse) { 82913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (getTotalDuration() == DURATION_INFINITE) { 83013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu throw new UnsupportedOperationException("Cannot reverse AnimatorSet with infinite" 83113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu + " duration"); 83213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 83313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu long duration = getTotalDuration() - mStartDelay; 83413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu currentPlayTime = Math.min(currentPlayTime, duration); 83513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu currentPlayTime = duration - currentPlayTime; 83613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu lastPlayTime = duration - lastPlayTime; 83713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu inReverse = false; 83813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 83913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // Skip all values to start, and iterate mEvents to get animations to the right fraction. 84013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu skipToStartValue(false); 84113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 84213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu ArrayList<Node> unfinishedNodes = new ArrayList<>(); 84313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // Assumes forward playing from here on. 84413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu for (int i = 0; i < mEvents.size(); i++) { 84513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu AnimationEvent event = mEvents.get(i); 846290271695d66cb6be38b1ebe80dc3473e6163738Doris Liu if (event.getTime() > currentPlayTime || event.getTime() == DURATION_INFINITE) { 84713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu break; 84813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 84913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 85013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // This animation started prior to the current play time, and won't finish before the 85113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // play time, add to the unfinished list. 85213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (event.mEvent == AnimationEvent.ANIMATION_DELAY_ENDED) { 85313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (event.mNode.mEndTime == DURATION_INFINITE 85413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu || event.mNode.mEndTime > currentPlayTime) { 85513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu unfinishedNodes.add(event.mNode); 85613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 85713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 85813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // For animations that do finish before the play time, end them in the sequence that 85913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // they would in a normal run. 86013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (event.mEvent == AnimationEvent.ANIMATION_END) { 86113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // Skip to the end of the animation. 86213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu event.mNode.mAnimation.skipToEndValue(false); 86313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 86413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 86513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 86613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // Seek unfinished animation to the right time. 86713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu for (int i = 0; i < unfinishedNodes.size(); i++) { 86813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu Node node = unfinishedNodes.get(i); 86913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu long playTime = getPlayTimeForNode(currentPlayTime, node, inReverse); 8706d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu if (!inReverse) { 8716d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu playTime -= node.mAnimation.getStartDelay(); 8726d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu } 87313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu node.mAnimation.animateBasedOnPlayTime(playTime, lastPlayTime, inReverse); 87413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 87513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 87613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 87713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu @Override 87813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu boolean isInitialized() { 87913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (mChildrenInitialized) { 88013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu return true; 88113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 88213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 88313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu boolean allInitialized = true; 88413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu for (int i = 0; i < mNodes.size(); i++) { 88513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (!mNodes.get(i).mAnimation.isInitialized()) { 88613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu allInitialized = false; 88713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu break; 88813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 88913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 89013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu mChildrenInitialized = allInitialized; 89113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu return mChildrenInitialized; 89213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 89313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 89413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu private void skipToStartValue(boolean inReverse) { 89513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu skipToEndValue(!inReverse); 89613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 89713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 89813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu /** 89913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu * Sets the position of the animation to the specified point in time. This time should 90013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu * be between 0 and the total duration of the animation, including any repetition. If 90113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu * the animation has not yet been started, then it will not advance forward after it is 90213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu * set to this time; it will simply set the time to this value and perform any appropriate 90313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu * actions based on that time. If the animation is already running, then setCurrentPlayTime() 90413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu * will set the current playing time to this value and continue playing from that point. 90513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu * 90613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu * @param playTime The time, in milliseconds, to which the animation is advanced or rewound. 90713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu * Unless the animation is reversing, the playtime is considered the time since 90813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu * the end of the start delay of the AnimatorSet in a forward playing direction. 90913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu * 91013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu */ 91113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu public void setCurrentPlayTime(long playTime) { 91213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (mReversing && getTotalDuration() == DURATION_INFINITE) { 91313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // Should never get here 91413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu throw new UnsupportedOperationException("Error: Cannot seek in reverse in an infinite" 91513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu + " AnimatorSet"); 91613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 91713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 91813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if ((getTotalDuration() != DURATION_INFINITE && playTime > getTotalDuration() - mStartDelay) 91913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu || playTime < 0) { 92013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu throw new UnsupportedOperationException("Error: Play time should always be in between" 92113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu + "0 and duration."); 92213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 92313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 92413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu initAnimation(); 92513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 92613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (!isStarted()) { 92713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (mReversing) { 92813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu throw new UnsupportedOperationException("Error: Something went wrong. mReversing" 92913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu + " should not be set when AnimatorSet is not started."); 93013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 93113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (!mSeekState.isActive()) { 93213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu findLatestEventIdForTime(0); 93313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // Set all the values to start values. 93413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu initChildren(); 93513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu skipToStartValue(mReversing); 93613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu mSeekState.setPlayTime(0, mReversing); 93713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 93813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu animateBasedOnPlayTime(playTime, 0, mReversing); 93913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu mSeekState.setPlayTime(playTime, mReversing); 94013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } else { 94113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // If the animation is running, just set the seek time and wait until the next frame 94213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // (i.e. doAnimationFrame(...)) to advance the animation. 94313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu mSeekState.setPlayTime(playTime, mReversing); 94413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 94513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 94613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 947dd65ab04071fd80007155e8cb4b205370acf759bDoris Liu /** 94802eabdff6b8d4a8c6152a4ef69c2839e66595512Doris Liu * Returns the milliseconds elapsed since the start of the animation. 949dd65ab04071fd80007155e8cb4b205370acf759bDoris Liu * 95002eabdff6b8d4a8c6152a4ef69c2839e66595512Doris Liu * <p>For ongoing animations, this method returns the current progress of the animation in 95102eabdff6b8d4a8c6152a4ef69c2839e66595512Doris Liu * terms of play time. For an animation that has not yet been started: if the animation has been 95202eabdff6b8d4a8c6152a4ef69c2839e66595512Doris Liu * seeked to a certain time via {@link #setCurrentPlayTime(long)}, the seeked play time will 95302eabdff6b8d4a8c6152a4ef69c2839e66595512Doris Liu * be returned; otherwise, this method will return 0. 95402eabdff6b8d4a8c6152a4ef69c2839e66595512Doris Liu * 95502eabdff6b8d4a8c6152a4ef69c2839e66595512Doris Liu * @return the current position in time of the animation in milliseconds 956dd65ab04071fd80007155e8cb4b205370acf759bDoris Liu */ 957dd65ab04071fd80007155e8cb4b205370acf759bDoris Liu public long getCurrentPlayTime() { 958dd65ab04071fd80007155e8cb4b205370acf759bDoris Liu if (mSeekState.isActive()) { 959dd65ab04071fd80007155e8cb4b205370acf759bDoris Liu return mSeekState.getPlayTime(); 960dd65ab04071fd80007155e8cb4b205370acf759bDoris Liu } 961dd65ab04071fd80007155e8cb4b205370acf759bDoris Liu if (mLastFrameTime == -1) { 962dd65ab04071fd80007155e8cb4b205370acf759bDoris Liu // Not yet started or during start delay 963dd65ab04071fd80007155e8cb4b205370acf759bDoris Liu return 0; 964dd65ab04071fd80007155e8cb4b205370acf759bDoris Liu } 965dd65ab04071fd80007155e8cb4b205370acf759bDoris Liu float durationScale = ValueAnimator.getDurationScale(); 966dd65ab04071fd80007155e8cb4b205370acf759bDoris Liu durationScale = durationScale == 0 ? 1 : durationScale; 967dd65ab04071fd80007155e8cb4b205370acf759bDoris Liu if (mReversing) { 968dd65ab04071fd80007155e8cb4b205370acf759bDoris Liu return (long) ((mLastFrameTime - mFirstFrame) / durationScale); 969dd65ab04071fd80007155e8cb4b205370acf759bDoris Liu } else { 970dd65ab04071fd80007155e8cb4b205370acf759bDoris Liu return (long) ((mLastFrameTime - mFirstFrame - mStartDelay) / durationScale); 971dd65ab04071fd80007155e8cb4b205370acf759bDoris Liu } 972dd65ab04071fd80007155e8cb4b205370acf759bDoris Liu } 973dd65ab04071fd80007155e8cb4b205370acf759bDoris Liu 97413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu private void initChildren() { 97513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (!isInitialized()) { 97613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu mChildrenInitialized = true; 97713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // Forcefully initialize all children based on their end time, so that if the start 97813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // value of a child is dependent on a previous animation, the animation will be 97913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // initialized after the the previous animations have been advanced to the end. 98013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu skipToEndValue(false); 98113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 98213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 98313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 98413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu /** 98513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu * @param frameTime The frame start time, in the {@link SystemClock#uptimeMillis()} time 98613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu * base. 98713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu * @return 98813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu * @hide 98913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu */ 99013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu @Override 99113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu public boolean doAnimationFrame(long frameTime) { 9926d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu float durationScale = ValueAnimator.getDurationScale(); 9936d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu if (durationScale == 0f) { 99421ba77f6b6c037e9507e210e22c18614fa9d2e9bDoris Liu // Duration scale is 0, end the animation right away. 9956d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu forceToEnd(); 9966d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu return true; 9976d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu } 9986d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu 9996d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu // After the first frame comes in, we need to wait for start delay to pass before updating 10006d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu // any animation values. 10016d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu if (mFirstFrame < 0) { 10026d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu mFirstFrame = frameTime; 100313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 100413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 100513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // Handle pause/resume 100613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (mPaused) { 100713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // Note: Child animations don't receive pause events. Since it's never a contract that 100813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // the child animators will be paused when set is paused, this is unlikely to be an 100913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // issue. 101013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu mPauseTime = frameTime; 101113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu removeAnimationCallback(); 101213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu return false; 101313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } else if (mPauseTime > 0) { 101413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // Offset by the duration that the animation was paused 101513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu mFirstFrame += (frameTime - mPauseTime); 101613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu mPauseTime = -1; 101713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 101813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 101913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // Continue at seeked position 102013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (mSeekState.isActive()) { 102113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu mSeekState.updateSeekDirection(mReversing); 10226d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu if (mReversing) { 10236d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu mFirstFrame = (long) (frameTime - mSeekState.getPlayTime() * durationScale); 10246d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu } else { 10256d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu mFirstFrame = (long) (frameTime - (mSeekState.getPlayTime() + mStartDelay) 10266d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu * durationScale); 10276d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu } 102813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu mSeekState.reset(); 102913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 103013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 10316d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu if (!mReversing && frameTime < mFirstFrame + mStartDelay * durationScale) { 10326d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu // Still during start delay in a forward playing case. 10336d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu return false; 10346d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu } 10356d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu 10366d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu // From here on, we always use unscaled play time. Note this unscaled playtime includes 10376d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu // the start delay. 10386d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu long unscaledPlayTime = (long) ((frameTime - mFirstFrame) / durationScale); 10396d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu mLastFrameTime = frameTime; 104013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 104113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // 1. Pulse the animators that will start or end in this frame 104213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // 2. Pulse the animators that will finish in a later frame 10436d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu int latestId = findLatestEventIdForTime(unscaledPlayTime); 104413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu int startId = mLastEventId; 104513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 10466d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu handleAnimationEvents(startId, latestId, unscaledPlayTime); 104713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 104813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu mLastEventId = latestId; 104913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 105013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // Pump a frame to the on-going animators 105113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu for (int i = 0; i < mPlayingSet.size(); i++) { 105213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu Node node = mPlayingSet.get(i); 105313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (!node.mEnded) { 10546d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu pulseFrame(node, getPlayTimeForNode(unscaledPlayTime, node)); 105513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 105613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 105713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 105813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // Remove all the finished anims 105913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu for (int i = mPlayingSet.size() - 1; i >= 0; i--) { 106013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (mPlayingSet.get(i).mEnded) { 106113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu mPlayingSet.remove(i); 106213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 106313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 106413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 10656d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu boolean finished = false; 10666d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu if (mReversing) { 10676d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu if (mPlayingSet.size() == 1 && mPlayingSet.get(0) == mRootNode) { 10686d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu // The only animation that is running is the delay animation. 10696d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu finished = true; 10706d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu } else if (mPlayingSet.isEmpty() && mLastEventId < 3) { 10716d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu // The only remaining animation is the delay animation 10726d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu finished = true; 107313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 10746d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu } else { 10756d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu finished = mPlayingSet.isEmpty() && mLastEventId == mEvents.size() - 1; 10766d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu } 10776d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu 10786d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu if (finished) { 10796d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu endAnimation(); 10806d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu return true; 108113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 108213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu return false; 108313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 108413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 108513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu /** 10865c71b8cc4ae3e76ce7c7462fff9426c8e96ea5f7Doris Liu * @hide 10875c71b8cc4ae3e76ce7c7462fff9426c8e96ea5f7Doris Liu */ 10885c71b8cc4ae3e76ce7c7462fff9426c8e96ea5f7Doris Liu @Override 10895c71b8cc4ae3e76ce7c7462fff9426c8e96ea5f7Doris Liu public void commitAnimationFrame(long frameTime) { 10905c71b8cc4ae3e76ce7c7462fff9426c8e96ea5f7Doris Liu // No op. 10915c71b8cc4ae3e76ce7c7462fff9426c8e96ea5f7Doris Liu } 10925c71b8cc4ae3e76ce7c7462fff9426c8e96ea5f7Doris Liu 10935c71b8cc4ae3e76ce7c7462fff9426c8e96ea5f7Doris Liu @Override 10945c71b8cc4ae3e76ce7c7462fff9426c8e96ea5f7Doris Liu boolean pulseAnimationFrame(long frameTime) { 10955c71b8cc4ae3e76ce7c7462fff9426c8e96ea5f7Doris Liu return doAnimationFrame(frameTime); 10965c71b8cc4ae3e76ce7c7462fff9426c8e96ea5f7Doris Liu } 10975c71b8cc4ae3e76ce7c7462fff9426c8e96ea5f7Doris Liu 10985c71b8cc4ae3e76ce7c7462fff9426c8e96ea5f7Doris Liu /** 109913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu * When playing forward, we call start() at the animation's scheduled start time, and make sure 110013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu * to pump a frame at the animation's scheduled end time. 110113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu * 110213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu * When playing in reverse, we should reverse the animation when we hit animation's end event, 110313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu * and expect the animation to end at the its delay ended event, rather than start event. 110413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu */ 110513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu private void handleAnimationEvents(int startId, int latestId, long playTime) { 110613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (mReversing) { 110713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu startId = startId == -1 ? mEvents.size() : startId; 110813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu for (int i = startId - 1; i >= latestId; i--) { 110913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu AnimationEvent event = mEvents.get(i); 111013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu Node node = event.mNode; 111113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (event.mEvent == AnimationEvent.ANIMATION_END) { 11125fb129c2abcf62ba09bff7322cad62adf9449887Doris Liu if (node.mAnimation.isStarted()) { 11135fb129c2abcf62ba09bff7322cad62adf9449887Doris Liu // If the animation has already been started before its due time (i.e. 11145fb129c2abcf62ba09bff7322cad62adf9449887Doris Liu // the child animator is being manipulated outside of the AnimatorSet), we 11155fb129c2abcf62ba09bff7322cad62adf9449887Doris Liu // need to cancel the animation to reset the internal state (e.g. frame 11165fb129c2abcf62ba09bff7322cad62adf9449887Doris Liu // time tracking) and remove the self pulsing callbacks 11175fb129c2abcf62ba09bff7322cad62adf9449887Doris Liu node.mAnimation.cancel(); 11185fb129c2abcf62ba09bff7322cad62adf9449887Doris Liu } 11195fb129c2abcf62ba09bff7322cad62adf9449887Doris Liu node.mEnded = false; 112013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu mPlayingSet.add(event.mNode); 112113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu node.mAnimation.startWithoutPulsing(true); 11225c71b8cc4ae3e76ce7c7462fff9426c8e96ea5f7Doris Liu pulseFrame(node, 0); 112313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } else if (event.mEvent == AnimationEvent.ANIMATION_DELAY_ENDED && !node.mEnded) { 112413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // end event: 11255c71b8cc4ae3e76ce7c7462fff9426c8e96ea5f7Doris Liu pulseFrame(node, getPlayTimeForNode(playTime, node)); 112613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 112713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 112813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } else { 112913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu for (int i = startId + 1; i <= latestId; i++) { 113013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu AnimationEvent event = mEvents.get(i); 113113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu Node node = event.mNode; 113213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (event.mEvent == AnimationEvent.ANIMATION_START) { 113313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu mPlayingSet.add(event.mNode); 11345fb129c2abcf62ba09bff7322cad62adf9449887Doris Liu if (node.mAnimation.isStarted()) { 11355fb129c2abcf62ba09bff7322cad62adf9449887Doris Liu // If the animation has already been started before its due time (i.e. 11365fb129c2abcf62ba09bff7322cad62adf9449887Doris Liu // the child animator is being manipulated outside of the AnimatorSet), we 11375fb129c2abcf62ba09bff7322cad62adf9449887Doris Liu // need to cancel the animation to reset the internal state (e.g. frame 11385fb129c2abcf62ba09bff7322cad62adf9449887Doris Liu // time tracking) and remove the self pulsing callbacks 11395fb129c2abcf62ba09bff7322cad62adf9449887Doris Liu node.mAnimation.cancel(); 11405fb129c2abcf62ba09bff7322cad62adf9449887Doris Liu } 11415fb129c2abcf62ba09bff7322cad62adf9449887Doris Liu node.mEnded = false; 114213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu node.mAnimation.startWithoutPulsing(false); 11435c71b8cc4ae3e76ce7c7462fff9426c8e96ea5f7Doris Liu pulseFrame(node, 0); 114413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } else if (event.mEvent == AnimationEvent.ANIMATION_END && !node.mEnded) { 114513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // start event: 11465c71b8cc4ae3e76ce7c7462fff9426c8e96ea5f7Doris Liu pulseFrame(node, getPlayTimeForNode(playTime, node)); 114713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 114813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 114913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 115013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 115113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 11526d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu /** 11536d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu * This method pulses frames into child animations. It scales the input animation play time 11546d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu * with the duration scale and pass that to the child animation via pulseAnimationFrame(long). 11556d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu * 11566d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu * @param node child animator node 11576d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu * @param animPlayTime unscaled play time (including start delay) for the child animator 11586d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu */ 11596d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu private void pulseFrame(Node node, long animPlayTime) { 11605c71b8cc4ae3e76ce7c7462fff9426c8e96ea5f7Doris Liu if (!node.mEnded) { 1161d7968dc174f60e3b8f5aaddd05703cf6ce9114adDoris Liu float durationScale = ValueAnimator.getDurationScale(); 1162d7968dc174f60e3b8f5aaddd05703cf6ce9114adDoris Liu durationScale = durationScale == 0 ? 1 : durationScale; 11636d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu node.mEnded = node.mAnimation.pulseAnimationFrame( 1164d7968dc174f60e3b8f5aaddd05703cf6ce9114adDoris Liu (long) (animPlayTime * durationScale)); 11655c71b8cc4ae3e76ce7c7462fff9426c8e96ea5f7Doris Liu } 11665c71b8cc4ae3e76ce7c7462fff9426c8e96ea5f7Doris Liu } 11675c71b8cc4ae3e76ce7c7462fff9426c8e96ea5f7Doris Liu 116813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu private long getPlayTimeForNode(long overallPlayTime, Node node) { 116913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu return getPlayTimeForNode(overallPlayTime, node, mReversing); 117013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 117113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 117213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu private long getPlayTimeForNode(long overallPlayTime, Node node, boolean inReverse) { 117313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (inReverse) { 117413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu overallPlayTime = getTotalDuration() - overallPlayTime; 117513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu return node.mEndTime - overallPlayTime; 117613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } else { 117713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu return overallPlayTime - node.mStartTime; 117813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 117913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 118013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 118113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu private void startAnimation() { 118266c564e390ea71576db7d4c1803b169cc98e987bDoris Liu addDummyListener(); 118366c564e390ea71576db7d4c1803b169cc98e987bDoris Liu 118413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // Register animation callback 11856d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu addAnimationCallback(0); 118613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 118713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (mSeekState.getPlayTimeNormalized() == 0 && mReversing) { 118813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // Maintain old behavior, if seeked to 0 then call reverse, we'll treat the case 118913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // the same as no seeking at all. 119013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu mSeekState.reset(); 119113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 119213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // Set the child animators to the right end: 119313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (mShouldResetValuesAtStart) { 11946d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu if (isInitialized()) { 11956d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu skipToEndValue(!mReversing); 11966d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu } else if (mReversing) { 11976d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu // Reversing but haven't initialized all the children yet. 11986d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu initChildren(); 119913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu skipToEndValue(!mReversing); 120013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } else { 120113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // If not all children are initialized and play direction is forward 120213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu for (int i = mEvents.size() - 1; i >= 0; i--) { 120313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (mEvents.get(i).mEvent == AnimationEvent.ANIMATION_DELAY_ENDED) { 120413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu Animator anim = mEvents.get(i).mNode.mAnimation; 120513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // Only reset the animations that have been initialized to start value, 120613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // so that if they are defined without a start value, they will get the 120713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // values set at the right time (i.e. the next animation run) 120813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (anim.isInitialized()) { 120913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu anim.skipToEndValue(true); 121013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 121113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 121213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 121313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 121413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 121513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 121613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (mReversing || mStartDelay == 0 || mSeekState.isActive()) { 121713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu long playTime; 121813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // If no delay, we need to call start on the first animations to be consistent with old 121913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // behavior. 122013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (mSeekState.isActive()) { 122113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu mSeekState.updateSeekDirection(mReversing); 122213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu playTime = mSeekState.getPlayTime(); 122313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } else { 122413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu playTime = 0; 122513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 122613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu int toId = findLatestEventIdForTime(playTime); 122713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu handleAnimationEvents(-1, toId, playTime); 12286d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu for (int i = mPlayingSet.size() - 1; i >= 0; i--) { 12296d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu if (mPlayingSet.get(i).mEnded) { 12306d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu mPlayingSet.remove(i); 12316d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu } 12326d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu } 123313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu mLastEventId = toId; 123413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 123513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 123613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 123766c564e390ea71576db7d4c1803b169cc98e987bDoris Liu // This is to work around the issue in b/34736819, as the old behavior in AnimatorSet had 123866c564e390ea71576db7d4c1803b169cc98e987bDoris Liu // masked a real bug in play movies. TODO: remove this and below once the root cause is fixed. 123966c564e390ea71576db7d4c1803b169cc98e987bDoris Liu private void addDummyListener() { 124066c564e390ea71576db7d4c1803b169cc98e987bDoris Liu for (int i = 1; i < mNodes.size(); i++) { 124166c564e390ea71576db7d4c1803b169cc98e987bDoris Liu mNodes.get(i).mAnimation.addListener(mDummyListener); 124266c564e390ea71576db7d4c1803b169cc98e987bDoris Liu } 124366c564e390ea71576db7d4c1803b169cc98e987bDoris Liu } 124466c564e390ea71576db7d4c1803b169cc98e987bDoris Liu 124566c564e390ea71576db7d4c1803b169cc98e987bDoris Liu private void removeDummyListener() { 124666c564e390ea71576db7d4c1803b169cc98e987bDoris Liu for (int i = 1; i < mNodes.size(); i++) { 124766c564e390ea71576db7d4c1803b169cc98e987bDoris Liu mNodes.get(i).mAnimation.removeListener(mDummyListener); 124866c564e390ea71576db7d4c1803b169cc98e987bDoris Liu } 124966c564e390ea71576db7d4c1803b169cc98e987bDoris Liu } 125066c564e390ea71576db7d4c1803b169cc98e987bDoris Liu 125113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu private int findLatestEventIdForTime(long currentPlayTime) { 125213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu int size = mEvents.size(); 125313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu int latestId = mLastEventId; 125413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // Call start on the first animations now to be consistent with the old behavior 125513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (mReversing) { 125613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu currentPlayTime = getTotalDuration() - currentPlayTime; 125713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu mLastEventId = mLastEventId == -1 ? size : mLastEventId; 125813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu for (int j = mLastEventId - 1; j >= 0; j--) { 125913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu AnimationEvent event = mEvents.get(j); 126013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (event.getTime() >= currentPlayTime) { 126113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu latestId = j; 126213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 126313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 126413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } else { 126513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu for (int i = mLastEventId + 1; i < size; i++) { 126613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu AnimationEvent event = mEvents.get(i); 1267290271695d66cb6be38b1ebe80dc3473e6163738Doris Liu // TODO: need a function that accounts for infinite duration to compare time 1268290271695d66cb6be38b1ebe80dc3473e6163738Doris Liu if (event.getTime() != DURATION_INFINITE && event.getTime() <= currentPlayTime) { 126913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu latestId = i; 127013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 127113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 127213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 127313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu return latestId; 127413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 127513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 127613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu private void endAnimation() { 127713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu mStarted = false; 127813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu mLastFrameTime = -1; 127913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu mFirstFrame = -1; 128013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu mLastEventId = -1; 128113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu mPaused = false; 128213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu mPauseTime = -1; 128313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu mSeekState.reset(); 128413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu mPlayingSet.clear(); 128513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 128613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // No longer receive callbacks 128713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu removeAnimationCallback(); 128813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // Call end listener 128913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (mListeners != null) { 129013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu ArrayList<AnimatorListener> tmpListeners = 129113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu (ArrayList<AnimatorListener>) mListeners.clone(); 129213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu int numListeners = tmpListeners.size(); 129313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu for (int i = 0; i < numListeners; ++i) { 129413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu tmpListeners.get(i).onAnimationEnd(this, mReversing); 129513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 129613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 129766c564e390ea71576db7d4c1803b169cc98e987bDoris Liu removeDummyListener(); 129813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu mSelfPulse = true; 129913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu mReversing = false; 130013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 130113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 130213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu private void removeAnimationCallback() { 130313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (!mSelfPulse) { 130413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu return; 130513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 130613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu AnimationHandler handler = AnimationHandler.getInstance(); 130713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu handler.removeCallback(this); 130813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 130913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 131013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu private void addAnimationCallback(long delay) { 131113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (!mSelfPulse) { 131213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu return; 131313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 131413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu AnimationHandler handler = AnimationHandler.getInstance(); 131513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu handler.addAnimationFrameCallback(this, delay); 13161309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu } 13171309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu 131849afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase @Override 1319a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase public AnimatorSet clone() { 1320a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase final AnimatorSet anim = (AnimatorSet) super.clone(); 132149afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase /* 132249afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase * The basic clone() operation copies all items. This doesn't work very well for 1323a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * AnimatorSet, because it will copy references that need to be recreated and state 132449afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase * that may not apply. What we need to do now is put the clone in an uninitialized 132549afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase * state, with fresh, empty data structures. Then we will build up the nodes list 132649afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase * manually, as we clone each Node (and its animation). The clone will then be sorted, 132749afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase * and will populate any appropriate lists, when it is started. 132849afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase */ 1329d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar final int nodeCount = mNodes.size(); 13308b699792b677bd4dd8442b32641ac09d48fdd79cChet Haase anim.mStarted = false; 133113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu anim.mLastFrameTime = -1; 133213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu anim.mFirstFrame = -1; 133313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu anim.mLastEventId = -1; 133413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu anim.mPaused = false; 133513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu anim.mPauseTime = -1; 133613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu anim.mSeekState = new SeekState(); 133713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu anim.mSelfPulse = true; 133813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu anim.mPlayingSet = new ArrayList<Node>(); 1339d7444427d9f44b6b7448d4c21edca866132c8b59Doris Liu anim.mNodeMap = new ArrayMap<Animator, Node>(); 1340d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar anim.mNodes = new ArrayList<Node>(nodeCount); 134113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu anim.mEvents = new ArrayList<AnimationEvent>(); 13426ba5ed322c04f260c51d58f2e49c6189b672f2d3Doris Liu anim.mDummyListener = new AnimatorListenerAdapter() { 13436ba5ed322c04f260c51d58f2e49c6189b672f2d3Doris Liu @Override 13446ba5ed322c04f260c51d58f2e49c6189b672f2d3Doris Liu public void onAnimationEnd(Animator animation) { 13456ba5ed322c04f260c51d58f2e49c6189b672f2d3Doris Liu if (anim.mNodeMap.get(animation) == null) { 13466ba5ed322c04f260c51d58f2e49c6189b672f2d3Doris Liu throw new AndroidRuntimeException("Error: animation ended is not in the node" 13476ba5ed322c04f260c51d58f2e49c6189b672f2d3Doris Liu + " map"); 13486ba5ed322c04f260c51d58f2e49c6189b672f2d3Doris Liu } 13496ba5ed322c04f260c51d58f2e49c6189b672f2d3Doris Liu anim.mNodeMap.get(animation).mEnded = true; 13506ba5ed322c04f260c51d58f2e49c6189b672f2d3Doris Liu 13516ba5ed322c04f260c51d58f2e49c6189b672f2d3Doris Liu } 13526ba5ed322c04f260c51d58f2e49c6189b672f2d3Doris Liu }; 135313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu anim.mReversing = false; 135413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu anim.mDependencyDirty = true; 135549afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase 135649afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase // Walk through the old nodes list, cloning each node and adding it to the new nodemap. 1357a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase // One problem is that the old node dependencies point to nodes in the old AnimatorSet. 135849afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase // We need to track the old/new nodes in order to reconstruct the dependencies in the clone. 1359d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar 136056b2df05f56b2c7b82a50b46341d055f6d70a913Doris Liu HashMap<Node, Node> clonesMap = new HashMap<>(nodeCount); 1361d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar for (int n = 0; n < nodeCount; n++) { 1362d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar final Node node = mNodes.get(n); 136349afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase Node nodeClone = node.clone(); 1364c457547cbd26e754d126f4f3651b4a7b2a262799Doris Liu // Remove the old internal listener from the cloned child 1365c457547cbd26e754d126f4f3651b4a7b2a262799Doris Liu nodeClone.mAnimation.removeListener(mDummyListener); 136656b2df05f56b2c7b82a50b46341d055f6d70a913Doris Liu clonesMap.put(node, nodeClone); 136749afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase anim.mNodes.add(nodeClone); 13681309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu anim.mNodeMap.put(nodeClone.mAnimation, nodeClone); 136949afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase } 13701309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu 137156b2df05f56b2c7b82a50b46341d055f6d70a913Doris Liu anim.mRootNode = clonesMap.get(mRootNode); 13721309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu anim.mDelayAnim = (ValueAnimator) anim.mRootNode.mAnimation; 13731309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu 137449afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase // Now that we've cloned all of the nodes, we're ready to walk through their 137549afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase // dependencies, mapping the old dependencies to the new nodes 13761309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu for (int i = 0; i < nodeCount; i++) { 13771309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu Node node = mNodes.get(i); 13781309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu // Update dependencies for node's clone 137956b2df05f56b2c7b82a50b46341d055f6d70a913Doris Liu Node nodeClone = clonesMap.get(node); 138056b2df05f56b2c7b82a50b46341d055f6d70a913Doris Liu nodeClone.mLatestParent = node.mLatestParent == null 138156b2df05f56b2c7b82a50b46341d055f6d70a913Doris Liu ? null : clonesMap.get(node.mLatestParent); 13821309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu int size = node.mChildNodes == null ? 0 : node.mChildNodes.size(); 13831309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu for (int j = 0; j < size; j++) { 138456b2df05f56b2c7b82a50b46341d055f6d70a913Doris Liu nodeClone.mChildNodes.set(j, clonesMap.get(node.mChildNodes.get(j))); 1385d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar } 13861309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu size = node.mSiblings == null ? 0 : node.mSiblings.size(); 13871309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu for (int j = 0; j < size; j++) { 138856b2df05f56b2c7b82a50b46341d055f6d70a913Doris Liu nodeClone.mSiblings.set(j, clonesMap.get(node.mSiblings.get(j))); 1389d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar } 13901309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu size = node.mParents == null ? 0 : node.mParents.size(); 13911309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu for (int j = 0; j < size; j++) { 139256b2df05f56b2c7b82a50b46341d055f6d70a913Doris Liu nodeClone.mParents.set(j, clonesMap.get(node.mParents.get(j))); 139349afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase } 139449afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase } 139549afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase return anim; 139649afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase } 139749afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase 139817fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase 139917fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase /** 1400c4bb185d41cfb960ed9a3178a4f8974c351abdb0Doris Liu * AnimatorSet is only reversible when the set contains no sequential animation, and no child 1401c4bb185d41cfb960ed9a3178a4f8974c351abdb0Doris Liu * animators have a start delay. 14027bc6a3f023ca3e1dde91fc97b6036dee3ba538a2ztenghui * @hide 14037bc6a3f023ca3e1dde91fc97b6036dee3ba538a2ztenghui */ 14047bc6a3f023ca3e1dde91fc97b6036dee3ba538a2ztenghui @Override 14057bc6a3f023ca3e1dde91fc97b6036dee3ba538a2ztenghui public boolean canReverse() { 140613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu return getTotalDuration() != DURATION_INFINITE; 14077bc6a3f023ca3e1dde91fc97b6036dee3ba538a2ztenghui } 14087bc6a3f023ca3e1dde91fc97b6036dee3ba538a2ztenghui 14097bc6a3f023ca3e1dde91fc97b6036dee3ba538a2ztenghui /** 141013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu * Plays the AnimatorSet in reverse. If the animation has been seeked to a specific play time 141113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu * using {@link #setCurrentPlayTime(long)}, it will play backwards from the point seeked when 141213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu * reverse was called. Otherwise, then it will start from the end and play backwards. This 141313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu * behavior is only set for the current animation; future playing of the animation will use the 141413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu * default behavior of playing forward. 141513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu * <p> 141613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu * Note: reverse is not supported for infinite AnimatorSet. 14177bc6a3f023ca3e1dde91fc97b6036dee3ba538a2ztenghui */ 14187bc6a3f023ca3e1dde91fc97b6036dee3ba538a2ztenghui @Override 14197bc6a3f023ca3e1dde91fc97b6036dee3ba538a2ztenghui public void reverse() { 142013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu start(true, true); 14217bc6a3f023ca3e1dde91fc97b6036dee3ba538a2ztenghui } 14227bc6a3f023ca3e1dde91fc97b6036dee3ba538a2ztenghui 1423d430753cba09acb07af8b313286f247c78a41a32Chet Haase @Override 1424d430753cba09acb07af8b313286f247c78a41a32Chet Haase public String toString() { 1425d430753cba09acb07af8b313286f247c78a41a32Chet Haase String returnVal = "AnimatorSet@" + Integer.toHexString(hashCode()) + "{"; 14261309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu int size = mNodes.size(); 14271309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu for (int i = 0; i < size; i++) { 14281309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu Node node = mNodes.get(i); 14291309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu returnVal += "\n " + node.mAnimation.toString(); 1430d430753cba09acb07af8b313286f247c78a41a32Chet Haase } 1431d430753cba09acb07af8b313286f247c78a41a32Chet Haase return returnVal + "\n}"; 1432d430753cba09acb07af8b313286f247c78a41a32Chet Haase } 1433d430753cba09acb07af8b313286f247c78a41a32Chet Haase 14341309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu private void printChildCount() { 14351309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu // Print out the child count through a level traverse. 14361309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu ArrayList<Node> list = new ArrayList<>(mNodes.size()); 14371309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu list.add(mRootNode); 14381309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu Log.d(TAG, "Current tree: "); 14391309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu int index = 0; 14401309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu while (index < list.size()) { 14411309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu int listSize = list.size(); 14421309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu StringBuilder builder = new StringBuilder(); 14431309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu for (; index < listSize; index++) { 14441309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu Node node = list.get(index); 14451309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu int num = 0; 14461309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu if (node.mChildNodes != null) { 14471309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu for (int i = 0; i < node.mChildNodes.size(); i++) { 14481309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu Node child = node.mChildNodes.get(i); 14491309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu if (child.mLatestParent == node) { 14501309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu num++; 14511309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu list.add(child); 14521309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu } 14531309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu } 14541309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu } 14551309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu builder.append(" "); 14561309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu builder.append(num); 14571309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu } 14581309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu Log.d(TAG, builder.toString()); 14591309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu } 14601309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu } 14611309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu 14621309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu private void createDependencyGraph() { 14631309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu if (!mDependencyDirty) { 146449db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu // Check whether any duration of the child animations has changed 146549db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu boolean durationChanged = false; 146649db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu for (int i = 0; i < mNodes.size(); i++) { 146749db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu Animator anim = mNodes.get(i).mAnimation; 146849db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu if (mNodes.get(i).mTotalDuration != anim.getTotalDuration()) { 146949db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu durationChanged = true; 147049db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu break; 147149db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu } 147249db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu } 147349db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu if (!durationChanged) { 147449db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu return; 147549db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu } 14761309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu } 14771309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu 14781309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu mDependencyDirty = false; 14791309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu // Traverse all the siblings and make sure they have all the parents 14801309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu int size = mNodes.size(); 14811309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu for (int i = 0; i < size; i++) { 14821309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu mNodes.get(i).mParentsAdded = false; 14831309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu } 14841309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu for (int i = 0; i < size; i++) { 14851309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu Node node = mNodes.get(i); 14861309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu if (node.mParentsAdded) { 14871309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu continue; 14881309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu } 14891309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu 14901309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu node.mParentsAdded = true; 14911309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu if (node.mSiblings == null) { 14921309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu continue; 14931309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu } 14941309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu 14951309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu // Find all the siblings 14961309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu findSiblings(node, node.mSiblings); 14971309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu node.mSiblings.remove(node); 14981309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu 14991309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu // Get parents from all siblings 15001309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu int siblingSize = node.mSiblings.size(); 15011309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu for (int j = 0; j < siblingSize; j++) { 15021309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu node.addParents(node.mSiblings.get(j).mParents); 15031309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu } 15041309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu 15051309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu // Now make sure all siblings share the same set of parents 15061309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu for (int j = 0; j < siblingSize; j++) { 15071309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu Node sibling = node.mSiblings.get(j); 15081309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu sibling.addParents(node.mParents); 15091309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu sibling.mParentsAdded = true; 15101309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu } 15111309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu } 15121309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu 15131309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu for (int i = 0; i < size; i++) { 15141309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu Node node = mNodes.get(i); 15151309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu if (node != mRootNode && node.mParents == null) { 15161309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu node.addParent(mRootNode); 15171309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu } 15181309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu } 15191309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu 15201309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu // Do a DFS on the tree 15211309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu ArrayList<Node> visited = new ArrayList<Node>(mNodes.size()); 15221309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu // Assign start/end time 15231309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu mRootNode.mStartTime = 0; 15241309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu mRootNode.mEndTime = mDelayAnim.getDuration(); 15251309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu updatePlayTime(mRootNode, visited); 15261309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu 152713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu sortAnimationEvents(); 152813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu mTotalDuration = mEvents.get(mEvents.size() - 1).getTime(); 152913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 153013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 153113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu private void sortAnimationEvents() { 153213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // Sort the list of events in ascending order of their time 153313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // Create the list including the delay animation. 153413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu mEvents.clear(); 153540e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu for (int i = 1; i < mNodes.size(); i++) { 15361309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu Node node = mNodes.get(i); 153713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu mEvents.add(new AnimationEvent(node, AnimationEvent.ANIMATION_START)); 153813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu mEvents.add(new AnimationEvent(node, AnimationEvent.ANIMATION_DELAY_ENDED)); 153913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu mEvents.add(new AnimationEvent(node, AnimationEvent.ANIMATION_END)); 154013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 154113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu mEvents.sort(new Comparator<AnimationEvent>() { 154213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu @Override 154313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu public int compare(AnimationEvent e1, AnimationEvent e2) { 154413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu long t1 = e1.getTime(); 154513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu long t2 = e2.getTime(); 154613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (t1 == t2) { 154740e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu // For events that happen at the same time, we need them to be in the sequence 154840e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu // (end, start, start delay ended) 154940e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu if (e2.mEvent + e1.mEvent == AnimationEvent.ANIMATION_START 155040e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu + AnimationEvent.ANIMATION_DELAY_ENDED) { 155140e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu // Ensure start delay happens after start 155213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu return e1.mEvent - e2.mEvent; 155340e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu } else { 155440e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu return e2.mEvent - e1.mEvent; 155513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 155613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 155713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (t2 == DURATION_INFINITE) { 155813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu return -1; 155913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 156013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (t1 == DURATION_INFINITE) { 156113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu return 1; 156213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 156313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // When neither event happens at INFINITE time: 156413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu return (int) (t1 - t2); 15651309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu } 156613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu }); 156713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 156840e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu int eventSize = mEvents.size(); 156940e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu // For the same animation, start event has to happen before end. 157040e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu for (int i = 0; i < eventSize;) { 157140e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu AnimationEvent event = mEvents.get(i); 157240e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu if (event.mEvent == AnimationEvent.ANIMATION_END) { 157340e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu boolean needToSwapStart; 157440e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu if (event.mNode.mStartTime == event.mNode.mEndTime) { 157540e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu needToSwapStart = true; 157640e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu } else if (event.mNode.mEndTime == event.mNode.mStartTime 157740e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu + event.mNode.mAnimation.getStartDelay()) { 157840e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu // Swapping start delay 157940e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu needToSwapStart = false; 158040e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu } else { 158140e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu i++; 158240e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu continue; 158340e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu } 158440e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu 158540e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu int startEventId = eventSize; 158640e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu int startDelayEndId = eventSize; 158740e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu for (int j = i + 1; j < eventSize; j++) { 158840e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu if (startEventId < eventSize && startDelayEndId < eventSize) { 158940e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu break; 159040e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu } 159140e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu if (mEvents.get(j).mNode == event.mNode) { 159240e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu if (mEvents.get(j).mEvent == AnimationEvent.ANIMATION_START) { 159340e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu // Found start event 159440e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu startEventId = j; 159540e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu } else if (mEvents.get(j).mEvent == AnimationEvent.ANIMATION_DELAY_ENDED) { 159640e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu startDelayEndId = j; 159740e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu } 159840e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu } 159940e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu 160040e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu } 160140e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu if (needToSwapStart && startEventId == mEvents.size()) { 160240e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu throw new UnsupportedOperationException("Something went wrong, no start is" 160340e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu + "found after stop for an animation that has the same start and end" 160440e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu + "time."); 160540e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu 160640e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu } 160740e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu if (startDelayEndId == mEvents.size()) { 160840e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu throw new UnsupportedOperationException("Something went wrong, no start" 160940e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu + "delay end is found after stop for an animation"); 161040e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu 161140e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu } 161240e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu 161340e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu // We need to make sure start is inserted before start delay ended event, 161440e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu // because otherwise inserting start delay ended events first would change 161540e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu // the start event index. 161640e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu if (needToSwapStart) { 161740e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu AnimationEvent startEvent = mEvents.remove(startEventId); 161840e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu mEvents.add(i, startEvent); 161940e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu i++; 162040e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu } 162140e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu 162240e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu AnimationEvent startDelayEndEvent = mEvents.remove(startDelayEndId); 162340e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu mEvents.add(i, startDelayEndEvent); 162440e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu i += 2; 162540e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu } else { 162640e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu i++; 162740e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu } 162813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 162940e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu 163040e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu if (!mEvents.isEmpty() && mEvents.get(0).mEvent != AnimationEvent.ANIMATION_START) { 163113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu throw new UnsupportedOperationException( 163240e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu "Sorting went bad, the start event should always be at index 0"); 163313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 163440e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu 163540e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu // Add AnimatorSet's start delay node to the beginning 163640e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu mEvents.add(0, new AnimationEvent(mRootNode, AnimationEvent.ANIMATION_START)); 163740e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu mEvents.add(1, new AnimationEvent(mRootNode, AnimationEvent.ANIMATION_DELAY_ENDED)); 163840e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu mEvents.add(2, new AnimationEvent(mRootNode, AnimationEvent.ANIMATION_END)); 163940e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu 164040e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu if (mEvents.get(mEvents.size() - 1).mEvent == AnimationEvent.ANIMATION_START 164140e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu || mEvents.get(mEvents.size() - 1).mEvent == AnimationEvent.ANIMATION_DELAY_ENDED) { 164213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu throw new UnsupportedOperationException( 164340e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu "Something went wrong, the last event is not an end event"); 16441309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu } 16451309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu } 16461309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu 16477bc6a3f023ca3e1dde91fc97b6036dee3ba538a2ztenghui /** 16481309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu * Based on parent's start/end time, calculate children's start/end time. If cycle exists in 16491309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu * the graph, all the nodes on the cycle will be marked to start at {@link #DURATION_INFINITE}, 16501309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu * meaning they will ever play. 165117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase */ 16521309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu private void updatePlayTime(Node parent, ArrayList<Node> visited) { 16531309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu if (parent.mChildNodes == null) { 165449db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu if (parent == mRootNode) { 165549db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu // All the animators are in a cycle 165649db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu for (int i = 0; i < mNodes.size(); i++) { 165749db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu Node node = mNodes.get(i); 165849db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu if (node != mRootNode) { 165949db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu node.mStartTime = DURATION_INFINITE; 166049db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu node.mEndTime = DURATION_INFINITE; 166149db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu } 166249db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu } 166349db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu } 16641309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu return; 16651309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu } 166617fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase 16671309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu visited.add(parent); 16681309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu int childrenSize = parent.mChildNodes.size(); 16691309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu for (int i = 0; i < childrenSize; i++) { 16701309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu Node child = parent.mChildNodes.get(i); 16717fb80f7d277bcde8a685088b97f52a39be567c7cPhilip Quinn child.mTotalDuration = child.mAnimation.getTotalDuration(); // Update cached duration. 16727fb80f7d277bcde8a685088b97f52a39be567c7cPhilip Quinn 16731309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu int index = visited.indexOf(child); 16741309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu if (index >= 0) { 16751309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu // Child has been visited, cycle found. Mark all the nodes in the cycle. 167649db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu for (int j = index; j < visited.size(); j++) { 16771309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu visited.get(j).mLatestParent = null; 16781309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu visited.get(j).mStartTime = DURATION_INFINITE; 16791309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu visited.get(j).mEndTime = DURATION_INFINITE; 16801309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu } 16811309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu child.mStartTime = DURATION_INFINITE; 16821309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu child.mEndTime = DURATION_INFINITE; 16831309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu child.mLatestParent = null; 16841309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu Log.w(TAG, "Cycle found in AnimatorSet: " + this); 16851309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu continue; 16861309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu } 168717fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase 16881309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu if (child.mStartTime != DURATION_INFINITE) { 16891309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu if (parent.mEndTime == DURATION_INFINITE) { 16901309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu child.mLatestParent = parent; 16911309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu child.mStartTime = DURATION_INFINITE; 16921309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu child.mEndTime = DURATION_INFINITE; 16931309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu } else { 16941309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu if (parent.mEndTime >= child.mStartTime) { 16951309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu child.mLatestParent = parent; 16961309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu child.mStartTime = parent.mEndTime; 16971309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu } 16981309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu 16997fb80f7d277bcde8a685088b97f52a39be567c7cPhilip Quinn child.mEndTime = child.mTotalDuration == DURATION_INFINITE 17007fb80f7d277bcde8a685088b97f52a39be567c7cPhilip Quinn ? DURATION_INFINITE : child.mStartTime + child.mTotalDuration; 17011309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu } 17021309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu } 17031309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu updatePlayTime(child, visited); 17041309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu } 17051309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu visited.remove(parent); 17061309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu } 17071309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu 17081309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu // Recursively find all the siblings 17091309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu private void findSiblings(Node node, ArrayList<Node> siblings) { 17101309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu if (!siblings.contains(node)) { 17111309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu siblings.add(node); 17121309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu if (node.mSiblings == null) { 17131309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu return; 17141309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu } 17151309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu for (int i = 0; i < node.mSiblings.size(); i++) { 17161309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu findSiblings(node.mSiblings.get(i), siblings); 17171309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu } 17181309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu } 17191309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu } 17201309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu 1721766431aa57c16ece8842287a92b2e7208e3b8ac3Doris Liu /** 1722766431aa57c16ece8842287a92b2e7208e3b8ac3Doris Liu * @hide 1723766431aa57c16ece8842287a92b2e7208e3b8ac3Doris Liu * TODO: For animatorSet defined in XML, we can use a flag to indicate what the play order 1724c470466d7c89b55d8c5a13d79076fa2f8d624da1Doris Liu * if defined (i.e. sequential or together), then we can use the flag instead of calculating 1725c470466d7c89b55d8c5a13d79076fa2f8d624da1Doris Liu * dynamically. Note that when AnimatorSet is empty this method returns true. 1726766431aa57c16ece8842287a92b2e7208e3b8ac3Doris Liu * @return whether all the animators in the set are supposed to play together 1727766431aa57c16ece8842287a92b2e7208e3b8ac3Doris Liu */ 1728766431aa57c16ece8842287a92b2e7208e3b8ac3Doris Liu public boolean shouldPlayTogether() { 1729766431aa57c16ece8842287a92b2e7208e3b8ac3Doris Liu updateAnimatorsDuration(); 1730766431aa57c16ece8842287a92b2e7208e3b8ac3Doris Liu createDependencyGraph(); 1731766431aa57c16ece8842287a92b2e7208e3b8ac3Doris Liu // All the child nodes are set out to play right after the delay animation 1732c470466d7c89b55d8c5a13d79076fa2f8d624da1Doris Liu return mRootNode.mChildNodes == null || mRootNode.mChildNodes.size() == mNodes.size() - 1; 1733766431aa57c16ece8842287a92b2e7208e3b8ac3Doris Liu } 1734766431aa57c16ece8842287a92b2e7208e3b8ac3Doris Liu 17351309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu @Override 17361309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu public long getTotalDuration() { 17371309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu updateAnimatorsDuration(); 17381309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu createDependencyGraph(); 17391309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu return mTotalDuration; 17401309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu } 174117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase 17421309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu private Node getNodeForAnimation(Animator anim) { 17431309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu Node node = mNodeMap.get(anim); 17441309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu if (node == null) { 17451309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu node = new Node(anim); 17461309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu mNodeMap.put(anim, node); 17471309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu mNodes.add(node); 174817fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase } 17491309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu return node; 175017fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase } 175117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase 175217fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase /** 1753a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * A Node is an embodiment of both the Animator that it wraps as well as 175417fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * any dependencies that are associated with that Animation. This includes 175517fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * both dependencies upon other nodes (in the dependencies list) as 175617fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * well as dependencies of other nodes upon this (in the nodeDependents list). 175717fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase */ 175849afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase private static class Node implements Cloneable { 17591309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu Animator mAnimation; 176017fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase 176117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase /** 17621309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu * Child nodes are the nodes associated with animations that will be played immediately 17631309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu * after current node. 176417fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase */ 17651309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu ArrayList<Node> mChildNodes = null; 176617fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase 176717fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase /** 17681309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu * Flag indicating whether the animation in this node is finished. This flag 17691309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu * is used by AnimatorSet to check, as each animation ends, whether all child animations 17701309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu * are mEnded and it's time to send out an end event for the entire AnimatorSet. 177117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase */ 17721309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu boolean mEnded = false; 177317fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase 177417fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase /** 17751309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu * Nodes with animations that are defined to play simultaneously with the animation 17761309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu * associated with this current node. 177717fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase */ 17781309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu ArrayList<Node> mSiblings; 177917fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase 178017fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase /** 17811309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu * Parent nodes are the nodes with animations preceding current node's animation. Parent 17821309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu * nodes here are derived from user defined animation sequence. 17831e0ac5a1063d00670f4dc7ca5b120ef2836f9e8fChet Haase */ 17841309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu ArrayList<Node> mParents; 17851e0ac5a1063d00670f4dc7ca5b120ef2836f9e8fChet Haase 17861e0ac5a1063d00670f4dc7ca5b120ef2836f9e8fChet Haase /** 17871309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu * Latest parent is the parent node associated with a animation that finishes after all 17881309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu * the other parents' animations. 1789d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar */ 17901309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu Node mLatestParent = null; 17911309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu 17921309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu boolean mParentsAdded = false; 17931309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu long mStartTime = 0; 17941309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu long mEndTime = 0; 179549db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu long mTotalDuration = 0; 1796d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar 1797d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar /** 179817fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * Constructs the Node with the animation that it encapsulates. A Node has no 179917fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * dependencies by default; dependencies are added via the addDependency() 180017fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * method. 180117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * 180217fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * @param animation The animation that the Node encapsulates. 180317fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase */ 1804a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase public Node(Animator animation) { 18051309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu this.mAnimation = animation; 180617fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase } 180749afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase 180849afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase @Override 180921cd1389d2ef218b20994b617c57af120841a57fChet Haase public Node clone() { 181021cd1389d2ef218b20994b617c57af120841a57fChet Haase try { 181121cd1389d2ef218b20994b617c57af120841a57fChet Haase Node node = (Node) super.clone(); 18121309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu node.mAnimation = mAnimation.clone(); 18131309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu if (mChildNodes != null) { 18141309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu node.mChildNodes = new ArrayList<>(mChildNodes); 18151309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu } 18165e66616c04e4a54df7e8783bc7ffe6c22bcb3040Doris Liu if (mSiblings != null) { 18175e66616c04e4a54df7e8783bc7ffe6c22bcb3040Doris Liu node.mSiblings = new ArrayList<>(mSiblings); 18185e66616c04e4a54df7e8783bc7ffe6c22bcb3040Doris Liu } 18195e66616c04e4a54df7e8783bc7ffe6c22bcb3040Doris Liu if (mParents != null) { 18205e66616c04e4a54df7e8783bc7ffe6c22bcb3040Doris Liu node.mParents = new ArrayList<>(mParents); 18215e66616c04e4a54df7e8783bc7ffe6c22bcb3040Doris Liu } 18221309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu node.mEnded = false; 182321cd1389d2ef218b20994b617c57af120841a57fChet Haase return node; 182421cd1389d2ef218b20994b617c57af120841a57fChet Haase } catch (CloneNotSupportedException e) { 182521cd1389d2ef218b20994b617c57af120841a57fChet Haase throw new AssertionError(); 182621cd1389d2ef218b20994b617c57af120841a57fChet Haase } 182749afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase } 18281309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu 18291309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu void addChild(Node node) { 18301309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu if (mChildNodes == null) { 18311309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu mChildNodes = new ArrayList<>(); 18321309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu } 18331309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu if (!mChildNodes.contains(node)) { 18341309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu mChildNodes.add(node); 18351309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu node.addParent(this); 18361309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu } 18371309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu } 18381309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu 18391309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu public void addSibling(Node node) { 18401309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu if (mSiblings == null) { 18411309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu mSiblings = new ArrayList<Node>(); 18421309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu } 18431309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu if (!mSiblings.contains(node)) { 18441309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu mSiblings.add(node); 18451309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu node.addSibling(this); 18461309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu } 18471309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu } 18481309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu 18491309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu public void addParent(Node node) { 18501309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu if (mParents == null) { 18511309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu mParents = new ArrayList<Node>(); 18521309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu } 18531309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu if (!mParents.contains(node)) { 18541309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu mParents.add(node); 18551309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu node.addChild(this); 18561309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu } 18571309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu } 18581309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu 18591309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu public void addParents(ArrayList<Node> parents) { 18601309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu if (parents == null) { 18611309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu return; 18621309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu } 18631309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu int size = parents.size(); 18641309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu for (int i = 0; i < size; i++) { 18651309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu addParent(parents.get(i)); 18661309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu } 18671309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu } 186817fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase } 186917fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase 187017fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase /** 187113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu * This class is a wrapper around a node and an event for the animation corresponding to the 187213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu * node. The 3 types of events represent the start of an animation, the end of a start delay of 187313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu * an animation, and the end of an animation. When playing forward (i.e. in the non-reverse 187413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu * direction), start event marks when start() should be called, and end event corresponds to 187513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu * when the animation should finish. When playing in reverse, start delay will not be a part 187613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu * of the animation. Therefore, reverse() is called at the end event, and animation should end 187713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu * at the delay ended event. 187813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu */ 187913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu private static class AnimationEvent { 188013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu static final int ANIMATION_START = 0; 188113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu static final int ANIMATION_DELAY_ENDED = 1; 188213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu static final int ANIMATION_END = 2; 188313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu final Node mNode; 188413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu final int mEvent; 188513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 188613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu AnimationEvent(Node node, int event) { 188713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu mNode = node; 188813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu mEvent = event; 188913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 189013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 189113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu long getTime() { 189213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (mEvent == ANIMATION_START) { 189313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu return mNode.mStartTime; 189413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } else if (mEvent == ANIMATION_DELAY_ENDED) { 1895e8a5d8d211983522661cd2a059b66e71b09a5e07Doris Liu return mNode.mStartTime == DURATION_INFINITE 1896c9b5034178809a0c51f368735fded79bd73527bdDoris Liu ? DURATION_INFINITE : mNode.mStartTime + mNode.mAnimation.getStartDelay(); 189713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } else { 189813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu return mNode.mEndTime; 189913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 190013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 190113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 190213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu public String toString() { 190313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu String eventStr = mEvent == ANIMATION_START ? "start" : ( 190413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu mEvent == ANIMATION_DELAY_ENDED ? "delay ended" : "end"); 190513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu return eventStr + " " + mNode.mAnimation.toString(); 190613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 190713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 190813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 190913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu private class SeekState { 191013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu private long mPlayTime = -1; 191113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu private boolean mSeekingInReverse = false; 191213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu void reset() { 191313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu mPlayTime = -1; 191413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu mSeekingInReverse = false; 191513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 191613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 191713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu void setPlayTime(long playTime, boolean inReverse) { 191813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // TODO: This can be simplified. 191913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 192013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // Clamp the play time 192113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (getTotalDuration() != DURATION_INFINITE) { 192213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu mPlayTime = Math.min(playTime, getTotalDuration() - mStartDelay); 192313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 192413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu mPlayTime = Math.max(0, mPlayTime); 192513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu mSeekingInReverse = inReverse; 192613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 192713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 192813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu void updateSeekDirection(boolean inReverse) { 192913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu // Change seek direction without changing the overall fraction 193013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (inReverse && getTotalDuration() == DURATION_INFINITE) { 193113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu throw new UnsupportedOperationException("Error: Cannot reverse infinite animator" 193213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu + " set"); 193313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 193413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (mPlayTime >= 0) { 193513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (inReverse != mSeekingInReverse) { 193613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu mPlayTime = getTotalDuration() - mStartDelay - mPlayTime; 19376d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu mSeekingInReverse = inReverse; 193813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 193913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 194013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 194113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 194213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu long getPlayTime() { 194313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu return mPlayTime; 194413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 194513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 194613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu /** 194713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu * Returns the playtime assuming the animation is forward playing 194813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu */ 194913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu long getPlayTimeNormalized() { 195013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu if (mReversing) { 195113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu return getTotalDuration() - mStartDelay - mPlayTime; 195213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 195313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu return mPlayTime; 195413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 195513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 195613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu boolean isActive() { 195713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu return mPlayTime != -1; 195813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 195913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu } 196013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu 196113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu /** 196217fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * The <code>Builder</code> object is a utility class to facilitate adding animations to a 1963a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * <code>AnimatorSet</code> along with the relationships between the various animations. The 196417fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * intention of the <code>Builder</code> methods, along with the {@link 19658b699792b677bd4dd8442b32641ac09d48fdd79cChet Haase * AnimatorSet#play(Animator) play()} method of <code>AnimatorSet</code> is to make it possible 19668b699792b677bd4dd8442b32641ac09d48fdd79cChet Haase * to express the dependency relationships of animations in a natural way. Developers can also 19678b699792b677bd4dd8442b32641ac09d48fdd79cChet Haase * use the {@link AnimatorSet#playTogether(Animator[]) playTogether()} and {@link 1968a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * AnimatorSet#playSequentially(Animator[]) playSequentially()} methods if these suit the need, 1969a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * but it might be easier in some situations to express the AnimatorSet of animations in pairs. 197017fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * <p/> 197117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * <p>The <code>Builder</code> object cannot be constructed directly, but is rather constructed 1972a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * internally via a call to {@link AnimatorSet#play(Animator)}.</p> 197317fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * <p/> 1974a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * <p>For example, this sets up a AnimatorSet to play anim1 and anim2 at the same time, anim3 to 197517fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * play when anim2 finishes, and anim4 to play when anim3 finishes:</p> 197617fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * <pre> 1977a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * AnimatorSet s = new AnimatorSet(); 197817fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * s.play(anim1).with(anim2); 197917fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * s.play(anim2).before(anim3); 198017fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * s.play(anim4).after(anim3); 198117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * </pre> 198217fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * <p/> 1983a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * <p>Note in the example that both {@link Builder#before(Animator)} and {@link 1984a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * Builder#after(Animator)} are used. These are just different ways of expressing the same 198517fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * relationship and are provided to make it easier to say things in a way that is more natural, 198617fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * depending on the situation.</p> 198717fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * <p/> 198817fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * <p>It is possible to make several calls into the same <code>Builder</code> object to express 198917fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * multiple relationships. However, note that it is only the animation passed into the initial 1990a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * {@link AnimatorSet#play(Animator)} method that is the dependency in any of the successive 199117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * calls to the <code>Builder</code> object. For example, the following code starts both anim2 199217fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * and anim3 when anim1 ends; there is no direct dependency relationship between anim2 and 199317fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * anim3: 199417fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * <pre> 1995a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * AnimatorSet s = new AnimatorSet(); 199617fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * s.play(anim1).before(anim2).before(anim3); 199717fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * </pre> 199817fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * If the desired result is to play anim1 then anim2 then anim3, this code expresses the 199917fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * relationship correctly:</p> 200017fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * <pre> 2001a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * AnimatorSet s = new AnimatorSet(); 200217fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * s.play(anim1).before(anim2); 200317fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * s.play(anim2).before(anim3); 200417fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * </pre> 200517fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * <p/> 200617fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * <p>Note that it is possible to express relationships that cannot be resolved and will not 200717fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * result in sensible results. For example, <code>play(anim1).after(anim1)</code> makes no 200817fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * sense. In general, circular dependencies like this one (or more indirect ones where a depends 2009a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * on b, which depends on c, which depends on a) should be avoided. Only create AnimatorSets 2010a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * that can boil down to a simple, one-way relationship of animations starting with, before, and 201117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * after other, different, animations.</p> 201217fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase */ 201317fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase public class Builder { 201417fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase 201517fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase /** 201617fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * This tracks the current node being processed. It is supplied to the play() method 2017a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * of AnimatorSet and passed into the constructor of Builder. 201817fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase */ 201917fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase private Node mCurrentNode; 202017fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase 202117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase /** 2022a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * package-private constructor. Builders are only constructed by AnimatorSet, when the 202317fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * play() method is called. 202417fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * 202517fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * @param anim The animation that is the dependency for the other animations passed into 202617fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * the other methods of this Builder object. 202717fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase */ 2028a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase Builder(Animator anim) { 20291309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu mDependencyDirty = true; 20301309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu mCurrentNode = getNodeForAnimation(anim); 203117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase } 203217fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase 203317fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase /** 203417fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * Sets up the given animation to play at the same time as the animation supplied in the 2035a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object. 203617fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * 203717fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * @param anim The animation that will play when the animation supplied to the 2038a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * {@link AnimatorSet#play(Animator)} method starts. 203917fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase */ 20402970c499388b4dcd1232cd622a9b80b395eeb2b4Chet Haase public Builder with(Animator anim) { 20411309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu Node node = getNodeForAnimation(anim); 20421309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu mCurrentNode.addSibling(node); 20432970c499388b4dcd1232cd622a9b80b395eeb2b4Chet Haase return this; 204417fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase } 204517fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase 204617fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase /** 204717fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * Sets up the given animation to play when the animation supplied in the 2048a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object 204917fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * ends. 205017fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * 205117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * @param anim The animation that will play when the animation supplied to the 2052a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * {@link AnimatorSet#play(Animator)} method ends. 205317fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase */ 20542970c499388b4dcd1232cd622a9b80b395eeb2b4Chet Haase public Builder before(Animator anim) { 20551309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu Node node = getNodeForAnimation(anim); 20561309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu mCurrentNode.addChild(node); 20572970c499388b4dcd1232cd622a9b80b395eeb2b4Chet Haase return this; 205817fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase } 205917fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase 206017fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase /** 206117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * Sets up the given animation to play when the animation supplied in the 2062a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object 206317fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * to start when the animation supplied in this method call ends. 206417fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * 206517fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * @param anim The animation whose end will cause the animation supplied to the 2066a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * {@link AnimatorSet#play(Animator)} method to play. 206717fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase */ 20682970c499388b4dcd1232cd622a9b80b395eeb2b4Chet Haase public Builder after(Animator anim) { 20691309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu Node node = getNodeForAnimation(anim); 20701309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu mCurrentNode.addParent(node); 20712970c499388b4dcd1232cd622a9b80b395eeb2b4Chet Haase return this; 207217fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase } 207317fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase 207417fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase /** 207517fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * Sets up the animation supplied in the 2076a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object 207717fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * to play when the given amount of time elapses. 207817fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * 207917fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * @param delay The number of milliseconds that should elapse before the 208017fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * animation starts. 208117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase */ 20822970c499388b4dcd1232cd622a9b80b395eeb2b4Chet Haase public Builder after(long delay) { 2083a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase // setup dummy ValueAnimator just to run the clock 20842794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f); 20852794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase anim.setDuration(delay); 20862794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase after(anim); 20872970c499388b4dcd1232cd622a9b80b395eeb2b4Chet Haase return this; 208817fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase } 208917fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase 209017fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase } 209117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase 209217fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase} 2093