Merge "Prevent double selection of second split app" into tm-dev
diff --git a/Android.bp b/Android.bp
index b3027bc..a523a62 100644
--- a/Android.bp
+++ b/Android.bp
@@ -107,6 +107,7 @@
         "androidx.cardview_cardview",
         "com.google.android.material_material",
         "iconloader_base",
+        "modules-utils-build",
     ],
     manifest: "AndroidManifest-common.xml",
     sdk_version: "current",
diff --git a/go/quickstep/res/values-pt-rPT/strings.xml b/go/quickstep/res/values-pt-rPT/strings.xml
index 7041f41..e64f520 100644
--- a/go/quickstep/res/values-pt-rPT/strings.xml
+++ b/go/quickstep/res/values-pt-rPT/strings.xml
@@ -10,9 +10,9 @@
     <string name="dialog_settings" msgid="6564397136021186148">"DEFINIÇÕES"</string>
     <string name="niu_actions_confirmation_title" msgid="3863451714863526143">"Traduza ou ouça o texto no ecrã"</string>
     <string name="niu_actions_confirmation_text" msgid="2105271481950866089">"Informações como o texto no ecrã, endereços Web e capturas de ecrã podem ser partilhadas com a Google.\n\nPara alterar as informações que partilha, aceda a "<b>"Definições &gt; Apps &gt; App predefinidas &gt; App de assistente digital"</b>"."</string>
-    <string name="assistant_not_selected_title" msgid="5017072974603345228">"Escolha um assistente para utilizar esta funcionalidade"</string>
+    <string name="assistant_not_selected_title" msgid="5017072974603345228">"Escolha um assistente para usar esta funcionalidade"</string>
     <string name="assistant_not_selected_text" msgid="3244613673884359276">"Para ouvir ou traduzir o texto no ecrã, escolha uma app de assistente digital nas Definições"</string>
-    <string name="assistant_not_supported_title" msgid="1675788067597484142">"Mude de assistente para utilizar esta funcionalidade"</string>
+    <string name="assistant_not_supported_title" msgid="1675788067597484142">"Mude de assistente para usar esta funcionalidade"</string>
     <string name="assistant_not_supported_text" msgid="1708031078549268884">"Para ouvir ou traduzir o texto no ecrã, mude de app de assistente digital nas Definições"</string>
     <string name="tooltip_listen" msgid="7634466447860989102">"Toque aqui para ouvir o texto neste ecrã"</string>
     <string name="tooltip_translate" msgid="4184845868901542567">"Toque aqui para traduzir o texto neste ecrã"</string>
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
index 8e31a74..e02a9d7 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -19,6 +19,7 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
 
+import static com.android.launcher3.util.DisplayController.CHANGE_DENSITY;
 import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
 
 import android.content.ComponentCallbacks;
@@ -41,7 +42,6 @@
 import com.android.launcher3.statemanager.StatefulActivity;
 import com.android.launcher3.taskbar.unfold.NonDestroyableScopedUnfoldTransitionProgressProvider;
 import com.android.launcher3.util.DisplayController;
-import com.android.launcher3.util.DisplayController.Info;
 import com.android.launcher3.util.SettingsCache;
 import com.android.launcher3.util.SimpleBroadcastReceiver;
 import com.android.quickstep.RecentsActivity;
@@ -55,7 +55,7 @@
 /**
  * Class to manage taskbar lifecycle
  */
-public class TaskbarManager implements DisplayController.DisplayInfoChangeListener {
+public class TaskbarManager {
 
     private static final Uri USER_SETUP_COMPLETE_URI = Settings.Secure.getUriFor(
             Settings.Secure.USER_SETUP_COMPLETE);
@@ -91,8 +91,15 @@
      * navigation mode, that callback gets called too soon, before it's internal navigation mode
      * reflects the current one.
      * DisplayController's callback is delayed enough to get the correct nav mode value
+     *
+     * We also use density change here because DeviceProfile has had a chance to update it's state
+     * whereas density for component callbacks registered in this class don't update DeviceProfile.
+     * Confused? Me too. Make it less confusing (TODO: b/227669780)
+     *
+     * Flags used with {@link #mDispInfoChangeListener}
      */
-    private static final int CHANGE_FLAGS = CHANGE_NAVIGATION_MODE;
+    private static final int CHANGE_FLAGS = CHANGE_NAVIGATION_MODE | CHANGE_DENSITY;
+    private final DisplayController.DisplayInfoChangeListener mDispInfoChangeListener;
 
     private boolean mUserUnlocked = false;
 
@@ -105,6 +112,7 @@
                 SystemUiProxy.INSTANCE.get(mContext), new Handler());
         mUserSetupCompleteListener = isUserSetupComplete -> recreateTaskbar();
         mNavBarKidsModeListener = isNavBarKidsMode -> recreateTaskbar();
+        // TODO(b/227669780): Consolidate this w/ DisplayController callbacks
         mComponentCallbacks = new ComponentCallbacks() {
             private Configuration mOldConfig = mContext.getResources().getConfiguration();
 
@@ -116,7 +124,7 @@
                 int configDiff = mOldConfig.diff(newConfig);
                 int configsRequiringRecreate = ActivityInfo.CONFIG_ASSETS_PATHS
                         | ActivityInfo.CONFIG_LAYOUT_DIRECTION | ActivityInfo.CONFIG_UI_MODE
-                        | ActivityInfo.CONFIG_DENSITY | ActivityInfo.CONFIG_SCREEN_SIZE;
+                        | ActivityInfo.CONFIG_SCREEN_SIZE;
                 boolean requiresRecreate = (configDiff & configsRequiringRecreate) != 0;
                 if ((configDiff & ActivityInfo.CONFIG_SCREEN_SIZE) != 0
                         && mTaskbarActivityContext != null && dp != null) {
@@ -151,8 +159,12 @@
             public void onLowMemory() { }
         };
         mShutdownReceiver = new SimpleBroadcastReceiver(i -> destroyExistingTaskbar());
-
-        mDisplayController.addChangeListener(this);
+        mDispInfoChangeListener = (context, info, flags) -> {
+            if ((flags & CHANGE_FLAGS) != 0) {
+                recreateTaskbar();
+            }
+        };
+        mDisplayController.addChangeListener(mDispInfoChangeListener);
         SettingsCache.INSTANCE.get(mContext).register(USER_SETUP_COMPLETE_URI,
                 mUserSetupCompleteListener);
         SettingsCache.INSTANCE.get(mContext).register(NAV_BAR_KIDS_MODE,
@@ -163,13 +175,6 @@
         recreateTaskbar();
     }
 
-    @Override
-    public void onDisplayInfoChanged(Context context, Info info, int flags) {
-        if ((flags & CHANGE_FLAGS) != 0) {
-            recreateTaskbar();
-        }
-    }
-
     private void destroyExistingTaskbar() {
         if (mTaskbarActivityContext != null) {
             mTaskbarActivityContext.onDestroy();
@@ -310,7 +315,7 @@
      */
     public void destroy() {
         destroyExistingTaskbar();
-        mDisplayController.removeChangeListener(this);
+        mDisplayController.removeChangeListener(mDispInfoChangeListener);
         SettingsCache.INSTANCE.get(mContext).unregister(USER_SETUP_COMPLETE_URI,
                 mUserSetupCompleteListener);
         SettingsCache.INSTANCE.get(mContext).unregister(NAV_BAR_KIDS_MODE,
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
index 86c42ca..4d3b057 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
@@ -21,7 +21,7 @@
 import static com.android.launcher3.LauncherAnimUtils.newCancelListener;
 import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
+import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PULL_BACK_PROGRESS;
 import static com.android.launcher3.anim.AnimatorListeners.forSuccessCallback;
 import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_ALL_APPS_EDU;
@@ -147,7 +147,7 @@
             AbstractFloatingView.closeOpenContainer(mLauncher, AbstractFloatingView.TYPE_TASK_MENU);
         } else if (mStartState == ALL_APPS) {
             AllAppsTransitionController allAppsController = mLauncher.getAllAppsController();
-            builder.setFloat(allAppsController, ALL_APPS_PROGRESS,
+            builder.setFloat(allAppsController, ALL_APPS_PULL_BACK_PROGRESS,
                     -mPullbackDistance / allAppsController.getShiftRange(), PULLBACK_INTERPOLATOR);
 
             // Slightly fade out all apps content to further distinguish from scrolling.
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index 5d77a6e..d11d50b 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -54,7 +54,7 @@
  * Singleton class to load and manage recents model.
  */
 @TargetApi(Build.VERSION_CODES.O)
-public class RecentsModel extends TaskStackChangeListener implements IconChangeListener {
+public class RecentsModel implements IconChangeListener, TaskStackChangeListener {
 
     // We do not need any synchronization for this variable as its only written on UI thread.
     public static final MainThreadInitializedObject<RecentsModel> INSTANCE =
diff --git a/quickstep/src/com/android/quickstep/RotationTouchHelper.java b/quickstep/src/com/android/quickstep/RotationTouchHelper.java
index f952e0d..f1e20db 100644
--- a/quickstep/src/com/android/quickstep/RotationTouchHelper.java
+++ b/quickstep/src/com/android/quickstep/RotationTouchHelper.java
@@ -74,7 +74,6 @@
 
         @Override
         public void onActivityRotation(int displayId) {
-            super.onActivityRotation(displayId);
             // This always gets called before onDisplayInfoChanged() so we know how to process
             // the rotation in that method. This is done to avoid having a race condition between
             // the sensor readings and onDisplayInfoChanged() call
diff --git a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
index 70fde1d..b222f51 100644
--- a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
+++ b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
@@ -56,6 +56,7 @@
     private final int mTaskId;
     private final ComponentName mComponentName;
     private final SurfaceControl mLeash;
+    private final Rect mSourceRectHint = new Rect();
     private final Rect mAppBounds = new Rect();
     private final Matrix mHomeToWindowPositionMap = new Matrix();
     private final Rect mStartBounds = new Rect();
@@ -140,6 +141,7 @@
         }
 
         if (sourceRectHint == null) {
+            mSourceRectHint.setEmpty();
             mSourceHintRectInsets = null;
 
             // Create a new overlay layer
@@ -169,6 +171,7 @@
                 t.apply();
             });
         } else {
+            mSourceRectHint.set(sourceRectHint);
             mSourceHintRectInsets = new Rect(sourceRectHint.left - appBounds.left,
                     sourceRectHint.top - appBounds.top,
                     appBounds.right - sourceRectHint.right,
@@ -249,7 +252,8 @@
             return mSurfaceTransactionHelper.scaleAndRotate(tx, mLeash, mAppBounds, bounds, insets,
                     rotatedPosition.degree, rotatedPosition.positionX, rotatedPosition.positionY);
         } else {
-            return mSurfaceTransactionHelper.scaleAndCrop(tx, mLeash, mAppBounds, bounds, insets);
+            return mSurfaceTransactionHelper.scaleAndCrop(tx, mLeash, mSourceRectHint, mAppBounds,
+                    bounds, insets);
         }
     }
 
diff --git a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
index 6b15807..99a2d6f 100644
--- a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
+++ b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
@@ -155,7 +155,7 @@
     public void setInsets(Rect insets) {
         mInsets.set(insets);
         updateVerticalMargin(DisplayController.getNavigationMode(getContext()));
-        updatePaddingAndTranslations();
+        updatePadding();
     }
 
     public void updateHiddenFlags(@ActionsHiddenFlags int visibilityFlags, boolean enable) {
@@ -199,10 +199,9 @@
     }
 
     /**
-     * Aligns OverviewActionsView vertically with and offsets horizontal position based on
-     * 3 button nav container in taskbar.
+     * Offsets OverviewActionsView horizontal position based on 3 button nav container in taskbar.
      */
-    private void updatePaddingAndTranslations() {
+    private void updatePadding() {
         boolean alignFor3ButtonTaskbar = mDp.isTaskbarPresent &&
                 DisplayController.getNavigationMode(getContext()) == THREE_BUTTONS;
         if (alignFor3ButtonTaskbar) {
@@ -213,20 +212,8 @@
             } else {
                 setPadding(mInsets.left, 0, mInsets.right + additionalPadding, 0);
             }
-
-            // Align vertically, using taskbar height + mDp.taskbarOffsetY() to estimate where
-            // the button nav top is.
-            int marginBottom = getOverviewActionsBottomMarginPx(
-                    DisplayController.getNavigationMode(getContext()), mDp);
-            int actionsTop =
-                    (mDp.heightPx - marginBottom - mInsets.bottom) - mDp.overviewActionsHeight;
-            int navTop = mDp.heightPx - (mDp.taskbarSize + mDp.getTaskbarOffsetY());
-            int transY = navTop - actionsTop + ((mDp.taskbarSize - mDp.overviewActionsHeight) / 2);
-            setTranslationY(transY);
         } else {
             setPadding(mInsets.left, 0, mInsets.right, 0);
-            setTranslationX(0);
-            setTranslationY(0);
         }
     }
 
@@ -287,19 +274,28 @@
 
     /** Get the bottom margin associated with the action buttons in Overview. */
     public static int getOverviewActionsBottomMarginPx(NavigationMode mode, DeviceProfile dp) {
-        int inset = dp.getInsets().bottom;
+        int bottomInset = dp.getInsets().bottom;
 
         if (dp.isVerticalBarLayout()) {
-            return inset;
+            return bottomInset;
         }
 
-        // Actions button will be aligned with nav buttons in updatePaddingAndTranslations().
         if (mode == NavigationMode.THREE_BUTTONS) {
-            return dp.overviewActionsMarginThreeButtonPx + inset;
+            int bottomMargin = dp.overviewActionsMarginThreeButtonPx + bottomInset;
+            if (dp.isTaskbarPresent) {
+                // Align vertically, using taskbar height + mDp.taskbarOffsetY() to estimate where
+                // the button nav top is.
+                int actionsTop = (dp.heightPx - bottomMargin - bottomInset)
+                        - dp.overviewActionsHeight;
+                int navTop = dp.heightPx - (dp.taskbarSize + dp.getTaskbarOffsetY());
+                bottomMargin -=
+                        navTop - actionsTop + ((dp.taskbarSize - dp.overviewActionsHeight) / 2);
+            }
+            return bottomMargin;
         }
 
         // There is no bottom inset when taskbar is present, use stashed taskbar as padding instead.
         return dp.overviewActionsBottomMarginGesturePx
-                + (dp.isTaskbarPresent ? dp.stashedTaskbarSize : inset);
+                + (dp.isTaskbarPresent ? dp.stashedTaskbarSize : bottomInset);
     }
 }
diff --git a/res/values-sw600dp/dimens.xml b/res/values-sw600dp/dimens.xml
index 602dd6d..eb347f2 100644
--- a/res/values-sw600dp/dimens.xml
+++ b/res/values-sw600dp/dimens.xml
@@ -38,10 +38,10 @@
 
 <!-- Hotseat -->
     <dimen name="dynamic_grid_hotseat_side_padding">0dp</dimen>
-    <dimen name="spring_loaded_hotseat_top_margin">97dp</dimen>
+    <dimen name="spring_loaded_hotseat_top_margin">65dp</dimen>
 
 <!-- Dragging -->
-    <dimen name="drop_target_top_margin">34dp</dimen>
+    <dimen name="drop_target_top_margin">64dp</dimen>
     <dimen name="drop_target_bottom_margin">16dp</dimen>
     <dimen name="drop_target_button_drawable_horizontal_padding">16dp</dimen>
     <dimen name="drop_target_button_drawable_vertical_padding">16dp</dimen>
diff --git a/res/values-sw720dp/dimens.xml b/res/values-sw720dp/dimens.xml
index 3ec211a..fad8c95 100644
--- a/res/values-sw720dp/dimens.xml
+++ b/res/values-sw720dp/dimens.xml
@@ -28,11 +28,11 @@
     <dimen name="drop_target_button_drawable_horizontal_padding">24dp</dimen>
     <dimen name="drop_target_button_drawable_vertical_padding">20dp</dimen>
     <dimen name="drop_target_button_gap">32dp</dimen>
-    <dimen name="drop_target_top_margin">32dp</dimen>
-    <dimen name="drop_target_bottom_margin">32dp</dimen>
+    <dimen name="drop_target_top_margin">110dp</dimen>
+    <dimen name="drop_target_bottom_margin">48dp</dimen>
 
 <!-- Hotseat -->
-    <dimen name="spring_loaded_hotseat_top_margin">164dp</dimen>
+    <dimen name="spring_loaded_hotseat_top_margin">108dp</dimen>
 
 <!-- Widget picker-->
     <dimen name="widget_list_horizontal_margin">30dp</dimen>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index ae4c310..97d02d0 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -52,7 +52,7 @@
     <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"İş"</string>
     <string name="widget_category_conversations" msgid="8894438636213590446">"Görüşmeler"</string>
     <string name="widget_education_header" msgid="4874760613775913787">"Faydalı bilgiler parmaklarınızın ucunda"</string>
-    <string name="widget_education_content" msgid="1731667670753497052">"Uygulamaları açmadan bilgi almak için ana ekranınıza widget\'lar ekleyebilirsiniz"</string>
+    <string name="widget_education_content" msgid="1731667670753497052">"Uygulama açmadan bilgi almak için ana ekranınıza widget ekleyebilirsiniz"</string>
     <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Widget ayarlarını değiştirmek için dokunun"</string>
     <string name="widget_education_close_button" msgid="8676165703104836580">"Anladım"</string>
     <string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"Widget ayarlarını değiştir"</string>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index f699fca..ffa1e3f 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -303,6 +303,17 @@
     <!-- Title for an app whose download has been started. -->
     <string name="app_waiting_download_title"><xliff:g id="name" example="Messenger">%1$s</xliff:g> waiting to install</string>
 
+
+    <!-- Title shown on the alert dialog prompting the user to update the application in market
+     in order to re-enable the disabled shortcuts -->
+    <string name="dialog_update_title">App update required</string>
+    <!-- Message shown on the alert dialog prompting the user to update the application -->
+    <string name="dialog_update_message">The app for this icon isn\'t updated. You can update manually to re-enable this shortcut, or remove the icon.</string>
+    <!-- Message for the update button in the alert dialog, will bring the user to market -->
+    <string name="dialog_update">Update</string>
+    <!-- Message for the remove button in the alert dialog -->
+    <string name="dialog_remove">Remove</string>
+
     <!-- Strings for widgets & more in the popup container/bottom sheet -->
 
     <!-- Accessibility title for the popup containing a list of widgets. [CHAR_LIMIT=50] -->
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 9241c45..6d39857 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -74,7 +74,6 @@
 
 import androidx.annotation.NonNull;
 import androidx.core.graphics.ColorUtils;
-import androidx.core.os.BuildCompat;
 
 import com.android.launcher3.dragndrop.FolderAdaptiveIcon;
 import com.android.launcher3.graphics.GridCustomizationsProvider;
@@ -94,6 +93,7 @@
 import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.views.BaseDragLayer;
 import com.android.launcher3.widget.PendingAddShortcutInfo;
+import com.android.modules.utils.build.SdkLevel;
 
 import java.lang.reflect.Method;
 import java.util.ArrayList;
@@ -126,11 +126,11 @@
 
     public static final boolean ATLEAST_Q = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q;
 
-    public static final boolean ATLEAST_R = Build.VERSION.SDK_INT >= Build.VERSION_CODES.R;
+    public static final boolean ATLEAST_R = SdkLevel.isAtLeastR();
 
-    public static final boolean ATLEAST_S = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S;
+    public static final boolean ATLEAST_S = SdkLevel.isAtLeastS();
 
-    public static final boolean ATLEAST_T = BuildCompat.isAtLeastT();
+    public static final boolean ATLEAST_T = SdkLevel.isAtLeastT();
 
     /**
      * Set on a motion event dispatched from the nav bar. See {@link MotionEvent#setEdgeFlags(int)}.
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 86e88b7..ed01660 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -50,7 +50,6 @@
 import android.os.Handler;
 import android.os.Message;
 import android.os.Parcelable;
-import android.os.UserHandle;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.SparseArray;
@@ -123,7 +122,6 @@
 import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlay;
 
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 import java.util.function.Consumer;
@@ -3297,9 +3295,12 @@
         }
     }
 
-    public void removeAbandonedPromise(String packageName, UserHandle user) {
-        ItemInfoMatcher matcher = ItemInfoMatcher.ofPackages(
-                Collections.singleton(packageName), user);
+    /**
+     * Remove workspace icons & widget information related to items in matcher.
+     *
+     * @param matcher  the matcher generated by the caller.
+     */
+    public void persistRemoveItemsByMatcher(ItemInfoMatcher matcher) {
         mLauncher.getModelWriter().deleteItemsFromDatabase(matcher);
         removeItemsByMatcher(matcher);
     }
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index cdc313f..8662d00 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -44,6 +44,7 @@
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.statemanager.StateManager.StateHandler;
 import com.android.launcher3.states.StateAnimationConfig;
+import com.android.launcher3.util.UiThreadHelper;
 import com.android.launcher3.views.ScrimView;
 
 /**
@@ -75,6 +76,21 @@
                 }
             };
 
+    public static final FloatProperty<AllAppsTransitionController> ALL_APPS_PULL_BACK_PROGRESS =
+            new FloatProperty<AllAppsTransitionController>("allAppsPullBackProgress") {
+
+                @Override
+                public Float get(AllAppsTransitionController controller) {
+                    return controller.mPullBackProgress;
+                }
+
+                @Override
+                public void setValue(AllAppsTransitionController controller, float progress) {
+                    controller.setPullBackProgress(progress);
+                }
+            };
+
+
     private ActivityAllAppsContainerView<Launcher> mAppsView;
 
     private final Launcher mLauncher;
@@ -88,15 +104,17 @@
     // When {@link mProgress} is 1, all apps container is pulled down.
     private float mShiftRange;      // changes depending on the orientation
     private float mProgress;        // [0, 1], mShiftRange * mProgress = shiftCurrent
+    private float mPullBackProgress;  // [0, 1], mShiftRange * mPullBackProgress = shiftCurrent
 
     private ScrimView mScrimView;
+    private View mPullBackView;
 
     public AllAppsTransitionController(Launcher l) {
         mLauncher = l;
         DeviceProfile dp = mLauncher.getDeviceProfile();
         setShiftRange(dp.allAppsShiftRange);
         mProgress = 1f;
-
+        mPullBackProgress = 1f;
         mIsVerticalLayout = dp.isVerticalBarLayout();
         mLauncher.addOnDeviceProfileChangeListener(this);
     }
@@ -114,6 +132,8 @@
             mLauncher.getHotseat().setTranslationY(0);
             mLauncher.getWorkspace().getPageIndicator().setTranslationY(0);
         }
+
+        mPullBackView = dp.isTablet ? mAppsView.getRecyclerViewContainer() : mAppsView;
     }
 
     /**
@@ -133,12 +153,19 @@
         return mProgress;
     }
 
+    private void setPullBackProgress(float progress) {
+        mPullBackProgress = progress;
+        mPullBackView.setTranslationY(mPullBackProgress * mShiftRange);
+    }
+
     /**
      * Sets the vertical transition progress to {@param state} and updates all the dependent UI
      * accordingly.
      */
     @Override
     public void setState(LauncherState state) {
+        // Always reset pull back progress when switching states.
+        setPullBackProgress(0f);
         setProgress(state.getVerticalProgress(mLauncher));
         setAlphas(state, new StateAnimationConfig(), NO_ANIM_PROPERTY_SETTER);
         onProgressAnimationEnd();
@@ -151,6 +178,12 @@
     @Override
     public void setStateWithAnimation(LauncherState toState,
             StateAnimationConfig config, PendingAnimation builder) {
+        if (NORMAL.equals(toState) && mLauncher.isInState(ALL_APPS)) {
+            UiThreadHelper.hideKeyboardAsync(mLauncher, mLauncher.getAppsView().getWindowToken());
+        }
+
+        // Always reset pull back progress when switching states.
+        setPullBackProgress(0f);
         float targetProgress = toState.getVerticalProgress(mLauncher);
         if (Float.compare(mProgress, targetProgress) == 0) {
             setAlphas(toState, config, builder);
@@ -212,6 +245,8 @@
                             | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
         }
         mAppsView.setScrimView(scrimView);
+        mPullBackView = mLauncher.getDeviceProfile().isTablet
+                ? mAppsView.getRecyclerViewContainer() : mAppsView;
     }
 
     /**
diff --git a/src/com/android/launcher3/model/data/ItemInfoWithIcon.java b/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
index 19345d7..76a0c4d 100644
--- a/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
+++ b/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
@@ -71,10 +71,6 @@
      */
     public static final int FLAG_DISABLED_LOCKED_USER = 1 << 5;
 
-    public static final int FLAG_DISABLED_MASK = FLAG_DISABLED_SAFEMODE
-            | FLAG_DISABLED_NOT_AVAILABLE | FLAG_DISABLED_SUSPENDED
-            | FLAG_DISABLED_QUIET_USER | FLAG_DISABLED_BY_PUBLISHER | FLAG_DISABLED_LOCKED_USER;
-
     /**
      * The item points to a system app.
      */
@@ -114,6 +110,16 @@
             | FLAG_INCREMENTAL_DOWNLOAD_ACTIVE;
 
     /**
+     * Indicates that the icon is a disabled shortcut and application updates are required.
+     */
+    public static final int FLAG_DISABLED_VERSION_LOWER = 1 << 12;
+
+    public static final int FLAG_DISABLED_MASK = FLAG_DISABLED_SAFEMODE
+            | FLAG_DISABLED_NOT_AVAILABLE | FLAG_DISABLED_SUSPENDED
+            | FLAG_DISABLED_QUIET_USER | FLAG_DISABLED_BY_PUBLISHER | FLAG_DISABLED_LOCKED_USER
+            | FLAG_DISABLED_VERSION_LOWER;
+
+    /**
      * Status associated with the system state of the underlying item. This is calculated every
      * time a new info is created and not persisted on the disk.
      */
diff --git a/src/com/android/launcher3/model/data/WorkspaceItemInfo.java b/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
index a195979..2b3da33 100644
--- a/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
+++ b/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
@@ -180,12 +180,25 @@
             runtimeStatusFlags |= FLAG_DISABLED_BY_PUBLISHER;
         }
         disabledMessage = shortcutInfo.getDisabledMessage();
+        if (Utilities.ATLEAST_P
+                && shortcutInfo.getDisabledReason() == ShortcutInfo.DISABLED_REASON_VERSION_LOWER) {
+            runtimeStatusFlags |= FLAG_DISABLED_VERSION_LOWER;
+        } else {
+            runtimeStatusFlags &= ~FLAG_DISABLED_VERSION_LOWER;
+        }
 
         Person[] persons = ApiWrapper.getPersons(shortcutInfo);
         personKeys = persons.length == 0 ? Utilities.EMPTY_STRING_ARRAY
             : Arrays.stream(persons).map(Person::getKey).sorted().toArray(String[]::new);
     }
 
+    /**
+     * {@code true} if the shortcut is disabled due to its app being a lower version.
+     */
+    public boolean isDisabledVersionLower() {
+        return (runtimeStatusFlags & FLAG_DISABLED_VERSION_LOWER) != 0;
+    }
+
     /** Returns the WorkspaceItemInfo id associated with the deep shortcut. */
     public String getDeepShortcutId() {
         return itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT
diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java
index 8d57d69..cd00f15 100644
--- a/src/com/android/launcher3/touch/ItemClickHandler.java
+++ b/src/com/android/launcher3/touch/ItemClickHandler.java
@@ -43,6 +43,7 @@
 
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.folder.Folder;
@@ -58,8 +59,10 @@
 import com.android.launcher3.model.data.SearchActionItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.pm.InstallSessionHelper;
+import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.views.FloatingIconView;
 import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
@@ -67,6 +70,8 @@
 import com.android.launcher3.widget.WidgetAddFlowHandler;
 import com.android.launcher3.widget.WidgetManagerHelper;
 
+import java.util.Collections;
+
 /**
  * Class for handling clicks on workspace and all-apps items
  */
@@ -171,7 +176,8 @@
                         (d, i) -> startMarketIntentForPackage(v, launcher, packageName))
                 .setNeutralButton(R.string.abandoned_clean_this,
                         (d, i) -> launcher.getWorkspace()
-                                .removeAbandonedPromise(packageName, user))
+                                .persistRemoveItemsByMatcher(ItemInfoMatcher.ofPackages(
+                                        Collections.singleton(packageName), user)))
                 .create().show();
     }
 
@@ -205,6 +211,12 @@
     public static boolean handleDisabledItemClicked(WorkspaceItemInfo shortcut, Context context) {
         final int disabledFlags = shortcut.runtimeStatusFlags
                 & WorkspaceItemInfo.FLAG_DISABLED_MASK;
+        // Handle the case where the disabled reason is DISABLED_REASON_VERSION_LOWER.
+        // Show an AlertDialog for the user to choose either updating the app or cancel the launch.
+        if (maybeCreateAlertDialogForShortcut(shortcut, context)) {
+            return true;
+        }
+
         if ((disabledFlags
                 & ~FLAG_DISABLED_SUSPENDED
                 & ~FLAG_DISABLED_QUIET_USER) == 0) {
@@ -230,6 +242,37 @@
         }
     }
 
+    private static boolean maybeCreateAlertDialogForShortcut(final WorkspaceItemInfo shortcut,
+            Context context) {
+        try {
+            final Launcher launcher = Launcher.getLauncher(context);
+            if (shortcut.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
+                    && shortcut.isDisabledVersionLower()) {
+
+                new AlertDialog.Builder(context)
+                        .setTitle(R.string.dialog_update_title)
+                        .setMessage(R.string.dialog_update_message)
+                        .setPositiveButton(R.string.dialog_update, (d, i) -> {
+                            // Direct the user to the play store to update the app
+                            context.startActivity(shortcut.getMarketIntent(context));
+                        })
+                        .setNeutralButton(R.string.dialog_remove, (d, i) -> {
+                            // Remove the icon if launcher is successfully initialized
+                            launcher.getWorkspace().persistRemoveItemsByMatcher(ItemInfoMatcher
+                                    .ofShortcutKeys(Collections.singleton(ShortcutKey
+                                            .fromItemInfo(shortcut))));
+                        })
+                        .create()
+                        .show();
+                return true;
+            }
+        } catch (Exception e) {
+            Log.e(TAG, "Error creating alert dialog", e);
+        }
+
+        return false;
+    }
+
     /**
      * Event handler for an app shortcut click.
      *