Add cache for each animation's duration in AnimatorSet

Each animator's total druation is cached in the set, so that when
their duration changes, the dependency graph will be updated to
reflect the change.

Change-Id: I677bc289f2ba430466f2d90ebc14368cb7b75118
diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java
index f7a7403..ff7705b 100644
--- a/core/java/android/animation/AnimatorSet.java
+++ b/core/java/android/animation/AnimatorSet.java
@@ -450,6 +450,9 @@
             mReversible = false;
         }
         long delta = startDelay - mStartDelay;
+        if (delta == 0) {
+            return;
+        }
         mStartDelay = startDelay;
         if (!mDependencyDirty) {
             // Dependency graph already constructed, update all the nodes' start/end time
@@ -463,9 +466,12 @@
                             DURATION_INFINITE : node.mStartTime + delta;
                     node.mEndTime = node.mEndTime == DURATION_INFINITE ?
                             DURATION_INFINITE : node.mEndTime + delta;
-
                 }
             }
+            // Update total duration, if necessary.
+            if (mTotalDuration != DURATION_INFINITE) {
+                mTotalDuration += delta;
+            }
         }
     }
 
@@ -857,13 +863,20 @@
 
     private void createDependencyGraph() {
         if (!mDependencyDirty) {
-            return;
+            // Check whether any duration of the child animations has changed
+            boolean durationChanged = false;
+            for (int i = 0; i < mNodes.size(); i++) {
+                Animator anim = mNodes.get(i).mAnimation;
+                if (mNodes.get(i).mTotalDuration != anim.getTotalDuration()) {
+                    durationChanged = true;
+                    break;
+                }
+            }
+            if (!durationChanged) {
+                return;
+            }
         }
 
-        // TODO: In addition to checking the dirty flag, we should also cache the duration for
-        // each animator, so that when the animator's duration is changed, we can detect that and
-        // update the dependency graph.
-
         mDependencyDirty = false;
         // Traverse all the siblings and make sure they have all the parents
         int size = mNodes.size();
@@ -916,6 +929,7 @@
         long maxEndTime = 0;
         for (int i = 0; i < size; i++) {
             Node node = mNodes.get(i);
+            node.mTotalDuration = node.mAnimation.getTotalDuration();
             if (node.mEndTime == DURATION_INFINITE) {
                 maxEndTime = DURATION_INFINITE;
                 break;
@@ -933,6 +947,16 @@
      */
     private void updatePlayTime(Node parent,  ArrayList<Node> visited) {
         if (parent.mChildNodes == null) {
+            if (parent == mRootNode) {
+                // All the animators are in a cycle
+                for (int i = 0; i < mNodes.size(); i++) {
+                    Node node = mNodes.get(i);
+                    if (node != mRootNode) {
+                        node.mStartTime = DURATION_INFINITE;
+                        node.mEndTime = DURATION_INFINITE;
+                    }
+                }
+            }
             return;
         }
 
@@ -943,7 +967,7 @@
             int index = visited.indexOf(child);
             if (index >= 0) {
                 // Child has been visited, cycle found. Mark all the nodes in the cycle.
-                for (int j = index; j < visited.size(); i++) {
+                for (int j = index; j < visited.size(); j++) {
                     visited.get(j).mLatestParent = null;
                     visited.get(j).mStartTime = DURATION_INFINITE;
                     visited.get(j).mEndTime = DURATION_INFINITE;
@@ -1057,6 +1081,7 @@
         boolean mParentsAdded = false;
         long mStartTime = 0;
         long mEndTime = 0;
+        long mTotalDuration = 0;
 
         /**
          * Constructs the Node with the animation that it encapsulates. A Node has no