Make AddDesktopButton can be navigated to through keys

Add the focus outline to the button as well.
Same color, ripple effect as the ClearAllButton is applied.

Flag: com.android.window.flags.enable_multiple_desktops_frontend
      com.android.window.flags.enable_multiple_desktops_backend
Bug:391875235
Test: TaskGridNavHelperTest
Change-Id: If93f86a1dce4df8ee6b1c50dfc44092dad2eafab
This commit is contained in:
minch
2025-02-07 18:33:23 +00:00
parent 734411999e
commit b39d3015f8
11 changed files with 329 additions and 93 deletions
@@ -29,6 +29,7 @@ import java.util.List;
*/
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 class TaskGridNavHelper {
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);
}
/**
@@ -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 @@ class AddDesktopButton @JvmOverloads constructor(context: Context, attrs: Attrib
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)
}
}
@@ -1632,6 +1632,9 @@ public abstract class RecentsView<
taskView.setBorderEnabled(enabled);
}
mClearAllButton.setBorderEnabled(enabled);
if (mAddDesktopButton != null) {
mAddDesktopButton.setBorderEnabled(enabled);
}
}
/**
@@ -4540,24 +4543,32 @@ public abstract class RecentsView<
// 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) {
@@ -6095,8 +6106,10 @@ public abstract class RecentsView<
}
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();
@@ -248,8 +248,43 @@ constructor(
)
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 @@ constructor(
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)