Merge "Make AddDesktopButton can be navigated to through keys" into main
diff --git a/quickstep/res/drawable/bg_overview_add_desktop_button.xml b/quickstep/res/drawable/bg_overview_add_desktop_button.xml
new file mode 100644
index 0000000..12581bf
--- /dev/null
+++ b/quickstep/res/drawable/bg_overview_add_desktop_button.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2025 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.
+-->
+<ripple android:color="?android:attr/colorControlHighlight"
+    xmlns:android="http://schemas.android.com/apk/res/android">
+    <item>
+        <shape
+            android:shape="rectangle"
+            android:tint="?colorButtonNormal">
+            <corners android:radius="@dimen/add_desktop_button_size" />
+            <solid android:color="@color/materialColorSurfaceBright"/>
+        </shape>
+    </item>
+</ripple>
\ No newline at end of file
diff --git a/quickstep/res/drawable/bg_overview_clear_all_button.xml b/quickstep/res/drawable/bg_overview_clear_all_button.xml
index 7f58cf8..2f28689 100644
--- a/quickstep/res/drawable/bg_overview_clear_all_button.xml
+++ b/quickstep/res/drawable/bg_overview_clear_all_button.xml
@@ -15,8 +15,7 @@
      limitations under the License.
 -->
 <ripple android:color="?android:attr/colorControlHighlight"
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+    xmlns:android="http://schemas.android.com/apk/res/android">
     <item>
         <shape android:shape="rectangle"
             android:tint="?colorButtonNormal">
diff --git a/quickstep/res/layout/overview_add_desktop_button.xml b/quickstep/res/layout/overview_add_desktop_button.xml
index e36cf72..a1c64f3 100644
--- a/quickstep/res/layout/overview_add_desktop_button.xml
+++ b/quickstep/res/layout/overview_add_desktop_button.xml
@@ -16,9 +16,11 @@
 -->
 <com.android.quickstep.views.AddDesktopButton
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:launcher="http://schemas.android.com/apgk/res-auto"
+    xmlns:launcher="http://schemas.android.com/apk/res-auto"
     android:id="@+id/add_desktop_button"
     android:layout_width="@dimen/add_desktop_button_size"
     android:layout_height="@dimen/add_desktop_button_size"
     android:src="@drawable/ic_desktop_add"
-    android:padding="10dp" />
\ No newline at end of file
+    android:background="@drawable/bg_overview_add_desktop_button"
+    launcher:focusBorderColor="@color/materialColorOutline"
+    android:padding="10dp" />
diff --git a/quickstep/res/layout/overview_clear_all_button.xml b/quickstep/res/layout/overview_clear_all_button.xml
index 18a6240..034c3c2 100644
--- a/quickstep/res/layout/overview_clear_all_button.xml
+++ b/quickstep/res/layout/overview_clear_all_button.xml
@@ -16,7 +16,6 @@
 -->
 <com.android.quickstep.views.ClearAllButton
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     xmlns:launcher="http://schemas.android.com/apk/res-auto"
     style="@style/OverviewClearAllButton"
     android:id="@+id/clear_all"
@@ -25,4 +24,4 @@
     android:text="@string/recents_clear_all"
     android:textColor="@color/materialColorOnSurface"
     launcher:focusBorderColor="@color/materialColorOutline"
-    android:textSize="14sp" />
\ No newline at end of file
+    android:textSize="14sp" />
diff --git a/quickstep/res/values/attrs.xml b/quickstep/res/values/attrs.xml
index 7fd6b5c..28c0d5c 100644
--- a/quickstep/res/values/attrs.xml
+++ b/quickstep/res/values/attrs.xml
@@ -36,6 +36,11 @@
         <attr name="focusBorderColor" />
     </declare-styleable>
 
+    <declare-styleable name="AddDesktopButton">
+        <!-- focus border color for overview add desktop button views -->
+        <attr name="focusBorderColor" />
+    </declare-styleable>
+
     <!--
          Gesture nav edu specific attributes. These attributes are used to customize Gesture nav edu
          view lottie animation colors in XML files.
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index b253343..f2f1ebd 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -108,6 +108,7 @@
 
     <!-- Recents add desktop button -->
     <dimen name="add_desktop_button_size">56dp</dimen>
+    <dimen name="add_desktop_button_outline_padding">2dp</dimen>
 
     <!-- The speed in dp/s at which the user needs to be scrolling in recents such that we start
              loading full resolution screenshots. -->
diff --git a/quickstep/src/com/android/quickstep/util/TaskGridNavHelper.java b/quickstep/src/com/android/quickstep/util/TaskGridNavHelper.java
index 498078b..ceffbe4 100644
--- a/quickstep/src/com/android/quickstep/util/TaskGridNavHelper.java
+++ b/quickstep/src/com/android/quickstep/util/TaskGridNavHelper.java
@@ -29,6 +29,7 @@
  */
 public class TaskGridNavHelper {
     public static final int CLEAR_ALL_PLACEHOLDER_ID = -1;
+    public static final int ADD_DESK_PLACEHOLDER_ID = -2;
 
     public static final int DIRECTION_UP = 0;
     public static final int DIRECTION_DOWN = 1;
@@ -41,44 +42,42 @@
     public @interface TASK_NAV_DIRECTION {}
 
     private final IntArray mOriginalTopRowIds;
-    private IntArray mTopRowIds;
-    private IntArray mBottomRowIds;
+    private final IntArray mTopRowIds = new IntArray();
+    private final IntArray mBottomRowIds = new IntArray();
 
     public TaskGridNavHelper(IntArray topIds, IntArray bottomIds,
-            List<Integer> largeTileIds) {
+            List<Integer> largeTileIds, boolean hasAddDesktopButton) {
         mOriginalTopRowIds = topIds.clone();
-        generateTaskViewIdGrid(topIds, bottomIds, largeTileIds);
+        generateTaskViewIdGrid(topIds, bottomIds, largeTileIds, hasAddDesktopButton);
     }
 
     private void generateTaskViewIdGrid(IntArray topRowIdArray, IntArray bottomRowIdArray,
-            List<Integer> largeTileIds) {
-
-        int maxSize = Math.max(topRowIdArray.size(), bottomRowIdArray.size())
-                + largeTileIds.size();
-        int minSize = Math.min(topRowIdArray.size(), bottomRowIdArray.size())
-                + largeTileIds.size();
-
-        // Add Large tile task views first at the beginning
-        for (int i = 0; i < largeTileIds.size(); i++) {
-            topRowIdArray.add(i, largeTileIds.get(i));
-            bottomRowIdArray.add(i, largeTileIds.get(i));
+            List<Integer> largeTileIds, boolean hasAddDesktopButton) {
+        // Add AddDesktopButton and lage tiles to both rows.
+        if (hasAddDesktopButton) {
+            mTopRowIds.add(ADD_DESK_PLACEHOLDER_ID);
+            mBottomRowIds.add(ADD_DESK_PLACEHOLDER_ID);
         }
+        for (Integer tileId : largeTileIds) {
+            mTopRowIds.add(tileId);
+            mBottomRowIds.add(tileId);
+        }
+
+        // Add row ids to their respective rows.
+        mTopRowIds.addAll(topRowIdArray);
+        mBottomRowIds.addAll(bottomRowIdArray);
 
         // Fill in the shorter array with the ids from the longer one.
-        for (int i = minSize; i < maxSize; i++) {
-            if (i >= topRowIdArray.size()) {
-                topRowIdArray.add(bottomRowIdArray.get(i));
-            } else {
-                bottomRowIdArray.add(topRowIdArray.get(i));
-            }
+        while (mTopRowIds.size() > mBottomRowIds.size()) {
+            mBottomRowIds.add(mTopRowIds.get(mBottomRowIds.size()));
+        }
+        while (mBottomRowIds.size() > mTopRowIds.size()) {
+            mTopRowIds.add(mBottomRowIds.get(mTopRowIds.size()));
         }
 
-        // Add the clear all button to the end of both arrays
-        topRowIdArray.add(CLEAR_ALL_PLACEHOLDER_ID);
-        bottomRowIdArray.add(CLEAR_ALL_PLACEHOLDER_ID);
-
-        mTopRowIds = topRowIdArray;
-        mBottomRowIds = bottomRowIdArray;
+        // Add the clear all button to the end of both arrays.
+        mTopRowIds.add(CLEAR_ALL_PLACEHOLDER_ID);
+        mBottomRowIds.add(CLEAR_ALL_PLACEHOLDER_ID);
     }
 
     /**
diff --git a/quickstep/src/com/android/quickstep/views/AddDesktopButton.kt b/quickstep/src/com/android/quickstep/views/AddDesktopButton.kt
index e353160..9f3c017 100644
--- a/quickstep/src/com/android/quickstep/views/AddDesktopButton.kt
+++ b/quickstep/src/com/android/quickstep/views/AddDesktopButton.kt
@@ -17,13 +17,15 @@
 package com.android.quickstep.views
 
 import android.content.Context
-import android.graphics.drawable.ShapeDrawable
-import android.graphics.drawable.shapes.RoundRectShape
+import android.graphics.Canvas
+import android.graphics.Rect
 import android.util.AttributeSet
 import android.widget.ImageButton
 import com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X
 import com.android.launcher3.R
 import com.android.launcher3.util.MultiPropertyFactory
+import com.android.quickstep.util.BorderAnimator
+import com.android.quickstep.util.BorderAnimator.Companion.createSimpleBorderAnimator
 
 /**
  * Button for supporting multiple desktop sessions. The button will be next to the first TaskView
@@ -55,20 +57,49 @@
             multiTranslationX[TranslationX.OFFSET.ordinal].value = value
         }
 
-    override fun onFinishInflate() {
-        super.onFinishInflate()
+    private val focusBorderAnimator: BorderAnimator =
+        createSimpleBorderAnimator(
+            context.resources.getDimensionPixelSize(R.dimen.add_desktop_button_size),
+            context.resources.getDimensionPixelSize(R.dimen.keyboard_quick_switch_border_width),
+            this::getBorderBounds,
+            this,
+            context
+                .obtainStyledAttributes(attrs, R.styleable.AddDesktopButton)
+                .getColor(
+                    R.styleable.AddDesktopButton_focusBorderColor,
+                    BorderAnimator.DEFAULT_BORDER_COLOR,
+                ),
+        )
 
-        background =
-            ShapeDrawable().apply {
-                shape =
-                    RoundRectShape(
-                        FloatArray(8) { R.dimen.add_desktop_button_size.toFloat() },
-                        null,
-                        null,
-                    )
-                setTint(
-                    resources.getColor(android.R.color.system_surface_bright_light, context.theme)
-                )
+    var borderEnabled = false
+        set(value) {
+            if (field == value) {
+                return
             }
+            field = value
+            focusBorderAnimator.setBorderVisibility(visible = field && isFocused, animated = true)
+        }
+
+    public override fun onFocusChanged(
+        gainFocus: Boolean,
+        direction: Int,
+        previouslyFocusedRect: Rect?,
+    ) {
+        super.onFocusChanged(gainFocus, direction, previouslyFocusedRect)
+        if (borderEnabled) {
+            focusBorderAnimator.setBorderVisibility(gainFocus, /* animated= */ true)
+        }
+    }
+
+    private fun getBorderBounds(bounds: Rect) {
+        bounds.set(0, 0, width, height)
+        val outlinePadding =
+            context.resources.getDimensionPixelSize(R.dimen.add_desktop_button_outline_padding)
+        bounds.inset(-outlinePadding, -outlinePadding)
+    }
+
+    override fun draw(canvas: Canvas) {
+        focusBorderAnimator.drawBorder(canvas)
+        super.draw(canvas)
     }
 }
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index dec603b..99bfa7e 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -1633,6 +1633,9 @@
             taskView.setBorderEnabled(enabled);
         }
         mClearAllButton.setBorderEnabled(enabled);
+        if (mAddDesktopButton != null) {
+            mAddDesktopButton.setBorderEnabled(enabled);
+        }
     }
 
     /**
@@ -4571,24 +4574,32 @@
 
         // Init task grid nav helper with top/bottom id arrays.
         TaskGridNavHelper taskGridNavHelper = new TaskGridNavHelper(getTopRowIdArray(),
-                getBottomRowIdArray(), mUtils.getLargeTaskViewIds());
+                getBottomRowIdArray(), mUtils.getLargeTaskViewIds(), mAddDesktopButton != null);
 
         // Get current page's task view ID.
         TaskView currentPageTaskView = getCurrentPageTaskView();
         int currentPageTaskViewId;
+        final int clearAllButtonIndex = indexOfChild(mClearAllButton);
+        final int addDesktopButtonIndex = indexOfChild(mAddDesktopButton);
         if (currentPageTaskView != null) {
             currentPageTaskViewId = currentPageTaskView.getTaskViewId();
-        } else if (mCurrentPage == indexOfChild(mClearAllButton)) {
+        } else if (mCurrentPage == clearAllButtonIndex) {
             currentPageTaskViewId = TaskGridNavHelper.CLEAR_ALL_PLACEHOLDER_ID;
+        } else if (mCurrentPage == addDesktopButtonIndex) {
+            currentPageTaskViewId = TaskGridNavHelper.ADD_DESK_PLACEHOLDER_ID;
         } else {
             return INVALID_PAGE;
         }
 
-        int nextGridPage =
+        final int nextGridPage =
                 taskGridNavHelper.getNextGridPage(currentPageTaskViewId, delta, direction, cycle);
-        return nextGridPage == TaskGridNavHelper.CLEAR_ALL_PLACEHOLDER_ID
-                ? indexOfChild(mClearAllButton)
-                : indexOfChild(getTaskViewFromTaskViewId(nextGridPage));
+        if (nextGridPage == TaskGridNavHelper.CLEAR_ALL_PLACEHOLDER_ID) {
+            return clearAllButtonIndex;
+        }
+        if (nextGridPage == TaskGridNavHelper.ADD_DESK_PLACEHOLDER_ID) {
+            return addDesktopButtonIndex;
+        }
+        return indexOfChild(getTaskViewFromTaskViewId(nextGridPage));
     }
 
     private void runDismissAnimation(PendingAnimation pendingAnim) {
@@ -6126,8 +6137,10 @@
     }
 
     private int getFirstViewIndex() {
-        final TaskView firstView;
-        if (mShowAsGridLastOnLayout) {
+        final View firstView;
+        if (mAddDesktopButton != null) {
+            firstView = mAddDesktopButton;
+        } else if (mShowAsGridLastOnLayout) {
             // For grid Overview, it always start if a large tile (focused task or desktop task) if
             // they exist, otherwise it start with the first task.
             TaskView firstLargeTaskView = mUtils.getFirstLargeTaskView();
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.kt b/quickstep/src/com/android/quickstep/views/TaskView.kt
index 4b1b8dc..0465dbc 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskView.kt
@@ -248,8 +248,43 @@
             )
 
     private val tempCoordinates = FloatArray(2)
-    private val focusBorderAnimator: BorderAnimator?
-    private val hoverBorderAnimator: BorderAnimator?
+    private val focusBorderAnimator: BorderAnimator? =
+        focusBorderAnimator
+            ?: createSimpleBorderAnimator(
+                TaskCornerRadius.get(context).toInt(),
+                context.resources.getDimensionPixelSize(R.dimen.keyboard_quick_switch_border_width),
+                this::getThumbnailBounds,
+                this,
+                context
+                    .obtainStyledAttributes(attrs, R.styleable.TaskView, defStyleAttr, defStyleRes)
+                    .getColor(
+                        R.styleable.TaskView_focusBorderColor,
+                        BorderAnimator.DEFAULT_BORDER_COLOR,
+                    ),
+            )
+
+    private val hoverBorderAnimator: BorderAnimator? =
+        hoverBorderAnimator
+            ?: if (enableCursorHoverStates())
+                createSimpleBorderAnimator(
+                    TaskCornerRadius.get(context).toInt(),
+                    context.resources.getDimensionPixelSize(R.dimen.task_hover_border_width),
+                    this::getThumbnailBounds,
+                    this,
+                    context
+                        .obtainStyledAttributes(
+                            attrs,
+                            R.styleable.TaskView,
+                            defStyleAttr,
+                            defStyleRes,
+                        )
+                        .getColor(
+                            R.styleable.TaskView_hoverBorderColor,
+                            BorderAnimator.DEFAULT_BORDER_COLOR,
+                        ),
+                )
+            else null
+
     private val rootViewDisplayId: Int
         get() = rootView.display?.displayId ?: Display.DEFAULT_DISPLAY
 
@@ -519,40 +554,7 @@
     init {
         setOnClickListener { _ -> onClick() }
 
-        val cursorHoverStatesEnabled = enableCursorHoverStates()
-        setWillNotDraw(!cursorHoverStatesEnabled)
-        context.obtainStyledAttributes(attrs, R.styleable.TaskView, defStyleAttr, defStyleRes).use {
-            this.focusBorderAnimator =
-                focusBorderAnimator
-                    ?: createSimpleBorderAnimator(
-                        TaskCornerRadius.get(context).toInt(),
-                        context.resources.getDimensionPixelSize(
-                            R.dimen.keyboard_quick_switch_border_width
-                        ),
-                        { bounds: Rect -> getThumbnailBounds(bounds) },
-                        this,
-                        it.getColor(
-                            R.styleable.TaskView_focusBorderColor,
-                            BorderAnimator.DEFAULT_BORDER_COLOR,
-                        ),
-                    )
-            this.hoverBorderAnimator =
-                hoverBorderAnimator
-                    ?: if (cursorHoverStatesEnabled)
-                        createSimpleBorderAnimator(
-                            TaskCornerRadius.get(context).toInt(),
-                            context.resources.getDimensionPixelSize(
-                                R.dimen.task_hover_border_width
-                            ),
-                            { bounds: Rect -> getThumbnailBounds(bounds) },
-                            this,
-                            it.getColor(
-                                R.styleable.TaskView_hoverBorderColor,
-                                BorderAnimator.DEFAULT_BORDER_COLOR,
-                            ),
-                        )
-                    else null
-        }
+        setWillNotDraw(!enableCursorHoverStates())
     }
 
     @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/util/TaskGridNavHelperTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/util/TaskGridNavHelperTest.kt
index 7aab75f..7066d21 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/util/TaskGridNavHelperTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/util/TaskGridNavHelperTest.kt
@@ -16,6 +16,7 @@
 package com.android.quickstep.util
 
 import com.android.launcher3.util.IntArray
+import com.android.quickstep.util.TaskGridNavHelper.ADD_DESK_PLACEHOLDER_ID
 import com.android.quickstep.util.TaskGridNavHelper.CLEAR_ALL_PLACEHOLDER_ID
 import com.android.quickstep.util.TaskGridNavHelper.DIRECTION_DOWN
 import com.android.quickstep.util.TaskGridNavHelper.DIRECTION_LEFT
@@ -619,6 +620,161 @@
             .isEqualTo(CLEAR_ALL_PLACEHOLDER_ID)
     }
 
+    /*
+                        5   3   1→----|
+                                      ↓
+         CLEAR_ALL                   ADD_DESKTOP
+                        6   4   2
+    */
+    @Test
+    fun withAddDesktopButton_pressRightFromTop_goesToAddDesktopButton() {
+        assertThat(
+                getNextGridPage(
+                    currentPageTaskViewId = 1,
+                    DIRECTION_RIGHT,
+                    delta = -1,
+                    hasAddDesktopButton = true,
+                )
+            )
+            .isEqualTo(ADD_DESK_PLACEHOLDER_ID)
+    }
+
+    /*
+                    5   3   1
+       CLEAR_ALL                 ADD_DESKTOP
+                                  ↑
+                    6   4   2→----↑
+    */
+    @Test
+    fun withAddDesktopButton_pressRightFromBottom_goesToAddDesktopButton() {
+        assertThat(
+                getNextGridPage(
+                    currentPageTaskViewId = 2,
+                    DIRECTION_RIGHT,
+                    delta = -1,
+                    hasAddDesktopButton = true,
+                )
+            )
+            .isEqualTo(ADD_DESK_PLACEHOLDER_ID)
+    }
+
+    /*
+         ↓-------------------------------←|
+         |                                ↑
+         ↓      5   3   1                 |
+    CLEAR_ALL               ADD_DESKTOP--→
+                6   4   2
+    */
+    @Test
+    fun withAddDesktopButton_pressRightFromAddDesktopButton_goesToClearAllButton() {
+        assertThat(
+                getNextGridPage(
+                    currentPageTaskViewId = ADD_DESK_PLACEHOLDER_ID,
+                    DIRECTION_RIGHT,
+                    delta = -1,
+                    hasAddDesktopButton = true,
+                )
+            )
+            .isEqualTo(CLEAR_ALL_PLACEHOLDER_ID)
+    }
+
+    /*
+           |→--------------------------------|
+           |                                 |
+           ↑                5   3   1        ↓
+           ←------CLEAR_ALL             ADD_DESKTOP
+
+                            6   4   2
+    */
+    @Test
+    fun withAddDesktopButton_pressLeftFromClearAllButton_goesToAddDesktopButton() {
+        assertThat(
+                getNextGridPage(
+                    currentPageTaskViewId = CLEAR_ALL_PLACEHOLDER_ID,
+                    DIRECTION_LEFT,
+                    delta = 1,
+                    hasAddDesktopButton = true,
+                )
+            )
+            .isEqualTo(ADD_DESK_PLACEHOLDER_ID)
+    }
+
+    /*
+                        5   3   1
+                                   ←--↑
+        CLEAR_ALL                  ↓-→ADD_DESKTOP
+                        6   4   2
+    */
+    @Test
+    fun withAddDesktopButton_pressUpOnAddDesktop_stayOnAddDesktopButton() {
+        assertThat(
+                getNextGridPage(
+                    currentPageTaskViewId = ADD_DESK_PLACEHOLDER_ID,
+                    DIRECTION_UP,
+                    delta = 1,
+                    hasAddDesktopButton = true,
+                )
+            )
+            .isEqualTo(ADD_DESK_PLACEHOLDER_ID)
+    }
+
+    /*
+                        5   3   1
+        CLEAR_ALL                  ↑--→ADD_DESKTOP
+                                   ↑←--↓
+                        6   4   2
+    */
+    @Test
+    fun withAddDesktopButton_pressDownOnAddDesktop_stayOnAddDesktopButton() {
+        assertThat(
+                getNextGridPage(
+                    currentPageTaskViewId = ADD_DESK_PLACEHOLDER_ID,
+                    DIRECTION_DOWN,
+                    delta = 1,
+                    hasAddDesktopButton = true,
+                )
+            )
+            .isEqualTo(ADD_DESK_PLACEHOLDER_ID)
+    }
+
+    /*
+                        5   3   1
+           CLEAR_ALL                DESKTOP--→ADD_DESKTOP
+                        6   4   2
+    */
+    @Test
+    fun withAddDesktopButton_pressRightFromDesktopTask_goesToAddDesktopButton() {
+        assertThat(
+                getNextGridPage(
+                    currentPageTaskViewId = ADD_DESK_PLACEHOLDER_ID,
+                    DIRECTION_LEFT,
+                    delta = 1,
+                    largeTileIds = listOf(DESKTOP_TASK_ID),
+                    hasAddDesktopButton = true,
+                )
+            )
+            .isEqualTo(DESKTOP_TASK_ID)
+    }
+
+    /*
+                        5   3   1
+           CLEAR_ALL                DESKTOP←--ADD_DESKTOP
+                        6   4   2
+    */
+    @Test
+    fun withAddDesktopButton_pressLeftFromAddDesktopButton_goesToDesktopTask() {
+        assertThat(
+                getNextGridPage(
+                    currentPageTaskViewId = DESKTOP_TASK_ID,
+                    DIRECTION_RIGHT,
+                    delta = -1,
+                    largeTileIds = listOf(DESKTOP_TASK_ID),
+                    hasAddDesktopButton = true,
+                )
+            )
+            .isEqualTo(ADD_DESK_PLACEHOLDER_ID)
+    }
+
     private fun getNextGridPage(
         currentPageTaskViewId: Int,
         direction: Int,
@@ -626,8 +782,10 @@
         topIds: IntArray = IntArray.wrap(1, 3, 5),
         bottomIds: IntArray = IntArray.wrap(2, 4, 6),
         largeTileIds: List<Int> = emptyList(),
+        hasAddDesktopButton: Boolean = false,
     ): Int {
-        val taskGridNavHelper = TaskGridNavHelper(topIds, bottomIds, largeTileIds)
+        val taskGridNavHelper =
+            TaskGridNavHelper(topIds, bottomIds, largeTileIds, hasAddDesktopButton)
         return taskGridNavHelper.getNextGridPage(currentPageTaskViewId, delta, direction, true)
     }