Add CanvasProperty for drawCircle

Change-Id: Icbcc030f5033d2094e567d7c519b9d672f2aac1c
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 34b85d9..11948b2 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -18,6 +18,7 @@
 
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.graphics.CanvasProperty;
 import android.graphics.DrawFilter;
 import android.graphics.Matrix;
 import android.graphics.NinePatch;
@@ -889,6 +890,16 @@
             float radius, long paint);
 
     @Override
+    public void drawCircle(CanvasProperty<Float> cx, CanvasProperty<Float> cy,
+            CanvasProperty<Float> radius, CanvasProperty<Paint> paint) {
+        nDrawCircle(mRenderer, cx.getNativeContainer(), cy.getNativeContainer(),
+                radius.getNativeContainer(), paint.getNativeContainer());
+    }
+
+    private static native void nDrawCircle(long renderer, long propCx,
+            long propCy, long propRadius, long propPaint);
+
+    @Override
     public void drawColor(int color) {
         drawColor(color, PorterDuff.Mode.SRC_OVER);
     }
diff --git a/core/java/android/view/HardwareCanvas.java b/core/java/android/view/HardwareCanvas.java
index 233f846..7ec2cc6 100644
--- a/core/java/android/view/HardwareCanvas.java
+++ b/core/java/android/view/HardwareCanvas.java
@@ -18,6 +18,7 @@
 
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.graphics.CanvasProperty;
 import android.graphics.Paint;
 import android.graphics.Rect;
 
@@ -189,4 +190,7 @@
      * @hide
      */
     abstract void clearLayerUpdates();
+
+    public abstract void drawCircle(CanvasProperty<Float> cx, CanvasProperty<Float> cy,
+            CanvasProperty<Float> radius, CanvasProperty<Paint> paint);
 }
diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java
index b70ae3d..b5089b0 100644
--- a/core/java/android/view/RenderNodeAnimator.java
+++ b/core/java/android/view/RenderNodeAnimator.java
@@ -16,6 +16,8 @@
 
 package android.view;
 
+import android.graphics.CanvasProperty;
+import android.graphics.Paint;
 import android.util.SparseIntArray;
 
 import java.lang.ref.WeakReference;
@@ -26,18 +28,22 @@
 public final class RenderNodeAnimator {
 
     // Keep in sync with enum RenderProperty in Animator.h
-    private static final int TRANSLATION_X = 0;
-    private static final int TRANSLATION_Y = 1;
-    private static final int TRANSLATION_Z = 2;
-    private static final int SCALE_X = 3;
-    private static final int SCALE_Y = 4;
-    private static final int ROTATION = 5;
-    private static final int ROTATION_X = 6;
-    private static final int ROTATION_Y = 7;
-    private static final int X = 8;
-    private static final int Y = 9;
-    private static final int Z = 10;
-    private static final int ALPHA = 11;
+    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;
+
+    // Keep in sync with enum PaintFields in Animator.h
+    public static final int PAINT_STROKE_WIDTH = 0;
+    public static final int PAINT_ALPHA = 1;
 
     // ViewPropertyAnimator uses a mask for its values, we need to remap them
     // to the enum values here. RenderPropertyAnimator can't use the mask values
@@ -59,8 +65,8 @@
     }};
 
     // Keep in sync DeltaValueType in Animator.h
-    private static final int DELTA_TYPE_ABSOLUTE = 0;
-    private static final int DELTA_TYPE_DELTA = 1;
+    public static final int DELTA_TYPE_ABSOLUTE = 0;
+    public static final int DELTA_TYPE_DELTA = 1;
 
     private RenderNode mTarget;
     private long mNativePtr;
@@ -74,6 +80,19 @@
                 property, deltaType, deltaValue);
     }
 
+    public RenderNodeAnimator(CanvasProperty<Float> property, int deltaType, float deltaValue) {
+        mNativePtr = nCreateCanvasPropertyFloatAnimator(
+                new WeakReference<RenderNodeAnimator>(this),
+                property.getNativeContainer(), deltaType, deltaValue);
+    }
+
+    public RenderNodeAnimator(CanvasProperty<Paint> property, int paintField,
+            int deltaType, float deltaValue) {
+        mNativePtr = nCreateCanvasPropertyPaintAnimator(
+                new WeakReference<RenderNodeAnimator>(this),
+                property.getNativeContainer(), paintField, deltaType, deltaValue);
+    }
+
     public void start(View target) {
         mTarget = target.mRenderNode;
         mTarget.addAnimator(this);
@@ -117,6 +136,10 @@
 
     private static native long nCreateAnimator(WeakReference<RenderNodeAnimator> weakThis,
             int property, int deltaValueType, float deltaValue);
+    private static native long nCreateCanvasPropertyFloatAnimator(WeakReference<RenderNodeAnimator> weakThis,
+            long canvasProperty, int deltaValueType, float deltaValue);
+    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 void nUnref(long nativePtr);
 }
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index ee59c8a..667bf6c 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -92,6 +92,7 @@
 	android/graphics/BitmapFactory.cpp \
 	android/graphics/Camera.cpp \
 	android/graphics/Canvas.cpp \
+	android/graphics/CanvasProperty.cpp \
 	android/graphics/ColorFilter.cpp \
 	android/graphics/DrawFilter.cpp \
 	android/graphics/CreateJavaOutputStreamAdaptor.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index f964cd2..66fbb8e 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -105,6 +105,7 @@
 extern int register_android_content_XmlBlock(JNIEnv* env);
 extern int register_android_emoji_EmojiFactory(JNIEnv* env);
 extern int register_android_graphics_Canvas(JNIEnv* env);
+extern int register_android_graphics_CanvasProperty(JNIEnv* env);
 extern int register_android_graphics_ColorFilter(JNIEnv* env);
 extern int register_android_graphics_DrawFilter(JNIEnv* env);
 extern int register_android_graphics_Matrix(JNIEnv* env);
@@ -1221,6 +1222,7 @@
     REG_JNI(register_android_graphics_Camera),
     REG_JNI(register_android_graphics_CreateJavaOutputStreamAdaptor),
     REG_JNI(register_android_graphics_Canvas),
+    REG_JNI(register_android_graphics_CanvasProperty),
     REG_JNI(register_android_graphics_ColorFilter),
     REG_JNI(register_android_graphics_DrawFilter),
     REG_JNI(register_android_graphics_Interpolator),
diff --git a/core/jni/android/graphics/CanvasProperty.cpp b/core/jni/android/graphics/CanvasProperty.cpp
new file mode 100644
index 0000000..70e2db5
--- /dev/null
+++ b/core/jni/android/graphics/CanvasProperty.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 20014 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.
+ */
+
+#include "jni.h"
+#include "GraphicsJNI.h"
+#include <android_runtime/AndroidRuntime.h>
+
+#include <utils/VirtualLightRefBase.h>
+#include <CanvasProperty.h>
+
+namespace android {
+
+using namespace uirenderer;
+
+#ifdef USE_OPENGL_RENDERER
+
+static jlong incRef(VirtualLightRefBase* ptr) {
+    ptr->incStrong(0);
+    return reinterpret_cast<jlong>(ptr);
+}
+
+static jlong createFloat(JNIEnv* env, jobject clazz, jfloat initialValue) {
+    return incRef(new CanvasPropertyPrimitive(initialValue));
+}
+
+static jlong createPaint(JNIEnv* env, jobject clazz, jlong paintPtr) {
+    const SkPaint* paint = reinterpret_cast<const SkPaint*>(paintPtr);
+    return incRef(new CanvasPropertyPaint(*paint));
+}
+
+static void unref(JNIEnv* env, jobject clazz, jlong containerPtr) {
+    reinterpret_cast<VirtualLightRefBase*>(containerPtr)->decStrong(0);
+}
+
+#endif
+
+// ----------------------------------------------------------------------------
+// JNI Glue
+// ----------------------------------------------------------------------------
+
+const char* const kClassPathName = "android/graphics/CanvasProperty";
+
+static JNINativeMethod gMethods[] = {
+#ifdef USE_OPENGL_RENDERER
+    { "nCreateFloat", "(F)J", (void*) createFloat },
+    { "nCreatePaint", "(J)J", (void*) createPaint },
+    { "nUnref", "(J)V", (void*) unref },
+#endif
+};
+
+int register_android_graphics_CanvasProperty(JNIEnv* env) {
+    return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
+}
+
+}; // namespace android
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index ef5ebd0..3aa179d 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -49,6 +49,7 @@
 #include <Stencil.h>
 #include <Rect.h>
 #include <RenderNode.h>
+#include <CanvasProperty.h>
 
 #include <TextLayout.h>
 #include <TextLayoutCache.h>
@@ -544,6 +545,16 @@
     renderer->drawCircle(x, y, radius, paint);
 }
 
+static void android_view_GLES20Canvas_drawCircleProps(JNIEnv* env, jobject clazz,
+        jlong rendererPtr, jlong xPropPtr, jlong yPropPtr, jlong radiusPropPtr, jlong paintPropPtr) {
+    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
+    CanvasPropertyPrimitive* xProp = reinterpret_cast<CanvasPropertyPrimitive*>(xPropPtr);
+    CanvasPropertyPrimitive* yProp = reinterpret_cast<CanvasPropertyPrimitive*>(yPropPtr);
+    CanvasPropertyPrimitive* radiusProp = reinterpret_cast<CanvasPropertyPrimitive*>(radiusPropPtr);
+    CanvasPropertyPaint* paintProp = reinterpret_cast<CanvasPropertyPaint*>(paintPropPtr);
+    renderer->drawCircle(xProp, yProp, radiusProp, paintProp);
+}
+
 static void android_view_GLES20Canvas_drawOval(JNIEnv* env, jobject clazz,
         jlong rendererPtr, jfloat left, jfloat top, jfloat right, jfloat bottom,
         jlong paintPtr) {
@@ -1041,6 +1052,7 @@
     { "nDrawRects",         "(J[FIJ)V",        (void*) android_view_GLES20Canvas_drawRects },
     { "nDrawRoundRect",     "(JFFFFFFJ)V",     (void*) android_view_GLES20Canvas_drawRoundRect },
     { "nDrawCircle",        "(JFFFJ)V",        (void*) android_view_GLES20Canvas_drawCircle },
+    { "nDrawCircle",        "(JJJJJ)V",        (void*) android_view_GLES20Canvas_drawCircleProps },
     { "nDrawOval",          "(JFFFFJ)V",       (void*) android_view_GLES20Canvas_drawOval },
     { "nDrawArc",           "(JFFFFFFZJ)V",    (void*) android_view_GLES20Canvas_drawArc },
     { "nDrawPoints",        "(J[FIIJ)V",       (void*) android_view_GLES20Canvas_drawPoints },
diff --git a/core/jni/android_view_RenderNodeAnimator.cpp b/core/jni/android_view_RenderNodeAnimator.cpp
index b92c992..3be013b 100644
--- a/core/jni/android_view_RenderNodeAnimator.cpp
+++ b/core/jni/android_view_RenderNodeAnimator.cpp
@@ -16,8 +16,6 @@
 
 #define LOG_TAG "OpenGLRenderer"
 
-#include "android_view_RenderNodeAnimator.h"
-
 #include "jni.h"
 #include "GraphicsJNI.h"
 #include <nativehelper/JNIHelp.h>
@@ -47,46 +45,93 @@
     return env;
 }
 
-RenderNodeAnimator::RenderNodeAnimator(JNIEnv* env, jobject weakThis,
-                RenderProperty property, DeltaValueType deltaType, float delta)
-        : RenderPropertyAnimator(property, deltaType, delta) {
-    mWeakThis = env->NewGlobalRef(weakThis);
-    env->GetJavaVM(&mJvm);
-}
+class AnimationListenerBridge : public AnimationListener {
+public:
+    // This holds a strong reference to a Java WeakReference<T> object. This avoids
+    // cyclic-references-of-doom. If you think "I know, just use NewWeakGlobalRef!"
+    // then you end up with basically a PhantomReference, which is totally not
+    // what we want.
+    AnimationListenerBridge(JNIEnv* env, jobject weakThis) {
+        mWeakThis = env->NewGlobalRef(weakThis);
+        env->GetJavaVM(&mJvm);
+    }
 
-RenderNodeAnimator::~RenderNodeAnimator() {
-    JNIEnv* env = getEnv(mJvm);
-    env->DeleteGlobalRef(mWeakThis);
-    mWeakThis = NULL;
-}
+    virtual ~AnimationListenerBridge() {
+        JNIEnv* env = getEnv(mJvm);
+        env->DeleteGlobalRef(mWeakThis);
+        mWeakThis = NULL;
+    }
 
-void RenderNodeAnimator::callOnFinished() {
-    JNIEnv* env = getEnv(mJvm);
-    env->CallStaticVoidMethod(
-            gRenderNodeAnimatorClassInfo.clazz,
-            gRenderNodeAnimatorClassInfo.callOnFinished,
-            mWeakThis);
-}
+    virtual void onAnimationFinished(BaseAnimator*) {
+        JNIEnv* env = getEnv(mJvm);
+        env->CallStaticVoidMethod(
+                gRenderNodeAnimatorClassInfo.clazz,
+                gRenderNodeAnimatorClassInfo.callOnFinished,
+                mWeakThis);
+    }
 
-static jlong createAnimator(JNIEnv* env, jobject clazz, jobject weakThis,
-        jint property, jint deltaType, jfloat deltaValue) {
-    LOG_ALWAYS_FATAL_IF(property < 0 || property > RenderNodeAnimator::ALPHA,
+private:
+    JavaVM* mJvm;
+    jobject mWeakThis;
+};
+
+static inline RenderPropertyAnimator::RenderProperty toRenderProperty(jint property) {
+    LOG_ALWAYS_FATAL_IF(property < 0 || property > RenderPropertyAnimator::ALPHA,
             "Invalid property %d", property);
+    return static_cast<RenderPropertyAnimator::RenderProperty>(property);
+}
+
+static inline RenderPropertyAnimator::DeltaValueType toDeltaType(jint deltaType) {
     LOG_ALWAYS_FATAL_IF(deltaType != RenderPropertyAnimator::DELTA
             && deltaType != RenderPropertyAnimator::ABSOLUTE,
             "Invalid delta type %d", deltaType);
+    return static_cast<RenderPropertyAnimator::DeltaValueType>(deltaType);
+}
 
-    RenderNodeAnimator* animator = new RenderNodeAnimator(env, weakThis,
-            static_cast<RenderPropertyAnimator::RenderProperty>(property),
-            static_cast<RenderPropertyAnimator::DeltaValueType>(deltaType),
-            deltaValue);
+static inline CanvasPropertyPaintAnimator::PaintField toPaintField(jint field) {
+    LOG_ALWAYS_FATAL_IF(field < 0
+            || field > CanvasPropertyPaintAnimator::ALPHA,
+            "Invalid paint field %d", field);
+    return static_cast<CanvasPropertyPaintAnimator::PaintField>(field);
+}
+
+static jlong createAnimator(JNIEnv* env, jobject clazz, jobject weakThis,
+        jint propertyRaw, jint deltaTypeRaw, jfloat deltaValue) {
+    RenderPropertyAnimator::RenderProperty property = toRenderProperty(propertyRaw);
+    RenderPropertyAnimator::DeltaValueType deltaType = toDeltaType(deltaTypeRaw);
+
+    BaseAnimator* animator = new RenderPropertyAnimator(property, deltaType, deltaValue);
     animator->incStrong(0);
+    animator->setListener(new AnimationListenerBridge(env, weakThis));
+    return reinterpret_cast<jlong>( animator );
+}
+
+static jlong createCanvasPropertyFloatAnimator(JNIEnv* env, jobject clazz,
+        jobject weakThis, jlong canvasPropertyPtr, jint deltaTypeRaw, jfloat deltaValue) {
+    RenderPropertyAnimator::DeltaValueType deltaType = toDeltaType(deltaTypeRaw);
+    CanvasPropertyPrimitive* canvasProperty = reinterpret_cast<CanvasPropertyPrimitive*>(canvasPropertyPtr);
+    BaseAnimator* animator = new CanvasPropertyPrimitiveAnimator(canvasProperty, deltaType, deltaValue);
+    animator->incStrong(0);
+    animator->setListener(new AnimationListenerBridge(env, weakThis));
+    return reinterpret_cast<jlong>( animator );
+}
+
+static jlong createCanvasPropertyPaintAnimator(JNIEnv* env, jobject clazz,
+        jobject weakThis, jlong canvasPropertyPtr, jint paintFieldRaw,
+        jint deltaTypeRaw, jfloat deltaValue) {
+    RenderPropertyAnimator::DeltaValueType deltaType = toDeltaType(deltaTypeRaw);
+    CanvasPropertyPaint* canvasProperty = reinterpret_cast<CanvasPropertyPaint*>(canvasPropertyPtr);
+    CanvasPropertyPaintAnimator::PaintField paintField = toPaintField(paintFieldRaw);
+    BaseAnimator* animator = new CanvasPropertyPaintAnimator(
+            canvasProperty, paintField, deltaType, deltaValue);
+    animator->incStrong(0);
+    animator->setListener(new AnimationListenerBridge(env, weakThis));
     return reinterpret_cast<jlong>( animator );
 }
 
 static void setDuration(JNIEnv* env, jobject clazz, jlong animatorPtr, jint duration) {
     LOG_ALWAYS_FATAL_IF(duration < 0, "Duration cannot be negative");
-    RenderNodeAnimator* animator = reinterpret_cast<RenderNodeAnimator*>(animatorPtr);
+    BaseAnimator* animator = reinterpret_cast<BaseAnimator*>(animatorPtr);
     animator->setDuration(duration);
 }
 
@@ -106,6 +151,8 @@
 static JNINativeMethod gMethods[] = {
 #ifdef USE_OPENGL_RENDERER
     { "nCreateAnimator", "(Ljava/lang/ref/WeakReference;IIF)J", (void*) createAnimator },
+    { "nCreateCanvasPropertyFloatAnimator", "(Ljava/lang/ref/WeakReference;JIF)J", (void*) createCanvasPropertyFloatAnimator },
+    { "nCreateCanvasPropertyPaintAnimator", "(Ljava/lang/ref/WeakReference;JIIF)J", (void*) createCanvasPropertyPaintAnimator },
     { "nSetDuration", "(JI)V", (void*) setDuration },
     { "nUnref", "(J)V", (void*) unref },
 #endif
diff --git a/core/jni/android_view_RenderNodeAnimator.h b/core/jni/android_view_RenderNodeAnimator.h
deleted file mode 100644
index 760ca91..0000000
--- a/core/jni/android_view_RenderNodeAnimator.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2012 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.
- */
-
-#include "jni.h"
-
-#ifdef USE_OPENGL_RENDERER
-
-#include <Animator.h>
-
-namespace android {
-
-class RenderNodeAnimator : public uirenderer::RenderPropertyAnimator {
-public:
-    RenderNodeAnimator(JNIEnv* env, jobject callbackObject,
-            RenderProperty property, DeltaValueType deltaType, float delta);
-    virtual ~RenderNodeAnimator();
-
-    void callOnFinished();
-
-private:
-    JavaVM* mJvm;
-    jobject mWeakThis;
-};
-
-}
-
-#endif
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 58fc1e1..564c9a6 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -26,7 +26,7 @@
 #include <android_runtime/android_view_Surface.h>
 #include <system/window.h>
 
-#include "android_view_RenderNodeAnimator.h"
+#include <Animator.h>
 #include <RenderNode.h>
 #include <renderthread/RenderProxy.h>
 #include <renderthread/RenderTask.h>
@@ -67,26 +67,34 @@
     jobject mRunnable;
 };
 
+class OnFinishedEvent {
+public:
+    OnFinishedEvent(BaseAnimator* animator, AnimationListener* listener)
+            : animator(animator), listener(listener) {}
+    sp<BaseAnimator> animator;
+    sp<AnimationListener> listener;
+};
+
 class InvokeAnimationListeners : public MessageHandler {
 public:
-    InvokeAnimationListeners(std::vector< sp<RenderNodeAnimator> >& animators) {
-        mAnimators.swap(animators);
+    InvokeAnimationListeners(std::vector<OnFinishedEvent>& events) {
+        mOnFinishedEvents.swap(events);
     }
 
-    static void callOnFinished(const sp<RenderNodeAnimator>& animator) {
-        animator->callOnFinished();
+    static void callOnFinished(OnFinishedEvent& event) {
+        event.listener->onAnimationFinished(event.animator.get());
     }
 
     virtual void handleMessage(const Message& message) {
-        std::for_each(mAnimators.begin(), mAnimators.end(), callOnFinished);
-        mAnimators.clear();
+        std::for_each(mOnFinishedEvents.begin(), mOnFinishedEvents.end(), callOnFinished);
+        mOnFinishedEvents.clear();
     }
 
 private:
-    std::vector< sp<RenderNodeAnimator> > mAnimators;
+    std::vector<OnFinishedEvent> mOnFinishedEvents;
 };
 
-class RootRenderNode : public RenderNode, public AnimationListener {
+class RootRenderNode : public RenderNode, public AnimationHook {
 public:
     RootRenderNode() : RenderNode() {
         mLooper = Looper::getForThread();
@@ -96,27 +104,27 @@
 
     virtual ~RootRenderNode() {}
 
-    void onAnimationFinished(const sp<RenderPropertyAnimator>& animator) {
-        mFinishedAnimators.push_back(
-                reinterpret_cast<RenderNodeAnimator*>(animator.get()));
+    virtual void callOnFinished(BaseAnimator* animator, AnimationListener* listener) {
+        OnFinishedEvent event(animator, listener);
+        mOnFinishedEvents.push_back(event);
     }
 
     virtual void prepareTree(TreeInfo& info) {
-        info.animationListener = this;
+        info.animationHook = this;
         RenderNode::prepareTree(info);
-        info.animationListener = NULL;
+        info.animationHook = NULL;
 
         // post all the finished stuff
-        if (mFinishedAnimators.size()) {
+        if (mOnFinishedEvents.size()) {
             sp<InvokeAnimationListeners> message
-                    = new InvokeAnimationListeners(mFinishedAnimators);
+                    = new InvokeAnimationListeners(mOnFinishedEvents);
             mLooper->sendMessage(message, 0);
         }
     }
 
 private:
     sp<Looper> mLooper;
-    std::vector< sp<RenderNodeAnimator> > mFinishedAnimators;
+    std::vector<OnFinishedEvent> mOnFinishedEvents;
 };
 
 static void android_view_ThreadedRenderer_postToRenderThread(JNIEnv* env, jobject clazz,
diff --git a/graphics/java/android/graphics/CanvasProperty.java b/graphics/java/android/graphics/CanvasProperty.java
new file mode 100644
index 0000000..99ea9b1
--- /dev/null
+++ b/graphics/java/android/graphics/CanvasProperty.java
@@ -0,0 +1,56 @@
+/*
+ * 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;
+
+/**
+ * TODO: Make public?
+ * @hide
+ */
+public final class CanvasProperty<T> {
+    private long mNativeContainer;
+
+    public static CanvasProperty<Float> createFloat(float initialValue) {
+        return new CanvasProperty<Float>(nCreateFloat(initialValue));
+    }
+
+    public static CanvasProperty<Paint> createPaint(Paint initialValue) {
+        return new CanvasProperty<Paint>(nCreatePaint(initialValue.mNativePaint));
+    }
+
+    private CanvasProperty(long nativeContainer) {
+        mNativeContainer = nativeContainer;
+    }
+
+    /** @hide */
+    public long getNativeContainer() {
+        return mNativeContainer;
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            nUnref(mNativeContainer);
+            mNativeContainer = 0;
+        } finally {
+            super.finalize();
+        }
+    }
+
+    private static native long nCreateFloat(float initialValue);
+    private static native long nCreatePaint(long initialValuePaintPtr);
+    private static native void nUnref(long ptr);
+}
diff --git a/libs/hwui/Animator.cpp b/libs/hwui/Animator.cpp
index ee16586..6a3003e 100644
--- a/libs/hwui/Animator.cpp
+++ b/libs/hwui/Animator.cpp
@@ -20,130 +20,13 @@
 
 #include <set>
 
+#include "RenderNode.h"
 #include "RenderProperties.h"
 
 namespace android {
 namespace uirenderer {
 
 /************************************************************
- *  Private header
- ************************************************************/
-
-typedef void (RenderProperties::*SetFloatProperty)(float value);
-typedef float (RenderProperties::*GetFloatProperty)() const;
-
-struct PropertyAccessors {
-    GetFloatProperty getter;
-    SetFloatProperty setter;
-};
-
-// Maps RenderProperty enum to accessors
-static const PropertyAccessors PROPERTY_ACCESSOR_LUT[] = {
-    {&RenderProperties::getTranslationX, &RenderProperties::setTranslationX },
-    {&RenderProperties::getTranslationY, &RenderProperties::setTranslationY },
-    {&RenderProperties::getTranslationZ, &RenderProperties::setTranslationZ },
-    {&RenderProperties::getScaleX, &RenderProperties::setScaleX },
-    {&RenderProperties::getScaleY, &RenderProperties::setScaleY },
-    {&RenderProperties::getRotation, &RenderProperties::setRotation },
-    {&RenderProperties::getRotationX, &RenderProperties::setRotationX },
-    {&RenderProperties::getRotationY, &RenderProperties::setRotationY },
-    {&RenderProperties::getX, &RenderProperties::setX },
-    {&RenderProperties::getY, &RenderProperties::setY },
-    {&RenderProperties::getZ, &RenderProperties::setZ },
-    {&RenderProperties::getAlpha, &RenderProperties::setAlpha },
-};
-
-// Helper class to contain generic animator helpers
-class BaseAnimator {
-public:
-    BaseAnimator();
-    virtual ~BaseAnimator();
-
-    void setInterpolator(Interpolator* interpolator);
-    void setDuration(nsecs_t durationInMs);
-
-    bool isFinished() { return mPlayState == FINISHED; }
-
-protected:
-    // This is the main animation entrypoint that subclasses should call
-    // to generate the onAnimation* lifecycle events
-    // Returns true if the animation has finished, false otherwise
-    bool animateFrame(nsecs_t frameTime);
-
-    // Called when PlayState switches from PENDING to RUNNING
-    virtual void onAnimationStarted() {}
-    virtual void onAnimationUpdated(float fraction) = 0;
-    virtual void onAnimationFinished() {}
-
-private:
-    enum PlayState {
-        PENDING,
-        RUNNING,
-        FINISHED,
-    };
-
-    Interpolator* mInterpolator;
-    PlayState mPlayState;
-    long mStartTime;
-    long mDuration;
-};
-
-// Hide the base classes & private bits from the exported RenderPropertyAnimator
-// in this Impl class so that subclasses of RenderPropertyAnimator don't require
-// knowledge of the inner guts but only the public virtual methods.
-// Animates a single property
-class RenderPropertyAnimatorImpl : public BaseAnimator {
-public:
-    RenderPropertyAnimatorImpl(GetFloatProperty getter, SetFloatProperty setter,
-            RenderPropertyAnimator::DeltaValueType deltaType, float delta);
-    ~RenderPropertyAnimatorImpl();
-
-    bool animate(RenderProperties* target, TreeInfo& info);
-
-protected:
-    virtual void onAnimationStarted();
-    virtual void onAnimationUpdated(float fraction);
-
-private:
-    // mTarget is only valid inside animate()
-    RenderProperties* mTarget;
-    GetFloatProperty mGetter;
-    SetFloatProperty mSetter;
-
-    RenderPropertyAnimator::DeltaValueType mDeltaValueType;
-    float mDeltaValue;
-    float mFromValue;
-};
-
-RenderPropertyAnimator::RenderPropertyAnimator(RenderProperty property,
-        DeltaValueType deltaType, float deltaValue) {
-    PropertyAccessors pa = PROPERTY_ACCESSOR_LUT[property];
-    mImpl = new RenderPropertyAnimatorImpl(pa.getter, pa.setter, deltaType, deltaValue);
-}
-
-RenderPropertyAnimator::~RenderPropertyAnimator() {
-    delete mImpl;
-    mImpl = NULL;
-}
-
-void RenderPropertyAnimator::setInterpolator(Interpolator* interpolator) {
-    mImpl->setInterpolator(interpolator);
-}
-
-void RenderPropertyAnimator::setDuration(nsecs_t durationInMs) {
-    mImpl->setDuration(durationInMs);
-}
-
-bool RenderPropertyAnimator::isFinished() {
-    return mImpl->isFinished();
-}
-
-bool RenderPropertyAnimator::animate(RenderProperties* target, TreeInfo& info) {
-    return mImpl->animate(target, info);
-}
-
-
-/************************************************************
  *  Base animator
  ************************************************************/
 
@@ -168,10 +51,10 @@
     mDuration = duration;
 }
 
-bool BaseAnimator::animateFrame(nsecs_t frameTime) {
+bool BaseAnimator::animateFrame(TreeInfo& info) {
     if (mPlayState == PENDING) {
         mPlayState = RUNNING;
-        mStartTime = frameTime;
+        mStartTime = info.frameTimeMs;
         // No interpolator was set, use the default
         if (!mInterpolator) {
             setInterpolator(Interpolator::createDefaultInterpolator());
@@ -181,7 +64,7 @@
 
     float fraction = 1.0f;
     if (mPlayState == RUNNING) {
-        fraction = mDuration > 0 ? (float)(frameTime - mStartTime) / mDuration : 1.0f;
+        fraction = mDuration > 0 ? (float)(info.frameTimeMs - mStartTime) / mDuration : 1.0f;
         if (fraction >= 1.0f) {
             fraction = 1.0f;
             mPlayState = FINISHED;
@@ -192,48 +75,140 @@
 
     if (mPlayState == FINISHED) {
         onAnimationFinished();
+        callOnFinishedListener(info);
         return true;
     }
     return false;
 }
 
+void BaseAnimator::callOnFinishedListener(TreeInfo& info) {
+    if (mListener.get()) {
+        if (!info.animationHook) {
+            mListener->onAnimationFinished(this);
+        } else {
+            info.animationHook->callOnFinished(this, mListener.get());
+        }
+    }
+}
+
+/************************************************************
+ *  BaseRenderNodeAnimator
+ ************************************************************/
+
+BaseRenderNodeAnimator::BaseRenderNodeAnimator(
+                BaseRenderNodeAnimator::DeltaValueType deltaType, float delta)
+        : mTarget(0)
+        , mDeltaValueType(deltaType)
+        , mDeltaValue(delta)
+        , mFromValue(-1) {
+}
+
+bool BaseRenderNodeAnimator::animate(RenderNode* target, TreeInfo& info) {
+    mTarget = target;
+    bool finished = animateFrame(info);
+    mTarget = NULL;
+    return finished;
+}
+
+void BaseRenderNodeAnimator::onAnimationStarted() {
+    mFromValue = getValue();
+
+    if (mDeltaValueType == BaseRenderNodeAnimator::ABSOLUTE) {
+        mDeltaValue = (mDeltaValue - mFromValue);
+        mDeltaValueType = BaseRenderNodeAnimator::DELTA;
+    }
+}
+
+void BaseRenderNodeAnimator::onAnimationUpdated(float fraction) {
+    float value = mFromValue + (mDeltaValue * fraction);
+    setValue(value);
+}
+
 /************************************************************
  *  RenderPropertyAnimator
  ************************************************************/
 
-RenderPropertyAnimatorImpl::RenderPropertyAnimatorImpl(
-                GetFloatProperty getter, SetFloatProperty setter,
-                RenderPropertyAnimator::DeltaValueType deltaType, float delta)
-        : mTarget(0)
-        , mGetter(getter)
-        , mSetter(setter)
-        , mDeltaValueType(deltaType)
-        , mDeltaValue(delta)
-        , mFromValue(-1) {
+// Maps RenderProperty enum to accessors
+const RenderPropertyAnimator::PropertyAccessors RenderPropertyAnimator::PROPERTY_ACCESSOR_LUT[] = {
+    {&RenderProperties::getTranslationX, &RenderProperties::setTranslationX },
+    {&RenderProperties::getTranslationY, &RenderProperties::setTranslationY },
+    {&RenderProperties::getTranslationZ, &RenderProperties::setTranslationZ },
+    {&RenderProperties::getScaleX, &RenderProperties::setScaleX },
+    {&RenderProperties::getScaleY, &RenderProperties::setScaleY },
+    {&RenderProperties::getRotation, &RenderProperties::setRotation },
+    {&RenderProperties::getRotationX, &RenderProperties::setRotationX },
+    {&RenderProperties::getRotationY, &RenderProperties::setRotationY },
+    {&RenderProperties::getX, &RenderProperties::setX },
+    {&RenderProperties::getY, &RenderProperties::setY },
+    {&RenderProperties::getZ, &RenderProperties::setZ },
+    {&RenderProperties::getAlpha, &RenderProperties::setAlpha },
+};
+
+RenderPropertyAnimator::RenderPropertyAnimator(RenderProperty property,
+                DeltaValueType deltaType, float deltaValue)
+        : BaseRenderNodeAnimator(deltaType, deltaValue)
+        , mPropertyAccess(PROPERTY_ACCESSOR_LUT[property]) {
 }
 
-RenderPropertyAnimatorImpl::~RenderPropertyAnimatorImpl() {
+float RenderPropertyAnimator::getValue() const {
+    return (target()->animatorProperties().*mPropertyAccess.getter)();
 }
 
-bool RenderPropertyAnimatorImpl::animate(RenderProperties* target, TreeInfo& info) {
-    mTarget = target;
-    bool finished = animateFrame(info.frameTimeMs);
-    mTarget = NULL;
-    return finished;
+void RenderPropertyAnimator::setValue(float value) {
+    (target()->animatorProperties().*mPropertyAccess.setter)(value);
 }
 
-void RenderPropertyAnimatorImpl::onAnimationStarted() {
-    mFromValue = (mTarget->*mGetter)();
+/************************************************************
+ *  CanvasPropertyPrimitiveAnimator
+ ************************************************************/
 
-    if (mDeltaValueType == RenderPropertyAnimator::ABSOLUTE) {
-        mDeltaValue = (mDeltaValue - mFromValue);
-        mDeltaValueType = RenderPropertyAnimator::DELTA;
+CanvasPropertyPrimitiveAnimator::CanvasPropertyPrimitiveAnimator(
+                CanvasPropertyPrimitive* property, DeltaValueType deltaType, float deltaValue)
+        : BaseRenderNodeAnimator(deltaType, deltaValue)
+        , mProperty(property) {
+}
+
+float CanvasPropertyPrimitiveAnimator::getValue() const {
+    return mProperty->value;
+}
+
+void CanvasPropertyPrimitiveAnimator::setValue(float value) {
+    mProperty->value = value;
+}
+
+/************************************************************
+ *  CanvasPropertySkPaintAnimator
+ ************************************************************/
+
+CanvasPropertyPaintAnimator::CanvasPropertyPaintAnimator(
+                CanvasPropertyPaint* property, PaintField field,
+                DeltaValueType deltaType, float deltaValue)
+        : BaseRenderNodeAnimator(deltaType, deltaValue)
+        , mProperty(property)
+        , mField(field) {
+}
+
+float CanvasPropertyPaintAnimator::getValue() const {
+    switch (mField) {
+    case STROKE_WIDTH:
+        return mProperty->value.getStrokeWidth();
+    case ALPHA:
+        return mProperty->value.getAlpha();
     }
+    LOG_ALWAYS_FATAL("Unknown field %d", (int) mField);
+    return -1;
 }
 
-void RenderPropertyAnimatorImpl::onAnimationUpdated(float fraction) {
-    float value = mFromValue + (mDeltaValue * fraction);
-    (mTarget->*mSetter)(value);
+void CanvasPropertyPaintAnimator::setValue(float value) {
+    switch (mField) {
+    case STROKE_WIDTH:
+        mProperty->value.setStrokeWidth(value);
+        return;
+    case ALPHA:
+        mProperty->value.setAlpha(value);
+        return;
+    }
+    LOG_ALWAYS_FATAL("Unknown field %d", (int) mField);
 }
 
 } /* namespace uirenderer */
diff --git a/libs/hwui/Animator.h b/libs/hwui/Animator.h
index 1c8361b..0b074cc 100644
--- a/libs/hwui/Animator.h
+++ b/libs/hwui/Animator.h
@@ -17,18 +17,72 @@
 #define ANIMATOR_H
 
 #include <cutils/compiler.h>
+#include <utils/StrongPointer.h>
 
+#include "CanvasProperty.h"
 #include "Interpolator.h"
 #include "TreeInfo.h"
+#include "utils/Macros.h"
 #include "utils/VirtualLightRefBase.h"
 
 namespace android {
 namespace uirenderer {
 
+class RenderNode;
 class RenderProperties;
-class RenderPropertyAnimatorImpl;
 
-class RenderPropertyAnimator : public VirtualLightRefBase {
+class AnimationListener : public VirtualLightRefBase {
+public:
+    ANDROID_API virtual void onAnimationFinished(BaseAnimator*) = 0;
+protected:
+    ANDROID_API virtual ~AnimationListener() {}
+};
+
+// Helper class to contain generic animator helpers
+class BaseAnimator : public VirtualLightRefBase {
+    PREVENT_COPY_AND_ASSIGN(BaseAnimator);
+public:
+
+    ANDROID_API void setInterpolator(Interpolator* interpolator);
+    ANDROID_API void setDuration(nsecs_t durationInMs);
+    ANDROID_API void setListener(AnimationListener* listener) {
+        mListener = listener;
+    }
+
+    bool isFinished() { return mPlayState == FINISHED; }
+
+protected:
+    BaseAnimator();
+    virtual ~BaseAnimator();
+
+    // This is the main animation entrypoint that subclasses should call
+    // to generate the onAnimation* lifecycle events
+    // Returns true if the animation has finished, false otherwise
+    bool animateFrame(TreeInfo& info);
+
+    // Called when PlayState switches from PENDING to RUNNING
+    virtual void onAnimationStarted() {}
+    virtual void onAnimationUpdated(float fraction) = 0;
+    virtual void onAnimationFinished() {}
+
+private:
+    void callOnFinishedListener(TreeInfo& info);
+
+    enum PlayState {
+        PENDING,
+        RUNNING,
+        FINISHED,
+    };
+
+    Interpolator* mInterpolator;
+    PlayState mPlayState;
+    long mStartTime;
+    long mDuration;
+
+   sp<AnimationListener> mListener;
+};
+
+class BaseRenderNodeAnimator : public BaseAnimator {
 public:
     // Since the UI thread doesn't necessarily know what the current values
     // actually are and thus can't do the calculations, this is used to inform
@@ -43,6 +97,29 @@
         DELTA,
     };
 
+    bool animate(RenderNode* target, TreeInfo& info);
+
+protected:
+    BaseRenderNodeAnimator(DeltaValueType deltaType, float deltaValue);
+
+    RenderNode* target() const { return mTarget; }
+    virtual float getValue() const = 0;
+    virtual void setValue(float value) = 0;
+
+private:
+    virtual void onAnimationStarted();
+    virtual void onAnimationUpdated(float fraction);
+
+    // mTarget is only valid inside animate()
+    RenderNode* mTarget;
+
+    BaseRenderNodeAnimator::DeltaValueType mDeltaValueType;
+    float mDeltaValue;
+    float mFromValue;
+};
+
+class RenderPropertyAnimator : public BaseRenderNodeAnimator {
+public:
     enum RenderProperty {
         TRANSLATION_X = 0,
         TRANSLATION_Y,
@@ -58,19 +135,53 @@
         ALPHA,
     };
 
-    ANDROID_API void setInterpolator(Interpolator* interpolator);
-    ANDROID_API void setDuration(nsecs_t durationInMs);
-    ANDROID_API bool isFinished();
-
-    bool animate(RenderProperties* target, TreeInfo& info);
+    ANDROID_API RenderPropertyAnimator(RenderProperty property,
+                DeltaValueType deltaType, float deltaValue);
 
 protected:
-    ANDROID_API RenderPropertyAnimator(RenderProperty property, DeltaValueType deltaType,
-            float deltaValue);
-    ANDROID_API virtual ~RenderPropertyAnimator();
+    ANDROID_API virtual float getValue() const;
+    ANDROID_API virtual void setValue(float value);
 
 private:
-    RenderPropertyAnimatorImpl* mImpl;
+    typedef void (RenderProperties::*SetFloatProperty)(float value);
+    typedef float (RenderProperties::*GetFloatProperty)() const;
+
+    struct PropertyAccessors {
+        GetFloatProperty getter;
+        SetFloatProperty setter;
+    };
+
+    PropertyAccessors mPropertyAccess;
+
+    static const PropertyAccessors PROPERTY_ACCESSOR_LUT[];
+};
+
+class CanvasPropertyPrimitiveAnimator : public BaseRenderNodeAnimator {
+public:
+    ANDROID_API CanvasPropertyPrimitiveAnimator(CanvasPropertyPrimitive* property,
+            DeltaValueType deltaType, float deltaValue);
+protected:
+    ANDROID_API virtual float getValue() const;
+    ANDROID_API virtual void setValue(float value);
+private:
+    sp<CanvasPropertyPrimitive> mProperty;
+};
+
+class CanvasPropertyPaintAnimator : public BaseRenderNodeAnimator {
+public:
+    enum PaintField {
+        STROKE_WIDTH = 0,
+        ALPHA,
+    };
+
+    ANDROID_API CanvasPropertyPaintAnimator(CanvasPropertyPaint* property,
+            PaintField field, DeltaValueType deltaType, float deltaValue);
+protected:
+    ANDROID_API virtual float getValue() const;
+    ANDROID_API virtual void setValue(float value);
+private:
+    sp<CanvasPropertyPaint> mProperty;
+    PaintField mField;
 };
 
 } /* namespace uirenderer */
diff --git a/libs/hwui/CanvasProperty.h b/libs/hwui/CanvasProperty.h
new file mode 100644
index 0000000..2e1d176
--- /dev/null
+++ b/libs/hwui/CanvasProperty.h
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+#ifndef CANVASPROPERTY_H
+#define CANVASPROPERTY_H
+
+#include "utils/Macros.h"
+#include "utils/VirtualLightRefBase.h"
+
+#include <SkPaint.h>
+
+namespace android {
+namespace uirenderer {
+
+class CanvasPropertyPrimitive : public VirtualLightRefBase {
+    PREVENT_COPY_AND_ASSIGN(CanvasPropertyPrimitive);
+public:
+    CanvasPropertyPrimitive(float initialValue) : value(initialValue) {}
+
+    float value;
+};
+
+class CanvasPropertyPaint : public VirtualLightRefBase {
+    PREVENT_COPY_AND_ASSIGN(CanvasPropertyPaint);
+public:
+    CanvasPropertyPaint(const SkPaint& initialValue) : value(initialValue) {}
+
+    SkPaint value;
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* CANVASPROPERTY_H */
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
index fe70d13..eaeb772 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -140,6 +140,14 @@
     void addChild(DrawDisplayListOp* childOp);
     const Vector<DrawDisplayListOp*>& children() { return mChildren; }
 
+    void refProperty(CanvasPropertyPrimitive* prop) {
+        mReferenceHolders.push(prop);
+    }
+
+    void refProperty(CanvasPropertyPaint* prop) {
+        mReferenceHolders.push(prop);
+    }
+
 private:
     Vector< sp<VirtualLightRefBase> > mReferenceHolders;
 
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index 6dfb918..ce92beb 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -1198,6 +1198,27 @@
     float mRadius;
 };
 
+class DrawCirclePropsOp : public DrawOp {
+public:
+    DrawCirclePropsOp(float* x, float* y, float* radius, const SkPaint* paint)
+            : DrawOp(paint), mX(x), mY(y), mRadius(radius) {}
+
+    virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
+        return renderer.drawCircle(*mX, *mY, *mRadius, getPaint(renderer));
+    }
+
+    virtual void output(int level, uint32_t logFlags) const {
+        OP_LOG("Draw Circle Props x %p, y %p, r %p", mX, mY, mRadius);
+    }
+
+    virtual const char* name() { return "DrawCircleProps"; }
+
+private:
+    float* mX;
+    float* mY;
+    float* mRadius;
+};
+
 class DrawOvalOp : public DrawStrokableOp {
 public:
     DrawOvalOp(float left, float top, float right, float bottom, const SkPaint* paint)
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index e36d975..8afd106 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -299,6 +299,17 @@
     return DrawGlInfo::kStatusDone;
 }
 
+status_t DisplayListRenderer::drawCircle(CanvasPropertyPrimitive* x, CanvasPropertyPrimitive* y,
+        CanvasPropertyPrimitive* radius, CanvasPropertyPaint* paint) {
+    mDisplayListData->refProperty(x);
+    mDisplayListData->refProperty(y);
+    mDisplayListData->refProperty(radius);
+    mDisplayListData->refProperty(paint);
+    addDrawOp(new (alloc()) DrawCirclePropsOp(&x->value, &y->value,
+            &radius->value, &paint->value));
+    return DrawGlInfo::kStatusDone;
+}
+
 status_t DisplayListRenderer::drawOval(float left, float top, float right, float bottom,
         const SkPaint* paint) {
     paint = refPaint(paint);
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index 04c5a73..25e78c1 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -135,6 +135,8 @@
     virtual status_t drawRoundRect(float left, float top, float right, float bottom,
             float rx, float ry, const SkPaint* paint);
     virtual status_t drawCircle(float x, float y, float radius, const SkPaint* paint);
+    virtual status_t drawCircle(CanvasPropertyPrimitive* x, CanvasPropertyPrimitive* y,
+                CanvasPropertyPrimitive* radius, CanvasPropertyPaint* paint);
     virtual status_t drawOval(float left, float top, float right, float bottom,
             const SkPaint* paint);
     virtual status_t drawArc(float left, float top, float right, float bottom,
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index b49d1e1..7794abc 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -49,6 +49,7 @@
 #include "UvMapper.h"
 #include "Vertex.h"
 #include "Caches.h"
+#include "CanvasProperty.h"
 
 namespace android {
 namespace uirenderer {
@@ -200,6 +201,12 @@
     virtual status_t drawRoundRect(float left, float top, float right, float bottom,
             float rx, float ry, const SkPaint* paint);
     virtual status_t drawCircle(float x, float y, float radius, const SkPaint* paint);
+    virtual status_t drawCircle(CanvasPropertyPrimitive* x, CanvasPropertyPrimitive* y,
+            CanvasPropertyPrimitive* radius, CanvasPropertyPaint* paint) {
+        // TODO: Remove once android_view_GLES20Canvas uses DisplayListRenderer
+        // directly
+        return drawCircle(x->value, y->value, radius->value, &paint->value);
+    }
     virtual status_t drawOval(float left, float top, float right, float bottom,
             const SkPaint* paint);
     virtual status_t drawArc(float left, float top, float right, float bottom,
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 7a9c181..2c29985 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -109,7 +109,7 @@
     prepareSubTree(info, mDisplayListData);
 }
 
-static bool is_finished(const sp<RenderPropertyAnimator>& animator) {
+static bool is_finished(const sp<BaseRenderNodeAnimator>& animator) {
     return animator->isFinished();
 }
 
@@ -120,7 +120,7 @@
     }
     if (mNeedsAnimatorsSync) {
         mAnimators.resize(mStagingAnimators.size());
-        std::vector< sp<RenderPropertyAnimator> >::iterator it;
+        std::vector< sp<BaseRenderNodeAnimator> >::iterator it;
         // hint: this means copy_if_not()
         it = std::remove_copy_if(mStagingAnimators.begin(), mStagingAnimators.end(),
                 mAnimators.begin(), is_finished);
@@ -141,26 +141,22 @@
 
 class AnimateFunctor {
 public:
-    AnimateFunctor(RenderProperties* target, TreeInfo& info)
+    AnimateFunctor(RenderNode* target, TreeInfo& info)
             : mTarget(target), mInfo(info) {}
 
-    bool operator() (sp<RenderPropertyAnimator>& animator) {
-        bool finished = animator->animate(mTarget, mInfo);
-        if (finished && mInfo.animationListener) {
-            mInfo.animationListener->onAnimationFinished(animator);
-        }
-        return finished;
+    bool operator() (sp<BaseRenderNodeAnimator>& animator) {
+        return animator->animate(mTarget, mInfo);
     }
 private:
-    RenderProperties* mTarget;
+    RenderNode* mTarget;
     TreeInfo& mInfo;
 };
 
 void RenderNode::evaluateAnimations(TreeInfo& info) {
     if (!mAnimators.size()) return;
 
-    AnimateFunctor functor(&mProperties, info);
-    std::vector< sp<RenderPropertyAnimator> >::iterator newEnd;
+    AnimateFunctor functor(this, info);
+    std::vector< sp<BaseRenderNodeAnimator> >::iterator newEnd;
     newEnd = std::remove_if(mAnimators.begin(), mAnimators.end(), functor);
     mAnimators.erase(newEnd, mAnimators.end());
     mProperties.updateMatrix();
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index 294f436..159903c 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -128,6 +128,10 @@
         return mProperties;
     }
 
+    RenderProperties& animatorProperties() {
+        return mProperties;
+    }
+
     const RenderProperties& stagingProperties() {
         return mStagingProperties;
     }
@@ -148,13 +152,13 @@
     ANDROID_API virtual void prepareTree(TreeInfo& info);
 
     // UI thread only!
-    ANDROID_API void addAnimator(const sp<RenderPropertyAnimator>& animator) {
+    ANDROID_API void addAnimator(const sp<BaseRenderNodeAnimator>& animator) {
         mStagingAnimators.insert(animator);
         mNeedsAnimatorsSync = true;
     }
 
     // UI thread only!
-    ANDROID_API void removeAnimator(const sp<RenderPropertyAnimator>& animator) {
+    ANDROID_API void removeAnimator(const sp<BaseRenderNodeAnimator>& animator) {
         mStagingAnimators.erase(animator);
         mNeedsAnimatorsSync = true;
     }
@@ -233,8 +237,8 @@
     DisplayListData* mStagingDisplayListData;
 
     bool mNeedsAnimatorsSync;
-    std::set< sp<RenderPropertyAnimator> > mStagingAnimators;
-    std::vector< sp<RenderPropertyAnimator> > mAnimators;
+    std::set< sp<BaseRenderNodeAnimator> > mStagingAnimators;
+    std::vector< sp<BaseRenderNodeAnimator> > mAnimators;
 
     /**
      * Draw time state - these properties are only set and used during rendering
diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h
index 8957607..a383fbf 100644
--- a/libs/hwui/TreeInfo.h
+++ b/libs/hwui/TreeInfo.h
@@ -16,20 +16,19 @@
 #ifndef TREEINFO_H
 #define TREEINFO_H
 
-#include <cutils/compiler.h>
 #include <utils/Timers.h>
-#include <utils/StrongPointer.h>
 
 namespace android {
 namespace uirenderer {
 
-class RenderPropertyAnimator;
+class BaseAnimator;
+class AnimationListener;
 
-class AnimationListener {
+class AnimationHook {
 public:
-    ANDROID_API virtual void onAnimationFinished(const sp<RenderPropertyAnimator>&) = 0;
+    virtual void callOnFinished(BaseAnimator* animator, AnimationListener* listener) = 0;
 protected:
-    ANDROID_API virtual ~AnimationListener() {}
+    ~AnimationHook() {}
 };
 
 struct TreeInfo {
@@ -41,7 +40,7 @@
             , frameTimeMs(0)
             , evaluateAnimations(false)
             , hasAnimations(false)
-            , animationListener(0)
+            , animationHook(0)
     {}
 
     bool hasFunctors;
@@ -53,7 +52,7 @@
     bool evaluateAnimations;
     // This is only updated if evaluateAnimations is true
     bool hasAnimations;
-    AnimationListener* animationListener;
+    AnimationHook* animationHook;
 
     // TODO: Damage calculations
 };
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index ff4be71..45f5cb0 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -112,6 +112,10 @@
     initTreeInfo(info);
     mContext->processLayerUpdates(&mLayers, info);
     mContext->prepareTree(info);
+    if (info.hasAnimations) {
+        // TODO: dirty calculations, for now just do a full-screen inval
+        mDirty.setEmpty();
+    }
     // If prepareTextures is false, we ran out of texture cache space
     return !info.hasFunctors && info.prepareTextures;
 }
diff --git a/libs/hwui/utils/Macros.h b/libs/hwui/utils/Macros.h
new file mode 100644
index 0000000..14a3ec0
--- /dev/null
+++ b/libs/hwui/utils/Macros.h
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+#ifndef MACROS_H
+#define MACROS_H
+
+#define PREVENT_COPY_AND_ASSIGN(Type) \
+    private: \
+        Type(const Type&); \
+        void operator=(const Type&)
+
+
+#endif /* MACROS_H */
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 5c2583b..af0d0ad 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -288,6 +288,15 @@
         </activity>
 
         <activity
+                android:name="CirclePropActivity"
+                android:label="Draw/Circle Props">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="com.android.test.hwui.TEST" />
+            </intent-filter>
+        </activity>
+
+        <activity
                 android:name="ClearActivity"
                 android:label="Window/Clear">
             <intent-filter>
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/CirclePropActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/CirclePropActivity.java
new file mode 100644
index 0000000..f060bc8
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/CirclePropActivity.java
@@ -0,0 +1,137 @@
+/*
+ * 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.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.CanvasProperty;
+import android.graphics.Paint;
+import android.graphics.Paint.Style;
+import android.os.Bundle;
+import android.os.Trace;
+import android.view.HardwareCanvas;
+import android.view.RenderNodeAnimator;
+import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.LinearLayout.LayoutParams;
+import android.widget.ProgressBar;
+
+import java.util.ArrayList;
+
+public class CirclePropActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        final LinearLayout layout = new LinearLayout(this);
+        layout.setOrientation(LinearLayout.VERTICAL);
+
+        ProgressBar spinner = new ProgressBar(this, null, android.R.attr.progressBarStyleLarge);
+        layout.addView(spinner, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
+
+        layout.addView(new CircleView(this),
+                new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
+
+        setContentView(layout);
+    }
+
+    static class CircleView extends View {
+        static final int DURATION = 500;
+
+        private boolean mToggle = false;
+        ArrayList<RenderNodeAnimator> mRunningAnimations = new ArrayList<RenderNodeAnimator>();
+
+        CanvasProperty<Float> mX;
+        CanvasProperty<Float> mY;
+        CanvasProperty<Float> mRadius;
+        CanvasProperty<Paint> mPaint;
+
+        CircleView(Context c) {
+            super(c);
+            setClickable(true);
+
+            mX = CanvasProperty.createFloat(200.0f);
+            mY = CanvasProperty.createFloat(200.0f);
+            mRadius = CanvasProperty.createFloat(150.0f);
+
+            Paint p = new Paint();
+            p.setAntiAlias(true);
+            p.setColor(0xFFFF0000);
+            p.setStyle(Style.STROKE);
+            p.setStrokeWidth(60.0f);
+            mPaint = CanvasProperty.createPaint(p);
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            super.onDraw(canvas);
+
+            if (canvas.isHardwareAccelerated()) {
+                HardwareCanvas hwcanvas = (HardwareCanvas) canvas;
+                hwcanvas.drawCircle(mX, mY, mRadius, mPaint);
+            }
+        }
+
+        @Override
+        public boolean performClick() {
+            for (int i = 0; i < mRunningAnimations.size(); i++) {
+                mRunningAnimations.get(i).cancel();
+            }
+            mRunningAnimations.clear();
+
+            mToggle = !mToggle;
+
+            mRunningAnimations.add(new RenderNodeAnimator(
+                    mX, RenderNodeAnimator.DELTA_TYPE_ABSOLUTE, mToggle ? 400.0f : 200.0f));
+
+            mRunningAnimations.add(new RenderNodeAnimator(
+                    mY, RenderNodeAnimator.DELTA_TYPE_ABSOLUTE, mToggle ? 600.0f : 200.0f));
+
+            mRunningAnimations.add(new RenderNodeAnimator(
+                    mRadius, RenderNodeAnimator.DELTA_TYPE_ABSOLUTE, mToggle ? 250.0f : 150.0f));
+
+            mRunningAnimations.add(new RenderNodeAnimator(
+                    mPaint, RenderNodeAnimator.PAINT_ALPHA,
+                    RenderNodeAnimator.DELTA_TYPE_ABSOLUTE, mToggle ? 64.0f : 255.0f));
+
+            mRunningAnimations.add(new RenderNodeAnimator(
+                    mPaint, RenderNodeAnimator.PAINT_STROKE_WIDTH,
+                    RenderNodeAnimator.DELTA_TYPE_ABSOLUTE, mToggle ? 5.0f : 60.0f));
+
+            for (int i = 0; i < mRunningAnimations.size(); i++) {
+                mRunningAnimations.get(i).start(this);
+            }
+
+            if (mToggle) {
+                post(new Runnable() {
+                    @Override
+                    public void run() {
+                        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "pretendBusy");
+                        try {
+                            Thread.sleep(DURATION);
+                        } catch (InterruptedException e) {
+                        }
+                        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+                    }
+                });
+            }
+
+            return true;
+        }
+    }
+}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/SmallCircleActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/SmallCircleActivity.java
index 8c02539..a3f4ddc 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/SmallCircleActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/SmallCircleActivity.java
@@ -57,7 +57,7 @@
             
             mPaint = new Paint();
             mPaint.setAntiAlias(true);
-            mPaint.setColor(0xffffffff);
+            mPaint.setColor(0xff00ffff);
         }
 
         @Override
diff --git a/tests/RenderThreadTest/src/com/example/renderthread/MainActivity.java b/tests/RenderThreadTest/src/com/example/renderthread/MainActivity.java
index 1d209dd..8f9cf58 100644
--- a/tests/RenderThreadTest/src/com/example/renderthread/MainActivity.java
+++ b/tests/RenderThreadTest/src/com/example/renderthread/MainActivity.java
@@ -19,8 +19,6 @@
 
 public class MainActivity extends Activity implements OnItemClickListener {
 
-    static final int TRANSLATION_Y = 1;
-    static final int DELTA_TYPE_DELTA = 1;
     static final int DURATION = 400;
 
     static final String KEY_NAME = "name";
@@ -75,7 +73,7 @@
             float delta = (pos - clickedPosition) * 1.1f;
             if (delta == 0) delta = -1;
             RenderNodeAnimator animator = new RenderNodeAnimator(
-                    TRANSLATION_Y, DELTA_TYPE_DELTA, dy * delta);
+                    RenderNodeAnimator.TRANSLATION_Y, RenderNodeAnimator.DELTA_TYPE_DELTA, dy * delta);
             animator.setDuration(DURATION);
             animator.start(child);
         }