Merge "[Search] Add logging to People and badding as to icons." into ub-launcher3-master
diff --git a/Android.bp b/Android.bp
index e132854..081f485 100644
--- a/Android.bp
+++ b/Android.bp
@@ -35,14 +35,12 @@
     name: "launcher_log_protos_lite",
     srcs: [
         "protos/*.proto",
-        "proto_overrides/*.proto",
     ],
     sdk_version: "current",
     proto: {
         type: "lite",
         local_include_dirs:[
             "protos",
-            "proto_overrides",
         ],
     },
     static_libs: ["libprotobuf-java-lite"],
diff --git a/proto_overrides/launcher_log_extension.proto b/proto_overrides/launcher_log_extension.proto
deleted file mode 100644
index 2995aa2..0000000
--- a/proto_overrides/launcher_log_extension.proto
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-syntax = "proto2";
-
-option java_package = "com.android.launcher3.userevent";
-option java_outer_classname = "LauncherLogExtensions";
-
-package userevent;
-
-//
-// Use this to add any app specific extensions to the proto.
-//
-message LauncherEventExtension {
-}
-
-message TargetExtension {
-}
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index c85fe6c..c7c0c7e 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -51,6 +51,8 @@
     <dimen name="recents_empty_message_text_size">16sp</dimen>
     <dimen name="recents_empty_message_text_padding">16dp</dimen>
 
+    <dimen name="max_shadow_radius">5dp</dimen>
+
     <!-- Total space (start + end) between the task card and the edge of the screen
          in various configurations -->
     <dimen name="task_card_vert_space">40dp</dimen>
diff --git a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
index 38adf39..ae4bd96 100644
--- a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
@@ -16,14 +16,10 @@
 
 package com.android.launcher3;
 
-import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.quickstep.TaskViewUtils.createRecentsWindowAnimator;
 import static com.android.quickstep.TaskViewUtils.findTaskViewToLaunch;
 
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.content.Context;
@@ -32,11 +28,8 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.anim.PendingAnimation;
+import com.android.quickstep.TaskViewUtils;
 import com.android.quickstep.views.RecentsView;
-import com.android.quickstep.views.TaskView;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
 /**
@@ -53,60 +46,16 @@
     protected boolean isLaunchingFromRecents(@NonNull View v,
             @Nullable RemoteAnimationTargetCompat[] targets) {
         return mLauncher.getStateManager().getState().overviewUi
-                && findTaskViewToLaunch(mLauncher, v, targets) != null;
+                && findTaskViewToLaunch(mLauncher.getOverviewPanel(), v, targets) != null;
     }
 
     @Override
     protected void composeRecentsLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
             @NonNull RemoteAnimationTargetCompat[] appTargets,
             @NonNull RemoteAnimationTargetCompat[] wallpaperTargets, boolean launcherClosing) {
-        RecentsView recentsView = mLauncher.getOverviewPanel();
-        boolean skipLauncherChanges = !launcherClosing;
-
-        TaskView taskView = findTaskViewToLaunch(mLauncher, v, appTargets);
-        PendingAnimation pa = new PendingAnimation(RECENTS_LAUNCH_DURATION);
-        createRecentsWindowAnimator(taskView, skipLauncherChanges, appTargets, wallpaperTargets,
-                mLauncher.getDepthController(), pa);
-        anim.play(pa.buildAnim());
-
-        Animator childStateAnimation = null;
-        // Found a visible recents task that matches the opening app, lets launch the app from there
-        Animator launcherAnim;
-        final AnimatorListenerAdapter windowAnimEndListener;
-        if (launcherClosing) {
-            launcherAnim = recentsView.createAdjacentPageAnimForTaskLaunch(taskView);
-            launcherAnim.setInterpolator(Interpolators.TOUCH_RESPONSE_INTERPOLATOR);
-            launcherAnim.setDuration(RECENTS_LAUNCH_DURATION);
-
-            // Make sure recents gets fixed up by resetting task alphas and scales, etc.
-            windowAnimEndListener = new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    mLauncher.getStateManager().moveToRestState();
-                    mLauncher.getStateManager().reapplyState();
-                }
-            };
-        } else {
-            AnimatorPlaybackController controller =
-                    mLauncher.getStateManager().createAnimationToNewWorkspace(NORMAL,
-                            RECENTS_LAUNCH_DURATION);
-            controller.dispatchOnStart();
-            childStateAnimation = controller.getTarget();
-            launcherAnim = controller.getAnimationPlayer().setDuration(RECENTS_LAUNCH_DURATION);
-            windowAnimEndListener = new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    mLauncher.getStateManager().goToState(NORMAL, false);
-                }
-            };
-        }
-        anim.play(launcherAnim);
-
-        // Set the current animation first, before adding windowAnimEndListener. Setting current
-        // animation adds some listeners which need to be called before windowAnimEndListener
-        // (the ordering of listeners matter in this case).
-        mLauncher.getStateManager().setCurrentAnimation(anim, childStateAnimation);
-        anim.addListener(windowAnimEndListener);
+        TaskViewUtils.composeRecentsLaunchAnimator(anim, v, appTargets, wallpaperTargets,
+                launcherClosing, mLauncher.getStateManager(), mLauncher.getOverviewPanel(),
+                mLauncher.getDepthController());
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
index d453bc3..df1833d 100644
--- a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
@@ -153,6 +153,7 @@
     private final float mContentTransY;
     private final float mWorkspaceTransY;
     private final float mClosingWindowTransY;
+    private final float mMaxShadowRadius;
 
     private DeviceProfile mDeviceProfile;
 
@@ -186,6 +187,7 @@
         mContentTransY = res.getDimensionPixelSize(R.dimen.content_trans_y);
         mWorkspaceTransY = res.getDimensionPixelSize(R.dimen.workspace_trans_y);
         mClosingWindowTransY = res.getDimensionPixelSize(R.dimen.closing_window_trans_y);
+        mMaxShadowRadius = res.getDimensionPixelSize(R.dimen.max_shadow_radius);
 
         mLauncher.addOnDeviceProfileChangeListener(this);
     }
@@ -538,6 +540,8 @@
                     EXAGGERATED_EASE);
             FloatProp mWindowRadius = new FloatProp(initialWindowRadius, windowRadius, 0,
                     RADIUS_DURATION, EXAGGERATED_EASE);
+            FloatProp mShadowRadius = new FloatProp(0, mMaxShadowRadius, 0,
+                    APP_LAUNCH_DURATION, EXAGGERATED_EASE);
 
             @Override
             public void onUpdate(float percent) {
@@ -600,7 +604,8 @@
                         builder.withMatrix(matrix)
                                 .withWindowCrop(crop)
                                 .withAlpha(1f - mIconAlpha.value)
-                                .withCornerRadius(mWindowRadius.value);
+                                .withCornerRadius(mWindowRadius.value)
+                                .withShadowRadius(mShadowRadius.value);
                     } else {
                         tmpPos.set(target.position.x, target.position.y);
                         if (target.localBounds != null) {
@@ -750,6 +755,8 @@
             FloatProp mDy = new FloatProp(0, mClosingWindowTransY, 0, duration, DEACCEL_1_7);
             FloatProp mScale = new FloatProp(1f, 1f, 0, duration, DEACCEL_1_7);
             FloatProp mAlpha = new FloatProp(1f, 0f, 25, 125, LINEAR);
+            FloatProp mShadowRadius = new FloatProp(mMaxShadowRadius, 0, 0, duration,
+                    DEACCEL_1_7);
 
             @Override
             public void onUpdate(float percent) {
@@ -771,7 +778,8 @@
                         matrix.postTranslate(tmpPos.x, tmpPos.y);
                         builder.withMatrix(matrix)
                                 .withAlpha(mAlpha.value)
-                                .withCornerRadius(windowCornerRadius);
+                                .withCornerRadius(windowCornerRadius)
+                                .withShadowRadius(mShadowRadius.value);
                     } else {
                         matrix.setTranslate(tmpPos.x, tmpPos.y);
                         builder.withMatrix(matrix)
diff --git a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
index 5b066c6..b5ba8a6 100644
--- a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
+++ b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
@@ -108,6 +108,8 @@
 
     AllAppsSectionDecorator.SectionDecorationHandler mDecorationHandler;
 
+    @Nullable private List<ItemInfo> mPendingPredictedItems;
+
     public PredictionRowView(@NonNull Context context) {
         this(context, null);
     }
@@ -146,6 +148,10 @@
 
     private void updateVisibility() {
         setVisibility(mPredictionsEnabled ? VISIBLE : GONE);
+        if (FeatureFlags.ENABLE_DEVICE_SEARCH.get() && mLauncher.getAppsView() != null
+                && mLauncher.getAppsView().getActiveRecyclerView() != null) {
+            mLauncher.getAppsView().invalidate();
+        }
     }
 
     @Override
@@ -203,6 +209,16 @@
      * we can optimize by swapping them in place.
      */
     public void setPredictedApps(List<ItemInfo> items) {
+        if (isShown() && getWindowVisibility() == View.VISIBLE) {
+            mPendingPredictedItems = items;
+            return;
+        }
+
+        applyPredictedApps(items);
+    }
+
+    private void applyPredictedApps(List<ItemInfo> items) {
+        mPendingPredictedItems = null;
         mPredictedApps.clear();
         items.stream()
                 .filter(itemInfo -> itemInfo instanceof WorkspaceItemInfo)
@@ -341,4 +357,13 @@
     public View getFocusedChild() {
         return getChildAt(0);
     }
+
+    @Override
+    public void onVisibilityAggregated(boolean isVisible) {
+        super.onVisibilityAggregated(isVisible);
+
+        if (mPendingPredictedItems != null && !isVisible) {
+            applyPredictedApps(mPendingPredictedItems);
+        }
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
index 3807350..76bab59 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
@@ -251,6 +251,26 @@
      * Sets or updates the predicted items
      */
     public void setPredictedItems(FixedContainerItems items) {
+        if (mHotseat.isShown() && mHotseat.getWindowVisibility() == View.VISIBLE) {
+            mHotseat.setOnVisibilityAggregatedCallback((isVisible) -> {
+                if (isVisible) {
+                    return;
+                }
+                mHotseat.setOnVisibilityAggregatedCallback(null);
+
+                applyPredictedItems(items);
+            });
+        } else {
+            mHotseat.setOnVisibilityAggregatedCallback(null);
+
+            applyPredictedItems(items);
+        }
+    }
+
+    /**
+     * Sets or updates the predicted items only once the hotseat becomes hidden to the user
+     */
+    private void applyPredictedItems(FixedContainerItems items) {
         mPredictedItems = items.items;
         if (mPredictedItems.isEmpty()) {
             HotseatRestoreHelper.restoreBackup(mLauncher);
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 1439d4e..dbf75fa 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -41,10 +41,14 @@
 import static com.android.quickstep.GestureState.STATE_END_TARGET_SET;
 import static com.android.quickstep.GestureState.STATE_RECENTS_SCROLLING_FINISHED;
 import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
+import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
 import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
 
 import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
 import android.animation.ValueAnimator;
 import android.annotation.TargetApi;
 import android.app.ActivityManager;
@@ -104,6 +108,7 @@
 import com.android.systemui.shared.system.TaskStackChangeListener;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.function.Consumer;
 
 /**
@@ -1376,17 +1381,64 @@
     private void setupLauncherUiAfterSwipeUpToRecentsAnimation() {
         endLauncherTransitionController();
         mActivityInterface.onSwipeUpToRecentsComplete();
-        if (mRecentsAnimationController != null) {
-            mRecentsAnimationController.setDeferCancelUntilNextTransition(true /* defer */,
-                    true /* screenshot */);
-        }
         mRecentsView.onSwipeUpAnimationSuccess();
+        if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+            mTaskAnimationManager.setLaunchOtherTaskInLiveTileModeHandler(
+                    this::launchOtherTaskInLiveTileMode);
+        }
 
         SystemUiProxy.INSTANCE.get(mContext).onOverviewShown(false, TAG);
         doLogGesture(RECENTS, mRecentsView.getCurrentPageTaskView());
         reset();
     }
 
+    private void launchOtherTaskInLiveTileMode(RemoteAnimationTargetCompat appearedTaskTarget) {
+        TaskView taskView = mRecentsView.getTaskView(appearedTaskTarget.taskId);
+        if (taskView == null) {
+            return;
+        }
+
+        RemoteAnimationTargetCompat[] apps = Arrays.copyOf(
+                mRecentsAnimationTargets.apps,
+                mRecentsAnimationTargets.apps.length + 1);
+        apps[apps.length - 1] = appearedTaskTarget;
+        boolean launcherClosing =
+                taskIsATargetWithMode(apps, mActivity.getTaskId(), MODE_CLOSING);
+
+        AnimatorSet anim = new AnimatorSet();
+        TaskViewUtils.composeRecentsLaunchAnimator(
+                anim, taskView, apps,
+                mRecentsAnimationTargets.wallpapers, launcherClosing,
+                mActivity.getStateManager(), mRecentsView,
+                mActivityInterface.getDepthController());
+        anim.addListener(new AnimatorListenerAdapter(){
+
+            @Override
+            public void onAnimationEnd(Animator animator) {
+                cleanUp(false);
+            }
+
+            @Override
+            public void onAnimationCancel(Animator animator) {
+                cleanUp(true);
+            }
+
+            private void cleanUp(boolean canceled) {
+                if (mRecentsAnimationController != null) {
+                    mRecentsAnimationController.finish(false /* toRecents */,
+                            null /* onFinishComplete */);
+                    if (canceled) {
+                        mRecentsAnimationController = null;
+                    } else {
+                        mActivityInterface.onLaunchTaskSuccess();
+                    }
+                    ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", false);
+                }
+            }
+        });
+        anim.start();
+    }
+
     private void addLiveTileOverlay() {
         if (LiveTileOverlay.INSTANCE.attach(mActivity.getRootView().getOverlay())) {
             mRecentsView.setLiveTileOverlayAttached(true);
@@ -1444,39 +1496,33 @@
 
     protected void startNewTask(Consumer<Boolean> resultCallback) {
         // Launch the task user scrolled to (mRecentsView.getNextPage()).
-        if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
-            // We finish recents animation inside launchTask() when live tile is enabled.
-            mRecentsView.getNextPageTaskView().launchTask(false /* animate */,
-                    true /* freezeTaskList */);
-        } else {
-            if (!mCanceled) {
-                TaskView nextTask = mRecentsView.getNextPageTaskView();
-                if (nextTask != null) {
-                    int taskId = nextTask.getTask().key.id;
-                    mGestureState.updateLastStartedTaskId(taskId);
-                    boolean hasTaskPreviouslyAppeared = mGestureState.getPreviouslyAppearedTaskIds()
-                            .contains(taskId);
-                    nextTask.launchTask(false /* animate */, true /* freezeTaskList */,
-                            success -> {
-                                resultCallback.accept(success);
-                                if (success) {
-                                    if (hasTaskPreviouslyAppeared) {
-                                        onRestartPreviouslyAppearedTask();
-                                    }
-                                } else {
-                                    mActivityInterface.onLaunchTaskFailed();
-                                    nextTask.notifyTaskLaunchFailed(TAG);
-                                    mRecentsAnimationController.finish(true /* toRecents */, null);
+        if (!mCanceled) {
+            TaskView nextTask = mRecentsView.getNextPageTaskView();
+            if (nextTask != null) {
+                int taskId = nextTask.getTask().key.id;
+                mGestureState.updateLastStartedTaskId(taskId);
+                boolean hasTaskPreviouslyAppeared = mGestureState.getPreviouslyAppearedTaskIds()
+                        .contains(taskId);
+                nextTask.launchTask(false /* animate */, true /* freezeTaskList */,
+                        success -> {
+                            resultCallback.accept(success);
+                            if (success) {
+                                if (hasTaskPreviouslyAppeared) {
+                                    onRestartPreviouslyAppearedTask();
                                 }
-                            }, MAIN_EXECUTOR.getHandler());
-                } else {
-                    mActivityInterface.onLaunchTaskFailed();
-                    Toast.makeText(mContext, R.string.activity_not_available, LENGTH_SHORT).show();
-                    mRecentsAnimationController.finish(true /* toRecents */, null);
-                }
+                            } else {
+                                mActivityInterface.onLaunchTaskFailed();
+                                nextTask.notifyTaskLaunchFailed(TAG);
+                                mRecentsAnimationController.finish(true /* toRecents */, null);
+                            }
+                        }, MAIN_EXECUTOR.getHandler());
+            } else {
+                mActivityInterface.onLaunchTaskFailed();
+                Toast.makeText(mContext, R.string.activity_not_available, LENGTH_SHORT).show();
+                mRecentsAnimationController.finish(true /* toRecents */, null);
             }
-            mCanceled = false;
         }
+        mCanceled = false;
     }
 
     /**
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationController.java b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
index 798c12b..62bbf4d 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationController.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
@@ -90,24 +90,6 @@
     }
 
     /**
-     * Notifies the controller that we want to defer cancel until the next app transition starts.
-     * If {@param screenshot} is set, then we will receive a screenshot on the next
-     * {@link RecentsAnimationCallbacks#onAnimationCanceled(ThumbnailData)} and we must also call
-     * {@link #cleanupScreenshot()} when that screenshot is no longer used.
-     */
-    public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) {
-        mController.setDeferCancelUntilNextTransition(defer, screenshot);
-    }
-
-    /**
-     * Cleans up the screenshot previously returned from
-     * {@link RecentsAnimationCallbacks#onAnimationCanceled(ThumbnailData)}.
-     */
-    public void cleanupScreenshot() {
-        UI_HELPER_EXECUTOR.execute(() -> mController.cleanupScreenshot());
-    }
-
-    /**
      * Remove task remote animation target from
      * {@link RecentsAnimationCallbacks#onTaskAppeared(RemoteAnimationTargetCompat)}}.
      */
diff --git a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
index b6eaa1c..a46de1f 100644
--- a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
+++ b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
@@ -29,6 +29,7 @@
 import androidx.annotation.UiThread;
 
 import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.launcher3.anim.AnimatorPlaybackController;
@@ -66,6 +67,8 @@
     // How much further we can drag past recents, as a factor of mTransitionDragLength.
     protected float mDragLengthFactor = 1;
 
+    protected final float mMaxShadowRadius;
+
     protected AnimatorControllerWithResistance mWindowTransitionController;
 
     public SwipeUpAnimationLogic(Context context, RecentsAnimationDeviceState deviceState,
@@ -80,6 +83,9 @@
                 mDeviceState.getRotationTouchHelper().getCurrentActiveRotation(),
                 mDeviceState.getRotationTouchHelper().getDisplayRotation());
         mTaskViewSimulator.setDrawsBelowRecents(true);
+
+        mMaxShadowRadius = context.getResources().getDimensionPixelSize(R.dimen.max_shadow_radius);
+        mTransformParams.setShadowRadius(mMaxShadowRadius);
     }
 
     protected void initTransitionEndpoints(DeviceProfile dp) {
@@ -251,9 +257,11 @@
 
             mMatrix.setRectToRect(mCropRectF, mWindowCurrentRect, ScaleToFit.FILL);
             float cornerRadius = Utilities.mapRange(progress, mStartRadius, mEndRadius);
+            float shadowRadius = Utilities.mapRange(progress, mMaxShadowRadius, 0);
             mTransformParams
                     .setTargetAlpha(getWindowAlpha(progress))
-                    .setCornerRadius(cornerRadius);
+                    .setCornerRadius(cornerRadius)
+                    .setShadowRadius(mMaxShadowRadius);
 
             mTransformParams.applySurfaceParams(mTransformParams.createSurfaceParams(this));
             mAnimationFactory.update(currentRect, progress, mMatrix.mapRadius(cornerRadius));
@@ -264,7 +272,8 @@
                 Builder builder, RemoteAnimationTargetCompat app, TransformParams params) {
             builder.withMatrix(mMatrix)
                     .withWindowCrop(mCropRect)
-                    .withCornerRadius(params.getCornerRadius());
+                    .withCornerRadius(params.getCornerRadius())
+                    .withShadowRadius(params.getShadowRadius());
         }
 
         @Override
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index cad51f4..f38c1ea 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -17,6 +17,7 @@
 
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS;
 import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_INITIALIZED;
 import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_STARTED;
 
@@ -31,6 +32,8 @@
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
+import java.util.function.Consumer;
+
 public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAnimationListener {
 
     private RecentsAnimationController mController;
@@ -39,6 +42,7 @@
     // Temporary until we can hook into gesture state events
     private GestureState mLastGestureState;
     private RemoteAnimationTargetCompat mLastAppearedTaskTarget;
+    private Consumer<RemoteAnimationTargetCompat> mLaunchOtherTaskHandler;
 
     /**
      * Preloads the recents animation.
@@ -88,22 +92,21 @@
 
             @Override
             public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
-                if (thumbnailData != null) {
-                    // If a screenshot is provided, switch to the screenshot before cleaning up
-                    activityInterface.switchRunningTaskViewToScreenshot(thumbnailData,
-                            () -> cleanUpRecentsAnimation(thumbnailData));
-                } else {
-                    cleanUpRecentsAnimation(null /* canceledThumbnail */);
-                }
+                cleanUpRecentsAnimation();
             }
 
             @Override
             public void onRecentsAnimationFinished(RecentsAnimationController controller) {
-                cleanUpRecentsAnimation(null /* canceledThumbnail */);
+                cleanUpRecentsAnimation();
             }
 
             @Override
             public void onTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) {
+                if (mLaunchOtherTaskHandler != null
+                        && mLastGestureState.getEndTarget() == RECENTS) {
+                    mLaunchOtherTaskHandler.accept(appearedTaskTarget);
+                    return;
+                }
                 if (mController != null) {
                     if (mLastAppearedTaskTarget == null
                             || appearedTaskTarget.taskId != mLastAppearedTaskTarget.taskId) {
@@ -138,6 +141,15 @@
     }
 
     /**
+     * The passed-in handler is used to render side task launch animation in recents in live tile
+     * mode.
+     */
+    public void setLaunchOtherTaskInLiveTileModeHandler(
+            Consumer<RemoteAnimationTargetCompat> handler) {
+        mLaunchOtherTaskHandler = handler;
+    }
+
+    /**
      * Finishes the running recents animation.
      */
     public void finishRunningRecentsAnimation(boolean toHome) {
@@ -146,7 +158,7 @@
             Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), toHome
                     ? mController::finishAnimationToHome
                     : mController::finishAnimationToApp);
-            cleanUpRecentsAnimation(null /* canceledThumbnail */);
+            cleanUpRecentsAnimation();
         }
     }
 
@@ -173,12 +185,7 @@
     /**
      * Cleans up the recents animation entirely.
      */
-    private void cleanUpRecentsAnimation(ThumbnailData canceledThumbnail) {
-        // Clean up the screenshot if necessary
-        if (mController != null && canceledThumbnail != null) {
-            mController.cleanupScreenshot();
-        }
-
+    private void cleanUpRecentsAnimation() {
         // Release all the target leashes
         if (mTargets != null) {
             mTargets.release();
@@ -194,6 +201,7 @@
         mTargets = null;
         mLastGestureState = null;
         mLastAppearedTaskTarget = null;
+        mLaunchOtherTaskHandler = null;
     }
 
     public void dump() {
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index a5af181..7299c38 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -17,15 +17,20 @@
 
 import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
 import static com.android.launcher3.LauncherState.BACKGROUND_APP;
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.QuickstepAppTransitionManagerImpl.RECENTS_LAUNCH_DURATION;
 import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
 import static com.android.launcher3.anim.Interpolators.clampToProgress;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
 import static com.android.launcher3.statehandlers.DepthController.DEPTH;
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
 import android.annotation.TargetApi;
 import android.content.ComponentName;
 import android.content.Context;
@@ -35,12 +40,16 @@
 import android.os.Build;
 import android.view.View;
 
+import androidx.annotation.NonNull;
+
 import com.android.launcher3.BaseActivity;
-import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.statehandlers.DepthController;
+import com.android.launcher3.statemanager.StateManager;
 import com.android.launcher3.util.DisplayController;
 import com.android.quickstep.util.SurfaceTransactionApplier;
 import com.android.quickstep.util.TaskViewSimulator;
@@ -67,8 +76,7 @@
      * opening remote target (which we don't get until onAnimationStart) will resolve to a TaskView.
      */
     public static TaskView findTaskViewToLaunch(
-            BaseDraggingActivity activity, View v, RemoteAnimationTargetCompat[] targets) {
-        RecentsView recentsView = activity.getOverviewPanel();
+            RecentsView recentsView, View v, RemoteAnimationTargetCompat[] targets) {
         if (v instanceof TaskView) {
             TaskView taskView = (TaskView) v;
             return recentsView.isTaskViewVisible(taskView) ? taskView : null;
@@ -130,7 +138,8 @@
 
         SurfaceTransactionApplier applier = new SurfaceTransactionApplier(v);
         final RemoteAnimationTargets targets =
-                new RemoteAnimationTargets(appTargets, wallpaperTargets, MODE_OPENING);
+                new RemoteAnimationTargets(appTargets, wallpaperTargets,
+                        ENABLE_QUICKSTEP_LIVE_TILE.get() ? MODE_CLOSING : MODE_OPENING);
         targets.addReleaseCheck(applier);
 
         TransformParams params = new TransformParams()
@@ -235,4 +244,57 @@
                     TOUCH_RESPONSE_INTERPOLATOR);
         }
     }
+
+    public static void composeRecentsLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
+            @NonNull RemoteAnimationTargetCompat[] appTargets,
+            @NonNull RemoteAnimationTargetCompat[] wallpaperTargets, boolean launcherClosing,
+            @NonNull StateManager stateManager, @NonNull RecentsView recentsView,
+            @NonNull DepthController depthController) {
+        boolean skipLauncherChanges = !launcherClosing;
+
+        TaskView taskView = findTaskViewToLaunch(recentsView, v, appTargets);
+        PendingAnimation pa = new PendingAnimation(RECENTS_LAUNCH_DURATION);
+        createRecentsWindowAnimator(taskView, skipLauncherChanges, appTargets, wallpaperTargets,
+                depthController, pa);
+        anim.play(pa.buildAnim());
+
+        Animator childStateAnimation = null;
+        // Found a visible recents task that matches the opening app, lets launch the app from there
+        Animator launcherAnim;
+        final AnimatorListenerAdapter windowAnimEndListener;
+        if (launcherClosing) {
+            launcherAnim = recentsView.createAdjacentPageAnimForTaskLaunch(taskView);
+            launcherAnim.setInterpolator(Interpolators.TOUCH_RESPONSE_INTERPOLATOR);
+            launcherAnim.setDuration(RECENTS_LAUNCH_DURATION);
+
+            // Make sure recents gets fixed up by resetting task alphas and scales, etc.
+            windowAnimEndListener = new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    stateManager.moveToRestState();
+                    stateManager.reapplyState();
+                }
+            };
+        } else {
+            AnimatorPlaybackController controller =
+                    stateManager.createAnimationToNewWorkspace(NORMAL,
+                            RECENTS_LAUNCH_DURATION);
+            controller.dispatchOnStart();
+            childStateAnimation = controller.getTarget();
+            launcherAnim = controller.getAnimationPlayer().setDuration(RECENTS_LAUNCH_DURATION);
+            windowAnimEndListener = new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    stateManager.goToState(NORMAL, false);
+                }
+            };
+        }
+        anim.play(launcherAnim);
+
+        // Set the current animation first, before adding windowAnimEndListener. Setting current
+        // animation adds some listeners which need to be called before windowAnimEndListener
+        // (the ordering of listeners matter in this case).
+        stateManager.setCurrentAnimation(anim, childStateAnimation);
+        anim.addListener(windowAnimEndListener);
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 7bf8fba..d3c4f55 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -732,8 +732,11 @@
     private void reset() {
         mConsumer = mUncheckedConsumer = mResetGestureInputConsumer;
         mGestureState = DEFAULT_STATE;
-        // By default, use batching of the input events
-        mInputEventReceiver.setBatchingEnabled(true);
+        // By default, use batching of the input events, but check receiver before using in the rare
+        // case that the monitor was disposed before the swipe settled
+        if (mInputEventReceiver != null) {
+            mInputEventReceiver.setBatchingEnabled(true);
+        }
     }
 
     private void preloadOverview(boolean fromInit) {
diff --git a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
index 5f0ef83..bb84380 100644
--- a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
+++ b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
@@ -539,6 +539,7 @@
      * @return "MyObject@1234"
      */
     private static String extractObjectNameAndAddress(String stringToExtract) {
-        return stringToExtract.substring(stringToExtract.lastIndexOf(DELIMITER_DOT));
+        int index = stringToExtract.lastIndexOf(DELIMITER_DOT);
+        return index >= 0 ? stringToExtract.substring(index) : "";
     }
 }
diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
index 3a54bd6..08fe48d 100644
--- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -322,7 +322,8 @@
             Builder builder, RemoteAnimationTargetCompat app, TransformParams params) {
         builder.withMatrix(mMatrix)
                 .withWindowCrop(mTmpCropRect)
-                .withCornerRadius(getCurrentCornerRadius());
+                .withCornerRadius(getCurrentCornerRadius())
+                .withShadowRadius(params.getShadowRadius());
 
         if (ENABLE_QUICKSTEP_LIVE_TILE.get() && params.getRecentsSurface() != null) {
             // When relativeLayer = 0, it reverts the surfaces back to the original order.
diff --git a/quickstep/src/com/android/quickstep/util/TransformParams.java b/quickstep/src/com/android/quickstep/util/TransformParams.java
index 45f49bb..c6a0c66 100644
--- a/quickstep/src/com/android/quickstep/util/TransformParams.java
+++ b/quickstep/src/com/android/quickstep/util/TransformParams.java
@@ -57,6 +57,7 @@
     private float mProgress;
     private float mTargetAlpha;
     private float mCornerRadius;
+    private float mShadowRadius;
     private RemoteAnimationTargets mTargetSet;
     private SurfaceTransactionApplier mSyncTransactionApplier;
     private SurfaceControl mRecentsSurface;
@@ -68,6 +69,7 @@
         mProgress = 0;
         mTargetAlpha = 1;
         mCornerRadius = -1;
+        mShadowRadius = 0;
     }
 
     /**
@@ -91,6 +93,14 @@
     }
 
     /**
+     * Sets the shadow radius of the transformed window, in pixels.
+     */
+    public TransformParams setShadowRadius(float shadowRadius) {
+        mShadowRadius = shadowRadius;
+        return this;
+    }
+
+    /**
      * Specifies the alpha of the transformed window. Default is 1.
      */
     public TransformParams setTargetAlpha(float targetAlpha) {
@@ -197,6 +207,10 @@
         return mCornerRadius;
     }
 
+    public float getShadowRadius() {
+        return mShadowRadius;
+    }
+
     public SurfaceControl getRecentsSurface() {
         return mRecentsSurface;
     }
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index 6547b53..e4bdb39 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -25,6 +25,10 @@
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
 
+import androidx.annotation.Nullable;
+
+import java.util.function.Consumer;
+
 /**
  * View class that represents the bottom row of the home screen.
  */
@@ -34,6 +38,7 @@
     private boolean mHasVerticalHotseat;
     private Workspace mWorkspace;
     private boolean mSendTouchToWorkspace;
+    @Nullable private Consumer<Boolean> mOnVisibilityAggregatedCallback;
 
     public Hotseat(Context context) {
         this(context, null);
@@ -129,4 +134,18 @@
         }
         return event.getY() > getCellHeight();
     }
+
+    @Override
+    public void onVisibilityAggregated(boolean isVisible) {
+        super.onVisibilityAggregated(isVisible);
+
+        if (mOnVisibilityAggregatedCallback != null) {
+            mOnVisibilityAggregatedCallback.accept(isVisible);
+        }
+    }
+
+    /** Sets a callback to be called onVisibilityAggregated */
+    public void setOnVisibilityAggregatedCallback(@Nullable Consumer<Boolean> callback) {
+        mOnVisibilityAggregatedCallback = callback;
+    }
 }
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 2b4520b..c0d5882 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -49,6 +49,7 @@
 import android.util.TimingLogger;
 
 import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.LauncherModel;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.Utilities;
@@ -306,6 +307,7 @@
         final PackageManagerHelper pmHelper = new PackageManagerHelper(context);
         final boolean isSafeMode = pmHelper.isSafeMode();
         final boolean isSdCardReady = Utilities.isBootCompleted();
+        final WidgetManagerHelper widgetHelper = new WidgetManagerHelper(context);
 
         boolean clearDb = false;
         try {
@@ -391,6 +393,7 @@
 
                 WorkspaceItemInfo info;
                 LauncherAppWidgetInfo appWidgetInfo;
+                LauncherAppWidgetProviderInfo widgetProviderInfo;
                 Intent intent;
                 String targetPkg;
 
@@ -720,6 +723,19 @@
                                             + appWidgetInfo.spanX + "x" + appWidgetInfo.spanY);
                                     continue;
                                 }
+                                widgetProviderInfo =
+                                        widgetHelper.getLauncherAppWidgetInfo(appWidgetId);
+                                if (widgetProviderInfo != null
+                                        && (appWidgetInfo.spanX < widgetProviderInfo.minSpanX
+                                        || appWidgetInfo.spanY < widgetProviderInfo.minSpanY)) {
+                                    // This can happen when display size changes.
+                                    c.markDeleted("Widget removed, min sizes not met: "
+                                            + "span=" + appWidgetInfo.spanX + "x"
+                                            + appWidgetInfo.spanY + " minSpan="
+                                            + widgetProviderInfo.minSpanX + "x"
+                                            + widgetProviderInfo.minSpanY);
+                                    continue;
+                                }
                                 if (!c.isOnWorkspaceOrHotseat()) {
                                     c.markDeleted("Widget found where container != " +
                                             "CONTAINER_DESKTOP nor CONTAINER_HOTSEAT - ignoring!");
diff --git a/src/com/android/launcher3/views/SearchResultIconRow.java b/src/com/android/launcher3/views/SearchResultIconRow.java
index 313ae5e..6438d1d 100644
--- a/src/com/android/launcher3/views/SearchResultIconRow.java
+++ b/src/com/android/launcher3/views/SearchResultIconRow.java
@@ -34,6 +34,7 @@
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload;
 import com.android.launcher3.allapps.search.AllAppsSearchBarController;
+import com.android.launcher3.icons.BitmapInfo;
 import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.ItemInfoWithIcon;
@@ -83,7 +84,8 @@
         if (payload.mRemoteAction != null) {
             prepareUsingRemoteAction(payload.mRemoteAction,
                     payload.bundle.getString(SearchTarget.REMOTE_ACTION_TOKEN),
-                    payload.bundle.getBoolean(SearchTarget.REMOTE_ACTION_SHOULD_START));
+                    payload.bundle.getBoolean(SearchTarget.REMOTE_ACTION_SHOULD_START),
+                    payload.type == ItemType.ACTION);
         } else {
             prepareUsingShortcutInfo(payload.shortcuts.get(0));
         }
@@ -102,7 +104,8 @@
         });
     }
 
-    private void prepareUsingRemoteAction(RemoteAction remoteAction, String token, boolean start) {
+    private void prepareUsingRemoteAction(RemoteAction remoteAction, String token, boolean start,
+            boolean useIconToBadge) {
         RemoteActionItemInfo itemInfo = new RemoteActionItemInfo(remoteAction, token, start);
 
         applyFromRemoteActionInfo(itemInfo);
@@ -110,8 +113,17 @@
             // If the Drawable from the remote action is not AdaptiveBitmap, styling will not work.
             try (LauncherIcons li = LauncherIcons.obtain(getContext())) {
                 Drawable d = itemInfo.getRemoteAction().getIcon().loadDrawable(getContext());
-                itemInfo.bitmap = li.createBadgedIconBitmap(d, itemInfo.user,
+                BitmapInfo bitmap = li.createBadgedIconBitmap(d, itemInfo.user,
                         Build.VERSION.SDK_INT);
+
+                if (useIconToBadge) {
+                    BitmapInfo placeholder = li.createIconBitmap(
+                            itemInfo.getRemoteAction().getTitle().toString().substring(0, 1),
+                            bitmap.color);
+                    itemInfo.bitmap = li.badgeBitmap(placeholder.icon, bitmap);
+                } else {
+                    itemInfo.bitmap = bitmap;
+                }
                 reapplyItemInfoAsync(itemInfo);
             }
         });
diff --git a/src/com/android/launcher3/views/SearchResultSuggestRow.java b/src/com/android/launcher3/views/SearchResultSuggestRow.java
index b5abbcc..6543c76 100644
--- a/src/com/android/launcher3/views/SearchResultSuggestRow.java
+++ b/src/com/android/launcher3/views/SearchResultSuggestRow.java
@@ -96,7 +96,8 @@
     private void handleSelection(int eventType) {
         ItemInfo itemInfo = (ItemInfo) getTag();
         Launcher launcher = Launcher.getLauncher(getContext());
-        if (itemInfo instanceof  RemoteActionItemInfo) return;
+
+        if (!(itemInfo instanceof  RemoteActionItemInfo)) return;
 
         RemoteActionItemInfo remoteItemInfo = (RemoteActionItemInfo) itemInfo;
         ItemClickHandler.onClickRemoteAction(launcher, remoteItemInfo);
diff --git a/tests/res/xml/appwidget_hidden.xml b/tests/res/xml/appwidget_hidden.xml
index 6f0e006..f6cffb5 100644
--- a/tests/res/xml/appwidget_hidden.xml
+++ b/tests/res/xml/appwidget_hidden.xml
@@ -1,8 +1,8 @@
 <?xml version="1.0" encoding="utf-8"?>
 <appwidget-provider
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:minWidth="180dp"
-    android:minHeight="110dp"
+    android:minWidth="1dp"
+    android:minHeight="1dp"
     android:updatePeriodMillis="86400000"
     android:initialLayout="@layout/test_layout_appwidget_blue"
     android:resizeMode="horizontal|vertical"
diff --git a/tests/res/xml/appwidget_no_config.xml b/tests/res/xml/appwidget_no_config.xml
index d24dfe3..0d932dc 100644
--- a/tests/res/xml/appwidget_no_config.xml
+++ b/tests/res/xml/appwidget_no_config.xml
@@ -1,8 +1,8 @@
 <?xml version="1.0" encoding="utf-8"?>
 <appwidget-provider
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:minWidth="180dp"
-    android:minHeight="110dp"
+    android:minWidth="1dp"
+    android:minHeight="1dp"
     android:updatePeriodMillis="86400000"
     android:initialLayout="@layout/test_layout_appwidget_red"
     android:resizeMode="horizontal|vertical"
diff --git a/tests/res/xml/appwidget_with_config.xml b/tests/res/xml/appwidget_with_config.xml
index 8403689..813e6df 100644
--- a/tests/res/xml/appwidget_with_config.xml
+++ b/tests/res/xml/appwidget_with_config.xml
@@ -1,8 +1,8 @@
 <?xml version="1.0" encoding="utf-8"?>
 <appwidget-provider
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:minWidth="180dp"
-    android:minHeight="110dp"
+    android:minWidth="1dp"
+    android:minHeight="1dp"
     android:updatePeriodMillis="86400000"
     android:initialLayout="@layout/test_layout_appwidget_blue"
     android:configure="com.android.launcher3.testcomponent.WidgetConfigActivity"