From 43ba3e927689bb05f8bfadad925cfb95dfb37b83 Mon Sep 17 00:00:00 2001 From: Jordan Silva Date: Thu, 13 Mar 2025 18:33:51 +0000 Subject: [PATCH] Fix App Chip positioning in Fake Orientation This CL changes how the layoutDirection of the App Chip is handled and update translations in the PagedViewHandler for the app chip for fake orientation. - The App Chip was updated to use the device layoutDirection instead of the same of RecentsView. This prevents the app chip being displayed in RTL when the device is LTR and vice-versa. - It also applies the correct layout direction to `IconAppChipView` and its related views to ensure proper RTL behavior. - RecentsView now updates layoutDirection for its children when updateChildTaskOrientation is called. This will make sure its children have the the latest and the correct layoutDirection.. Before it was only applied when adding a view (addView). - In `SeascapePagedViewHandler`, it adjust the gravity for app chip be aligned correctly in Seascape. - In `GroupedTaskView`, it checks if the device is in RTL to align the app chip correctly for split tasks. - Multiple test combinations were done to validate this fix, please see table in b/326377497#comment7. Fix: 326377497 Bug: 403284758 Flag: com.android.launcher3.enable_overview_icon_menu Test: Manual, please check b/326377497#comment7. Test: OverviewImageTest Test: OverviewSplitTaskImageTest Change-Id: I8e8050ccf58c3f90718a48773ee7e32ddbc00f13 --- .../orientation/SeascapePagedViewHandler.kt | 6 ++-- .../quickstep/views/GroupedTaskView.kt | 6 ++-- .../android/quickstep/views/RecentsView.java | 31 ++++------------ .../quickstep/views/RecentsViewUtils.kt | 36 ++++++++++++++----- .../com/android/quickstep/views/TaskView.kt | 19 +++++++--- .../SeascapePagedViewHandlerTest.kt | 14 +++----- 6 files changed, 58 insertions(+), 54 deletions(-) diff --git a/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt b/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt index 67358bbb45..80b50cbd83 100644 --- a/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt +++ b/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt @@ -248,14 +248,14 @@ class SeascapePagedViewHandler : LandscapePagedViewHandler() { val iconCenter = iconAppChipView.getHeight() / 2f if (isRtl) { - iconMenuParams.gravity = Gravity.TOP or Gravity.END + iconMenuParams.gravity = Gravity.TOP or Gravity.START iconMenuParams.topMargin = iconMenuMargin iconMenuParams.marginEnd = thumbnailTopMargin // Use half menu height to place the pivot within the X/Y center of icon in the menu. iconAppChipView.pivotX = iconMenuParams.width / 2f iconAppChipView.pivotY = iconMenuParams.width / 2f } else { - iconMenuParams.gravity = Gravity.BOTTOM or Gravity.START + iconMenuParams.gravity = Gravity.BOTTOM or Gravity.END iconMenuParams.topMargin = 0 iconMenuParams.marginEnd = 0 iconAppChipView.pivotX = iconCenter @@ -409,7 +409,7 @@ class SeascapePagedViewHandler : LandscapePagedViewHandler() { if (Flags.enableOverviewIconMenu()) { val appChipView = iconView as IconAppChipView layoutParams.gravity = - if (isRtl) Gravity.TOP or Gravity.END else Gravity.BOTTOM or Gravity.START + if (isRtl) Gravity.TOP or Gravity.START else Gravity.BOTTOM or Gravity.END appChipView.layoutParams = layoutParams appChipView.setSplitTranslationX(0f) appChipView.setSplitTranslationY(translationY.toFloat()) diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt b/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt index 71a4dde395..10a2e902cb 100644 --- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt +++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt @@ -182,7 +182,6 @@ class GroupedTaskView @JvmOverloads constructor(context: Context, attrs: Attribu val splitBoundsConfig = splitBoundsConfig ?: return val deviceProfile = container.deviceProfile val taskIconHeight = deviceProfile.overviewTaskIconSizePx - val isRtl = layoutDirection == LAYOUT_DIRECTION_RTL val inSplitSelection = getThisTaskCurrentlyInSplitSelection() != INVALID_TASK_ID var oneIconHiddenDueToSmallWidth = false @@ -211,6 +210,7 @@ class GroupedTaskView @JvmOverloads constructor(context: Context, attrs: Attribu } if (enableOverviewIconMenu()) { + val isDeviceRtl = Utilities.isRtl(resources) val groupedTaskViewSizes = pagedOrientationHandler.getGroupedTaskViewSizes( deviceProfile, @@ -226,7 +226,7 @@ class GroupedTaskView @JvmOverloads constructor(context: Context, attrs: Attribu groupedTaskViewSizes.first.y, layoutParams.height, layoutParams.width, - isRtl, + isDeviceRtl, deviceProfile, splitBoundsConfig, inSplitSelection, @@ -241,7 +241,7 @@ class GroupedTaskView @JvmOverloads constructor(context: Context, attrs: Attribu leftTopTaskContainer.taskContentView.measuredHeight, measuredHeight, measuredWidth, - isRtl, + isLayoutRtl, deviceProfile, splitBoundsConfig, inSplitSelection, diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index 6067550507..6d1aa87521 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -34,8 +34,6 @@ import static com.android.app.animation.Interpolators.FINAL_FRAME; import static com.android.app.animation.Interpolators.LINEAR; import static com.android.app.animation.Interpolators.clampToProgress; import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE; -import static com.android.launcher3.AbstractFloatingView.TYPE_TASK_MENU; -import static com.android.launcher3.AbstractFloatingView.getTopOpenViewWithType; import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS; import static com.android.launcher3.Flags.enableAdditionalHomeAnimations; import static com.android.launcher3.Flags.enableDesktopExplodedView; @@ -142,7 +140,6 @@ import androidx.annotation.UiThread; import androidx.core.graphics.ColorUtils; import androidx.dynamicanimation.animation.SpringAnimation; -import com.android.app.tracing.TraceUtilsKt; import com.android.internal.jank.Cuj; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.BaseActivity.MultiWindowModeChangedListener; @@ -832,7 +829,7 @@ public abstract class RecentsView< mOrientationState.setMultiWindowMode(inMultiWindowMode); setLayoutRotation(mOrientationState.getTouchRotation(), mOrientationState.getDisplayRotation()); - updateChildTaskOrientations(); + mUtils.updateChildTaskOrientations(); if (!inMultiWindowMode && mOverviewStateEnabled) { // TODO: Re-enable layout transitions for addition of the unpinned task reloadIfNeeded(); @@ -2087,7 +2084,7 @@ public abstract class RecentsView< traceBegin(Trace.TRACE_TAG_APP, "RecentsView.applyLoadPlan.layouts"); updateTaskSize(); - updateChildTaskOrientations(); + mUtils.updateChildTaskOrientations(); traceEnd(Trace.TRACE_TAG_APP); TaskView newRunningTaskView = mUtils.getDesktopTaskViewForDeskId(runningTaskViewDeskId); @@ -2339,7 +2336,7 @@ public abstract class RecentsView< updateSizeAndPadding(); // Update TaskView's DeviceProfile dependent layout. - updateChildTaskOrientations(); + mUtils.updateChildTaskOrientations(); requestLayout(); // Reapply the current page to update page scrolls. @@ -2949,22 +2946,6 @@ public abstract class RecentsView< return as; } - private void updateChildTaskOrientations() { - for (TaskView taskView : getTaskViews()) { - taskView.setOrientationState(mOrientationState); - } - boolean shouldRotateMenuForFakeRotation = - !mOrientationState.isRecentsActivityRotationAllowed(); - if (!shouldRotateMenuForFakeRotation) { - return; - } - AbstractFloatingView floatingView = getTopOpenViewWithType(mContainer, TYPE_TASK_MENU); - if (floatingView instanceof TaskMenuView taskMenuView) { - // Rotation is supported on phone (details at b/254198019#comment4) - taskMenuView.onRotationChanged(); - } - } - /** * Called when a gesture from an app has finished, and an end target has been determined. */ @@ -3201,7 +3182,7 @@ public abstract class RecentsView< setRunningTaskHidden(runningTaskTileHidden); // Update task size after setting current task. updateTaskSize(); - updateChildTaskOrientations(); + mUtils.updateChildTaskOrientations(); // Reload the task list reloadIfNeeded(); @@ -4354,7 +4335,7 @@ public abstract class RecentsView< finalNextFocusedTaskView.getDismissIconFadeInAnimator().start(); } updateTaskSize(); - updateChildTaskOrientations(); + mUtils.updateChildTaskOrientations(); // Update scroll and snap to page. updateScrollSynchronously(); @@ -7034,7 +7015,7 @@ public abstract class RecentsView< addView(desktopTaskView, insertionIndex); updateTaskSize(); - updateChildTaskOrientations(); + mUtils.updateChildTaskOrientations(); // TODO: b/401002178 - Recalculate the new current page such that the addition of the new // desk does not result in a change in the current scroll page. diff --git a/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt b/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt index 24b7fa7c81..9c35913841 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt +++ b/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt @@ -20,10 +20,13 @@ import android.graphics.Rect import android.util.FloatProperty import android.view.KeyEvent import android.view.View +import android.view.View.LAYOUT_DIRECTION_LTR +import android.view.View.LAYOUT_DIRECTION_RTL import androidx.core.view.children -import com.android.launcher3.AbstractFloatingView -import com.android.launcher3.Flags +import com.android.launcher3.AbstractFloatingView.TYPE_TASK_MENU +import com.android.launcher3.AbstractFloatingView.getTopOpenViewWithType import com.android.launcher3.Flags.enableLargeDesktopWindowingTile +import com.android.launcher3.Flags.enableOverviewIconMenu import com.android.launcher3.Flags.enableSeparateExternalDisplayTasks import com.android.launcher3.statehandlers.DesktopVisibilityController import com.android.launcher3.statehandlers.DesktopVisibilityController.Companion.INACTIVE_DESK_ID @@ -357,18 +360,33 @@ class RecentsViewUtils(private val recentsView: RecentsView<*, *>) { } } + private fun getTaskMenu(): TaskMenuView? = + getTopOpenViewWithType(recentsView.mContainer, TYPE_TASK_MENU) as? TaskMenuView + 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 + if (enableOverviewIconMenu()) { + return getTaskMenu()?.isOpen == true || event.keyCode == KeyEvent.KEYCODE_TAB } return false } + fun updateChildTaskOrientations() { + with(recentsView) { + taskViews.forEach { it.setOrientationState(mOrientationState) } + if (enableOverviewIconMenu()) { + children.forEach { + it.layoutDirection = if (isRtl) LAYOUT_DIRECTION_LTR else LAYOUT_DIRECTION_RTL + } + } + + // Return when it's not fake landscape + if (mOrientationState.isRecentsActivityRotationAllowed) return@with + + // Rotation is supported on phone (details at b/254198019#comment4) + getTaskMenu()?.onRotationChanged() + } + } + var deskExplodeProgress: Float = 0f set(value) { field = value diff --git a/quickstep/src/com/android/quickstep/views/TaskView.kt b/quickstep/src/com/android/quickstep/views/TaskView.kt index 8d95b131bf..c014d32fb0 100644 --- a/quickstep/src/com/android/quickstep/views/TaskView.kt +++ b/quickstep/src/com/android/quickstep/views/TaskView.kt @@ -615,6 +615,16 @@ constructor( super.draw(canvas) } + override fun setLayoutDirection(layoutDirection: Int) { + super.setLayoutDirection(layoutDirection) + if (enableOverviewIconMenu()) { + val deviceLayoutDirection = resources.configuration.layoutDirection + taskContainers.forEach { + (it.iconView as IconAppChipView).layoutDirection = deviceLayoutDirection + } + } + } + override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { super.onLayout(changed, left, top, right, bottom) val thumbnailTopMargin = container.deviceProfile.overviewTaskThumbnailTopMarginPx @@ -1484,10 +1494,11 @@ constructor( } private fun closeTaskMenu(): Boolean { - val floatingView: AbstractFloatingView? = AbstractFloatingView.getTopOpenViewWithType( - container, - AbstractFloatingView.TYPE_TASK_MENU - ) + val floatingView: AbstractFloatingView? = + AbstractFloatingView.getTopOpenViewWithType( + container, + AbstractFloatingView.TYPE_TASK_MENU, + ) if (floatingView?.isOpen == true) { floatingView.close(true) return true diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/orientation/SeascapePagedViewHandlerTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/orientation/SeascapePagedViewHandlerTest.kt index 37886888ea..d455b0d1e9 100644 --- a/quickstep/tests/multivalentTests/src/com/android/quickstep/orientation/SeascapePagedViewHandlerTest.kt +++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/orientation/SeascapePagedViewHandlerTest.kt @@ -43,12 +43,12 @@ class SeascapePagedViewHandlerTest { if (isEnabled) { setFlagsRule.enableFlags( Flags.FLAG_ENABLE_GRID_ONLY_OVERVIEW, - Flags.FLAG_ENABLE_OVERVIEW_ICON_MENU + Flags.FLAG_ENABLE_OVERVIEW_ICON_MENU, ) } else { setFlagsRule.disableFlags( Flags.FLAG_ENABLE_GRID_ONLY_OVERVIEW, - Flags.FLAG_ENABLE_OVERVIEW_ICON_MENU + Flags.FLAG_ENABLE_OVERVIEW_ICON_MENU, ) } } @@ -110,12 +110,6 @@ class SeascapePagedViewHandlerTest { val (topLeftY, bottomRightY) = getSplitIconsPosition(isRTL = true) - // TODO(b/326377497): When started in fake seascape and rotated to landscape, - // the icon chips are in RTL and wrongly positioned at the right side of the snapshot. - // Top-Left app chip should be placed at the top left of the first snapshot, but because - // this issue, it's displayed at the top-right of the second snapshot. - // The Bottom-Right app chip is displayed at the top-right of the first snapshot because - // of this issue. assertThat(topLeftY).isEqualTo(316) assertThat(bottomRightY).isEqualTo(0) } @@ -167,7 +161,7 @@ class SeascapePagedViewHandlerTest { `when`(iconView.layoutParams).thenReturn(frameLayout) sut.updateSplitIconsPosition(iconView, expectedTranslationY, false) - assertThat(frameLayout.gravity).isEqualTo(Gravity.BOTTOM or Gravity.START) + assertThat(frameLayout.gravity).isEqualTo(Gravity.BOTTOM or Gravity.END) verify(iconView).setSplitTranslationX(0f) verify(iconView).setSplitTranslationY(expectedTranslationY.toFloat()) } @@ -182,7 +176,7 @@ class SeascapePagedViewHandlerTest { `when`(iconView.layoutParams).thenReturn(frameLayout) sut.updateSplitIconsPosition(iconView, expectedTranslationY, true) - assertThat(frameLayout.gravity).isEqualTo(Gravity.TOP or Gravity.END) + assertThat(frameLayout.gravity).isEqualTo(Gravity.TOP or Gravity.START) verify(iconView).setSplitTranslationX(0f) verify(iconView).setSplitTranslationY(expectedTranslationY.toFloat()) }