improving performance of first AllApps/Customize animation

Change-Id: I71e5cc634f7e1346617d101efd6945c00484cab3
diff --git a/src/com/android/launcher2/AllAppsPagedView.java b/src/com/android/launcher2/AllAppsPagedView.java
index 4158b4a..b9b38c3 100644
--- a/src/com/android/launcher2/AllAppsPagedView.java
+++ b/src/com/android/launcher2/AllAppsPagedView.java
@@ -63,6 +63,7 @@
     private int mAppFilter = ALL_APPS_FLAG;
 
     private final LayoutInflater mInflater;
+    private boolean mAllowHardwareLayerCreation;
 
 
     public AllAppsPagedView(Context context) {
@@ -95,6 +96,22 @@
         mCenterPagesVertically = false;
     }
 
+    void allowHardwareLayerCreation() {
+        // This is called after the first time we launch into All Apps. Before that point,
+        // there's no need for hardware layers here since there's a hardware layer set on the
+        // parent, AllAppsTabbed, during the AllApps transition -- creating hardware layers here
+        // before the animation is done slows down the animation
+        if (mAllowHardwareLayerCreation) {
+            return;
+        }
+        mAllowHardwareLayerCreation = true;
+        int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            PagedViewCellLayout page = (PagedViewCellLayout) getChildAt(i);
+            page.allowHardwareLayerCreation();
+        }
+    }
+
     @Override
     public void setLauncher(Launcher launcher) {
         mLauncher = launcher;
@@ -121,8 +138,6 @@
         cancelLongPress();
 
         if (isVisible()) {
-            getParent().bringChildToFront(this);
-            setVisibility(View.VISIBLE);
             if (animate) {
                 startAnimation(AnimationUtils.loadAnimation(getContext(),
                         R.anim.all_apps_2d_fade_in));
@@ -141,7 +156,6 @@
 
     protected void onAnimationEnd() {
         if (!isVisible()) {
-            setVisibility(View.GONE);
             mZoom = 0.0f;
 
             endChoiceMode();
@@ -388,6 +402,7 @@
         }
         mFilteredApps = rebuildFilteredApps(mApps);
     }
+
     @Override
     public void removeApps(ArrayList<ApplicationInfo> list) {
         removeAppsWithoutInvalidate(list);
@@ -434,12 +449,15 @@
         // remove any extra pages after the "last" page
         int extraPageDiff = curNumPages - numPages;
         for (int i = 0; i < extraPageDiff; ++i) {
+            PagedViewCellLayout page = (PagedViewCellLayout) getChildAt(numPages);
             removeViewAt(numPages);
         }
         // add any necessary pages
         for (int i = curNumPages; i < numPages; ++i) {
             PagedViewCellLayout layout = new PagedViewCellLayout(getContext());
-            layout.enableHardwareLayers();
+            if (mAllowHardwareLayerCreation) {
+                layout.allowHardwareLayerCreation();
+            }
             layout.setCellCount(mCellCountX, mCellCountY);
             layout.setPadding(mPageLayoutPaddingLeft, mPageLayoutPaddingTop,
                     mPageLayoutPaddingRight, mPageLayoutPaddingBottom);
diff --git a/src/com/android/launcher2/AllAppsTabbed.java b/src/com/android/launcher2/AllAppsTabbed.java
index 6619150..aa828b0 100644
--- a/src/com/android/launcher2/AllAppsTabbed.java
+++ b/src/com/android/launcher2/AllAppsTabbed.java
@@ -41,7 +41,7 @@
 /**
  * Implements a tabbed version of AllApps2D.
  */
-public class AllAppsTabbed extends TabHost implements AllAppsView, LauncherAnimatable {
+public class AllAppsTabbed extends TabHost implements AllAppsView, LauncherTransitionable {
 
     private static final String TAG = "Launcher.AllAppsTabbed";
 
@@ -172,21 +172,36 @@
     }
 
     @Override
-    public void onLauncherAnimationStart() {
-        // Turn on hardware layers for performance
-        setLayerType(LAYER_TYPE_HARDWARE, null);
-        // Re-enable the rendering of the dimmed background in All Apps for performance reasons
-        mLauncher.getWorkspace().disableBackground();
-        mBackground.setVisibility(VISIBLE);
+    public void onLauncherTransitionStart(Animator animation) {
+        if (animation != null) {
+            // Turn on hardware layers for performance
+            setLayerType(LAYER_TYPE_HARDWARE, null);
+            // Re-enable the rendering of the dimmed background in All Apps for performance reasons
+            mLauncher.getWorkspace().disableBackground();
+            mBackground.setVisibility(VISIBLE);
+            // just a sanity check that we don't build a layer before a call to onLayout
+            if (!mFirstLayout) {
+                // force building the layer at the beginning of the animation, so you don't get a
+                // blip early in the animation
+                buildLayer();
+            }
+        }
     }
 
     @Override
-    public void onLauncherAnimationEnd() {
-        setLayerType(LAYER_TYPE_NONE, null);
+    public void onLauncherTransitionEnd(Animator animation) {
+        if (animation != null) {
+            setLayerType(LAYER_TYPE_NONE, null);
+            // To improve the performance of the first time All Apps is run, we initially keep
+            // hardware layers in AllAppsPagedView disabled since AllAppsTabbed itself is drawn in a
+            // hardware layer, and creating additional hardware layers slows down the animation. We
+            // create them here, after the animation is over.
+        }
         // Move the rendering of the dimmed background to workspace after the all apps animation
         // is done, so that the background is not rendered *above* the mini workspace screens
         mLauncher.getWorkspace().enableBackground();
         mBackground.setVisibility(GONE);
+        mAllApps.allowHardwareLayerCreation();
     }
 
     @Override
diff --git a/src/com/android/launcher2/CellLayout.java b/src/com/android/launcher2/CellLayout.java
index 0fb87ba..c1c12b5 100644
--- a/src/com/android/launcher2/CellLayout.java
+++ b/src/com/android/launcher2/CellLayout.java
@@ -571,6 +571,10 @@
         mChildren.draw(canvas);
     }
 
+    void buildChildrenLayer() {
+        mChildren.buildLayer();
+    }
+
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
diff --git a/src/com/android/launcher2/CustomizeTrayTabHost.java b/src/com/android/launcher2/CustomizeTrayTabHost.java
index 5029172..76cfc84 100644
--- a/src/com/android/launcher2/CustomizeTrayTabHost.java
+++ b/src/com/android/launcher2/CustomizeTrayTabHost.java
@@ -16,11 +16,14 @@
 
 package com.android.launcher2;
 
+import android.animation.Animator;
 import android.content.Context;
 import android.util.AttributeSet;
 import android.widget.TabHost;
 
-public class CustomizeTrayTabHost extends TabHost implements LauncherAnimatable {
+public class CustomizeTrayTabHost extends TabHost implements LauncherTransitionable {
+    private boolean mFirstLayout = true;
+
     public CustomizeTrayTabHost(Context context) {
         super(context);
     }
@@ -30,12 +33,28 @@
     }
 
     @Override
-    public void onLauncherAnimationStart() {
-        setLayerType(LAYER_TYPE_HARDWARE, null);
+    public void onLauncherTransitionStart(Animator animation) {
+        if (animation != null) {
+            setLayerType(LAYER_TYPE_HARDWARE, null);
+            // just a sanity check that we don't build a layer before a call to onLayout
+            if (!mFirstLayout) {
+                // force building the layer at the beginning of the animation, so you don't get a
+                // blip early in the animation
+                buildLayer();
+            }
+        }
     }
 
     @Override
-    public void onLauncherAnimationEnd() {
-        setLayerType(LAYER_TYPE_NONE, null);
+    public void onLauncherTransitionEnd(Animator animation) {
+        if (animation != null) {
+            setLayerType(LAYER_TYPE_NONE, null);
+        }
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        mFirstLayout = false;
+        super.onLayout(changed, l, t, r, b);
     }
 }
diff --git a/src/com/android/launcher2/Launcher.java b/src/com/android/launcher2/Launcher.java
index 994c3b8..81569cd 100644
--- a/src/com/android/launcher2/Launcher.java
+++ b/src/com/android/launcher2/Launcher.java
@@ -2781,7 +2781,7 @@
         }
 
         if (animated) {
-            ValueAnimator scaleAnim = ValueAnimator.ofFloat(0f, 1f).setDuration(duration);
+            final ValueAnimator scaleAnim = ValueAnimator.ofFloat(0f, 1f).setDuration(duration);
             scaleAnim.setInterpolator(new Workspace.ZoomOutInterpolator());
             scaleAnim.addUpdateListener(new AnimatorUpdateListener() {
                 public void onAnimationUpdate(ValueAnimator animation) {
@@ -2808,8 +2808,8 @@
                 alphaAnim.start();
             }
 
-            if (toView instanceof LauncherAnimatable) {
-                ((LauncherAnimatable) toView).onLauncherAnimationStart();
+            if (toView instanceof LauncherTransitionable) {
+                ((LauncherTransitionable) toView).onLauncherTransitionStart(scaleAnim);
             }
             scaleAnim.addListener(new AnimatorListenerAdapter() {
                 @Override
@@ -2829,8 +2829,8 @@
                     // not fix that
                     toView.setScaleX(1.0f);
                     toView.setScaleY(1.0f);
-                    if (toView instanceof LauncherAnimatable) {
-                        ((LauncherAnimatable) toView).onLauncherAnimationEnd();
+                    if (toView instanceof LauncherTransitionable) {
+                        ((LauncherTransitionable) toView).onLauncherTransitionEnd(scaleAnim);
                     }
                 }
             });
@@ -2857,6 +2857,10 @@
             toView.setScaleX(1.0f);
             toView.setScaleY(1.0f);
             toView.setVisibility(View.VISIBLE);
+            if (toView instanceof LauncherTransitionable) {
+                ((LauncherTransitionable) toView).onLauncherTransitionStart(null);
+                ((LauncherTransitionable) toView).onLauncherTransitionEnd(null);
+            }
             hideAndShowToolbarButtons(toState, null, null);
         }
     }
@@ -2912,7 +2916,7 @@
                     fromView.setFastScaleY(a * oldScaleY + b * scaleFactor);
                 }
             });
-            ValueAnimator alphaAnim = ValueAnimator.ofFloat(0f, 1f);
+            final ValueAnimator alphaAnim = ValueAnimator.ofFloat(0f, 1f);
             alphaAnim.setDuration(res.getInteger(R.integer.config_allAppsFadeOutTime));
             alphaAnim.setInterpolator(new DecelerateInterpolator(1.5f));
             alphaAnim.addUpdateListener(new AnimatorUpdateListener() {
@@ -2923,15 +2927,15 @@
                     fromView.setFastAlpha(a * 1f + b * 0f);
                 }
             });
-            if (fromView instanceof LauncherAnimatable) {
-                ((LauncherAnimatable) fromView).onLauncherAnimationStart();
+            if (fromView instanceof LauncherTransitionable) {
+                ((LauncherTransitionable) fromView).onLauncherTransitionStart(alphaAnim);
             }
             alphaAnim.addListener(new AnimatorListenerAdapter() {
                 @Override
                 public void onAnimationEnd(Animator animation) {
                     fromView.setVisibility(View.GONE);
-                    if (fromView instanceof LauncherAnimatable) {
-                        ((LauncherAnimatable) fromView).onLauncherAnimationEnd();
+                    if (fromView instanceof LauncherTransitionable) {
+                        ((LauncherTransitionable) fromView).onLauncherTransitionEnd(alphaAnim);
                     }
                 }
             });
@@ -2951,6 +2955,10 @@
             mStateAnimation.start();
         } else {
             fromView.setVisibility(View.GONE);
+            if (fromView instanceof LauncherTransitionable) {
+                ((LauncherTransitionable) fromView).onLauncherTransitionStart(null);
+                ((LauncherTransitionable) fromView).onLauncherTransitionEnd(null);
+            }
             if (!springLoaded) {
                 hideAndShowToolbarButtons(State.WORKSPACE, null, null);
             }
@@ -3753,7 +3761,7 @@
     }
 }
 
-interface LauncherAnimatable {
-    void onLauncherAnimationStart();
-    void onLauncherAnimationEnd();
+interface LauncherTransitionable {
+    void onLauncherTransitionStart(Animator animation);
+    void onLauncherTransitionEnd(Animator animation);
 }
diff --git a/src/com/android/launcher2/PagedViewCellLayout.java b/src/com/android/launcher2/PagedViewCellLayout.java
index 57d41fa..28bb78b 100644
--- a/src/com/android/launcher2/PagedViewCellLayout.java
+++ b/src/com/android/launcher2/PagedViewCellLayout.java
@@ -40,7 +40,8 @@
     private static int sDefaultCellDimensions = 96;
     protected PagedViewCellLayoutChildren mChildren;
     private PagedViewCellLayoutChildren mHolographicChildren;
-    private boolean mUseHardwareLayers = false;
+    private boolean mAllowHardwareLayerCreation = false;
+    private boolean mCreateHardwareLayersIfAllowed = false;
 
     public PagedViewCellLayout(Context context) {
         this(context, null);
@@ -74,8 +75,17 @@
         addView(mHolographicChildren);
     }
 
-    public void enableHardwareLayers() {
-        mUseHardwareLayers = true;
+    public void allowHardwareLayerCreation() {
+        // This is called after the first time we launch into All Apps. Before that point,
+        // there's no need for hardware layers here since there's a hardware layer set on the
+        // parent, AllAppsTabbed, during the AllApps transition -- creating hardware layers here
+        // before the animation is done slows down the animation
+        if (!mAllowHardwareLayerCreation) {
+            mAllowHardwareLayerCreation = true;
+            if (mCreateHardwareLayersIfAllowed) {
+                createHardwareLayers();
+            }
+        }
     }
 
     @Override
@@ -85,13 +95,18 @@
     }
 
     void destroyHardwareLayers() {
-        if (mUseHardwareLayers) {
+        // called when a page is no longer visible (triggered by loadAssociatedPages ->
+        // removeAllViewsOnPage)
+        mCreateHardwareLayersIfAllowed = false;
+        if (mAllowHardwareLayerCreation) {
             mChildren.destroyHardwareLayer();
             mHolographicChildren.destroyHardwareLayer();
         }
     }
     void createHardwareLayers() {
-        if (mUseHardwareLayers) {
+        // called when a page is visible (triggered by loadAssociatedPages -> syncPageItems)
+        mCreateHardwareLayersIfAllowed = true;
+        if (mAllowHardwareLayerCreation) {
             mChildren.createHardwareLayer();
             mHolographicChildren.createHardwareLayer();
         }
@@ -127,7 +142,7 @@
 
             if (child instanceof PagedViewIcon) {
                 PagedViewIcon pagedViewIcon = (PagedViewIcon) child;
-                if (mUseHardwareLayers) {
+                if (mAllowHardwareLayerCreation) {
                     pagedViewIcon.disableCache();
                 }
                 mHolographicChildren.addView(pagedViewIcon.getHolographicOutlineView(), index, lp);
diff --git a/src/com/android/launcher2/PagedViewCellLayoutChildren.java b/src/com/android/launcher2/PagedViewCellLayoutChildren.java
index 27da02a..92ff461 100644
--- a/src/com/android/launcher2/PagedViewCellLayoutChildren.java
+++ b/src/com/android/launcher2/PagedViewCellLayoutChildren.java
@@ -139,12 +139,12 @@
     }
 
     void destroyHardwareLayer() {
-        if (getLayerType() == LAYER_TYPE_HARDWARE) {
+        if (getLayerType() != LAYER_TYPE_NONE) {
             setLayerType(LAYER_TYPE_NONE, null);
         }
     }
     void createHardwareLayer() {
-        if (getLayerType() == LAYER_TYPE_NONE) {
+        if (getLayerType() != LAYER_TYPE_HARDWARE) {
             setLayerType(LAYER_TYPE_HARDWARE, null);
         }
     }
diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java
index 123ab1e..fe40fc1 100644
--- a/src/com/android/launcher2/Workspace.java
+++ b/src/com/android/launcher2/Workspace.java
@@ -1513,6 +1513,11 @@
 
             oldAlphas[i] = cl.getAlpha();
             newAlphas[i] = finalAlpha;
+            if (animated && (oldAlphas[i] != 0f || newAlphas[i] != 0f)) {
+                // if the CellLayout will be visible during the animation, force building its
+                // hardware layer immediately so we don't see a blip later in the animation
+                cl.buildChildrenLayer();
+            }
             if (animated) {
                 oldXs[i] = cl.getX();
                 oldYs[i] = cl.getY();