Merge "Fix for end events from Sequencer objects"
diff --git a/api/current.xml b/api/current.xml
index 619b257..ebb8b3e 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -19870,6 +19870,17 @@
  visibility="public"
 >
 </method>
+<method name="isRunning"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="removeAllListeners"
  return="void"
  abstract="false"
@@ -20968,6 +20979,17 @@
  visibility="public"
 >
 </method>
+<method name="isRunning"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="play"
  return="android.animation.Sequencer.Builder"
  abstract="false"
diff --git a/core/java/android/animation/Animatable.java b/core/java/android/animation/Animatable.java
index a49ca8f..d6cf7c0 100644
--- a/core/java/android/animation/Animatable.java
+++ b/core/java/android/animation/Animatable.java
@@ -56,6 +56,13 @@
     public void end() {
     }
 
+
+    /**
+     * Returns whether this Animatable is currently running (having been started and not yet ended).
+     * @return Whether the Animatable is running.
+     */
+    public abstract boolean isRunning();
+
     /**
      * Adds a listener to the set of listeners that are sent events through the life of an
      * animation, such as start, repeat, and end.
diff --git a/core/java/android/animation/Animator.java b/core/java/android/animation/Animator.java
index 18017ba..71b6d99 100755
--- a/core/java/android/animation/Animator.java
+++ b/core/java/android/animation/Animator.java
@@ -699,15 +699,21 @@
 
     @Override
     public void end() {
+        if (!sAnimations.contains(this) &&
+                (Thread.currentThread() == Looper.getMainLooper().getThread())) {
+            // Special case if the animation has not yet started. Set the end value.
+            long endTime = mDuration;
+            if (mRepeatCount > 0) {
+                endTime += mRepeatCount * mDuration;
+            }
+            setCurrentPlayTime(endTime);
+        }
         // Just set the ENDED flag - this causes the animation to end the next time a frame
         // is processed.
         mPlayingState = ENDED;
     }
 
-    /**
-     * Returns whether this Animator is currently running (having been started and not yet ended).
-     * @return Wehther the Animator is running.
-     */
+    @Override
     public boolean isRunning() {
         return mPlayingState == RUNNING;
     }
@@ -737,6 +743,7 @@
      */
     private void endAnimation() {
         sAnimations.remove(this);
+        mPlayingState = STOPPED;
         if (mListeners != null) {
             ArrayList<AnimatableListener> tmpListeners =
                     (ArrayList<AnimatableListener>) mListeners.clone();
@@ -744,7 +751,6 @@
                 listener.onAnimationEnd(this);
             }
         }
-        mPlayingState = STOPPED;
     }
 
     /**
diff --git a/core/java/android/animation/Sequencer.java b/core/java/android/animation/Sequencer.java
index 7749429..a9e4e3b 100644
--- a/core/java/android/animation/Sequencer.java
+++ b/core/java/android/animation/Sequencer.java
@@ -241,6 +241,21 @@
     }
 
     /**
+     * Returns true if any of the child animations of this Sequencer have been started and have not
+     * yet ended.
+     * @return Whether this Sequencer has been started and has not yet ended.
+     */
+    @Override
+    public boolean isRunning() {
+        for (Node node : mNodes) {
+            if (node.animation.isRunning()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
      * {@inheritDoc}
      *
      * <p>Starting this <code>Sequencer</code> will, in turn, start the animations for which
@@ -467,14 +482,10 @@
         public void onAnimationEnd(Animatable animation) {
             animation.removeListener(this);
             mPlayingSet.remove(animation);
-            Node animNode = mSequencer.mNodeMap.get(animation);
-            animNode.done = true;
             ArrayList<Node> sortedNodes = mSequencer.mSortedNodes;
-            int numNodes = sortedNodes.size();
-            int nodeIndex = sortedNodes.indexOf(animNode);
             boolean allDone = true;
-            for (int i = nodeIndex + 1; i < numNodes; ++i) {
-                if (!sortedNodes.get(i).done) {
+            for (Node node : sortedNodes) {
+                if (node.animation.isRunning()) {
                     allDone = false;
                     break;
                 }
@@ -558,7 +569,6 @@
                         }
                     }
                 }
-                node.done = false; // also reset done flag
             }
         }
     }
@@ -626,13 +636,6 @@
         public ArrayList<Node> nodeDependents = null;
 
         /**
-         * Flag indicating whether the animation in this node is finished. This flag
-         * is used by Sequencer to check, as each animation ends, whether all child animations
-         * are done and it's time to send out an end event for the entire Sequencer.
-         */
-        public boolean done = false;
-
-        /**
          * Constructs the Node with the animation that it encapsulates. A Node has no
          * dependencies by default; dependencies are added via the addDependency()
          * method.
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index 0e3522e..2f7482c 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -75,7 +75,7 @@
             }
         }
         tl = new TextLine();
-        Log.e("TLINE", "new: " + tl);
+        Log.v("TLINE", "new: " + tl);
         return tl;
     }