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
diff --git a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
index 63e93ba..87aace8 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 @@
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 @@
outlineProvider =
object : ViewOutlineProvider() {
override fun getOutline(view: View, outline: Outline) {
- outline.setRoundRect(bounds, cornerRadius)
+ outline.setRoundRect(outlineBounds ?: bounds, cornerRadius)
}
}
}
@@ -124,6 +134,7 @@
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 da160f1..fbda3b3 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.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.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.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 @@
* 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<DesktopTaskBoundsData> = emptyList()
+ private var fullscreenTaskPositions: List<DesktopTaskBoundsData> = emptyList()
/**
* When enableDesktopExplodedView is enabled, this controls the gradual transition from the
@@ -172,42 +175,44 @@
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 @@
)
}
val toRect =
- TEMP_RECTF2.apply {
- set(position)
+ TEMP_TO_RECTF.apply {
+ set(overviewTaskPosition)
scale(scaleWidth)
offset(
lastComputedTaskSize.left.toFloat(),
@@ -230,10 +235,10 @@
}
}
- 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 @@
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 @@
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 @@
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()
}
}