Multiple performance changes to speed recents invocation/app launching time. (Bug 16987565)

- Reverting changes to the existing thumbnail transition to prevent breaking applications
  that currently depend on that transition.  As a result, we need to create a new, hidden,
  aspect-scaled thumbnail transition, and instead use that thumbnail to animate the
  recents header so that we don't have to wait to do that inside the Recents activity.

  In order for this to work, we also have to ensure that the thumbnail surface destruction
  is synchronized with the application that is currently closing (when going down to
  recents) or opening (when coming back up).  The current thumbnail is destroyed when the
  animation ends, but that can be at least 1 frame before the surface for the animating
  window is destroyed.  We change this by deferring destruction of this thumbnail window
  to the animation that is being closed.

  Especially on the way up, not having to wait for us to hide the header before doing the
  transition up can save us the duration of that first animation (> 100ms).

- Other optimizations:
  * No longer creating a new stack view on each transition to calculate the target rect
  * Removing unnecessary call to get the thumbnail when transitioning up/down (the actual
    window does its own animation.
  * We reduced numerous system calls per task by adding a flag to ignore home-stack tasks
    and caching the activity label and icon (and task description icon).  These caches
    follow the same eviction schemes as the thumbnail and icon cache.

- Also tweaked the touch slop for the nav bar swiping gesture to prevent conflicting with
  tapping on home (Bug 17109581)

Change-Id: Ica697aad788051a9203edd9351c583e1cb038a71
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index f6ec86d..0126675 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -19,7 +19,6 @@
 import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
-import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.Debug;
 import android.os.Handler;
@@ -111,11 +110,14 @@
      * The new window will show briefly and then be gone. */
     public static final int TRANSIT_TASK_OPEN_BEHIND = 16;
 
+    /** Fraction of animation at which the recents thumbnail stays completely transparent */
+    private static final float RECENTS_THUMBNAIL_FADEIN_FRACTION = 0.7f;
     /** Fraction of animation at which the recents thumbnail becomes completely transparent */
-    private static final float RECENTS_THUMBNAIL_FADEOUT_FRACTION = 0.25f;
+    private static final float RECENTS_THUMBNAIL_FADEOUT_FRACTION = 0.3f;
 
     private static final int DEFAULT_APP_TRANSITION_DURATION = 250;
-    private static final int THUMBNAIL_APP_TRANSITION_DURATION = 275;
+    private static final int THUMBNAIL_APP_TRANSITION_DURATION = 300;
+    private static final int THUMBNAIL_APP_TRANSITION_ALPHA_DURATION = 325;
 
     private final Context mContext;
     private final Handler mH;
@@ -127,6 +129,8 @@
     private static final int NEXT_TRANSIT_TYPE_SCALE_UP = 2;
     private static final int NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP = 3;
     private static final int NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN = 4;
+    private static final int NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP = 5;
+    private static final int NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN = 6;
     private int mNextAppTransitionType = NEXT_TRANSIT_TYPE_NONE;
 
     // These are the possible states for the enter/exit activities during a thumbnail transition
@@ -146,6 +150,7 @@
     private int mNextAppTransitionStartY;
     private int mNextAppTransitionStartWidth;
     private int mNextAppTransitionStartHeight;
+    private Rect mNextAppTransitionInsets = new Rect();
 
     private Rect mTmpFromClipRect = new Rect();
     private Rect mTmpToClipRect = new Rect();
@@ -158,7 +163,8 @@
 
     private final int mConfigShortAnimTime;
     private final Interpolator mDecelerateInterpolator;
-    private final Interpolator mThumbnailFadeoutInterpolator;
+    private final Interpolator mThumbnailFadeInInterpolator;
+    private final Interpolator mThumbnailFadeOutInterpolator;
     private final Interpolator mThumbnailFastOutSlowInInterpolator;
 
     private int mCurrentUserId = 0;
@@ -172,14 +178,25 @@
                 com.android.internal.R.interpolator.decelerate_cubic);
         mThumbnailFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
                 com.android.internal.R.interpolator.fast_out_slow_in);
-        mThumbnailFadeoutInterpolator = new Interpolator() {
+        mThumbnailFadeInInterpolator = new Interpolator() {
+            @Override
+            public float getInterpolation(float input) {
+                // Linear response for first fraction, then complete after that.
+                if (input < RECENTS_THUMBNAIL_FADEIN_FRACTION) {
+                    return 0f;
+                }
+                return (input - RECENTS_THUMBNAIL_FADEIN_FRACTION) /
+                        (1f - RECENTS_THUMBNAIL_FADEIN_FRACTION);
+            }
+        };
+        mThumbnailFadeOutInterpolator = new Interpolator() {
             @Override
             public float getInterpolation(float input) {
                 // Linear response for first fraction, then complete after that.
                 if (input < RECENTS_THUMBNAIL_FADEOUT_FRACTION) {
                     return input / RECENTS_THUMBNAIL_FADEOUT_FRACTION;
                 }
-                return 1.0f;
+                return 1f;
             }
         };
     }
@@ -233,9 +250,23 @@
         return mNextAppTransitionThumbnail;
     }
 
-    void getStartingPoint(Point outPoint) {
-        outPoint.x = mNextAppTransitionStartX;
-        outPoint.y = mNextAppTransitionStartY;
+    /** Returns whether the next thumbnail transition is aspect scaled up. */
+    boolean isNextThumbnailTransitionAspectScaled() {
+        return mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP ||
+                mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN;
+    }
+
+    /** Returns whether the next thumbnail transition is scaling up. */
+    boolean isNextThumbnailTransitionScaleUp() {
+        return mNextAppTransitionScaleUp;
+    }
+
+    int getStartingX() {
+        return mNextAppTransitionStartX;
+    }
+
+    int getStartingY() {
+        return mNextAppTransitionStartY;
     }
 
     void prepare() {
@@ -372,7 +403,7 @@
             scale.setInterpolator(mDecelerateInterpolator);
 
             Animation alpha = new AlphaAnimation(0, 1);
-            alpha.setInterpolator(mThumbnailFadeoutInterpolator);
+            alpha.setInterpolator(mThumbnailFadeOutInterpolator);
 
             AnimationSet set = new AnimationSet(false);
             set.addAnimation(scale);
@@ -417,7 +448,9 @@
      */
     Animation prepareThumbnailAnimationWithDuration(Animation a, int appWidth, int appHeight,
             int duration, Interpolator interpolator) {
-        a.setDuration(duration);
+        if (duration > 0) {
+            a.setDuration(duration);
+        }
         a.setFillAfter(true);
         a.setInterpolator(interpolator);
         a.initialize(appWidth, appHeight, appWidth, appHeight);
@@ -468,50 +501,73 @@
      * This animation runs for the thumbnail that gets cross faded with the enter/exit activity
      * when a thumbnail is specified with the activity options.
      */
-    Animation createThumbnailScaleAnimationLocked(int appWidth, int appHeight, int transit) {
+    Animation createThumbnailAspectScaleAnimationLocked(int appWidth, int appHeight,
+            int deviceWidth, int transit) {
         Animation a;
         final int thumbWidthI = mNextAppTransitionThumbnail.getWidth();
         final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
         final int thumbHeightI = mNextAppTransitionThumbnail.getHeight();
         final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
 
+        float scaleW = deviceWidth / thumbWidth;
+        float unscaledWidth = deviceWidth;
+        float unscaledHeight = thumbHeight * scaleW;
+        float unscaledStartY = mNextAppTransitionStartY - (unscaledHeight - thumbHeight) / 2f;
         if (mNextAppTransitionScaleUp) {
-            // Animation for the thumbnail zooming from its initial size to the full screen
-            float scaleW = appWidth / thumbWidth;
-            float scaleH = appHeight / thumbHeight;
-            Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH,
-                    computePivot(mNextAppTransitionStartX, 1 / scaleW),
-                    computePivot(mNextAppTransitionStartY, 1 / scaleH));
-            scale.setInterpolator(mDecelerateInterpolator);
-
+            // Animation up from the thumbnail to the full screen
+            Animation scale = new ScaleAnimation(1f, scaleW, 1f, scaleW,
+                    mNextAppTransitionStartX + (thumbWidth / 2f),
+                    mNextAppTransitionStartY + (thumbHeight / 2f));
+            scale.setInterpolator(mThumbnailFastOutSlowInInterpolator);
+            scale.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
             Animation alpha = new AlphaAnimation(1, 0);
-            alpha.setInterpolator(mThumbnailFadeoutInterpolator);
+            alpha.setInterpolator(mThumbnailFadeOutInterpolator);
+            alpha.setDuration(THUMBNAIL_APP_TRANSITION_ALPHA_DURATION);
+            Animation translate = new TranslateAnimation(0, 0, 0, -unscaledStartY +
+                    mNextAppTransitionInsets.top);
+            translate.setInterpolator(mThumbnailFastOutSlowInInterpolator);
+            translate.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
 
             // This AnimationSet uses the Interpolators assigned above.
             AnimationSet set = new AnimationSet(false);
             set.addAnimation(scale);
             set.addAnimation(alpha);
+            set.addAnimation(translate);
             a = set;
         } else {
-            // Animation for the thumbnail zooming down from the full screen to its final size
-            float scaleW = appWidth / thumbWidth;
-            float scaleH = appHeight / thumbHeight;
-            a = new ScaleAnimation(scaleW, 1, scaleH, 1,
-                    computePivot(mNextAppTransitionStartX, 1 / scaleW),
-                    computePivot(mNextAppTransitionStartY, 1 / scaleH));
-        }
+            // Animation down from the full screen to the thumbnail
+            Animation scale = new ScaleAnimation(scaleW, 1f, scaleW, 1f,
+                    mNextAppTransitionStartX + (thumbWidth / 2f),
+                    mNextAppTransitionStartY + (thumbHeight / 2f));
+            scale.setInterpolator(mThumbnailFastOutSlowInInterpolator);
+            scale.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
+            Animation alpha = new AlphaAnimation(0f, 1f);
+            alpha.setInterpolator(mThumbnailFadeInInterpolator);
+            alpha.setDuration(THUMBNAIL_APP_TRANSITION_ALPHA_DURATION);
+            Animation translate = new TranslateAnimation(0, 0, -unscaledStartY +
+                    mNextAppTransitionInsets.top, 0);
+            translate.setInterpolator(mThumbnailFastOutSlowInInterpolator);
+            translate.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
 
-        return prepareThumbnailAnimation(a, appWidth, appHeight, transit);
+            // This AnimationSet uses the Interpolators assigned above.
+            AnimationSet set = new AnimationSet(false);
+            set.addAnimation(scale);
+            set.addAnimation(alpha);
+            set.addAnimation(translate);
+            a = set;
+
+        }
+        return prepareThumbnailAnimationWithDuration(a, appWidth, appHeight, 0,
+                mThumbnailFastOutSlowInInterpolator);
     }
 
     /**
      * This alternate animation is created when we are doing a thumbnail transition, for the
      * activity that is leaving, and the activity that is entering.
      */
-    Animation createAlternateThumbnailEnterExitAnimationLocked(int thumbTransitState, int appWidth,
-                                                    int appHeight, int orientation, int transit,
-                                                    Rect containingFrame, Rect contentInsets,
-                                                    boolean isFullScreen) {
+    Animation createAspectScaledThumbnailEnterExitAnimationLocked(int thumbTransitState,
+            int appWidth, int appHeight, int orientation, int transit, Rect containingFrame,
+            Rect contentInsets, boolean isFullScreen) {
         Animation a;
         final int thumbWidthI = mNextAppTransitionThumbnail.getWidth();
         final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
@@ -524,7 +580,7 @@
 
         switch (thumbTransitState) {
             case THUMBNAIL_TRANSITION_ENTER_SCALE_UP: {
-                // Entering app scales up with the thumbnail
+                // App window scaling up to become full screen
                 if (orientation == Configuration.ORIENTATION_PORTRAIT) {
                     // In portrait, we scale the width and clip to the top/left square
                     scale = thumbWidth / appWidth;
@@ -550,16 +606,15 @@
                     mTmpFromClipRect.right = (mTmpFromClipRect.left + unscaledThumbWidth);
                     mTmpToClipRect.set(containingFrame);
                 }
+                mNextAppTransitionInsets.set(contentInsets);
 
                 Animation scaleAnim = new ScaleAnimation(scale, 1, scale, 1,
                         computePivot(mNextAppTransitionStartX, scale),
                         computePivot(mNextAppTransitionStartY, scale));
-                Animation alphaAnim = new AlphaAnimation(1, 1);
                 Animation clipAnim = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect);
                 Animation translateAnim = new TranslateAnimation(0, 0, -scaledTopDecor, 0);
 
                 AnimationSet set = new AnimationSet(true);
-                set.addAnimation(alphaAnim);
                 set.addAnimation(clipAnim);
                 set.addAnimation(scaleAnim);
                 set.addAnimation(translateAnim);
@@ -567,26 +622,29 @@
                 break;
             }
             case THUMBNAIL_TRANSITION_EXIT_SCALE_UP: {
-                // Exiting app while the thumbnail is scaling up should fade
+                // Previous app window during the scale up
                 if (transit == TRANSIT_WALLPAPER_INTRA_OPEN) {
-                    // Fade out while bringing up selected activity. This keeps the
-                    // current activity from showing through a launching wallpaper
+                    // Fade out the source activity if we are animating to a wallpaper
                     // activity.
                     a = new AlphaAnimation(1, 0);
                 } else {
-                    // noop animation
                     a = new AlphaAnimation(1, 1);
                 }
                 break;
             }
             case THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN: {
-                // Entering the other app, it should just be visible while we scale the thumbnail
-                // down above it
-                a = new AlphaAnimation(1, 1);
+                // Target app window during the scale down
+                if (transit == TRANSIT_WALLPAPER_INTRA_OPEN) {
+                    // Fade in the destination activity if we are animating from a wallpaper
+                    // activity.
+                    a = new AlphaAnimation(0, 1);
+                } else {
+                    a = new AlphaAnimation(1, 1);
+                }
                 break;
             }
             case THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN: {
-                // Exiting the current app, the app should scale down with the thumbnail
+                // App window scaling down from full screen
                 if (orientation == Configuration.ORIENTATION_PORTRAIT) {
                     // In portrait, we scale the width and clip to the top/left square
                     scale = thumbWidth / appWidth;
@@ -612,16 +670,15 @@
                     }
                     mTmpToClipRect.right = (mTmpToClipRect.left + unscaledThumbWidth);
                 }
+                mNextAppTransitionInsets.set(contentInsets);
 
                 Animation scaleAnim = new ScaleAnimation(1, scale, 1, scale,
                         computePivot(mNextAppTransitionStartX, scale),
                         computePivot(mNextAppTransitionStartY, scale));
-                Animation alphaAnim = new AlphaAnimation(1, 1);
                 Animation clipAnim = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect);
                 Animation translateAnim = new TranslateAnimation(0, 0, 0, -scaledTopDecor);
 
                 AnimationSet set = new AnimationSet(true);
-                set.addAnimation(alphaAnim);
                 set.addAnimation(clipAnim);
                 set.addAnimation(scaleAnim);
                 set.addAnimation(translateAnim);
@@ -639,6 +696,46 @@
     }
 
     /**
+     * This animation runs for the thumbnail that gets cross faded with the enter/exit activity
+     * when a thumbnail is specified with the activity options.
+     */
+    Animation createThumbnailScaleAnimationLocked(int appWidth, int appHeight, int transit) {
+        Animation a;
+        final int thumbWidthI = mNextAppTransitionThumbnail.getWidth();
+        final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
+        final int thumbHeightI = mNextAppTransitionThumbnail.getHeight();
+        final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
+
+        if (mNextAppTransitionScaleUp) {
+            // Animation for the thumbnail zooming from its initial size to the full screen
+            float scaleW = appWidth / thumbWidth;
+            float scaleH = appHeight / thumbHeight;
+            Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH,
+                    computePivot(mNextAppTransitionStartX, 1 / scaleW),
+                    computePivot(mNextAppTransitionStartY, 1 / scaleH));
+            scale.setInterpolator(mDecelerateInterpolator);
+
+            Animation alpha = new AlphaAnimation(1, 0);
+            alpha.setInterpolator(mThumbnailFadeOutInterpolator);
+
+            // This AnimationSet uses the Interpolators assigned above.
+            AnimationSet set = new AnimationSet(false);
+            set.addAnimation(scale);
+            set.addAnimation(alpha);
+            a = set;
+        } else {
+            // Animation for the thumbnail zooming down from the full screen to its final size
+            float scaleW = appWidth / thumbWidth;
+            float scaleH = appHeight / thumbHeight;
+            a = new ScaleAnimation(scaleW, 1, scaleH, 1,
+                    computePivot(mNextAppTransitionStartX, 1 / scaleW),
+                    computePivot(mNextAppTransitionStartY, 1 / scaleH));
+        }
+
+        return prepareThumbnailAnimation(a, appWidth, appHeight, transit);
+    }
+
+    /**
      * This animation is created when we are doing a thumbnail transition, for the activity that is
      * leaving, and the activity that is entering.
      */
@@ -747,12 +844,26 @@
                 mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN) {
             mNextAppTransitionScaleUp =
                     (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP);
-            a = createAlternateThumbnailEnterExitAnimationLocked(
+            a = createThumbnailEnterExitAnimationLocked(getThumbnailTransitionState(enter),
+                    appWidth, appHeight, transit);
+            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) {
+                String animName = mNextAppTransitionScaleUp ?
+                        "ANIM_THUMBNAIL_SCALE_UP" : "ANIM_THUMBNAIL_SCALE_DOWN";
+                Slog.v(TAG, "applyAnimation:"
+                        + " anim=" + a + " nextAppTransition=" + animName
+                        + " transit=" + transit + " isEntrance=" + enter
+                        + " Callers=" + Debug.getCallers(3));
+            }
+        } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP ||
+                mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN) {
+            mNextAppTransitionScaleUp =
+                    (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP);
+            a = createAspectScaledThumbnailEnterExitAnimationLocked(
                     getThumbnailTransitionState(enter), appWidth, appHeight, orientation,
                     transit, containingFrame, contentInsets, isFullScreen);
             if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) {
                 String animName = mNextAppTransitionScaleUp ?
-                        "ANIM_THUMBNAIL_SCALE_UP" : "ANIM_THUMBNAIL_SCALE_DOWN";
+                        "ANIM_THUMBNAIL_ASPECT_SCALE_UP" : "ANIM_THUMBNAIL_ASPECT_SCALE_DOWN";
                 Slog.v(TAG, "applyAnimation:"
                         + " anim=" + a + " nextAppTransition=" + animName
                         + " transit=" + transit + " isEntrance=" + enter
@@ -881,6 +992,23 @@
         }
     }
 
+    void overridePendingAppTransitionAspectScaledThumb(Bitmap srcThumb, int startX, int startY,
+            IRemoteCallback startedCallback, boolean scaleUp) {
+        if (isTransitionSet()) {
+            mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP
+                    : NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN;
+            mNextAppTransitionPackage = null;
+            mNextAppTransitionThumbnail = srcThumb;
+            mNextAppTransitionScaleUp = scaleUp;
+            mNextAppTransitionStartX = startX;
+            mNextAppTransitionStartY = startY;
+            postAnimationCallback();
+            mNextAppTransitionCallback = startedCallback;
+        } else {
+            postAnimationCallback();
+        }
+    }
+
     @Override
     public String toString() {
         return "mNextAppTransition=0x" + Integer.toHexString(mNextAppTransition);
@@ -966,6 +1094,10 @@
                 return "NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP";
             case NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN:
                 return "NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN";
+            case NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP:
+                return "NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP";
+            case NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN:
+                return "NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN";
             default:
                 return "unknown type=" + mNextAppTransitionType;
         }
@@ -998,6 +1130,8 @@
                 break;
             case NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP:
             case NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN:
+            case NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP:
+            case NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN:
                 pw.print("  mNextAppTransitionThumbnail=");
                         pw.print(mNextAppTransitionThumbnail);
                         pw.print(" mNextAppTransitionStartX=");