Fixes to startDelay
Bug: 15991758
Don't update the UI thread with final value until after
startDelay
Change-Id: Ie8bffb5a3ace353ec1d82943a4efcbd01c42c28f
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index 1066430..f41afcf 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -431,7 +431,7 @@
/**
* 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
diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java
index 1363a5c..4b53c8e 100644
--- a/core/java/android/view/RenderNodeAnimator.java
+++ b/core/java/android/view/RenderNodeAnimator.java
@@ -18,6 +18,7 @@
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 @@
/**
* @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 @@
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 @@
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 void setTarget(View view) {
mViewTarget = view;
mTarget = view.mRenderNode;
+ mTarget.addAnimator(this);
}
public void setTarget(Canvas canvas) {
@@ -213,12 +231,12 @@
}
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 @@
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 @@
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 @@
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
index 0000000..151277a
--- /dev/null
+++ b/core/java/android/view/RenderNodeAnimatorCompat.java
@@ -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();
+ }
+ }
+ }
+}
diff --git a/core/java/android/view/ViewPropertyAnimatorRT.java b/core/java/android/view/ViewPropertyAnimatorRT.java
index 8b4277a..05df7a0 100644
--- a/core/java/android/view/ViewPropertyAnimatorRT.java
+++ b/core/java/android/view/ViewPropertyAnimatorRT.java
@@ -73,21 +73,14 @@
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();
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index 6ba22bf..28473e0 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -456,7 +456,6 @@
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
RenderPropertyAnimator* animator = reinterpret_cast<RenderPropertyAnimator*>(animatorPtr);
renderNode->addAnimator(animator);
- animator->start();
}
#endif // USE_OPENGL_RENDERER
diff --git a/core/jni/android_view_RenderNodeAnimator.cpp b/core/jni/android_view_RenderNodeAnimator.cpp
index de3dd16..ed57979 100644
--- a/core/jni/android_view_RenderNodeAnimator.cpp
+++ b/core/jni/android_view_RenderNodeAnimator.cpp
@@ -149,6 +149,11 @@
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 @@
{ "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
};
diff --git a/libs/hwui/Animator.cpp b/libs/hwui/Animator.cpp
index 4a8c122..f3ef48b 100644
--- a/libs/hwui/Animator.cpp
+++ b/libs/hwui/Animator.cpp
@@ -32,7 +32,8 @@
************************************************************/
BaseRenderNodeAnimator::BaseRenderNodeAnimator(float finalValue)
- : mFinalValue(finalValue)
+ : mTarget(NULL)
+ , mFinalValue(finalValue)
, mDeltaValue(0)
, mFromValue(0)
, mInterpolator(0)
@@ -81,9 +82,14 @@
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 @@
}
// 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 @@
}
fraction = mInterpolator->interpolate(fraction);
- setValue(target, mFromValue + (mDeltaValue * fraction));
+ setValue(mTarget, mFromValue + (mDeltaValue * fraction));
if (mPlayState == FINISHED) {
callOnFinishedListener(info);
@@ -188,12 +199,17 @@
, 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)());
}
- (target->mutateStagingProperties().*mPropertyAccess->setter)(finalValue());
+}
+
+void RenderPropertyAnimator::onStagingPlayStateChanged() {
+ if (mStagingPlayState == RUNNING) {
+ (mTarget->mutateStagingProperties().*mPropertyAccess->setter)(finalValue());
+ }
}
uint32_t RenderPropertyAnimator::dirtyMask() {
diff --git a/libs/hwui/Animator.h b/libs/hwui/Animator.h
index a981b5a..0dda23f 100644
--- a/libs/hwui/Animator.h
+++ b/libs/hwui/Animator.h
@@ -50,12 +50,14 @@
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 @@
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 @@
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 @@
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);
diff --git a/libs/hwui/AnimatorManager.cpp b/libs/hwui/AnimatorManager.cpp
index 6a10cf8..27b0893 100644
--- a/libs/hwui/AnimatorManager.cpp
+++ b/libs/hwui/AnimatorManager.cpp
@@ -25,6 +25,7 @@
using namespace std;
static void unref(BaseRenderNodeAnimator* animator) {
+ animator->detach();
animator->decStrong(0);
}
@@ -39,7 +40,7 @@
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 @@
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 @@
: mTarget(target), mInfo(info) {}
bool operator() (BaseRenderNodeAnimator* animator) {
- bool remove = animator->animate(&mTarget, mInfo);
+ bool remove = animator->animate(mInfo);
if (remove) {
animator->decStrong(0);
}