Merge "Update for stashed taskbar animation" into tm-qpr-dev
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 50f1236..5a58bc2 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -272,8 +272,6 @@
     <dimen name="taskbar_stashed_small_screen">108dp</dimen>
     <dimen name="taskbar_unstash_input_area">316dp</dimen>
     <dimen name="taskbar_stashed_handle_height">4dp</dimen>
-    <dimen name="taskbar_edu_wave_anim_trans_y">25dp</dimen>
-    <dimen name="taskbar_edu_wave_anim_trans_y_return_overshoot">4dp</dimen>
     <dimen name="taskbar_edu_horizontal_margin">112dp</dimen>
     <dimen name="taskbar_nav_buttons_width_kids">88dp</dimen>
     <dimen name="taskbar_nav_buttons_height_kids">40dp</dimen>
diff --git a/quickstep/src/com/android/launcher3/popup/QuickstepSystemShortcut.java b/quickstep/src/com/android/launcher3/popup/QuickstepSystemShortcut.java
index 7cf8201..c65fa5f 100644
--- a/quickstep/src/com/android/launcher3/popup/QuickstepSystemShortcut.java
+++ b/quickstep/src/com/android/launcher3/popup/QuickstepSystemShortcut.java
@@ -19,6 +19,8 @@
 import static com.android.launcher3.util.SplitConfigurationOptions.getLogEventForPosition;
 import static com.android.quickstep.util.SplitAnimationTimings.TABLET_HOME_TO_SPLIT;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.content.Intent;
 import android.graphics.Bitmap;
 import android.graphics.Rect;
@@ -115,13 +117,19 @@
 
             PendingAnimation anim = new PendingAnimation(TABLET_HOME_TO_SPLIT.getDuration());
             RectF startingTaskRect = new RectF();
-            FloatingTaskView floatingTaskView = FloatingTaskView.getFloatingTaskView(mTarget,
-                    source.view, null /* thumbnail */,
-                    source.drawable, startingTaskRect);
+            final FloatingTaskView floatingTaskView = FloatingTaskView.getFloatingTaskView(mTarget,
+                    source.view, null /* thumbnail */, source.drawable, startingTaskRect);
             floatingTaskView.setAlpha(1);
             floatingTaskView.addStagingAnimation(anim, startingTaskRect, mTempRect,
                     false /* fadeWithThumbnail */, true /* isStagedTask */);
             controller.setFirstFloatingTaskView(floatingTaskView);
+            anim.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationCancel(Animator animation) {
+                    mTarget.getDragLayer().removeView(floatingTaskView);
+                    controller.resetState();
+                }
+            });
             anim.buildAnim().start();
         }
     }
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index 0945bf2..8775359 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -42,7 +42,6 @@
 import com.android.launcher3.logging.InstanceId;
 import com.android.launcher3.logging.InstanceIdSequence;
 import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.model.data.ItemInfoWithIcon;
 import com.android.launcher3.uioverrides.QuickstepLauncher;
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.OnboardingPrefs;
@@ -51,7 +50,6 @@
 import com.android.quickstep.views.RecentsView;
 
 import java.io.PrintWriter;
-import java.util.Arrays;
 import java.util.Set;
 import java.util.stream.Stream;
 
@@ -256,11 +254,6 @@
         mTaskbarOverrideBackgroundAlpha.updateValue(forceHide ? 0 : 1);
     }
 
-    @Override
-    public Stream<ItemInfoWithIcon> getAppIconsForEdu() {
-        return Arrays.stream(mLauncher.getAppsView().getAppsStore().getApps());
-    }
-
     /**
      * Starts the taskbar education flow, if the user hasn't seen it yet.
      */
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index 875327d..2435236 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -210,7 +210,7 @@
         DeviceProfile deviceProfile = mContext.getDeviceProfile();
         Resources resources = mContext.getResources();
         Point p = !mContext.isUserSetupComplete()
-                ? new Point(0, resources.getDimensionPixelSize(R.dimen.taskbar_suw_frame))
+                ? new Point(0, controllers.taskbarActivityContext.getSetupWindowHeight())
                 : DimensionUtils.getTaskbarPhoneDimensions(deviceProfile, resources,
                         TaskbarManager.isPhoneMode(deviceProfile));
         mNavButtonsView.getLayoutParams().height = p.y;
@@ -691,8 +691,9 @@
                         (resources.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_size) / 2);
         navButtonsLayoutParams.setMarginEnd(0);
         navButtonsLayoutParams.gravity = Gravity.START;
+        mNavButtonsView.getLayoutParams().height =
+                mControllers.taskbarActivityContext.getSetupWindowHeight();
         mNavButtonContainer.setLayoutParams(navButtonsLayoutParams);
-        mNavButtonContainer.requestLayout();
     }
 
     /**
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 13ca58d..d9d46d4 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -341,6 +341,9 @@
 
     public void onConfigurationChanged(@Config int configChanges) {
         mControllers.onConfigurationChanged(configChanges);
+        if (!mIsUserSetupComplete) {
+            setTaskbarWindowHeight(getSetupWindowHeight());
+        }
     }
 
     public boolean isThreeButtonNav() {
@@ -673,7 +676,7 @@
         }
 
         if (!isUserSetupComplete()) {
-            return resources.getDimensionPixelSize(R.dimen.taskbar_suw_frame);
+            return getSetupWindowHeight();
         }
 
         if (DisplayController.isTransientTaskbar(this)) {
@@ -685,6 +688,10 @@
         return mDeviceProfile.taskbarSize + Math.max(getLeftCornerRadius(), getRightCornerRadius());
     }
 
+    public int getSetupWindowHeight() {
+        return getResources().getDimensionPixelSize(R.dimen.taskbar_suw_frame);
+    }
+
     /**
      * Either adds or removes {@link WindowManager.LayoutParams#FLAG_NOT_FOCUSABLE} on the taskbar
      * window.
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduController.java
index 365ec75..c53595d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduController.java
@@ -15,70 +15,27 @@
  */
 package com.android.launcher3.taskbar;
 
-import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
-import static com.android.launcher3.anim.Interpolators.ACCEL_2;
-import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
-import static com.android.launcher3.anim.Interpolators.DEACCEL;
-import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.Keyframe;
-import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
-import android.animation.TimeInterpolator;
-import android.content.res.Resources;
-import android.text.TextUtils;
-import android.view.View;
+import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_STASHED_IN_APP_EDU;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 
 import com.android.launcher3.R;
-import com.android.launcher3.icons.BitmapInfo;
-import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
-import com.android.launcher3.uioverrides.PredictedAppIcon;
+import com.android.launcher3.taskbar.overlay.TaskbarOverlayController;
 
 import java.io.PrintWriter;
-import java.util.Collections;
-import java.util.List;
-import java.util.stream.Collectors;
 
 /** Handles the Taskbar Education flow. */
 public class TaskbarEduController implements TaskbarControllers.LoggableTaskbarController {
 
-    private static final long WAVE_ANIM_DELAY = 250;
-    private static final long WAVE_ANIM_STAGGER = 50;
-    private static final long WAVE_ANIM_EACH_ICON_DURATION = 633;
-    private static final long WAVE_ANIM_SLOT_MACHINE_DURATION = 1085;
-    // The fraction of each icon's animation at which we reach the top point of the wave.
-    private static final float WAVE_ANIM_FRACTION_TOP = 0.4f;
-    // The fraction of each icon's animation at which we reach the bottom, before overshooting.
-    private static final float WAVE_ANIM_FRACTION_BOTTOM = 0.9f;
-    private static final TimeInterpolator WAVE_ANIM_TO_TOP_INTERPOLATOR = FAST_OUT_SLOW_IN;
-    private static final TimeInterpolator WAVE_ANIM_TO_BOTTOM_INTERPOLATOR = ACCEL_2;
-    private static final TimeInterpolator WAVE_ANIM_OVERSHOOT_INTERPOLATOR = DEACCEL;
-    private static final TimeInterpolator WAVE_ANIM_OVERSHOOT_RETURN_INTERPOLATOR = ACCEL_DEACCEL;
-    private static final float WAVE_ANIM_ICON_SCALE = 1.2f;
-    // How many icons to cycle through in the slot machine (+ the original icon at each end).
-    private static final int WAVE_ANIM_SLOT_MACHINE_NUM_ICONS = 3;
-
     private final TaskbarActivityContext mActivity;
-    private final float mWaveAnimTranslationY;
-    private final float mWaveAnimTranslationYReturnOvershoot;
 
     // Initialized in init.
     TaskbarControllers mControllers;
 
     private TaskbarEduView mTaskbarEduView;
-    private Animator mAnim;
 
     public TaskbarEduController(TaskbarActivityContext activity) {
         mActivity = activity;
-
-        final Resources resources = activity.getResources();
-        mWaveAnimTranslationY = resources.getDimension(R.dimen.taskbar_edu_wave_anim_trans_y);
-        mWaveAnimTranslationYReturnOvershoot = resources.getDimension(
-                R.dimen.taskbar_edu_wave_anim_trans_y_return_overshoot);
     }
 
     public void init(TaskbarControllers controllers) {
@@ -86,115 +43,32 @@
     }
 
     void showEdu() {
-        mActivity.setTaskbarWindowFullscreen(true);
-        mActivity.getDragLayer().post(() -> {
-            TaskbarOverlayContext overlayContext =
-                    mControllers.taskbarOverlayController.requestWindow();
-            mTaskbarEduView = (TaskbarEduView) overlayContext.getLayoutInflater().inflate(
-                    R.layout.taskbar_edu, overlayContext.getDragLayer(), false);
-            mTaskbarEduView.init(new TaskbarEduCallbacks());
-            mControllers.navbarButtonsViewController.setSlideInViewVisible(true);
-            mTaskbarEduView.setOnCloseBeginListener(
-                    () -> mControllers.navbarButtonsViewController.setSlideInViewVisible(false));
-            mTaskbarEduView.addOnCloseListener(() -> mTaskbarEduView = null);
-            mTaskbarEduView.show();
-            startAnim(createWaveAnim());
+        TaskbarOverlayController overlayController = mControllers.taskbarOverlayController;
+        TaskbarOverlayContext overlayContext = overlayController.requestWindow();
+        mTaskbarEduView = (TaskbarEduView) overlayContext.getLayoutInflater().inflate(
+                R.layout.taskbar_edu, overlayContext.getDragLayer(), false);
+        mTaskbarEduView.init(new TaskbarEduCallbacks());
+        mControllers.navbarButtonsViewController.setSlideInViewVisible(true);
+
+        TaskbarStashController stashController = mControllers.taskbarStashController;
+        stashController.updateStateForFlag(FLAG_STASHED_IN_APP_EDU, true);
+        stashController.applyState(overlayController.getOpenDuration());
+
+        mTaskbarEduView.setOnCloseBeginListener(() -> {
+            mControllers.navbarButtonsViewController.setSlideInViewVisible(false);
+            // Post in case view is closing due to gesture navigation. If a gesture is in progress,
+            // wait to unstash until after the gesture is finished.
+            MAIN_EXECUTOR.post(() -> stashController.resetFlagIfNoGestureInProgress(
+                    FLAG_STASHED_IN_APP_EDU));
         });
-    }
-
-    /**
-     * Starts the given animation, ending the previous animation first if it's still playing.
-     */
-    private void startAnim(Animator anim) {
-        if (mAnim != null) {
-            mAnim.end();
-        }
-        mAnim = anim;
-        mAnim.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                mAnim = null;
-            }
-        });
-        mAnim.start();
-    }
-
-    /**
-     * Creates a staggered "wave" animation where each icon translates and scales up in succession.
-     */
-    private Animator createWaveAnim() {
-        AnimatorSet waveAnim = new AnimatorSet();
-        View[] icons = mControllers.taskbarViewController.getIconViews();
-        for (int i = 0; i < icons.length; i++) {
-            View icon = icons[i];
-            AnimatorSet iconAnim = new AnimatorSet();
-
-            Keyframe[] scaleKeyframes = new Keyframe[] {
-                    Keyframe.ofFloat(0, 1f),
-                    Keyframe.ofFloat(WAVE_ANIM_FRACTION_TOP, WAVE_ANIM_ICON_SCALE),
-                    Keyframe.ofFloat(WAVE_ANIM_FRACTION_BOTTOM, 1f),
-                    Keyframe.ofFloat(1f, 1f)
-            };
-            scaleKeyframes[1].setInterpolator(WAVE_ANIM_TO_TOP_INTERPOLATOR);
-            scaleKeyframes[2].setInterpolator(WAVE_ANIM_TO_BOTTOM_INTERPOLATOR);
-
-            Keyframe[] translationYKeyframes = new Keyframe[] {
-                    Keyframe.ofFloat(0, 0f),
-                    Keyframe.ofFloat(WAVE_ANIM_FRACTION_TOP, -mWaveAnimTranslationY),
-                    Keyframe.ofFloat(WAVE_ANIM_FRACTION_BOTTOM, 0f),
-                    // Half of the remaining fraction overshoots, then the other half returns to 0.
-                    Keyframe.ofFloat(
-                            WAVE_ANIM_FRACTION_BOTTOM + (1 - WAVE_ANIM_FRACTION_BOTTOM) / 2f,
-                            mWaveAnimTranslationYReturnOvershoot),
-                    Keyframe.ofFloat(1f, 0f)
-            };
-            translationYKeyframes[1].setInterpolator(WAVE_ANIM_TO_TOP_INTERPOLATOR);
-            translationYKeyframes[2].setInterpolator(WAVE_ANIM_TO_BOTTOM_INTERPOLATOR);
-            translationYKeyframes[3].setInterpolator(WAVE_ANIM_OVERSHOOT_INTERPOLATOR);
-            translationYKeyframes[4].setInterpolator(WAVE_ANIM_OVERSHOOT_RETURN_INTERPOLATOR);
-
-            iconAnim.play(ObjectAnimator.ofPropertyValuesHolder(icon,
-                    PropertyValuesHolder.ofKeyframe(SCALE_PROPERTY, scaleKeyframes))
-                    .setDuration(WAVE_ANIM_EACH_ICON_DURATION));
-            iconAnim.play(ObjectAnimator.ofPropertyValuesHolder(icon,
-                    PropertyValuesHolder.ofKeyframe(View.TRANSLATION_Y, translationYKeyframes))
-                    .setDuration(WAVE_ANIM_EACH_ICON_DURATION));
-
-            if (icon instanceof PredictedAppIcon) {
-                // Play slot machine animation through random icons from AllAppsList.
-                PredictedAppIcon predictedAppIcon = (PredictedAppIcon) icon;
-                ItemInfo itemInfo = (ItemInfo) icon.getTag();
-                List<BitmapInfo> iconsToAnimate = mControllers.uiController.getAppIconsForEdu()
-                        .filter(appInfo -> !TextUtils.equals(appInfo.title, itemInfo.title))
-                        .map(appInfo -> appInfo.bitmap)
-                        .filter(bitmap -> !bitmap.isNullOrLowRes())
-                        .collect(Collectors.toList());
-                // Pick n icons at random.
-                Collections.shuffle(iconsToAnimate);
-                if (iconsToAnimate.size() > WAVE_ANIM_SLOT_MACHINE_NUM_ICONS) {
-                    iconsToAnimate = iconsToAnimate.subList(0, WAVE_ANIM_SLOT_MACHINE_NUM_ICONS);
-                }
-                Animator slotMachineAnim = predictedAppIcon.createSlotMachineAnim(iconsToAnimate);
-                if (slotMachineAnim != null) {
-                    iconAnim.play(slotMachineAnim.setDuration(WAVE_ANIM_SLOT_MACHINE_DURATION));
-                }
-            }
-
-            iconAnim.setStartDelay(WAVE_ANIM_STAGGER * i);
-            waveAnim.play(iconAnim);
-        }
-        waveAnim.setStartDelay(WAVE_ANIM_DELAY);
-        return waveAnim;
+        mTaskbarEduView.addOnCloseListener(() -> mTaskbarEduView = null);
+        mTaskbarEduView.show();
     }
 
     @Override
     public void dumpLogs(String prefix, PrintWriter pw) {
         pw.println(prefix + "TaskbarEduController:");
-
         pw.println(prefix + "\tisShowingEdu=" + (mTaskbarEduView != null));
-        pw.println(prefix + "\tmWaveAnimTranslationY=" + mWaveAnimTranslationY);
-        pw.println(prefix + "\tmWaveAnimTranslationYReturnOvershoot="
-                + mWaveAnimTranslationYReturnOvershoot);
     }
 
     /**
@@ -221,5 +95,13 @@
         int getIconLayoutBoundsWidth() {
             return mControllers.taskbarViewController.getIconLayoutWidth();
         }
+
+        int getOpenDuration() {
+            return mControllers.taskbarOverlayController.getOpenDuration();
+        }
+
+        int getCloseDuration() {
+            return mControllers.taskbarOverlayController.getCloseDuration();
+        }
     }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduView.java
index bb87f48..d38c6d1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduView.java
@@ -35,9 +35,6 @@
 public class TaskbarEduView extends AbstractSlideInView<TaskbarOverlayContext>
         implements Insettable {
 
-    private static final int DEFAULT_OPEN_DURATION = 500;
-    private static final int DEFAULT_CLOSE_DURATION = 200;
-
     private final Rect mInsets = new Rect();
 
     // Initialized in init.
@@ -65,7 +62,7 @@
 
     @Override
     protected void handleClose(boolean animate) {
-        handleClose(animate, DEFAULT_CLOSE_DURATION);
+        handleClose(animate, mTaskbarEduCallbacks.getCloseDuration());
     }
 
     @Override
@@ -161,7 +158,7 @@
         mOpenCloseAnimator.setValues(
                 PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
         mOpenCloseAnimator.setInterpolator(AGGRESSIVE_EASE);
-        mOpenCloseAnimator.setDuration(DEFAULT_OPEN_DURATION).start();
+        mOpenCloseAnimator.setDuration(mTaskbarEduCallbacks.getOpenDuration()).start();
     }
 
     void snapToPage(int page) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
index 32c1972..b388512 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
@@ -149,7 +149,8 @@
         if (context.dragLayer.alpha < AlphaUpdateListener.ALPHA_CUTOFF_THRESHOLD) {
             // Let touches pass through us.
             insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION)
-        } else if (controllers.navbarButtonsViewController.isImeVisible) {
+        } else if (controllers.navbarButtonsViewController.isImeVisible
+                && controllers.taskbarStashController.isStashed()) {
             insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION)
         } else if (!controllers.uiController.isTaskbarTouchable) {
             // Let touches pass through us.
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index 722430e..e62e533 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -17,7 +17,6 @@
 
 import static android.view.HapticFeedbackConstants.LONG_PRESS;
 
-import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_LONGPRESS_HIDE;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_LONGPRESS_SHOW;
 import static com.android.launcher3.taskbar.Utilities.appendFlag;
@@ -80,6 +79,7 @@
     public static final int FLAG_IN_SETUP = 1 << 8; // In the Setup Wizard
     public static final int FLAG_STASHED_SMALL_SCREEN = 1 << 9; // phone screen gesture nav, stashed
     public static final int FLAG_STASHED_IN_APP_AUTO = 1 << 10; // Autohide (transient taskbar).
+    public static final int FLAG_STASHED_IN_APP_EDU = 1 << 11; // EDU is visible.
 
     // If any of these flags are enabled, isInApp should return true.
     private static final int FLAGS_IN_APP = FLAG_IN_APP | FLAG_IN_SETUP;
@@ -88,7 +88,7 @@
     private static final int FLAGS_STASHED_IN_APP = FLAG_STASHED_IN_APP_MANUAL
             | FLAG_STASHED_IN_SYSUI_STATE | FLAG_STASHED_IN_APP_EMPTY | FLAG_STASHED_IN_APP_SETUP
             | FLAG_STASHED_IN_APP_IME | FLAG_STASHED_IN_TASKBAR_ALL_APPS
-            | FLAG_STASHED_SMALL_SCREEN | FLAG_STASHED_IN_APP_AUTO;
+            | FLAG_STASHED_SMALL_SCREEN | FLAG_STASHED_IN_APP_AUTO | FLAG_STASHED_IN_APP_EDU;
 
     private static final int FLAGS_STASHED_IN_APP_IGNORING_IME =
             FLAGS_STASHED_IN_APP & ~FLAG_STASHED_IN_APP_IME;
@@ -98,7 +98,8 @@
     // Currently any flag that causes us to stash in an app is included, except for IME or All Apps
     // since those cover the underlying app anyway and thus the app shouldn't change insets.
     private static final int FLAGS_REPORT_STASHED_INSETS_TO_APP = FLAGS_STASHED_IN_APP
-            & ~FLAG_STASHED_IN_APP_IME & ~FLAG_STASHED_IN_TASKBAR_ALL_APPS;
+            & ~FLAG_STASHED_IN_APP_IME & ~FLAG_STASHED_IN_TASKBAR_ALL_APPS
+            & ~FLAG_STASHED_IN_APP_EDU;
 
     /**
      * How long to stash/unstash when manually invoked via long press.
@@ -374,11 +375,11 @@
 
         if (supportsVisualStashing() && hasAnyFlag(FLAGS_REPORT_STASHED_INSETS_TO_APP)) {
             DeviceProfile dp = mActivity.getDeviceProfile();
-            if (hasAnyFlag(FLAG_STASHED_IN_APP_SETUP) && dp.isTaskbarPresent && !dp.isLandscape) {
+            if (hasAnyFlag(FLAG_STASHED_IN_APP_SETUP) && dp.isTaskbarPresent) {
                 // We always show the back button in SUW but in portrait the SUW layout may not
-                // be wide enough to support overlapping the nav bar with its content.  For now,
-                // just inset by the bar height.
-                return mUnstashedHeight;
+                // be wide enough to support overlapping the nav bar with its content.
+                // We're sending different res values in portrait vs landscape
+                return mActivity.getResources().getDimensionPixelSize(R.dimen.taskbar_suw_insets);
             }
             boolean isAnimating = mAnimator != null && mAnimator.isStarted();
             if (!mControllers.stashedHandleViewController.isStashedHandleVisible()
@@ -392,10 +393,6 @@
             return mStashedHeight;
         }
 
-        if (!mActivity.isUserSetupComplete()) {
-            // Special insets for SUW.
-            return mActivity.getResources().getDimensionPixelSize(R.dimen.taskbar_suw_insets);
-        }
         return mUnstashedHeight;
     }
 
@@ -706,34 +703,27 @@
 
         // Only update the following flags when system gesture is not in progress.
         boolean shouldStashForIme = shouldStashForIme();
-        maybeResetStashedInAppAllApps(
-                hasAnyFlag(FLAG_STASHED_IN_APP_IME) == shouldStashForIme);
+        updateStateForFlag(FLAG_STASHED_IN_TASKBAR_ALL_APPS, false);
+        updateStateForFlag(FLAG_STASHED_IN_APP_EDU, false);
         if (hasAnyFlag(FLAG_STASHED_IN_APP_IME) != shouldStashForIme) {
             updateStateForFlag(FLAG_STASHED_IN_APP_IME, shouldStashForIme);
             applyState(TASKBAR_STASH_DURATION_FOR_IME, getTaskbarStashStartDelayForIme());
+        } else {
+            applyState(mControllers.taskbarOverlayController.getCloseDuration());
         }
     }
 
     /**
-     * Reset stashed in all apps only if no system gesture is in progress.
+     * Resets the flag if no system gesture is in progress.
      * <p>
      * Otherwise, the reset should be deferred until after the gesture is finished.
      *
      * @see #setSystemGestureInProgress
      */
-    public void maybeResetStashedInAppAllApps() {
-        maybeResetStashedInAppAllApps(true);
-    }
-
-    private void maybeResetStashedInAppAllApps(boolean applyState) {
-        if (mIsSystemGestureInProgress) {
-            return;
-        }
-
-        updateStateForFlag(FLAG_STASHED_IN_TASKBAR_ALL_APPS, false);
-        if (applyState) {
-            applyState(ALL_APPS.getTransitionDuration(
-                    mControllers.taskbarActivityContext, false /* isToState */));
+    public void resetFlagIfNoGestureInProgress(int flag) {
+        if (!mIsSystemGestureInProgress) {
+            updateStateForFlag(flag, false);
+            applyState(mControllers.taskbarOverlayController.getCloseDuration());
         }
     }
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
index 4ec9b41..6c6b002 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -29,7 +29,6 @@
 import com.android.quickstep.views.TaskView;
 
 import java.io.PrintWriter;
-import java.util.stream.Stream;
 
 /**
  * Base class for providing different taskbar UI
@@ -66,10 +65,6 @@
 
     protected void onStashedInAppChanged() { }
 
-    public Stream<ItemInfoWithIcon> getAppIconsForEdu() {
-        return Stream.empty();
-    }
-
     /** Called when an icon is launched. */
     public void onTaskbarIconLaunched(ItemInfo item) { }
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index 9936d27..b5e6fac 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -193,7 +193,8 @@
      * Should be called when the IME visibility changes, so we can make Taskbar not steal touches.
      */
     public void setImeIsVisible(boolean isImeVisible) {
-        mTaskbarView.setTouchesEnabled(!isImeVisible);
+        mTaskbarView.setTouchesEnabled(!isImeVisible
+                || DisplayController.isTransientTaskbar(mActivity));
     }
 
     /**
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
index c8bfc2a..8502752 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
@@ -15,7 +15,6 @@
  */
 package com.android.launcher3.taskbar.allapps;
 
-import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.anim.Interpolators.EMPHASIZED;
 
 import android.animation.PropertyValuesHolder;
@@ -28,6 +27,7 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Insettable;
 import com.android.launcher3.R;
+import com.android.launcher3.taskbar.allapps.TaskbarAllAppsViewController.TaskbarAllAppsCallbacks;
 import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
 import com.android.launcher3.views.AbstractSlideInView;
 
@@ -37,6 +37,9 @@
     private TaskbarAllAppsContainerView mAppsView;
     private float mShiftRange;
 
+    // Initialized in init.
+    private TaskbarAllAppsCallbacks mAllAppsCallbacks;
+
     public TaskbarAllAppsSlideInView(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
     }
@@ -46,6 +49,10 @@
         super(context, attrs, defStyleAttr);
     }
 
+    void init(TaskbarAllAppsCallbacks callbacks) {
+        mAllAppsCallbacks = callbacks;
+    }
+
     /** Opens the all apps view. */
     void show(boolean animate) {
         if (mIsOpen || mOpenCloseAnimator.isRunning()) {
@@ -58,8 +65,7 @@
             mOpenCloseAnimator.setValues(
                     PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
             mOpenCloseAnimator.setInterpolator(EMPHASIZED);
-            mOpenCloseAnimator.setDuration(
-                    ALL_APPS.getTransitionDuration(mActivityContext, true /* isToState */)).start();
+            mOpenCloseAnimator.setDuration(mAllAppsCallbacks.getOpenDuration()).start();
         } else {
             mTranslationShift = TRANSLATION_SHIFT_OPENED;
         }
@@ -72,8 +78,7 @@
 
     @Override
     protected void handleClose(boolean animate) {
-        handleClose(animate,
-                ALL_APPS.getTransitionDuration(mActivityContext, false /* isToState */));
+        handleClose(animate, mAllAppsCallbacks.getCloseDuration());
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java
index 54392b2..4165486 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java
@@ -17,6 +17,7 @@
 
 import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_STASHED_IN_TASKBAR_ALL_APPS;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.OnboardingPrefs.ALL_APPS_VISITED_COUNT;
 
 import com.android.launcher3.AbstractFloatingView;
@@ -26,6 +27,7 @@
 import com.android.launcher3.taskbar.TaskbarControllers;
 import com.android.launcher3.taskbar.TaskbarStashController;
 import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
+import com.android.launcher3.taskbar.overlay.TaskbarOverlayController;
 
 /**
  * Handles the {@link TaskbarAllAppsContainerView} behavior and synchronizes its transitions with
@@ -38,6 +40,7 @@
     private final TaskbarAllAppsContainerView mAppsView;
     private final TaskbarStashController mTaskbarStashController;
     private final NavbarButtonsViewController mNavbarButtonsViewController;
+    private final TaskbarOverlayController mOverlayController;
 
     TaskbarAllAppsViewController(
             TaskbarOverlayContext context,
@@ -49,7 +52,9 @@
         mAppsView = mSlideInView.getAppsView();
         mTaskbarStashController = taskbarControllers.taskbarStashController;
         mNavbarButtonsViewController = taskbarControllers.navbarButtonsViewController;
+        mOverlayController = taskbarControllers.taskbarOverlayController;
 
+        mSlideInView.init(new TaskbarAllAppsCallbacks());
         setUpIconLongClick();
         setUpAppDivider();
         setUpTaskbarStashing();
@@ -93,7 +98,18 @@
                     mContext, AbstractFloatingView.TYPE_ACTION_POPUP);
             // Post in case view is closing due to gesture navigation. If a gesture is in progress,
             // wait to unstash until after the gesture is finished.
-            mSlideInView.post(mTaskbarStashController::maybeResetStashedInAppAllApps);
+            MAIN_EXECUTOR.post(() -> mTaskbarStashController.resetFlagIfNoGestureInProgress(
+                    FLAG_STASHED_IN_TASKBAR_ALL_APPS));
         });
     }
+
+    class TaskbarAllAppsCallbacks {
+        int getOpenDuration() {
+            return mOverlayController.getOpenDuration();
+        }
+
+        int getCloseDuration() {
+            return mOverlayController.getCloseDuration();
+        }
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java
index 019e5a6..476e0a8 100644
--- a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java
@@ -20,6 +20,7 @@
 
 import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
 import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE;
+import static com.android.launcher3.LauncherState.ALL_APPS;
 
 import android.annotation.SuppressLint;
 import android.content.Context;
@@ -150,6 +151,16 @@
         });
     }
 
+    /** The default open duration for overlays. */
+    public int getOpenDuration() {
+        return ALL_APPS.getTransitionDuration(mTaskbarContext, true);
+    }
+
+    /** The default close duration for overlays. */
+    public int getCloseDuration() {
+        return ALL_APPS.getTransitionDuration(mTaskbarContext, false);
+    }
+
     @SuppressLint("WrongConstant")
     private LayoutParams createLayoutParams() {
         LayoutParams layoutParams = new LayoutParams(
diff --git a/quickstep/src/com/android/quickstep/TaskIconCache.java b/quickstep/src/com/android/quickstep/TaskIconCache.java
index dc60875..7c05a10 100644
--- a/quickstep/src/com/android/quickstep/TaskIconCache.java
+++ b/quickstep/src/com/android/quickstep/TaskIconCache.java
@@ -249,12 +249,13 @@
     private BitmapInfo getBitmapInfo(Drawable drawable, int userId,
             int primaryColor, boolean isInstantApp) {
         try (BaseIconFactory bif = getIconFactory()) {
-            bif.disableColorExtraction();
             bif.setWrapperBackgroundColor(primaryColor);
 
             // User version code O, so that the icon is always wrapped in an adaptive icon container
             return bif.createBadgedIconBitmap(drawable,
-                    new IconOptions().setUser(UserHandle.of(userId)).setInstantApp(isInstantApp));
+                    new IconOptions().setUser(UserHandle.of(userId))
+                            .setInstantApp(isInstantApp)
+                            .setExtractedColor(0));
         }
     }
 
diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
index 0ef4597..0a155cb 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -342,6 +342,10 @@
                 return;
             }
 
+            if (mItemInfo == null) {
+                return;
+            }
+
             if (mItemInfo.container < 0 || appState == null) {
                 // Write log on the model thread so that logs do not go out of order
                 // (for eg: drop comes after drag)
diff --git a/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java b/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java
index e8a4b0a..e5c74dc 100644
--- a/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java
@@ -19,21 +19,39 @@
 import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_FULLSCREEN_WITH_KEYBOARD_SHORTCUTS;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.content.Intent;
+import android.graphics.Rect;
+import android.graphics.RectF;
 import android.view.View;
 
+import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.icons.BitmapInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.quickstep.views.FloatingTaskView;
+import com.android.quickstep.views.RecentsView;
+import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
 
 /** Handles when the stage split lands on the home screen. */
 public class SplitToWorkspaceController {
 
     private final Launcher mLauncher;
+    private final DeviceProfile mDP;
     private final SplitSelectStateController mController;
 
+    private final int mHalfDividerSize;
+
     public SplitToWorkspaceController(Launcher launcher, SplitSelectStateController controller) {
         mLauncher = launcher;
+        mDP = mLauncher.getDeviceProfile();
         mController = controller;
+
+        mHalfDividerSize = mLauncher.getResources().getDimensionPixelSize(
+                R.dimen.multi_window_task_divider_size) / 2;
     }
 
     /**
@@ -48,19 +66,74 @@
         }
         Object tag = view.getTag();
         Intent intent;
+        BitmapInfo bitmapInfo;
         if (tag instanceof WorkspaceItemInfo) {
             final WorkspaceItemInfo workspaceItemInfo = (WorkspaceItemInfo) tag;
             intent = workspaceItemInfo.intent;
+            bitmapInfo = workspaceItemInfo.bitmap;
         } else if (tag instanceof com.android.launcher3.model.data.AppInfo) {
             final com.android.launcher3.model.data.AppInfo appInfo =
                     (com.android.launcher3.model.data.AppInfo) tag;
             intent = appInfo.intent;
+            bitmapInfo = appInfo.bitmap;
         } else {
             return false;
         }
+
         mController.setSecondTask(intent);
-        mController.launchSplitTasks(aBoolean -> mLauncher.getDragLayer().removeView(
-                mController.getFirstFloatingTaskView()));
+
+        boolean isTablet = mLauncher.getDeviceProfile().isTablet;
+        SplitAnimationTimings timings = AnimUtils.getDeviceSplitToConfirmTimings(isTablet);
+        PendingAnimation pendingAnimation = new PendingAnimation(timings.getDuration());
+
+        Rect firstTaskStartingBounds = new Rect();
+        Rect firstTaskEndingBounds = new Rect();
+        RectF secondTaskStartingBounds = new RectF();
+        Rect secondTaskEndingBounds = new Rect();
+
+        RecentsView recentsView = mLauncher.getOverviewPanel();
+        recentsView.getPagedOrientationHandler().getFinalSplitPlaceholderBounds(mHalfDividerSize,
+                mDP, mController.getActiveSplitStagePosition(), firstTaskEndingBounds,
+                secondTaskEndingBounds);
+
+        FloatingTaskView firstFloatingTaskView = mController.getFirstFloatingTaskView();
+        firstFloatingTaskView.getBoundsOnScreen(firstTaskStartingBounds);
+        firstFloatingTaskView.addConfirmAnimation(pendingAnimation,
+                new RectF(firstTaskStartingBounds), firstTaskEndingBounds,
+                false /* fadeWithThumbnail */, true /* isStagedTask */);
+
+        FloatingTaskView secondFloatingTaskView = FloatingTaskView.getFloatingTaskView(mLauncher,
+                view, null /* thumbnail */, bitmapInfo.newIcon(mLauncher),
+                secondTaskStartingBounds);
+        secondFloatingTaskView.setAlpha(1);
+        secondFloatingTaskView.addConfirmAnimation(pendingAnimation, secondTaskStartingBounds,
+                secondTaskEndingBounds, true /* fadeWithThumbnail */, false /* isStagedTask */);
+
+        pendingAnimation.addListener(new AnimatorListenerAdapter() {
+            private boolean mIsCancelled = false;
+
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                mIsCancelled = true;
+                cleanUp();
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                if (!mIsCancelled) {
+                    mController.launchSplitTasks(aBoolean -> cleanUp());
+                    InteractionJankMonitorWrapper.end(
+                            InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER);
+                }
+            }
+
+            private void cleanUp() {
+                mLauncher.getDragLayer().removeView(firstFloatingTaskView);
+                mLauncher.getDragLayer().removeView(secondFloatingTaskView);
+                mController.resetState();
+            }
+        });
+        pendingAnimation.buildAnim().start();
         return true;
     }
 }
diff --git a/res/layout/all_apps.xml b/res/layout/all_apps.xml
index d0d82d4..f8a871a 100644
--- a/res/layout/all_apps.xml
+++ b/res/layout/all_apps.xml
@@ -25,34 +25,5 @@
     android:focusable="false"
     android:saveEnabled="false">
 
-    <include
-        layout="@layout/all_apps_bottom_sheet_background"
-        android:visibility="gone" />
-
-    <include
-        layout="@layout/search_results_rv_layout"
-        android:visibility="gone" />
-
-    <include
-        layout="@layout/all_apps_rv_layout"
-        android:visibility="gone" />
-
-    <com.android.launcher3.allapps.FloatingHeaderView
-        android:id="@+id/all_apps_header"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:clipToPadding="false"
-        android:paddingTop="@dimen/all_apps_header_top_padding"
-        android:paddingBottom="@dimen/all_apps_header_bottom_padding"
-        android:orientation="vertical" >
-
-        <include layout="@layout/floating_header_content" />
-
-        <include layout="@layout/all_apps_personal_work_tabs" />
-
-    </com.android.launcher3.allapps.FloatingHeaderView>
-
-    <include layout="@layout/search_container_all_apps" />
-
-    <include layout="@layout/all_apps_fast_scroller" />
+    <include layout="@layout/all_apps_content" />
 </com.android.launcher3.allapps.LauncherAllAppsContainerView>
\ No newline at end of file
diff --git a/res/layout/all_apps_content.xml b/res/layout/all_apps_content.xml
new file mode 100644
index 0000000..b33029f
--- /dev/null
+++ b/res/layout/all_apps_content.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2022 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.
+-->
+<!-- This file is used by multiple all_apps.xml. Layout consists of all contents
+     showed in all apps screen
+-->
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <include
+        layout="@layout/all_apps_bottom_sheet_background"
+        android:visibility="gone" />
+
+    <include
+        layout="@layout/search_results_rv_layout"
+        android:visibility="gone" />
+
+    <include
+        layout="@layout/all_apps_rv_layout"
+        android:visibility="gone" />
+
+    <com.android.launcher3.allapps.FloatingHeaderView
+        android:id="@+id/all_apps_header"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:clipToPadding="false"
+        android:paddingTop="@dimen/all_apps_header_top_padding"
+        android:paddingBottom="@dimen/all_apps_header_bottom_padding"
+        android:orientation="vertical" >
+
+        <include layout="@layout/floating_header_content" />
+
+        <include layout="@layout/all_apps_personal_work_tabs" />
+
+    </com.android.launcher3.allapps.FloatingHeaderView>
+
+    <include layout="@layout/search_container_all_apps" />
+
+    <include layout="@layout/all_apps_fast_scroller" />
+
+</merge>
\ No newline at end of file
diff --git a/src/com/android/launcher3/ExtendedEditText.java b/src/com/android/launcher3/ExtendedEditText.java
index 11f2020..c59f25d 100644
--- a/src/com/android/launcher3/ExtendedEditText.java
+++ b/src/com/android/launcher3/ExtendedEditText.java
@@ -18,6 +18,7 @@
 import static com.android.launcher3.logging.KeyboardStateManager.KeyboardState.SHOW;
 
 import android.content.Context;
+import android.graphics.Rect;
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.view.DragEvent;
@@ -27,12 +28,17 @@
 
 import com.android.launcher3.views.ActivityContext;
 
+import java.util.HashSet;
+import java.util.Set;
+
 
 /**
  * The edit text that reports back when the back key has been pressed.
  * Note: AppCompatEditText doesn't fully support #displayCompletions and #onCommitCompletion
  */
 public class ExtendedEditText extends EditText {
+    private final Set<OnFocusChangeListener> mOnFocusChangeListeners = new HashSet<>();
+
     private boolean mForceDisableSuggestions = false;
 
     /**
@@ -129,4 +135,28 @@
             setText("");
         }
     }
+
+    /**
+     * This method should be preferred to {@link #setOnFocusChangeListener(OnFocusChangeListener)},
+     * as it allows for multiple listeners from different sources.
+     */
+    public void addOnFocusChangeListener(OnFocusChangeListener listener) {
+        mOnFocusChangeListeners.add(listener);
+    }
+
+    /**
+     * Removes the given listener from the set of registered focus listeners, or does nothing if it
+     * wasn't registered in the first place.
+     */
+    public void removeOnFocusChangeListener(OnFocusChangeListener listener) {
+        mOnFocusChangeListeners.remove(listener);
+    }
+
+    @Override
+    protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
+        super.onFocusChanged(focused, direction, previouslyFocusedRect);
+        for (OnFocusChangeListener listener : mOnFocusChangeListeners) {
+            listener.onFocusChange(this, focused);
+        }
+    }
 }
diff --git a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
index 4d58eb0..4427a49 100644
--- a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
+++ b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
@@ -67,7 +67,7 @@
         mInput.addTextChangedListener(this);
         mInput.setOnEditorActionListener(this);
         mInput.setOnBackKeyListener(this);
-        mInput.setOnFocusChangeListener(this);
+        mInput.addOnFocusChangeListener(this);
         mSearchAlgorithm = searchAlgorithm;
     }
 
@@ -153,6 +153,7 @@
         mCallback.clearSearchResult();
         mInput.reset();
         mQuery = null;
+        mInput.removeOnFocusChangeListener(this);
     }
 
     /**
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 669159f..88654b7 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -124,6 +124,10 @@
             "FOLDER_NAME_MAJORITY_RANKING", true,
             "Suggests folder names based on majority based ranking.");
 
+    public static final BooleanFlag INJECT_FALLBACK_APP_CORPUS_RESULTS = new DeviceFlag(
+            "INJECT_FALLBACK_APP_CORPUS_RESULTS", false, "Inject "
+            + "fallback app corpus result when AiAi fails to return it.");
+
     public static final BooleanFlag ASSISTANT_GIVES_LAUNCHER_FOCUS = getDebugFlag(
             "ASSISTANT_GIVES_LAUNCHER_FOCUS", false,
             "Allow Launcher to handle nav bar gestures while Assistant is running over it");
@@ -297,10 +301,6 @@
             "USE_SEARCH_REQUEST_TIMEOUT_OVERRIDES", false,
             "Use local overrides for search request timeout");
 
-    public static final BooleanFlag ENABLE_RICH_ANSWER = new DeviceFlag(
-            "ENABLE_RICH_ANSWER", false,
-            "Enable rich answer new UI for web answer search results");
-
     public static final BooleanFlag CONTINUOUS_VIEW_TREE_CAPTURE = getDebugFlag(
             "CONTINUOUS_VIEW_TREE_CAPTURE", false, "Capture View tree every frame");
 
@@ -330,6 +330,10 @@
             "HOME_GARDENING_WORKSPACE_BUTTONS", false,
             "Change workspace edit buttons to reflect home gardening");
 
+    public static final BooleanFlag ENABLE_DOWNLOAD_APP_UX_V2 = getDebugFlag(
+            "ENABLE_DOWNLOAD_APP_UX_V2", false, "Updates the download app UX"
+                    + " to have better visuals");
+
     public static final BooleanFlag ENABLE_TASKBAR_REVISED_THRESHOLDS = getDebugFlag(
             "ENABLE_TASKBAR_REVISED_THRESHOLDS", false,
             "Uses revised thresholds for transient taskbar.");
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 99822da..94eea35 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -282,7 +282,6 @@
         mFolderName = findViewById(R.id.folder_name);
         mFolderName.setTextSize(TypedValue.COMPLEX_UNIT_PX, dp.folderLabelTextSizePx);
         mFolderName.setOnBackKeyListener(this);
-        mFolderName.setOnFocusChangeListener(this);
         mFolderName.setOnEditorActionListener(this);
         mFolderName.setSelectAllOnFocus(true);
         mFolderName.setInputType(mFolderName.getInputType()
@@ -457,6 +456,13 @@
         // the folder itself.
         requestFocus();
         super.onAttachedToWindow();
+        mFolderName.addOnFocusChangeListener(this);
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        mFolderName.removeOnFocusChangeListener(this);
     }
 
     @Override
diff --git a/src/com/android/launcher3/icons/ShortcutCachingLogic.java b/src/com/android/launcher3/icons/ShortcutCachingLogic.java
index 9a86ede..bb7248f 100644
--- a/src/com/android/launcher3/icons/ShortcutCachingLogic.java
+++ b/src/com/android/launcher3/icons/ShortcutCachingLogic.java
@@ -32,6 +32,7 @@
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.icons.BaseIconFactory.IconOptions;
 import com.android.launcher3.icons.cache.CachingLogic;
 import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.util.Themes;
@@ -76,9 +77,8 @@
             Drawable unbadgedDrawable = ShortcutCachingLogic.getIcon(
                     context, info, LauncherAppState.getIDP(context).fillResIconDpi);
             if (unbadgedDrawable == null) return BitmapInfo.LOW_RES_INFO;
-            return new BitmapInfo(
-                    li.createScaledBitmap(unbadgedDrawable, BaseIconFactory.MODE_WITH_SHADOW),
-                    Themes.getColorAccent(context));
+            return li.createBadgedIconBitmap(unbadgedDrawable,
+                    new IconOptions().setExtractedColor(Themes.getColorAccent(context)));
         }
     }
 
diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java
index b4be061..098cf80 100644
--- a/src/com/android/launcher3/touch/ItemClickHandler.java
+++ b/src/com/android/launcher3/touch/ItemClickHandler.java
@@ -28,6 +28,7 @@
 
 import android.app.AlertDialog;
 import android.app.PendingIntent;
+import android.content.ActivityNotFoundException;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentSender;
@@ -315,10 +316,16 @@
      */
     public static void onClickSearchAction(Launcher launcher, SearchActionItemInfo itemInfo) {
         if (itemInfo.getIntent() != null) {
-            if (itemInfo.hasFlags(SearchActionItemInfo.FLAG_SHOULD_START_FOR_RESULT)) {
-                launcher.startActivityForResult(itemInfo.getIntent(), 0);
-            } else {
-                launcher.startActivity(itemInfo.getIntent());
+            try {
+                if (itemInfo.hasFlags(SearchActionItemInfo.FLAG_SHOULD_START_FOR_RESULT)) {
+                    launcher.startActivityForResult(itemInfo.getIntent(), 0);
+                } else {
+                    launcher.startActivity(itemInfo.getIntent());
+                }
+            } catch (ActivityNotFoundException e) {
+                Toast.makeText(launcher,
+                        launcher.getResources().getText(R.string.shortcut_not_available),
+                        Toast.LENGTH_SHORT).show();
             }
         } else if (itemInfo.getPendingIntent() != null) {
             try {
diff --git a/tests/res/xml/shortcuts.xml b/tests/res/xml/shortcuts.xml
index bdc22f9..fde0dbb 100644
--- a/tests/res/xml/shortcuts.xml
+++ b/tests/res/xml/shortcuts.xml
@@ -2,6 +2,7 @@
 <shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
     <shortcut
         android:shortcutId="shortcut1"
+        android:icon="@drawable/test_theme_icon"
         android:shortcutShortLabel="@string/shortcut1">
         <intent android:action="com.android.launcher3.intent.action.test_shortcut"/>
     </shortcut>
diff --git a/tests/src/com/android/launcher3/ui/workspace/ThemeIconsTest.java b/tests/src/com/android/launcher3/ui/workspace/ThemeIconsTest.java
index e66810c..93329fa 100644
--- a/tests/src/com/android/launcher3/ui/workspace/ThemeIconsTest.java
+++ b/tests/src/com/android/launcher3/ui/workspace/ThemeIconsTest.java
@@ -32,8 +32,11 @@
 import com.android.launcher3.icons.ThemedIconDrawable;
 import com.android.launcher3.tapl.HomeAllApps;
 import com.android.launcher3.tapl.HomeAppIcon;
+import com.android.launcher3.tapl.HomeAppIconMenuItem;
 import com.android.launcher3.ui.AbstractLauncherUiTest;
 import com.android.launcher3.ui.TaplTestsLauncher3;
+import com.android.launcher3.util.Executors;
+import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
 
 import org.junit.Test;
 
@@ -49,6 +52,8 @@
 public class ThemeIconsTest extends AbstractLauncherUiTest {
 
     private static final String APP_NAME = "ThemeIconTestActivity";
+    private static final String SHORTCUT_APP_NAME = "LauncherTestApp";
+    private static final String SHORTCUT_NAME = "Shortcut 1";
 
     @Test
     public void testIconWithoutTheme() throws Exception {
@@ -60,9 +65,28 @@
 
         try {
             HomeAppIcon icon = allApps.getAppIcon(APP_NAME);
-            executeOnLauncher(l -> verifyIconTheme(l.getAppsView(), false));
+            executeOnLauncher(l -> verifyIconTheme(APP_NAME, l.getAppsView(), false));
             icon.dragToWorkspace(false, false);
-            executeOnLauncher(l -> verifyIconTheme(l.getWorkspace(), false));
+            executeOnLauncher(l -> verifyIconTheme(APP_NAME, l.getWorkspace(), false));
+        } finally {
+            allApps.unfreeze();
+        }
+    }
+
+    @Test
+    public void testShortcutIconWithoutTheme() throws Exception {
+        setThemeEnabled(false);
+        TaplTestsLauncher3.initialize(this);
+
+        HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps();
+        allApps.freeze();
+
+        try {
+            HomeAppIcon icon = allApps.getAppIcon(SHORTCUT_APP_NAME);
+            HomeAppIconMenuItem shortcutItem =
+                    (HomeAppIconMenuItem) icon.openDeepShortcutMenu().getMenuItem(SHORTCUT_NAME);
+            shortcutItem.dragToWorkspace(false, false);
+            executeOnLauncher(l -> verifyIconTheme(SHORTCUT_NAME, l.getWorkspace(), false));
         } finally {
             allApps.unfreeze();
         }
@@ -78,15 +102,42 @@
 
         try {
             HomeAppIcon icon = allApps.getAppIcon(APP_NAME);
-            executeOnLauncher(l -> verifyIconTheme(l.getAppsView(), false));
+            executeOnLauncher(l -> verifyIconTheme(APP_NAME, l.getAppsView(), false));
             icon.dragToWorkspace(false, false);
-            executeOnLauncher(l -> verifyIconTheme(l.getWorkspace(), true));
+            executeOnLauncher(l -> verifyIconTheme(APP_NAME, l.getWorkspace(), true));
         } finally {
             allApps.unfreeze();
         }
     }
 
-    private void verifyIconTheme(ViewGroup parent, boolean isThemed) {
+    @Test
+    @ScreenRecord // b/260722220
+    public void testShortcutIconWithTheme() throws Exception {
+        setThemeEnabled(true);
+        TaplTestsLauncher3.initialize(this);
+
+        HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps();
+        allApps.freeze();
+
+        try {
+            HomeAppIcon icon = allApps.getAppIcon(SHORTCUT_APP_NAME);
+            HomeAppIconMenuItem shortcutItem =
+                    (HomeAppIconMenuItem) icon.openDeepShortcutMenu().getMenuItem(SHORTCUT_NAME);
+            shortcutItem.dragToWorkspace(false, false);
+            executeOnLauncher(l -> verifyIconTheme(SHORTCUT_NAME, l.getWorkspace(), true));
+        } finally {
+            allApps.unfreeze();
+        }
+    }
+
+    private void verifyIconTheme(String title, ViewGroup parent, boolean isThemed) {
+        // Wait for Launcher model to be completed
+        try {
+            Executors.MODEL_EXECUTOR.submit(() -> { }).get();
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+
         // Find the app icon
         Queue<View> viewQueue = new ArrayDeque<>();
         viewQueue.add(parent);
@@ -100,7 +151,7 @@
                 }
             } else if (view instanceof BubbleTextView) {
                 BubbleTextView btv = (BubbleTextView) view;
-                if (APP_NAME.equals(btv.getText())) {
+                if (title.equals(btv.getText())) {
                     icon = btv;
                     break;
                 }
diff --git a/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java b/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java
index ddeeac2..bddb593 100644
--- a/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java
+++ b/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java
@@ -20,6 +20,8 @@
 import androidx.test.uiautomator.By;
 import androidx.test.uiautomator.UiObject2;
 
+import java.util.ArrayList;
+
 /**
  * Operations on search result page opened from home screen qsb.
  */
@@ -27,6 +29,9 @@
     // The input resource id in the search box.
     private static final String INPUT_RES = "input";
     private static final String BOTTOM_SHEET_RES_ID = "bottom_sheet_background";
+
+    // This particular ID change should happen with caution
+    private static final String SEARCH_CONTAINER_RES_ID = "search_results_list_view";
     private final LauncherInstrumentation mLauncher;
 
     SearchResultFromQsb(LauncherInstrumentation launcher) {
@@ -49,6 +54,33 @@
         return new AllAppsAppIcon(mLauncher, icon);
     }
 
+    /** Find the web suggestion from search suggestion's title text */
+    public void findWebSuggest(String text) {
+        ArrayList<UiObject2> goldenGateResults =
+                new ArrayList<>(mLauncher.waitForObjectsInContainer(
+                        mLauncher.waitForSystemLauncherObject(SEARCH_CONTAINER_RES_ID),
+                        By.clazz(TextView.class)));
+        boolean found = false;
+        for(UiObject2 uiObject: goldenGateResults) {
+            String currentString = uiObject.getText();
+            if (currentString.equals(text)) {
+                found = true;
+            }
+        }
+        if (!found) {
+            throw new IllegalStateException("Web suggestion title: " + text + " not found");
+        }
+    }
+
+    /** Find the total amount of views being displayed and return the size */
+    public int getSearchResultItemSize() {
+        ArrayList<UiObject2> searchResultItems =
+                new ArrayList<>(mLauncher.waitForObjectsInContainer(
+                        mLauncher.waitForSystemLauncherObject(SEARCH_CONTAINER_RES_ID),
+                        By.clazz(TextView.class)));
+        return searchResultItems.size();
+    }
+
     /**
      * Taps outside bottom sheet to dismiss and return to workspace. Available on tablets only.
      * @param tapRight Tap on the right of bottom sheet if true, or left otherwise.