Merge "Integrating snapshot logging in Launcher" into ub-launcher3-master
diff --git a/quickstep/robolectric_tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java b/quickstep/robolectric_tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
index 5491daa..6c88e55 100644
--- a/quickstep/robolectric_tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
+++ b/quickstep/robolectric_tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
@@ -144,7 +144,7 @@
                     LauncherActivityInterface.INSTANCE);
             tvs.setDp(mDeviceProfile);
 
-            int launcherRotation = DisplayController.INSTANCE.get(mContext).getInfo().rotation;
+            int launcherRotation = DisplayController.getDefaultDisplay(mContext).getInfo().rotation;
             if (mAppRotation < 0) {
                 mAppRotation = launcherRotation;
             }
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index cc109f6..a1988a1 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -95,8 +95,10 @@
 import com.android.quickstep.util.InputConsumerProxy;
 import com.android.quickstep.util.MotionPauseDetector;
 import com.android.quickstep.util.ProtoTracer;
+import com.android.quickstep.util.RecentsOrientedState;
 import com.android.quickstep.util.RectFSpringAnim;
 import com.android.quickstep.util.SurfaceTransactionApplier;
+import com.android.quickstep.util.SwipePipToHomeAnimator;
 import com.android.quickstep.util.TransformParams;
 import com.android.quickstep.views.LiveTileOverlay;
 import com.android.quickstep.views.RecentsView;
@@ -233,6 +235,10 @@
 
     private final Runnable mOnDeferredActivityLaunch = this::onDeferredActivityLaunch;
 
+    private static final long SWIPE_PIP_TO_HOME_DURATION = 425;
+    private SwipePipToHomeAnimator mSwipePipToHomeAnimator;
+    protected boolean mIsSwipingPipToHome;
+
     public AbsSwipeUpHandler(Context context, RecentsAnimationDeviceState deviceState,
             TaskAnimationManager taskAnimationManager, GestureState gestureState,
             long touchTimeMs, boolean continuingLastGesture,
@@ -618,13 +624,6 @@
 
         updateSysUiFlags(mCurrentShift.value);
         applyWindowTransform();
-        if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
-            if (mRecentsAnimationTargets != null) {
-                LiveTileOverlay.INSTANCE.update(
-                        mTaskViewSimulator.getCurrentRect(),
-                        mTaskViewSimulator.getCurrentCornerRadius());
-            }
-        }
 
         updateLauncherTransitionProgress();
     }
@@ -800,6 +799,8 @@
     }
 
     private void onSettledOnEndTarget() {
+        // Fast-finish the attaching animation if it's still running.
+        maybeUpdateRecentsAttachedState(false);
         final GestureEndTarget endTarget = mGestureState.getEndTarget();
         switch (endTarget) {
             case HOME:
@@ -1053,25 +1054,46 @@
 
         if (mGestureState.getEndTarget() == HOME) {
             mTaskViewSimulator.setDrawsBelowRecents(false);
-            HomeAnimationFactory homeAnimFactory = createHomeAnimationFactory(duration);
-            RectFSpringAnim windowAnim = createWindowAnimationToHome(start, homeAnimFactory);
-            windowAnim.addAnimatorListener(new AnimationSuccessListener() {
-                @Override
-                public void onAnimationSuccess(Animator animator) {
-                    if (mRecentsAnimationController == null) {
-                        // If the recents animation is interrupted, we still end the running
-                        // animation (not canceled) so this is still called. In that case, we can
-                        // skip doing any future work here for the current gesture.
-                        return;
-                    }
-                    // Finalize the state and notify of the change
-                    mGestureState.setState(STATE_END_TARGET_ANIMATION_FINISHED);
-                }
-            });
             getOrientationHandler().adjustFloatingIconStartVelocity(velocityPxPerMs);
-            windowAnim.start(mContext, velocityPxPerMs);
+            final RemoteAnimationTargetCompat runningTaskTarget = mRecentsAnimationTargets != null
+                    ? mRecentsAnimationTargets.findTask(mGestureState.getRunningTaskId())
+                    : null;
+            HomeAnimationFactory homeAnimFactory = createHomeAnimationFactory(duration);
+            mIsSwipingPipToHome = homeAnimFactory.supportSwipePipToHome()
+                    && runningTaskTarget != null
+                    && runningTaskTarget.pictureInPictureParams != null
+                    && TaskInfoCompat.isAutoEnterPipEnabled(
+                            runningTaskTarget.pictureInPictureParams)
+                    && TaskInfoCompat.getPipSourceRectHint(
+                            runningTaskTarget.pictureInPictureParams) != null;
+            if (mIsSwipingPipToHome) {
+                mSwipePipToHomeAnimator = getSwipePipToHomeAnimator(
+                        homeAnimFactory, runningTaskTarget);
+                mSwipePipToHomeAnimator.setDuration(SWIPE_PIP_TO_HOME_DURATION);
+                mSwipePipToHomeAnimator.setInterpolator(interpolator);
+                mSwipePipToHomeAnimator.setFloatValues(0f, 1f);
+                mSwipePipToHomeAnimator.start();
+                mRunningWindowAnim = RunningWindowAnim.wrap(mSwipePipToHomeAnimator);
+            } else {
+                mSwipePipToHomeAnimator = null;
+                RectFSpringAnim windowAnim = createWindowAnimationToHome(start, homeAnimFactory);
+                windowAnim.addAnimatorListener(new AnimationSuccessListener() {
+                    @Override
+                    public void onAnimationSuccess(Animator animator) {
+                        if (mRecentsAnimationController == null) {
+                            // If the recents animation is interrupted, we still end the running
+                            // animation (not canceled) so this is still called. In that case,
+                            // we can skip doing any future work here for the current gesture.
+                            return;
+                        }
+                        // Finalize the state and notify of the change
+                        mGestureState.setState(STATE_END_TARGET_ANIMATION_FINISHED);
+                    }
+                });
+                windowAnim.start(mContext, velocityPxPerMs);
+                mRunningWindowAnim = RunningWindowAnim.wrap(windowAnim);
+            }
             homeAnimFactory.playAtomicAnimation(velocityPxPerMs.y);
-            mRunningWindowAnim = RunningWindowAnim.wrap(windowAnim);
             mLauncherTransitionController = null;
         } else {
             ValueAnimator windowAnim = mCurrentShift.animateToValue(start, end);
@@ -1115,6 +1137,47 @@
         }
     }
 
+    private SwipePipToHomeAnimator getSwipePipToHomeAnimator(HomeAnimationFactory homeAnimFactory,
+            RemoteAnimationTargetCompat runningTaskTarget) {
+        // Directly animate the app to PiP (picture-in-picture) mode
+        final ActivityManager.RunningTaskInfo taskInfo = mGestureState.getRunningTask();
+        final RecentsOrientedState orientationState = mTaskViewSimulator.getOrientationState();
+        final Rect destinationBounds = SystemUiProxy.INSTANCE.get(mContext)
+                .startSwipePipToHome(taskInfo.topActivity,
+                        TaskInfoCompat.getTopActivityInfo(taskInfo),
+                        runningTaskTarget.pictureInPictureParams,
+                        orientationState.getRecentsActivityRotation(),
+                        mDp.hotseatBarSizePx);
+        final SwipePipToHomeAnimator swipePipToHomeAnimator = new SwipePipToHomeAnimator(
+                runningTaskTarget.taskId,
+                taskInfo.topActivity,
+                runningTaskTarget.leash.getSurfaceControl(),
+                TaskInfoCompat.getPipSourceRectHint(runningTaskTarget.pictureInPictureParams),
+                TaskInfoCompat.getWindowConfigurationBounds(taskInfo),
+                destinationBounds);
+        swipePipToHomeAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                // Ensure Launcher ends in NORMAL state, we intentionally skip other callbacks
+                // since they are not relevant in this swipe-pip-to-home case.
+                homeAnimFactory.createActivityAnimationToHome().dispatchOnStart();
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                if (mRecentsAnimationController == null) {
+                    // If the recents animation is interrupted, we still end the running
+                    // animation (not canceled) so this is still called. In that case, we can
+                    // skip doing any future work here for the current gesture.
+                    return;
+                }
+                // Finalize the state and notify of the change
+                mGestureState.setState(STATE_END_TARGET_ANIMATION_FINISHED);
+            }
+        });
+        return swipePipToHomeAnimator;
+    }
+
     private void computeRecentsScrollIfInvisible() {
         if (mRecentsView != null && mRecentsView.getVisibility() != View.VISIBLE) {
             // Views typically don't compute scroll when invisible as an optimization,
@@ -1372,6 +1435,7 @@
             // If there are no targets or the animation not started, then there is nothing to finish
             mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED);
         } else {
+            maybeFinishSwipePipToHome();
             finishRecentsControllerToHome(
                     () -> mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED));
         }
@@ -1379,6 +1443,22 @@
         doLogGesture(HOME, mRecentsView == null ? null : mRecentsView.getCurrentPageTaskView());
     }
 
+    /**
+     * Resets the {@link #mIsSwipingPipToHome} and notifies SysUI that transition is finished
+     * if applicable. This should happen before {@link #finishRecentsControllerToHome(Runnable)}.
+     */
+    private void maybeFinishSwipePipToHome() {
+        if (mIsSwipingPipToHome && mSwipePipToHomeAnimator != null) {
+            SystemUiProxy.INSTANCE.get(mContext).stopSwipePipToHome(
+                    mSwipePipToHomeAnimator.getComponentName(),
+                    mSwipePipToHomeAnimator.getDestinationBounds());
+            mRecentsAnimationController.setFinishTaskBounds(
+                    mSwipePipToHomeAnimator.getTaskId(),
+                    mSwipePipToHomeAnimator.getDestinationBounds());
+            mIsSwipingPipToHome = false;
+        }
+    }
+
     protected abstract void finishRecentsControllerToHome(Runnable callback);
 
     private void setupLauncherUiAfterSwipeUpToRecentsAnimation() {
@@ -1616,6 +1696,11 @@
             }
             mTaskViewSimulator.apply(mTransformParams);
         }
+        if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mRecentsAnimationTargets != null) {
+            LiveTileOverlay.INSTANCE.update(
+                    mTaskViewSimulator.getCurrentRect(),
+                    mTaskViewSimulator.getCurrentCornerRadius());
+        }
         ProtoTracer.INSTANCE.get(mContext).scheduleFrameUpdate();
     }
 
diff --git a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
index 4411455..842fb84 100644
--- a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
+++ b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
@@ -56,6 +56,7 @@
             final TaskView runningTaskView = mRecentsView.getRunningTaskView();
             final View workspaceView;
             if (runningTaskView != null
+                    && !mIsSwipingPipToHome
                     && runningTaskView.getTask().key.getComponent() != null) {
                 workspaceView = mActivity.getWorkspace().getFirstMatchForAppClose(
                         runningTaskView.getTask().key.getComponent().getPackageName(),
@@ -140,5 +141,10 @@
             new StaggeredWorkspaceAnim(mActivity, velocity,
                     true /* animateOverviewScrim */).start();
         }
+
+        @Override
+        public boolean supportSwipePipToHome() {
+            return true;
+        }
     }
 }
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationController.java b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
index 62bbf4d..7de658c 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationController.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
@@ -18,6 +18,8 @@
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 
+import android.graphics.Rect;
+
 import androidx.annotation.NonNull;
 import androidx.annotation.UiThread;
 
@@ -140,6 +142,18 @@
     }
 
     /**
+     * Sets the final bounds on a Task. This is used by Launcher to notify the system that
+     * animating Activity to PiP has completed and the associated task surface should be updated
+     * accordingly. This should be called before `finish`
+     * @param taskId for which the leash should be updated
+     * @param destinationBounds bounds of the final PiP window
+     */
+    public void setFinishTaskBounds(int taskId, Rect destinationBounds) {
+        UI_HELPER_EXECUTOR.execute(
+                () -> mController.setFinishTaskBounds(taskId, destinationBounds));
+    }
+
+    /**
      * Enables the input consumer to start intercepting touches in the app window.
      */
     public void enableInputConsumer() {
diff --git a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
index 7406dea..8f7ec3b 100644
--- a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
+++ b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
@@ -165,6 +165,14 @@
         public void update(RectF currentRect, float progress, float radius) { }
 
         public void onCancel() { }
+
+        /**
+         * @return {@code true} if this factory supports animating an Activity to PiP window on
+         * swiping up to home.
+         */
+        public boolean supportSwipePipToHome() {
+            return false;
+        }
     }
 
     /**
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index 5520ef7..46c7768 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -41,6 +41,7 @@
 import android.view.View;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
 import com.android.launcher3.BaseActivity;
 import com.android.launcher3.DeviceProfile;
@@ -129,6 +130,21 @@
         return taskView;
     }
 
+    public static void createRecentsWindowAnimator(TaskView v, boolean skipViewChanges,
+            RemoteAnimationTargetCompat[] appTargets,
+            RemoteAnimationTargetCompat[] wallpaperTargets, DepthController depthController,
+            PendingAnimation out) {
+        boolean isRunningTask = v.isRunningTask();
+        TransformParams params = null;
+        TaskViewSimulator tsv = null;
+        if (ENABLE_QUICKSTEP_LIVE_TILE.get() && isRunningTask) {
+            params = v.getRecentsView().getLiveTileParams();
+            tsv = v.getRecentsView().getLiveTileTaskViewSimulator();
+        }
+        createRecentsWindowAnimator(v, skipViewChanges, appTargets, wallpaperTargets,
+                depthController, out, params, tsv);
+    }
+
     /**
      * Creates an animation that controls the window of the opening targets for the recents launch
      * animation.
@@ -136,19 +152,25 @@
     public static void createRecentsWindowAnimator(TaskView v, boolean skipViewChanges,
             RemoteAnimationTargetCompat[] appTargets,
             RemoteAnimationTargetCompat[] wallpaperTargets, DepthController depthController,
-            PendingAnimation out) {
+            PendingAnimation out, @Nullable TransformParams params,
+            @Nullable TaskViewSimulator tsv) {
         boolean isQuickSwitch = v.isEndQuickswitchCuj();
         v.setEndQuickswitchCuj(false);
 
-        SurfaceTransactionApplier applier = new SurfaceTransactionApplier(v);
+        boolean inLiveTileMode =
+                ENABLE_QUICKSTEP_LIVE_TILE.get() && v.getRecentsView().getRunningTaskIndex() != -1;
         final RemoteAnimationTargets targets =
                 new RemoteAnimationTargets(appTargets, wallpaperTargets,
-                        ENABLE_QUICKSTEP_LIVE_TILE.get() ? MODE_CLOSING : MODE_OPENING);
-        targets.addReleaseCheck(applier);
+                        inLiveTileMode ? MODE_CLOSING : MODE_OPENING);
 
-        TransformParams params = new TransformParams()
+        if (params == null) {
+            SurfaceTransactionApplier applier = new SurfaceTransactionApplier(v);
+            targets.addReleaseCheck(applier);
+
+            params = new TransformParams()
                     .setSyncTransactionApplier(applier)
                     .setTargetSet(targets);
+        }
 
         final RecentsView recentsView = v.getRecentsView();
         int taskIndex = recentsView.indexOfChild(v);
@@ -162,8 +184,9 @@
         int displayRotation = DisplayController.getDefaultDisplay(context).getInfo().rotation;
 
         TaskViewSimulator topMostSimulator = null;
-        if (targets.apps.length > 0) {
-            TaskViewSimulator tsv = new TaskViewSimulator(context, recentsView.getSizeStrategy());
+
+        if (tsv == null && targets.apps.length > 0) {
+            tsv = new TaskViewSimulator(context, recentsView.getSizeStrategy());
             tsv.setDp(dp);
             tsv.setLayoutRotation(displayRotation, displayRotation);
             tsv.setPreview(targets.apps[targets.apps.length - 1]);
@@ -171,19 +194,24 @@
             tsv.recentsViewScale.value = 1;
             tsv.setScroll(startScroll);
 
+            // Fade in the task during the initial 20% of the animation
+            out.addFloat(params, TransformParams.TARGET_ALPHA, 0, 1,
+                    clampToProgress(LINEAR, 0, 0.2f));
+        }
+
+        if (tsv != null) {
             out.setFloat(tsv.fullScreenProgress,
                     AnimatedFloat.VALUE, 1, TOUCH_RESPONSE_INTERPOLATOR);
             out.setFloat(tsv.recentsViewScale,
                     AnimatedFloat.VALUE, tsv.getFullScreenScale(), TOUCH_RESPONSE_INTERPOLATOR);
             out.setInt(tsv, TaskViewSimulator.SCROLL, 0, TOUCH_RESPONSE_INTERPOLATOR);
 
-            out.addOnFrameCallback(() -> tsv.apply(params));
+            TaskViewSimulator finalTsv = tsv;
+            TransformParams finalParams = params;
+            out.addOnFrameCallback(() -> finalTsv.apply(finalParams));
             topMostSimulator = tsv;
         }
 
-        // Fade in the task during the initial 20% of the animation
-        out.addFloat(params, TransformParams.TARGET_ALPHA, 0, 1, clampToProgress(LINEAR, 0, 0.2f));
-
         if (!skipViewChanges && parallaxCenterAndAdjacentTask && topMostSimulator != null) {
             out.addFloat(v, VIEW_ALPHA, 1, 0, clampToProgress(LINEAR, 0.2f, 0.4f));
 
diff --git a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
index d0f6879..da5f59e 100644
--- a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
+++ b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
@@ -193,7 +193,10 @@
                 if (isFirstDetectedPause) {
                     mOnMotionPauseListener.onMotionPauseDetected();
                 }
-                mOnMotionPauseListener.onMotionPauseChanged(mIsPaused);
+                // Null check again as onMotionPauseDetected() maybe have called clear().
+                if (mOnMotionPauseListener != null) {
+                    mOnMotionPauseListener.onMotionPauseChanged(mIsPaused);
+                }
             }
         }
     }
diff --git a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
new file mode 100644
index 0000000..87fee79
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2020 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.quickstep.util;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.RectEvaluator;
+import android.animation.ValueAnimator;
+import android.content.ComponentName;
+import android.graphics.Rect;
+import android.view.SurfaceControl;
+
+import androidx.annotation.NonNull;
+
+import com.android.systemui.shared.pip.PipSurfaceTransactionHelper;
+
+/**
+ * An {@link Animator} that animates an Activity to PiP (picture-in-picture) window when
+ * swiping up (in gesture navigation mode). Note that this class is derived from
+ * {@link com.android.wm.shell.pip.PipAnimationController.PipTransitionAnimator}.
+ *
+ * TODO: consider sharing this class including the animator and leash operations between
+ * Launcher and SysUI. Also, there should be one source of truth for the corner radius of the
+ * PiP window, which would ideally be on SysUI side as well.
+ */
+public class SwipePipToHomeAnimator extends ValueAnimator implements
+        ValueAnimator.AnimatorUpdateListener {
+    private final int mTaskId;
+    private final ComponentName mComponentName;
+    private final SurfaceControl mLeash;
+    private final Rect mStartBounds = new Rect();
+    private final Rect mDestinationBounds = new Rect();
+    private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
+
+    /** for calculating the transform in {@link #onAnimationUpdate(ValueAnimator)} */
+    private final RectEvaluator mRectEvaluator = new RectEvaluator(new Rect());
+    private final RectEvaluator mInsetsEvaluator = new RectEvaluator(new Rect());
+    private final Rect mSourceHintRectInsets = new Rect();
+    private final Rect mSourceInsets = new Rect();
+
+    /**
+     * Flag to avoid the double-end problem since the leash would have been released
+     * after the first end call and any further operations upon it would lead to NPE.
+     */
+    private boolean mHasAnimationEnded;
+
+    public SwipePipToHomeAnimator(int taskId,
+            @NonNull ComponentName componentName,
+            @NonNull SurfaceControl leash,
+            @NonNull Rect sourceRectHint,
+            @NonNull Rect startBounds,
+            @NonNull Rect destinationBounds) {
+        mTaskId = taskId;
+        mComponentName = componentName;
+        mLeash = leash;
+        mStartBounds.set(startBounds);
+        mDestinationBounds.set(destinationBounds);
+        mSurfaceTransactionHelper = new PipSurfaceTransactionHelper();
+
+        mSourceHintRectInsets.set(sourceRectHint.left - startBounds.left,
+                sourceRectHint.top - startBounds.top,
+                startBounds.right - sourceRectHint.right,
+                startBounds.bottom - sourceRectHint.bottom);
+
+        addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                SwipePipToHomeAnimator.this.onAnimationEnd();
+            }
+        });
+        addUpdateListener(this);
+    }
+
+    @Override
+    public void onAnimationUpdate(ValueAnimator animator) {
+        if (mHasAnimationEnded) return;
+
+        final float fraction = animator.getAnimatedFraction();
+        final Rect bounds = mRectEvaluator.evaluate(fraction, mStartBounds, mDestinationBounds);
+        final Rect insets = mInsetsEvaluator.evaluate(fraction, mSourceInsets,
+                mSourceHintRectInsets);
+        final SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
+        mSurfaceTransactionHelper.scaleAndCrop(tx, mLeash, mStartBounds, bounds, insets);
+        mSurfaceTransactionHelper.resetCornerRadius(tx, mLeash);
+        tx.apply();
+    }
+
+    public int getTaskId() {
+        return mTaskId;
+    }
+
+    public ComponentName getComponentName() {
+        return mComponentName;
+    }
+
+    public Rect getDestinationBounds() {
+        return mDestinationBounds;
+    }
+
+    private void onAnimationEnd() {
+        if (mHasAnimationEnded) return;
+
+        final SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
+        mSurfaceTransactionHelper.reset(tx, mLeash, mDestinationBounds);
+        tx.apply();
+        mHasAnimationEnded = true;
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/views/LiveTileOverlay.java b/quickstep/src/com/android/quickstep/views/LiveTileOverlay.java
index c6c2d7e..f6eb0e2 100644
--- a/quickstep/src/com/android/quickstep/views/LiveTileOverlay.java
+++ b/quickstep/src/com/android/quickstep/views/LiveTileOverlay.java
@@ -65,6 +65,10 @@
         invalidateSelf();
     }
 
+    public void update(float left, float top, float right, float bottom) {
+        mCurrentRect.set(left, top, right, bottom);
+    }
+
     public void setIcon(Drawable icon) {
         mIcon = icon;
     }
@@ -94,18 +98,16 @@
 
     @Override
     public void draw(Canvas canvas) {
-        if (mCurrentRect != null) {
-            canvas.drawRoundRect(mCurrentRect, mCornerRadius, mCornerRadius, mPaint);
-            if (mIcon != null && mIconAnimationProgress > 0f) {
-                canvas.save();
-                float scale = Interpolators.clampToProgress(FAST_OUT_SLOW_IN, 0f,
-                        1f).getInterpolation(mIconAnimationProgress);
-                canvas.translate(mCurrentRect.centerX() - mIcon.getBounds().width() / 2 * scale,
-                        mCurrentRect.top - mIcon.getBounds().height() / 2 * scale);
-                canvas.scale(scale, scale);
-                mIcon.draw(canvas);
-                canvas.restore();
-            }
+        canvas.drawRoundRect(mCurrentRect, mCornerRadius, mCornerRadius, mPaint);
+        if (mIcon != null && mIconAnimationProgress > 0f) {
+            canvas.save();
+            float scale = Interpolators.clampToProgress(FAST_OUT_SLOW_IN, 0f,
+                    1f).getInterpolation(mIconAnimationProgress);
+            canvas.translate(mCurrentRect.centerX() - mIcon.getBounds().width() / 2 * scale,
+                    mCurrentRect.top - mIcon.getBounds().height() / 2 * scale);
+            canvas.scale(scale, scale);
+            mIcon.draw(canvas);
+            canvas.restore();
         }
     }
 
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 2158e03..826555c 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -230,6 +230,7 @@
                     view.setScaleX(scale);
                     view.setScaleY(scale);
                     view.mLastComputedTaskPushOutDistance = null;
+                    view.mLiveTileTaskViewSimulator.recentsViewScale.value = scale;
                     view.updatePageOffsets();
                     view.setTaskViewsSecondaryTranslation(view.mTaskViewsSecondaryTranslation);
                 }
@@ -876,6 +877,10 @@
             mLiveTileTaskViewSimulator.fullScreenProgress.value = 0;
             mLiveTileTaskViewSimulator.recentsViewScale.value = 1;
             mLiveTileTaskViewSimulator.setOffsetY(0);
+
+            // Reset the live tile rect
+            DeviceProfile deviceProfile = mActivity.getDeviceProfile();
+            LiveTileOverlay.INSTANCE.update(0, 0, deviceProfile.widthPx, deviceProfile.heightPx);
         }
         if (mRunningTaskTileHidden) {
             setRunningTaskHidden(mRunningTaskTileHidden);
@@ -1986,6 +1991,7 @@
             TaskView task = getTaskViewAt(i);
             mOrientationHandler.getSecondaryViewTranslate().set(task, translation / getScaleY());
         }
+        mLiveTileTaskViewSimulator.recentsViewSecondaryTranslation.value = translation;
     }
 
     /**
@@ -2264,6 +2270,10 @@
         return mLiveTileTaskViewSimulator;
     }
 
+    public TransformParams getLiveTileParams() {
+        return mLiveTileParams;
+    }
+
     // TODO: To be removed in a follow up CL
     public void setRecentsAnimationTargets(RecentsAnimationController recentsAnimationController,
             RecentsAnimationTargets recentsAnimationTargets) {
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 54a793c..5154018 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -39,6 +39,7 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator;
@@ -65,7 +66,6 @@
 import android.widget.FrameLayout;
 import android.widget.Toast;
 
-import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.R;
@@ -75,6 +75,7 @@
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.popup.SystemShortcut;
+import com.android.launcher3.statemanager.StatefulActivity;
 import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.touch.PagedOrientationHandler;
@@ -82,10 +83,12 @@
 import com.android.launcher3.util.TransformingTouchDelegate;
 import com.android.launcher3.util.ViewPool.Reusable;
 import com.android.quickstep.RecentsModel;
+import com.android.quickstep.RemoteAnimationTargets;
 import com.android.quickstep.TaskIconCache;
 import com.android.quickstep.TaskOverlayFactory;
 import com.android.quickstep.TaskThumbnailCache;
 import com.android.quickstep.TaskUtils;
+import com.android.quickstep.TaskViewUtils;
 import com.android.quickstep.util.CancellableTask;
 import com.android.quickstep.util.RecentsOrientedState;
 import com.android.quickstep.util.TaskCornerRadius;
@@ -175,7 +178,7 @@
     private float mCurveScale;
     private float mFullscreenProgress;
     private final FullscreenDrawParams mCurrentFullscreenParams;
-    private final BaseDraggingActivity mActivity;
+    private final StatefulActivity mActivity;
 
     private ObjectAnimator mIconAndDimAnimator;
     private float mIconScaleAnimStartProgress = 0;
@@ -212,18 +215,31 @@
 
     public TaskView(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
-        mActivity = BaseDraggingActivity.fromContext(context);
+        mActivity = StatefulActivity.fromContext(context);
         setOnClickListener((view) -> {
             if (getTask() == null) {
                 return;
             }
-            if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
-                if (isRunningTask()) {
-                    // TODO: Replace this animation with createRecentsWindowAnimator
-                    createLaunchAnimationForRunningTask().start();
-                } else {
-                    launchTask(true /* animate */);
-                }
+            if (ENABLE_QUICKSTEP_LIVE_TILE.get() && isRunningTask()) {
+                RecentsView recentsView = getRecentsView();
+                RemoteAnimationTargets targets = recentsView.getLiveTileParams().getTargetSet();
+                recentsView.getLiveTileTaskViewSimulator().setDrawsBelowRecents(false);
+
+                AnimatorSet anim = new AnimatorSet();
+                TaskViewUtils.composeRecentsLaunchAnimator(
+                        anim, this, targets.apps,
+                        targets.wallpapers, true /* launcherClosing */,
+                        mActivity.getStateManager(), recentsView,
+                        recentsView.getDepthController());
+                anim.addListener(new AnimatorListenerAdapter() {
+
+                    @Override
+                    public void onAnimationEnd(Animator animator) {
+                        recentsView.getLiveTileTaskViewSimulator().setDrawsBelowRecents(true);
+                        recentsView.finishRecentsAnimation(false, null);
+                    }
+                });
+                anim.start();
             } else {
                 launchTask(true /* animate */);
             }
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 6245637..1015a32 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -705,8 +705,13 @@
 
         updateIcon(icon);
 
+        ItemInfo itemInfo = (ItemInfo) getTag();
+
         // If the current icon is a placeholder color, animate its update.
-        if (mIcon != null && mIcon instanceof PlaceHolderIconDrawable) {
+        if (mIcon != null
+                && mIcon instanceof PlaceHolderIconDrawable
+                && (itemInfo == null
+                    || itemInfo.itemType != LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT)) {
             animateIconUpdate((PlaceHolderIconDrawable) mIcon, icon);
         }
 
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 370bd6f..ff53b5f 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -201,7 +201,7 @@
                 DisplayController.getDefaultDisplay(context).getInfo(),
                 getPredefinedDeviceProfiles(context, gridName));
 
-        Info myInfo = new Info(context, display);
+        Info myInfo = new Info(display);
         DisplayOption myDisplayOption = invDistWeightedInterpolate(
                 myInfo, getPredefinedDeviceProfiles(context, gridName));
 
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 98328cf..777ea3c 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -139,6 +139,10 @@
 
     public static final int DEFAULT_PAGE = 0;
 
+    private static final int DEFAULT_SMARTSPACE_HEIGHT = 1;
+
+    private static final int EXPANDED_SMARTSPACE_HEIGHT = 2;
+
     private LayoutTransition mLayoutTransition;
     @Thunk final WallpaperManager mWallpaperManager;
 
@@ -507,7 +511,10 @@
                     .inflate(R.layout.search_container_workspace, firstPage, false);
         }
 
-        CellLayout.LayoutParams lp = new CellLayout.LayoutParams(0, 0, firstPage.getCountX(), 1);
+        int cellVSpan = FeatureFlags.EXPANDED_SMARTSPACE.get()
+                ? EXPANDED_SMARTSPACE_HEIGHT : DEFAULT_SMARTSPACE_HEIGHT;
+        CellLayout.LayoutParams lp = new CellLayout.LayoutParams(0, 0, firstPage.getCountX(),
+                cellVSpan);
         lp.canReorder = false;
         if (!firstPage.addViewToCellLayout(qsb, 0, R.id.search_container_workspace, lp, true)) {
             Log.e(TAG, "Failed to add to item at (0, 0) to CellLayout");
@@ -2078,40 +2085,40 @@
         mLastReorderY = -1;
     }
 
-   /*
-    *
-    * Convert the 2D coordinate xy from the parent View's coordinate space to this CellLayout's
-    * coordinate space. The argument xy is modified with the return result.
-    */
-   private void mapPointFromSelfToChild(View v, float[] xy) {
-       xy[0] = xy[0] - v.getLeft();
-       xy[1] = xy[1] - v.getTop();
-   }
+    /*
+     *
+     * Convert the 2D coordinate xy from the parent View's coordinate space to this CellLayout's
+     * coordinate space. The argument xy is modified with the return result.
+     */
+    private void mapPointFromSelfToChild(View v, float[] xy) {
+        xy[0] = xy[0] - v.getLeft();
+        xy[1] = xy[1] - v.getTop();
+    }
 
-   boolean isPointInSelfOverHotseat(int x, int y) {
-       mTempFXY[0] = x;
-       mTempFXY[1] = y;
-       mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempFXY, true);
-       View hotseat = mLauncher.getHotseat();
-       return mTempFXY[0] >= hotseat.getLeft() &&
-               mTempFXY[0] <= hotseat.getRight() &&
-               mTempFXY[1] >= hotseat.getTop() &&
-               mTempFXY[1] <= hotseat.getBottom();
-   }
+    boolean isPointInSelfOverHotseat(int x, int y) {
+        mTempFXY[0] = x;
+        mTempFXY[1] = y;
+        mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempFXY, true);
+        View hotseat = mLauncher.getHotseat();
+        return mTempFXY[0] >= hotseat.getLeft()
+                && mTempFXY[0] <= hotseat.getRight()
+                && mTempFXY[1] >= hotseat.getTop()
+                && mTempFXY[1] <= hotseat.getBottom();
+    }
 
     /**
      * Updates the point in {@param xy} to point to the co-ordinate space of {@param layout}
      * @param layout either hotseat of a page in workspace
      * @param xy the point location in workspace co-ordinate space
      */
-   private void mapPointFromDropLayout(CellLayout layout, float[] xy) {
-       if (mLauncher.isHotseatLayout(layout)) {
-           mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, xy, true);
-           mLauncher.getDragLayer().mapCoordInSelfToDescendant(layout, xy);
-       } else {
-           mapPointFromSelfToChild(layout, xy);
-       }
-   }
+    private void mapPointFromDropLayout(CellLayout layout, float[] xy) {
+        if (mLauncher.isHotseatLayout(layout)) {
+            mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, xy, true);
+            mLauncher.getDragLayer().mapCoordInSelfToDescendant(layout, xy);
+        } else {
+            mapPointFromSelfToChild(layout, xy);
+        }
+    }
 
     private boolean isDragWidget(DragObject d) {
         return (d.dragInfo instanceof LauncherAppWidgetInfo ||
@@ -2393,9 +2400,9 @@
             spanY = mDragInfo.spanY;
         }
 
-        final int container = mLauncher.isHotseatLayout(cellLayout) ?
-                LauncherSettings.Favorites.CONTAINER_HOTSEAT :
-                    LauncherSettings.Favorites.CONTAINER_DESKTOP;
+        final int container = mLauncher.isHotseatLayout(cellLayout)
+                ? LauncherSettings.Favorites.CONTAINER_HOTSEAT
+                : LauncherSettings.Favorites.CONTAINER_DESKTOP;
         final int screenId = getIdForScreen(cellLayout);
         if (!mLauncher.isHotseatLayout(cellLayout)
                 && screenId != getScreenIdForPageIndex(mCurrentPage)
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index 72b6d94..1fa43d0 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -24,6 +24,7 @@
 import android.graphics.Canvas;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.util.SparseIntArray;
 import android.view.MotionEvent;
 import android.view.View;
@@ -45,6 +46,8 @@
  * A RecyclerView with custom fast scroll support for the all apps view.
  */
 public class AllAppsRecyclerView extends BaseRecyclerView {
+    private static final String TAG = "AllAppsContainerView";
+    private static final boolean DEBUG = true;
 
     private AlphabeticalAppsList mApps;
     private final int mNumAppsPerRow;
@@ -131,7 +134,9 @@
         if (mEmptySearchBackground != null && mEmptySearchBackground.getAlpha() > 0) {
             mEmptySearchBackground.draw(c);
         }
-
+        if (DEBUG) {
+            Log.d(TAG, "onDraw at = " + System.currentTimeMillis());
+        }
         super.onDraw(c);
     }
 
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 58b0a87..8e6c2a7 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -182,9 +182,13 @@
             "Uses a separate recents activity instead of using the integrated recents+Launcher UI");
 
     public static final BooleanFlag ENABLE_MINIMAL_DEVICE = getDebugFlag(
-            "ENABLE_MINIMAL_DEVICE", true,
+            "ENABLE_MINIMAL_DEVICE", false,
             "Allow user to toggle minimal device mode in launcher.");
 
+    public static final BooleanFlag EXPANDED_SMARTSPACE = new DeviceFlag(
+            "EXPANDED_SMARTSPACE", false, "Expands smartspace height to two rows. "
+              + "Any apps occupying the first row will be removed from workspace.");
+
     public static void initialize(Context context) {
         synchronized (sDebugFlags) {
             for (DebugFlag flag : sDebugFlags) {
diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java
index a27ac23..532834e 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -445,7 +445,8 @@
             if (item.screenId == Workspace.FIRST_SCREEN_ID) {
                 // Mark the first row as occupied (if the feature is enabled)
                 // in order to account for the QSB.
-                screen.markCells(0, 0, countX + 1, 1, FeatureFlags.QSB_ON_FIRST_SCREEN);
+                int spanY = FeatureFlags.EXPANDED_SMARTSPACE.get() ? 2 : 1;
+                screen.markCells(0, 0, countX + 1, spanY, FeatureFlags.QSB_ON_FIRST_SCREEN);
             }
             occupied.put(item.screenId, screen);
         }
diff --git a/src/com/android/launcher3/util/DisplayController.java b/src/com/android/launcher3/util/DisplayController.java
index 355c949..3ab736a 100644
--- a/src/com/android/launcher3/util/DisplayController.java
+++ b/src/com/android/launcher3/util/DisplayController.java
@@ -31,6 +31,8 @@
 
 import androidx.annotation.VisibleForTesting;
 
+import com.android.launcher3.Utilities;
+
 import java.util.ArrayList;
 
 /**
@@ -157,13 +159,13 @@
         private final ArrayList<DisplayInfoChangeListener> mListeners = new ArrayList<>();
         private DisplayController.Info mInfo;
 
-        private DisplayHolder(Context displayContext) {
+        private DisplayHolder(Context displayContext, Display display) {
             mDisplayContext = displayContext;
             // Note that the Display object must be obtained from DisplayManager which is
             // associated to the display context, so the Display is isolated from Activity and
             // Application to provide the actual state of device that excludes the additional
             // adjustment and override.
-            mInfo = new DisplayController.Info(mDisplayContext);
+            mInfo = new DisplayController.Info(display);
             mId = mInfo.id;
         }
 
@@ -180,22 +182,31 @@
         }
 
         protected void handleOnChange() {
+            Display display = Utilities.ATLEAST_R
+                    ? mDisplayContext.getDisplay()
+                    : mDisplayContext
+                        .getSystemService(DisplayManager.class)
+                        .getDisplay(mId);
+            if (display == null) {
+                return;
+            }
+
             Info oldInfo = mInfo;
-            Info info = new Info(mDisplayContext);
+            Info newInfo = new Info(display);
 
             int change = 0;
-            if (info.hasDifferentSize(oldInfo)) {
+            if (newInfo.hasDifferentSize(oldInfo)) {
                 change |= CHANGE_SIZE;
             }
-            if (oldInfo.rotation != info.rotation) {
+            if (newInfo.rotation != oldInfo.rotation) {
                 change |= CHANGE_ROTATION;
             }
-            if (info.singleFrameMs != oldInfo.singleFrameMs) {
+            if (newInfo.singleFrameMs != oldInfo.singleFrameMs) {
                 change |= CHANGE_FRAME_DELAY;
             }
 
             if (change != 0) {
-                mInfo = info;
+                mInfo = newInfo;
                 final int flags = change;
                 MAIN_EXECUTOR.execute(() -> notifyChange(flags));
             }
@@ -216,7 +227,7 @@
             // Use application context to create display context so that it can have its own
             // Resources.
             Context displayContext = context.getApplicationContext().createDisplayContext(display);
-            return new DisplayHolder(displayContext);
+            return new DisplayHolder(displayContext, display);
         }
     }
 
@@ -244,12 +255,7 @@
             this.metrics = metrics;
         }
 
-        private Info(Context context) {
-            this(context, context.getSystemService(DisplayManager.class)
-                    .getDisplay(DEFAULT_DISPLAY));
-        }
-
-        public Info(Context context, Display display) {
+        public Info(Display display) {
             id = display.getDisplayId();
             rotation = display.getRotation();
 
@@ -262,7 +268,8 @@
             display.getRealSize(realSize);
             display.getCurrentSizeRange(smallestSize, largestSize);
 
-            metrics = context.getResources().getDisplayMetrics();
+            metrics = new DisplayMetrics();
+            display.getMetrics(metrics);
         }
 
         private boolean hasDifferentSize(Info info) {
diff --git a/src/com/android/launcher3/views/SearchResultIconRow.java b/src/com/android/launcher3/views/SearchResultIconRow.java
index fe904ff..bdbe890 100644
--- a/src/com/android/launcher3/views/SearchResultIconRow.java
+++ b/src/com/android/launcher3/views/SearchResultIconRow.java
@@ -63,11 +63,11 @@
     public static final String REMOTE_ACTION_SHOULD_START = "should_start_for_result";
     public static final String REMOTE_ACTION_TOKEN = "action_token";
 
-    private final int mCustomIconResId;
     private final boolean mMatchesInset;
 
     private SearchTarget mSearchTarget;
 
+    @Nullable private Drawable mCustomIcon;
 
     public SearchResultIconRow(@NonNull Context context) {
         this(context, null, 0);
@@ -83,10 +83,15 @@
         super(context, attrs, defStyleAttr);
         TypedArray a = context.obtainStyledAttributes(attrs,
                 R.styleable.SearchResultIconRow, defStyleAttr, 0);
-        mCustomIconResId = a.getResourceId(R.styleable.SearchResultIconRow_customIcon, 0);
         mMatchesInset = a.getBoolean(R.styleable.SearchResultIconRow_matchTextInsetWithQuery,
                 false);
 
+        int customIconResId = a.getResourceId(R.styleable.SearchResultIconRow_customIcon, 0);
+
+        if (customIconResId != 0) {
+            mCustomIcon = Launcher.getLauncher(context).getDrawable(customIconResId);
+        }
+
         a.recycle();
     }
 
@@ -172,13 +177,16 @@
     }
 
     private boolean loadIconFromResource() {
-        if (mCustomIconResId == 0) return false;
-        setIcon(Launcher.getLauncher(getContext()).getDrawable(mCustomIconResId));
+        if (mCustomIcon == null) return false;
+        setIcon(mCustomIcon);
         return true;
     }
 
     void reapplyItemInfoAsync(ItemInfoWithIcon itemInfoWithIcon) {
-        MAIN_EXECUTOR.post(() -> reapplyItemInfo(itemInfoWithIcon));
+        MAIN_EXECUTOR.post(() -> {
+            reapplyItemInfo(itemInfoWithIcon);
+            mCustomIcon = getIcon();
+        });
     }
 
     @Override
diff --git a/src/com/android/launcher3/views/ThumbnailSearchResultView.java b/src/com/android/launcher3/views/ThumbnailSearchResultView.java
index d11b1ef..e929d7f 100644
--- a/src/com/android/launcher3/views/ThumbnailSearchResultView.java
+++ b/src/com/android/launcher3/views/ThumbnailSearchResultView.java
@@ -90,6 +90,7 @@
             bitmap = Bitmap.createBitmap(bitmap, 0,
                     bitmap.getHeight() / 2 - bitmap.getWidth() / 2,
                     bitmap.getWidth(), bitmap.getWidth());
+            setTag(itemInfo);
         } else {
             bitmap = (Bitmap) target.getExtras().getParcelable("bitmap");
             WorkspaceItemInfo itemInfo = new WorkspaceItemInfo();