OSDN Git Service

Fixes to startDelay
authorJohn Reck <jreck@google.com>
Tue, 1 Jul 2014 22:23:45 +0000 (15:23 -0700)
committerJohn Reck <jreck@google.com>
Tue, 1 Jul 2014 22:32:12 +0000 (15:32 -0700)
 Bug: 15991758

 Don't update the UI thread with final value until after
 startDelay

Change-Id: Ie8bffb5a3ace353ec1d82943a4efcbd01c42c28f

core/java/android/view/Choreographer.java
core/java/android/view/RenderNodeAnimator.java
core/java/android/view/RenderNodeAnimatorCompat.java [new file with mode: 0644]
core/java/android/view/ViewPropertyAnimatorRT.java
core/jni/android_view_RenderNode.cpp
core/jni/android_view_RenderNodeAnimator.cpp
libs/hwui/Animator.cpp
libs/hwui/Animator.h
libs/hwui/AnimatorManager.cpp

index 1066430..f41afcf 100644 (file)
@@ -431,7 +431,7 @@ public final class Choreographer {
     /**
      * Gets the time when the current frame started.
      * <p>
-     * This method provides the time in nanoseconds when the frame started being rendered.
+     * This method provides the time in milliseconds when the frame started being rendered.
      * The frame time provides a stable time base for synchronizing animations
      * and drawing.  It should be used instead of {@link SystemClock#uptimeMillis()}
      * or {@link System#nanoTime()} for animations and drawing in the UI.  Using the frame
index 1363a5c..4b53c8e 100644 (file)
@@ -18,6 +18,7 @@ package android.view;
 
 import android.animation.Animator;
 import android.animation.TimeInterpolator;
+import android.animation.ValueAnimator;
 import android.graphics.Canvas;
 import android.graphics.CanvasProperty;
 import android.graphics.Paint;
@@ -34,7 +35,7 @@ import java.util.ArrayList;
 /**
  * @hide
  */
-public final class RenderNodeAnimator extends Animator {
+public class RenderNodeAnimator extends Animator {
     // Keep in sync with enum RenderProperty in Animator.h
     public static final int TRANSLATION_X = 0;
     public static final int TRANSLATION_Y = 1;
@@ -83,16 +84,23 @@ public final class RenderNodeAnimator extends Animator {
 
     private RenderNode mTarget;
     private View mViewTarget;
+    private int mRenderProperty = -1;
+    private float mFinalValue;
     private TimeInterpolator mInterpolator;
 
     private boolean mStarted = false;
     private boolean mFinished = false;
 
+    private long mUnscaledDuration = 300;
+    private long mUnscaledStartDelay = 0;
+
     public static int mapViewPropertyToRenderProperty(int viewProperty) {
         return sViewPropertyAnimatorMap.get(viewProperty);
     }
 
     public RenderNodeAnimator(int property, float finalValue) {
+        mRenderProperty = property;
+        mFinalValue = finalValue;
         init(nCreateAnimator(new WeakReference<RenderNodeAnimator>(this),
                 property, finalValue));
     }
@@ -156,7 +164,16 @@ public final class RenderNodeAnimator extends Animator {
 
         mStarted = true;
         applyInterpolator();
-        mTarget.addAnimator(this);
+        nStart(mNativePtr.get());
+
+        // Alpha is a special snowflake that has the canonical value stored
+        // in mTransformationInfo instead of in RenderNode, so we need to update
+        // it with the final value here.
+        if (mRenderProperty == RenderNodeAnimator.ALPHA) {
+            // Don't need null check because ViewPropertyAnimator's
+            // ctor calls ensureTransformationInfo()
+            mViewTarget.mTransformationInfo.mAlpha = mFinalValue;
+        }
 
         final ArrayList<AnimatorListener> listeners = getListeners();
         final int numListeners = listeners == null ? 0 : listeners.size();
@@ -201,6 +218,7 @@ public final class RenderNodeAnimator extends Animator {
     public void setTarget(View view) {
         mViewTarget = view;
         mTarget = view.mRenderNode;
+        mTarget.addAnimator(this);
     }
 
     public void setTarget(Canvas canvas) {
@@ -213,12 +231,12 @@ public final class RenderNodeAnimator extends Animator {
     }
 
     public void setTarget(RenderNode node) {
+        if (mTarget != null) {
+            throw new IllegalStateException("Target already set!");
+        }
         mViewTarget = null;
         mTarget = node;
-    }
-
-    public RenderNode getTarget() {
-        return mTarget;
+        mTarget.addAnimator(this);
     }
 
     public void setStartValue(float startValue) {
@@ -232,12 +250,13 @@ public final class RenderNodeAnimator extends Animator {
         if (startDelay < 0) {
             throw new IllegalArgumentException("startDelay must be positive; " + startDelay);
         }
-        nSetStartDelay(mNativePtr.get(), startDelay);
+        mUnscaledStartDelay = startDelay;
+        nSetStartDelay(mNativePtr.get(), (long) (startDelay * ValueAnimator.getDurationScale()));
     }
 
     @Override
     public long getStartDelay() {
-        return nGetStartDelay(mNativePtr.get());
+        return mUnscaledStartDelay;
     }
 
     @Override
@@ -246,13 +265,14 @@ public final class RenderNodeAnimator extends Animator {
         if (duration < 0) {
             throw new IllegalArgumentException("duration must be positive; " + duration);
         }
-        nSetDuration(mNativePtr.get(), duration);
+        mUnscaledDuration = duration;
+        nSetDuration(mNativePtr.get(), (long) (duration * ValueAnimator.getDurationScale()));
         return this;
     }
 
     @Override
     public long getDuration() {
-        return nGetDuration(mNativePtr.get());
+        return mUnscaledDuration;
     }
 
     @Override
@@ -307,5 +327,6 @@ public final class RenderNodeAnimator extends Animator {
     private static native long nGetStartDelay(long nativePtr);
     private static native void nSetInterpolator(long animPtr, long interpolatorPtr);
 
+    private static native void nStart(long animPtr);
     private static native void nCancel(long animPtr);
 }
diff --git a/core/java/android/view/RenderNodeAnimatorCompat.java b/core/java/android/view/RenderNodeAnimatorCompat.java
new file mode 100644 (file)
index 0000000..151277a
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.animation.ValueAnimator;
+
+import java.util.ArrayList;
+
+/**
+ * This class provides compatibility for things like start listeners &
+ * start delays for use by ViewPropertyAnimator and ObjectAnimator
+ * @hide
+ */
+public class RenderNodeAnimatorCompat extends RenderNodeAnimator {
+
+    private long mUnscaledStartDelay = 0;
+    private long mStartDelay = 0;
+    private long mStartTime;
+    private boolean mCanceled;
+
+    public RenderNodeAnimatorCompat(int property, float finalValue) {
+        super(property, finalValue);
+    }
+
+    @Override
+    public void setStartDelay(long startDelay) {
+        mUnscaledStartDelay = startDelay;
+        mStartDelay = (long) (ValueAnimator.getDurationScale() * startDelay);
+    }
+
+    @Override
+    public long getStartDelay() {
+        return mUnscaledStartDelay;
+    }
+
+    @Override
+    public void start() {
+        if (mStartDelay <= 0) {
+            doStart();
+        } else {
+            getHelper().addDelayedAnimation(this);
+        }
+    }
+
+    private void doStart() {
+        if (!mCanceled) {
+            super.start();
+        }
+    }
+
+    @Override
+    public void cancel() {
+        mCanceled = true;
+        super.cancel();
+    }
+
+    /**
+     * @return true if the animator was started, false if still delayed
+     */
+    private boolean processDelayed(long frameTimeMs) {
+        if (mCanceled) return true;
+
+        if (mStartTime == 0) {
+            mStartTime = frameTimeMs;
+        } else if ((frameTimeMs - mStartTime) >= mStartDelay) {
+            doStart();
+            return true;
+        }
+        return false;
+    }
+
+    private static AnimationHelper getHelper() {
+        AnimationHelper helper = sAnimationHelper.get();
+        if (helper == null) {
+            helper = new AnimationHelper();
+            sAnimationHelper.set(helper);
+        }
+        return helper;
+    }
+
+    private static ThreadLocal<AnimationHelper> sAnimationHelper =
+            new ThreadLocal<AnimationHelper>();
+
+    private static class AnimationHelper implements Runnable {
+
+        private ArrayList<RenderNodeAnimatorCompat> mDelayedAnims = new ArrayList<RenderNodeAnimatorCompat>();
+        private final Choreographer mChoreographer;
+        private boolean mCallbackScheduled;
+
+        public AnimationHelper() {
+            mChoreographer = Choreographer.getInstance();
+        }
+
+        public void addDelayedAnimation(RenderNodeAnimatorCompat animator) {
+            mDelayedAnims.add(animator);
+            scheduleCallback();
+        }
+
+        private void scheduleCallback() {
+            if (!mCallbackScheduled) {
+                mCallbackScheduled = true;
+                mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, this, null);
+            }
+        }
+
+        @Override
+        public void run() {
+            long frameTimeMs = mChoreographer.getFrameTime();
+            mCallbackScheduled = false;
+
+            int end = 0;
+            for (int i = 0; i < mDelayedAnims.size(); i++) {
+                RenderNodeAnimatorCompat animator = mDelayedAnims.get(i);
+                if (!animator.processDelayed(frameTimeMs)) {
+                    if (end != i) {
+                        mDelayedAnims.set(end, animator);
+                    }
+                    end++;
+                }
+            }
+            while (mDelayedAnims.size() > end) {
+                mDelayedAnims.remove(mDelayedAnims.size() - 1);
+            }
+
+            if (mDelayedAnims.size() > 0) {
+                scheduleCallback();
+            }
+        }
+    }
+}
index 8b4277a..05df7a0 100644 (file)
@@ -73,21 +73,14 @@ class ViewPropertyAnimatorRT {
             int property = RenderNodeAnimator.mapViewPropertyToRenderProperty(holder.mNameConstant);
 
             final float finalValue = holder.mFromValue + holder.mDeltaValue;
-            RenderNodeAnimator animator = new RenderNodeAnimator(property, finalValue);
+            RenderNodeAnimator animator = new RenderNodeAnimatorCompat(property, finalValue);
             animator.setStartDelay(startDelay);
             animator.setDuration(duration);
             animator.setInterpolator(interpolator);
             animator.setTarget(mView);
             animator.start();
 
-            // Alpha is a special snowflake that has the canonical value stored
-            // in mTransformationInfo instead of in RenderNode, so we need to update
-            // it with the final value here.
-            if (property == RenderNodeAnimator.ALPHA) {
-                // Don't need null check because ViewPropertyAnimator's
-                // ctor calls ensureTransformationInfo()
-                parent.mView.mTransformationInfo.mAlpha = finalValue;
-            }
+            mAnimators[property] = animator;
         }
 
         parent.mPendingAnimations.clear();
index 6ba22bf..28473e0 100644 (file)
@@ -456,7 +456,6 @@ static void android_view_RenderNode_addAnimator(JNIEnv* env, jobject clazz,
     RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
     RenderPropertyAnimator* animator = reinterpret_cast<RenderPropertyAnimator*>(animatorPtr);
     renderNode->addAnimator(animator);
-    animator->start();
 }
 
 #endif // USE_OPENGL_RENDERER
index de3dd16..ed57979 100644 (file)
@@ -149,6 +149,11 @@ static void setInterpolator(JNIEnv* env, jobject clazz, jlong animatorPtr, jlong
     animator->setInterpolator(interpolator);
 }
 
+static void start(JNIEnv* env, jobject clazz, jlong animatorPtr) {
+    BaseRenderNodeAnimator* animator = reinterpret_cast<BaseRenderNodeAnimator*>(animatorPtr);
+    animator->start();
+}
+
 static void cancel(JNIEnv* env, jobject clazz, jlong animatorPtr) {
     BaseRenderNodeAnimator* animator = reinterpret_cast<BaseRenderNodeAnimator*>(animatorPtr);
     animator->cancel();
@@ -173,6 +178,7 @@ static JNINativeMethod gMethods[] = {
     { "nSetStartDelay", "(JJ)V", (void*) setStartDelay },
     { "nGetStartDelay", "(J)J", (void*) getStartDelay },
     { "nSetInterpolator", "(JJ)V", (void*) setInterpolator },
+    { "nStart", "(J)V", (void*) start },
     { "nCancel", "(J)V", (void*) cancel },
 #endif
 };
index 4a8c122..f3ef48b 100644 (file)
@@ -32,7 +32,8 @@ namespace uirenderer {
  ************************************************************/
 
 BaseRenderNodeAnimator::BaseRenderNodeAnimator(float finalValue)
-        : mFinalValue(finalValue)
+        : mTarget(NULL)
+        , mFinalValue(finalValue)
         , mDeltaValue(0)
         , mFromValue(0)
         , mInterpolator(0)
@@ -81,9 +82,14 @@ void BaseRenderNodeAnimator::setStartDelay(nsecs_t startDelay) {
     mStartDelay = startDelay;
 }
 
-void BaseRenderNodeAnimator::pushStaging(RenderNode* target, TreeInfo& info) {
+void BaseRenderNodeAnimator::attach(RenderNode* target) {
+    mTarget = target;
+    onAttached();
+}
+
+void BaseRenderNodeAnimator::pushStaging(TreeInfo& info) {
     if (!mHasStartValue) {
-        doSetStartValue(getValue(target));
+        doSetStartValue(getValue(mTarget));
     }
     if (mStagingPlayState > mPlayState) {
         mPlayState = mStagingPlayState;
@@ -109,20 +115,25 @@ void BaseRenderNodeAnimator::transitionToRunning(TreeInfo& info) {
     }
     // No interpolator was set, use the default
     if (!mInterpolator) {
-        setInterpolator(Interpolator::createDefaultInterpolator());
+        mInterpolator = Interpolator::createDefaultInterpolator();
     }
     if (mDuration < 0 || mDuration > 50000) {
         ALOGW("Your duration is strange and confusing: %" PRId64, mDuration);
     }
 }
 
-bool BaseRenderNodeAnimator::animate(RenderNode* target, TreeInfo& info) {
+bool BaseRenderNodeAnimator::animate(TreeInfo& info) {
     if (mPlayState < RUNNING) {
         return false;
     }
 
+    // If BaseRenderNodeAnimator is handling the delay (not typical), then
+    // because the staging properties reflect the final value, we always need
+    // to call setValue even if the animation isn't yet running or is still
+    // being delayed as we need to override the staging value
     if (mStartTime > info.frameTimeMs) {
         info.out.hasAnimations |= true;
+        setValue(mTarget, mFromValue);
         return false;
     }
 
@@ -136,7 +147,7 @@ bool BaseRenderNodeAnimator::animate(RenderNode* target, TreeInfo& info) {
     }
 
     fraction = mInterpolator->interpolate(fraction);
-    setValue(target, mFromValue + (mDeltaValue * fraction));
+    setValue(mTarget, mFromValue + (mDeltaValue * fraction));
 
     if (mPlayState == FINISHED) {
         callOnFinishedListener(info);
@@ -188,12 +199,17 @@ RenderPropertyAnimator::RenderPropertyAnimator(RenderProperty property, float fi
         , mPropertyAccess(&(PROPERTY_ACCESSOR_LUT[property])) {
 }
 
-void RenderPropertyAnimator::onAttached(RenderNode* target) {
+void RenderPropertyAnimator::onAttached() {
     if (!mHasStartValue
-            && target->isPropertyFieldDirty(mPropertyAccess->dirtyMask)) {
-        setStartValue((target->stagingProperties().*mPropertyAccess->getter)());
+            && mTarget->isPropertyFieldDirty(mPropertyAccess->dirtyMask)) {
+        setStartValue((mTarget->stagingProperties().*mPropertyAccess->getter)());
+    }
+}
+
+void RenderPropertyAnimator::onStagingPlayStateChanged() {
+    if (mStagingPlayState == RUNNING) {
+        (mTarget->mutateStagingProperties().*mPropertyAccess->setter)(finalValue());
     }
-    (target->mutateStagingProperties().*mPropertyAccess->setter)(finalValue());
 }
 
 uint32_t RenderPropertyAnimator::dirtyMask() {
index a981b5a..0dda23f 100644 (file)
@@ -50,12 +50,14 @@ public:
     ANDROID_API void setListener(AnimationListener* listener) {
         mListener = listener;
     }
-    ANDROID_API void start() { mStagingPlayState = RUNNING; }
-    ANDROID_API void cancel() { mStagingPlayState = FINISHED; }
+    ANDROID_API void start() { mStagingPlayState = RUNNING; onStagingPlayStateChanged(); }
+    ANDROID_API void cancel() { mStagingPlayState = FINISHED; onStagingPlayStateChanged(); }
 
-    virtual void onAttached(RenderNode* target) {}
-    virtual void pushStaging(RenderNode* target, TreeInfo& info);
-    bool animate(RenderNode* target, TreeInfo& info);
+    void attach(RenderNode* target);
+    virtual void onAttached() {}
+    void detach() { mTarget = 0; }
+    void pushStaging(TreeInfo& info);
+    bool animate(TreeInfo& info);
 
     bool isFinished() { return mPlayState == FINISHED; }
     float finalValue() { return mFinalValue; }
@@ -68,15 +70,20 @@ protected:
 
     virtual float getValue(RenderNode* target) const = 0;
     virtual void setValue(RenderNode* target, float value) = 0;
+    RenderNode* target() { return mTarget; }
 
     void callOnFinishedListener(TreeInfo& info);
 
+    virtual void onStagingPlayStateChanged() {}
+
     enum PlayState {
         NOT_STARTED,
         RUNNING,
         FINISHED,
     };
 
+    RenderNode* mTarget;
+
     float mFinalValue;
     float mDeltaValue;
     float mFromValue;
@@ -92,9 +99,9 @@ protected:
     sp<AnimationListener> mListener;
 
 private:
-    void doSetStartValue(float value);
     inline void checkMutable();
-    void transitionToRunning(TreeInfo& info);
+    virtual void transitionToRunning(TreeInfo& info);
+    void doSetStartValue(float value);
 };
 
 class RenderPropertyAnimator : public BaseRenderNodeAnimator {
@@ -116,13 +123,13 @@ public:
 
     ANDROID_API RenderPropertyAnimator(RenderProperty property, float finalValue);
 
-    virtual void onAttached(RenderNode* target);
-
     ANDROID_API virtual uint32_t dirtyMask();
 
 protected:
     virtual float getValue(RenderNode* target) const;
     virtual void setValue(RenderNode* target, float value);
+    virtual void onAttached();
+    virtual void onStagingPlayStateChanged();
 
 private:
     typedef bool (RenderProperties::*SetFloatProperty)(float value);
index 6a10cf8..27b0893 100644 (file)
@@ -25,6 +25,7 @@ namespace uirenderer {
 using namespace std;
 
 static void unref(BaseRenderNodeAnimator* animator) {
+    animator->detach();
     animator->decStrong(0);
 }
 
@@ -39,7 +40,7 @@ AnimatorManager::~AnimatorManager() {
 
 void AnimatorManager::addAnimator(const sp<BaseRenderNodeAnimator>& animator) {
     animator->incStrong(0);
-    animator->onAttached(&mParent);
+    animator->attach(&mParent);
     mNewAnimators.push_back(animator.get());
 }
 
@@ -58,7 +59,7 @@ void AnimatorManager::pushStaging(TreeInfo& info) {
         move_all(mNewAnimators, mAnimators);
     }
     for (vector<BaseRenderNodeAnimator*>::iterator it = mAnimators.begin(); it != mAnimators.end(); it++) {
-        (*it)->pushStaging(&mParent, info);
+        (*it)->pushStaging(info);
     }
 }
 
@@ -68,7 +69,7 @@ public:
             : mTarget(target), mInfo(info) {}
 
     bool operator() (BaseRenderNodeAnimator* animator) {
-        bool remove = animator->animate(&mTarget, mInfo);
+        bool remove = animator->animate(mInfo);
         if (remove) {
             animator->decStrong(0);
         }