Add keyboard navigation for App Chip and Chip Menu
Bug: 400410772
Flag: com.android.launcher3.enable_overview_icon_menu
Test: Manual using Keyboard. Access the app chip via TAB.
Change-Id: I75f80b5c6f366ecfc27596519a721cc6ace18d45
diff --git a/quickstep/res/layout/icon_app_chip_view.xml b/quickstep/res/layout/icon_app_chip_view.xml
index de05d59..09fb509 100644
--- a/quickstep/res/layout/icon_app_chip_view.xml
+++ b/quickstep/res/layout/icon_app_chip_view.xml
@@ -22,7 +22,8 @@
android:layout_width="@dimen/task_thumbnail_icon_menu_expanded_width"
android:layout_height="@dimen/task_thumbnail_icon_menu_expanded_height"
android:clipToOutline="true"
- android:focusable="false"
+ android:focusable="true"
+ android:focusableInTouchMode="false"
android:importantForAccessibility="no"
android:autoMirrored="true"
android:elevation="@dimen/task_thumbnail_icon_menu_elevation"
diff --git a/quickstep/src/com/android/quickstep/views/IconAppChipView.kt b/quickstep/src/com/android/quickstep/views/IconAppChipView.kt
index c20aa11..7683a15 100644
--- a/quickstep/src/com/android/quickstep/views/IconAppChipView.kt
+++ b/quickstep/src/com/android/quickstep/views/IconAppChipView.kt
@@ -122,6 +122,9 @@
field = max(value, minMaxWidth)
}
+ var isExpanded: Boolean = false
+ private set
+
override fun onFinishInflate() {
super.onFinishInflate()
iconView = findViewById(R.id.icon_view)
@@ -356,6 +359,7 @@
ObjectAnimator.ofFloat(iconArrowView, SCALE_Y, -1f),
)
animator!!.setDuration(MENU_BACKGROUND_REVEAL_DURATION.toLong())
+ isExpanded = true
} else {
// Clip expanded text with reveal animation so it doesn't go beyond the edge of the menu
val expandedTextClipAnim =
@@ -390,6 +394,7 @@
ObjectAnimator.ofFloat(iconArrowView, SCALE_Y, 1f),
)
animator!!.setDuration(MENU_BACKGROUND_HIDE_DURATION.toLong())
+ isExpanded = false
}
if (!animated) animator!!.duration = 0
@@ -416,6 +421,17 @@
}
}
+ override fun focusSearch(direction: Int): View? {
+ if (mParent == null) return null
+ return when (direction) {
+ FOCUS_RIGHT,
+ FOCUS_DOWN -> mParent.focusSearch(this, View.FOCUS_FORWARD)
+ FOCUS_UP,
+ FOCUS_LEFT -> mParent.focusSearch(this, View.FOCUS_BACKWARD)
+ else -> super.focusSearch(direction)
+ }
+ }
+
override fun asView(): View = this
private companion object {
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 07eae21..6067550 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -4780,6 +4780,11 @@
if (isHandlingTouch() || event.getAction() != KeyEvent.ACTION_DOWN) {
return super.dispatchKeyEvent(event);
}
+
+ if (mUtils.shouldInterceptKeyEvent(event)) {
+ return super.dispatchKeyEvent(event);
+ }
+
switch (event.getKeyCode()) {
case KeyEvent.KEYCODE_TAB:
return snapToPageRelative(event.isShiftPressed() ? -1 : 1, true /* cycle */,
diff --git a/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt b/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt
index 29601ef..24b7fa7 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt
+++ b/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt
@@ -18,8 +18,11 @@
import android.graphics.Rect
import android.util.FloatProperty
+import android.view.KeyEvent
import android.view.View
import androidx.core.view.children
+import com.android.launcher3.AbstractFloatingView
+import com.android.launcher3.Flags
import com.android.launcher3.Flags.enableLargeDesktopWindowingTile
import com.android.launcher3.Flags.enableSeparateExternalDisplayTasks
import com.android.launcher3.statehandlers.DesktopVisibilityController
@@ -354,6 +357,18 @@
}
}
+ fun shouldInterceptKeyEvent(event: KeyEvent): Boolean {
+ if (Flags.enableOverviewIconMenu()) {
+ val floatingView: AbstractFloatingView? = AbstractFloatingView.getTopOpenViewWithType(
+ recentsView.mContainer as RecentsViewContainer,
+ AbstractFloatingView.TYPE_TASK_MENU
+ )
+ val isMenuOpen = floatingView?.isOpen
+ return isMenuOpen == true || event.keyCode == KeyEvent.KEYCODE_TAB
+ }
+ return false
+ }
+
var deskExplodeProgress: Float = 0f
set(value) {
field = value
diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuView.kt b/quickstep/src/com/android/quickstep/views/TaskMenuView.kt
index 6bc0666..696f934 100644
--- a/quickstep/src/com/android/quickstep/views/TaskMenuView.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskMenuView.kt
@@ -26,6 +26,7 @@
import android.graphics.drawable.shapes.RectShape
import android.util.AttributeSet
import android.view.Gravity
+import android.view.KeyEvent
import android.view.MotionEvent
import android.view.View
import android.view.ViewOutlineProvider
@@ -465,6 +466,24 @@
animatorBuilder.with(menuTranslationXAnim)
}
+ override fun dispatchKeyEvent(event: KeyEvent): Boolean {
+ if (enableOverviewIconMenu()) {
+ if (event.action != KeyEvent.ACTION_DOWN) return super.dispatchKeyEvent(event)
+
+ val isFirstMenuOptionFocused = optionLayout.indexOfChild(optionLayout.focusedChild) == 0
+ val isLastMenuOptionFocused =
+ optionLayout.indexOfChild(optionLayout.focusedChild) == optionLayout.childCount - 1
+ if (
+ (isLastMenuOptionFocused && event.keyCode == KeyEvent.KEYCODE_DPAD_DOWN)
+ || (isFirstMenuOptionFocused && event.keyCode == KeyEvent.KEYCODE_DPAD_UP)
+ ) {
+ iconView.requestFocus()
+ return true
+ }
+ }
+ return super.dispatchKeyEvent(event)
+ }
+
companion object {
private val REVEAL_OPEN_DURATION = if (enableOverviewIconMenu()) 417L else 150L
private val REVEAL_CLOSE_DURATION = if (enableOverviewIconMenu()) 333L else 100L
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.kt b/quickstep/src/com/android/quickstep/views/TaskView.kt
index cdf5f81..8d95b13 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskView.kt
@@ -43,6 +43,7 @@
import androidx.core.view.updateLayoutParams
import com.android.app.animation.Interpolators
import com.android.app.tracing.traceSection
+import com.android.launcher3.AbstractFloatingView
import com.android.launcher3.Flags.enableCursorHoverStates
import com.android.launcher3.Flags.enableDesktopExplodedView
import com.android.launcher3.Flags.enableGridOnlyOverview
@@ -1482,6 +1483,19 @@
return showTaskMenuWithContainer(menuContainer)
}
+ private fun closeTaskMenu(): Boolean {
+ val floatingView: AbstractFloatingView? = AbstractFloatingView.getTopOpenViewWithType(
+ container,
+ AbstractFloatingView.TYPE_TASK_MENU
+ )
+ if (floatingView?.isOpen == true) {
+ floatingView.close(true)
+ return true
+ } else {
+ return false
+ }
+ }
+
private fun showTaskMenuWithContainer(menuContainer: TaskContainer): Boolean {
val recentsView = recentsView ?: return false
if (enableHoverOfChildElementsInTaskview()) {
@@ -1489,12 +1503,16 @@
recentsView.setTaskBorderEnabled(false)
}
return if (enableOverviewIconMenu() && menuContainer.iconView is IconAppChipView) {
- menuContainer.iconView.revealAnim(/* isRevealing= */ true)
- TaskMenuView.showForTask(menuContainer) {
- val isAnimated = !recentsView.isSplitSelectionActive
- menuContainer.iconView.revealAnim(/* isRevealing= */ false, isAnimated)
- if (enableHoverOfChildElementsInTaskview()) {
- recentsView.setTaskBorderEnabled(true)
+ if (menuContainer.iconView.isExpanded) {
+ closeTaskMenu()
+ } else {
+ menuContainer.iconView.revealAnim(/* isRevealing= */ true)
+ TaskMenuView.showForTask(menuContainer) {
+ val isAnimated = !recentsView.isSplitSelectionActive
+ menuContainer.iconView.revealAnim(/* isRevealing= */ false, isAnimated)
+ if (enableHoverOfChildElementsInTaskview()) {
+ recentsView.setTaskBorderEnabled(true)
+ }
}
}
} else if (container.deviceProfile.isTablet) {