Reorganize taskbar controllers

Organize existing properties as follows:
- TaskbarViewController contains properties affecting TaskbarView (though child icons are still supplied by TaskbarHotseatController)
- TaskbarDragLayerController contains properties related to TaskbarDragLayer itself
- Renamed NavbarButtonUiController to NavbarButtonsViewController, following the pattern of TaskbarViewController and TaskbarDragLayerController
- TaskbarControllers contains the different controllers to make it easier to construct, initialize, destroy, and pass them around
- Removed TaskbarIconController as its responsibilities were moved to more specific controllers

Test: compiles and runs, manually tested
Bug: 187353581
Change-Id: Idccd95d47117101bf9617e5532a5b87635d2b8f6
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index 79af7cc..9a836aa 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -16,9 +16,12 @@
 package com.android.launcher3.taskbar;
 
 import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.taskbar.TaskbarViewController.ALPHA_INDEX_HOME;
+import static com.android.launcher3.taskbar.TaskbarViewController.ALPHA_INDEX_LAUNCHER_STATE;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
+import android.graphics.Rect;
 import android.view.MotionEvent;
 
 import androidx.annotation.NonNull;
@@ -30,6 +33,7 @@
 import com.android.launcher3.R;
 import com.android.launcher3.anim.AnimatorListeners;
 import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.util.MultiValueAlpha;
 import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
 import com.android.quickstep.AnimatedFloat;
 
@@ -47,7 +51,7 @@
     final TaskbarDragLayer mTaskbarDragLayer;
     final TaskbarView mTaskbarView;
 
-    private AnimatedFloat mTaskBarAlpha;
+    private AnimatedFloat mTaskbarBackgroundAlpha;
     private AlphaProperty mIconAlphaForHome;
     private @Nullable Animator mAnimator;
     private boolean mIsAnimatingToLauncher;
@@ -66,11 +70,14 @@
     }
 
     @Override
-    protected void init(AnimatedFloat taskBarAlpha, AlphaProperty iconAlphaForLauncherState,
-            AlphaProperty iconAlphaForHome) {
-        mTaskBarAlpha = taskBarAlpha;
-        mIconAlphaForHome = iconAlphaForHome;
-        mTaskbarStateHandler.setAnimationController(iconAlphaForLauncherState);
+    protected void init(TaskbarControllers taskbarControllers) {
+        mTaskbarBackgroundAlpha = taskbarControllers.taskbarDragLayerController
+                .getTaskbarBackgroundAlpha();
+        MultiValueAlpha taskbarIconAlpha = taskbarControllers.taskbarViewController
+                .getTaskbarIconAlpha();
+        mIconAlphaForHome = taskbarIconAlpha.getProperty(ALPHA_INDEX_HOME);
+        mTaskbarStateHandler.setAnimationController(taskbarIconAlpha.getProperty(
+                ALPHA_INDEX_LAUNCHER_STATE));
         mHotseatController.init();
         setTaskbarViewVisible(!mLauncher.hasBeenResumed());
         mLauncher.setTaskbarUIController(this);
@@ -94,6 +101,18 @@
         return !mIsAnimatingToLauncher;
     }
 
+    @Override
+    protected void updateContentInsets(Rect outContentInsets) {
+        // TaskbarDragLayer provides insets to other apps based on contentInsets. These
+        // insets should stay consistent even if we expand TaskbarDragLayer's bounds, e.g.
+        // to show a floating view like Folder. Thus, we set the contentInsets to be where
+        // mTaskbarView is, since its position never changes and insets rather than overlays.
+        outContentInsets.left = mTaskbarView.getLeft();
+        outContentInsets.top = mTaskbarView.getTop();
+        outContentInsets.right = mTaskbarDragLayer.getWidth() - mTaskbarView.getRight();
+        outContentInsets.bottom = mTaskbarDragLayer.getHeight() - mTaskbarView.getBottom();
+    }
+
     /**
      * Should be called from onResume() and onPause(), and animates the Taskbar accordingly.
      */
@@ -125,7 +144,7 @@
         PendingAnimation anim = new PendingAnimation(duration);
         mTaskbarStateHandler.setState(toState, anim);
 
-        anim.setFloat(mTaskBarAlpha, AnimatedFloat.VALUE, 0, LINEAR);
+        anim.setFloat(mTaskbarBackgroundAlpha, AnimatedFloat.VALUE, 0, LINEAR);
         mTaskbarView.alignIconsWithLauncher(mLauncher.getDeviceProfile(), anim);
 
         anim.addListener(new AnimatorListenerAdapter() {
@@ -146,7 +165,7 @@
 
     private Animator createAnimToApp(long duration) {
         PendingAnimation anim = new PendingAnimation(duration);
-        anim.setFloat(mTaskBarAlpha, AnimatedFloat.VALUE, 1, LINEAR);
+        anim.setFloat(mTaskbarBackgroundAlpha, AnimatedFloat.VALUE, 1, LINEAR);
         anim.addListener(AnimatorListeners.forEndCallback(mTaskbarView.resetIconPosition(anim)));
         anim.addListener(new AnimatorListenerAdapter() {
             @Override
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonUIController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
similarity index 86%
rename from quickstep/src/com/android/launcher3/taskbar/NavbarButtonUIController.java
rename to quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index dc292a1..2ae7d10 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -21,6 +21,7 @@
 import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_HOME;
 import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_IME_SWITCH;
 import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_RECENTS;
+import static com.android.launcher3.taskbar.TaskbarViewController.ALPHA_INDEX_IME;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
@@ -48,7 +49,6 @@
 import com.android.launcher3.taskbar.contextual.RotationButton;
 import com.android.launcher3.taskbar.contextual.RotationButtonController;
 import com.android.launcher3.util.MultiValueAlpha;
-import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
 import com.android.quickstep.AnimatedFloat;
 
 import java.util.ArrayList;
@@ -57,7 +57,7 @@
 /**
  * Controller for managing nav bar buttons in taskbar
  */
-public class NavbarButtonUIController {
+public class NavbarButtonsViewController {
 
     private final Rect mTempRect = new Rect();
 
@@ -74,48 +74,53 @@
     private int mState;
 
     private final TaskbarActivityContext mContext;
-    private View a11yButton;
+    private final FrameLayout mNavButtonsView;
+    private final ViewGroup mStartContainer;
+    private final ViewGroup mEndContainer;
+
+    // Initialized in init.
+    private TaskbarControllers mControllers;
+    private View mA11yButton;
     private int mSysuiStateFlags;
 
-    public NavbarButtonUIController(TaskbarActivityContext context) {
+    public NavbarButtonsViewController(TaskbarActivityContext context, FrameLayout navButtonsView) {
         mContext = context;
+        mNavButtonsView = navButtonsView;
+        mStartContainer = mNavButtonsView.findViewById(R.id.start_nav_buttons);
+        mEndContainer = mNavButtonsView.findViewById(R.id.end_nav_buttons);
     }
 
     /**
      * Initializes the controller
      */
-    public void init(TaskbarDragLayer dragLayer,
-            TaskbarNavButtonController navButtonController,
-            RotationButtonController rotationButtonController,
-            AnimatedFloat taskbarBackgroundAlpha, AlphaProperty taskbarIconAlpha) {
-        FrameLayout buttonController = dragLayer.findViewById(R.id.navbuttons_view);
-        buttonController.getLayoutParams().height = mContext.getDeviceProfile().taskbarSize;
+    public void init(TaskbarControllers controllers) {
+        mControllers = controllers;
+        mNavButtonsView.getLayoutParams().height = mContext.getDeviceProfile().taskbarSize;
 
         mA11yLongClickListener = view -> {
-            navButtonController.onButtonClick(BUTTON_A11Y_LONG_CLICK);
+            mControllers.navButtonController.onButtonClick(BUTTON_A11Y_LONG_CLICK);
             return true;
         };
 
         if (mContext.canShowNavButtons()) {
-            ViewGroup startContainer = buttonController.findViewById(R.id.start_nav_buttons);
-            ViewGroup endContainer = buttonController.findViewById(R.id.end_nav_buttons);
-
-            initButtons(startContainer, endContainer, navButtonController);
+            initButtons(mStartContainer, mEndContainer, mControllers.navButtonController);
 
             // Animate taskbar background when IME shows
-            mPropertyHolders.add(new StatePropertyHolder(taskbarBackgroundAlpha,
+            mPropertyHolders.add(new StatePropertyHolder(
+                    mControllers.taskbarDragLayerController.getNavbarBackgroundAlpha(),
                     flags -> (flags & FLAG_IME_VISIBLE) == 0,
                     AnimatedFloat.VALUE, 0, 1));
             mPropertyHolders.add(new StatePropertyHolder(
-                    taskbarIconAlpha, flags -> (flags & FLAG_IME_VISIBLE) == 0,
-                    MultiValueAlpha.VALUE, 1, 0));
+                    mControllers.taskbarViewController.getTaskbarIconAlpha()
+                            .getProperty(ALPHA_INDEX_IME),
+                    flags -> (flags & FLAG_IME_VISIBLE) == 0, MultiValueAlpha.VALUE, 1, 0));
 
             // Rotation button
-            RotationButton rotationButton = new RotationButtonImpl(addButton(endContainer));
+            RotationButton rotationButton = new RotationButtonImpl(addButton(mEndContainer));
             rotationButton.hide();
-            rotationButtonController.setRotationButton(rotationButton);
+            mControllers.rotationButtonController.setRotationButton(rotationButton);
         } else {
-            rotationButtonController.setRotationButton(new RotationButton() { });
+            mControllers.rotationButtonController.setRotationButton(new RotationButton() {});
         }
 
         applyState();
@@ -151,12 +156,12 @@
                         && ((flags & FLAG_A11Y_VISIBLE) == 0)));
 
         // A11y button
-        a11yButton = addButton(R.drawable.ic_sysbar_accessibility_button, BUTTON_A11Y,
+        mA11yButton = addButton(R.drawable.ic_sysbar_accessibility_button, BUTTON_A11Y,
                 endContainer, navButtonController);
-        mPropertyHolders.add(new StatePropertyHolder(a11yButton,
+        mPropertyHolders.add(new StatePropertyHolder(mA11yButton,
                 flags -> (flags & FLAG_A11Y_VISIBLE) != 0
                         && (flags & FLAG_ROTATION_BUTTON_VISIBLE) == 0));
-        a11yButton.setOnLongClickListener(mA11yLongClickListener);
+        mA11yButton.setOnLongClickListener(mA11yLongClickListener);
     }
 
     public void updateStateForSysuiFlags(int systemUiStateFlags, boolean forceUpdate) {
@@ -174,7 +179,7 @@
         updateStateForFlag(FLAG_IME_VISIBLE, isImeVisible);
         updateStateForFlag(FLAG_SWITCHER_SUPPORTED, isImeSwitcherShowing);
         updateStateForFlag(FLAG_A11Y_VISIBLE, a11yVisible);
-        a11yButton.setLongClickable(a11yLongClickable);
+        mA11yButton.setLongClickable(a11yLongClickable);
         applyState();
     }
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index a25eb38..6e47700 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -38,6 +38,7 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.WindowManager;
+import android.widget.FrameLayout;
 import android.widget.Toast;
 
 import androidx.annotation.NonNull;
@@ -52,12 +53,10 @@
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.taskbar.contextual.RotationButtonController;
 import com.android.launcher3.touch.ItemClickHandler;
-import com.android.launcher3.util.MultiValueAlpha;
 import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.TraceHelper;
 import com.android.launcher3.views.ActivityContext;
-import com.android.quickstep.AnimatedFloat;
 import com.android.quickstep.SysUINavigationMode;
 import com.android.quickstep.SysUINavigationMode.Mode;
 import com.android.systemui.shared.recents.model.Task;
@@ -77,16 +76,10 @@
 
     private static final String WINDOW_TITLE = "Taskbar";
 
-    private static final int ALPHA_INDEX_HOME = 0;
-    private static final int ALPHA_INDEX_LAUNCHER_STATE = 1;
-    private static final int ALPHA_INDEX_IME = 2;
-
     private final DeviceProfile mDeviceProfile;
     private final LayoutInflater mLayoutInflater;
     private final TaskbarDragLayer mDragLayer;
-    private final TaskbarIconController mIconController;
-    private final TaskbarDragController mDragController;
-    private final NavbarButtonUIController mNavbarButtonUIController;
+    private final TaskbarControllers mControllers;
 
     private final WindowManager mWindowManager;
     private WindowManager.LayoutParams mWindowLayoutParams;
@@ -95,57 +88,46 @@
     private int mLastRequestedNonFullscreenHeight;
 
     private final SysUINavigationMode.Mode mNavMode;
-    private final TaskbarNavButtonController mNavButtonController;
-    private final RotationButtonController mRotationButtonController;
 
     private final boolean mIsSafeModeEnabled;
 
-    @NonNull
-    private TaskbarUIController mUIController = TaskbarUIController.DEFAULT;
-
-    private final View.OnClickListener mOnTaskbarIconClickListener;
-    private final View.OnLongClickListener mOnTaskbarIconLongClickListener;
-
-    // Alpha property for task bar
-    private final AnimatedFloat mBgTaskbar = new AnimatedFloat(this::updateBackgroundAlpha);
-    private final AnimatedFloat mBgNavbar = new AnimatedFloat(this::updateBackgroundAlpha);
-
-    private final MultiValueAlpha mTaskbarIconAlpha;
-
     public TaskbarActivityContext(Context windowContext, DeviceProfile dp,
             TaskbarNavButtonController buttonController) {
         super(windowContext, Themes.getActivityThemeRes(windowContext));
         mDeviceProfile = dp;
-        mNavButtonController = buttonController;
+
         mNavMode = SysUINavigationMode.getMode(windowContext);
         mIsSafeModeEnabled = TraceHelper.allowIpcs("isSafeMode",
                 () -> getPackageManager().isSafeMode());
 
-        mDragController = new TaskbarDragController(this);
-        mOnTaskbarIconLongClickListener = mDragController::startDragOnLongClick;
-        mOnTaskbarIconClickListener = this::onTaskbarIconClicked;
-
         float taskbarIconSize = getResources().getDimension(R.dimen.taskbar_icon_size);
         mDeviceProfile.updateIconSize(1, getResources());
         float iconScale = taskbarIconSize / mDeviceProfile.iconSizePx;
         mDeviceProfile.updateIconSize(iconScale, getResources());
 
         mLayoutInflater = LayoutInflater.from(this).cloneInContext(this);
-        mDragLayer = (TaskbarDragLayer) mLayoutInflater
-                .inflate(R.layout.taskbar, null, false);
-        mRotationButtonController = new RotationButtonController(this,
-                R.color.popup_color_primary_light, R.color.popup_color_primary_light);
-        mNavbarButtonUIController = new NavbarButtonUIController(this);
-        mIconController = new TaskbarIconController(this, mDragLayer, mNavbarButtonUIController);
+
+        // Inflate views.
+        mDragLayer = (TaskbarDragLayer) mLayoutInflater.inflate(
+                R.layout.taskbar, null, false);
+        TaskbarView taskbarView = mDragLayer.findViewById(R.id.taskbar_view);
+        FrameLayout navButtonsView = mDragLayer.findViewById(R.id.navbuttons_view);
+
+        // Construct controllers.
+        mControllers = new TaskbarControllers(this,
+                new TaskbarDragController(this),
+                buttonController,
+                new NavbarButtonsViewController(this, navButtonsView),
+                new RotationButtonController(this, R.color.popup_color_primary_light,
+                        R.color.popup_color_primary_light),
+                new TaskbarDragLayerController(this, mDragLayer),
+                new TaskbarViewController(this, taskbarView));
 
         Display display = windowContext.getDisplay();
         Context c = display.getDisplayId() == Display.DEFAULT_DISPLAY
                 ? windowContext.getApplicationContext()
                 : windowContext.getApplicationContext().createDisplayContext(display);
         mWindowManager = c.getSystemService(WindowManager.class);
-
-        mTaskbarIconAlpha = new MultiValueAlpha(mDragLayer.findViewById(R.id.taskbar_view), 3);
-        mTaskbarIconAlpha.setUpdateVisibility(true);
     }
 
     public void init() {
@@ -170,13 +152,10 @@
                 new int[] { ITYPE_EXTRA_NAVIGATION_BAR, ITYPE_BOTTOM_TAPPABLE_ELEMENT }
         );
 
-        mIconController.init(mOnTaskbarIconClickListener, mOnTaskbarIconLongClickListener);
-        mNavbarButtonUIController.init(mDragLayer, mNavButtonController, mRotationButtonController,
-                mBgNavbar, mTaskbarIconAlpha.getProperty(ALPHA_INDEX_IME));
+        // Initialize controllers after all are constructed.
+        mControllers.init();
+
         mWindowManager.addView(mDragLayer, mWindowLayoutParams);
-        if (canShowNavButtons()) {
-            mRotationButtonController.init();
-        }
     }
 
     public boolean canShowNavButtons() {
@@ -200,23 +179,21 @@
 
     @Override
     public Rect getFolderBoundingBox() {
-        return mDragLayer.getFolderBoundingBox();
+        return mControllers.taskbarDragLayerController.getFolderBoundingBox();
     }
 
     @Override
     public TaskbarDragController getDragController() {
-        return mDragController;
+        return mControllers.taskbarDragController;
     }
 
     /**
      * Sets a new data-source for this taskbar instance
      */
     public void setUIController(@NonNull TaskbarUIController uiController) {
-        mUIController.onDestroy();
-        mUIController = uiController;
-        mIconController.setUIController(mUIController);
-        mUIController.init(mBgTaskbar, mTaskbarIconAlpha.getProperty(ALPHA_INDEX_LAUNCHER_STATE),
-                mTaskbarIconAlpha.getProperty(ALPHA_INDEX_HOME));
+        mControllers.uiController.onDestroy();
+        mControllers.uiController = uiController;
+        mControllers.uiController.init(mControllers);
     }
 
     /**
@@ -224,8 +201,7 @@
      */
     public void onDestroy() {
         setUIController(TaskbarUIController.DEFAULT);
-        mIconController.onDestroy();
-        mRotationButtonController.onDestroy();
+        mControllers.onDestroy();
         mWindowManager.removeViewImmediate(mDragLayer);
     }
 
@@ -233,23 +209,25 @@
         if (!canShowNavButtons()) {
             return;
         }
-        mNavbarButtonUIController.updateStateForSysuiFlags(systemUiStateFlags, forceUpdate);
-        mIconController.setImeIsVisible(mNavbarButtonUIController.isImeVisible());
+        mControllers.navbarButtonsViewController.updateStateForSysuiFlags(
+                systemUiStateFlags, forceUpdate);
+        mControllers.taskbarViewController.setImeIsVisible(
+                mControllers.navbarButtonsViewController.isImeVisible());
     }
 
     public void onRotationProposal(int rotation, boolean isValid) {
-        mRotationButtonController.onRotationProposal(rotation, isValid);
+        mControllers.rotationButtonController.onRotationProposal(rotation, isValid);
     }
 
     public void disable(int displayId, int state1, int state2, boolean animate) {
         if (displayId != getDisplayId()) {
             return;
         }
-        mRotationButtonController.onDisable2FlagChanged(state2);
+        mControllers.rotationButtonController.onDisable2FlagChanged(state2);
     }
 
     public void onSystemBarAttributesChanged(int displayId, int behavior) {
-        mRotationButtonController.onBehaviorChanged(displayId, behavior);
+        mControllers.rotationButtonController.onBehaviorChanged(displayId, behavior);
     }
 
     /**
@@ -296,8 +274,8 @@
                 folder.animateOpen();
 
                 folder.iterateOverItems((itemInfo, itemView) -> {
-                    itemView.setOnClickListener(mOnTaskbarIconClickListener);
-                    itemView.setOnLongClickListener(mOnTaskbarIconLongClickListener);
+                    mControllers.taskbarViewController
+                            .setClickAndLongClickListenersForIcon(itemView);
                     // To play haptic when dragging, like other Taskbar items do.
                     itemView.setHapticFeedbackEnabled(true);
                     return false;
@@ -343,9 +321,4 @@
 
         AbstractFloatingView.closeAllOpenViews(this);
     }
-
-    private void updateBackgroundAlpha() {
-        mDragLayer.setTaskbarBackgroundAlpha(Math.max(mBgNavbar.value, mBgTaskbar.value));
-    }
-
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
new file mode 100644
index 0000000..7712ffe
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+package com.android.launcher3.taskbar;
+
+import androidx.annotation.NonNull;
+
+import com.android.launcher3.taskbar.contextual.RotationButtonController;
+
+/**
+ * Hosts various taskbar controllers to facilitate passing between one another.
+ */
+public class TaskbarControllers {
+
+    public final TaskbarActivityContext taskbarActivityContext;
+    public final TaskbarDragController taskbarDragController;
+    public final TaskbarNavButtonController navButtonController;
+    public final NavbarButtonsViewController navbarButtonsViewController;
+    public final RotationButtonController rotationButtonController;
+    public final TaskbarDragLayerController taskbarDragLayerController;
+    public final TaskbarViewController taskbarViewController;
+
+    /** Do not store this controller, as it may change at runtime. */
+    @NonNull public TaskbarUIController uiController = TaskbarUIController.DEFAULT;
+
+    public TaskbarControllers(TaskbarActivityContext taskbarActivityContext,
+            TaskbarDragController taskbarDragController,
+            TaskbarNavButtonController navButtonController,
+            NavbarButtonsViewController navbarButtonsViewController,
+            RotationButtonController rotationButtonController,
+            TaskbarDragLayerController taskbarDragLayerController,
+            TaskbarViewController taskbarViewController) {
+        this.taskbarActivityContext = taskbarActivityContext;
+        this.taskbarDragController = taskbarDragController;
+        this.navButtonController = navButtonController;
+        this.navbarButtonsViewController = navbarButtonsViewController;
+        this.rotationButtonController = rotationButtonController;
+        this.taskbarDragLayerController = taskbarDragLayerController;
+        this.taskbarViewController = taskbarViewController;
+    }
+
+    /**
+     * Initializes all controllers. Note that controllers can now reference each other through this
+     * TaskbarControllers instance, but should be careful to only access things that were created
+     * in constructors for now, as some controllers may still be waiting for init().
+     */
+    public void init() {
+        navbarButtonsViewController.init(this);
+        if (taskbarActivityContext.canShowNavButtons()) {
+            rotationButtonController.init();
+        }
+        taskbarDragLayerController.init(this);
+        taskbarViewController.init(this);
+    }
+
+    /**
+     * Cleans up all controllers.
+     */
+    public void onDestroy() {
+        uiController.onDestroy();
+        rotationButtonController.onDestroy();
+        taskbarDragLayerController.onDestroy();
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
index 52a2c86..c684543 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
@@ -18,7 +18,6 @@
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Paint;
-import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.view.View;
 
@@ -37,11 +36,9 @@
  */
 public class TaskbarDragLayer extends BaseDragLayer<TaskbarActivityContext> {
 
-    private final int mFolderMargin;
     private final Paint mTaskbarBackgroundPaint;
 
-    private TaskbarIconController.TaskbarDragLayerCallbacks mControllerCallbacks;
-    private TaskbarView mTaskbarView;
+    private TaskbarDragLayerController.TaskbarDragLayerCallbacks mControllerCallbacks;
 
     private final OnComputeInsetsListener mTaskbarInsetsComputer = this::onComputeTaskbarInsets;
 
@@ -61,10 +58,13 @@
     public TaskbarDragLayer(@NonNull Context context, @Nullable AttributeSet attrs,
             int defStyleAttr, int defStyleRes) {
         super(context, attrs, 1 /* alphaChannelCount */);
-        mFolderMargin = getResources().getDimensionPixelSize(R.dimen.taskbar_folder_margin);
         mTaskbarBackgroundPaint = new Paint();
         mTaskbarBackgroundPaint.setColor(getResources().getColor(R.color.taskbar_background));
         mTaskbarBackgroundPaint.setAlpha(0);
+    }
+
+    public void init(TaskbarDragLayerController.TaskbarDragLayerCallbacks callbacks) {
+        mControllerCallbacks = callbacks;
         recreateControllers();
     }
 
@@ -73,15 +73,10 @@
         mControllers = new TouchController[] {mActivity.getDragController()};
     }
 
-    public void init(TaskbarIconController.TaskbarDragLayerCallbacks callbacks,
-            TaskbarView taskbarView) {
-        mControllerCallbacks = callbacks;
-        mTaskbarView = taskbarView;
-    }
-
     private void onComputeTaskbarInsets(InsetsInfo insetsInfo) {
         if (mControllerCallbacks != null) {
             mControllerCallbacks.updateInsetsTouchability(insetsInfo);
+            mControllerCallbacks.updateContentInsets(insetsInfo.contentInsets);
         }
     }
 
@@ -120,21 +115,12 @@
 
     @Override
     protected void dispatchDraw(Canvas canvas) {
-        canvas.drawRect(0, canvas.getHeight() - mTaskbarView.getHeight(), canvas.getWidth(),
-                canvas.getHeight(), mTaskbarBackgroundPaint);
+        canvas.drawRect(0, canvas.getHeight() - mControllerCallbacks.getTaskbarBackgroundHeight(),
+                canvas.getWidth(), canvas.getHeight(), mTaskbarBackgroundPaint);
         super.dispatchDraw(canvas);
     }
 
     /**
-     * @return Bounds (in our coordinates) where an opened Folder can display.
-     */
-    protected Rect getFolderBoundingBox() {
-        Rect boundingBox = new Rect(0, 0, getWidth(), getHeight() - mTaskbarView.getHeight());
-        boundingBox.inset(mFolderMargin, mFolderMargin);
-        return boundingBox;
-    }
-
-    /**
      * Sets the alpha of the background color behind all the Taskbar contents.
      * @param alpha 0 is fully transparent, 1 is fully opaque.
      */
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
new file mode 100644
index 0000000..2efbd4f
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
@@ -0,0 +1,137 @@
+/*
+ * 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.
+ */
+package com.android.launcher3.taskbar;
+
+import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
+import static com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo.TOUCHABLE_INSETS_FRAME;
+import static com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo.TOUCHABLE_INSETS_REGION;
+
+import android.content.res.Resources;
+import android.graphics.Rect;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.R;
+import com.android.launcher3.anim.AlphaUpdateListener;
+import com.android.quickstep.AnimatedFloat;
+import com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo;
+
+/**
+ * Handles properties/data collection, then passes the results to TaskbarDragLayer to render.
+ */
+public class TaskbarDragLayerController {
+
+    private final TaskbarActivityContext mActivity;
+    private final TaskbarDragLayer mTaskbarDragLayer;
+    private final int mFolderMargin;
+    // Alpha properties for taskbar background.
+    private final AnimatedFloat mBgTaskbar = new AnimatedFloat(this::updateBackgroundAlpha);
+    private final AnimatedFloat mBgNavbar = new AnimatedFloat(this::updateBackgroundAlpha);
+
+    // Initialized in init.
+    private TaskbarControllers mControllers;
+
+    public TaskbarDragLayerController(TaskbarActivityContext activity,
+            TaskbarDragLayer taskbarDragLayer) {
+        mActivity = activity;
+        mTaskbarDragLayer = taskbarDragLayer;
+        final Resources resources = mTaskbarDragLayer.getResources();
+        mFolderMargin = resources.getDimensionPixelSize(R.dimen.taskbar_folder_margin);
+    }
+
+    public void init(TaskbarControllers controllers) {
+        mControllers = controllers;
+        mTaskbarDragLayer.init(new TaskbarDragLayerCallbacks());
+    }
+
+    public void onDestroy() {
+        mTaskbarDragLayer.onDestroy();
+    }
+
+    /**
+     * @return Bounds (in TaskbarDragLayer coordinates) where an opened Folder can display.
+     */
+    public Rect getFolderBoundingBox() {
+        Rect boundingBox = new Rect(0, 0, mTaskbarDragLayer.getWidth(),
+                mTaskbarDragLayer.getHeight() - mActivity.getDeviceProfile().taskbarSize);
+        boundingBox.inset(mFolderMargin, mFolderMargin);
+        return boundingBox;
+    }
+
+    public AnimatedFloat getTaskbarBackgroundAlpha() {
+        return mBgTaskbar;
+    }
+
+    public AnimatedFloat getNavbarBackgroundAlpha() {
+        return mBgNavbar;
+    }
+
+    private void updateBackgroundAlpha() {
+        mTaskbarDragLayer.setTaskbarBackgroundAlpha(Math.max(mBgNavbar.value, mBgTaskbar.value));
+    }
+
+    /**
+     * Callbacks for {@link TaskbarDragLayer} to interact with its controller.
+     */
+    public class TaskbarDragLayerCallbacks {
+
+        /**
+         * Called to update the touchable insets.
+         * @see InsetsInfo#setTouchableInsets(int)
+         */
+        public void updateInsetsTouchability(InsetsInfo insetsInfo) {
+            insetsInfo.touchableRegion.setEmpty();
+            if (mTaskbarDragLayer.getAlpha() < AlphaUpdateListener.ALPHA_CUTOFF_THRESHOLD) {
+                // Let touches pass through us.
+                insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
+            } else if (mControllers.navbarButtonsViewController.isImeVisible()) {
+                insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_FRAME);
+            } else if (!mControllers.uiController.isTaskbarTouchable()) {
+                // Let touches pass through us.
+                insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
+            } else if (mControllers.taskbarViewController.areIconsVisible()) {
+                // Buttons are visible, take over the full taskbar area
+                insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_FRAME);
+            } else {
+                mControllers.navbarButtonsViewController.addVisibleButtonsRegion(
+                        mTaskbarDragLayer, insetsInfo.touchableRegion);
+                insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
+            }
+        }
+
+        /**
+         * Called to update the {@link InsetsInfo#contentInsets}.
+         */
+        public void updateContentInsets(Rect outContentInsets) {
+            mControllers.uiController.updateContentInsets(outContentInsets);
+        }
+
+        /**
+         * Called when a child is removed from TaskbarDragLayer.
+         */
+        public void onDragLayerViewRemoved() {
+            if (AbstractFloatingView.getOpenView(mActivity, TYPE_ALL) == null) {
+                mActivity.setTaskbarWindowFullscreen(false);
+            }
+        }
+
+        /**
+         * Returns how tall the background should be drawn at the bottom of the screen.
+         */
+        public int getTaskbarBackgroundHeight() {
+            return mActivity.getDeviceProfile().taskbarSize;
+        }
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarIconController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarIconController.java
deleted file mode 100644
index 7dca19c..0000000
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarIconController.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * 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.
- */
-package com.android.launcher3.taskbar;
-
-import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
-import static com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo.TOUCHABLE_INSETS_FRAME;
-import static com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo.TOUCHABLE_INSETS_REGION;
-
-import android.view.View.OnClickListener;
-import android.view.View.OnLongClickListener;
-
-import androidx.annotation.NonNull;
-
-import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.R;
-import com.android.launcher3.anim.AlphaUpdateListener;
-import com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo;
-
-/**
- * Controller for taskbar icon UI
- */
-public class TaskbarIconController {
-
-    private final TaskbarActivityContext mActivity;
-    private final TaskbarDragLayer mDragLayer;
-    private final NavbarButtonUIController mNavbarButtonUIController;
-
-    private final TaskbarView mTaskbarView;
-
-    @NonNull
-    private TaskbarUIController mUIController = TaskbarUIController.DEFAULT;
-
-    TaskbarIconController(TaskbarActivityContext activity, TaskbarDragLayer dragLayer,
-            NavbarButtonUIController navbarButtonUIController) {
-        mActivity = activity;
-        mDragLayer = dragLayer;
-        mNavbarButtonUIController = navbarButtonUIController;
-        mTaskbarView = mDragLayer.findViewById(R.id.taskbar_view);
-    }
-
-    public void init(OnClickListener clickListener, OnLongClickListener longClickListener) {
-        mTaskbarView.init(clickListener, longClickListener);
-        mTaskbarView.getLayoutParams().height = mActivity.getDeviceProfile().taskbarSize;
-
-        mDragLayer.init(new TaskbarDragLayerCallbacks(), mTaskbarView);
-    }
-
-    public void onDestroy() {
-        mDragLayer.onDestroy();
-    }
-
-    public void setUIController(@NonNull TaskbarUIController uiController) {
-        mUIController = uiController;
-    }
-
-    /**
-     * Should be called when the IME visibility changes, so we can hide/show Taskbar accordingly.
-     */
-    public void setImeIsVisible(boolean isImeVisible) {
-        mTaskbarView.setTouchesEnabled(!isImeVisible);
-    }
-
-    /**
-     * Callbacks for {@link TaskbarDragLayer} to interact with the icon controller
-     */
-    public class TaskbarDragLayerCallbacks {
-
-        /**
-         * Called to update the touchable insets
-         */
-        public void updateInsetsTouchability(InsetsInfo insetsInfo) {
-            insetsInfo.touchableRegion.setEmpty();
-            if (mDragLayer.getAlpha() < AlphaUpdateListener.ALPHA_CUTOFF_THRESHOLD) {
-                // Let touches pass through us.
-                insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
-            } else if (mNavbarButtonUIController.isImeVisible()) {
-                insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_FRAME);
-            } else if (!mUIController.isTaskbarTouchable()) {
-                // Let touches pass through us.
-                insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
-            } else if (mTaskbarView.areIconsVisible()) {
-                // Buttons are visible, take over the full taskbar area
-                insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_FRAME);
-            } else {
-                mNavbarButtonUIController.addVisibleButtonsRegion(
-                        mDragLayer, insetsInfo.touchableRegion);
-                insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
-            }
-
-            // TaskbarContainerView provides insets to other apps based on contentInsets. These
-            // insets should stay consistent even if we expand TaskbarContainerView's bounds, e.g.
-            // to show a floating view like Folder. Thus, we set the contentInsets to be where
-            // mTaskbarView is, since its position never changes and insets rather than overlays.
-            insetsInfo.contentInsets.left = mTaskbarView.getLeft();
-            insetsInfo.contentInsets.top = mTaskbarView.getTop();
-            insetsInfo.contentInsets.right = mDragLayer.getWidth() - mTaskbarView.getRight();
-            insetsInfo.contentInsets.bottom = mDragLayer.getHeight() - mTaskbarView.getBottom();
-        }
-
-        public void onDragLayerViewRemoved() {
-            if (AbstractFloatingView.getOpenView(mActivity, TYPE_ALL) == null) {
-                mActivity.setTaskbarWindowFullscreen(false);
-            }
-        }
-    }
-}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
index 34f66ba..260cedc 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -15,8 +15,7 @@
  */
 package com.android.launcher3.taskbar;
 
-import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
-import com.android.quickstep.AnimatedFloat;
+import android.graphics.Rect;
 
 /**
  * Base class for providing different taskbar UI
@@ -25,12 +24,13 @@
 
     public static final TaskbarUIController DEFAULT = new TaskbarUIController();
 
-    protected void init(AnimatedFloat taskBarAlpha, AlphaProperty iconAlphaForLauncherState,
-            AlphaProperty iconAlphaForHome) { }
+    protected void init(TaskbarControllers taskbarControllers) { }
 
     protected void onDestroy() { }
 
     protected boolean isTaskbarTouchable() {
         return true;
     }
+
+    protected void updateContentInsets(Rect outContentInsets) { }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index d415502..373ca2a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -60,6 +60,7 @@
     private final TaskbarActivityContext mActivityContext;
 
     // Initialized in init.
+    private TaskbarViewController.TaskbarViewCallbacks mControllerCallbacks;
     private View.OnClickListener mIconClickListener;
     private View.OnLongClickListener mIconLongClickListener;
 
@@ -98,9 +99,10 @@
         mItemPadding = (mIconTouchSize - actualIconSize) / 2;
     }
 
-    protected void init(OnClickListener clickListener, OnLongClickListener longClickListener) {
-        mIconClickListener = clickListener;
-        mIconLongClickListener = longClickListener;
+    protected void init(TaskbarViewController.TaskbarViewCallbacks callbacks) {
+        mControllerCallbacks = callbacks;
+        mIconClickListener = mControllerCallbacks.getOnClickListener();
+        mIconLongClickListener = mControllerCallbacks.getOnLongClickListener();
 
         int numHotseatIcons = mActivityContext.getDeviceProfile().numShownHotseatIcons;
         updateHotseatItems(new ItemInfo[numHotseatIcons]);
@@ -200,11 +202,9 @@
                     && hotseatItemInfo instanceof WorkspaceItemInfo) {
                 ((BubbleTextView) hotseatView).applyFromWorkspaceItem(
                         (WorkspaceItemInfo) hotseatItemInfo);
-                hotseatView.setOnClickListener(mIconClickListener);
-                hotseatView.setOnLongClickListener(mIconLongClickListener);
+                setClickAndLongClickListenersForIcon(hotseatView);
             } else if (isFolder) {
-                hotseatView.setOnClickListener(mIconClickListener);
-                hotseatView.setOnLongClickListener(mIconLongClickListener);
+                setClickAndLongClickListenersForIcon(hotseatView);
             } else {
                 hotseatView.setOnClickListener(null);
                 hotseatView.setOnLongClickListener(null);
@@ -214,6 +214,14 @@
         }
     }
 
+    /**
+     * Sets OnClickListener and OnLongClickListener for the given view.
+     */
+    public void setClickAndLongClickListenersForIcon(View icon) {
+        icon.setOnClickListener(mIconClickListener);
+        icon.setOnLongClickListener(mIconLongClickListener);
+    }
+
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         int count = getChildCount();
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
new file mode 100644
index 0000000..b6184c9
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ */
+package com.android.launcher3.taskbar;
+
+import android.view.View;
+
+import com.android.launcher3.util.MultiValueAlpha;
+
+/**
+ * Handles properties/data collection, then passes the results to TaskbarView to render.
+ */
+public class TaskbarViewController {
+
+    public static final int ALPHA_INDEX_HOME = 0;
+    public static final int ALPHA_INDEX_LAUNCHER_STATE = 1;
+    public static final int ALPHA_INDEX_IME = 2;
+
+    private final TaskbarActivityContext mActivity;
+    private final TaskbarView mTaskbarView;
+    private final MultiValueAlpha mTaskbarIconAlpha;
+
+    // Initialized in init.
+    private TaskbarControllers mControllers;
+
+    public TaskbarViewController(TaskbarActivityContext activity, TaskbarView taskbarView) {
+        mActivity = activity;
+        mTaskbarView = taskbarView;
+        mTaskbarIconAlpha = new MultiValueAlpha(mTaskbarView, 3);
+        mTaskbarIconAlpha.setUpdateVisibility(true);
+    }
+
+    public void init(TaskbarControllers controllers) {
+        mControllers = controllers;
+        mTaskbarView.init(new TaskbarViewCallbacks());
+        mTaskbarView.getLayoutParams().height = mActivity.getDeviceProfile().taskbarSize;
+    }
+
+    public boolean areIconsVisible() {
+        return mTaskbarView.areIconsVisible();
+    }
+
+    public MultiValueAlpha getTaskbarIconAlpha() {
+        return mTaskbarIconAlpha;
+    }
+
+    /**
+     * Should be called when the IME visibility changes, so we can make Taskbar not steal touches.
+     */
+    public void setImeIsVisible(boolean isImeVisible) {
+        mTaskbarView.setTouchesEnabled(!isImeVisible);
+    }
+
+    /**
+     * Sets OnClickListener and OnLongClickListener for the given view.
+     */
+    public void setClickAndLongClickListenersForIcon(View icon) {
+        mTaskbarView.setClickAndLongClickListenersForIcon(icon);
+    }
+
+    /**
+     * Callbacks for {@link TaskbarView} to interact with its controller.
+     */
+    public class TaskbarViewCallbacks {
+        public View.OnClickListener getOnClickListener() {
+            return mActivity::onTaskbarIconClicked;
+        }
+
+        public View.OnLongClickListener getOnLongClickListener() {
+            return mControllers.taskbarDragController::startDragOnLongClick;
+        }
+    }
+}