From b392f61ced6f7fa6890e8ae827eeeda59536dd2b Mon Sep 17 00:00:00 2001 From: Jeremy Sim Date: Wed, 19 Feb 2025 15:38:27 -0800 Subject: [PATCH] Refactor SplitBounds to always use creation-time measurements In flexible split ratios like 90:10 and 10:90, we hide the Overview icon of the smaller app completely (ag/30949010). However, there was an unintentional side effect where one of the icons would disappear when rotating the display. This happened (basically) because SplitBounds is not recalculated on device rotation, so fields like leftTaskPercent and topTaskPercent should not be used directly when the rotation state is not known. I added an API to SplitBounds that hopefully makes it harder to write bugs like this in the future. Fixes: 395783367 Flag: com.android.wm.shell.enable_flexible_two_app_split Test: Icons do not disappear on rotation. When app chip menus are enabled, the (existing) correct behavior is not changed. Change-Id: I2c1b38f4bd9a76dfb127b7bbf230897d747d3c49 --- .../taskbar/KeyboardQuickSwitchTaskView.java | 3 +- .../orientation/LandscapePagedViewHandler.kt | 30 +++----------- .../orientation/PortraitPagedViewHandler.java | 34 +++++---------- .../orientation/SeascapePagedViewHandler.kt | 24 ++--------- .../quickstep/views/DigitalWellBeingToast.kt | 4 +- .../quickstep/views/GroupedTaskView.kt | 16 +++----- .../util/SplitConfigurationOptions.java | 41 +++++++++++++++++-- 7 files changed, 65 insertions(+), 87 deletions(-) diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java index bf5c0c862f..f80dc909f8 100644 --- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java +++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java @@ -197,8 +197,7 @@ public class KeyboardQuickSwitchTaskView extends ConstraintLayout { final boolean isLeftRightSplit = !splitBounds.appsStackedVertically; - final float leftOrTopTaskPercent = isLeftRightSplit - ? splitBounds.leftTaskPercent : splitBounds.topTaskPercent; + final float leftOrTopTaskPercent = splitBounds.getLeftTopTaskPercent(); ConstraintLayout.LayoutParams leftTopParams = (ConstraintLayout.LayoutParams) mThumbnailView1.getLayoutParams(); diff --git a/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.kt b/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.kt index e72ccbfb7b..59ea8fa98a 100644 --- a/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.kt +++ b/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.kt @@ -285,11 +285,7 @@ open class LandscapePagedViewHandler : RecentsPagedOrientationHandler { translationY = snapshotParams.topMargin.toFloat() } else { val topLeftTaskPlusDividerPercent = - if (splitBounds.appsStackedVertically) { - splitBounds.topTaskPercent + splitBounds.dividerHeightPercent - } else { - splitBounds.leftTaskPercent + splitBounds.dividerWidthPercent - } + splitBounds.leftTopTaskPercent + splitBounds.dividerPercent translationY = snapshotParams.topMargin + (taskViewHeight - snapshotParams.topMargin) * topLeftTaskPlusDividerPercent @@ -440,15 +436,8 @@ open class LandscapePagedViewHandler : RecentsPagedOrientationHandler { splitInfo: SplitBounds, desiredStagePosition: Int ) { - val topLeftTaskPercent: Float - val dividerBarPercent: Float - if (splitInfo.appsStackedVertically) { - topLeftTaskPercent = splitInfo.topTaskPercent - dividerBarPercent = splitInfo.dividerHeightPercent - } else { - topLeftTaskPercent = splitInfo.leftTaskPercent - dividerBarPercent = splitInfo.dividerWidthPercent - } + val topLeftTaskPercent = splitInfo.leftTopTaskPercent + val dividerBarPercent = splitInfo.dividerPercent if (desiredStagePosition == STAGE_POSITION_TOP_OR_LEFT) { outRect.bottom = outRect.top + (outRect.height() * topLeftTaskPercent).toInt() @@ -510,12 +499,7 @@ open class LandscapePagedViewHandler : RecentsPagedOrientationHandler { val totalThumbnailHeight = parentHeight - spaceAboveSnapshot val dividerBar = getDividerBarSize(totalThumbnailHeight, splitBoundsConfig) - val taskPercent = - if (splitBoundsConfig.appsStackedVertically) { - splitBoundsConfig.topTaskPercent - } else { - splitBoundsConfig.leftTaskPercent - } + val taskPercent = splitBoundsConfig.leftTopTaskPercent val firstTaskViewSize = Point(parentWidth, (totalThumbnailHeight * taskPercent).toInt()) val secondTaskViewSize = Point(parentWidth, totalThumbnailHeight - firstTaskViewSize.y - dividerBar) @@ -715,11 +699,7 @@ open class LandscapePagedViewHandler : RecentsPagedOrientationHandler { * @return The divider size for the group task view. */ protected fun getDividerBarSize(totalThumbnailHeight: Int, splitConfig: SplitBounds): Int { - return Math.round( - totalThumbnailHeight * - if (splitConfig.appsStackedVertically) splitConfig.dividerHeightPercent - else splitConfig.dividerWidthPercent - ) + return Math.round(totalThumbnailHeight * splitConfig.dividerPercent) } /** diff --git a/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.java b/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.java index c1e1c2b115..d9ad7cec3b 100644 --- a/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.java +++ b/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.java @@ -271,12 +271,8 @@ public class PortraitPagedViewHandler extends DefaultPagedViewHandler implements if (splitBounds != null) { if (deviceProfile.isLeftRightSplit) { if (desiredTaskId == splitBounds.rightBottomTaskId) { - float leftTopTaskPercent = splitBounds.appsStackedVertically - ? splitBounds.topTaskPercent - : splitBounds.leftTaskPercent; - float dividerThicknessPercent = splitBounds.appsStackedVertically - ? splitBounds.dividerHeightPercent - : splitBounds.dividerWidthPercent; + float leftTopTaskPercent = splitBounds.getLeftTopTaskPercent(); + float dividerThicknessPercent = splitBounds.getDividerPercent(); translationX = ((taskViewWidth * leftTopTaskPercent) + (taskViewWidth * dividerThicknessPercent)); } @@ -285,9 +281,9 @@ public class PortraitPagedViewHandler extends DefaultPagedViewHandler implements FrameLayout.LayoutParams snapshotParams = (FrameLayout.LayoutParams) thumbnailViews[0] .getLayoutParams(); - float bottomRightTaskPlusDividerPercent = splitBounds.appsStackedVertically - ? (1f - splitBounds.topTaskPercent) - : (1f - splitBounds.leftTaskPercent); + float bottomRightTaskPlusDividerPercent = + splitBounds.getRightBottomTaskPercent() + + splitBounds.getDividerPercent(); translationY = -((taskViewHeight - snapshotParams.topMargin) * bottomRightTaskPlusDividerPercent); } @@ -506,12 +502,8 @@ public class PortraitPagedViewHandler extends DefaultPagedViewHandler implements @Override public void setSplitTaskSwipeRect(DeviceProfile dp, Rect outRect, SplitBounds splitInfo, int desiredStagePosition) { - float topLeftTaskPercent = splitInfo.appsStackedVertically - ? splitInfo.topTaskPercent - : splitInfo.leftTaskPercent; - float dividerBarPercent = splitInfo.appsStackedVertically - ? splitInfo.dividerHeightPercent - : splitInfo.dividerWidthPercent; + float topLeftTaskPercent = splitInfo.getLeftTopTaskPercent(); + float dividerBarPercent = splitInfo.getDividerPercent(); int taskbarHeight = dp.isTransientTaskbar ? 0 : dp.taskbarHeight; float scale = (float) outRect.height() / (dp.availableHeightPx - taskbarHeight); @@ -559,9 +551,7 @@ public class PortraitPagedViewHandler extends DefaultPagedViewHandler implements primaryParams.topMargin = spaceAboveSnapshot; int totalThumbnailHeight = parentHeight - spaceAboveSnapshot; - float dividerScale = splitBoundsConfig.appsStackedVertically - ? splitBoundsConfig.dividerHeightPercent - : splitBoundsConfig.dividerWidthPercent; + float dividerScale = splitBoundsConfig.getDividerPercent(); Pair taskViewSizes = getGroupedTaskViewSizes(dp, splitBoundsConfig, parentWidth, parentHeight); if (!inSplitSelection) { @@ -610,12 +600,8 @@ public class PortraitPagedViewHandler extends DefaultPagedViewHandler implements int parentHeight) { int spaceAboveSnapshot = dp.overviewTaskThumbnailTopMarginPx; int totalThumbnailHeight = parentHeight - spaceAboveSnapshot; - float dividerScale = splitBoundsConfig.appsStackedVertically - ? splitBoundsConfig.dividerHeightPercent - : splitBoundsConfig.dividerWidthPercent; - float taskPercent = splitBoundsConfig.appsStackedVertically - ? splitBoundsConfig.topTaskPercent - : splitBoundsConfig.leftTaskPercent; + float dividerScale = splitBoundsConfig.getDividerPercent(); + float taskPercent = splitBoundsConfig.getLeftTopTaskPercent(); Point firstTaskViewSize = new Point(); Point secondTaskViewSize = new Point(); diff --git a/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt b/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt index 3fb4f54ae5..9bfa2bf722 100644 --- a/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt +++ b/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt @@ -106,15 +106,8 @@ class SeascapePagedViewHandler : LandscapePagedViewHandler() { splitInfo: SplitBounds, desiredStagePosition: Int ) { - val topLeftTaskPercent: Float - val dividerBarPercent: Float - if (splitInfo.appsStackedVertically) { - topLeftTaskPercent = splitInfo.topTaskPercent - dividerBarPercent = splitInfo.dividerHeightPercent - } else { - topLeftTaskPercent = splitInfo.leftTaskPercent - dividerBarPercent = splitInfo.dividerWidthPercent - } + val topLeftTaskPercent = splitInfo.leftTopTaskPercent + val dividerBarPercent = splitInfo.dividerPercent // In seascape, the primary thumbnail is counterintuitively placed at the physical bottom of // the screen. This is to preserve consistency when the user rotates: From the user's POV, @@ -166,11 +159,7 @@ class SeascapePagedViewHandler : LandscapePagedViewHandler() { } else { if (desiredTaskId == splitBounds.leftTopTaskId) { val bottomRightTaskPlusDividerPercent = - if (splitBounds.appsStackedVertically) { - 1f - splitBounds.topTaskPercent - } else { - 1f - splitBounds.leftTaskPercent - } + splitBounds.rightBottomTaskPercent + splitBounds.dividerPercent translationY = banner.height - (taskViewHeight - snapshotParams.topMargin) * @@ -331,12 +320,7 @@ class SeascapePagedViewHandler : LandscapePagedViewHandler() { val totalThumbnailHeight = parentHeight - spaceAboveSnapshot val dividerBar = getDividerBarSize(totalThumbnailHeight, splitBoundsConfig) - val taskPercent = - if (splitBoundsConfig.appsStackedVertically) { - splitBoundsConfig.topTaskPercent - } else { - splitBoundsConfig.leftTaskPercent - } + val taskPercent = splitBoundsConfig.leftTopTaskPercent val firstTaskViewSize = Point(parentWidth, (totalThumbnailHeight * taskPercent).toInt()) val secondTaskViewSize = Point(parentWidth, totalThumbnailHeight - firstTaskViewSize.y - dividerBar) diff --git a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.kt b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.kt index c07b7fbfb1..5c4a35da63 100644 --- a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.kt +++ b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.kt @@ -189,11 +189,11 @@ constructor( SplitBannerConfig.SPLIT_GRID_BANNER_LARGE // For landscape grid, for 30% width we only show icon, otherwise show icon and time task.key.id == splitBounds.leftTopTaskId -> - if (splitBounds.leftTaskPercent < THRESHOLD_LEFT_ICON_ONLY) + if (splitBounds.leftTopTaskPercent < THRESHOLD_LEFT_ICON_ONLY) SplitBannerConfig.SPLIT_GRID_BANNER_SMALL else SplitBannerConfig.SPLIT_GRID_BANNER_LARGE else -> - if (splitBounds.leftTaskPercent > THRESHOLD_RIGHT_ICON_ONLY) + if (splitBounds.leftTopTaskPercent > THRESHOLD_RIGHT_ICON_ONLY) SplitBannerConfig.SPLIT_GRID_BANNER_SMALL else SplitBannerConfig.SPLIT_GRID_BANNER_LARGE } diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt b/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt index 2abfb13abd..25011d784e 100644 --- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt +++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt @@ -189,16 +189,12 @@ class GroupedTaskView @JvmOverloads constructor(context: Context, attrs: Attribu val inSplitSelection = getThisTaskCurrentlyInSplitSelection() != INVALID_TASK_ID if (enableFlexibleTwoAppSplit()) { - val topLeftTaskPercent = - if (deviceProfile.isLeftRightSplit) splitBoundsConfig.leftTaskPercent - else splitBoundsConfig.topTaskPercent - val bottomRightTaskPercent = 1 - topLeftTaskPercent - leftTopTaskContainer.iconView.setFlexSplitAlpha( - if (topLeftTaskPercent < MINIMUM_RATIO_TO_SHOW_ICON) 0f else 1f - ) - rightBottomTaskContainer.iconView.setFlexSplitAlpha( - if (bottomRightTaskPercent < MINIMUM_RATIO_TO_SHOW_ICON) 0f else 1f - ) + val topLeftTaskPercent = splitBoundsConfig.leftTopTaskPercent + val bottomRightTaskPercent = splitBoundsConfig.rightBottomTaskPercent + val hideTopLeftIcon = topLeftTaskPercent < MINIMUM_RATIO_TO_SHOW_ICON + val hideBottomRightIcon = bottomRightTaskPercent < MINIMUM_RATIO_TO_SHOW_ICON + leftTopTaskContainer.iconView.setFlexSplitAlpha(if (hideTopLeftIcon) 0f else 1f) + rightBottomTaskContainer.iconView.setFlexSplitAlpha(if (hideBottomRightIcon) 0f else 1f) } if (enableOverviewIconMenu()) { diff --git a/src/com/android/launcher3/util/SplitConfigurationOptions.java b/src/com/android/launcher3/util/SplitConfigurationOptions.java index 44a7c6fa74..e1ef77a00a 100644 --- a/src/com/android/launcher3/util/SplitConfigurationOptions.java +++ b/src/com/android/launcher3/util/SplitConfigurationOptions.java @@ -127,10 +127,10 @@ public final class SplitConfigurationOptions { /** This rect represents the actual gap between the two apps */ public final Rect visualDividerBounds; // This class is orientation-agnostic, so we compute both for later use - public final float topTaskPercent; - public final float leftTaskPercent; - public final float dividerWidthPercent; - public final float dividerHeightPercent; + private final float topTaskPercent; + private final float leftTaskPercent; + private final float dividerWidthPercent; + private final float dividerHeightPercent; public final int snapPosition; /** @@ -190,6 +190,39 @@ public final class SplitConfigurationOptions { dividerHeightPercent = visualDividerBounds.height() / totalHeight; } + /** + * Returns the percentage size of the left/top task (compared to the full width/height of + * the split pair). E.g. if the left task is 4 units wide, the divider is 2 units, and the + * right task is 4 units, this method will return 0.4f. + */ + public float getLeftTopTaskPercent() { + // topTaskPercent and leftTaskPercent are defined at creation time, and are not updated + // on device rotate, so we have to check appsStackedVertically to return the right + // creation-time measurements. + return appsStackedVertically ? topTaskPercent : leftTaskPercent; + } + + /** + * Returns the percentage size of the divider's thickness (compared to the full width/height + * of the split pair). E.g. if the left task is 4 units wide, the divider is 2 units, and + * the right task is 4 units, this method will return 0.2f. + */ + public float getDividerPercent() { + // dividerHeightPercent and dividerWidthPercent are defined at creation time, and are + // not updated on device rotate, so we have to check appsStackedVertically to return + // the right creation-time measurements. + return appsStackedVertically ? dividerHeightPercent : dividerWidthPercent; + } + + /** + * Returns the percentage size of the right/bottom task (compared to the full width/height + * of the split pair). E.g. if the left task is 4 units wide, the divider is 2 units, and + * the right task is 4 units, this method will return 0.4f. + */ + public float getRightBottomTaskPercent() { + return 1 - (getLeftTopTaskPercent() + getDividerPercent()); + } + @Override public String toString() { return "LeftTop: " + leftTopBounds + ", taskId: " + leftTopTaskId + "\n"