OSDN Git Service

Make sure all transition components run on the same thread
authorAdam Cohen <adamcohen@google.com>
Wed, 27 May 2015 06:03:31 +0000 (23:03 -0700)
committerAdam Cohen <adamcohen@google.com>
Wed, 27 May 2015 20:55:09 +0000 (13:55 -0700)
-> The framework circular reveal transition runs on the render
   thread which can cause problems when mixed in an AnimatorSet
   with transitions that don't run on the render thread
-> See issue 17556455

issue 21445293

Change-Id: Ie19c184c55060651e817d426ec83049b06af56ba

src/com/android/launcher3/LauncherAnimUtils.java
src/com/android/launcher3/LauncherStateTransitionAnimation.java
src/com/android/launcher3/Utilities.java
src/com/android/launcher3/util/RevealOutlineProvider.java [new file with mode: 0644]
src/com/android/launcher3/util/UiThreadCircularReveal.java [new file with mode: 0644]

index 6ff7666..6a248a3 100644 (file)
@@ -26,6 +26,9 @@ import android.os.Build;
 import android.view.View;
 import android.view.ViewAnimationUtils;
 import android.view.ViewTreeObserver;
+
+import com.android.launcher3.util.UiThreadCircularReveal;
+
 import java.util.HashSet;
 import java.util.WeakHashMap;
 
@@ -130,13 +133,11 @@ public class LauncherAnimUtils {
     }
 
     @TargetApi(Build.VERSION_CODES.LOLLIPOP)
-    public static Animator createCircularReveal(View view, int centerX,
+    public static ValueAnimator createCircularReveal(View view, int centerX,
             int centerY, float startRadius, float endRadius) {
-        Animator anim = ViewAnimationUtils.createCircularReveal(view, centerX,
+        ValueAnimator anim = UiThreadCircularReveal.createCircularReveal(view, centerX,
                 centerY, startRadius, endRadius);
-        if (anim instanceof ValueAnimator) {
-            new FirstFrameAnimatorHelper((ValueAnimator) anim, view);
-        }
+        new FirstFrameAnimatorHelper(anim, view);
         return anim;
     }
 }
index f373fde..a006d14 100644 (file)
@@ -26,12 +26,14 @@ import android.annotation.SuppressLint;
 import android.content.res.Resources;
 import android.util.Log;
 import android.view.View;
-import android.view.ViewAnimationUtils;
 import android.view.animation.AccelerateInterpolator;
 import android.view.animation.DecelerateInterpolator;
+
 import com.android.launcher3.allapps.AllAppsContainerView;
+import com.android.launcher3.util.UiThreadCircularReveal;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.widget.WidgetsContainerView;
+
 import java.util.HashMap;
 
 /**
@@ -320,7 +322,7 @@ public class LauncherStateTransitionAnimation {
                 float startRadius = pCb.getMaterialRevealViewStartFinalRadius();
                 AnimatorListenerAdapter listener = pCb.getMaterialRevealViewAnimatorListener(
                         revealView, allAppsButtonView);
-                Animator reveal = ViewAnimationUtils.createCircularReveal(revealView, width / 2,
+                Animator reveal = UiThreadCircularReveal.createCircularReveal(revealView, width / 2,
                         height / 2, startRadius, revealRadius);
                 reveal.setDuration(revealDuration);
                 reveal.setInterpolator(new LogDecelerateInterpolator(100, 0));
@@ -587,14 +589,14 @@ public class LauncherStateTransitionAnimation {
                 TimeInterpolator decelerateInterpolator = material ?
                         new LogDecelerateInterpolator(100, 0) :
                         new DecelerateInterpolator(1f);
-                ObjectAnimator panelDriftY = LauncherAnimUtils.ofFloat(revealView, "translationY",
+                ObjectAnimator panelDriftY = ObjectAnimator.ofFloat(revealView, "translationY",
                         0, revealViewToYDrift);
                 panelDriftY.setDuration(revealDuration - SINGLE_FRAME_DELAY);
                 panelDriftY.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
                 panelDriftY.setInterpolator(decelerateInterpolator);
                 mStateAnimation.play(panelDriftY);
 
-                ObjectAnimator panelDriftX = LauncherAnimUtils.ofFloat(revealView, "translationX",
+                ObjectAnimator panelDriftX = ObjectAnimator.ofFloat(revealView, "translationX",
                         0, revealViewToXDrift);
                 panelDriftX.setDuration(revealDuration - SINGLE_FRAME_DELAY);
                 panelDriftX.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
@@ -605,7 +607,7 @@ public class LauncherStateTransitionAnimation {
                 final float revealViewToAlpha = !material ? 0f :
                         pCb.getMaterialRevealViewFinalAlpha(revealView);
                 if (revealViewToAlpha != 1f) {
-                    ObjectAnimator panelAlpha = LauncherAnimUtils.ofFloat(revealView, "alpha",
+                    ObjectAnimator panelAlpha = ObjectAnimator.ofFloat(revealView, "alpha",
                             1f, revealViewToAlpha);
                     panelAlpha.setDuration(material ? revealDuration : 150);
                     panelAlpha.setStartDelay(material ? 0 : itemsAlphaStagger + SINGLE_FRAME_DELAY);
@@ -617,7 +619,7 @@ public class LauncherStateTransitionAnimation {
                 layerViews.put(contentView, BUILD_AND_SET_LAYER);
 
                 // Create the individual animators
-                ObjectAnimator pageDrift = LauncherAnimUtils.ofFloat(contentView, "translationY",
+                ObjectAnimator pageDrift = ObjectAnimator.ofFloat(contentView, "translationY",
                         0, revealViewToYDrift);
                 contentView.setTranslationY(0);
                 pageDrift.setDuration(revealDuration - SINGLE_FRAME_DELAY);
@@ -626,7 +628,7 @@ public class LauncherStateTransitionAnimation {
                 mStateAnimation.play(pageDrift);
 
                 contentView.setAlpha(1f);
-                ObjectAnimator itemsAlpha = LauncherAnimUtils.ofFloat(contentView, "alpha", 1f, 0f);
+                ObjectAnimator itemsAlpha = ObjectAnimator.ofFloat(contentView, "alpha", 1f, 0f);
                 itemsAlpha.setDuration(100);
                 itemsAlpha.setInterpolator(decelerateInterpolator);
                 mStateAnimation.play(itemsAlpha);
@@ -636,9 +638,8 @@ public class LauncherStateTransitionAnimation {
                     float finalRadius = pCb.getMaterialRevealViewStartFinalRadius();
                     AnimatorListenerAdapter listener =
                             pCb.getMaterialRevealViewAnimatorListener(revealView, allAppsButtonView);
-                    Animator reveal =
-                            LauncherAnimUtils.createCircularReveal(revealView, width / 2,
-                                    height / 2, revealRadius, finalRadius);
+                    Animator reveal = UiThreadCircularReveal.createCircularReveal(revealView, width / 2,
+                            height / 2, revealRadius, finalRadius);
                     reveal.setInterpolator(new LogDecelerateInterpolator(100, 0));
                     reveal.setDuration(revealDuration);
                     reveal.setStartDelay(itemsAlphaStagger);
@@ -782,4 +783,4 @@ public class LauncherStateTransitionAnimation {
             mStateAnimation = null;
         }
     }
-}
\ No newline at end of file
+}
index 6c4b720..256eba0 100644 (file)
@@ -127,6 +127,11 @@ public final class Utilities {
         return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
     }
 
+    public static boolean isLmpMR1OrAbove() {
+        // TODO(adamcohen): update to Build.VERSION_CODES.LOLLIPOP_MR1 once building against 22;
+        return Build.VERSION.SDK_INT >= 22;
+    }
+
     static Bitmap createIconBitmap(Cursor c, int iconIndex, Context context) {
         byte[] data = c.getBlob(iconIndex);
         try {
@@ -588,7 +593,6 @@ public final class Utilities {
     }
 
     public static final Comparator<ItemInfo> RANK_COMPARATOR = new Comparator<ItemInfo>() {
-
         @Override
         public int compare(ItemInfo lhs, ItemInfo rhs) {
             return lhs.rank - rhs.rank;
diff --git a/src/com/android/launcher3/util/RevealOutlineProvider.java b/src/com/android/launcher3/util/RevealOutlineProvider.java
new file mode 100644 (file)
index 0000000..0db3984
--- /dev/null
@@ -0,0 +1,49 @@
+package com.android.launcher3.util;
+
+import android.annotation.TargetApi;
+import android.graphics.Outline;
+import android.graphics.Rect;
+import android.os.Build;
+import android.view.View;
+import android.view.ViewOutlineProvider;
+
+@TargetApi(Build.VERSION_CODES.LOLLIPOP)
+public class RevealOutlineProvider extends ViewOutlineProvider {
+
+    private int mCenterX;
+    private int mCenterY;
+    private float mRadius0;
+    private float mRadius1;
+    private int mCurrentRadius;
+
+    private final Rect mOval;
+
+    /**
+     * @param x reveal center x
+     * @param y reveal center y
+     * @param r0 initial radius
+     * @param r1 final radius
+     */
+    public RevealOutlineProvider(int x, int y, float r0, float r1) {
+        mCenterX = x;
+        mCenterY = y;
+        mRadius0 = r0;
+        mRadius1 = r1;
+
+        mOval = new Rect();
+    }
+
+    public void setProgress(float progress) {
+        mCurrentRadius = (int) ((1 - progress) * mRadius0 + progress * mRadius1);
+
+        mOval.left = mCenterX - mCurrentRadius;
+        mOval.top = mCenterY - mCurrentRadius;
+        mOval.right = mCenterX + mCurrentRadius;
+        mOval.bottom = mCenterY + mCurrentRadius;
+    }
+
+    @Override
+    public void getOutline(View v, Outline outline) {
+        outline.setRoundRect(mOval, mCurrentRadius);
+    }
+}
diff --git a/src/com/android/launcher3/util/UiThreadCircularReveal.java b/src/com/android/launcher3/util/UiThreadCircularReveal.java
new file mode 100644 (file)
index 0000000..c7324fb
--- /dev/null
@@ -0,0 +1,55 @@
+package com.android.launcher3.util;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.annotation.TargetApi;
+import android.os.Build;
+import android.view.View;
+import android.view.ViewOutlineProvider;
+
+import com.android.launcher3.Utilities;
+
+@TargetApi(Build.VERSION_CODES.LOLLIPOP)
+public class UiThreadCircularReveal {
+
+    public static ValueAnimator createCircularReveal(View v, int x, int y, float r0, float r1) {
+        ValueAnimator va = ValueAnimator.ofFloat(0f, 1f);
+
+        final View revealView = v;
+        final RevealOutlineProvider outlineProvider = new RevealOutlineProvider(x, y, r0, r1);
+        final ViewOutlineProvider originalProvider = revealView.getOutlineProvider();
+        final float elevation = v.getElevation();
+
+        va.addListener(new AnimatorListenerAdapter() {
+            public void onAnimationStart(Animator animation) {
+                revealView.setOutlineProvider(outlineProvider);
+                revealView.setClipToOutline(true);
+                revealView.setTranslationZ(-elevation);
+            }
+
+            public void onAnimationEnd(Animator animation) {
+                revealView.setOutlineProvider(originalProvider);
+                revealView.setClipToOutline(false);
+                revealView.setTranslationZ(0);
+            }
+
+        });
+
+        va.addUpdateListener(new AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator arg0) {
+                float progress = arg0.getAnimatedFraction();
+                outlineProvider.setProgress(progress);
+                if (Utilities.isLmpMR1OrAbove()) {
+                    revealView.invalidateOutline();
+                } else {
+                    // On L, a bug requires calling a full view invalidate.
+                    revealView.invalidate();
+                }
+            }
+        });
+        return va;
+    }
+}