Merge "Add TaskbarView as a child of Hotseat" into sc-dev
diff --git a/quickstep/res/layout/taskbar_divider.xml b/quickstep/res/layout/taskbar_divider.xml
index 6e1aa1e..87649f7 100644
--- a/quickstep/res/layout/taskbar_divider.xml
+++ b/quickstep/res/layout/taskbar_divider.xml
@@ -18,6 +18,4 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="@dimen/taskbar_divider_thickness"
     android:layout_height="@dimen/taskbar_divider_height"
-    android:layout_marginStart="@dimen/taskbar_icon_spacing"
-    android:layout_marginEnd="@dimen/taskbar_icon_spacing"
     android:background="@color/taskbar_divider" />
\ No newline at end of file
diff --git a/quickstep/res/layout/taskbar_view.xml b/quickstep/res/layout/taskbar_view.xml
new file mode 100644
index 0000000..34a88ea
--- /dev/null
+++ b/quickstep/res/layout/taskbar_view.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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.
+-->
+
+<com.android.launcher3.taskbar.TaskbarView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/taskbar_view"
+    android:layout_width="match_parent"
+    android:layout_height="@dimen/taskbar_size"
+    android:background="@android:color/transparent"
+    android:layout_gravity="bottom"
+    android:gravity="center"
+    android:visibility="gone" />
+
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index c4becf1..9ab49ce 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -46,6 +46,7 @@
 import com.android.launcher3.taskbar.TaskbarActivityContext;
 import com.android.launcher3.taskbar.TaskbarController;
 import com.android.launcher3.taskbar.TaskbarStateHandler;
+import com.android.launcher3.taskbar.TaskbarView;
 import com.android.launcher3.uioverrides.RecentsViewStateController;
 import com.android.launcher3.util.ActivityOptionsWrapper;
 import com.android.launcher3.util.DisplayController;
@@ -243,9 +244,10 @@
             mTaskbarController = null;
         }
         if (mDeviceProfile.isTaskbarPresent) {
+            TaskbarView taskbarViewOnHome = (TaskbarView) mHotseat.getTaskbarView();
             TaskbarActivityContext taskbarActivityContext = new TaskbarActivityContext(this);
             mTaskbarController = new TaskbarController(this,
-                    taskbarActivityContext.getTaskbarContainerView());
+                    taskbarActivityContext.getTaskbarContainerView(), taskbarViewOnHome);
             mTaskbarController.init();
         }
     }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 8312b82..fc5e2c1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -54,10 +54,7 @@
         return mTaskbarContainerView;
     }
 
-    /**
-     * @return A LayoutInflater to use in this Context. Views inflated with this LayoutInflater will
-     * be able to access this TaskbarActivityContext via ActivityContext.lookupContext().
-     */
+    @Override
     public LayoutInflater getLayoutInflater() {
         return mLayoutInflater;
     }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarContainerView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarContainerView.java
index 528f43e..5202d91 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarContainerView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarContainerView.java
@@ -84,7 +84,7 @@
     private ViewTreeObserverWrapper.OnComputeInsetsListener createTaskbarInsetsComputer() {
         return insetsInfo -> {
             if (getAlpha() < AlphaUpdateListener.ALPHA_CUTOFF_THRESHOLD
-                    || mTaskbarView.isDraggingItem()) {
+                    || mTaskbarView.getVisibility() != VISIBLE || mTaskbarView.isDraggingItem()) {
                 // We're invisible or dragging out of taskbar, let touches pass through us.
                 insetsInfo.touchableRegion.setEmpty();
                 insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java
index f652961..abf6d54 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java
@@ -19,8 +19,6 @@
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 
-import static com.android.launcher3.AbstractFloatingView.TYPE_HIDE_TASKBAR;
-import static com.android.launcher3.AbstractFloatingView.TYPE_REPLACE_TASKBAR_WITH_HOTSEAT;
 import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.systemui.shared.system.WindowManagerWrapper.ITYPE_BOTTOM_TAPPABLE_ELEMENT;
@@ -43,7 +41,6 @@
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.BaseQuickstepLauncher;
 import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.Hotseat;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.QuickstepTransitionManager;
 import com.android.launcher3.R;
@@ -54,6 +51,7 @@
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.states.StateAnimationConfig;
 import com.android.launcher3.touch.ItemClickHandler;
+import com.android.launcher3.views.ActivityContext;
 import com.android.quickstep.AnimatedFloat;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -70,7 +68,8 @@
     private static final String WINDOW_TITLE = "Taskbar";
 
     private final TaskbarContainerView mTaskbarContainerView;
-    private final TaskbarView mTaskbarView;
+    private final TaskbarView mTaskbarViewInApp;
+    private final TaskbarView mTaskbarViewOnHome;
     private final BaseQuickstepLauncher mLauncher;
     private final WindowManager mWindowManager;
     // Layout width and height of the Taskbar in the default state.
@@ -91,14 +90,17 @@
 
     private @Nullable Animator mAnimator;
     private boolean mIsAnimatingToLauncher;
+    private boolean mIsAnimatingToApp;
 
     public TaskbarController(BaseQuickstepLauncher launcher,
-            TaskbarContainerView taskbarContainerView) {
+            TaskbarContainerView taskbarContainerView, TaskbarView taskbarViewOnHome) {
         mLauncher = launcher;
         mTaskbarContainerView = taskbarContainerView;
         mTaskbarContainerView.construct(createTaskbarContainerViewCallbacks());
-        mTaskbarView = mTaskbarContainerView.findViewById(R.id.taskbar_view);
-        mTaskbarView.construct(createTaskbarViewCallbacks());
+        mTaskbarViewInApp = mTaskbarContainerView.findViewById(R.id.taskbar_view);
+        mTaskbarViewInApp.construct(createTaskbarViewCallbacks());
+        mTaskbarViewOnHome = taskbarViewOnHome;
+        mTaskbarViewOnHome.construct(createTaskbarViewCallbacks());
         mWindowManager = mLauncher.getWindowManager();
         mTaskbarSize = new Point(MATCH_PARENT, mLauncher.getDeviceProfile().taskbarSize);
         mTaskbarStateHandler = mLauncher.getTaskbarStateHandler();
@@ -115,12 +117,13 @@
         return new TaskbarVisibilityControllerCallbacks() {
             @Override
             public void updateTaskbarBackgroundAlpha(float alpha) {
-                mTaskbarView.setBackgroundAlpha(alpha);
+                mTaskbarViewInApp.setBackgroundAlpha(alpha);
             }
 
             @Override
             public void updateTaskbarVisibilityAlpha(float alpha) {
                 mTaskbarContainerView.setAlpha(alpha);
+                mTaskbarViewOnHome.setAlpha(alpha);
             }
         };
     }
@@ -196,7 +199,7 @@
             public View.OnLongClickListener getItemOnLongClickListener() {
                 return view -> {
                     if (mLauncher.hasBeenResumed() && view.getTag() instanceof ItemInfo) {
-                        alignRealHotseatWithTaskbar();
+                        // TODO: remove this path
                         return mDragController.startWorkspaceDragOnLongClick(view);
                     } else {
                         return mDragController.startSystemDragOnLongClick(view);
@@ -205,11 +208,24 @@
             }
 
             @Override
-            public int getEmptyHotseatViewVisibility() {
+            public int getEmptyHotseatViewVisibility(TaskbarView taskbarView) {
                 // When on the home screen, we want the empty hotseat views to take up their full
                 // space so that the others line up with the home screen hotseat.
-                return mLauncher.hasBeenResumed() || mIsAnimatingToLauncher
-                        ? View.INVISIBLE : View.GONE;
+                boolean isOnHomeScreen = taskbarView == mTaskbarViewOnHome
+                        || mLauncher.hasBeenResumed() || mIsAnimatingToLauncher;
+                return isOnHomeScreen ? View.INVISIBLE : View.GONE;
+            }
+
+            @Override
+            public float getNonIconScale(TaskbarView taskbarView) {
+                return taskbarView == mTaskbarViewOnHome ? getTaskbarScaleOnHome() : 1f;
+            }
+
+            @Override
+            public void onItemPositionsChanged(TaskbarView taskbarView) {
+                if (taskbarView == mTaskbarViewOnHome) {
+                    alignRealHotseatWithTaskbar();
+                }
             }
         };
     }
@@ -218,7 +234,7 @@
         return new TaskbarHotseatControllerCallbacks() {
             @Override
             public void updateHotseatItems(ItemInfo[] hotseatItemInfos) {
-                mTaskbarView.updateHotseatItems(hotseatItemInfos);
+                mTaskbarViewInApp.updateHotseatItems(hotseatItemInfos);
                 mLatestLoadedHotseatItems = hotseatItemInfos;
                 dedupeAndUpdateRecentItems();
             }
@@ -235,7 +251,8 @@
 
             @Override
             public void updateRecentTaskAtIndex(int taskIndex, Task task) {
-                mTaskbarView.updateRecentTaskAtIndex(taskIndex, task);
+                mTaskbarViewInApp.updateRecentTaskAtIndex(taskIndex, task);
+                mTaskbarViewOnHome.updateRecentTaskAtIndex(taskIndex, task);
             }
         };
     }
@@ -244,16 +261,20 @@
      * Initializes the Taskbar, including adding it to the screen.
      */
     public void init() {
-        mTaskbarView.init(mHotseatController.getNumHotseatIcons(),
+        mTaskbarViewInApp.init(mHotseatController.getNumHotseatIcons(),
                 mRecentsController.getNumRecentIcons());
-        mTaskbarContainerView.init(mTaskbarView);
+        mTaskbarViewOnHome.init(mHotseatController.getNumHotseatIcons(),
+                mRecentsController.getNumRecentIcons());
+        mTaskbarContainerView.init(mTaskbarViewInApp);
         addToWindowManager();
         mTaskbarStateHandler.setTaskbarCallbacks(createTaskbarStateHandlerCallbacks());
         mTaskbarVisibilityController.init();
         mHotseatController.init();
         mRecentsController.init();
 
-        SCALE_PROPERTY.set(mTaskbarView, mLauncher.hasBeenResumed() ? getTaskbarScaleOnHome() : 1f);
+        SCALE_PROPERTY.set(mTaskbarViewInApp, mLauncher.hasBeenResumed()
+                ? getTaskbarScaleOnHome() : 1f);
+        updateWhichTaskbarViewIsVisible();
     }
 
     private TaskbarStateHandlerCallbacks createTaskbarStateHandlerCallbacks() {
@@ -274,7 +295,8 @@
             mAnimator.end();
         }
 
-        mTaskbarView.cleanup();
+        mTaskbarViewInApp.cleanup();
+        mTaskbarViewOnHome.cleanup();
         mTaskbarContainerView.cleanup();
         removeFromWindowManager();
         mTaskbarStateHandler.setTaskbarCallbacks(null);
@@ -313,7 +335,7 @@
         TaskbarContainerView.LayoutParams taskbarLayoutParams =
                 new TaskbarContainerView.LayoutParams(mTaskbarSize.x, mTaskbarSize.y);
         taskbarLayoutParams.gravity = gravity;
-        mTaskbarView.setLayoutParams(taskbarLayoutParams);
+        mTaskbarViewInApp.setLayoutParams(taskbarLayoutParams);
 
         mWindowManager.addView(mTaskbarContainerView, mWindowLayoutParams);
     }
@@ -330,7 +352,6 @@
             mAnimator = createAnimToLauncher(null, duration);
         } else {
             mAnimator = createAnimToApp(duration);
-            replaceTaskbarWithHotseatOrViceVersa();
         }
         mAnimator.addListener(new AnimatorListenerAdapter() {
             @Override
@@ -351,36 +372,41 @@
         if (toState != null) {
             mTaskbarStateHandler.setStateWithAnimation(toState, new StateAnimationConfig(), anim);
         }
-        anim.addFloat(mTaskbarView, SCALE_PROPERTY, mTaskbarView.getScaleX(),
+        anim.addFloat(mTaskbarViewInApp, SCALE_PROPERTY, mTaskbarViewInApp.getScaleX(),
                 getTaskbarScaleOnHome(), LINEAR);
 
         anim.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationStart(Animator animation) {
                 mIsAnimatingToLauncher = true;
-                mTaskbarView.updateHotseatItemsVisibility();
+                mTaskbarViewInApp.updateHotseatItemsVisibility();
             }
 
             @Override
             public void onAnimationEnd(Animator animation) {
                 mIsAnimatingToLauncher = false;
+                updateWhichTaskbarViewIsVisible();
             }
         });
 
-        anim.addOnFrameCallback(this::alignRealHotseatWithTaskbar);
-
         return anim.buildAnim();
     }
 
     private Animator createAnimToApp(long duration) {
         PendingAnimation anim = new PendingAnimation(duration);
         anim.add(mTaskbarVisibilityController.createAnimToBackgroundAlpha(1, duration));
-        anim.addFloat(mTaskbarView, SCALE_PROPERTY, mTaskbarView.getScaleX(), 1f, LINEAR);
+        anim.addFloat(mTaskbarViewInApp, SCALE_PROPERTY, mTaskbarViewInApp.getScaleX(), 1f, LINEAR);
         anim.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationStart(Animator animation) {
-                mTaskbarView.updateHotseatItemsVisibility();
-                setReplaceTaskbarWithHotseat(false);
+                mIsAnimatingToApp = true;
+                mTaskbarViewInApp.updateHotseatItemsVisibility();
+                updateWhichTaskbarViewIsVisible();
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mIsAnimatingToApp = false;
             }
         });
         return anim.buildAnim();
@@ -405,11 +431,11 @@
      * @return Whether any Taskbar item could handle the given MotionEvent if given the chance.
      */
     public boolean isEventOverAnyTaskbarItem(MotionEvent ev) {
-        return mTaskbarView.isEventOverAnyItem(ev);
+        return mTaskbarViewInApp.isEventOverAnyItem(ev);
     }
 
     public boolean isDraggingItem() {
-        return mTaskbarView.isDraggingItem();
+        return mTaskbarViewInApp.isDraggingItem() || mTaskbarViewOnHome.isDraggingItem();
     }
 
     private void dedupeAndUpdateRecentItems() {
@@ -454,7 +480,8 @@
             tasksArray[tasksArray.length - 1 - i] = task;
         }
 
-        mTaskbarView.updateRecentTasks(tasksArray);
+        mTaskbarViewInApp.updateRecentTasks(tasksArray);
+        mTaskbarViewOnHome.updateRecentTasks(tasksArray);
         mRecentsController.loadIconsForTasks(tasksArray);
     }
 
@@ -475,10 +502,11 @@
         int hotseatHeight = grid.workspacePadding.bottom + grid.taskbarSize;
         int hotseatTopDiff = hotseatHeight - grid.taskbarSize;
 
-        mTaskbarView.getHotseatBoundsAtScale(getTaskbarScaleOnHome()).roundOut(hotseatBounds);
-        mLauncher.getHotseat().setPadding(hotseatBounds.left, hotseatBounds.top + hotseatTopDiff,
-                mTaskbarView.getWidth() - hotseatBounds.right,
-                mTaskbarView.getHeight() - hotseatBounds.bottom);
+        mTaskbarViewOnHome.getHotseatBounds().roundOut(hotseatBounds);
+        mLauncher.getHotseat().setPadding(hotseatBounds.left,
+                hotseatBounds.top + hotseatTopDiff,
+                mTaskbarViewOnHome.getWidth() - hotseatBounds.right,
+                mTaskbarViewOnHome.getHeight() - hotseatBounds.bottom);
     }
 
     /**
@@ -486,36 +514,32 @@
      * show the real one instead.
      */
     public void onLauncherDragLayerHierarchyChanged() {
-        replaceTaskbarWithHotseatOrViceVersa();
+        // TODO: remove, as this is a no-op now
     }
 
-    private void replaceTaskbarWithHotseatOrViceVersa() {
-        boolean replaceTaskbarWithHotseat = AbstractFloatingView.getTopOpenViewWithType(mLauncher,
-                TYPE_REPLACE_TASKBAR_WITH_HOTSEAT) != null;
-        if (!mLauncher.hasBeenResumed()) {
-            replaceTaskbarWithHotseat = false;
-        }
-        setReplaceTaskbarWithHotseat(replaceTaskbarWithHotseat);
-
-        boolean hideTaskbar = AbstractFloatingView.getTopOpenViewWithType(mLauncher,
-                TYPE_HIDE_TASKBAR) != null;
-        mTaskbarVisibilityController.animateToVisibilityForFloatingView(hideTaskbar ? 0f : 1f);
-    }
-
-    private void setReplaceTaskbarWithHotseat(boolean replaceTaskbarWithHotseat) {
-        Hotseat hotseat = mLauncher.getHotseat();
-        if (replaceTaskbarWithHotseat) {
-            alignRealHotseatWithTaskbar();
-            hotseat.getReplaceTaskbarAlpha().setValue(1f);
-            mTaskbarView.setHotseatViewsHidden(true);
+    private void updateWhichTaskbarViewIsVisible() {
+        boolean isInApp = !mLauncher.hasBeenResumed() || mIsAnimatingToLauncher
+                || mIsAnimatingToApp;
+        if (isInApp) {
+            mTaskbarViewInApp.setVisibility(View.VISIBLE);
+            mTaskbarViewOnHome.setVisibility(View.INVISIBLE);
+            mLauncher.getHotseat().setIconsAlpha(0);
         } else {
-            hotseat.getReplaceTaskbarAlpha().setValue(0f);
-            mTaskbarView.setHotseatViewsHidden(false);
+            mTaskbarViewInApp.setVisibility(View.INVISIBLE);
+            mTaskbarViewOnHome.setVisibility(View.VISIBLE);
+            mLauncher.getHotseat().setIconsAlpha(1);
         }
     }
 
-    private float getTaskbarScaleOnHome() {
-        return 1f / mTaskbarContainerView.getTaskbarActivityContext().getTaskbarIconScale();
+    /**
+     * Returns the ratio of the taskbar icon size on home vs in an app.
+     */
+    public float getTaskbarScaleOnHome() {
+        DeviceProfile inAppDp = mTaskbarContainerView.getTaskbarActivityContext()
+                .getDeviceProfile();
+        DeviceProfile onHomeDp = ActivityContext.lookupContext(mTaskbarViewOnHome.getContext())
+                .getDeviceProfile();
+        return (float) onHomeDp.cellWidthPx / inAppDp.cellWidthPx;
     }
 
     /**
@@ -561,7 +585,10 @@
     protected interface TaskbarViewCallbacks {
         View.OnClickListener getItemOnClickListener();
         View.OnLongClickListener getItemOnLongClickListener();
-        int getEmptyHotseatViewVisibility();
+        int getEmptyHotseatViewVisibility(TaskbarView taskbarView);
+        /** Returns how much to scale non-icon elements such as spacing and dividers. */
+        float getNonIconScale(TaskbarView taskbarView);
+        void onItemPositionsChanged(TaskbarView taskbarView);
     }
 
     /**
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index 1d762e9..3567c17 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -15,11 +15,14 @@
  */
 package com.android.launcher3.taskbar;
 
+import android.animation.Animator;
+import android.animation.AnimatorSet;
 import android.animation.LayoutTransition;
+import android.animation.ValueAnimator;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Canvas;
-import android.graphics.Matrix;
+import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
@@ -36,6 +39,7 @@
 
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.Insettable;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.folder.FolderIcon;
@@ -48,20 +52,23 @@
 /**
  * Hosts the Taskbar content such as Hotseat and Recent Apps. Drawn on top of other apps.
  */
-public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconParent {
+public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconParent, Insettable {
 
     private final ColorDrawable mBackgroundDrawable;
-    private final int mItemMarginLeftRight;
+    private final int mDividerWidth;
+    private final int mDividerHeight;
     private final int mIconTouchSize;
     private final boolean mIsRtl;
     private final int mTouchSlop;
     private final RectF mTempDelegateBounds = new RectF();
     private final RectF mDelegateSlopBounds = new RectF();
     private final int[] mTempOutLocation = new int[2];
-    private final Matrix mTempMatrix = new Matrix();
 
     // Initialized in TaskbarController constructor.
     private TaskbarController.TaskbarViewCallbacks mControllerCallbacks;
+    // Scale on elements that aren't icons.
+    private float mNonIconScale;
+    private int mItemMarginLeftRight;
 
     // Initialized in init().
     private LayoutTransition mLayoutTransition;
@@ -78,7 +85,6 @@
     private boolean mIsDraggingItem;
     // Only non-null when the corresponding Folder is open.
     private @Nullable FolderIcon mLeaveBehindFolderIcon;
-    private boolean mIsHotseatHidden;
 
     public TaskbarView(@NonNull Context context) {
         this(context, null);
@@ -99,7 +105,8 @@
 
         Resources resources = getResources();
         mBackgroundDrawable = (ColorDrawable) getBackground();
-        mItemMarginLeftRight = resources.getDimensionPixelSize(R.dimen.taskbar_icon_spacing);
+        mDividerWidth = resources.getDimensionPixelSize(R.dimen.taskbar_divider_thickness);
+        mDividerHeight = resources.getDimensionPixelSize(R.dimen.taskbar_divider_height);
         mIconTouchSize = resources.getDimensionPixelSize(R.dimen.taskbar_icon_touch_size);
         mIsRtl = Utilities.isRtl(resources);
         mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
@@ -107,11 +114,16 @@
 
     protected void construct(TaskbarController.TaskbarViewCallbacks taskbarViewCallbacks) {
         mControllerCallbacks = taskbarViewCallbacks;
+        mNonIconScale = mControllerCallbacks.getNonIconScale(this);
+        mItemMarginLeftRight = getResources().getDimensionPixelSize(R.dimen.taskbar_icon_spacing);
+        mItemMarginLeftRight = Math.round(mItemMarginLeftRight * mNonIconScale);
     }
 
     protected void init(int numHotseatIcons, int numRecentIcons) {
         mLayoutTransition = new LayoutTransition();
-        setLayoutTransitionsEnabled(true);
+        addUpdateListenerForAllLayoutTransitions(
+                () -> mControllerCallbacks.onItemPositionsChanged(this));
+        setLayoutTransition(mLayoutTransition);
 
         mHotseatStartIndex = 0;
         mHotseatEndIndex = mHotseatStartIndex + numHotseatIcons - 1;
@@ -125,12 +137,30 @@
         updateRecentTasks(new Task[numRecentIcons]);
     }
 
-    private void setLayoutTransitionsEnabled(boolean enabled) {
-        setLayoutTransition(enabled ? mLayoutTransition : null);
+    private void addUpdateListenerForAllLayoutTransitions(Runnable onUpdate) {
+        addUpdateListenerForLayoutTransition(LayoutTransition.CHANGE_APPEARING, onUpdate);
+        addUpdateListenerForLayoutTransition(LayoutTransition.CHANGE_DISAPPEARING, onUpdate);
+        addUpdateListenerForLayoutTransition(LayoutTransition.CHANGING, onUpdate);
+        addUpdateListenerForLayoutTransition(LayoutTransition.APPEARING, onUpdate);
+        addUpdateListenerForLayoutTransition(LayoutTransition.DISAPPEARING, onUpdate);
+    }
+
+    private void addUpdateListenerForLayoutTransition(int transitionType, Runnable onUpdate) {
+        Animator anim = mLayoutTransition.getAnimator(transitionType);
+        if (anim instanceof ValueAnimator) {
+            ((ValueAnimator) anim).addUpdateListener(valueAnimator -> onUpdate.run());
+        } else {
+            AnimatorSet animSet = new AnimatorSet();
+            ValueAnimator updateAnim = ValueAnimator.ofFloat(0, 1);
+            updateAnim.addUpdateListener(valueAnimator -> onUpdate.run());
+            animSet.playTogether(anim, updateAnim);
+            mLayoutTransition.setAnimator(transitionType, animSet);
+        }
     }
 
     protected void cleanup() {
         removeAllViews();
+        mHotseatRecentsDivider = null;
     }
 
     /**
@@ -170,12 +200,11 @@
             if (hotseatView == null || hotseatView.getSourceLayoutResId() != expectedLayoutResId
                     || needsReinflate) {
                 removeView(hotseatView);
-                TaskbarActivityContext activityContext =
-                        ActivityContext.lookupContext(getContext());
+                ActivityContext activityContext = ActivityContext.lookupContext(getContext());
                 if (isFolder) {
                     FolderInfo folderInfo = (FolderInfo) hotseatItemInfo;
                     FolderIcon folderIcon = FolderIcon.inflateFolderAndIcon(expectedLayoutResId,
-                            activityContext, this, folderInfo);
+                            ActivityContext.lookupContext(getContext()), this, folderInfo);
                     folderIcon.setTextVisible(false);
                     hotseatView = folderIcon;
                 } else {
@@ -216,22 +245,12 @@
         }
     }
 
-    /**
-     * Hides or shows the hotseat items immediately (without layout transitions).
-     */
-    protected void setHotseatViewsHidden(boolean hidden) {
-        mIsHotseatHidden = hidden;
-        setLayoutTransitionsEnabled(false);
-        updateHotseatItemsVisibility();
-        setLayoutTransitionsEnabled(true);
-    }
-
     private void updateHotseatItemVisibility(View hotseatView) {
         if (hotseatView.getTag() != null) {
-            hotseatView.setVisibility(mIsHotseatHidden ? INVISIBLE : VISIBLE);
+            hotseatView.setVisibility(VISIBLE);
         } else {
             int oldVisibility = hotseatView.getVisibility();
-            int newVisibility = mControllerCallbacks.getEmptyHotseatViewVisibility();
+            int newVisibility = mControllerCallbacks.getEmptyHotseatViewVisibility(this);
             hotseatView.setVisibility(newVisibility);
             if (oldVisibility == GONE && newVisibility != GONE) {
                 // By default, the layout transition only runs when going to VISIBLE,
@@ -243,7 +262,11 @@
 
     private View addDivider(int dividerIndex) {
         View divider = inflate(R.layout.taskbar_divider);
-        addView(divider, dividerIndex);
+        LayoutParams lp = new LayoutParams(mDividerWidth, mDividerHeight);
+        lp.setMargins(mItemMarginLeftRight, 0, mItemMarginLeftRight, 0);
+        divider.setScaleX(mNonIconScale);
+        divider.setScaleY(mNonIconScale);
+        addView(divider, dividerIndex, lp);
         return divider;
     }
 
@@ -437,9 +460,9 @@
     }
 
     /**
-     * @return The bounding box of where the hotseat elements will be when we reach the given scale.
+     * @return The bounding box of where the hotseat elements are relative to this TaskbarView.
      */
-    protected RectF getHotseatBoundsAtScale(float taskbarViewScale) {
+    protected RectF getHotseatBounds() {
         View firstHotseatView = null, lastHotseatView = null;
         for (int i = mHotseatStartIndex; i <= mHotseatEndIndex; i++) {
             View child = getChildAt(i);
@@ -455,14 +478,11 @@
         }
         View leftmostHotseatView = !mIsRtl ? firstHotseatView : lastHotseatView;
         View rightmostHotseatView = !mIsRtl ? lastHotseatView : firstHotseatView;
-        RectF hotseatBounds = new RectF(
+        return new RectF(
                 leftmostHotseatView.getLeft() - mItemMarginLeftRight,
                 leftmostHotseatView.getTop(),
                 rightmostHotseatView.getRight() + mItemMarginLeftRight,
                 rightmostHotseatView.getBottom());
-        mTempMatrix.setScale(taskbarViewScale, taskbarViewScale, getPivotX(), getPivotY());
-        mTempMatrix.mapRect(hotseatBounds);
-        return hotseatBounds;
     }
 
     // FolderIconParent implemented methods.
@@ -493,7 +513,12 @@
     }
 
     private View inflate(@LayoutRes int layoutResId) {
-        TaskbarActivityContext taskbarActivityContext = ActivityContext.lookupContext(getContext());
-        return taskbarActivityContext.getLayoutInflater().inflate(layoutResId, this, false);
+        return ActivityContext.lookupContext(getContext()).getLayoutInflater()
+                .inflate(layoutResId, this, false);
+    }
+
+    @Override
+    public void setInsets(Rect insets) {
+        // Ignore, we just implement Insettable to draw behind system insets.
     }
 }
diff --git a/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java b/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
index ae644cd..3faf72a 100644
--- a/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
+++ b/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
@@ -112,20 +112,32 @@
         }
 
         // Set up springs for the hotseat and qsb.
-        ViewGroup hotseatChild = (ViewGroup) hotseat.getChildAt(0);
+        ViewGroup hotseatIcons = hotseat.getShortcutsAndWidgets();
         if (grid.isVerticalBarLayout()) {
-            for (int i = hotseatChild.getChildCount() - 1; i >= 0; i--) {
-                View child = hotseatChild.getChildAt(i);
+            for (int i = hotseatIcons.getChildCount() - 1; i >= 0; i--) {
+                View child = hotseatIcons.getChildAt(i);
                 CellLayout.LayoutParams lp = ((CellLayout.LayoutParams) child.getLayoutParams());
                 addStaggeredAnimationForView(child, lp.cellY + 1, totalRows);
             }
         } else {
-            for (int i = hotseatChild.getChildCount() - 1; i >= 0; i--) {
-                View child = hotseatChild.getChildAt(i);
-                addStaggeredAnimationForView(child, grid.inv.numRows + 1, totalRows);
+            final int hotseatRow, qsbRow, taskbarRow;
+            if (grid.isTaskbarPresent) {
+                qsbRow = grid.inv.numRows + 1;
+                hotseatRow = grid.inv.numRows + 2;
+            } else {
+                hotseatRow = grid.inv.numRows + 1;
+                qsbRow = grid.inv.numRows + 2;
+            }
+            // Taskbar and hotseat overlap.
+            taskbarRow = hotseatRow;
+
+            for (int i = hotseatIcons.getChildCount() - 1; i >= 0; i--) {
+                View child = hotseatIcons.getChildAt(i);
+                addStaggeredAnimationForView(child, hotseatRow, totalRows);
             }
 
-            addStaggeredAnimationForView(hotseat.getQsb(), grid.inv.numRows + 2, totalRows);
+            addStaggeredAnimationForView(hotseat.getQsb(), qsbRow, totalRows);
+            addStaggeredAnimationForView(hotseat.getTaskbarView(), taskbarRow, totalRows);
         }
 
         if (animateOverviewScrim) {
diff --git a/res/layout/taskbar_view.xml b/res/layout/taskbar_view.xml
new file mode 100644
index 0000000..96ae43d
--- /dev/null
+++ b/res/layout/taskbar_view.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2021 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.
+-->
+<Space
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="0dp"
+    android:layout_height="0dp"
+    android:visibility="gone" />
\ No newline at end of file
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index af4a843..4049ed6 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -29,8 +29,6 @@
 
 import androidx.annotation.Nullable;
 
-import com.android.launcher3.util.MultiValueAlpha;
-
 import java.util.function.Consumer;
 
 /**
@@ -38,10 +36,6 @@
  */
 public class Hotseat extends CellLayout implements Insettable {
 
-    private static final int ALPHA_INDEX_STATE = 0;
-    private static final int ALPHA_INDEX_REPLACE_TASKBAR = 1;
-    private static final int NUM_ALPHA_CHANNELS = 2;
-
     // Ratio of empty space, qsb should take up to appear visually centered.
     public static final float QSB_CENTER_FACTOR = .325f;
 
@@ -52,10 +46,12 @@
     @Nullable
     private Consumer<Boolean> mOnVisibilityAggregatedCallback;
 
-    private final MultiValueAlpha mMultiValueAlpha;
     private final View mQsb;
     private final int mQsbHeight;
 
+    private final View mTaskbarView;
+    private final int mTaskbarViewHeight;
+
     public Hotseat(Context context) {
         this(context, null);
     }
@@ -66,12 +62,15 @@
 
     public Hotseat(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
-        mMultiValueAlpha = new MultiValueAlpha(this, NUM_ALPHA_CHANNELS, MultiValueAlpha.Mode.MAX);
-        mMultiValueAlpha.setUpdateVisibility(true);
 
         mQsb = LayoutInflater.from(context).inflate(R.layout.search_container_hotseat, this, false);
         mQsbHeight = mQsb.getLayoutParams().height;
         addView(mQsb);
+
+        mTaskbarView = LayoutInflater.from(context).inflate(R.layout.taskbar_view, this, false);
+        mTaskbarViewHeight = mTaskbarView.getLayoutParams().height;
+        // We want taskbar in the back so its background applies to Hotseat as well.
+        addView(mTaskbarView, 0);
     }
 
     /**
@@ -193,6 +192,8 @@
         int width = getShortcutsAndWidgets().getMeasuredWidth();
         mQsb.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
                 MeasureSpec.makeMeasureSpec(mQsbHeight, MeasureSpec.EXACTLY));
+        mTaskbarView.measure(MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY),
+                MeasureSpec.makeMeasureSpec(mTaskbarViewHeight, MeasureSpec.EXACTLY));
     }
 
     @Override
@@ -212,6 +213,13 @@
                 - (dp.isTaskbarPresent ? dp.taskbarSize : dp.getInsets().bottom);
         int top = bottom - mQsbHeight;
         mQsb.layout(left, top, right, bottom);
+
+        int taskbarWidth = mTaskbarView.getMeasuredWidth();
+        left = (r - l - taskbarWidth) / 2;
+        right = left + taskbarWidth;
+        bottom = b - t;
+        top = bottom - mTaskbarViewHeight;
+        mTaskbarView.layout(left, top, right, bottom);
     }
 
     /**
@@ -221,12 +229,11 @@
         return mWorkspace.getFirstMatch(new CellLayout[] { this }, itemOperator);
     }
 
-    public MultiValueAlpha.AlphaProperty getStateAlpha() {
-        return mMultiValueAlpha.getProperty(ALPHA_INDEX_STATE);
-    }
-
-    public MultiValueAlpha.AlphaProperty getReplaceTaskbarAlpha() {
-        return mMultiValueAlpha.getProperty(ALPHA_INDEX_REPLACE_TASKBAR);
+    /**
+     * Sets the alpha value of just our ShortcutAndWidgetContainer.
+     */
+    public void setIconsAlpha(float alpha) {
+        getShortcutsAndWidgets().setAlpha(alpha);
     }
 
     /**
@@ -235,4 +242,11 @@
     public View getQsb() {
         return mQsb;
     }
+
+    /**
+     * Returns the Taskbar inside hotseat
+     */
+    public View getTaskbarView() {
+        return mTaskbarView;
+    }
 }
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index e9a3495..0f8c6b9 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -185,12 +185,7 @@
     }
 
     public int getVisibleElements(Launcher launcher) {
-        DeviceProfile deviceProfile = launcher.getDeviceProfile();
-        int flags = WORKSPACE_PAGE_INDICATOR | VERTICAL_SWIPE_INDICATOR | TASKBAR;
-        if (!deviceProfile.isTaskbarPresent) {
-            flags |= HOTSEAT_ICONS;
-        }
-        return flags;
+        return HOTSEAT_ICONS | WORKSPACE_PAGE_INDICATOR | VERTICAL_SWIPE_INDICATOR | TASKBAR;
     }
 
     /**
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index 412754e..cfcf0a0 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -54,7 +54,6 @@
 import com.android.launcher3.graphics.WorkspaceDragScrim;
 import com.android.launcher3.states.StateAnimationConfig;
 import com.android.launcher3.util.DynamicResource;
-import com.android.launcher3.util.MultiValueAlpha;
 import com.android.systemui.plugins.ResourceProvider;
 
 /**
@@ -135,8 +134,8 @@
             }
 
             float hotseatIconsAlpha = (elements & HOTSEAT_ICONS) != 0 ? 1 : 0;
-            propertySetter.setFloat(hotseat.getStateAlpha(), MultiValueAlpha.VALUE,
-                    hotseatIconsAlpha, config.getInterpolator(ANIM_HOTSEAT_FADE, fadeInterpolator));
+            propertySetter.setViewAlpha(hotseat, hotseatIconsAlpha,
+                    config.getInterpolator(ANIM_HOTSEAT_FADE, fadeInterpolator));
             float workspacePageIndicatorAlpha = (elements & WORKSPACE_PAGE_INDICATOR) != 0 ? 1 : 0;
             propertySetter.setViewAlpha(mLauncher.getWorkspace().getPageIndicator(),
                     workspacePageIndicatorAlpha, fadeInterpolator);
diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java
index 505c6ce..71aa4ac 100644
--- a/src/com/android/launcher3/views/ActivityContext.java
+++ b/src/com/android/launcher3/views/ActivityContext.java
@@ -18,6 +18,7 @@
 import android.content.Context;
 import android.content.ContextWrapper;
 import android.graphics.Rect;
+import android.view.LayoutInflater;
 import android.view.View.AccessibilityDelegate;
 
 import com.android.launcher3.DeviceProfile;
@@ -67,13 +68,28 @@
     }
 
     /**
+     * Returns a LayoutInflater that is cloned in this Context, so that Views inflated by it will
+     * have the same Context. (i.e. {@link #lookupContext(Context)} will find this ActivityContext.)
+     */
+    default LayoutInflater getLayoutInflater() {
+        if (this instanceof Context) {
+            Context context = (Context) this;
+            return LayoutInflater.from(context).cloneInContext(context);
+        }
+        return null;
+    }
+
+    /**
      * The root view to support drag-and-drop and popup support.
      */
     BaseDragLayer getDragLayer();
 
     DeviceProfile getDeviceProfile();
 
-    static <T extends ActivityContext> T lookupContext(Context context) {
+    /**
+     * Returns the ActivityContext associated with the given Context.
+     */
+    static <T extends Context & ActivityContext> T lookupContext(Context context) {
         if (context instanceof ActivityContext) {
             return (T) context;
         } else if (context instanceof ContextWrapper) {