Merge "Add new API to Animator to allow seeking of animations"
diff --git a/api/current.xml b/api/current.xml
index 5cc0ea8..b24ba43 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -19914,6 +19914,17 @@
  visibility="public"
 >
 </method>
+<method name="getCurrentPlayTime"
+ return="long"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getFrameDelay"
  return="long"
  abstract="false"
@@ -19993,6 +20004,19 @@
 <parameter name="listener" type="android.animation.Animator.AnimatorUpdateListener">
 </parameter>
 </method>
+<method name="setCurrentPlayTime"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="playTime" type="long">
+</parameter>
+</method>
 <method name="setEvaluator"
  return="void"
  abstract="false"
diff --git a/core/java/android/animation/Animator.java b/core/java/android/animation/Animator.java
index d8c6fff..1013e01 100755
--- a/core/java/android/animation/Animator.java
+++ b/core/java/android/animation/Animator.java
@@ -65,6 +65,7 @@
     private static final int RUNNING    = 1; // Playing normally
     private static final int CANCELED   = 2; // cancel() called - need to end it
     private static final int ENDED      = 3; // end() called - need to end it
+    private static final int SEEKED     = 4; // Seeked to some time value
 
     /**
      * Enum values used in XML attributes to indicate the value for mValueType
@@ -85,6 +86,12 @@
     // to animateFrame()
     private long mStartTime;
 
+    /**
+     * Set when setCurrentPlayTime() is called. If negative, animation is not currently seeked
+     * to a value.
+     */
+    private long mSeekTime = -1;
+
     // The static sAnimationHandler processes the internal timing loop on which all animations
     // are based
     private static AnimationHandler sAnimationHandler;
@@ -144,6 +151,12 @@
     private static final ArrayList<Animator> sDelayedAnims = new ArrayList<Animator>();
     private static final ArrayList<Animator> sReadyAnims = new ArrayList<Animator>();
 
+    /**
+     * Flag that denotes whether the animation is set up and ready to go. Used by seek() to
+     * set up animation that has not yet been started.
+     */
+    private boolean mInitialized = false;
+
     //
     // Backing variables
     //
@@ -411,6 +424,44 @@
         }
         mPlayingBackwards = false;
         mCurrentIteration = 0;
+        mInitialized = true;
+    }
+
+    /**
+     * Sets the position of the animation to the specified point in time. This time should
+     * be between 0 and the total duration of the animation, including any repetition. If
+     * the animation has not yet been started, then it will not advance forward after it is
+     * set to this time; it will simply set the time to this value and perform any appropriate
+     * actions based on that time. If the animation is already running, then seek() will
+     * set the current playing time to this value and continue playing from that point.
+     *
+     * @param playTime The time, in milliseconds, to which the animation is advanced or rewound.
+     */
+    public void setCurrentPlayTime(long playTime) {
+        if (!mInitialized) {
+            initAnimation();
+        }
+        long currentTime = AnimationUtils.currentAnimationTimeMillis();
+        if (mPlayingState != RUNNING) {
+            mSeekTime = playTime;
+            mPlayingState = SEEKED;
+        }
+        mStartTime = currentTime - playTime;
+        animationFrame(currentTime);
+    }
+
+    /**
+     * Gets the current position of the animation in time, which is equal to the current
+     * time minus the time that the animation started. An animation that is not yet started will
+     * return a value of zero.
+     *
+     * @return The current position in time of the animation.
+     */
+    public long getCurrentPlayTime() {
+        if (!mInitialized) {
+            return 0;
+        }
+        return AnimationUtils.currentAnimationTimeMillis() - mStartTime;
     }
 
     /**
@@ -740,6 +791,7 @@
     }
 
     public void start() {
+        mPlayingState = STOPPED;
         sPendingAnimations.add(this);
         if (sAnimationHandler == null) {
             sAnimationHandler = new AnimationHandler();
@@ -845,10 +897,17 @@
 
         if (mPlayingState == STOPPED) {
             mPlayingState = RUNNING;
-            mStartTime = currentTime;
+            if (mSeekTime < 0) {
+                mStartTime = currentTime;
+            } else {
+                mStartTime = currentTime - mSeekTime;
+                // Now that we're playing, reset the seek time
+                mSeekTime = -1;
+            }
         }
         switch (mPlayingState) {
         case RUNNING:
+        case SEEKED:
             float fraction = (float)(currentTime - mStartTime) / mDuration;
             if (fraction >= 1f) {
                 if (mCurrentIteration < mRepeatCount || mRepeatCount == INFINITE) {