Support running AVD on UI thread

By default, AVD animates on RT thread. But since in some cases there's a
need for a finer control on when the frame update for the animation should
happen, such as coordiating the AVD animation with other animations that
run on UI thread, we are providing a way to force the AVD to run on UI
thread.

Bug: 27278616
Change-Id: I372ecd3dc52e3fa0bdce3a1e9c19443f9b199027
diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java
index a4cb703..a45c18d 100644
--- a/core/java/android/view/RenderNode.java
+++ b/core/java/android/view/RenderNode.java
@@ -779,7 +779,7 @@
         return mOwningView != null && mOwningView.mAttachInfo != null;
     }
 
-    public void addAnimator(AnimatedVectorDrawable.VectorDrawableAnimator animatorSet) {
+    public void addAnimator(AnimatedVectorDrawable.VectorDrawableAnimatorRT animatorSet) {
         if (mOwningView == null || mOwningView.mAttachInfo == null) {
             throw new IllegalStateException("Cannot start this animator on a detached view!");
         }
diff --git a/core/jni/android_graphics_drawable_AnimatedVectorDrawable.cpp b/core/jni/android_graphics_drawable_AnimatedVectorDrawable.cpp
index 14badb7..4b2a72d 100644
--- a/core/jni/android_graphics_drawable_AnimatedVectorDrawable.cpp
+++ b/core/jni/android_graphics_drawable_AnimatedVectorDrawable.cpp
@@ -175,13 +175,13 @@
     {"nCreatePathPropertyHolder", "!(JIFF)J", (void*)createPathPropertyHolder},
     {"nCreateRootAlphaPropertyHolder", "!(JFF)J", (void*)createRootAlphaPropertyHolder},
     {"nSetPropertyHolderData", "(J[FI)V", (void*)setPropertyHolderData},
-    {"nStart", "(JLandroid/graphics/drawable/AnimatedVectorDrawable$VectorDrawableAnimator;I)V", (void*)start},
-    {"nReverse", "(JLandroid/graphics/drawable/AnimatedVectorDrawable$VectorDrawableAnimator;I)V", (void*)reverse},
+    {"nStart", "(JLandroid/graphics/drawable/AnimatedVectorDrawable$VectorDrawableAnimatorRT;I)V", (void*)start},
+    {"nReverse", "(JLandroid/graphics/drawable/AnimatedVectorDrawable$VectorDrawableAnimatorRT;I)V", (void*)reverse},
     {"nEnd", "!(J)V", (void*)end},
     {"nReset", "!(J)V", (void*)reset},
 };
 
-const char* const kClassPathName = "android/graphics/drawable/AnimatedVectorDrawable$VectorDrawableAnimator";
+const char* const kClassPathName = "android/graphics/drawable/AnimatedVectorDrawable$VectorDrawableAnimatorRT";
 int register_android_graphics_drawable_AnimatedVectorDrawable(JNIEnv* env) {
     gVectorDrawableAnimatorClassInfo.clazz = FindClassOrDie(env, kClassPathName);
     gVectorDrawableAnimatorClassInfo.clazz = MakeGlobalRefOrDie(env,
@@ -189,7 +189,7 @@
 
     gVectorDrawableAnimatorClassInfo.callOnFinished = GetStaticMethodIDOrDie(
             env, gVectorDrawableAnimatorClassInfo.clazz, "callOnFinished",
-            "(Landroid/graphics/drawable/AnimatedVectorDrawable$VectorDrawableAnimator;I)V");
+            "(Landroid/graphics/drawable/AnimatedVectorDrawable$VectorDrawableAnimatorRT;I)V");
     return RegisterMethodsOrDie(env, "android/graphics/drawable/AnimatedVectorDrawable",
             gMethods, NELEM(gMethods));
 }
diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
index c486c1f..3901af3 100644
--- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
@@ -154,7 +154,7 @@
     private static final boolean DBG_ANIMATION_VECTOR_DRAWABLE = false;
 
     /** Local, mutable animator set. */
-    private final VectorDrawableAnimator mAnimatorSet = new VectorDrawableAnimator(this);
+    private VectorDrawableAnimator mAnimatorSet = new VectorDrawableAnimatorRT(this);
 
     /**
      * The resources against which this drawable was created. Used to attempt
@@ -164,8 +164,8 @@
 
     private AnimatedVectorDrawableState mAnimatedVectorState;
 
-    /** Whether the animator set has been prepared. */
-    private boolean mHasAnimatorSet;
+    /** The animator set that is parsed from the xml. */
+    private AnimatorSet mAnimatorSetFromXml = null;
 
     private boolean mMutated;
 
@@ -234,9 +234,7 @@
 
     @Override
     public void draw(Canvas canvas) {
-        if (canvas.isHardwareAccelerated()) {
-            mAnimatorSet.recordLastSeenTarget((DisplayListCanvas) canvas);
-        }
+        mAnimatorSet.onDraw(canvas);
         mAnimatedVectorState.mVectorDrawable.draw(canvas);
     }
 
@@ -392,6 +390,24 @@
         mRes = state.mPendingAnims == null ? null : res;
     }
 
+    /**
+     * Force to animate on UI thread.
+     * @hide
+     */
+    public void forceAnimationOnUI() {
+        if (mAnimatorSet instanceof VectorDrawableAnimatorRT) {
+            VectorDrawableAnimatorRT animator = (VectorDrawableAnimatorRT) mAnimatorSet;
+            if (animator.isRunning()) {
+                throw new UnsupportedOperationException("Cannot force Animated Vector Drawable to" +
+                        " run on UI thread when the animation has started on RenderThread.");
+            }
+            mAnimatorSet = new VectorDrawableAnimatorUI(this);
+            if (mAnimatorSetFromXml != null) {
+                mAnimatorSet.init(mAnimatorSetFromXml);
+            }
+        }
+    }
+
     @Override
     public boolean canApplyTheme() {
         return (mAnimatedVectorState != null && mAnimatedVectorState.canApplyTheme())
@@ -612,6 +628,7 @@
      * Resets the AnimatedVectorDrawable to the start state as specified in the animators.
      */
     public void reset() {
+        ensureAnimatorSet();
         mAnimatorSet.reset();
     }
 
@@ -623,13 +640,12 @@
 
     @NonNull
     private void ensureAnimatorSet() {
-        if (!mHasAnimatorSet) {
+        if (mAnimatorSetFromXml == null) {
             // TODO: Skip the AnimatorSet creation and init the VectorDrawableAnimator directly
             // with a list of LocalAnimators.
-            AnimatorSet set = new AnimatorSet();
-            mAnimatedVectorState.prepareLocalAnimators(set, mRes);
-            mHasAnimatorSet = true;
-            mAnimatorSet.initWithAnimatorSet(set);
+            mAnimatorSetFromXml = new AnimatorSet();
+            mAnimatedVectorState.prepareLocalAnimators(mAnimatorSetFromXml, mRes);
+            mAnimatorSet.init(mAnimatorSetFromXml);
             mRes = null;
         }
     }
@@ -724,7 +740,7 @@
     // A helper function to clean up the animator listener in the mAnimatorSet.
     private void removeAnimatorSetListener() {
         if (mAnimatorListener != null) {
-            mAnimatorSet.removeListener();
+            mAnimatorSet.removeListener(mAnimatorListener);
             mAnimatorListener = null;
         }
     }
@@ -754,10 +770,100 @@
         mAnimationCallbacks.clear();
     }
 
+    private interface VectorDrawableAnimator {
+        void init(AnimatorSet set);
+        void start();
+        void end();
+        void reset();
+        void reverse();
+        boolean canReverse();
+        void setListener(AnimatorListener listener);
+        void removeListener(AnimatorListener listener);
+        void onDraw(Canvas canvas);
+        boolean isStarted();
+        boolean isRunning();
+    }
+
+    private static class VectorDrawableAnimatorUI implements VectorDrawableAnimator {
+        private AnimatorSet mSet = new AnimatorSet();
+        private final Drawable mDrawable;
+
+        VectorDrawableAnimatorUI(AnimatedVectorDrawable drawable) {
+            mDrawable = drawable;
+        }
+
+        @Override
+        public void init(AnimatorSet set) {
+            mSet = set;
+        }
+
+        @Override
+        public void start() {
+            if (mSet.isStarted()) {
+                return;
+            }
+            mSet.start();
+            invalidateOwningView();
+        }
+
+        @Override
+        public void end() {
+            mSet.end();
+        }
+
+        @Override
+        public void reset() {
+            start();
+            mSet.cancel();
+        }
+
+        @Override
+        public void reverse() {
+            mSet.reverse();
+            invalidateOwningView();
+        }
+
+        @Override
+        public boolean canReverse() {
+            return mSet.canReverse();
+        }
+
+        @Override
+        public void setListener(AnimatorListener listener) {
+            mSet.addListener(listener);
+        }
+
+        @Override
+        public void removeListener(AnimatorListener listener) {
+            mSet.removeListener(listener);
+        }
+
+        @Override
+        public void onDraw(Canvas canvas) {
+            if (mSet.isStarted()) {
+                invalidateOwningView();
+            }
+        }
+
+        @Override
+        public boolean isStarted() {
+            return mSet.isStarted();
+        }
+
+        @Override
+        public boolean isRunning() {
+            return mSet.isRunning();
+        }
+
+        private void invalidateOwningView() {
+            mDrawable.invalidateSelf();
+        }
+    }
+
     /**
      * @hide
      */
-    public static class VectorDrawableAnimator {
+    public static class VectorDrawableAnimatorRT implements VectorDrawableAnimator {
         private static final int NONE = 0;
         private static final int START_ANIMATION = 1;
         private static final int REVERSE_ANIMATION = 2;
@@ -779,7 +885,7 @@
         private int mPendingAnimationAction = NONE;
         private final Drawable mDrawable;
 
-        VectorDrawableAnimator(AnimatedVectorDrawable drawable) {
+        VectorDrawableAnimatorRT(AnimatedVectorDrawable drawable) {
             mDrawable = drawable;
             mSetPtr = nCreateAnimatorSet();
             // Increment ref count on native AnimatorSet, so it doesn't get released before Java
@@ -787,7 +893,8 @@
             mSetRefBasePtr = new VirtualRefBasePtr(mSetPtr);
         }
 
-        private void initWithAnimatorSet(AnimatorSet set) {
+        @Override
+        public void init(AnimatorSet set) {
             if (mInitialized) {
                 // Already initialized
                 throw new UnsupportedOperationException("VectorDrawableAnimator cannot be " +
@@ -816,7 +923,7 @@
             ArrayList<Animator> animators = set.getChildAnimations();
 
             boolean playTogether = set.shouldPlayTogether();
-            // Convert AnimatorSet to VectorDrawableAnimator
+            // Convert AnimatorSet to VectorDrawableAnimatorRT
             for (int i = 0; i < animators.size(); i++) {
                 Animator animator = animators.get(i);
                 // Here we only support ObjectAnimator
@@ -1060,6 +1167,7 @@
             mDrawable.invalidateSelf();
         }
 
+        @Override
         public void start() {
             if (!mInitialized) {
                 return;
@@ -1083,6 +1191,7 @@
             }
         }
 
+        @Override
         public void end() {
             if (mInitialized && useLastSeenTarget()) {
                 // If no target has ever been set, no-op
@@ -1091,6 +1200,7 @@
             }
         }
 
+        @Override
         public void reset() {
             if (mInitialized && useLastSeenTarget()) {
                 // If no target has ever been set, no-op
@@ -1101,7 +1211,8 @@
 
         // Current (imperfect) Java AnimatorSet cannot be reversed when the set contains sequential
         // animators or when the animator set has a start delay
-        void reverse() {
+        @Override
+        public void reverse() {
             if (!mIsReversible || !mInitialized) {
                 return;
             }
@@ -1125,29 +1236,41 @@
             return mSetPtr;
         }
 
-        boolean canReverse() {
+        @Override
+        public boolean canReverse() {
             return mIsReversible;
         }
 
-        boolean isStarted() {
+        @Override
+        public boolean isStarted() {
             return mStarted;
         }
 
-        boolean isRunning() {
+        @Override
+        public boolean isRunning() {
             if (!mInitialized) {
                 return false;
             }
             return mStarted;
         }
 
-        void setListener(AnimatorListener listener) {
+        @Override
+        public void setListener(AnimatorListener listener) {
             mListener = listener;
         }
 
-        void removeListener() {
+        @Override
+        public void removeListener(AnimatorListener listener) {
             mListener = null;
         }
 
+        @Override
+        public void onDraw(Canvas canvas) {
+            if (canvas.isHardwareAccelerated()) {
+                recordLastSeenTarget((DisplayListCanvas) canvas);
+            }
+        }
+
         private void onAnimationEnd(int listenerId) {
             if (listenerId != mLastListenerId) {
                 return;
@@ -1162,7 +1285,7 @@
         }
 
         // onFinished: should be called from native
-        private static void callOnFinished(VectorDrawableAnimator set, int id) {
+        private static void callOnFinished(VectorDrawableAnimatorRT set, int id) {
             set.onAnimationEnd(id);
         }
     }
@@ -1183,8 +1306,8 @@
     private static native long nCreateRootAlphaPropertyHolder(long nativePtr, float startValue,
             float endValue);
     private static native void nSetPropertyHolderData(long nativePtr, float[] data, int length);
-    private static native void nStart(long animatorSetPtr, VectorDrawableAnimator set, int id);
-    private static native void nReverse(long animatorSetPtr, VectorDrawableAnimator set, int id);
+    private static native void nStart(long animatorSetPtr, VectorDrawableAnimatorRT set, int id);
+    private static native void nReverse(long animatorSetPtr, VectorDrawableAnimatorRT set, int id);
     private static native void nEnd(long animatorSetPtr);
     private static native void nReset(long animatorSetPtr);
 }
diff --git a/preloaded-classes b/preloaded-classes
index 9535cc2..be645d2 100644
--- a/preloaded-classes
+++ b/preloaded-classes
@@ -907,7 +907,7 @@
 android.graphics.drawable.AnimatedVectorDrawable$1
 android.graphics.drawable.AnimatedVectorDrawable$AnimatedVectorDrawableState
 android.graphics.drawable.AnimatedVectorDrawable$AnimatedVectorDrawableState$PendingAnimator
-android.graphics.drawable.AnimatedVectorDrawable$VectorDrawableAnimator
+android.graphics.drawable.AnimatedVectorDrawable$VectorDrawableAnimatorRT
 android.graphics.drawable.AnimationDrawable
 android.graphics.drawable.AnimationDrawable$AnimationState
 android.graphics.drawable.BitmapDrawable