Merge "Skip passing gesture to bubble when notif expanded" into tm-dev
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
index 7b01670..3b1f119 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -58,17 +58,21 @@
 
     @Override
     public ScaleAndTranslation getWorkspaceScaleAndTranslation(Launcher launcher) {
-        ScaleAndTranslation scaleAndTranslation =
-                new ScaleAndTranslation(NO_SCALE, NO_OFFSET, NO_OFFSET);
+        return new ScaleAndTranslation(0.97f, NO_OFFSET, NO_OFFSET);
+    }
+
+    @Override
+    public ScaleAndTranslation getHotseatScaleAndTranslation(Launcher launcher) {
         if (launcher.getDeviceProfile().isTablet) {
-            scaleAndTranslation.scale = 0.97f;
+            return getWorkspaceScaleAndTranslation(launcher);
         } else {
             ScaleAndTranslation overviewScaleAndTranslation = LauncherState.OVERVIEW
                     .getWorkspaceScaleAndTranslation(launcher);
-            scaleAndTranslation.translationX = overviewScaleAndTranslation.translationX;
-            scaleAndTranslation.translationY = overviewScaleAndTranslation.translationY;
+            return new ScaleAndTranslation(
+                    NO_SCALE,
+                    overviewScaleAndTranslation.translationX,
+                    overviewScaleAndTranslation.translationY);
         }
-        return scaleAndTranslation;
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
index b5b03a1..f2162b0 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
@@ -192,7 +192,10 @@
                     1 - ALL_APPS_SCRIM_VISIBLE_THRESHOLD));
             config.setInterpolator(ANIM_VERTICAL_PROGRESS, EMPHASIZED_ACCELERATE);
         } else if (fromState == NORMAL && toState == ALL_APPS) {
-            config.setInterpolator(ANIM_VERTICAL_PROGRESS, EMPHASIZED_DECELERATE);
+            if (mActivity.getDeviceProfile().isTablet) {
+                config.setInterpolator(ANIM_VERTICAL_PROGRESS, EMPHASIZED_DECELERATE);
+            }
+            // TODO(b/231682175): centralize this setup in AllAppsSwipeController
         }
     }
 }
diff --git a/quickstep/src/com/android/quickstep/TaskIconCache.java b/quickstep/src/com/android/quickstep/TaskIconCache.java
index 300f085..3803f03 100644
--- a/quickstep/src/com/android/quickstep/TaskIconCache.java
+++ b/quickstep/src/com/android/quickstep/TaskIconCache.java
@@ -256,7 +256,7 @@
     private BaseIconFactory getIconFactory() {
         if (mIconFactory == null) {
             mIconFactory = new BaseIconFactory(mContext,
-                    DisplayController.INSTANCE.get(mContext).getInfo().densityDpi,
+                    DisplayController.INSTANCE.get(mContext).getInfo().getDensityDpi(),
                     mContext.getResources().getDimensionPixelSize(R.dimen.taskbar_icon_size));
         }
         return mIconFactory;
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index dd3e08b..13f20c2 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -159,6 +159,17 @@
          defaults to 2 * numHotseatIcons -->
         <attr name="numExtendedHotseatIcons" format="integer" />
 
+        <!-- alignment of hotseat to the grid.
+        Not applicable for 3 button mode when taskbar is enabled -->
+        <!-- defaults to numColumns, if not specified -->
+        <attr name="hotseatColumnSpan" format="integer" />
+        <!-- defaults to numColumns, if not specified -->
+        <attr name="hotseatColumnSpanLandscape" format="integer" />
+        <!-- defaults to numColumns, if not specified -->
+        <attr name="hotseatColumnSpanTwoPanelLandscape" format="integer" />
+        <!-- defaults to numColumns, if not specified -->
+        <attr name="hotseatColumnSpanTwoPanelPortrait" format="integer" />
+
         <attr name="dbFile" format="string" />
         <attr name="defaultLayoutId" format="reference" />
         <attr name="defaultSplitDisplayLayoutId" format="reference" />
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index a8ee721..098c694 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -100,7 +100,7 @@
     <dimen name="fastscroll_end_margin">-26dp</dimen>
 
     <!-- All Apps -->
-    <dimen name="all_apps_starting_vertical_translate">320dp</dimen>
+    <dimen name="all_apps_starting_vertical_translate">300dp</dimen>
     <dimen name="all_apps_search_bar_field_height">48dp</dimen>
     <!-- all_apps_search_bar_field_height / 2 -->
     <dimen name="all_apps_search_bar_content_overlap">24dp</dimen>
diff --git a/res/xml/device_profiles.xml b/res/xml/device_profiles.xml
index 07ce598..290bc8c 100644
--- a/res/xml/device_profiles.xml
+++ b/res/xml/device_profiles.xml
@@ -144,6 +144,7 @@
         launcher:numFolderRows="3"
         launcher:numFolderColumns="3"
         launcher:numHotseatIcons="6"
+        launcher:hotseatColumnSpanLandscape="4"
         launcher:numAllAppsColumns="6"
         launcher:isScalable="true"
         launcher:devicePaddingId="@xml/paddings_6x5"
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 87bbac6..76e945d 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -65,6 +65,7 @@
 import com.android.launcher3.dragndrop.DraggableView;
 import com.android.launcher3.folder.PreviewBackground;
 import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.LauncherAppWidgetInfo;
 import com.android.launcher3.util.CellAndSpan;
 import com.android.launcher3.util.GridOccupancy;
 import com.android.launcher3.util.ParcelableSparseArray;
@@ -290,7 +291,7 @@
 
         for (int i = 0; i < mDragOutlineAnims.length; i++) {
             final InterruptibleInOutAnimator anim =
-                new InterruptibleInOutAnimator(duration, fromAlphaValue, toAlphaValue);
+                    new InterruptibleInOutAnimator(duration, fromAlphaValue, toAlphaValue);
             anim.getAnimator().setInterpolator(mEaseOutInterpolator);
             final int thisIndex = i;
             anim.getAnimator().addUpdateListener(new AnimatorUpdateListener() {
@@ -2440,7 +2441,7 @@
 
         // First we determine if things have moved enough to cause a different layout
         ItemConfiguration swapSolution = findReorderSolution(pixelXY[0], pixelXY[1], spanX, spanY,
-                 spanX,  spanY, direction, dragView,  true,  new ItemConfiguration());
+                spanX,  spanY, direction, dragView,  true,  new ItemConfiguration());
 
         setUseTempCoords(true);
         if (swapSolution != null && swapSolution.isSolution) {
@@ -2477,7 +2478,7 @@
         // direction vector, since we want the solution to match the preview, and it's possible
         // that the exact position of the item has changed to result in a new reordering outcome.
         if ((mode == MODE_ON_DROP || mode == MODE_ON_DROP_EXTERNAL || mode == MODE_ACCEPT_DROP)
-               && mPreviousReorderDirection[0] != INVALID_DIRECTION) {
+                && mPreviousReorderDirection[0] != INVALID_DIRECTION) {
             mDirectionVector[0] = mPreviousReorderDirection[0];
             mDirectionVector[1] = mPreviousReorderDirection[1];
             // We reset this vector after drop
@@ -2493,7 +2494,7 @@
 
         // Find a solution involving pushing / displacing any items in the way
         ItemConfiguration swapSolution = findReorderSolution(pixelX, pixelY, minSpanX, minSpanY,
-                 spanX,  spanY, mDirectionVector, dragView,  true,  new ItemConfiguration());
+                spanX,  spanY, mDirectionVector, dragView,  true,  new ItemConfiguration());
 
         // We attempt the approach which doesn't shuffle views at all
         ItemConfiguration noShuffleSolution = findConfigurationNoShuffle(pixelX, pixelY, minSpanX,
@@ -2733,12 +2734,24 @@
     }
 
     public void markCellsAsOccupiedForView(View view) {
+        if (view instanceof LauncherAppWidgetHostView
+                && view.getTag() instanceof LauncherAppWidgetInfo) {
+            LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) view.getTag();
+            mOccupied.markCells(info.cellX, info.cellY, info.spanX, info.spanY, true);
+            return;
+        }
         if (view == null || view.getParent() != mShortcutsAndWidgets) return;
         LayoutParams lp = (LayoutParams) view.getLayoutParams();
         mOccupied.markCells(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, true);
     }
 
     public void markCellsAsUnoccupiedForView(View view) {
+        if (view instanceof LauncherAppWidgetHostView
+                && view.getTag() instanceof LauncherAppWidgetInfo) {
+            LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) view.getTag();
+            mOccupied.markCells(info.cellX, info.cellY, info.spanX, info.spanY, false);
+            return;
+        }
         if (view == null || view.getParent() != mShortcutsAndWidgets) return;
         LayoutParams lp = (LayoutParams) view.getLayoutParams();
         mOccupied.markCells(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, false);
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 31f1da8..88030ae 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -158,6 +158,7 @@
     public final int numShownHotseatIcons;
     public int hotseatCellHeightPx;
     private final int hotseatExtraVerticalSize;
+    private final boolean areNavButtonsInline;
     // In portrait: size = height, in landscape: size = width
     public int hotseatBarSizePx;
     public int hotseatBarTopPaddingPx;
@@ -358,7 +359,7 @@
 
         // We shrink hotseat sizes regardless of orientation, if nav buttons are inline and QSB
         // might be inline in either orientations, to keep hotseat size consistent across rotation.
-        boolean areNavButtonsInline = isTaskbarPresent && !isGestureMode;
+        areNavButtonsInline = isTaskbarPresent && !isGestureMode;
         if (areNavButtonsInline && canQsbInline) {
             numShownHotseatIcons = inv.numShrunkenHotseatIcons;
         } else {
@@ -373,15 +374,14 @@
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_top_padding);
         if (isQsbInline) {
             hotseatBarBottomPaddingPx = res.getDimensionPixelSize(R.dimen.inline_qsb_bottom_margin);
-            qsbWidth = calculateQsbWidth();
         } else {
             hotseatBarBottomPaddingPx = (isTallDevice ? res.getDimensionPixelSize(
                     R.dimen.dynamic_grid_hotseat_bottom_tall_padding)
                     : res.getDimensionPixelSize(
                             R.dimen.dynamic_grid_hotseat_bottom_non_tall_padding))
                     + res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_bottom_padding);
-            qsbWidth = 0;
         }
+
         springLoadedHotseatBarTopMarginPx = res.getDimensionPixelSize(
                 R.dimen.spring_loaded_hotseat_top_margin);
         hotseatBarSidePaddingEndPx =
@@ -390,9 +390,7 @@
         hotseatBarSidePaddingStartPx = isVerticalBarLayout() ? workspacePageIndicatorHeight : 0;
         hotseatExtraVerticalSize =
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_extra_vertical_size);
-        hotseatBorderSpace = pxFromDp(inv.hotseatBorderSpaces[mTypeIndex], mMetrics);
-        updateHotseatIconSize(
-                pxFromDp(inv.iconSize[INDEX_DEFAULT], mMetrics));
+        updateHotseatIconSize(pxFromDp(inv.iconSize[INDEX_DEFAULT], mMetrics));
 
         qsbBottomMarginOriginalPx = isScalableGrid
                 ? res.getDimensionPixelSize(R.dimen.scalable_grid_qsb_bottom_margin)
@@ -483,6 +481,10 @@
                 cellLayoutPadding);
         updateWorkspacePadding();
 
+        // Hotseat and QSB width depends on updated cellSize and workspace padding
+        hotseatBorderSpace = calculateHotseatBorderSpace();
+        qsbWidth = calculateQsbWidth();
+
         flingToDeleteThresholdVelocity = res.getDimensionPixelSize(
                 R.dimen.drag_flingToDeleteMinVelocity);
 
@@ -493,14 +495,26 @@
                 new DotRenderer(allAppsIconSizePx, dotPath, DEFAULT_DOT_SIZE);
     }
 
+    /**
+     * QSB width is always calculated because when in 3 button nav the width doesn't follow the
+     * width of the hotseat.
+     */
     private int calculateQsbWidth() {
-        int columns = isTwoPanels ? inv.numColumns * 2 : inv.numColumns;
+        if (isQsbInline) {
+            int columns = isTwoPanels ? inv.numColumns * 2 : inv.numColumns;
+            return getIconToIconWidthForColumns(columns)
+                    - iconSizePx * numShownHotseatIcons
+                    - hotseatBorderSpace * numShownHotseatIcons;
+        } else {
+            int columns = inv.hotseatColumnSpan[mTypeIndex];
+            return getIconToIconWidthForColumns(columns);
+        }
+    }
 
-        return cellWidthPx * columns
-                + cellLayoutBorderSpacePx.x * (columns - 1)
-                - (cellWidthPx - iconSizePx) // left and right cell space
-                - iconSizePx * numShownHotseatIcons
-                - hotseatBorderSpace * numShownHotseatIcons;
+    private int getIconToIconWidthForColumns(int columns) {
+        return columns * getCellSize().x
+                + (columns - 1) * cellLayoutBorderSpacePx.x
+                - (getCellSize().x - iconSizePx);  // left and right cell space
     }
 
     private int getHorizontalMarginPx(InvariantDeviceProfile idp, Resources res) {
@@ -741,13 +755,6 @@
         // All apps
         updateAllAppsIconSize(scale, res);
 
-        // Hotseat
-        hotseatBorderSpace = pxFromDp(inv.hotseatBorderSpaces[mTypeIndex], mMetrics, scale);
-        if (isQsbInline) {
-            qsbWidth = calculateQsbWidth();
-        } else {
-            qsbWidth = 0;
-        }
         updateHotseatIconSize(iconSizePx);
 
         // Folder icon
@@ -755,6 +762,23 @@
         folderIconOffsetYPx = (iconSizePx - folderIconSizePx) / 2;
     }
 
+    /**
+     * Hotseat width spans a certain number of columns on scalable grids.
+     * This method calculates the space between the icons to achieve that width.
+     */
+    private int calculateHotseatBorderSpace() {
+        if (!isScalableGrid) return 0;
+        //TODO(http://b/228998082) remove this when 3 button spaces are fixed
+        if (areNavButtonsInline) {
+            return pxFromDp(inv.hotseatBorderSpaces[mTypeIndex], mMetrics);
+        } else {
+            int columns = inv.hotseatColumnSpan[mTypeIndex];
+            float hotseatWidthPx = getIconToIconWidthForColumns(columns);
+            float hotseatIconsTotalPx = iconSizePx * numShownHotseatIcons;
+            return (int) (hotseatWidthPx - hotseatIconsTotalPx) / (numShownHotseatIcons - 1);
+        }
+    }
+
 
     /**
      * Updates the iconSize for allApps* variants.
@@ -1070,6 +1094,13 @@
                 mHotseatPadding.left -= diff;
                 mHotseatPadding.right += diff;
             }
+        } else if (isScalableGrid) {
+            int sideSpacing = (availableWidthPx - qsbWidth) / 2;
+            mHotseatPadding.set(sideSpacing,
+                    hotseatBarTopPaddingPx,
+                    sideSpacing,
+                    hotseatBarSizePx - hotseatCellHeightPx - hotseatBarTopPaddingPx
+                            + mInsets.bottom);
         } else {
             // We want the edges of the hotseat to line up with the edges of the workspace, but the
             // icons in the hotseat are a different size, and so don't line up perfectly. To account
@@ -1306,6 +1337,7 @@
         writer.println(prefix + pxToDpStr("allAppsLeftRightMargin", allAppsLeftRightMargin));
 
         writer.println(prefix + pxToDpStr("hotseatBarSizePx", hotseatBarSizePx));
+        writer.println(prefix + "\tinv.hotseatColumnSpan: " + inv.hotseatColumnSpan[mTypeIndex]);
         writer.println(prefix + pxToDpStr("hotseatCellHeightPx", hotseatCellHeightPx));
         writer.println(prefix + pxToDpStr("hotseatBarTopPaddingPx", hotseatBarTopPaddingPx));
         writer.println(prefix + pxToDpStr("hotseatBarBottomPaddingPx", hotseatBarBottomPaddingPx));
@@ -1384,7 +1416,7 @@
     private static Context getContext(Context c, Info info, int orientation, WindowBounds bounds) {
         Configuration config = new Configuration(c.getResources().getConfiguration());
         config.orientation = orientation;
-        config.densityDpi = info.densityDpi;
+        config.densityDpi = info.getDensityDpi();
         config.smallestScreenWidthDp = (int) info.smallestSizeDp(bounds);
         return c.createConfigurationContext(config);
     }
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index a9db5ce..76106fc 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -173,17 +173,9 @@
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
 
-        int width;
-        if (mActivity.getDeviceProfile().isQsbInline) {
-            width = mActivity.getDeviceProfile().qsbWidth;
-        } else {
-            MarginLayoutParams qsbParams = (MarginLayoutParams) mQsb.getLayoutParams();
-            width = getShortcutsAndWidgets().getMeasuredWidth()
-                    - qsbParams.getMarginStart()
-                    - qsbParams.getMarginEnd();
-        }
+        int qsbWidth = mActivity.getDeviceProfile().qsbWidth;
 
-        mQsb.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
+        mQsb.measure(MeasureSpec.makeMeasureSpec(qsbWidth, MeasureSpec.EXACTLY),
                 MeasureSpec.makeMeasureSpec(mQsbHeight, MeasureSpec.EXACTLY));
     }
 
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 1f92079..db43b44 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -154,6 +154,8 @@
      */
     public int numDatabaseHotseatIcons;
 
+    public int[] hotseatColumnSpan;
+
     /**
      * Number of columns in the all apps list.
      */
@@ -357,6 +359,7 @@
         numShrunkenHotseatIcons = closestProfile.numShrunkenHotseatIcons;
         numDatabaseHotseatIcons = deviceType == TYPE_MULTI_DISPLAY
                 ? closestProfile.numDatabaseHotseatIcons : closestProfile.numHotseatIcons;
+        hotseatColumnSpan = closestProfile.hotseatColumnSpan;
         hotseatBorderSpaces = displayOption.hotseatBorderSpaces;
 
         numAllAppsColumns = closestProfile.numAllAppsColumns;
@@ -398,7 +401,8 @@
             // We need to ensure that there is enough extra space in the wallpaper
             // for the intended parallax effects
             float parallaxFactor =
-                    dpiFromPx(Math.min(displayWidth, displayHeight), displayInfo.densityDpi) < 720
+                    dpiFromPx(Math.min(displayWidth, displayHeight), displayInfo.getDensityDpi())
+                            < 720
                             ? 2
                             : wallpaperTravelToScreenWidthRatio(displayWidth, displayHeight);
             defaultWallpaperSize.x =
@@ -587,8 +591,8 @@
             }
         }
 
-        float width = dpiFromPx(minWidthPx, displayInfo.densityDpi);
-        float height = dpiFromPx(minHeightPx, displayInfo.densityDpi);
+        float width = dpiFromPx(minWidthPx, displayInfo.getDensityDpi());
+        float height = dpiFromPx(minHeightPx, displayInfo.getDensityDpi());
 
         // Sort the profiles based on the closeness to the device size
         Collections.sort(points, (a, b) ->
@@ -735,6 +739,7 @@
         private final int numHotseatIcons;
         private final int numShrunkenHotseatIcons;
         private final int numDatabaseHotseatIcons;
+        private final int[] hotseatColumnSpan = new int[COUNT_SIZES];
 
         private final String dbFile;
 
@@ -774,6 +779,16 @@
                     R.styleable.GridDisplayOption_numShrunkenHotseatIcons, numHotseatIcons / 2);
             numDatabaseHotseatIcons = a.getInt(
                     R.styleable.GridDisplayOption_numExtendedHotseatIcons, 2 * numHotseatIcons);
+            hotseatColumnSpan[INDEX_DEFAULT] = a.getInt(
+                    R.styleable.GridDisplayOption_hotseatColumnSpan, numColumns);
+            hotseatColumnSpan[INDEX_LANDSCAPE] = a.getInt(
+                    R.styleable.GridDisplayOption_hotseatColumnSpanLandscape, numColumns);
+            hotseatColumnSpan[INDEX_TWO_PANEL_LANDSCAPE] = a.getInt(
+                    R.styleable.GridDisplayOption_hotseatColumnSpanTwoPanelLandscape,
+                    numColumns);
+            hotseatColumnSpan[INDEX_TWO_PANEL_PORTRAIT] = a.getInt(
+                    R.styleable.GridDisplayOption_hotseatColumnSpanTwoPanelPortrait,
+                    numColumns);
 
             numFolderRows = a.getInt(
                     R.styleable.GridDisplayOption_numFolderRows, numRows);
@@ -821,6 +836,7 @@
         private float folderBorderSpace;
         private final PointF[] borderSpaces = new PointF[COUNT_SIZES];
         private final float[] horizontalMargin = new float[COUNT_SIZES];
+        //TODO(http://b/228998082) remove this when 3 button spaces are fixed
         private final float[] hotseatBorderSpaces = new float[COUNT_SIZES];
 
         private final float[] iconSizes = new float[COUNT_SIZES];
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index bf56ac0..a991c2f 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -36,6 +36,7 @@
 import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
 import static com.android.launcher3.graphics.Scrim.SCRIM_PROGRESS;
 import static com.android.launcher3.graphics.SysUiScrim.SYSUI_PROGRESS;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_FADE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_SCALE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_TRANSLATE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_SCRIM_FADE;
@@ -117,8 +118,6 @@
         }
 
         int elements = state.getVisibleElements(mLauncher);
-        Interpolator fadeInterpolator = config.getInterpolator(ANIM_WORKSPACE_FADE,
-                pageAlphaProvider.interpolator);
         Hotseat hotseat = mWorkspace.getHotseat();
         Interpolator scaleInterpolator = config.getInterpolator(ANIM_WORKSPACE_SCALE, ZOOM_OUT);
         LauncherState fromState = mLauncher.getStateManager().getState();
@@ -146,11 +145,15 @@
                     hotseatScaleInterpolator);
         }
 
-        float hotseatIconsAlpha = (elements & HOTSEAT_ICONS) != 0 ? 1 : 0;
-        propertySetter.setViewAlpha(hotseat, hotseatIconsAlpha, fadeInterpolator);
+        Interpolator workspaceFadeInterpolator = config.getInterpolator(ANIM_WORKSPACE_FADE,
+                pageAlphaProvider.interpolator);
         float workspacePageIndicatorAlpha = (elements & WORKSPACE_PAGE_INDICATOR) != 0 ? 1 : 0;
         propertySetter.setViewAlpha(mLauncher.getWorkspace().getPageIndicator(),
-                workspacePageIndicatorAlpha, fadeInterpolator);
+                workspacePageIndicatorAlpha, workspaceFadeInterpolator);
+        Interpolator hotseatFadeInterpolator = config.getInterpolator(ANIM_HOTSEAT_FADE,
+                workspaceFadeInterpolator);
+        float hotseatIconsAlpha = (elements & HOTSEAT_ICONS) != 0 ? 1 : 0;
+        propertySetter.setViewAlpha(hotseat, hotseatIconsAlpha, hotseatFadeInterpolator);
 
         // Update the accessibility flags for hotseat based on launcher state.
         hotseat.setImportantForAccessibility(
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index bdfeada..096e2c8 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -228,6 +228,8 @@
                 requestFocus();
                 mgr.logger().sendToInteractionJankMonitor(
                         LAUNCHER_ALLAPPS_VERTICAL_SWIPE_BEGIN, this);
+                hideKeyboardAsync(ActivityContext.lookupContext(getContext()),
+                        getApplicationWindowToken());
                 break;
             case SCROLL_STATE_IDLE:
                 mgr.logger().sendToInteractionJankMonitor(
@@ -243,8 +245,6 @@
                 && mEmptySearchBackground != null && mEmptySearchBackground.getAlpha() > 0) {
             mEmptySearchBackground.setHotspot(e.getX(), e.getY());
         }
-        hideKeyboardAsync(ActivityContext.lookupContext(getContext()),
-                getApplicationWindowToken());
         return result;
     }
 
diff --git a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
index 72ca5a7..95c67dd 100644
--- a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
@@ -17,6 +17,7 @@
 
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_TAP_ON_PERSONAL_TAB;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_TAP_ON_WORK_TAB;
+import static com.android.launcher3.util.UiThreadHelper.hideKeyboardAsync;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -458,6 +459,8 @@
                             mActivityContext.getStatsLogManager().logger()
                                     .log(LAUNCHER_ALLAPPS_TAP_ON_PERSONAL_TAB);
                         }
+                        hideKeyboardAsync(ActivityContext.lookupContext(getContext()),
+                                getApplicationWindowToken());
                     });
             findViewById(R.id.tab_work)
                     .setOnClickListener((View view) -> {
@@ -465,6 +468,8 @@
                             mActivityContext.getStatsLogManager().logger()
                                     .log(LAUNCHER_ALLAPPS_TAP_ON_WORK_TAB);
                         }
+                        hideKeyboardAsync(ActivityContext.lookupContext(getContext()),
+                                getApplicationWindowToken());
                     });
             setDeviceManagementResources();
             onActivePageChanged(mViewPager.getNextPage());
diff --git a/src/com/android/launcher3/anim/AnimationSuccessListener.java b/src/com/android/launcher3/anim/AnimationSuccessListener.java
index a312070..6196df2 100644
--- a/src/com/android/launcher3/anim/AnimationSuccessListener.java
+++ b/src/com/android/launcher3/anim/AnimationSuccessListener.java
@@ -19,6 +19,8 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 
+import androidx.annotation.CallSuper;
+
 /**
  * Extension of {@link AnimatorListenerAdapter} for listening for non-cancelled animations
  */
@@ -27,6 +29,7 @@
     protected boolean mCancelled = false;
 
     @Override
+    @CallSuper
     public void onAnimationCancel(Animator animation) {
         mCancelled = true;
     }
diff --git a/src/com/android/launcher3/anim/Interpolators.java b/src/com/android/launcher3/anim/Interpolators.java
index f8a2c79..c38cf9a 100644
--- a/src/com/android/launcher3/anim/Interpolators.java
+++ b/src/com/android/launcher3/anim/Interpolators.java
@@ -57,6 +57,11 @@
     public static final Interpolator DECELERATED_EASE = new PathInterpolator(0, 0, .2f, 1f);
     public static final Interpolator ACCELERATED_EASE = new PathInterpolator(0.4f, 0, 1f, 1f);
 
+    public static final Interpolator EMPHASIZED_ACCELERATE = new PathInterpolator(
+            0.3f, 0f, 0.8f, 0.15f);
+    public static final Interpolator EMPHASIZED_DECELERATE = new PathInterpolator(
+            0.05f, 0.7f, 0.1f, 1f);
+
     public static final Interpolator EXAGGERATED_EASE;
 
     public static final Interpolator INSTANT = t -> 1;
diff --git a/src/com/android/launcher3/statemanager/StateManager.java b/src/com/android/launcher3/statemanager/StateManager.java
index 2aa9dde..9f50ff9 100644
--- a/src/com/android/launcher3/statemanager/StateManager.java
+++ b/src/com/android/launcher3/statemanager/StateManager.java
@@ -335,7 +335,13 @@
             @Override
             public void onAnimationStart(Animator animation) {
                 // Change the internal state only when the transition actually starts
-                onStateTransitionStart(state);
+                onStateTransitionStart(mCancelled ? mCurrentStableState : state);
+            }
+
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                super.onAnimationCancel(animation);
+                mState = mCurrentStableState;
             }
 
             @Override
diff --git a/src/com/android/launcher3/states/StateAnimationConfig.java b/src/com/android/launcher3/states/StateAnimationConfig.java
index f04e685..f99519d 100644
--- a/src/com/android/launcher3/states/StateAnimationConfig.java
+++ b/src/com/android/launcher3/states/StateAnimationConfig.java
@@ -53,6 +53,7 @@
             ANIM_WORKSPACE_FADE,
             ANIM_HOTSEAT_SCALE,
             ANIM_HOTSEAT_TRANSLATE,
+            ANIM_HOTSEAT_FADE,
             ANIM_OVERVIEW_SCALE,
             ANIM_OVERVIEW_TRANSLATE_X,
             ANIM_OVERVIEW_TRANSLATE_Y,
@@ -72,6 +73,7 @@
     public static final int ANIM_WORKSPACE_FADE = 3;
     public static final int ANIM_HOTSEAT_SCALE = 4;
     public static final int ANIM_HOTSEAT_TRANSLATE = 5;
+    public static final int ANIM_HOTSEAT_FADE = 16;
     public static final int ANIM_OVERVIEW_SCALE = 6;
     public static final int ANIM_OVERVIEW_TRANSLATE_X = 7;
     public static final int ANIM_OVERVIEW_TRANSLATE_Y = 8;
@@ -83,7 +85,7 @@
     public static final int ANIM_OVERVIEW_ACTIONS_FADE = 14;
     public static final int ANIM_WORKSPACE_PAGE_TRANSLATE_X = 15;
 
-    private static final int ANIM_TYPES_COUNT = 16;
+    private static final int ANIM_TYPES_COUNT = 17;
 
     protected final Interpolator[] mInterpolators = new Interpolator[ANIM_TYPES_COUNT];
 
diff --git a/src/com/android/launcher3/touch/AllAppsSwipeController.java b/src/com/android/launcher3/touch/AllAppsSwipeController.java
index 5aac3f3..9d7fd9a 100644
--- a/src/com/android/launcher3/touch/AllAppsSwipeController.java
+++ b/src/com/android/launcher3/touch/AllAppsSwipeController.java
@@ -17,11 +17,19 @@
 
 import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.anim.Interpolators.DECELERATED_EASE;
+import static com.android.launcher3.anim.Interpolators.EMPHASIZED_ACCELERATE;
+import static com.android.launcher3.anim.Interpolators.EMPHASIZED_DECELERATE;
 import static com.android.launcher3.anim.Interpolators.FINAL_FRAME;
 import static com.android.launcher3.anim.Interpolators.INSTANT;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_DEPTH;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_FADE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_TRANSLATE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_SCRIM_FADE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_SCALE;
 
 import android.view.MotionEvent;
 import android.view.animation.Interpolator;
@@ -39,11 +47,41 @@
 
     private static final float ALLAPPS_STAGGERED_FADE_THRESHOLD = 0.5f;
 
+    // Custom timing for NORMAL -> ALL_APPS on phones only.
+    private static final float WORKSPACE_MOTION_START = 0.1667f;
+    private static final float ALL_APPS_STATE_TRANSITION = 0.305f;
+    private static final float ALL_APPS_FADE_END = 0.4717f;
+
     public static final Interpolator ALLAPPS_STAGGERED_FADE_EARLY_RESPONDER =
             Interpolators.clampToProgress(LINEAR, 0, ALLAPPS_STAGGERED_FADE_THRESHOLD);
     public static final Interpolator ALLAPPS_STAGGERED_FADE_LATE_RESPONDER =
             Interpolators.clampToProgress(LINEAR, ALLAPPS_STAGGERED_FADE_THRESHOLD, 1f);
 
+    // Custom interpolators for NORMAL -> ALL_APPS on phones only.
+    public static final Interpolator BLUR =
+            Interpolators.clampToProgress(
+                    EMPHASIZED_DECELERATE, WORKSPACE_MOTION_START, ALL_APPS_STATE_TRANSITION);
+    public static final Interpolator WORKSPACE_SCALE =
+            Interpolators.clampToProgress(
+                    EMPHASIZED_DECELERATE, WORKSPACE_MOTION_START, ALL_APPS_STATE_TRANSITION);
+    public static final Interpolator HOTSEAT_FADE =
+            Interpolators.clampToProgress(FINAL_FRAME, 0f, ALL_APPS_STATE_TRANSITION);
+    public static final Interpolator HOTSEAT_TRANSLATE =
+            Interpolators.clampToProgress(
+                    EMPHASIZED_ACCELERATE, WORKSPACE_MOTION_START, ALL_APPS_STATE_TRANSITION);
+    public static final Interpolator SCRIM_FADE =
+            Interpolators.clampToProgress(
+                    Interpolators.mapToProgress(LINEAR, 0f, 0.8f),
+                    WORKSPACE_MOTION_START, ALL_APPS_STATE_TRANSITION);
+    public static final Interpolator ALL_APPS_FADE =
+            Interpolators.clampToProgress(
+                    Interpolators.mapToProgress(DECELERATED_EASE, 0.2f, 1.0f),
+                    ALL_APPS_STATE_TRANSITION, ALL_APPS_FADE_END);
+    public static final Interpolator ALL_APPS_VERTICAL_PROGRESS =
+            Interpolators.clampToProgress(
+                    Interpolators.mapToProgress(EMPHASIZED_DECELERATE, 0.4f, 1.0f),
+                    ALL_APPS_STATE_TRANSITION, 1.0f);
+
     public AllAppsSwipeController(Launcher l) {
         super(l, SingleAxisSwipeDetector.VERTICAL);
     }
@@ -118,11 +156,17 @@
      */
     public static void applyNormalToAllAppsAnimConfig(Launcher launcher,
             StateAnimationConfig config) {
-        boolean isTablet = launcher.getDeviceProfile().isTablet;
-        config.setInterpolator(ANIM_SCRIM_FADE, ALLAPPS_STAGGERED_FADE_EARLY_RESPONDER);
-        config.setInterpolator(ANIM_ALL_APPS_FADE, isTablet
-                ? INSTANT : ALLAPPS_STAGGERED_FADE_LATE_RESPONDER);
+        if (launcher.getDeviceProfile().isTablet) {
+            config.setInterpolator(ANIM_SCRIM_FADE, ALLAPPS_STAGGERED_FADE_EARLY_RESPONDER);
+            config.setInterpolator(ANIM_ALL_APPS_FADE, INSTANT);
+        } else {
+            config.setInterpolator(ANIM_DEPTH, BLUR);
+            config.setInterpolator(ANIM_WORKSPACE_SCALE, WORKSPACE_SCALE);
+            config.setInterpolator(ANIM_HOTSEAT_FADE, HOTSEAT_FADE);
+            config.setInterpolator(ANIM_HOTSEAT_TRANSLATE, HOTSEAT_TRANSLATE);
+            config.setInterpolator(ANIM_SCRIM_FADE, SCRIM_FADE);
+            config.setInterpolator(ANIM_ALL_APPS_FADE, ALL_APPS_FADE);
+            config.setInterpolator(ANIM_VERTICAL_PROGRESS, ALL_APPS_VERTICAL_PROGRESS);
+        }
     }
-
-
 }
diff --git a/src/com/android/launcher3/util/DisplayController.java b/src/com/android/launcher3/util/DisplayController.java
index 777da23..7c73be5 100644
--- a/src/com/android/launcher3/util/DisplayController.java
+++ b/src/com/android/launcher3/util/DisplayController.java
@@ -290,7 +290,7 @@
 
         // Configuration property
         public final float fontScale;
-        public final int densityDpi;
+        private final int densityDpi;
         public final NavigationMode navigationMode;
 
         private final PortraitSize mScreenSizeDp;
@@ -357,6 +357,10 @@
         public float smallestSizeDp(WindowBounds bounds) {
             return dpiFromPx(Math.min(bounds.bounds.width(), bounds.bounds.height()), densityDpi);
         }
+
+        public int getDensityDpi() {
+            return densityDpi;
+        }
     }
 
     /**
diff --git a/src/com/android/launcher3/views/RecyclerViewFastScroller.java b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
index a982786..7a8e9d5 100644
--- a/src/com/android/launcher3/views/RecyclerViewFastScroller.java
+++ b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
@@ -20,6 +20,8 @@
 
 import static androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_IDLE;
 
+import static com.android.launcher3.util.UiThreadHelper.hideKeyboardAsync;
+
 import android.animation.ObjectAnimator;
 import android.content.Context;
 import android.content.res.Resources;
@@ -283,6 +285,9 @@
                 }
                 break;
             case MotionEvent.ACTION_UP:
+                hideKeyboardAsync(ActivityContext.lookupContext(getContext()),
+                        getApplicationWindowToken());
+                break;
             case MotionEvent.ACTION_CANCEL:
                 mRv.onFastScrollCompleted();
                 mTouchOffsetY = 0;
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java b/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java
index 52a6759..ec921e8 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -59,15 +59,21 @@
 
     @Override
     public ScaleAndTranslation getWorkspaceScaleAndTranslation(Launcher launcher) {
-        ScaleAndTranslation scaleAndTranslation =
-                new ScaleAndTranslation(NO_SCALE, NO_OFFSET, NO_OFFSET);
+        return new ScaleAndTranslation(0.97f, NO_OFFSET, NO_OFFSET);
+    }
+
+    @Override
+    public ScaleAndTranslation getHotseatScaleAndTranslation(Launcher launcher) {
         if (launcher.getDeviceProfile().isTablet) {
-            scaleAndTranslation.scale = 0.97f;
+            return getWorkspaceScaleAndTranslation(launcher);
         } else {
-            scaleAndTranslation.translationY =
-                    -launcher.getAllAppsController().getShiftRange() * PARALLAX_COEFFICIENT;
+            ScaleAndTranslation overviewScaleAndTranslation = LauncherState.OVERVIEW
+                    .getWorkspaceScaleAndTranslation(launcher);
+            return new ScaleAndTranslation(
+                    NO_SCALE,
+                    overviewScaleAndTranslation.translationX,
+                    overviewScaleAndTranslation.translationY);
         }
-        return scaleAndTranslation;
     }
 
     @Override
diff --git a/tests/src/com/android/launcher3/DeviceProfileBaseTest.kt b/tests/src/com/android/launcher3/DeviceProfileBaseTest.kt
index f91f1c4..6d0fcb6 100644
--- a/tests/src/com/android/launcher3/DeviceProfileBaseTest.kt
+++ b/tests/src/com/android/launcher3/DeviceProfileBaseTest.kt
@@ -64,6 +64,7 @@
         windowBounds = WindowBounds(x, y, x, y - 100, 0)
 
         whenever(info.isTablet(any())).thenReturn(false)
+        whenever(info.getDensityDpi()).thenReturn(560)
 
         inv = newScalableInvariantDeviceProfile()
     }
@@ -77,6 +78,7 @@
         windowBounds = WindowBounds(x, y, x, y - 100, 0)
 
         whenever(info.isTablet(any())).thenReturn(true)
+        whenever(info.getDensityDpi()).thenReturn(320)
 
         inv = newScalableInvariantDeviceProfile()
     }
@@ -107,6 +109,7 @@
                 PointF(16f, 16f)
             ).toTypedArray()
             hotseatBorderSpaces = FloatArray(4) { 16f }
+            hotseatColumnSpan = IntArray(4) { 4 }
             iconSize = FloatArray(4) { 56f }
             allAppsIconSize = FloatArray(4) { 56f }
             iconTextSize = FloatArray(4) { 14f }
diff --git a/tests/src/com/android/launcher3/HotseatSizeTest.kt b/tests/src/com/android/launcher3/HotseatShownIconsTest.kt
similarity index 97%
rename from tests/src/com/android/launcher3/HotseatSizeTest.kt
rename to tests/src/com/android/launcher3/HotseatShownIconsTest.kt
index a44939f..593239d 100644
--- a/tests/src/com/android/launcher3/HotseatSizeTest.kt
+++ b/tests/src/com/android/launcher3/HotseatShownIconsTest.kt
@@ -23,15 +23,13 @@
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers
-import org.mockito.Mockito.`when` as whenever
 
 /**
  * Test for [DeviceProfile]
  */
 @SmallTest
 @RunWith(AndroidJUnit4::class)
-class HotseatSizeTest : DeviceProfileBaseTest() {
+class HotseatShownIconsTest : DeviceProfileBaseTest() {
 
     @Test
     fun hotseat_size_is_normal_for_handhelds() {
diff --git a/tests/src/com/android/launcher3/InlineQsbTest.kt b/tests/src/com/android/launcher3/InlineQsbTest.kt
index e00dca8..905c1e1 100644
--- a/tests/src/com/android/launcher3/InlineQsbTest.kt
+++ b/tests/src/com/android/launcher3/InlineQsbTest.kt
@@ -29,17 +29,16 @@
 class InlineQsbTest : DeviceProfileBaseTest() {
 
     @Test
-    fun qsbWidth_is_match_parent_for_phones() {
+    fun qsb_is_not_inline_for_phones() {
         initializeVarsForPhone()
 
         val dp = newDP()
 
         assertThat(dp.isQsbInline).isFalse()
-        assertThat(dp.qsbWidth).isEqualTo(0)
     }
 
     @Test
-    fun qsbWidth_is_match_parent_for_tablet_portrait() {
+    fun qsb_is_inline_for_tablet_portrait() {
         initializeVarsForTablet()
         inv = newScalableInvariantDeviceProfile().apply {
             inlineQsb = booleanArrayOf(
@@ -62,11 +61,10 @@
         )
 
         assertThat(dp.isQsbInline).isFalse()
-        assertThat(dp.qsbWidth).isEqualTo(0)
     }
 
     @Test
-    fun qsbWidth_has_size_for_tablet_landscape() {
+    fun qsb_is_inline_for_tablet_landscape() {
         initializeVarsForTablet(isLandscape = true)
         inv = newScalableInvariantDeviceProfile().apply {
             inlineQsb = booleanArrayOf(
@@ -75,16 +73,17 @@
                 false,
                 false
             )
+            numColumns = 6
+            numRows = 5
+            numShownHotseatIcons = 6
         }
 
         val dp = newDP()
 
         if (dp.hotseatQsbHeight > 0) {
             assertThat(dp.isQsbInline).isTrue()
-            assertThat(dp.qsbWidth).isGreaterThan(0)
         } else { // Launcher3 doesn't have QSB height
             assertThat(dp.isQsbInline).isFalse()
-            assertThat(dp.qsbWidth).isEqualTo(0)
         }
     }
 
@@ -92,14 +91,13 @@
      * This test is to make sure that a tablet doesn't inline the QSB if the layout doesn't support
      */
     @Test
-    fun qsbWidth_is_match_parent_for_tablet_landscape_without_inline() {
+    fun qsb_is_not_inline_for_tablet_landscape_without_inline() {
         initializeVarsForTablet(isLandscape = true)
         useTwoPanels = true
 
         val dp = newDP()
 
         assertThat(dp.isQsbInline).isFalse()
-        assertThat(dp.qsbWidth).isEqualTo(0)
     }
 
 }
\ No newline at end of file
diff --git a/tests/src/com/android/launcher3/deviceemulator/models/DeviceEmulationData.java b/tests/src/com/android/launcher3/deviceemulator/models/DeviceEmulationData.java
index 3623513..8d275cc 100644
--- a/tests/src/com/android/launcher3/deviceemulator/models/DeviceEmulationData.java
+++ b/tests/src/com/android/launcher3/deviceemulator/models/DeviceEmulationData.java
@@ -135,7 +135,7 @@
             resourceOverrides.put(s, getDimenByName(s, context.getResources(), 0));
         }
         return new DeviceEmulationData(info.currentSize.x, info.currentSize.y,
-                info.densityDpi, info.cutout, code, grids, resourceOverrides);
+                info.getDensityDpi(), info.cutout, code, grids, resourceOverrides);
     }
 
     public static DeviceEmulationData getDevice(String deviceCode) throws Exception {