Add TimeInterpolator support to RNA

 Bug: 14678626

Change-Id: I6554e7fcd42c49fac3618ca792083bb68e358f55
diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java
index 33964a0..8f4b710 100644
--- a/core/java/android/util/TimeUtils.java
+++ b/core/java/android/util/TimeUtils.java
@@ -245,6 +245,9 @@
     private static final int SECONDS_PER_HOUR = 60 * 60;
     private static final int SECONDS_PER_DAY = 24 * 60 * 60;
 
+    /** @hide */
+    public static final long NANOS_PER_MS = 1000000;
+
     private static final Object sFormatSync = new Object();
     private static char[] sFormatStr = new char[HUNDRED_DAY_FIELD_LEN+5];
 
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index 0a76075..1066430 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -112,8 +112,6 @@
     private static final int SKIPPED_FRAME_WARNING_LIMIT = SystemProperties.getInt(
             "debug.choreographer.skipwarning", 30);
 
-    private static final long NANOS_PER_MS = 1000000;
-
     private static final int MSG_DO_FRAME = 0;
     private static final int MSG_DO_SCHEDULE_VSYNC = 1;
     private static final int MSG_DO_SCHEDULE_CALLBACK = 2;
@@ -263,7 +261,7 @@
      * @return The refresh rate as the nanoseconds between frames
      * @hide
      */
-    long getFrameIntervalNanos() {
+    public long getFrameIntervalNanos() {
         return mFrameIntervalNanos;
     }
 
@@ -456,7 +454,7 @@
      * @hide
      */
     public long getFrameTime() {
-        return getFrameTimeNanos() / NANOS_PER_MS;
+        return getFrameTimeNanos() / TimeUtils.NANOS_PER_MS;
     }
 
     /**
@@ -497,7 +495,7 @@
                 }
             } else {
                 final long nextFrameTime = Math.max(
-                        mLastFrameTimeNanos / NANOS_PER_MS + sFrameDelay, now);
+                        mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now);
                 if (DEBUG) {
                     Log.d(TAG, "Scheduling next frame in " + (nextFrameTime - now) + " ms.");
                 }
@@ -746,7 +744,7 @@
             mFrame = frame;
             Message msg = Message.obtain(mHandler, this);
             msg.setAsynchronous(true);
-            mHandler.sendMessageAtTime(msg, timestampNanos / NANOS_PER_MS);
+            mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
         }
 
         @Override
diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java
index be3b6ce..ec4d560 100644
--- a/core/java/android/view/RenderNodeAnimator.java
+++ b/core/java/android/view/RenderNodeAnimator.java
@@ -16,12 +16,17 @@
 
 package android.view;
 
+import android.animation.TimeInterpolator;
 import android.graphics.Canvas;
 import android.graphics.CanvasProperty;
 import android.graphics.Paint;
 import android.util.SparseIntArray;
+import android.util.TimeUtils;
 
 import com.android.internal.util.VirtualRefBasePtr;
+import com.android.internal.view.animation.FallbackLUTInterpolator;
+import com.android.internal.view.animation.HasNativeInterpolator;
+import com.android.internal.view.animation.NativeInterpolatorFactory;
 
 import java.lang.ref.WeakReference;
 
@@ -71,9 +76,12 @@
     public static final int DELTA_TYPE_ABSOLUTE = 0;
     public static final int DELTA_TYPE_DELTA = 1;
 
-    private RenderNode mTarget;
     private VirtualRefBasePtr mNativePtr;
 
+    private RenderNode mTarget;
+    private TimeInterpolator mInterpolator;
+    private boolean mStarted = false;
+
     public int mapViewPropertyToRenderProperty(int viewProperty) {
         return sViewPropertyAnimatorMap.get(viewProperty);
     }
@@ -100,9 +108,37 @@
         mNativePtr = new VirtualRefBasePtr(ptr);
     }
 
-    public void start(View target) {
-        mTarget = target.mRenderNode;
+    private void checkMutable() {
+        if (mStarted) {
+            throw new IllegalStateException("Animator has already started, cannot change it now!");
+        }
+    }
+
+    private void applyInterpolator() {
+        if (mInterpolator == null) return;
+
+        long ni;
+        if (mInterpolator.getClass().isAnnotationPresent(HasNativeInterpolator.class)) {
+            ni = ((NativeInterpolatorFactory)mInterpolator).createNativeInterpolator();
+        } else {
+            int duration = nGetDuration(mNativePtr.get());
+            ni = FallbackLUTInterpolator.createNativeInterpolator(mInterpolator, duration);
+        }
+        nSetInterpolator(mNativePtr.get(), ni);
+    }
+
+    private void start(RenderNode node) {
+        if (mStarted) {
+            throw new IllegalStateException("Already started!");
+        }
+        mStarted = true;
+        applyInterpolator();
+        mTarget = node;
         mTarget.addAnimator(this);
+    }
+
+    public void start(View target) {
+        start(target.mRenderNode);
         // Kick off a frame to start the process
         target.invalidateViewProperty(true, false);
     }
@@ -112,8 +148,7 @@
             throw new IllegalArgumentException("Not a GLES20RecordingCanvas");
         }
         GLES20RecordingCanvas recordingCanvas = (GLES20RecordingCanvas) canvas;
-        mTarget = recordingCanvas.mNode;
-        mTarget.addAnimator(this);
+        start(recordingCanvas.mNode);
     }
 
     public void cancel() {
@@ -121,9 +156,15 @@
     }
 
     public void setDuration(int duration) {
+        checkMutable();
         nSetDuration(mNativePtr.get(), duration);
     }
 
+    public void setInterpolator(TimeInterpolator interpolator) {
+        checkMutable();
+        mInterpolator = interpolator;
+    }
+
     long getNativeAnimator() {
         return mNativePtr.get();
     }
@@ -147,4 +188,6 @@
     private static native long nCreateCanvasPropertyPaintAnimator(WeakReference<RenderNodeAnimator> weakThis,
             long canvasProperty, int paintField, int deltaValueType, float deltaValue);
     private static native void nSetDuration(long nativePtr, int duration);
+    private static native int nGetDuration(long nativePtr);
+    private static native void nSetInterpolator(long animPtr, long interpolatorPtr);
 }
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 2587ba1..5653066 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -20,6 +20,7 @@
 import android.graphics.Rect;
 import android.graphics.SurfaceTexture;
 import android.os.Trace;
+import android.util.TimeUtils;
 import android.view.Surface.OutOfResourcesException;
 import android.view.View.AttachInfo;
 
@@ -51,8 +52,6 @@
 
     private static final Rect NULL_RECT = new Rect();
 
-    private static final long NANOS_PER_MS = 1000000;
-
     // Keep in sync with DrawFrameTask.h SYNC_* flags
     // Nothing interesting to report
     private static final int SYNC_OK = 0x0;
@@ -203,7 +202,7 @@
     void draw(View view, AttachInfo attachInfo, HardwareDrawCallbacks callbacks, Rect dirty) {
         attachInfo.mIgnoreDirtyState = true;
         long frameTimeNanos = mChoreographer.getFrameTimeNanos();
-        attachInfo.mDrawingTime = frameTimeNanos / NANOS_PER_MS;
+        attachInfo.mDrawingTime = frameTimeNanos / TimeUtils.NANOS_PER_MS;
 
         updateRootDisplayList(view, callbacks);
 
diff --git a/core/java/android/view/animation/AccelerateDecelerateInterpolator.java b/core/java/android/view/animation/AccelerateDecelerateInterpolator.java
index 158c56e..ed6949a 100644
--- a/core/java/android/view/animation/AccelerateDecelerateInterpolator.java
+++ b/core/java/android/view/animation/AccelerateDecelerateInterpolator.java
@@ -19,12 +19,17 @@
 import android.content.Context;
 import android.util.AttributeSet;
 
+import com.android.internal.view.animation.HasNativeInterpolator;
+import com.android.internal.view.animation.NativeInterpolatorFactory;
+import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
+
 /**
  * An interpolator where the rate of change starts and ends slowly but
  * accelerates through the middle.
  * 
  */
-public class AccelerateDecelerateInterpolator implements Interpolator {
+@HasNativeInterpolator
+public class AccelerateDecelerateInterpolator implements Interpolator, NativeInterpolatorFactory {
     public AccelerateDecelerateInterpolator() {
     }
     
@@ -35,4 +40,10 @@
     public float getInterpolation(float input) {
         return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
     }
+
+    /** @hide */
+    @Override
+    public long createNativeInterpolator() {
+        return NativeInterpolatorFactoryHelper.createAccelerateDecelerateInterpolator();
+    }
 }
diff --git a/core/java/com/android/internal/view/animation/FallbackLUTInterpolator.java b/core/java/com/android/internal/view/animation/FallbackLUTInterpolator.java
new file mode 100644
index 0000000..aec2b7e
--- /dev/null
+++ b/core/java/com/android/internal/view/animation/FallbackLUTInterpolator.java
@@ -0,0 +1,66 @@
+/*
+ * 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 com.android.internal.view.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}
+ */
+@HasNativeInterpolator
+public class FallbackLUTInterpolator implements NativeInterpolatorFactory {
+
+    private final float mLut[];
+
+    /**
+     * Used to cache the float[] LUT for use across multiple native
+     * interpolator creation
+     */
+    public FallbackLUTInterpolator(TimeInterpolator interpolator, int duration) {
+        mLut = createLUT(interpolator, duration);
+    }
+
+    private static float[] createLUT(TimeInterpolator interpolator, int duration) {
+        long frameIntervalNanos = Choreographer.getInstance().getFrameIntervalNanos();
+        int animIntervalMs = (int) (frameIntervalNanos / TimeUtils.NANOS_PER_MS);
+        int numAnimFrames = (int) Math.ceil(duration / animIntervalMs);
+        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 NativeInterpolatorFactoryHelper.createLutInterpolator(mLut);
+    }
+
+    /**
+     * Used to create a one-shot float[] LUT & native interpolator
+     */
+    public static long createNativeInterpolator(TimeInterpolator interpolator, int duration) {
+        float[] lut = createLUT(interpolator, duration);
+        return NativeInterpolatorFactoryHelper.createLutInterpolator(lut);
+    }
+}
diff --git a/core/java/com/android/internal/view/animation/HasNativeInterpolator.java b/core/java/com/android/internal/view/animation/HasNativeInterpolator.java
new file mode 100644
index 0000000..48ea4da
--- /dev/null
+++ b/core/java/com/android/internal/view/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 com.android.internal.view.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 NativeInterpolatorFactory}
+ *
+ * The idea here is to prevent subclasses of interpolators from being treated as a
+ * NativeInterpolatorFactory, 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/core/java/com/android/internal/view/animation/NativeInterpolatorFactory.java b/core/java/com/android/internal/view/animation/NativeInterpolatorFactory.java
new file mode 100644
index 0000000..fcacd52
--- /dev/null
+++ b/core/java/com/android/internal/view/animation/NativeInterpolatorFactory.java
@@ -0,0 +1,21 @@
+/*
+ * 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 com.android.internal.view.animation;
+
+public interface NativeInterpolatorFactory {
+    long createNativeInterpolator();
+}
diff --git a/core/java/com/android/internal/view/animation/NativeInterpolatorFactoryHelper.java b/core/java/com/android/internal/view/animation/NativeInterpolatorFactoryHelper.java
new file mode 100644
index 0000000..2b25280
--- /dev/null
+++ b/core/java/com/android/internal/view/animation/NativeInterpolatorFactoryHelper.java
@@ -0,0 +1,28 @@
+/*
+ * 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 com.android.internal.view.animation;
+
+/**
+ * Static utility class for constructing native interpolators to keep the
+ * JNI simpler
+ */
+public final class NativeInterpolatorFactoryHelper {
+    private NativeInterpolatorFactoryHelper() {}
+
+    public static native long createAccelerateDecelerateInterpolator();
+    public static native long createLutInterpolator(float[] values);
+}
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 26f77b5..7dc639d 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -156,7 +156,8 @@
 	android_animation_PropertyValuesHolder.cpp \
 	com_android_internal_net_NetworkStatsFactory.cpp \
 	com_android_internal_os_Zygote.cpp \
-	com_android_internal_util_VirtualRefBasePtr.cpp
+	com_android_internal_util_VirtualRefBasePtr.cpp \
+	com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp
 
 LOCAL_C_INCLUDES += \
 	$(JNI_H_INCLUDE) \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 9941cd9..01d8814 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -131,6 +131,7 @@
 extern int register_android_view_SurfaceControl(JNIEnv* env);
 extern int register_android_view_SurfaceSession(JNIEnv* env);
 extern int register_android_view_TextureView(JNIEnv* env);
+extern int register_com_android_internal_view_animation_NativeInterpolatorFactoryHelper(JNIEnv *env);
 extern int register_android_database_CursorWindow(JNIEnv* env);
 extern int register_android_database_SQLiteConnection(JNIEnv* env);
 extern int register_android_database_SQLiteGlobal(JNIEnv* env);
@@ -1206,6 +1207,7 @@
     REG_JNI(register_android_view_SurfaceControl),
     REG_JNI(register_android_view_SurfaceSession),
     REG_JNI(register_android_view_TextureView),
+    REG_JNI(register_com_android_internal_view_animation_NativeInterpolatorFactoryHelper),
     REG_JNI(register_com_google_android_gles_jni_EGLImpl),
     REG_JNI(register_com_google_android_gles_jni_GLImpl),
     REG_JNI(register_android_opengl_jni_EGL14),
diff --git a/core/jni/android_view_RenderNodeAnimator.cpp b/core/jni/android_view_RenderNodeAnimator.cpp
index 4787d28..5733f60 100644
--- a/core/jni/android_view_RenderNodeAnimator.cpp
+++ b/core/jni/android_view_RenderNodeAnimator.cpp
@@ -132,6 +132,17 @@
     animator->setDuration(duration);
 }
 
+static jint getDuration(JNIEnv* env, jobject clazz, jlong animatorPtr) {
+    BaseAnimator* animator = reinterpret_cast<BaseAnimator*>(animatorPtr);
+    return static_cast<jint>(animator->duration());
+}
+
+static void setInterpolator(JNIEnv* env, jobject clazz, jlong animatorPtr, jlong interpolatorPtr) {
+    BaseAnimator* animator = reinterpret_cast<BaseAnimator*>(animatorPtr);
+    Interpolator* interpolator = reinterpret_cast<Interpolator*>(interpolatorPtr);
+    animator->setInterpolator(interpolator);
+}
+
 #endif
 
 // ----------------------------------------------------------------------------
@@ -146,6 +157,8 @@
     { "nCreateCanvasPropertyFloatAnimator", "(Ljava/lang/ref/WeakReference;JIF)J", (void*) createCanvasPropertyFloatAnimator },
     { "nCreateCanvasPropertyPaintAnimator", "(Ljava/lang/ref/WeakReference;JIIF)J", (void*) createCanvasPropertyPaintAnimator },
     { "nSetDuration", "(JI)V", (void*) setDuration },
+    { "nGetDuration", "(J)I", (void*) getDuration },
+    { "nSetInterpolator", "(JJ)V", (void*) setInterpolator },
 #endif
 };
 
diff --git a/core/jni/com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp b/core/jni/com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp
new file mode 100644
index 0000000..c2d3dd1
--- /dev/null
+++ b/core/jni/com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "OpenGLRenderer"
+
+#include "jni.h"
+#include <nativehelper/JNIHelp.h>
+#include <android_runtime/AndroidRuntime.h>
+
+#include <Interpolator.h>
+
+namespace android {
+
+using namespace uirenderer;
+
+#ifdef USE_OPENGL_RENDERER
+
+static jlong createAccelerateDecelerateInterpolator(JNIEnv* env, jobject clazz) {
+    return reinterpret_cast<jlong>(new AccelerateDecelerateInterpolator());
+}
+
+static jlong createLutInterpolator(JNIEnv* env, jobject clazz, jfloatArray jlut) {
+    jsize len = env->GetArrayLength(jlut);
+    if (len <= 0) {
+        return 0;
+    }
+    float* lut = new float[len];
+    env->GetFloatArrayRegion(jlut, 0, len, lut);
+    return reinterpret_cast<jlong>(new LUTInterpolator(lut, len));
+}
+
+#endif
+
+// ----------------------------------------------------------------------------
+// JNI Glue
+// ----------------------------------------------------------------------------
+
+const char* const kClassPathName = "com/android/internal/view/animation/NativeInterpolatorFactoryHelper";
+
+static JNINativeMethod gMethods[] = {
+#ifdef USE_OPENGL_RENDERER
+    { "createAccelerateDecelerateInterpolator", "()J", (void*) createAccelerateDecelerateInterpolator },
+    { "createLutInterpolator", "([F)J", (void*) createLutInterpolator },
+#endif
+};
+
+int register_com_android_internal_view_animation_NativeInterpolatorFactoryHelper(JNIEnv* env) {
+    return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
+}
+
+
+} // namespace android