diff --git a/quickstep/res/layout/icon_app_chip_view.xml b/quickstep/res/layout/icon_app_chip_view.xml index de05d591fc..09fb5094fe 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 c20aa11599..7683a1500a 100644 --- a/quickstep/src/com/android/quickstep/views/IconAppChipView.kt +++ b/quickstep/src/com/android/quickstep/views/IconAppChipView.kt @@ -122,6 +122,9 @@ constructor( 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 @@ constructor( 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 @@ constructor( ObjectAnimator.ofFloat(iconArrowView, SCALE_Y, 1f), ) animator!!.setDuration(MENU_BACKGROUND_HIDE_DURATION.toLong()) + isExpanded = false } if (!animated) animator!!.duration = 0 @@ -416,6 +421,17 @@ constructor( } } + 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 07eae21bdc..6067550507 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -4780,6 +4780,11 @@ public abstract class RecentsView< 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 29601effea..24b7fa7c81 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt +++ b/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt @@ -18,8 +18,11 @@ package com.android.quickstep.views 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 @@ class RecentsViewUtils(private val recentsView: RecentsView<*, *>) { } } + 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 6bc066631a..696f934701 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.ShapeDrawable 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 @@ constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int = 0) : 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 cdf5f81be3..8d95b131bf 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.annotation.VisibleForTesting 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 @@ constructor( 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 @@ constructor( 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) {