summaryrefslogtreecommitdiff
path: root/graphics/java
diff options
context:
space:
mode:
authorDerek Sollenberger <djsollen@google.com>2020-02-14 16:32:50 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2020-02-14 16:32:50 +0000
commitdb8164c0ac68ffa4f2c7c2d97f68b21f3eaa68b5 (patch)
tree845085e575a559f05794b0e21eb0a1c57be8dd56 /graphics/java
parent47f42fedeed263116b885e13c1658ee7407d362b (diff)
parent8c2f6b6e159512d2438a0b9504a7656157fab4c5 (diff)
Merge "Move RenderNodeAnimator to android.graphics.animation package"
Diffstat (limited to 'graphics/java')
-rw-r--r--graphics/java/android/graphics/RenderNode.java2
-rw-r--r--graphics/java/android/graphics/animation/FallbackLUTInterpolator.java80
-rw-r--r--graphics/java/android/graphics/animation/HasNativeInterpolator.java37
-rw-r--r--graphics/java/android/graphics/animation/NativeInterpolator.java29
-rw-r--r--graphics/java/android/graphics/animation/NativeInterpolatorFactory.java67
-rw-r--r--graphics/java/android/graphics/animation/RenderNodeAnimator.java513
-rw-r--r--graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java6
-rw-r--r--graphics/java/android/graphics/drawable/RippleForeground.java2
8 files changed, 731 insertions, 5 deletions
diff --git a/graphics/java/android/graphics/RenderNode.java b/graphics/java/android/graphics/RenderNode.java
index 3835b2d493c5..752695fd8e52 100644
--- a/graphics/java/android/graphics/RenderNode.java
+++ b/graphics/java/android/graphics/RenderNode.java
@@ -22,8 +22,8 @@ import android.annotation.FloatRange;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.graphics.animation.RenderNodeAnimator;
import android.view.NativeVectorDrawableAnimator;
-import android.view.RenderNodeAnimator;
import android.view.Surface;
import android.view.View;
diff --git a/graphics/java/android/graphics/animation/FallbackLUTInterpolator.java b/graphics/java/android/graphics/animation/FallbackLUTInterpolator.java
new file mode 100644
index 000000000000..36062c141ed2
--- /dev/null
+++ b/graphics/java/android/graphics/animation/FallbackLUTInterpolator.java
@@ -0,0 +1,80 @@
+/*
+ * 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.graphics.animation;
+
+import android.animation.TimeInterpolator;
+import android.util.TimeUtils;
+import android.view.Choreographer;
+
+/**
+ * Interpolator that builds a lookup table to use. This is a fallback for
+ * building a native interpolator from a TimeInterpolator that is not marked
+ * with {@link HasNativeInterpolator}
+ *
+ * This implements TimeInterpolator to allow for easier interop with Animators
+ * @hide
+ */
+@HasNativeInterpolator
+public class FallbackLUTInterpolator implements NativeInterpolator, TimeInterpolator {
+
+ // If the duration of an animation is more than 300 frames, we cap the sample size to 300.
+ private static final int MAX_SAMPLE_POINTS = 300;
+ private TimeInterpolator mSourceInterpolator;
+ private final float[] mLut;
+
+ /**
+ * Used to cache the float[] LUT for use across multiple native
+ * interpolator creation
+ */
+ public FallbackLUTInterpolator(TimeInterpolator interpolator, long duration) {
+ mSourceInterpolator = interpolator;
+ mLut = createLUT(interpolator, duration);
+ }
+
+ private static float[] createLUT(TimeInterpolator interpolator, long duration) {
+ long frameIntervalNanos = Choreographer.getInstance().getFrameIntervalNanos();
+ int animIntervalMs = (int) (frameIntervalNanos / TimeUtils.NANOS_PER_MS);
+ // We need 2 frame values as the minimal.
+ int numAnimFrames = Math.max(2, (int) Math.ceil(((double) duration) / animIntervalMs));
+ numAnimFrames = Math.min(numAnimFrames, MAX_SAMPLE_POINTS);
+ float[] values = new float[numAnimFrames];
+ float lastFrame = numAnimFrames - 1;
+ for (int i = 0; i < numAnimFrames; i++) {
+ float inValue = i / lastFrame;
+ values[i] = interpolator.getInterpolation(inValue);
+ }
+ return values;
+ }
+
+ @Override
+ public long createNativeInterpolator() {
+ return NativeInterpolatorFactory.createLutInterpolator(mLut);
+ }
+
+ /**
+ * Used to create a one-shot float[] LUT & native interpolator
+ */
+ public static long createNativeInterpolator(TimeInterpolator interpolator, long duration) {
+ float[] lut = createLUT(interpolator, duration);
+ return NativeInterpolatorFactory.createLutInterpolator(lut);
+ }
+
+ @Override
+ public float getInterpolation(float input) {
+ return mSourceInterpolator.getInterpolation(input);
+ }
+}
diff --git a/graphics/java/android/graphics/animation/HasNativeInterpolator.java b/graphics/java/android/graphics/animation/HasNativeInterpolator.java
new file mode 100644
index 000000000000..c53d5a42524d
--- /dev/null
+++ b/graphics/java/android/graphics/animation/HasNativeInterpolator.java
@@ -0,0 +1,37 @@
+/*
+ * 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.graphics.animation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * This is a class annotation that signals that it is safe to create
+ * a native interpolator counterpart via {@link NativeInterpolator}
+ *
+ * The idea here is to prevent subclasses of interpolators from being treated as a
+ * NativeInterpolator, and instead have them fall back to the LUT & LERP
+ * method like a custom interpolator.
+ *
+ * @hide
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE})
+public @interface HasNativeInterpolator {
+}
diff --git a/graphics/java/android/graphics/animation/NativeInterpolator.java b/graphics/java/android/graphics/animation/NativeInterpolator.java
new file mode 100644
index 000000000000..1e6fea8da466
--- /dev/null
+++ b/graphics/java/android/graphics/animation/NativeInterpolator.java
@@ -0,0 +1,29 @@
+/*
+ * 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.graphics.animation;
+
+/**
+ * @hide
+ */
+public interface NativeInterpolator {
+ /**
+ * Generates a native interpolator object that can be used by HardwareRenderer to draw
+ * RenderNodes.
+ * @return ptr to native object
+ */
+ long createNativeInterpolator();
+}
diff --git a/graphics/java/android/graphics/animation/NativeInterpolatorFactory.java b/graphics/java/android/graphics/animation/NativeInterpolatorFactory.java
new file mode 100644
index 000000000000..38866145ee50
--- /dev/null
+++ b/graphics/java/android/graphics/animation/NativeInterpolatorFactory.java
@@ -0,0 +1,67 @@
+/*
+ * 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.graphics.animation;
+
+import android.animation.TimeInterpolator;
+
+/**
+ * Static utility class for constructing native interpolators to keep the
+ * JNI simpler
+ *
+ * @hide
+ */
+public final class NativeInterpolatorFactory {
+ private NativeInterpolatorFactory() {}
+
+ /**
+ * Create a native interpolator from the provided param generating a LUT variant if a native
+ * implementation does not exist.
+ */
+ public static long createNativeInterpolator(TimeInterpolator interpolator, long
+ duration) {
+ if (interpolator == null) {
+ return createLinearInterpolator();
+ } else if (RenderNodeAnimator.isNativeInterpolator(interpolator)) {
+ return ((NativeInterpolator) interpolator).createNativeInterpolator();
+ } else {
+ return FallbackLUTInterpolator.createNativeInterpolator(interpolator, duration);
+ }
+ }
+
+ /** Creates a specialized native interpolator for Accelerate/Decelerate */
+ public static native long createAccelerateDecelerateInterpolator();
+ /** Creates a specialized native interpolator for Accelerate */
+ public static native long createAccelerateInterpolator(float factor);
+ /** Creates a specialized native interpolator for Anticipate */
+ public static native long createAnticipateInterpolator(float tension);
+ /** Creates a specialized native interpolator for Anticipate with Overshoot */
+ public static native long createAnticipateOvershootInterpolator(float tension);
+ /** Creates a specialized native interpolator for Bounce */
+ public static native long createBounceInterpolator();
+ /** Creates a specialized native interpolator for Cycle */
+ public static native long createCycleInterpolator(float cycles);
+ /** Creates a specialized native interpolator for Decelerate */
+ public static native long createDecelerateInterpolator(float factor);
+ /** Creates a specialized native interpolator for Linear interpolation */
+ public static native long createLinearInterpolator();
+ /** Creates a specialized native interpolator for Overshoot */
+ public static native long createOvershootInterpolator(float tension);
+ /** Creates a specialized native interpolator for along traveling along a Path */
+ public static native long createPathInterpolator(float[] x, float[] y);
+ /** Creates a specialized native interpolator for LUT */
+ public static native long createLutInterpolator(float[] values);
+}
diff --git a/graphics/java/android/graphics/animation/RenderNodeAnimator.java b/graphics/java/android/graphics/animation/RenderNodeAnimator.java
new file mode 100644
index 000000000000..282b2f959fc4
--- /dev/null
+++ b/graphics/java/android/graphics/animation/RenderNodeAnimator.java
@@ -0,0 +1,513 @@
+/*
+ * 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.graphics.animation;
+
+import android.animation.Animator;
+import android.animation.TimeInterpolator;
+import android.animation.ValueAnimator;
+import android.graphics.CanvasProperty;
+import android.graphics.Paint;
+import android.graphics.RecordingCanvas;
+import android.graphics.RenderNode;
+import android.os.Handler;
+import android.os.Looper;
+import android.view.Choreographer;
+
+import com.android.internal.util.VirtualRefBasePtr;
+
+import java.util.ArrayList;
+
+/**
+ * @hide
+ */
+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;
+ public static final int TRANSLATION_Z = 2;
+ public static final int SCALE_X = 3;
+ public static final int SCALE_Y = 4;
+ public static final int ROTATION = 5;
+ public static final int ROTATION_X = 6;
+ public static final int ROTATION_Y = 7;
+ public static final int X = 8;
+ public static final int Y = 9;
+ public static final int Z = 10;
+ public static final int ALPHA = 11;
+ // The last value in the enum, used for array size initialization
+ public static final int LAST_VALUE = ALPHA;
+
+ // Keep in sync with enum PaintFields in Animator.h
+ public static final int PAINT_STROKE_WIDTH = 0;
+
+ /**
+ * Field for the Paint alpha channel, which should be specified as a value
+ * between 0 and 255.
+ */
+ public static final int PAINT_ALPHA = 1;
+
+ private VirtualRefBasePtr mNativePtr;
+
+ private Handler mHandler;
+ private RenderNode mTarget;
+ private ViewListener mViewListener;
+ private int mRenderProperty = -1;
+ private float mFinalValue;
+ private TimeInterpolator mInterpolator;
+
+ private static final int STATE_PREPARE = 0;
+ private static final int STATE_DELAYED = 1;
+ private static final int STATE_RUNNING = 2;
+ private static final int STATE_FINISHED = 3;
+ private int mState = STATE_PREPARE;
+
+ private long mUnscaledDuration = 300;
+ private long mUnscaledStartDelay = 0;
+ // If this is true, we will run any start delays on the UI thread. This is
+ // the safe default, and is necessary to ensure start listeners fire at
+ // the correct time. Animators created by RippleDrawable (the
+ // CanvasProperty<> ones) do not have this expectation, and as such will
+ // set this to false so that the renderthread handles the startdelay instead
+ private final boolean mUiThreadHandlesDelay;
+ private long mStartDelay = 0;
+ private long mStartTime;
+
+ /**
+ * Interface used by the view system to update the view hierarchy in conjunction
+ * with this animator.
+ */
+ public interface ViewListener {
+ /** notify the listener that an alpha animation has begun. */
+ void onAlphaAnimationStart(float finalAlpha);
+ /** notify the listener that the animator has mutated a value that requires invalidation */
+ void invalidateParent(boolean forceRedraw);
+ }
+
+ public RenderNodeAnimator(int property, float finalValue) {
+ mRenderProperty = property;
+ mFinalValue = finalValue;
+ mUiThreadHandlesDelay = true;
+ init(nCreateAnimator(property, finalValue));
+ }
+
+ public RenderNodeAnimator(CanvasProperty<Float> property, float finalValue) {
+ init(nCreateCanvasPropertyFloatAnimator(
+ property.getNativeContainer(), finalValue));
+ mUiThreadHandlesDelay = false;
+ }
+
+ /**
+ * Creates a new render node animator for a field on a Paint property.
+ *
+ * @param property The paint property to target
+ * @param paintField Paint field to animate, one of {@link #PAINT_ALPHA} or
+ * {@link #PAINT_STROKE_WIDTH}
+ * @param finalValue The target value for the property
+ */
+ public RenderNodeAnimator(CanvasProperty<Paint> property, int paintField, float finalValue) {
+ init(nCreateCanvasPropertyPaintAnimator(
+ property.getNativeContainer(), paintField, finalValue));
+ mUiThreadHandlesDelay = false;
+ }
+
+ public RenderNodeAnimator(int x, int y, float startRadius, float endRadius) {
+ init(nCreateRevealAnimator(x, y, startRadius, endRadius));
+ mUiThreadHandlesDelay = true;
+ }
+
+ private void init(long ptr) {
+ mNativePtr = new VirtualRefBasePtr(ptr);
+ }
+
+ private void checkMutable() {
+ if (mState != STATE_PREPARE) {
+ throw new IllegalStateException("Animator has already started, cannot change it now!");
+ }
+ if (mNativePtr == null) {
+ throw new IllegalStateException("Animator's target has been destroyed "
+ + "(trying to modify an animation after activity destroy?)");
+ }
+ }
+
+ static boolean isNativeInterpolator(TimeInterpolator interpolator) {
+ return interpolator.getClass().isAnnotationPresent(HasNativeInterpolator.class);
+ }
+
+ private void applyInterpolator() {
+ if (mInterpolator == null || mNativePtr == null) return;
+
+ long ni;
+ if (isNativeInterpolator(mInterpolator)) {
+ ni = ((NativeInterpolator) mInterpolator).createNativeInterpolator();
+ } else {
+ long duration = nGetDuration(mNativePtr.get());
+ ni = FallbackLUTInterpolator.createNativeInterpolator(mInterpolator, duration);
+ }
+ nSetInterpolator(mNativePtr.get(), ni);
+ }
+
+ @Override
+ public void start() {
+ if (mTarget == null) {
+ throw new IllegalStateException("Missing target!");
+ }
+
+ if (mState != STATE_PREPARE) {
+ throw new IllegalStateException("Already started!");
+ }
+
+ mState = STATE_DELAYED;
+ if (mHandler == null) {
+ mHandler = new Handler(true);
+ }
+ applyInterpolator();
+
+ if (mNativePtr == null) {
+ // It's dead, immediately cancel
+ cancel();
+ } else if (mStartDelay <= 0 || !mUiThreadHandlesDelay) {
+ nSetStartDelay(mNativePtr.get(), mStartDelay);
+ doStart();
+ } else {
+ getHelper().addDelayedAnimation(this);
+ }
+ }
+
+ private void doStart() {
+ // 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 && mViewListener != null) {
+ mViewListener.onAlphaAnimationStart(mFinalValue);
+ }
+
+ moveToRunningState();
+
+ if (mViewListener != null) {
+ // Kick off a frame to start the process
+ mViewListener.invalidateParent(false);
+ }
+ }
+
+ private void moveToRunningState() {
+ mState = STATE_RUNNING;
+ if (mNativePtr != null) {
+ nStart(mNativePtr.get());
+ }
+ notifyStartListeners();
+ }
+
+ private void notifyStartListeners() {
+ final ArrayList<AnimatorListener> listeners = cloneListeners();
+ final int numListeners = listeners == null ? 0 : listeners.size();
+ for (int i = 0; i < numListeners; i++) {
+ listeners.get(i).onAnimationStart(this);
+ }
+ }
+
+ @Override
+ public void cancel() {
+ if (mState != STATE_PREPARE && mState != STATE_FINISHED) {
+ if (mState == STATE_DELAYED) {
+ getHelper().removeDelayedAnimation(this);
+ moveToRunningState();
+ }
+
+ final ArrayList<AnimatorListener> listeners = cloneListeners();
+ final int numListeners = listeners == null ? 0 : listeners.size();
+ for (int i = 0; i < numListeners; i++) {
+ listeners.get(i).onAnimationCancel(this);
+ }
+
+ end();
+ }
+ }
+
+ @Override
+ public void end() {
+ if (mState != STATE_FINISHED) {
+ if (mState < STATE_RUNNING) {
+ getHelper().removeDelayedAnimation(this);
+ doStart();
+ }
+ if (mNativePtr != null) {
+ nEnd(mNativePtr.get());
+ if (mViewListener != null) {
+ // Kick off a frame to flush the state change
+ mViewListener.invalidateParent(false);
+ }
+ } else {
+ // It's already dead, jump to onFinish
+ onFinished();
+ }
+ }
+ }
+
+ @Override
+ public void pause() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void resume() {
+ throw new UnsupportedOperationException();
+ }
+
+ /** @hide */
+ public void setViewListener(ViewListener listener) {
+ mViewListener = listener;
+ }
+
+ /** Sets the animation target to the owning view of the RecordingCanvas */
+ public final void setTarget(RecordingCanvas canvas) {
+ setTarget(canvas.mNode);
+ }
+
+ /** Sets the node that is to be the target of this animation */
+ protected void setTarget(RenderNode node) {
+ checkMutable();
+ if (mTarget != null) {
+ throw new IllegalStateException("Target already set!");
+ }
+ nSetListener(mNativePtr.get(), this);
+ mTarget = node;
+ mTarget.addAnimator(this);
+ }
+
+ /** Set the start value for the animation */
+ public void setStartValue(float startValue) {
+ checkMutable();
+ nSetStartValue(mNativePtr.get(), startValue);
+ }
+
+ @Override
+ public void setStartDelay(long startDelay) {
+ checkMutable();
+ if (startDelay < 0) {
+ throw new IllegalArgumentException("startDelay must be positive; " + startDelay);
+ }
+ mUnscaledStartDelay = startDelay;
+ mStartDelay = (long) (ValueAnimator.getDurationScale() * startDelay);
+ }
+
+ @Override
+ public long getStartDelay() {
+ return mUnscaledStartDelay;
+ }
+
+ @Override
+ public RenderNodeAnimator setDuration(long duration) {
+ checkMutable();
+ if (duration < 0) {
+ throw new IllegalArgumentException("duration must be positive; " + duration);
+ }
+ mUnscaledDuration = duration;
+ nSetDuration(mNativePtr.get(), (long) (duration * ValueAnimator.getDurationScale()));
+ return this;
+ }
+
+ @Override
+ public long getDuration() {
+ return mUnscaledDuration;
+ }
+
+ @Override
+ public long getTotalDuration() {
+ return mUnscaledDuration + mUnscaledStartDelay;
+ }
+
+ @Override
+ public boolean isRunning() {
+ return mState == STATE_DELAYED || mState == STATE_RUNNING;
+ }
+
+ @Override
+ public boolean isStarted() {
+ return mState != STATE_PREPARE;
+ }
+
+ @Override
+ public void setInterpolator(TimeInterpolator interpolator) {
+ checkMutable();
+ mInterpolator = interpolator;
+ }
+
+ @Override
+ public TimeInterpolator getInterpolator() {
+ return mInterpolator;
+ }
+
+ protected void onFinished() {
+ if (mState == STATE_PREPARE) {
+ // Unlikely but possible, the native side has been destroyed
+ // before we have started.
+ releaseNativePtr();
+ return;
+ }
+ if (mState == STATE_DELAYED) {
+ getHelper().removeDelayedAnimation(this);
+ notifyStartListeners();
+ }
+ mState = STATE_FINISHED;
+
+ final ArrayList<AnimatorListener> listeners = cloneListeners();
+ final int numListeners = listeners == null ? 0 : listeners.size();
+ for (int i = 0; i < numListeners; i++) {
+ listeners.get(i).onAnimationEnd(this);
+ }
+
+ // Release the native object, as it has a global reference to us. This
+ // breaks the cyclic reference chain, and allows this object to be
+ // GC'd
+ releaseNativePtr();
+ }
+
+ private void releaseNativePtr() {
+ if (mNativePtr != null) {
+ mNativePtr.release();
+ mNativePtr = null;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private ArrayList<AnimatorListener> cloneListeners() {
+ ArrayList<AnimatorListener> listeners = getListeners();
+ if (listeners != null) {
+ listeners = (ArrayList<AnimatorListener>) listeners.clone();
+ }
+ return listeners;
+ }
+
+ public long getNativeAnimator() {
+ return mNativePtr.get();
+ }
+
+ /**
+ * @return true if the animator was started, false if still delayed
+ */
+ private boolean processDelayed(long frameTimeMs) {
+ if (mStartTime == 0) {
+ mStartTime = frameTimeMs;
+ } else if ((frameTimeMs - mStartTime) >= mStartDelay) {
+ doStart();
+ return true;
+ }
+ return false;
+ }
+
+ private static DelayedAnimationHelper getHelper() {
+ DelayedAnimationHelper helper = sAnimationHelper.get();
+ if (helper == null) {
+ helper = new DelayedAnimationHelper();
+ sAnimationHelper.set(helper);
+ }
+ return helper;
+ }
+
+ private static ThreadLocal<DelayedAnimationHelper> sAnimationHelper =
+ new ThreadLocal<DelayedAnimationHelper>();
+
+ private static class DelayedAnimationHelper implements Runnable {
+
+ private ArrayList<RenderNodeAnimator> mDelayedAnims = new ArrayList<RenderNodeAnimator>();
+ private final Choreographer mChoreographer;
+ private boolean mCallbackScheduled;
+
+ DelayedAnimationHelper() {
+ mChoreographer = Choreographer.getInstance();
+ }
+
+ public void addDelayedAnimation(RenderNodeAnimator animator) {
+ mDelayedAnims.add(animator);
+ scheduleCallback();
+ }
+
+ public void removeDelayedAnimation(RenderNodeAnimator animator) {
+ mDelayedAnims.remove(animator);
+ }
+
+ 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++) {
+ RenderNodeAnimator 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();
+ }
+ }
+ }
+
+ // Called by native
+ private static void callOnFinished(RenderNodeAnimator animator) {
+ if (animator.mHandler != null) {
+ animator.mHandler.post(animator::onFinished);
+ } else {
+ new Handler(Looper.getMainLooper(), null, true).post(animator::onFinished);
+ }
+ }
+
+ @Override
+ public Animator clone() {
+ throw new IllegalStateException("Cannot clone this animator");
+ }
+
+ @Override
+ public void setAllowRunningAsynchronously(boolean mayRunAsync) {
+ checkMutable();
+ nSetAllowRunningAsync(mNativePtr.get(), mayRunAsync);
+ }
+
+ private static native long nCreateAnimator(int property, float finalValue);
+ private static native long nCreateCanvasPropertyFloatAnimator(
+ long canvasProperty, float finalValue);
+ private static native long nCreateCanvasPropertyPaintAnimator(
+ long canvasProperty, int paintField, float finalValue);
+ private static native long nCreateRevealAnimator(
+ int x, int y, float startRadius, float endRadius);
+
+ private static native void nSetStartValue(long nativePtr, float startValue);
+ private static native void nSetDuration(long nativePtr, long duration);
+ private static native long nGetDuration(long nativePtr);
+ private static native void nSetStartDelay(long nativePtr, long startDelay);
+ private static native void nSetInterpolator(long animPtr, long interpolatorPtr);
+ private static native void nSetAllowRunningAsync(long animPtr, boolean mayRunAsync);
+ private static native void nSetListener(long animPtr, RenderNodeAnimator listener);
+
+ private static native void nStart(long animPtr);
+ private static native void nEnd(long animPtr);
+}
diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
index 1acf6c512fbd..9fb72cf08b51 100644
--- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
@@ -42,6 +42,7 @@ import android.graphics.PixelFormat;
import android.graphics.RecordingCanvas;
import android.graphics.Rect;
import android.graphics.RenderNode;
+import android.graphics.animation.NativeInterpolatorFactory;
import android.os.Build;
import android.os.Handler;
import android.util.ArrayMap;
@@ -54,7 +55,6 @@ import android.util.Property;
import android.util.TimeUtils;
import android.view.Choreographer;
import android.view.NativeVectorDrawableAnimator;
-import android.view.RenderNodeAnimatorSetHelper;
import android.view.View;
import com.android.internal.R;
@@ -1532,7 +1532,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
long startDelay = extraDelay + animator.getStartDelay();
TimeInterpolator interpolator = animator.getInterpolator();
long nativeInterpolator =
- RenderNodeAnimatorSetHelper.createNativeInterpolator(interpolator, duration);
+ NativeInterpolatorFactory.createNativeInterpolator(interpolator, duration);
startDelay *= ValueAnimator.getDurationScale();
duration *= ValueAnimator.getDurationScale();
@@ -1548,7 +1548,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
* to the last seen RenderNode target and start right away.
*/
protected void recordLastSeenTarget(RecordingCanvas canvas) {
- final RenderNode node = RenderNodeAnimatorSetHelper.getTarget(canvas);
+ final RenderNode node = canvas.mNode;
mLastSeenTarget = new WeakReference<RenderNode>(node);
// Add the animator to the list of animators on every draw
if (mInitialized || mPendingAnimationActions.size() > 0) {
diff --git a/graphics/java/android/graphics/drawable/RippleForeground.java b/graphics/java/android/graphics/drawable/RippleForeground.java
index cce9ba31929f..0f376957c8ff 100644
--- a/graphics/java/android/graphics/drawable/RippleForeground.java
+++ b/graphics/java/android/graphics/drawable/RippleForeground.java
@@ -25,9 +25,9 @@ import android.graphics.CanvasProperty;
import android.graphics.Paint;
import android.graphics.RecordingCanvas;
import android.graphics.Rect;
+import android.graphics.animation.RenderNodeAnimator;
import android.util.FloatProperty;
import android.util.MathUtils;
-import android.view.RenderNodeAnimator;
import android.view.animation.AnimationUtils;
import android.view.animation.LinearInterpolator;
import android.view.animation.PathInterpolator;