From 08bd1bfcdb2f0b8eb462a4d1c93502e718d71447 Mon Sep 17 00:00:00 2001 From: Alex Chau Date: Fri, 8 Nov 2024 17:55:55 +0000 Subject: [PATCH] Crop Desktop TaskThumbnailView according to its intersection with parent - Override TaskThumbnailView's outline bounds to be the Rect that the view intersect with parent, so it draw corner radius around the intersection, to match the corner radius behavior in live tile - This is not fixed in TaskThumbnailViewDeprecated as it doesn't have an outline implemenation, and it's too complicated to rewrite that as a bugfix Fix: 379881502 Flag: com.android.launcher3.enable_refactor_task_thumbnail Flag: com.android.window.flags.enable_desktop_recents_transitions_corners_bugfix Test: OverviewDesktopTaskImageTest Change-Id: I67d7e6a05b15f0f651c8ec71f33ca1e02d662b06 --- .../task/thumbnail/TaskThumbnailView.kt | 13 +++- .../quickstep/views/DesktopTaskView.kt | 75 ++++++++++++------- 2 files changed, 61 insertions(+), 27 deletions(-) diff --git a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt index 63e93ba05c..87aace8aab 100644 --- a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt +++ b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt @@ -74,6 +74,16 @@ class TaskThumbnailView : FrameLayout, ViewPool.Reusable { private var taskThumbnailViewHeader: TaskThumbnailViewHeader? = null private var uiState: TaskThumbnailUiState = Uninitialized + + /** + * Sets the outline bounds of the view. Default to use view's bound as outline when set to null. + */ + var outlineBounds: Rect? = null + set(value) { + field = value + invalidateOutline() + } + private val bounds = Rect() var cornerRadius: Float = 0f @@ -109,7 +119,7 @@ class TaskThumbnailView : FrameLayout, ViewPool.Reusable { outlineProvider = object : ViewOutlineProvider() { override fun getOutline(view: View, outline: Outline) { - outline.setRoundRect(bounds, cornerRadius) + outline.setRoundRect(outlineBounds ?: bounds, cornerRadius) } } } @@ -124,6 +134,7 @@ class TaskThumbnailView : FrameLayout, ViewPool.Reusable { override fun onRecycle() { uiState = Uninitialized + outlineBounds = null resetViews() } diff --git a/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt b/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt index da160f134b..fbda3b3d7b 100644 --- a/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt +++ b/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt @@ -21,6 +21,7 @@ import android.content.Context import android.graphics.Matrix import android.graphics.PointF import android.graphics.Rect +import android.graphics.Rect.intersects import android.graphics.RectF import android.util.AttributeSet import android.util.Log @@ -30,6 +31,7 @@ import android.view.View import android.view.ViewStub import androidx.core.content.res.ResourcesCompat import androidx.core.view.updateLayoutParams +import com.android.internal.hidden_from_bootclasspath.com.android.window.flags.Flags.enableDesktopRecentsTransitionsCornersBugfix import com.android.launcher3.Flags.enableDesktopExplodedView import com.android.launcher3.Flags.enableOverviewIconMenu import com.android.launcher3.Flags.enableRefactorTaskThumbnail @@ -57,6 +59,7 @@ import com.android.quickstep.recents.ui.viewmodel.TaskData import com.android.quickstep.task.thumbnail.TaskThumbnailView import com.android.quickstep.util.DesktopTask import com.android.quickstep.util.RecentsOrientedState +import kotlin.math.roundToInt /** TaskView that contains all tasks that are part of the desktop. */ class DesktopTaskView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : @@ -102,7 +105,7 @@ class DesktopTaskView @JvmOverloads constructor(context: Context, attrs: Attribu * Holds the default (user placed) positions of task windows. This can be moved into the * viewModel once RefactorTaskThumbnail has been launched. */ - private var defaultTaskPositions: List = emptyList() + private var fullscreenTaskPositions: List = emptyList() /** * When enableDesktopExplodedView is enabled, this controls the gradual transition from the @@ -172,42 +175,44 @@ class DesktopTaskView @JvmOverloads constructor(context: Context, attrs: Attribu val thumbnailTopMarginPx = container.deviceProfile.overviewTaskThumbnailTopMarginPx - val containerWidth = layoutParams.width - val containerHeight = layoutParams.height - thumbnailTopMarginPx + val taskViewWidth = layoutParams.width + val taskViewHeight = layoutParams.height - thumbnailTopMarginPx BaseContainerInterface.getTaskDimension(mContext, container.deviceProfile, tempPointF) - val windowWidth = tempPointF.x.toInt() - val windowHeight = tempPointF.y.toInt() - val scaleWidth = containerWidth / windowWidth.toFloat() - val scaleHeight = containerHeight / windowHeight.toFloat() + val screenWidth = tempPointF.x.toInt() + val screenHeight = tempPointF.y.toInt() + val screenRect = Rect(0, 0, screenWidth, screenHeight) + val scaleWidth = taskViewWidth / screenWidth.toFloat() + val scaleHeight = taskViewHeight / screenHeight.toFloat() taskContainers.forEach { val taskId = it.task.key.id - val defaultPosition = defaultTaskPositions.firstOrNull { it.taskId == taskId } ?: return - val position = + val fullscreenTaskPosition = + fullscreenTaskPositions.firstOrNull { it.taskId == taskId } ?: return + val overviewTaskPosition = if (enableDesktopExplodedView()) { viewModel!! .organizedDesktopTaskPositions .firstOrNull { it.taskId == taskId } ?.let { organizedPosition -> - TEMP_RECT.apply { + TEMP_OVERVIEW_TASK_POSITION.apply { lerpRect( - defaultPosition.bounds, + fullscreenTaskPosition.bounds, organizedPosition.bounds, explodeProgress, ) } - } ?: defaultPosition.bounds + } ?: fullscreenTaskPosition.bounds } else { - defaultPosition.bounds + fullscreenTaskPosition.bounds } if (enableDesktopExplodedView()) { getRemoteTargetHandle(taskId)?.let { remoteTargetHandle -> val fromRect = - TEMP_RECTF1.apply { - set(defaultPosition.bounds) + TEMP_FROM_RECTF.apply { + set(fullscreenTaskPosition.bounds) scale(scaleWidth) offset( lastComputedTaskSize.left.toFloat(), @@ -215,8 +220,8 @@ class DesktopTaskView @JvmOverloads constructor(context: Context, attrs: Attribu ) } val toRect = - TEMP_RECTF2.apply { - set(position) + TEMP_TO_RECTF.apply { + set(overviewTaskPosition) scale(scaleWidth) offset( lastComputedTaskSize.left.toFloat(), @@ -230,10 +235,10 @@ class DesktopTaskView @JvmOverloads constructor(context: Context, attrs: Attribu } } - val taskLeft = position.left * scaleWidth - val taskTop = position.top * scaleHeight - val taskWidth = position.width() * scaleWidth - val taskHeight = position.height() * scaleHeight + val taskLeft = overviewTaskPosition.left * scaleWidth + val taskTop = overviewTaskPosition.top * scaleHeight + val taskWidth = overviewTaskPosition.width() * scaleWidth + val taskHeight = overviewTaskPosition.height() * scaleHeight // TODO(b/394660950): Revisit the choice to update the layout when explodeProgress == 1. // To run the explode animation in reverse, it may be simpler to use translation/scale // for all cases where the progress is non-zero. @@ -254,6 +259,24 @@ class DesktopTaskView @JvmOverloads constructor(context: Context, attrs: Attribu leftMargin = taskLeft.toInt() topMargin = taskTop.toInt() } + + if ( + enableDesktopRecentsTransitionsCornersBugfix() && enableRefactorTaskThumbnail() + ) { + it.thumbnailView.outlineBounds = + if (intersects(overviewTaskPosition, screenRect)) + Rect(overviewTaskPosition).apply { + intersectUnchecked(screenRect) + // Offset to 0,0 to transform into TaskThumbnailView's coordinate + // system. + offset(-overviewTaskPosition.left, -overviewTaskPosition.top) + left = (left * scaleWidth).roundToInt() + top = (top * scaleHeight).roundToInt() + right = (right * scaleWidth).roundToInt() + bottom = (bottom * scaleHeight).roundToInt() + } + else null + } } else { // During the animation, apply translation and scale such that the view is // transformed to where we want, without triggering layout. @@ -346,13 +369,13 @@ class DesktopTaskView @JvmOverloads constructor(context: Context, attrs: Attribu val desktopSize = Size(tempPointF.x.toInt(), tempPointF.y.toInt()) DEFAULT_BOUNDS.set(0, 0, desktopSize.width / 4, desktopSize.height / 4) - defaultTaskPositions = + fullscreenTaskPositions = taskContainers.map { DesktopTaskBoundsData(it.task.key.id, it.task.appBounds ?: DEFAULT_BOUNDS) } if (enableDesktopExplodedView()) { - viewModel?.organizeDesktopTasks(desktopSize, defaultTaskPositions) + viewModel?.organizeDesktopTasks(desktopSize, fullscreenTaskPositions) } positionTaskWindows() } @@ -449,8 +472,8 @@ class DesktopTaskView @JvmOverloads constructor(context: Context, attrs: Attribu private const val VIEW_POOL_INITIAL_SIZE = 0 private val DEFAULT_BOUNDS = Rect() // Temporaries used for various purposes to avoid allocations. - private val TEMP_RECT = Rect() - private val TEMP_RECTF1 = RectF() - private val TEMP_RECTF2 = RectF() + private val TEMP_OVERVIEW_TASK_POSITION = Rect() + private val TEMP_FROM_RECTF = RectF() + private val TEMP_TO_RECTF = RectF() } }