Add TaskContentView wrapper to TaskThumbnailView

- Keep TaskThumbnailViewDeprecated as-is
- Due to flag guarding and xml changes, added bottomright_snapshot id
- Remove redundant FrameLayout from task_header_view.xml and refactor
the view to handle alignment correctly
- Move feature flag logic to the state mapper
- Extract TaskThumbnailViewHeader logic and any related state out of
TaskThumbnailView and move to TaskContentView
- Use vertical LinearLayoutManager to hold the TaskThumbnailViewHeader
and TaskThumbnailView
- Rename TaskThumbnailViewHeader to TaskHeaderView. Rename xml and state
 similarly

This reverts commit df6dc455a7.
This reverts commit 714370a9bfb2e53738b0cd9958acd6540d8d649c.

Reason for revert: Reland previously reverted CL's with fixes

Fix: 408971730
Fix: 397889146
Fix: 401469907
Fix: 402277471
Fix: 403826044
Flag: com.android.launcher3.enable_refactor_task_thumbnail
Test: TaskUiStateMapperTest & TaskContentViewScreenshotTest & TaskThumbnailViewScreenshotTest & TaskHeaderViewScreenshotTest & SwitchBetweenSplitPairsGesturalNavPortrait
Change-Id: I01758447ad1194ebbeab748113621b42f3384db8
This commit is contained in:
samcackett
2025-03-21 14:21:02 +00:00
parent 1af8b49518
commit 2244b633cc
33 changed files with 988 additions and 464 deletions

View File

@@ -36,6 +36,7 @@ import com.android.launcher3.util.UserIconInfo
import com.android.quickstep.TaskOverlayFactory
import com.android.quickstep.TaskOverlayFactory.TaskOverlay
import com.android.quickstep.recents.di.RecentsDependencies
import com.android.quickstep.task.thumbnail.TaskContentView
import com.android.quickstep.task.thumbnail.TaskThumbnailView
import com.android.quickstep.views.RecentsView
import com.android.quickstep.views.TaskContainer
@@ -201,6 +202,7 @@ class TaskViewItemInfoTest {
return TaskContainer(
taskView,
task,
mock<TaskContentView>(),
if (enableRefactorTaskThumbnail()) mock<TaskThumbnailView>()
else mock<TaskThumbnailViewDeprecated>(),
mock<TaskViewIcon>(),

View File

@@ -19,32 +19,120 @@ package com.android.quickstep.recents.ui.mapper
import android.graphics.Bitmap
import android.graphics.Color
import android.graphics.drawable.ShapeDrawable
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.SetFlagsRule
import android.view.Surface
import android.view.View
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.launcher3.Flags
import com.android.quickstep.recents.ui.viewmodel.TaskData
import com.android.quickstep.task.thumbnail.TaskHeaderUiState
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.LiveTile
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Snapshot
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.ThumbnailHeader
import com.android.systemui.shared.recents.model.ThumbnailData
import com.google.common.truth.Truth.assertThat
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class TaskUiStateMapperTest {
@get:Rule val mSetFlagsRule = SetFlagsRule()
/** TaskHeaderUiState */
@Test
fun taskData_isNull_returns_Uninitialized() {
fun taskData_isNull_returns_HideHeader() {
val result =
TaskUiStateMapper.toTaskThumbnailUiState(
TaskUiStateMapper.toTaskHeaderState(
taskData = null,
hasHeader = false,
clickCloseListener = null,
)
assertThat(result).isEqualTo(TaskHeaderUiState.HideHeader)
}
@DisableFlags(Flags.FLAG_ENABLE_DESKTOP_EXPLODED_VIEW)
@Test
fun explodedFlagDisabled_returnsHideHeader() {
val inputs =
listOf(
TASK_DATA,
TASK_DATA.copy(thumbnailData = null),
TASK_DATA.copy(isLocked = true),
TASK_DATA.copy(title = null),
)
val closeCallback = View.OnClickListener {}
val expected = TaskHeaderUiState.HideHeader
inputs.forEach { taskData ->
val result =
TaskUiStateMapper.toTaskHeaderState(
taskData = taskData,
hasHeader = true,
clickCloseListener = closeCallback,
)
assertThat(result).isEqualTo(expected)
}
}
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_EXPLODED_VIEW)
@Test
fun taskData_hasHeader_and_taskData_returnsShowHeader() {
val inputs =
listOf(
TASK_DATA.copy(isLiveTile = true),
TASK_DATA.copy(isLiveTile = true, thumbnailData = null),
TASK_DATA.copy(isLiveTile = true, isLocked = true),
TASK_DATA.copy(isLiveTile = true, title = null),
)
val closeCallback = View.OnClickListener {}
val expected =
TaskHeaderUiState.ShowHeader(
header =
TaskHeaderUiState.ThumbnailHeader(
TASK_ICON,
TASK_TITLE_DESCRIPTION,
closeCallback,
)
)
inputs.forEach { taskData ->
val result =
TaskUiStateMapper.toTaskHeaderState(
taskData = taskData,
hasHeader = true,
clickCloseListener = closeCallback,
)
assertThat(result).isEqualTo(expected)
}
}
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_EXPLODED_VIEW)
@Test
fun taskData_hasHeader_emptyTaskData_returns_HideHeader() {
val inputs =
listOf(
TASK_DATA.copy(isLiveTile = true, icon = null),
TASK_DATA.copy(isLiveTile = true, titleDescription = null),
TASK_DATA.copy(isLiveTile = true, icon = null, titleDescription = null),
)
inputs.forEach { taskData ->
val result =
TaskUiStateMapper.toTaskHeaderState(
taskData = taskData,
hasHeader = true,
clickCloseListener = {},
)
assertThat(result).isEqualTo(TaskHeaderUiState.HideHeader)
}
}
/** TaskThumbnailUiState */
@Test
fun taskData_isNull_returns_Uninitialized() {
val result = TaskUiStateMapper.toTaskThumbnailUiState(taskData = null)
assertThat(result).isEqualTo(TaskThumbnailUiState.Uninitialized)
}
@@ -57,76 +145,19 @@ class TaskUiStateMapperTest {
TASK_DATA.copy(isLiveTile = true, isLocked = true),
)
inputs.forEach { input ->
val result =
TaskUiStateMapper.toTaskThumbnailUiState(
taskData = input,
hasHeader = false,
clickCloseListener = null,
)
assertThat(result).isEqualTo(LiveTile.WithoutHeader)
}
}
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_EXPLODED_VIEW)
@Test
fun taskData_isLiveTileWithHeader_returns_LiveTileWithHeader() {
val inputs =
listOf(
TASK_DATA.copy(isLiveTile = true),
TASK_DATA.copy(isLiveTile = true, thumbnailData = null),
TASK_DATA.copy(isLiveTile = true, isLocked = true),
TASK_DATA.copy(isLiveTile = true, title = null),
)
val closeCallback = View.OnClickListener {}
val expected =
LiveTile.WithHeader(
header = ThumbnailHeader(TASK_ICON, TASK_TITLE_DESCRIPTION, closeCallback)
)
inputs.forEach { taskData ->
val result =
TaskUiStateMapper.toTaskThumbnailUiState(
taskData = taskData,
hasHeader = true,
clickCloseListener = closeCallback,
)
assertThat(result).isEqualTo(expected)
}
}
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_EXPLODED_VIEW)
@Test
fun taskData_isLiveTileWithHeader_missingHeaderData_returns_LiveTileWithoutHeader() {
val inputs =
listOf(
TASK_DATA.copy(isLiveTile = true, icon = null),
TASK_DATA.copy(isLiveTile = true, titleDescription = null),
TASK_DATA.copy(isLiveTile = true, icon = null, titleDescription = null),
)
inputs.forEach { taskData ->
val result =
TaskUiStateMapper.toTaskThumbnailUiState(
taskData = taskData,
hasHeader = true,
clickCloseListener = {},
)
assertThat(result).isEqualTo(LiveTile.WithoutHeader)
val result = TaskUiStateMapper.toTaskThumbnailUiState(taskData = input)
assertThat(result).isEqualTo(LiveTile)
}
}
@Test
fun taskData_isStaticTile_returns_SnapshotSplash() {
val result =
TaskUiStateMapper.toTaskThumbnailUiState(
taskData = TASK_DATA,
hasHeader = false,
clickCloseListener = null,
)
val result = TaskUiStateMapper.toTaskThumbnailUiState(taskData = TASK_DATA)
val expected =
TaskThumbnailUiState.SnapshotSplash(
snapshot =
Snapshot.WithoutHeader(
Snapshot(
backgroundColor = TASK_BACKGROUND_COLOR,
bitmap = TASK_THUMBNAIL,
thumbnailRotation = Surface.ROTATION_0,
@@ -137,69 +168,11 @@ class TaskUiStateMapperTest {
assertThat(result).isEqualTo(expected)
}
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_EXPLODED_VIEW)
@Test
fun taskData_isStaticTile_withHeader_returns_SnapshotSplashWithHeader() {
val inputs = listOf(TASK_DATA, TASK_DATA.copy(title = null))
val closeCallback = View.OnClickListener {}
val expected =
TaskThumbnailUiState.SnapshotSplash(
snapshot =
Snapshot.WithHeader(
backgroundColor = TASK_BACKGROUND_COLOR,
bitmap = TASK_THUMBNAIL,
thumbnailRotation = Surface.ROTATION_0,
header = ThumbnailHeader(TASK_ICON, TASK_TITLE_DESCRIPTION, closeCallback),
),
splash = TASK_ICON,
)
inputs.forEach { taskData ->
val result =
TaskUiStateMapper.toTaskThumbnailUiState(
taskData = taskData,
hasHeader = true,
clickCloseListener = closeCallback,
)
assertThat(result).isEqualTo(expected)
}
}
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_EXPLODED_VIEW)
@Test
fun taskData_isStaticTile_missingHeaderData_returns_SnapshotSplashWithoutHeader() {
val inputs =
listOf(
TASK_DATA.copy(titleDescription = null, icon = null),
TASK_DATA.copy(titleDescription = null),
TASK_DATA.copy(icon = null),
)
val expected =
Snapshot.WithoutHeader(
backgroundColor = TASK_BACKGROUND_COLOR,
thumbnailRotation = Surface.ROTATION_0,
bitmap = TASK_THUMBNAIL,
)
inputs.forEach { taskData ->
val result =
TaskUiStateMapper.toTaskThumbnailUiState(
taskData = taskData,
hasHeader = true,
clickCloseListener = {},
)
assertThat(result).isInstanceOf(TaskThumbnailUiState.SnapshotSplash::class.java)
result as TaskThumbnailUiState.SnapshotSplash
assertThat(result.snapshot).isEqualTo(expected)
}
}
@Test
fun taskData_thumbnailIsNull_returns_BackgroundOnly() {
val result =
TaskUiStateMapper.toTaskThumbnailUiState(
taskData = TASK_DATA.copy(thumbnailData = null),
hasHeader = false,
clickCloseListener = null,
taskData = TASK_DATA.copy(thumbnailData = null)
)
val expected = TaskThumbnailUiState.BackgroundOnly(TASK_BACKGROUND_COLOR)
@@ -209,11 +182,7 @@ class TaskUiStateMapperTest {
@Test
fun taskData_isLocked_returns_BackgroundOnly() {
val result =
TaskUiStateMapper.toTaskThumbnailUiState(
taskData = TASK_DATA.copy(isLocked = true),
hasHeader = false,
clickCloseListener = null,
)
TaskUiStateMapper.toTaskThumbnailUiState(taskData = TASK_DATA.copy(isLocked = true))
val expected = TaskThumbnailUiState.BackgroundOnly(TASK_BACKGROUND_COLOR)
assertThat(result).isEqualTo(expected)