Landscape 3 button nav on taskbar phone supported

* TODO: Seascape bar positioning, add tests

Change-Id: I542be2f2f682d8c8a9cdd9bb6c667c44ca167f3e
Merged-In: I542be2f2f682d8c8a9cdd9bb6c667c44ca167f3e
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index 339f3a9..af422cb 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -23,7 +23,6 @@
 import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor;
 import static com.android.launcher3.taskbar.LauncherTaskbarUIController.SYSUI_SURFACE_PROGRESS_INDEX;
 import static com.android.launcher3.taskbar.TaskbarManager.isPhoneButtonNavMode;
-import static com.android.launcher3.taskbar.TaskbarManager.isPhoneMode;
 import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_A11Y;
 import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_BACK;
 import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_HOME;
@@ -54,6 +53,7 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Color;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.graphics.Region.Op;
@@ -81,6 +81,9 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.AlphaUpdateListener;
 import com.android.launcher3.taskbar.TaskbarNavButtonController.TaskbarButton;
+import com.android.launcher3.taskbar.navbutton.NavButtonLayoutFactory;
+import com.android.launcher3.taskbar.navbutton.NavButtonLayoutFactory.NavButtonLayoutter;
+import com.android.launcher3.util.DimensionUtils;
 import com.android.launcher3.util.MultiValueAlpha;
 import com.android.launcher3.util.TouchController;
 import com.android.launcher3.views.BaseDragLayer;
@@ -181,6 +184,7 @@
     private final ViewTreeObserver.OnComputeInternalInsetsListener mSeparateWindowInsetsComputer =
             this::onComputeInsetsForSeparateWindow;
     private final RecentsHitboxExtender mHitboxExtender = new RecentsHitboxExtender();
+    private View mRecentsButton;
 
     public NavbarButtonsViewController(TaskbarActivityContext context, FrameLayout navButtonsView) {
         mContext = context;
@@ -203,11 +207,11 @@
         boolean isThreeButtonNav = mContext.isThreeButtonNav();
         DeviceProfile deviceProfile = mContext.getDeviceProfile();
         Resources resources = mContext.getResources();
-        mNavButtonsView.getLayoutParams().height = !isPhoneMode(deviceProfile) ?
-                mContext.isUserSetupComplete()
-                        ? deviceProfile.taskbarSize
-                        : resources.getDimensionPixelSize(R.dimen.taskbar_suw_frame)
-                : resources.getDimensionPixelSize(R.dimen.taskbar_size);
+        Point p = !mContext.isUserSetupComplete()
+                ? new Point(0, resources.getDimensionPixelSize(R.dimen.taskbar_suw_frame))
+                : DimensionUtils.getTaskbarPhoneDimensions(deviceProfile, resources,
+                        TaskbarManager.isPhoneMode(deviceProfile));
+        mNavButtonsView.getLayoutParams().height = p.y;
 
         mIsImeRenderingNavButtons =
                 InputMethodService.canImeRenderGesturalNavButtons() && mContext.imeDrawsImeNavBar();
@@ -268,81 +272,6 @@
                     mControllers.navButtonController);
             updateButtonLayoutSpacing();
             updateStateForFlag(FLAG_SMALL_SCREEN, isPhoneButtonNavMode(mContext));
-            if (isInSetup) {
-                handleSetupUi();
-
-                // Hide back button in SUW if keyboard is showing (IME draws its own back).
-                mPropertyHolders.add(new StatePropertyHolder(
-                        mBackButtonAlpha.getProperty(ALPHA_INDEX_SUW),
-                        flags -> (flags & FLAG_IME_VISIBLE) == 0));
-
-                // TODO(b/210906568) Dark intensity is currently not propagated during setup, so set
-                //  it based on dark theme for now.
-                int mode = resources.getConfiguration().uiMode
-                        & Configuration.UI_MODE_NIGHT_MASK;
-                boolean isDarkTheme = mode == Configuration.UI_MODE_NIGHT_YES;
-                mTaskbarNavButtonDarkIntensity.updateValue(isDarkTheme ? 0 : 1);
-            } else if (isInKidsMode) {
-                int iconSize = resources.getDimensionPixelSize(
-                        R.dimen.taskbar_icon_size_kids);
-                int buttonWidth = resources.getDimensionPixelSize(
-                        R.dimen.taskbar_nav_buttons_width_kids);
-                int buttonHeight = resources.getDimensionPixelSize(
-                        R.dimen.taskbar_nav_buttons_height_kids);
-                int buttonRadius = resources.getDimensionPixelSize(
-                        R.dimen.taskbar_nav_buttons_corner_radius_kids);
-                int paddingleft = (buttonWidth - iconSize) / 2;
-                int paddingRight = paddingleft;
-                int paddingTop = (buttonHeight - iconSize) / 2;
-                int paddingBottom = paddingTop;
-
-                // Update icons
-                ((ImageView) mBackButton).setImageDrawable(
-                        mBackButton.getContext().getDrawable(R.drawable.ic_sysbar_back_kids));
-                ((ImageView) mBackButton).setScaleType(ImageView.ScaleType.FIT_CENTER);
-                mBackButton.setPadding(paddingleft, paddingTop, paddingRight, paddingBottom);
-                ((ImageView) mHomeButton).setImageDrawable(
-                        mHomeButton.getContext().getDrawable(R.drawable.ic_sysbar_home_kids));
-                ((ImageView) mHomeButton).setScaleType(ImageView.ScaleType.FIT_CENTER);
-                mHomeButton.setPadding(paddingleft, paddingTop, paddingRight, paddingBottom);
-
-                // Home button layout
-                LinearLayout.LayoutParams homeLayoutparams = new LinearLayout.LayoutParams(
-                        buttonWidth,
-                        buttonHeight
-                );
-                int homeButtonLeftMargin = resources.getDimensionPixelSize(
-                        R.dimen.taskbar_home_button_left_margin_kids);
-                homeLayoutparams.setMargins(homeButtonLeftMargin, 0, 0, 0);
-                mHomeButton.setLayoutParams(homeLayoutparams);
-
-                // Back button layout
-                LinearLayout.LayoutParams backLayoutParams = new LinearLayout.LayoutParams(
-                        buttonWidth,
-                        buttonHeight
-                );
-                int backButtonLeftMargin = resources.getDimensionPixelSize(
-                        R.dimen.taskbar_back_button_left_margin_kids);
-                backLayoutParams.setMargins(backButtonLeftMargin, 0, 0, 0);
-                mBackButton.setLayoutParams(backLayoutParams);
-
-                // Button backgrounds
-                int whiteWith10PctAlpha = Color.argb(0.1f, 1, 1, 1);
-                PaintDrawable buttonBackground = new PaintDrawable(whiteWith10PctAlpha);
-                buttonBackground.setCornerRadius(buttonRadius);
-                mHomeButton.setBackground(buttonBackground);
-                mBackButton.setBackground(buttonBackground);
-
-                // Update alignment within taskbar
-                FrameLayout.LayoutParams navButtonsLayoutParams = (FrameLayout.LayoutParams)
-                        mNavButtonContainer.getLayoutParams();
-                navButtonsLayoutParams.setMarginStart(navButtonsLayoutParams.getMarginEnd() / 2);
-                navButtonsLayoutParams.setMarginEnd(navButtonsLayoutParams.getMarginStart());
-                navButtonsLayoutParams.gravity = Gravity.CENTER;
-                mNavButtonContainer.requestLayout();
-
-                mHomeButton.setOnLongClickListener(null);
-            }
 
             // Animate taskbar background when either..
             // notification shade expanded AND not on keyguard
@@ -445,20 +374,20 @@
                         (flags & FLAG_DISABLE_HOME) == 0));
 
         // Recents button
-        View recentsButton = addButton(R.drawable.ic_sysbar_recent, BUTTON_RECENTS,
+        mRecentsButton = addButton(R.drawable.ic_sysbar_recent, BUTTON_RECENTS,
                 navContainer, navButtonController, R.id.recent_apps);
-        mHitboxExtender.init(recentsButton, mNavButtonsView, mContext.getDeviceProfile(),
+        mHitboxExtender.init(mRecentsButton, mNavButtonsView, mContext.getDeviceProfile(),
                 () -> {
                     float[] recentsCoords = new float[2];
-                    getDescendantCoordRelativeToAncestor(recentsButton, mNavButtonsView,
+                    getDescendantCoordRelativeToAncestor(mRecentsButton, mNavButtonsView,
                             recentsCoords, false);
                     return recentsCoords;
                 }, new Handler());
-        recentsButton.setOnClickListener(v -> {
+        mRecentsButton.setOnClickListener(v -> {
             navButtonController.onButtonClick(BUTTON_RECENTS, v);
             mHitboxExtender.onRecentsButtonClicked();
         });
-        mPropertyHolders.add(new StatePropertyHolder(recentsButton,
+        mPropertyHolders.add(new StatePropertyHolder(mRecentsButton,
                 flags -> (flags & FLAG_KEYGUARD_VISIBLE) == 0 && (flags & FLAG_DISABLE_RECENTS) == 0
                         && !mContext.isNavBarKidsModeActive()));
 
@@ -773,14 +702,22 @@
                 || !mContext.isUserSetupComplete()) {
             return;
         }
-
-        if (isPhoneButtonNavMode(mContext)) {
-            updatePhoneButtonSpacing();
-            return;
-        }
-
         DeviceProfile dp = mContext.getDeviceProfile();
         Resources res = mContext.getResources();
+        boolean isInSetup = !mContext.isUserSetupComplete();
+        // TODO(b/244231596) we're getting the incorrect kidsMode value in small-screen
+        boolean isInKidsMode = mContext.isNavBarKidsModeActive();
+
+        if (TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW) {
+            boolean isThreeButtonNav = mContext.isThreeButtonNav();
+
+            NavButtonLayoutter navButtonLayoutter =
+                    NavButtonLayoutFactory.Companion.getUiLayoutter(
+                            dp, mNavButtonsView, res, isInKidsMode, isInSetup, isThreeButtonNav,
+                            TaskbarManager.isPhoneMode(dp));
+            navButtonLayoutter.layoutButtons(dp, isContextualButtonShowing());
+            return;
+        }
 
         // Add spacing after the end of the last nav button
         FrameLayout.LayoutParams navButtonParams =
@@ -816,38 +753,84 @@
                 buttonLayoutParams.setMarginEnd(spaceInBetween / 2);
             }
         }
-    }
 
-    /** Uniformly spaces out the 3 button nav for smaller phone screens */
-    private void updatePhoneButtonSpacing() {
-        DeviceProfile dp = mContext.getDeviceProfile();
-        Resources res = mContext.getResources();
+        if (isInSetup) {
+            handleSetupUi();
 
-        // TODO: Polish pending, this is just to make it usable
-        FrameLayout.LayoutParams navContainerParams =
-                (FrameLayout.LayoutParams) mNavButtonContainer.getLayoutParams();
-        int endStartMargins = res.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_size);
-        navContainerParams.gravity = Gravity.CENTER;
-        navContainerParams.setMarginEnd(endStartMargins);
-        navContainerParams.setMarginStart(endStartMargins);
-        mNavButtonContainer.setLayoutParams(navContainerParams);
+            // Hide back button in SUW if keyboard is showing (IME draws its own back).
+            mPropertyHolders.add(new StatePropertyHolder(
+                    mBackButtonAlpha.getProperty(ALPHA_INDEX_SUW),
+                    flags -> (flags & FLAG_IME_VISIBLE) == 0));
 
-        // Add the spaces in between the nav buttons
-        int spaceInBetween = res.getDimensionPixelSize(R.dimen.taskbar_button_space_inbetween_phone);
-        for (int i = 0; i < mNavButtonContainer.getChildCount(); i++) {
-            View navButton = mNavButtonContainer.getChildAt(i);
-            LinearLayout.LayoutParams buttonLayoutParams =
-                    (LinearLayout.LayoutParams) navButton.getLayoutParams();
-            buttonLayoutParams.weight = 1;
-            if (i == 0) {
-                buttonLayoutParams.setMarginEnd(spaceInBetween / 2);
-            } else if (i == mNavButtonContainer.getChildCount() - 1) {
-                buttonLayoutParams.setMarginStart(spaceInBetween / 2);
-            } else {
-                buttonLayoutParams.setMarginStart(spaceInBetween / 2);
-                buttonLayoutParams.setMarginEnd(spaceInBetween / 2);
-            }
+            // TODO(b/210906568) Dark intensity is currently not propagated during setup, so set
+            //  it based on dark theme for now.
+            int mode = res.getConfiguration().uiMode
+                    & Configuration.UI_MODE_NIGHT_MASK;
+            boolean isDarkTheme = mode == Configuration.UI_MODE_NIGHT_YES;
+            mTaskbarNavButtonDarkIntensity.updateValue(isDarkTheme ? 0 : 1);
+        } else if (isInKidsMode) {
+            int iconSize = res.getDimensionPixelSize(
+                    R.dimen.taskbar_icon_size_kids);
+            int buttonWidth = res.getDimensionPixelSize(
+                    R.dimen.taskbar_nav_buttons_width_kids);
+            int buttonHeight = res.getDimensionPixelSize(
+                    R.dimen.taskbar_nav_buttons_height_kids);
+            int buttonRadius = res.getDimensionPixelSize(
+                    R.dimen.taskbar_nav_buttons_corner_radius_kids);
+            int paddingleft = (buttonWidth - iconSize) / 2;
+            int paddingRight = paddingleft;
+            int paddingTop = (buttonHeight - iconSize) / 2;
+            int paddingBottom = paddingTop;
+
+            // Update icons
+            ((ImageView) mBackButton).setImageDrawable(
+                    mBackButton.getContext().getDrawable(R.drawable.ic_sysbar_back_kids));
+            ((ImageView) mBackButton).setScaleType(ImageView.ScaleType.FIT_CENTER);
+            mBackButton.setPadding(paddingleft, paddingTop, paddingRight, paddingBottom);
+            ((ImageView) mHomeButton).setImageDrawable(
+                    mHomeButton.getContext().getDrawable(R.drawable.ic_sysbar_home_kids));
+            ((ImageView) mHomeButton).setScaleType(ImageView.ScaleType.FIT_CENTER);
+            mHomeButton.setPadding(paddingleft, paddingTop, paddingRight, paddingBottom);
+
+            // Home button layout
+            LinearLayout.LayoutParams homeLayoutparams = new LinearLayout.LayoutParams(
+                    buttonWidth,
+                    buttonHeight
+            );
+            int homeButtonLeftMargin = res.getDimensionPixelSize(
+                    R.dimen.taskbar_home_button_left_margin_kids);
+            homeLayoutparams.setMargins(homeButtonLeftMargin, 0, 0, 0);
+            mHomeButton.setLayoutParams(homeLayoutparams);
+
+            // Back button layout
+            LinearLayout.LayoutParams backLayoutParams = new LinearLayout.LayoutParams(
+                    buttonWidth,
+                    buttonHeight
+            );
+            int backButtonLeftMargin = res.getDimensionPixelSize(
+                    R.dimen.taskbar_back_button_left_margin_kids);
+            backLayoutParams.setMargins(backButtonLeftMargin, 0, 0, 0);
+            mBackButton.setLayoutParams(backLayoutParams);
+
+            // Button backgrounds
+            int whiteWith10PctAlpha = Color.argb(0.1f, 1, 1, 1);
+            PaintDrawable buttonBackground = new PaintDrawable(whiteWith10PctAlpha);
+            buttonBackground.setCornerRadius(buttonRadius);
+            mHomeButton.setBackground(buttonBackground);
+            mBackButton.setBackground(buttonBackground);
+
+            // Update alignment within taskbar
+            FrameLayout.LayoutParams navButtonsLayoutParams = (FrameLayout.LayoutParams)
+                    mNavButtonContainer.getLayoutParams();
+            navButtonsLayoutParams.setMarginStart(
+                    navButtonsLayoutParams.getMarginEnd() / 2);
+            navButtonsLayoutParams.setMarginEnd(navButtonsLayoutParams.getMarginStart());
+            navButtonsLayoutParams.gravity = Gravity.CENTER;
+            mNavButtonContainer.requestLayout();
+
+            mHomeButton.setOnLongClickListener(null);
         }
+
     }
 
     public void onDestroy() {
@@ -859,6 +842,8 @@
 
         moveNavButtonsBackToTaskbarWindow();
         mNavButtonContainer.removeAllViews();
+        mEndContextualContainer.removeAllViews();
+        mStartContextualContainer.removeAllViews();
         mAllButtons.clear();
     }
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
index 6b67b50..e23e27e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
@@ -101,8 +101,8 @@
                     resources.getDimensionPixelSize(R.dimen.taskbar_stashed_small_screen);
         } else {
             mStashedHandleView.getLayoutParams().height = deviceProfile.taskbarSize;
-            mStashedHandleWidth =
-                    resources.getDimensionPixelSize(R.dimen.taskbar_stashed_handle_width);
+            mStashedHandleWidth = resources
+                    .getDimensionPixelSize(R.dimen.taskbar_stashed_handle_width);
         }
 
         mTaskbarStashedHandleAlpha.getProperty(ALPHA_INDEX_STASHED).setValue(
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 72c163e..0c488cb 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -154,6 +154,8 @@
                 Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE), 0);
         mIsNavBarForceVisible = settingsCache.getValue(
                 Settings.Secure.getUriFor(Settings.Secure.NAV_BAR_KIDS_MODE), 0);
+        // TODO(b/244231596) For shared Taskbar window, update this value in init() instead so
+        //  to get correct value when recreating the taskbar
         mIsNavBarKidsMode = settingsCache.getValue(
                 Settings.Secure.getUriFor(Settings.Secure.NAV_BAR_KIDS_MODE), 0);
 
@@ -276,9 +278,13 @@
      * @param type The window type to pass to the created WindowManager.LayoutParams.
      */
     public WindowManager.LayoutParams createDefaultWindowLayoutParams(int type) {
+        DeviceProfile deviceProfile = getDeviceProfile();
+        // Taskbar is on the logical bottom of the screen
+        boolean isVerticalBarLayout = TaskbarManager.isPhoneMode(deviceProfile) &&
+                deviceProfile.isLandscape;
         WindowManager.LayoutParams windowLayoutParams = new WindowManager.LayoutParams(
-                MATCH_PARENT,
-                mLastRequestedNonFullscreenHeight,
+                isVerticalBarLayout ? mLastRequestedNonFullscreenHeight : MATCH_PARENT,
+                isVerticalBarLayout ? MATCH_PARENT : mLastRequestedNonFullscreenHeight,
                 type,
                 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                         | WindowManager.LayoutParams.FLAG_SLIPPERY
@@ -286,7 +292,10 @@
                 PixelFormat.TRANSLUCENT);
         windowLayoutParams.setTitle(WINDOW_TITLE);
         windowLayoutParams.packageName = getPackageName();
-        windowLayoutParams.gravity = Gravity.BOTTOM;
+        windowLayoutParams.gravity = !isVerticalBarLayout ?
+                Gravity.BOTTOM :
+                Gravity.END; // TODO(b/230394142): seascape
+
         windowLayoutParams.setFitInsetsTypes(0);
         windowLayoutParams.receiveInsetsIgnoringZOrder = true;
         windowLayoutParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
@@ -803,7 +812,7 @@
         return mIsUserSetupComplete;
     }
 
-    protected boolean isNavBarKidsModeActive() {
+    public boolean isNavBarKidsModeActive() {
         return mIsNavBarKidsMode && isThreeButtonNav();
     }
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
index 025fe7a..353f1e0 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
@@ -16,11 +16,13 @@
 package com.android.launcher3.taskbar;
 
 import android.content.res.Resources;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.view.ViewTreeObserver;
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
+import com.android.launcher3.util.DimensionUtils;
 import com.android.launcher3.util.TouchController;
 import com.android.quickstep.AnimatedFloat;
 
@@ -177,9 +179,12 @@
             DeviceProfile deviceProfile = mActivity.getDeviceProfile();
             if (TaskbarManager.isPhoneMode(deviceProfile)) {
                 Resources resources = mActivity.getResources();
-                return mActivity.isThreeButtonNav() ?
-                        resources.getDimensionPixelSize(R.dimen.taskbar_size) :
-                        resources.getDimensionPixelSize(R.dimen.taskbar_stashed_size);
+                Point taskbarDimensions =
+                        DimensionUtils.getTaskbarPhoneDimensions(deviceProfile, resources,
+                                TaskbarManager.isPhoneMode(deviceProfile));
+                return taskbarDimensions.y == -1 ?
+                        deviceProfile.getDisplayInfo().currentSize.y :
+                        taskbarDimensions.y;
             } else {
                 return deviceProfile.taskbarSize;
             }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
index 079e8a1..bbbc1e6 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
@@ -84,16 +84,16 @@
         val tappableHeight = controllers.taskbarStashController.tappableHeightToReportToApps
         for (provider in windowLayoutParams.providedInsets) {
             if (provider.type == ITYPE_EXTRA_NAVIGATION_BAR) {
-                provider.insetsSize = Insets.of(0, 0, 0, contentHeight)
+                provider.insetsSize = getInsetsByNavMode(contentHeight)
             } else if (provider.type == ITYPE_BOTTOM_TAPPABLE_ELEMENT
                       || provider.type == ITYPE_BOTTOM_MANDATORY_GESTURES) {
-                provider.insetsSize = Insets.of(0, 0, 0, tappableHeight)
+                provider.insetsSize = getInsetsByNavMode(tappableHeight)
             }
         }
 
-        val imeInsetsSize = Insets.of(0, 0, 0, taskbarHeightForIme)
+        val imeInsetsSize = getInsetsByNavMode(taskbarHeightForIme)
         // Use 0 insets for the VoiceInteractionWindow (assistant) when gesture nav is enabled.
-        val visInsetsSize = Insets.of(0, 0, 0, if (context.isGestureNav) 0 else tappableHeight)
+        val visInsetsSize = getInsetsByNavMode(if (context.isGestureNav) 0 else tappableHeight)
         val insetsSizeOverride = arrayOf(
             InsetsFrameProvider.InsetsSizeOverride(
                 TYPE_INPUT_METHOD,
@@ -110,6 +110,21 @@
     }
 
     /**
+     * @return [Insets] where the [bottomInset] is either used as a bottom inset or
+     *         right/left inset if using 3 button nav
+     */
+    private fun getInsetsByNavMode(bottomInset: Int) : Insets {
+        val devicePortrait = !context.deviceProfile.isLandscape
+        if (!TaskbarManager.isPhoneButtonNavMode(context) || devicePortrait) {
+            // Taskbar or portrait phone mode
+            return Insets.of(0, 0, 0, bottomInset)
+        }
+
+        // TODO(b/230394142): seascape
+        return Insets.of(0, 0, bottomInset, 0)
+    }
+
+    /**
      * Sets {@param providesInsetsTypes} as the inset types provided by {@param params}.
      * @param params The window layout params.
      * @param providesInsetsTypes The inset types we would like this layout params to provide.
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index 9fd2bf9..c5e1b8f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -715,8 +715,16 @@
         applyState(skipAnim ? 0 : animDuration, skipAnim ? 0 : startDelay);
     }
 
+    /**
+     * We stash when IME or IME switcher is showing AND NOT
+     *  * in small screen AND
+     *  * 3 button nav AND
+     *  * landscape (or seascape)
+     */
     private boolean shouldStashForIme() {
-        return mIsImeShowing || mIsImeSwitcherShowing;
+        return (mIsImeShowing || mIsImeSwitcherShowing) &&
+                !(isPhoneMode() && mActivity.isThreeButtonNav()
+                        && mActivity.getDeviceProfile().isLandscape);
     }
 
     /**
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt
new file mode 100644
index 0000000..68ea27a
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.launcher3.taskbar.navbutton
+
+import android.content.res.Resources
+import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.LinearLayout
+import com.android.launcher3.R
+import com.android.launcher3.taskbar.navbutton.NavButtonLayoutFactory.NavButtonLayoutter
+
+/**
+ * Meant to be a simple container for data subclasses will need
+ *
+ * Assumes that the 3 navigation buttons (back/home/recents) have already been added to
+ * [navButtonContainer]
+ *
+ * @property navButtonContainer ViewGroup that holds the 3 navigation buttons.
+ * @property endContextualContainer ViewGroup that holds the end contextual button (ex, IME dismiss).
+ * @property startContextualContainer ViewGroup that holds the start contextual button (ex, A11y).
+ */
+abstract class AbstractNavButtonLayoutter(
+        val resources: Resources,
+        val navButtonContainer: LinearLayout,
+        protected val endContextualContainer: ViewGroup,
+        protected val startContextualContainer: ViewGroup
+) : NavButtonLayoutter {
+    protected val homeButton: ImageView = navButtonContainer
+            .findViewById(R.id.home)
+    protected val recentsButton: ImageView = navButtonContainer
+            .findViewById(R.id.recent_apps)
+    protected val backButton: ImageView = navButtonContainer
+            .findViewById(R.id.back)
+}
+
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/KidsNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/KidsNavLayoutter.kt
new file mode 100644
index 0000000..c67ab79
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/KidsNavLayoutter.kt
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.launcher3.taskbar.navbutton
+
+import android.content.res.Resources
+import android.graphics.Color
+import android.graphics.drawable.PaintDrawable
+import android.view.Gravity
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import android.widget.ImageView
+import android.widget.LinearLayout
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.DIMEN_TASKBAR_BACK_BUTTON_LEFT_MARGIN_KIDS
+import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.DIMEN_TASKBAR_HOME_BUTTON_LEFT_MARGIN_KIDS
+import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.DIMEN_TASKBAR_ICON_SIZE_KIDS
+import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.DIMEN_TASKBAR_NAV_BUTTONS_CORNER_RADIUS_KIDS
+import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.DIMEN_TASKBAR_NAV_BUTTONS_HEIGHT_KIDS
+import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.DIMEN_TASKBAR_NAV_BUTTONS_WIDTH_KIDS
+import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.DRAWABLE_SYSBAR_BACK_KIDS
+import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.DRAWABLE_SYSBAR_HOME_KIDS
+
+class KidsNavLayoutter(
+        resources: Resources,
+        navBarContainer: LinearLayout,
+        endContextualContainer: ViewGroup,
+        startContextualContainer: ViewGroup
+) : AbstractNavButtonLayoutter(
+        resources,
+        navBarContainer,
+        endContextualContainer,
+        startContextualContainer
+) {
+
+    override fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean) {
+        val iconSize: Int = resources.getDimensionPixelSize(
+                DIMEN_TASKBAR_ICON_SIZE_KIDS)
+        val buttonWidth: Int = resources.getDimensionPixelSize(
+                DIMEN_TASKBAR_NAV_BUTTONS_WIDTH_KIDS)
+        val buttonHeight: Int = resources.getDimensionPixelSize(
+                DIMEN_TASKBAR_NAV_BUTTONS_HEIGHT_KIDS)
+        val buttonRadius: Int = resources.getDimensionPixelSize(
+                DIMEN_TASKBAR_NAV_BUTTONS_CORNER_RADIUS_KIDS)
+        val paddingLeft = (buttonWidth - iconSize) / 2
+        val paddingTop = (buttonHeight - iconSize) / 2
+
+        // Update icons
+        backButton.setImageDrawable(
+                backButton.context.getDrawable(DRAWABLE_SYSBAR_BACK_KIDS))
+        backButton.scaleType = ImageView.ScaleType.FIT_CENTER
+        backButton.setPadding(paddingLeft, paddingTop, paddingLeft, paddingTop)
+        homeButton.setImageDrawable(
+                homeButton.getContext().getDrawable(DRAWABLE_SYSBAR_HOME_KIDS))
+        homeButton.scaleType = ImageView.ScaleType.FIT_CENTER
+        homeButton.setPadding(paddingLeft, paddingTop, paddingLeft, paddingTop)
+
+        // Home button layout
+        val homeLayoutparams = LinearLayout.LayoutParams(
+                buttonWidth,
+                buttonHeight
+        )
+        val homeButtonLeftMargin: Int = resources.getDimensionPixelSize(
+                DIMEN_TASKBAR_HOME_BUTTON_LEFT_MARGIN_KIDS)
+        homeLayoutparams.setMargins(homeButtonLeftMargin, 0, 0, 0)
+        homeButton.layoutParams = homeLayoutparams
+
+        // Back button layout
+        val backLayoutParams = LinearLayout.LayoutParams(
+                buttonWidth,
+                buttonHeight
+        )
+        val backButtonLeftMargin: Int = resources.getDimensionPixelSize(
+                DIMEN_TASKBAR_BACK_BUTTON_LEFT_MARGIN_KIDS)
+        backLayoutParams.setMargins(backButtonLeftMargin, 0, 0, 0)
+        backButton.layoutParams = backLayoutParams
+
+        // Button backgrounds
+        val whiteWith10PctAlpha = Color.argb(0.1f, 1f, 1f, 1f)
+        val buttonBackground = PaintDrawable(whiteWith10PctAlpha)
+        buttonBackground.setCornerRadius(buttonRadius.toFloat())
+        homeButton.background = buttonBackground
+        backButton.background = buttonBackground
+
+        // Update alignment within taskbar
+        val navButtonsLayoutParams = navButtonContainer.layoutParams as FrameLayout.LayoutParams
+        navButtonsLayoutParams.apply {
+            marginStart = navButtonsLayoutParams.marginEnd / 2
+            marginEnd = navButtonsLayoutParams.marginStart
+            gravity = Gravity.CENTER
+        }
+        navButtonContainer.requestLayout()
+
+        homeButton.onLongClickListener = null
+    }
+}
\ No newline at end of file
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/LayoutResourceHelper.java b/quickstep/src/com/android/launcher3/taskbar/navbutton/LayoutResourceHelper.java
new file mode 100644
index 0000000..0d9b855
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/LayoutResourceHelper.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.launcher3.taskbar.navbutton;
+
+import android.annotation.DimenRes;
+import android.annotation.DrawableRes;
+import android.annotation.IdRes;
+
+import com.android.launcher3.R;
+
+/**
+ * A class for retrieving resources in Kotlin.
+ *
+ * This class should be removed once the build system supports resources loading in Kotlin.
+ */
+public final class LayoutResourceHelper {
+
+    // --------------------------
+    // Kids Nav Layout
+    @DimenRes
+    public static final int DIMEN_TASKBAR_ICON_SIZE_KIDS = R.dimen.taskbar_icon_size_kids;
+    @DrawableRes
+    public static final int DRAWABLE_SYSBAR_BACK_KIDS = R.drawable.ic_sysbar_back_kids;
+    @DrawableRes
+    public static final int DRAWABLE_SYSBAR_HOME_KIDS = R.drawable.ic_sysbar_home_kids;
+    @DimenRes
+    public static final int DIMEN_TASKBAR_HOME_BUTTON_LEFT_MARGIN_KIDS =
+            R.dimen.taskbar_home_button_left_margin_kids;
+    @DimenRes
+    public static final int DIMEN_TASKBAR_BACK_BUTTON_LEFT_MARGIN_KIDS =
+            R.dimen.taskbar_back_button_left_margin_kids;
+    @DimenRes
+    public static final int DIMEN_TASKBAR_NAV_BUTTONS_WIDTH_KIDS =
+            R.dimen.taskbar_nav_buttons_width_kids;
+    @DimenRes
+    public static final int DIMEN_TASKBAR_NAV_BUTTONS_HEIGHT_KIDS =
+            R.dimen.taskbar_nav_buttons_height_kids;
+    @DimenRes
+    public static final int DIMEN_TASKBAR_NAV_BUTTONS_CORNER_RADIUS_KIDS =
+            R.dimen.taskbar_nav_buttons_corner_radius_kids;
+
+    // --------------------------
+    // Nav Layout Factory
+    @IdRes
+    public static final int ID_START_CONTEXTUAL_BUTTONS = R.id.start_contextual_buttons;
+    @IdRes
+    public static final int ID_END_CONTEXTUAL_BUTTONS = R.id.end_contextual_buttons;
+    @IdRes
+    public static final int ID_END_NAV_BUTTONS = R.id.end_nav_buttons;
+
+    private LayoutResourceHelper() {
+
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt
new file mode 100644
index 0000000..db0a2d8
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.launcher3.taskbar.navbutton
+
+import android.content.res.Resources
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import android.widget.LinearLayout
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.ID_END_CONTEXTUAL_BUTTONS
+import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.ID_END_NAV_BUTTONS
+import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.ID_START_CONTEXTUAL_BUTTONS
+import com.android.launcher3.taskbar.navbutton.NavButtonLayoutFactory.Companion
+import com.android.launcher3.taskbar.navbutton.NavButtonLayoutFactory.NavButtonLayoutter
+
+/**
+ * Select the correct layout for nav buttons
+ *
+ * Since layouts are done dynamically for the nav buttons on Taskbar, this
+ * class returns a corresponding [NavButtonLayoutter] via
+ * [Companion.getUiLayoutter]
+ * that can help position the buttons based on the current [DeviceProfile]
+ */
+class NavButtonLayoutFactory {
+    companion object {
+        /**
+         * Get the correct instance of [NavButtonLayoutter]
+         *
+         * No layouts supported for configurations where:
+         *  * taskbar isn't showing AND
+         *  * the device is not in [phoneMode]
+         * OR
+         *  * phone is showing
+         *  * device is using gesture navigation
+         *
+         * @param navButtonsView ViewGroup that contains start, end, nav button ViewGroups
+         * @param isKidsMode no-op when taskbar is hidden/not showing
+         * @param isInSetup no-op when taskbar is hidden/not showing
+         * @param phoneMode refers to the device using the taskbar window on phones
+         * @param isThreeButtonNav are no-ops when taskbar is present/showing
+         */
+        fun getUiLayoutter(deviceProfile: DeviceProfile,
+                           navButtonsView: FrameLayout,
+                           resources: Resources,
+                           isKidsMode: Boolean,
+                           isInSetup: Boolean,
+                           isThreeButtonNav: Boolean,
+                           phoneMode: Boolean):
+                NavButtonLayoutter {
+            val navButtonContainer =
+                    navButtonsView.findViewById<LinearLayout>(ID_END_NAV_BUTTONS)
+            val endContextualContainer =
+                    navButtonsView.findViewById<ViewGroup>(ID_END_CONTEXTUAL_BUTTONS)
+            val startContextualContainer =
+                    navButtonsView.findViewById<ViewGroup>(ID_START_CONTEXTUAL_BUTTONS)
+            val isPhoneNavMode = phoneMode && isThreeButtonNav
+            return when {
+                isPhoneNavMode -> {
+                    if (!deviceProfile.isLandscape) {
+                        PhonePortraitNavLayoutter(resources, navButtonContainer,
+                                endContextualContainer, startContextualContainer)
+                    } else {
+                        PhoneLandscapeNavLayoutter(resources, navButtonContainer,
+                                endContextualContainer, startContextualContainer)
+                    }
+                }
+                deviceProfile.isTaskbarPresent -> {
+                    return when {
+                        isInSetup -> {
+                            SetupNavLayoutter(resources, navButtonContainer, endContextualContainer,
+                                    startContextualContainer)
+                        }
+                        isKidsMode -> {
+                            KidsNavLayoutter(resources, navButtonContainer, endContextualContainer,
+                                    startContextualContainer)
+                        }
+                        else ->
+                            TaskbarNavLayoutter(resources, navButtonContainer, endContextualContainer,
+                                    startContextualContainer)
+                    }
+                }
+                else -> error("No layoutter found")
+            }
+        }
+    }
+
+    /** Lays out and provides access to the home, recents, and back buttons for various mischief  */
+    interface NavButtonLayoutter {
+        fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean)
+    }
+}
\ No newline at end of file
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneLandscapeNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneLandscapeNavLayoutter.kt
new file mode 100644
index 0000000..a89476e
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneLandscapeNavLayoutter.kt
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.launcher3.taskbar.navbutton
+
+import android.content.res.Resources
+import android.view.Gravity
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import android.widget.LinearLayout
+import androidx.core.view.children
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.R
+import com.android.launcher3.taskbar.TaskbarManager
+import com.android.launcher3.util.DimensionUtils
+
+class PhoneLandscapeNavLayoutter(
+        resources: Resources,
+        navBarContainer: LinearLayout,
+        endContextualContainer: ViewGroup,
+        startContextualContainer: ViewGroup
+) : AbstractNavButtonLayoutter(
+        resources,
+        navBarContainer,
+        endContextualContainer,
+        startContextualContainer
+) {
+
+    override fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean) {
+        // TODO(b/230395757): Polish pending, this is just to make it usable
+        val navContainerParams = navButtonContainer.layoutParams as FrameLayout.LayoutParams
+        val endStartMargins =
+                resources.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_size)
+        val taskbarDimensions = DimensionUtils.getTaskbarPhoneDimensions(dp, resources,
+                TaskbarManager.isPhoneMode(dp))
+        navButtonContainer.removeAllViews()
+        navButtonContainer.orientation = LinearLayout.VERTICAL
+
+        navContainerParams.apply {
+            width = taskbarDimensions.x
+            height = ViewGroup.LayoutParams.MATCH_PARENT
+            gravity = Gravity.CENTER
+            topMargin = endStartMargins
+            bottomMargin = endStartMargins
+            marginEnd = 0
+            marginStart = 0
+        }
+
+        // Swap recents and back button
+        navButtonContainer.addView(recentsButton)
+        navButtonContainer.addView(homeButton)
+        navButtonContainer.addView(backButton)
+
+        navButtonContainer.layoutParams = navContainerParams
+
+        // Add the spaces in between the nav buttons
+        val spaceInBetween: Int =
+                resources.getDimensionPixelSize(R.dimen.taskbar_button_space_inbetween_phone)
+        navButtonContainer.children.forEachIndexed { i, navButton ->
+            val buttonLayoutParams = navButton.layoutParams as LinearLayout.LayoutParams
+            buttonLayoutParams.weight = 1f
+            when (i) {
+                0 -> {
+                    buttonLayoutParams.bottomMargin = spaceInBetween / 2
+                }
+                navButtonContainer.childCount - 1 -> {
+                    buttonLayoutParams.topMargin = spaceInBetween / 2
+                }
+                else -> {
+                    buttonLayoutParams.bottomMargin = spaceInBetween / 2
+                    buttonLayoutParams.topMargin = spaceInBetween / 2
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhonePortraitNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhonePortraitNavLayoutter.kt
new file mode 100644
index 0000000..275f59f
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhonePortraitNavLayoutter.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.launcher3.taskbar.navbutton
+
+import android.content.res.Resources
+import android.view.Gravity
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import android.widget.LinearLayout
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.R
+import com.android.launcher3.taskbar.TaskbarManager
+import com.android.launcher3.util.DimensionUtils
+
+class PhonePortraitNavLayoutter(resources: Resources, navBarContainer: LinearLayout,
+                                endContextualContainer: ViewGroup,
+                                startContextualContainer: ViewGroup) :
+        AbstractNavButtonLayoutter(resources, navBarContainer, endContextualContainer,
+                startContextualContainer) {
+
+    override fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean) {
+        // TODO(b/230395757): Polish pending, this is just to make it usable
+        val navContainerParams = navButtonContainer.layoutParams as FrameLayout.LayoutParams
+        val taskbarDimensions = DimensionUtils.getTaskbarPhoneDimensions(dp, resources,
+                TaskbarManager.isPhoneMode(dp))
+        val endStartMargins = resources.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_size)
+        navContainerParams.width = taskbarDimensions.x
+        navContainerParams.height = ViewGroup.LayoutParams.MATCH_PARENT
+        navContainerParams.gravity = Gravity.CENTER_VERTICAL
+
+        // Ensure order of buttons is correct
+        navButtonContainer.removeAllViews()
+        navButtonContainer.orientation = LinearLayout.HORIZONTAL
+        navContainerParams.topMargin = 0
+        navContainerParams.bottomMargin = 0
+        navContainerParams.marginEnd = endStartMargins
+        navContainerParams.marginStart = endStartMargins
+        // Swap recents and back button in case we were landscape prior to this
+        navButtonContainer.addView(backButton)
+        navButtonContainer.addView(homeButton)
+        navButtonContainer.addView(recentsButton)
+
+        navButtonContainer.layoutParams = navContainerParams
+
+        // Add the spaces in between the nav buttons
+        val spaceInBetween =
+                resources.getDimensionPixelSize(R.dimen.taskbar_button_space_inbetween_phone)
+        for (i in 0 until navButtonContainer.childCount) {
+            val navButton = navButtonContainer.getChildAt(i)
+            val buttonLayoutParams = navButton.layoutParams as LinearLayout.LayoutParams
+            buttonLayoutParams.weight = 1f
+            when (i) {
+                0 -> {
+                    // First button
+                    buttonLayoutParams.marginEnd = spaceInBetween / 2
+                }
+                navButtonContainer.childCount - 1 -> {
+                    // Last button
+                    buttonLayoutParams.marginStart = spaceInBetween / 2
+                }
+                else -> {
+                    // other buttons
+                    buttonLayoutParams.marginStart = spaceInBetween / 2
+                    buttonLayoutParams.marginEnd = spaceInBetween / 2
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt
new file mode 100644
index 0000000..afe70d6
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.launcher3.taskbar.navbutton
+
+import android.content.res.Resources
+import android.view.Gravity
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import android.widget.LinearLayout
+import com.android.launcher3.DeviceProfile
+
+class SetupNavLayoutter(
+        resources: Resources,
+        navButtonContainer: LinearLayout,
+        endContextualContainer: ViewGroup,
+        startContextualContainer: ViewGroup
+) : AbstractNavButtonLayoutter(
+        resources,
+        navButtonContainer,
+        endContextualContainer,
+        startContextualContainer
+) {
+
+    override fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean) {
+        // Since setup wizard only has back button enabled, it looks strange to be
+        // end-aligned, so start-align instead.
+        val navButtonsLayoutParams = navButtonContainer.layoutParams as FrameLayout.LayoutParams
+        navButtonsLayoutParams.apply {
+            marginStart = navButtonsLayoutParams.marginEnd
+            marginEnd = 0
+            gravity = Gravity.START
+        }
+        navButtonContainer.requestLayout()
+    }
+}
\ No newline at end of file
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt
new file mode 100644
index 0000000..b2ca2af
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.launcher3.taskbar.navbutton
+
+import android.content.res.Resources
+import android.view.Gravity
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import android.widget.LinearLayout
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.R
+
+/**
+ * Layoutter for showing 3 button navigation on large screen
+ */
+class TaskbarNavLayoutter(
+        resources: Resources,
+        navBarContainer: LinearLayout,
+        endContextualContainer: ViewGroup,
+        startContextualContainer: ViewGroup
+) : AbstractNavButtonLayoutter(
+        resources,
+        navBarContainer,
+        endContextualContainer,
+        startContextualContainer
+) {
+
+    override fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean) {
+        // Add spacing after the end of the last nav button
+        val navButtonParams = navButtonContainer.layoutParams as FrameLayout.LayoutParams
+        var navMarginEnd = resources.getDimension(dp.inv.inlineNavButtonsEndSpacing).toInt()
+        val contextualWidth = endContextualContainer.width
+        // If contextual buttons are showing, we check if the end margin is enough for the
+        // contextual button to be showing - if not, move the nav buttons over a smidge
+        if (isContextualButtonShowing && navMarginEnd < contextualWidth) {
+            // Additional spacing, eat up half of space between last icon and nav button
+            navMarginEnd += resources.getDimensionPixelSize(R.dimen.taskbar_hotseat_nav_spacing) / 2
+        }
+
+        navButtonParams.apply {
+            gravity = Gravity.END
+            width = FrameLayout.LayoutParams.WRAP_CONTENT
+            height = ViewGroup.LayoutParams.MATCH_PARENT
+            marginEnd = navMarginEnd
+        }
+        navButtonContainer.orientation = LinearLayout.HORIZONTAL
+        navButtonContainer.layoutParams = navButtonParams
+
+        // Add the spaces in between the nav buttons
+        val spaceInBetween = resources.getDimensionPixelSize(R.dimen.taskbar_button_space_inbetween)
+        for (i in 0 until navButtonContainer.childCount) {
+            val navButton = navButtonContainer.getChildAt(i)
+            val buttonLayoutParams = navButton.layoutParams as LinearLayout.LayoutParams
+            buttonLayoutParams.weight = 0f
+            when (i) {
+                0 -> {
+                    buttonLayoutParams.marginEnd = spaceInBetween / 2
+                }
+                navButtonContainer.childCount - 1 -> {
+                    buttonLayoutParams.marginStart = spaceInBetween / 2
+                }
+                else -> {
+                    buttonLayoutParams.marginStart = spaceInBetween / 2
+                    buttonLayoutParams.marginEnd = spaceInBetween / 2
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactoryTest.kt b/quickstep/tests/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactoryTest.kt
new file mode 100644
index 0000000..58f0949
--- /dev/null
+++ b/quickstep/tests/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactoryTest.kt
@@ -0,0 +1,148 @@
+package com.android.launcher3.taskbar.navbutton
+
+import android.content.res.Resources
+import android.view.View
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import android.widget.ImageView
+import android.widget.LinearLayout
+import androidx.test.runner.AndroidJUnit4
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.taskbar.TaskbarManager
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import com.android.launcher3.R
+import org.junit.Assume.assumeTrue
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
+import java.lang.IllegalStateException
+
+@RunWith(AndroidJUnit4::class)
+class NavButtonLayoutFactoryTest {
+
+    @Mock
+    lateinit var mockDeviceProfile: DeviceProfile
+    @Mock
+    lateinit var mockParentButtonContainer: FrameLayout
+    @Mock
+    lateinit var mockNavLayout: LinearLayout
+    @Mock
+    lateinit var mockStartContextualLayout: ViewGroup
+    @Mock
+    lateinit var mockEndContextualLayout: ViewGroup
+    @Mock
+    lateinit var mockResources: Resources
+    @Mock
+    lateinit var mockBackButton: ImageView
+    @Mock
+    lateinit var mockRecentsButton: ImageView
+    @Mock
+    lateinit var mockHomeButton: ImageView
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+
+        // Init end nav buttons
+        whenever(mockNavLayout.childCount).thenReturn(3)
+        whenever(mockNavLayout.findViewById<View>(R.id.back)).thenReturn(mockBackButton)
+        whenever(mockNavLayout.findViewById<View>(R.id.home)).thenReturn(mockHomeButton)
+        whenever(mockNavLayout.findViewById<View>(R.id.recent_apps)).thenReturn(mockRecentsButton)
+
+        // Init top level layout
+        whenever(mockParentButtonContainer.findViewById<LinearLayout>(R.id.end_nav_buttons))
+                .thenReturn(mockNavLayout)
+        whenever(mockParentButtonContainer.findViewById<ViewGroup>(R.id.end_contextual_buttons))
+                .thenReturn(mockEndContextualLayout)
+        whenever(mockParentButtonContainer.findViewById<ViewGroup>(R.id.start_contextual_buttons))
+                .thenReturn(mockStartContextualLayout)
+    }
+
+    @Test
+    fun getKidsLayoutter() {
+        assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW)
+        mockDeviceProfile.isTaskbarPresent = true
+        val layoutter: NavButtonLayoutFactory.NavButtonLayoutter =
+                getLayoutter(isKidsMode = true, isInSetup = false, isThreeButtonNav = false,
+                        phoneMode = false)
+        assert(layoutter is KidsNavLayoutter)
+    }
+
+    @Test
+    fun getSetupLayoutter() {
+        assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW)
+        mockDeviceProfile.isTaskbarPresent = true
+        val layoutter: NavButtonLayoutFactory.NavButtonLayoutter =
+                getLayoutter(isKidsMode = false, isInSetup = true, isThreeButtonNav = false,
+                        phoneMode = false)
+        assert(layoutter is SetupNavLayoutter)
+    }
+
+    @Test
+    fun getTaskbarNavLayoutter() {
+        assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW)
+        mockDeviceProfile.isTaskbarPresent = true
+        val layoutter: NavButtonLayoutFactory.NavButtonLayoutter =
+                getLayoutter(isKidsMode = false, isInSetup = false, isThreeButtonNav = false,
+                        phoneMode = false)
+        assert(layoutter is TaskbarNavLayoutter)
+    }
+
+    @Test(expected = IllegalStateException::class)
+    fun noValidLayoutForLargeScreenTaskbarNotPresent() {
+        assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW)
+        mockDeviceProfile.isTaskbarPresent = false
+        getLayoutter(isKidsMode = false, isInSetup = false, isThreeButtonNav = false,
+                        phoneMode = false)
+    }
+
+    @Test
+    fun getTaskbarPortraitLayoutter() {
+        assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW)
+        mockDeviceProfile.isTaskbarPresent = false
+        val layoutter: NavButtonLayoutFactory.NavButtonLayoutter =
+                getLayoutter(isKidsMode = false, isInSetup = false, isThreeButtonNav = true,
+                        phoneMode = true)
+        assert(layoutter is PhonePortraitNavLayoutter)
+    }
+
+    @Test
+    fun getTaskbarLandscapeLayoutter() {
+        assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW)
+        mockDeviceProfile.isTaskbarPresent = false
+        setDeviceProfileLandscape()
+        val layoutter: NavButtonLayoutFactory.NavButtonLayoutter =
+                getLayoutter(isKidsMode = false, isInSetup = false, isThreeButtonNav = true,
+                        phoneMode = true)
+        assert(layoutter is PhoneLandscapeNavLayoutter)
+    }
+
+    @Test(expected = IllegalStateException::class)
+    fun noValidLayoutForPhoneGestureNav() {
+        assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW)
+        mockDeviceProfile.isTaskbarPresent = false
+        getLayoutter(isKidsMode = false, isInSetup = false, isThreeButtonNav = false,
+                phoneMode = true)
+    }
+
+    private fun setDeviceProfileLandscape() {
+        // Use reflection to modify landscape field
+        val landscapeField = mockDeviceProfile.javaClass.getDeclaredField("isLandscape")
+        landscapeField.isAccessible = true
+        landscapeField.set(mockDeviceProfile, true)
+    }
+
+    private fun getLayoutter(isKidsMode: Boolean, isInSetup: Boolean,
+                             isThreeButtonNav: Boolean, phoneMode: Boolean):
+            NavButtonLayoutFactory.NavButtonLayoutter {
+        return NavButtonLayoutFactory.getUiLayoutter(
+                deviceProfile = mockDeviceProfile,
+                navButtonsView = mockParentButtonContainer,
+                resources = mockResources,
+                isKidsMode = isKidsMode, isInSetup = isInSetup,
+                isThreeButtonNav = isThreeButtonNav, phoneMode = phoneMode
+        )
+    }
+}
\ No newline at end of file
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 47584e2..a9ba07d 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -368,6 +368,7 @@
     <dimen name="taskbar_hotseat_nav_spacing">0dp</dimen>
     <dimen name="taskbar_button_margin_default">0dp</dimen>
     <dimen name="taskbar_button_space_inbetween">0dp</dimen>
+    <dimen name="taskbar_button_space_inbetween_phone">0dp</dimen>
     <dimen name="taskbar_button_margin_5_5">0dp</dimen>
     <dimen name="taskbar_button_margin_6_5">0dp</dimen>
     <dimen name="taskbar_button_margin_4_5">0dp</dimen>
diff --git a/src/com/android/launcher3/util/DimensionUtils.kt b/src/com/android/launcher3/util/DimensionUtils.kt
new file mode 100644
index 0000000..758b3a9
--- /dev/null
+++ b/src/com/android/launcher3/util/DimensionUtils.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.launcher3.util
+
+import android.content.res.Resources
+import android.graphics.Point
+import android.view.ViewGroup
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.R
+
+object DimensionUtils {
+    /**
+     * Point where x is width, and y is height of taskbar based on provided [deviceProfile]
+     * x or y could also be -1 to indicate there is no dimension specified
+     */
+    @JvmStatic
+    fun getTaskbarPhoneDimensions(deviceProfile: DeviceProfile, res: Resources,
+                                  isPhoneMode: Boolean): Point {
+        val p = Point()
+        // Taskbar for large screen
+        if (!isPhoneMode) {
+            p.x = ViewGroup.LayoutParams.MATCH_PARENT
+            p.y = deviceProfile.taskbarSize
+            return p
+        }
+
+        // Taskbar on phone using gesture nav, it will always be stashed
+        if (deviceProfile.isGestureMode) {
+            p.x = ViewGroup.LayoutParams.MATCH_PARENT
+            p.y = res.getDimensionPixelSize(R.dimen.taskbar_stashed_size)
+            return p
+        }
+
+        // Taskbar on phone, portrait
+        if (!deviceProfile.isLandscape) {
+            p.x = ViewGroup.LayoutParams.MATCH_PARENT
+            p.y = res.getDimensionPixelSize(R.dimen.taskbar_size)
+            return p
+        }
+
+        // Taskbar on phone, landscape
+        p.x = res.getDimensionPixelSize(R.dimen.taskbar_size)
+        p.y = ViewGroup.LayoutParams.MATCH_PARENT
+        return p
+    }
+}
\ No newline at end of file