diff --git a/quickstep/res/layout/task.xml b/quickstep/res/layout/task.xml
index 1df80aeff2..bac7b26c32 100644
--- a/quickstep/res/layout/task.xml
+++ b/quickstep/res/layout/task.xml
@@ -29,8 +29,7 @@
launcher:hoverBorderColor="@color/materialColorPrimary">
diff --git a/quickstep/res/layout/task_content_view.xml b/quickstep/res/layout/task_content_view.xml
new file mode 100644
index 0000000000..478ee55c06
--- /dev/null
+++ b/quickstep/res/layout/task_content_view.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/quickstep/res/layout/task_grouped.xml b/quickstep/res/layout/task_grouped.xml
index fe6ada98df..41f892f7fc 100644
--- a/quickstep/res/layout/task_grouped.xml
+++ b/quickstep/res/layout/task_grouped.xml
@@ -34,14 +34,12 @@
launcher:hoverBorderColor="@color/materialColorPrimary">
diff --git a/quickstep/res/layout/task_header_view.xml b/quickstep/res/layout/task_header_view.xml
new file mode 100644
index 0000000000..849153fd9e
--- /dev/null
+++ b/quickstep/res/layout/task_header_view.xml
@@ -0,0 +1,62 @@
+
+
+
+
+
+
diff --git a/quickstep/res/layout/task_thumbnail.xml b/quickstep/res/layout/task_thumbnail.xml
index 3b966159d1..8280e13f5b 100644
--- a/quickstep/res/layout/task_thumbnail.xml
+++ b/quickstep/res/layout/task_thumbnail.xml
@@ -17,7 +17,8 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/snapshot"
android:layout_width="match_parent"
- android:layout_height="match_parent" >
+ android:layout_height="0dp"
+ android:layout_weight="1" >
-
-
-
-
-
-
-
-
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 75bf331e0e..3b0be2f389 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -86,9 +86,9 @@
20dp
- 30dp
- 18dp
- 9dp
+ 6dp
+ 12dp
+ 8dp
18dp
16dp
diff --git a/quickstep/res/values/ids.xml b/quickstep/res/values/ids.xml
index c71bb762db..4cc69ade5a 100644
--- a/quickstep/res/values/ids.xml
+++ b/quickstep/res/values/ids.xml
@@ -21,4 +21,6 @@
+
+
\ No newline at end of file
diff --git a/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.kt b/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.kt
index 1e626cd78d..4868369de9 100644
--- a/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.kt
+++ b/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.kt
@@ -277,7 +277,7 @@ open class LandscapePagedViewHandler : RecentsPagedOrientationHandler {
desiredTaskId: Int,
banner: View,
): Pair {
- val snapshotParams = thumbnailViews[0].layoutParams as FrameLayout.LayoutParams
+ val snapshotParams = thumbnailViews[0].layoutParams as ViewGroup.MarginLayoutParams
val translationX = banner.height.toFloat()
val translationY: Float
if (splitBounds == null) {
diff --git a/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.kt b/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.kt
index 9132783b60..58ef09ed7d 100644
--- a/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.kt
+++ b/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.kt
@@ -257,7 +257,8 @@ class PortraitPagedViewHandler : DefaultPagedViewHandler(), RecentsPagedOrientat
}
} else {
if (desiredTaskId == splitBounds.leftTopTaskId) {
- val snapshotParams = thumbnailViews[0].layoutParams as FrameLayout.LayoutParams
+ val snapshotParams =
+ thumbnailViews[0].layoutParams as ViewGroup.MarginLayoutParams
val bottomRightTaskPlusDividerPercent =
(splitBounds.rightBottomTaskPercent + splitBounds.dividerPercent)
translationY =
diff --git a/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt b/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt
index dcd7d876e3..35103219a0 100644
--- a/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt
+++ b/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt
@@ -25,6 +25,7 @@ import android.view.Gravity
import android.view.Surface
import android.view.View
import android.view.View.MeasureSpec
+import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.core.util.component1
import androidx.core.util.component2
@@ -151,7 +152,7 @@ class SeascapePagedViewHandler : LandscapePagedViewHandler() {
desiredTaskId: Int,
banner: View,
): Pair {
- val snapshotParams = thumbnailViews[0].layoutParams as FrameLayout.LayoutParams
+ val snapshotParams = thumbnailViews[0].layoutParams as ViewGroup.MarginLayoutParams
val translationX: Float = (taskViewWidth - banner.height).toFloat()
val translationY: Float
if (splitBounds == null) {
diff --git a/quickstep/src/com/android/quickstep/recents/ui/mapper/TaskUiStateMapper.kt b/quickstep/src/com/android/quickstep/recents/ui/mapper/TaskUiStateMapper.kt
index 3f6ea0f386..7425d361ee 100644
--- a/quickstep/src/com/android/quickstep/recents/ui/mapper/TaskUiStateMapper.kt
+++ b/quickstep/src/com/android/quickstep/recents/ui/mapper/TaskUiStateMapper.kt
@@ -17,17 +17,50 @@
package com.android.quickstep.recents.ui.mapper
import android.view.View.OnClickListener
+import com.android.launcher3.Flags.enableDesktopExplodedView
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.BackgroundOnly
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.LiveTile
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Snapshot
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.SnapshotSplash
-import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.ThumbnailHeader
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Uninitialized
object TaskUiStateMapper {
+ /**
+ * Converts a [TaskData] object into a [TaskHeaderUiState] for display in the UI.
+ *
+ * This function handles different types of [TaskData] and determines the appropriate UI state
+ * based on the data and provided flags.
+ *
+ * @param taskData The [TaskData] to convert. Can be null or a specific subclass.
+ * @param hasHeader A flag indicating whether the UI should display a header.
+ * @param clickCloseListener A callback when the close button in the UI is clicked.
+ * @return A [TaskHeaderUiState] representing the UI state for the given task data.
+ */
+ fun toTaskHeaderState(
+ taskData: TaskData?,
+ hasHeader: Boolean,
+ clickCloseListener: OnClickListener?,
+ ): TaskHeaderUiState =
+ when {
+ taskData !is TaskData.Data -> TaskHeaderUiState.HideHeader
+ canHeaderBeCreated(taskData, hasHeader, clickCloseListener) -> {
+ TaskHeaderUiState.ShowHeader(
+ TaskHeaderUiState.ThumbnailHeader(
+ // TODO(http://b/353965691): figure out what to do when `icon` or
+ // `titleDescription` is null.
+ taskData.icon!!,
+ taskData.titleDescription!!,
+ clickCloseListener!!,
+ )
+ )
+ }
+ else -> TaskHeaderUiState.HideHeader
+ }
+
/**
* Converts a [TaskData] object into a [TaskThumbnailUiState] for display in the UI.
*
@@ -36,45 +69,24 @@ object TaskUiStateMapper {
*
* @param taskData The [TaskData] to convert. Can be null or a specific subclass.
* @param isLiveTile A flag indicating whether the task data represents live tile.
- * @param hasHeader A flag indicating whether the UI should display a header.
- * @param clickCloseListener A callback when the close button in the UI is clicked.
* @return A [TaskThumbnailUiState] representing the UI state for the given task data.
*/
- fun toTaskThumbnailUiState(
- taskData: TaskData?,
- hasHeader: Boolean,
- clickCloseListener: OnClickListener?,
- ): TaskThumbnailUiState =
+ fun toTaskThumbnailUiState(taskData: TaskData?): TaskThumbnailUiState =
when {
taskData !is TaskData.Data -> Uninitialized
- taskData.isLiveTile -> createLiveTileState(taskData, hasHeader, clickCloseListener)
+ taskData.isLiveTile -> LiveTile
isBackgroundOnly(taskData) -> BackgroundOnly(taskData.backgroundColor)
isSnapshotSplash(taskData) ->
SnapshotSplash(
- createSnapshotState(taskData, hasHeader, clickCloseListener),
+ Snapshot(
+ taskData.thumbnailData?.thumbnail!!,
+ taskData.thumbnailData.rotation,
+ taskData.backgroundColor,
+ ),
taskData.icon,
)
- else -> Uninitialized
- }
- private fun createSnapshotState(
- taskData: TaskData.Data,
- hasHeader: Boolean,
- clickCloseListener: OnClickListener?,
- ): Snapshot =
- if (canHeaderBeCreated(taskData, hasHeader, clickCloseListener)) {
- Snapshot.WithHeader(
- taskData.thumbnailData?.thumbnail!!,
- taskData.thumbnailData.rotation,
- taskData.backgroundColor,
- ThumbnailHeader(taskData.icon!!, taskData.titleDescription!!, clickCloseListener!!),
- )
- } else {
- Snapshot.WithoutHeader(
- taskData.thumbnailData?.thumbnail!!,
- taskData.thumbnailData.rotation,
- taskData.backgroundColor,
- )
+ else -> Uninitialized
}
private fun isBackgroundOnly(taskData: TaskData.Data) =
@@ -88,21 +100,9 @@ object TaskUiStateMapper {
hasHeader: Boolean,
clickCloseListener: OnClickListener?,
) =
- hasHeader &&
+ enableDesktopExplodedView() &&
+ hasHeader &&
taskData.icon != null &&
taskData.titleDescription != null &&
clickCloseListener != null
-
- private fun createLiveTileState(
- taskData: TaskData.Data,
- hasHeader: Boolean,
- clickCloseListener: OnClickListener?,
- ) =
- if (canHeaderBeCreated(taskData, hasHeader, clickCloseListener)) {
- // TODO(http://b/353965691): figure out what to do when `icon` or `titleDescription` is
- // null.
- LiveTile.WithHeader(
- ThumbnailHeader(taskData.icon!!, taskData.titleDescription!!, clickCloseListener!!)
- )
- } else LiveTile.WithoutHeader
}
diff --git a/quickstep/src/com/android/quickstep/task/thumbnail/TaskContentView.kt b/quickstep/src/com/android/quickstep/task/thumbnail/TaskContentView.kt
new file mode 100644
index 0000000000..a010f81c1f
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/task/thumbnail/TaskContentView.kt
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.task.thumbnail
+
+import android.content.Context
+import android.graphics.Outline
+import android.graphics.Path
+import android.graphics.Rect
+import android.util.AttributeSet
+import android.view.View
+import android.view.ViewOutlineProvider
+import android.view.ViewStub
+import android.widget.LinearLayout
+import androidx.core.view.isInvisible
+import com.android.launcher3.R
+import com.android.launcher3.util.ViewPool
+import com.android.quickstep.views.TaskHeaderView
+
+/**
+ * TaskContentView is a wrapper around the TaskHeaderView and TaskThumbnailView. It is a sibling to
+ * DWB, AiAi (TaskOverlay).
+ */
+class TaskContentView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
+ LinearLayout(context, attrs), ViewPool.Reusable {
+
+ private var taskHeaderView: TaskHeaderView? = null
+ private var taskThumbnailView: TaskThumbnailView? = null
+ private var onSizeChanged: ((width: Int, height: Int) -> Unit)? = null
+ private val outlinePath = Path()
+
+ /**
+ * 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
+ set(value) {
+ field = value
+ invalidateOutline()
+ }
+
+ override fun onFinishInflate() {
+ super.onFinishInflate()
+ createTaskThumbnailView()
+ }
+
+ override fun setScaleX(scaleX: Float) {
+ super.setScaleX(scaleX)
+ taskThumbnailView?.parentScaleXUpdated(scaleX)
+ }
+
+ override fun setScaleY(scaleY: Float) {
+ super.setScaleY(scaleY)
+ taskThumbnailView?.parentScaleYUpdated(scaleY)
+ }
+
+ override fun onAttachedToWindow() {
+ super.onAttachedToWindow()
+ clipToOutline = true
+ outlineProvider =
+ object : ViewOutlineProvider() {
+ override fun getOutline(view: View, outline: Outline) {
+ val outlineRect = outlineBounds ?: bounds
+ outlinePath.apply {
+ rewind()
+ addRoundRect(
+ outlineRect.left.toFloat(),
+ outlineRect.top.toFloat(),
+ outlineRect.right.toFloat(),
+ outlineRect.bottom.toFloat(),
+ cornerRadius / scaleX,
+ cornerRadius / scaleY,
+ Path.Direction.CW,
+ )
+ }
+ outline.setPath(outlinePath)
+ }
+ }
+ }
+
+ override fun onRecycle() {
+ taskHeaderView?.isInvisible = true
+ onSizeChanged = null
+ outlineBounds = null
+ alpha = 1.0f
+ taskThumbnailView?.onRecycle()
+ }
+
+ fun doOnSizeChange(action: (width: Int, height: Int) -> Unit) {
+ onSizeChanged = action
+ }
+
+ override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
+ super.onSizeChanged(w, h, oldw, oldh)
+ onSizeChanged?.invoke(width, height)
+ bounds.set(0, 0, w, h)
+ invalidateOutline()
+ }
+
+ private fun createHeaderView(taskHeaderState: TaskHeaderUiState) {
+ if (taskHeaderView == null && taskHeaderState is TaskHeaderUiState.ShowHeader) {
+ taskHeaderView =
+ findViewById(R.id.task_header_view)
+ .apply { layoutResource = R.layout.task_header_view }
+ .inflate() as TaskHeaderView
+ }
+ }
+
+ private fun createTaskThumbnailView() {
+ if (taskThumbnailView == null) {
+ taskThumbnailView =
+ findViewById(R.id.snapshot)
+ .apply { layoutResource = R.layout.task_thumbnail }
+ .inflate() as TaskThumbnailView
+ }
+ }
+
+ fun setState(
+ taskHeaderState: TaskHeaderUiState,
+ taskThumbnailUiState: TaskThumbnailUiState,
+ taskId: Int?,
+ ) {
+ createHeaderView(taskHeaderState)
+ taskHeaderView?.setState(taskHeaderState)
+ taskThumbnailView?.setState(taskThumbnailUiState, taskId)
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/task/thumbnail/TaskHeaderUiState.kt b/quickstep/src/com/android/quickstep/task/thumbnail/TaskHeaderUiState.kt
new file mode 100644
index 0000000000..09fb5409b5
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/task/thumbnail/TaskHeaderUiState.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.task.thumbnail
+
+import android.graphics.drawable.Drawable
+import android.view.View
+
+sealed class TaskHeaderUiState {
+ data class ShowHeader(val header: ThumbnailHeader) : TaskHeaderUiState()
+
+ data object HideHeader : TaskHeaderUiState()
+
+ data class ThumbnailHeader(
+ val icon: Drawable,
+ val title: String,
+ val clickCloseListener: View.OnClickListener,
+ )
+}
diff --git a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailUiState.kt b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailUiState.kt
index db593d34d3..a5c9ac032f 100644
--- a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailUiState.kt
+++ b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailUiState.kt
@@ -19,7 +19,6 @@ package com.android.quickstep.task.thumbnail
import android.graphics.Bitmap
import android.graphics.drawable.Drawable
import android.view.Surface
-import android.view.View.OnClickListener
import androidx.annotation.ColorInt
sealed class TaskThumbnailUiState {
@@ -27,37 +26,14 @@ sealed class TaskThumbnailUiState {
data class BackgroundOnly(@ColorInt val backgroundColor: Int) : TaskThumbnailUiState()
+ data object LiveTile : TaskThumbnailUiState()
+
data class SnapshotSplash(val snapshot: Snapshot, val splash: Drawable?) :
TaskThumbnailUiState()
- sealed class LiveTile : TaskThumbnailUiState() {
- data class WithHeader(val header: ThumbnailHeader) : LiveTile()
-
- data object WithoutHeader : LiveTile()
- }
-
- sealed class Snapshot {
- abstract val bitmap: Bitmap
- abstract val thumbnailRotation: Int
- abstract val backgroundColor: Int
-
- data class WithHeader(
- override val bitmap: Bitmap,
- @Surface.Rotation override val thumbnailRotation: Int,
- @ColorInt override val backgroundColor: Int,
- val header: ThumbnailHeader,
- ) : Snapshot()
-
- data class WithoutHeader(
- override val bitmap: Bitmap,
- @Surface.Rotation override val thumbnailRotation: Int,
- @ColorInt override val backgroundColor: Int,
- ) : Snapshot()
- }
-
- data class ThumbnailHeader(
- val icon: Drawable,
- val title: String,
- val clickCloseListener: OnClickListener,
+ data class Snapshot(
+ val bitmap: Bitmap,
+ @Surface.Rotation val thumbnailRotation: Int,
+ @ColorInt val backgroundColor: Int,
)
}
diff --git a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
index 52e2fcdf6c..83d6025d8e 100644
--- a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
+++ b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
@@ -25,13 +25,12 @@ import android.graphics.Rect
import android.graphics.drawable.ShapeDrawable
import android.util.AttributeSet
import android.util.Log
-import android.view.LayoutInflater
import android.view.View
import android.view.ViewOutlineProvider
import android.widget.FrameLayout
import androidx.annotation.ColorInt
import androidx.core.view.isInvisible
-import com.android.launcher3.Flags.enableDesktopExplodedView
+import com.android.launcher3.Flags.enableRefactorTaskContentView
import com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA
import com.android.launcher3.R
import com.android.launcher3.util.MultiPropertyFactory
@@ -42,7 +41,6 @@ import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Snapshot
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.SnapshotSplash
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Uninitialized
import com.android.quickstep.views.FixedSizeImageView
-import com.android.quickstep.views.TaskThumbnailViewHeader
class TaskThumbnailView : FrameLayout, ViewPool.Reusable {
private val scrimView: View by lazy { findViewById(R.id.task_thumbnail_scrim) }
@@ -56,8 +54,6 @@ class TaskThumbnailView : FrameLayout, ViewPool.Reusable {
private val outlinePath = Path()
private var onSizeChanged: ((width: Int, height: Int) -> Unit)? = null
- private var taskThumbnailViewHeader: TaskThumbnailViewHeader? = null
-
private var uiState: TaskThumbnailUiState = Uninitialized
/**
@@ -89,6 +85,9 @@ class TaskThumbnailView : FrameLayout, ViewPool.Reusable {
override fun onAttachedToWindow() {
super.onAttachedToWindow()
+ if (enableRefactorTaskContentView()) {
+ return
+ }
clipToOutline = true
outlineProvider =
object : ViewOutlineProvider() {
@@ -113,9 +112,10 @@ class TaskThumbnailView : FrameLayout, ViewPool.Reusable {
override fun onRecycle() {
uiState = Uninitialized
- onSizeChanged = null
- outlineBounds = null
- alpha = 1.0f
+ if (!enableRefactorTaskContentView()) {
+ onSizeChanged = null
+ outlineBounds = null
+ }
resetViews()
}
@@ -126,7 +126,7 @@ class TaskThumbnailView : FrameLayout, ViewPool.Reusable {
resetViews()
when (state) {
is Uninitialized -> {}
- is LiveTile -> drawLiveWindow(state)
+ is LiveTile -> drawLiveWindow()
is SnapshotSplash -> drawSnapshotSplash(state)
is BackgroundOnly -> drawBackground(state.backgroundColor)
}
@@ -152,10 +152,16 @@ class TaskThumbnailView : FrameLayout, ViewPool.Reusable {
}
fun doOnSizeChange(action: (width: Int, height: Int) -> Unit) {
+ if (enableRefactorTaskContentView()) {
+ return
+ }
onSizeChanged = action
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
+ if (enableRefactorTaskContentView()) {
+ return
+ }
super.onSizeChanged(w, h, oldw, oldh)
onSizeChanged?.invoke(width, height)
bounds.set(0, 0, w, h)
@@ -163,17 +169,33 @@ class TaskThumbnailView : FrameLayout, ViewPool.Reusable {
}
override fun setScaleX(scaleX: Float) {
+ if (enableRefactorTaskContentView()) {
+ return
+ }
super.setScaleX(scaleX)
// Splash icon should ignore scale on TTV
splashIcon.scaleX = 1 / scaleX
}
override fun setScaleY(scaleY: Float) {
+ if (enableRefactorTaskContentView()) {
+ return
+ }
super.setScaleY(scaleY)
// Splash icon should ignore scale on TTV
splashIcon.scaleY = 1 / scaleY
}
+ fun parentScaleXUpdated(scaleX: Float) {
+ // Splash icon should ignore scale on TTV
+ splashIcon.scaleX = 1 / scaleX
+ }
+
+ fun parentScaleYUpdated(scaleY: Float) {
+ // Splash icon should ignore scale on TTV
+ splashIcon.scaleY = 1 / scaleY
+ }
+
private fun resetViews() {
liveTileView.isInvisible = true
thumbnailView.isInvisible = true
@@ -182,22 +204,16 @@ class TaskThumbnailView : FrameLayout, ViewPool.Reusable {
splashIcon.alpha = 0f
splashIcon.setImageDrawable(null)
scrimView.alpha = 0f
+ alpha = 1.0f
setBackgroundColor(Color.BLACK)
- taskThumbnailViewHeader?.isInvisible = true
}
private fun drawBackground(@ColorInt background: Int) {
setBackgroundColor(background)
}
- private fun drawLiveWindow(liveTile: LiveTile) {
+ private fun drawLiveWindow() {
liveTileView.isInvisible = false
-
- if (liveTile is LiveTile.WithHeader) {
- maybeCreateHeader()
- taskThumbnailViewHeader?.isInvisible = false
- taskThumbnailViewHeader?.setHeader(liveTile.header)
- }
}
private fun drawSnapshotSplash(snapshotSplash: SnapshotSplash) {
@@ -209,12 +225,6 @@ class TaskThumbnailView : FrameLayout, ViewPool.Reusable {
}
private fun drawSnapshot(snapshot: Snapshot) {
- if (snapshot is Snapshot.WithHeader) {
- maybeCreateHeader()
- taskThumbnailViewHeader?.isInvisible = false
- taskThumbnailViewHeader?.setHeader(snapshot.header)
- }
-
drawBackground(snapshot.backgroundColor)
thumbnailView.setImageBitmap(snapshot.bitmap)
thumbnailView.isInvisible = false
@@ -230,16 +240,6 @@ class TaskThumbnailView : FrameLayout, ViewPool.Reusable {
Log.d(TAG, "[TaskThumbnailView@${Integer.toHexString(hashCode())}] $message")
}
- private fun maybeCreateHeader() {
- if (enableDesktopExplodedView() && taskThumbnailViewHeader == null) {
- taskThumbnailViewHeader =
- LayoutInflater.from(context)
- .inflate(R.layout.task_thumbnail_view_header, this, false)
- as TaskThumbnailViewHeader
- addView(taskThumbnailViewHeader)
- }
- }
-
private companion object {
const val TAG = "TaskThumbnailView"
private const val MAX_SCRIM_ALPHA = 0.4f
diff --git a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
index 78e2b1a720..a96707eefd 100644
--- a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
+++ b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
@@ -193,7 +193,7 @@ class SplitAnimationController(val splitSelectStateController: SplitSelectStateC
taskViewHeight: Int,
isPrimaryTaskSplitting: Boolean,
) {
- val snapshot = taskContainer.snapshotView
+ val taskContentView = taskContainer.taskContentView
val iconView: View = taskContainer.iconView.asView()
if (enableRefactorTaskThumbnail()) {
builder.add(
@@ -242,7 +242,11 @@ class SplitAnimationController(val splitSelectStateController: SplitSelectStateC
val centerThumbnailTranslationX: Float = (taskViewWidth - snapshotViewSize.x) / 2f
val finalScaleX: Float = taskViewWidth.toFloat() / snapshotViewSize.x
builder.add(
- ObjectAnimator.ofFloat(snapshot, View.TRANSLATION_X, centerThumbnailTranslationX)
+ ObjectAnimator.ofFloat(
+ taskContentView,
+ View.TRANSLATION_X,
+ centerThumbnailTranslationX,
+ )
)
if (!enableOverviewIconMenu()) {
// icons are anchored from Gravity.END, so need to use negative translation
@@ -251,15 +255,17 @@ class SplitAnimationController(val splitSelectStateController: SplitSelectStateC
ObjectAnimator.ofFloat(iconView, View.TRANSLATION_X, -centerIconTranslationX)
)
}
- builder.add(ObjectAnimator.ofFloat(snapshot, View.SCALE_X, finalScaleX))
+ builder.add(ObjectAnimator.ofFloat(taskContentView, View.SCALE_X, finalScaleX))
// Reset other dimensions
// TODO(b/271468547), can't set Y translate to 0, need to account for top space
- snapshot.scaleY = 1f
+ taskContentView.scaleY = 1f
val translateYResetVal: Float =
if (!isPrimaryTaskSplitting) 0f
else deviceProfile.overviewTaskThumbnailTopMarginPx.toFloat()
- builder.add(ObjectAnimator.ofFloat(snapshot, View.TRANSLATION_Y, translateYResetVal))
+ builder.add(
+ ObjectAnimator.ofFloat(taskContentView, View.TRANSLATION_Y, translateYResetVal)
+ )
} else {
val thumbnailSize = taskViewHeight - deviceProfile.overviewTaskThumbnailTopMarginPx
// Center view first so scaling happens uniformly, alternatively we can move pivotY to 0
@@ -282,18 +288,22 @@ class SplitAnimationController(val splitSelectStateController: SplitSelectStateC
}
val finalScaleY: Float = thumbnailSize.toFloat() / snapshotViewSize.y
builder.add(
- ObjectAnimator.ofFloat(snapshot, View.TRANSLATION_Y, centerThumbnailTranslationY)
+ ObjectAnimator.ofFloat(
+ taskContentView,
+ View.TRANSLATION_Y,
+ centerThumbnailTranslationY,
+ )
)
if (!enableOverviewIconMenu()) {
// icons are anchored from Gravity.END, so need to use negative translation
builder.add(ObjectAnimator.ofFloat(iconView, View.TRANSLATION_X, 0f))
}
- builder.add(ObjectAnimator.ofFloat(snapshot, View.SCALE_Y, finalScaleY))
+ builder.add(ObjectAnimator.ofFloat(taskContentView, View.SCALE_Y, finalScaleY))
// Reset other dimensions
- snapshot.scaleX = 1f
- builder.add(ObjectAnimator.ofFloat(snapshot, View.TRANSLATION_X, 0f))
+ taskContentView.scaleX = 1f
+ builder.add(ObjectAnimator.ofFloat(taskContentView, View.TRANSLATION_X, 0f))
}
}
diff --git a/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt b/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt
index 38c0eb3cc9..1f719480f8 100644
--- a/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt
+++ b/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt
@@ -33,6 +33,7 @@ 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.enableRefactorTaskContentView
import com.android.launcher3.Flags.enableRefactorTaskThumbnail
import com.android.launcher3.R
import com.android.launcher3.statehandlers.DesktopVisibilityController
@@ -56,6 +57,7 @@ import com.android.quickstep.recents.di.get
import com.android.quickstep.recents.domain.model.DesktopTaskBoundsData
import com.android.quickstep.recents.ui.viewmodel.DesktopTaskViewModel
import com.android.quickstep.recents.ui.viewmodel.TaskData
+import com.android.quickstep.task.thumbnail.TaskContentView
import com.android.quickstep.task.thumbnail.TaskThumbnailView
import com.android.quickstep.util.DesktopTask
import com.android.quickstep.util.RecentsOrientedState
@@ -100,6 +102,17 @@ class DesktopTaskView @JvmOverloads constructor(context: Context, attrs: Attribu
)
} else null
+ private val taskContentViewPool =
+ if (enableRefactorTaskContentView()) {
+ ViewPool(
+ context,
+ this,
+ R.layout.task_content_view,
+ VIEW_POOL_MAX_SIZE,
+ VIEW_POOL_INITIAL_SIZE,
+ )
+ } else null
+
private val tempPointF = PointF()
private val lastComputedTaskSize = Rect()
private lateinit var iconView: TaskViewIcon
@@ -241,7 +254,7 @@ class DesktopTaskView @JvmOverloads constructor(context: Context, attrs: Attribu
val overviewTaskHeight = overviewTaskBounds.height() * scaleHeight
if (updateLayout) {
// Position the task to the same position as it would be on the desktop
- taskContainer.snapshotView.updateLayoutParams {
+ taskContainer.taskContentView.updateLayoutParams {
gravity = Gravity.LEFT or Gravity.TOP
width = overviewTaskWidth.toInt()
height = overviewTaskHeight.toInt()
@@ -257,7 +270,7 @@ class DesktopTaskView @JvmOverloads constructor(context: Context, attrs: Attribu
overviewTaskBounds.width().toFloat() / currentTaskBounds.width()
val thumbnailScaleHeight =
overviewTaskBounds.height().toFloat() / currentTaskBounds.height()
- taskContainer.thumbnailView.outlineBounds =
+ val contentOutlineBounds =
if (intersects(currentTaskBounds, screenRect))
Rect(currentTaskBounds).apply {
intersectUnchecked(screenRect)
@@ -270,6 +283,13 @@ class DesktopTaskView @JvmOverloads constructor(context: Context, attrs: Attribu
bottom = (bottom * scaleHeight * thumbnailScaleHeight).roundToInt()
}
else null
+
+ if (enableRefactorTaskContentView()) {
+ (taskContainer.taskContentView as TaskContentView).outlineBounds =
+ contentOutlineBounds
+ } else {
+ taskContainer.thumbnailView.outlineBounds = contentOutlineBounds
+ }
}
val currentTaskLeft = currentTaskBounds.left * scaleWidth
@@ -278,7 +298,7 @@ class DesktopTaskView @JvmOverloads constructor(context: Context, attrs: Attribu
val currentTaskHeight = currentTaskBounds.height() * scaleHeight
// During the animation, apply translation and scale such that the view is transformed
// to where we want, without triggering layout.
- taskContainer.snapshotView.apply {
+ taskContainer.taskContentView.apply {
pivotX = 0.0f
pivotY = 0.0f
translationX = currentTaskLeft - overviewTaskLeft
@@ -288,7 +308,7 @@ class DesktopTaskView @JvmOverloads constructor(context: Context, attrs: Attribu
}
if (taskContainer.task.isMinimized) {
- taskContainer.snapshotView.alpha = explodeProgress
+ taskContainer.taskContentView.alpha = explodeProgress
}
}
}
@@ -331,17 +351,24 @@ class DesktopTaskView @JvmOverloads constructor(context: Context, attrs: Attribu
val backgroundViewIndex = contentView.indexOfChild(backgroundView)
taskContainers =
tasks.map { task ->
- val snapshotView =
- if (enableRefactorTaskThumbnail()) {
- taskThumbnailViewPool!!.view
- } else {
- taskThumbnailViewDeprecatedPool!!.view
+ val taskContentView =
+ when {
+ enableRefactorTaskContentView() -> taskContentViewPool!!.view
+ enableRefactorTaskThumbnail() -> taskThumbnailViewPool!!.view
+ else -> taskThumbnailViewDeprecatedPool!!.view
+ }
+ contentView.addView(taskContentView, backgroundViewIndex + 1)
+ val snapshotView =
+ if (enableRefactorTaskContentView()) {
+ taskContentView.findViewById(R.id.snapshot)
+ } else {
+ taskContentView
}
- contentView.addView(snapshotView, backgroundViewIndex + 1)
TaskContainer(
this,
task,
+ taskContentView,
snapshotView,
iconView,
TransformingTouchDelegate(iconView.asView()),
@@ -507,11 +534,13 @@ class DesktopTaskView @JvmOverloads constructor(context: Context, attrs: Attribu
}
private fun removeAndRecycleThumbnailView(taskContainer: TaskContainer) {
- contentView.removeView(taskContainer.snapshotView)
- if (enableRefactorTaskThumbnail()) {
- taskThumbnailViewPool!!.recycle(taskContainer.thumbnailView)
- } else {
- taskThumbnailViewDeprecatedPool!!.recycle(taskContainer.thumbnailViewDeprecated)
+ contentView.removeView(taskContainer.taskContentView)
+ when {
+ enableRefactorTaskContentView() ->
+ taskContentViewPool!!.recycle(taskContainer.taskContentView as TaskContentView)
+ enableRefactorTaskThumbnail() ->
+ taskThumbnailViewPool!!.recycle(taskContainer.taskContentView as TaskThumbnailView)
+ else -> taskThumbnailViewDeprecatedPool!!.recycle(taskContainer.thumbnailViewDeprecated)
}
}
diff --git a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.kt b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.kt
index 39739a1755..f72e351ece 100644
--- a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.kt
+++ b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.kt
@@ -339,7 +339,7 @@ constructor(
taskView.layoutParams.height,
splitBounds,
recentsViewContainer.deviceProfile,
- taskView.snapshotViews,
+ taskView.taskContentViews,
task.key.id,
this,
)
diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt b/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt
index e3e987ccf7..34abcc084b 100644
--- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt
+++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt
@@ -23,6 +23,7 @@ import android.util.Log
import android.view.View
import android.view.ViewStub
import com.android.internal.jank.Cuj
+import com.android.launcher3.Flags.enableRefactorTaskContentView
import com.android.launcher3.Flags.enableRefactorTaskThumbnail
import com.android.launcher3.R
import com.android.launcher3.Utilities
@@ -78,8 +79,8 @@ class GroupedTaskView @JvmOverloads constructor(context: Context, attrs: Attribu
val splitBoundsConfig = splitBoundsConfig ?: return
val inSplitSelection = getThisTaskCurrentlyInSplitSelection() != INVALID_TASK_ID
pagedOrientationHandler.measureGroupedTaskViewThumbnailBounds(
- leftTopTaskContainer.snapshotView,
- rightBottomTaskContainer.snapshotView,
+ leftTopTaskContainer.taskContentView,
+ rightBottomTaskContainer.taskContentView,
widthSize,
heightSize,
splitBoundsConfig,
@@ -95,11 +96,17 @@ class GroupedTaskView @JvmOverloads constructor(context: Context, attrs: Attribu
override fun inflateViewStubs() {
super.inflateViewStubs()
- findViewById(R.id.bottomright_snapshot)
+ findViewById(R.id.bottomright_task_content_view)
?.apply {
+ inflatedId =
+ if (enableRefactorTaskContentView()) R.id.bottomright_task_content_view
+ else R.id.bottomright_snapshot
layoutResource =
- if (enableRefactorTaskThumbnail()) R.layout.task_thumbnail
- else R.layout.task_thumbnail_deprecated
+ when {
+ enableRefactorTaskContentView() -> R.layout.task_content_view
+ enableRefactorTaskThumbnail() -> R.layout.task_thumbnail
+ else -> R.layout.task_thumbnail_deprecated
+ }
}
?.inflate()
findViewById(R.id.bottomRight_icon)
@@ -130,6 +137,7 @@ class GroupedTaskView @JvmOverloads constructor(context: Context, attrs: Attribu
listOf(
createTaskContainer(
splitTask.topLeftTask,
+ R.id.task_content_view,
R.id.snapshot,
R.id.icon,
R.id.show_windows,
@@ -139,7 +147,9 @@ class GroupedTaskView @JvmOverloads constructor(context: Context, attrs: Attribu
),
createTaskContainer(
splitTask.bottomRightTask,
- R.id.bottomright_snapshot,
+ R.id.bottomright_task_content_view,
+ if (enableRefactorTaskContentView()) R.id.snapshot
+ else R.id.bottomright_snapshot,
R.id.bottomRight_icon,
R.id.show_windows_right,
R.id.bottomRight_digital_wellbeing_toast,
@@ -242,8 +252,8 @@ class GroupedTaskView @JvmOverloads constructor(context: Context, attrs: Attribu
leftTopTaskContainer.iconView.asView(),
rightBottomTaskContainer.iconView.asView(),
taskIconHeight,
- leftTopTaskContainer.snapshotView.measuredWidth,
- leftTopTaskContainer.snapshotView.measuredHeight,
+ leftTopTaskContainer.taskContentView.measuredWidth,
+ leftTopTaskContainer.taskContentView.measuredHeight,
measuredHeight,
measuredWidth,
isLayoutRtl,
diff --git a/quickstep/src/com/android/quickstep/views/TaskContainer.kt b/quickstep/src/com/android/quickstep/views/TaskContainer.kt
index 919907bd01..b187df1e99 100644
--- a/quickstep/src/com/android/quickstep/views/TaskContainer.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskContainer.kt
@@ -21,6 +21,7 @@ import android.graphics.Matrix
import android.view.View
import android.view.View.OnClickListener
import com.android.app.tracing.traceSection
+import com.android.launcher3.Flags.enableRefactorTaskContentView
import com.android.launcher3.Flags.enableRefactorTaskThumbnail
import com.android.launcher3.model.data.TaskViewItemInfo
import com.android.launcher3.util.OverviewReleaseFlags.enableOverviewIconMenu
@@ -31,6 +32,7 @@ import com.android.quickstep.ViewUtils.addAccessibleChildToList
import com.android.quickstep.recents.domain.usecase.ThumbnailPosition
import com.android.quickstep.recents.ui.mapper.TaskUiStateMapper
import com.android.quickstep.recents.ui.viewmodel.TaskData
+import com.android.quickstep.task.thumbnail.TaskContentView
import com.android.quickstep.task.thumbnail.TaskThumbnailView
import com.android.systemui.shared.recents.model.Task
import com.android.systemui.shared.recents.model.ThumbnailData
@@ -39,6 +41,8 @@ import com.android.systemui.shared.recents.model.ThumbnailData
class TaskContainer(
val taskView: TaskView,
val task: Task,
+ // TODO(b/409248525): Upon flag cleanup, use the `TaskContentView` type
+ val taskContentView: View,
val snapshotView: View,
val iconView: TaskViewIcon,
/**
@@ -59,10 +63,19 @@ class TaskContainer(
private var overlayEnabledStatus = false
init {
- if (enableRefactorTaskThumbnail()) {
- require(snapshotView is TaskThumbnailView)
- } else {
- require(snapshotView is TaskThumbnailViewDeprecated)
+ when {
+ enableRefactorTaskContentView() -> {
+ require(taskContentView is TaskContentView)
+ require(snapshotView is TaskThumbnailView)
+ }
+ enableRefactorTaskThumbnail() -> {
+ require(taskContentView is TaskThumbnailView)
+ require(snapshotView is TaskThumbnailView)
+ }
+ else -> {
+ require(taskContentView is TaskThumbnailViewDeprecated)
+ require(snapshotView is TaskThumbnailViewDeprecated)
+ }
}
}
@@ -110,8 +123,8 @@ class TaskContainer(
fun destroy() =
traceSection("TaskContainer.destroy") {
digitalWellBeingToast?.destroy()
- snapshotView.scaleX = 1f
- snapshotView.scaleY = 1f
+ taskContentView.scaleX = 1f
+ taskContentView.scaleY = 1f
overlay.reset()
if (enableRefactorTaskThumbnail()) {
isThumbnailValid = false
@@ -159,7 +172,10 @@ class TaskContainer(
fun addChildForAccessibility(outChildren: ArrayList) {
addAccessibleChildToList(iconView.asView(), outChildren)
- addAccessibleChildToList(snapshotView, outChildren)
+ addAccessibleChildToList(
+ if (enableRefactorTaskContentView()) taskContentView else snapshotView,
+ outChildren,
+ )
showWindowsView?.let { addAccessibleChildToList(it, outChildren) }
digitalWellBeingToast?.let { addAccessibleChildToList(it, outChildren) }
overlay.addChildForAccessibility(outChildren)
@@ -167,10 +183,18 @@ class TaskContainer(
fun setState(state: TaskData?, hasHeader: Boolean, clickCloseListener: OnClickListener?) =
traceSection("TaskContainer.setState") {
- thumbnailView.setState(
- TaskUiStateMapper.toTaskThumbnailUiState(state, hasHeader, clickCloseListener),
- state?.taskId,
- )
+ if (enableRefactorTaskContentView()) {
+ (taskContentView as TaskContentView).setState(
+ TaskUiStateMapper.toTaskHeaderState(state, hasHeader, clickCloseListener),
+ TaskUiStateMapper.toTaskThumbnailUiState(state),
+ state?.taskId,
+ )
+ } else {
+ thumbnailView.setState(
+ TaskUiStateMapper.toTaskThumbnailUiState(state),
+ state?.taskId,
+ )
+ }
thumbnailData = if (state is TaskData.Data) state.thumbnailData else null
overlay.setThumbnailState(thumbnailData)
}
diff --git a/quickstep/src/com/android/quickstep/views/TaskThumbnailViewHeader.kt b/quickstep/src/com/android/quickstep/views/TaskHeaderView.kt
similarity index 64%
rename from quickstep/src/com/android/quickstep/views/TaskThumbnailViewHeader.kt
rename to quickstep/src/com/android/quickstep/views/TaskHeaderView.kt
index 9a8805bf0f..1fda5a3ace 100644
--- a/quickstep/src/com/android/quickstep/views/TaskThumbnailViewHeader.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskHeaderView.kt
@@ -18,23 +18,33 @@ package com.android.quickstep.views
import android.content.Context
import android.util.AttributeSet
-import android.widget.FrameLayout
import android.widget.ImageButton
import android.widget.ImageView
import android.widget.TextView
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.core.view.isGone
import com.android.launcher3.R
-import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.ThumbnailHeader
+import com.android.quickstep.task.thumbnail.TaskHeaderUiState
-class TaskThumbnailViewHeader
-@JvmOverloads
-constructor(context: Context, attrs: AttributeSet? = null) : FrameLayout(context, attrs) {
+class TaskHeaderView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
+ ConstraintLayout(context, attrs) {
private val headerTitleView: TextView by lazy { findViewById(R.id.header_app_title) }
private val headerIconView: ImageView by lazy { findViewById(R.id.header_app_icon) }
private val headerCloseButton: ImageButton by lazy { findViewById(R.id.header_close_button) }
- fun setHeader(header: ThumbnailHeader) {
- headerTitleView.setText(header.title)
+ fun setState(taskHeaderState: TaskHeaderUiState) {
+ when (taskHeaderState) {
+ is TaskHeaderUiState.ShowHeader -> {
+ setHeader(taskHeaderState.header)
+ isGone = false
+ }
+ TaskHeaderUiState.HideHeader -> isGone = true
+ }
+ }
+
+ private fun setHeader(header: TaskHeaderUiState.ThumbnailHeader) {
+ headerTitleView.text = header.title
headerIconView.setImageDrawable(header.icon)
headerCloseButton.setOnClickListener(header.clickCloseListener)
}
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.kt b/quickstep/src/com/android/quickstep/views/TaskView.kt
index 0edd87bfe3..49dd66805c 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskView.kt
@@ -48,6 +48,7 @@ import com.android.launcher3.Flags.enableCursorHoverStates
import com.android.launcher3.Flags.enableDesktopExplodedView
import com.android.launcher3.Flags.enableHoverOfChildElementsInTaskview
import com.android.launcher3.Flags.enableLargeDesktopWindowingTile
+import com.android.launcher3.Flags.enableRefactorTaskContentView
import com.android.launcher3.Flags.enableRefactorTaskThumbnail
import com.android.launcher3.Flags.enableSeparateExternalDisplayTasks
import com.android.launcher3.R
@@ -88,6 +89,7 @@ import com.android.quickstep.recents.domain.usecase.ThumbnailPosition
import com.android.quickstep.recents.ui.viewmodel.TaskData
import com.android.quickstep.recents.ui.viewmodel.TaskTileUiState
import com.android.quickstep.recents.ui.viewmodel.TaskViewModel
+import com.android.quickstep.task.thumbnail.TaskContentView
import com.android.quickstep.util.ActiveGestureErrorDetector
import com.android.quickstep.util.ActiveGestureLog
import com.android.quickstep.util.BorderAnimator
@@ -146,6 +148,9 @@ constructor(
val snapshotViews: Array
get() = taskContainers.map { it.snapshotView }.toTypedArray()
+ val taskContentViews: Array
+ get() = taskContainers.map { it.taskContentView }.toTypedArray()
+
val isGridTask: Boolean
/** Returns whether the task is part of overview grid and not being focused. */
get() = container.deviceProfile.isTablet && !isLargeTile
@@ -785,13 +790,19 @@ constructor(
}
protected open fun inflateViewStubs() {
- findViewById(R.id.snapshot)
+ findViewById(R.id.task_content_view)
?.apply {
+ inflatedId =
+ if (enableRefactorTaskContentView()) R.id.task_content_view else R.id.snapshot
layoutResource =
- if (enableRefactorTaskThumbnail()) R.layout.task_thumbnail
- else R.layout.task_thumbnail_deprecated
+ when {
+ enableRefactorTaskContentView() -> R.layout.task_content_view
+ enableRefactorTaskThumbnail() -> R.layout.task_thumbnail
+ else -> R.layout.task_thumbnail_deprecated
+ }
}
?.inflate()
+
findViewById(R.id.icon)
?.apply {
layoutResource =
@@ -950,6 +961,7 @@ constructor(
listOf(
createTaskContainer(
singleTask.task,
+ R.id.task_content_view,
R.id.snapshot,
R.id.icon,
R.id.show_windows,
@@ -972,7 +984,15 @@ constructor(
taskContainers.forEach { container ->
container.bind()
- if (enableRefactorTaskThumbnail()) {
+ if (enableRefactorTaskContentView()) {
+ (container.taskContentView as TaskContentView).cornerRadius =
+ thumbnailFullscreenParams.currentCornerRadius
+ container.taskContentView.doOnSizeChange { width, height ->
+ updateThumbnailValidity(container)
+ val thumbnailPosition = updateThumbnailMatrix(container, width, height)
+ container.refreshOverlay(thumbnailPosition)
+ }
+ } else if (enableRefactorTaskThumbnail()) {
container.thumbnailView.cornerRadius =
thumbnailFullscreenParams.currentCornerRadius
container.thumbnailView.doOnSizeChange { width, height ->
@@ -1002,6 +1022,7 @@ constructor(
protected fun createTaskContainer(
task: Task,
+ @IdRes taskContentViewId: Int,
@IdRes thumbnailViewId: Int,
@IdRes iconViewId: Int,
@IdRes showWindowViewId: Int,
@@ -1011,10 +1032,17 @@ constructor(
): TaskContainer =
traceSection("TaskView.createTaskContainer") {
val iconView = findViewById(iconViewId) as TaskViewIcon
+ val taskContentView =
+ if (enableRefactorTaskContentView()) findViewById(taskContentViewId)
+ else findViewById(thumbnailViewId)
+ val snapshotView =
+ if (enableRefactorTaskContentView()) taskContentView.findViewById(thumbnailViewId)
+ else taskContentView
return TaskContainer(
this,
task,
- findViewById(thumbnailViewId),
+ taskContentView,
+ snapshotView,
iconView,
TransformingTouchDelegate(iconView.asView()),
stagePosition,
@@ -1103,7 +1131,7 @@ constructor(
protected open fun updateThumbnailSize() {
// TODO(b/271468547), we should default to setting translations only on the snapshot instead
// of a hybrid of both margins and translations
- firstTaskContainer?.snapshotView?.updateLayoutParams {
+ firstTaskContainer?.taskContentView?.updateLayoutParams {
topMargin = container.deviceProfile.overviewTaskThumbnailTopMarginPx
}
taskContainers.forEach { it.digitalWellBeingToast?.setupLayout() }
@@ -1117,11 +1145,11 @@ constructor(
val thumbnailBounds = Rect()
if (relativeToDragLayer) {
container.dragLayer.getDescendantRectRelativeToSelf(
- it.snapshotView,
+ it.taskContentView,
thumbnailBounds,
)
} else {
- thumbnailBounds.set(it.snapshotView)
+ thumbnailBounds.set(it.taskContentView)
}
bounds.union(thumbnailBounds)
}
@@ -1762,7 +1790,7 @@ constructor(
open fun setThumbnailVisibility(visibility: Int, taskId: Int) {
taskContainers.forEach {
if (visibility == VISIBLE || it.task.key.id == taskId) {
- it.snapshotView.visibility = visibility
+ it.taskContentView.visibility = visibility
it.digitalWellBeingToast?.visibility = visibility
it.showWindowsView?.visibility = visibility
it.overlay.setVisibility(visibility)
@@ -1836,7 +1864,10 @@ constructor(
protected open fun updateFullscreenParams() {
updateFullscreenParams(thumbnailFullscreenParams)
taskContainers.forEach {
- if (enableRefactorTaskThumbnail()) {
+ if (enableRefactorTaskContentView()) {
+ (it.taskContentView as TaskContentView).cornerRadius =
+ thumbnailFullscreenParams.currentCornerRadius
+ } else if (enableRefactorTaskThumbnail()) {
it.thumbnailView.cornerRadius = thumbnailFullscreenParams.currentCornerRadius
} else {
it.thumbnailViewDeprecated.setFullscreenParams(thumbnailFullscreenParams)
diff --git a/quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/SplashHelper.kt b/quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/SplashHelper.kt
new file mode 100644
index 0000000000..8cc09d470b
--- /dev/null
+++ b/quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/SplashHelper.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.task.thumbnail
+
+import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.Paint
+
+object SplashHelper {
+ private val BITMAP_RECT_COLORS = listOf(Color.GREEN, Color.RED, Color.BLUE, Color.CYAN)
+
+ fun createSplash(): Bitmap = createBitmap(width = 20, height = 20, rectColorRotation = 1)
+
+ fun createBitmap(width: Int, height: Int, rectColorRotation: Int = 0): Bitmap =
+ Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888).apply {
+ Canvas(this).apply {
+ val paint = Paint()
+ paint.color = BITMAP_RECT_COLORS[rectColorRotation % 4]
+ drawRect(0f, 0f, width / 2f, height / 2f, paint)
+ paint.color = BITMAP_RECT_COLORS[(1 + rectColorRotation) % 4]
+ drawRect(width / 2f, 0f, width.toFloat(), height / 2f, paint)
+ paint.color = BITMAP_RECT_COLORS[(2 + rectColorRotation) % 4]
+ drawRect(0f, height / 2f, width / 2f, height.toFloat(), paint)
+ paint.color = BITMAP_RECT_COLORS[(3 + rectColorRotation) % 4]
+ drawRect(width / 2f, height / 2f, width.toFloat(), height.toFloat(), paint)
+ }
+ }
+}
diff --git a/quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/TaskContentViewScreenshotTest.kt b/quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/TaskContentViewScreenshotTest.kt
new file mode 100644
index 0000000000..5e546f7f1a
--- /dev/null
+++ b/quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/TaskContentViewScreenshotTest.kt
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.task.thumbnail
+
+import android.content.Context
+import android.graphics.Color
+import android.graphics.drawable.BitmapDrawable
+import android.platform.test.flag.junit.SetFlagsRule
+import android.view.LayoutInflater
+import com.android.launcher3.Flags
+import com.android.launcher3.R
+import com.android.launcher3.util.rule.setFlags
+import com.android.quickstep.task.thumbnail.SplashHelper.createSplash
+import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.BackgroundOnly
+import com.google.android.apps.nexuslauncher.imagecomparison.goldenpathmanager.ViewScreenshotGoldenPathManager
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
+import platform.test.screenshot.DeviceEmulationSpec
+import platform.test.screenshot.Displays
+import platform.test.screenshot.ViewScreenshotTestRule
+import platform.test.screenshot.getEmulatedDevicePathConfig
+
+/** Screenshot tests for [TaskContentView]. */
+@RunWith(ParameterizedAndroidJunit4::class)
+class TaskContentViewScreenshotTest(emulationSpec: DeviceEmulationSpec) {
+
+ @get:Rule(order = 0) val setFlagsRule = SetFlagsRule()
+
+ @get:Rule(order = 1)
+ val screenshotRule =
+ ViewScreenshotTestRule(
+ emulationSpec,
+ ViewScreenshotGoldenPathManager(getEmulatedDevicePathConfig(emulationSpec)),
+ )
+
+ @Before
+ fun setUp() {
+ setFlagsRule.setFlags(
+ true,
+ Flags.FLAG_ENABLE_REFACTOR_TASK_THUMBNAIL,
+ Flags.FLAG_ENABLE_REFACTOR_TASK_CONTENT_VIEW,
+ )
+ }
+
+ @Test
+ fun taskContentView_recyclesToUninitialized() {
+ screenshotRule.screenshotTest("taskContentView_uninitialized") { activity ->
+ activity.actionBar?.hide()
+ val taskContentView = createTaskContentView(activity)
+ taskContentView.setState(
+ TaskHeaderUiState.HideHeader,
+ BackgroundOnly(Color.YELLOW),
+ null,
+ )
+ taskContentView.onRecycle()
+ taskContentView
+ }
+ }
+
+ @Test
+ fun taskContentView_shows_thumbnail_and_header() {
+ screenshotRule.screenshotTest("taskContentView_shows_thumbnail_and_header") { activity ->
+ activity.actionBar?.hide()
+ createTaskContentView(activity).apply {
+ setState(
+ TaskHeaderUiState.ShowHeader(
+ TaskHeaderUiState.ThumbnailHeader(
+ BitmapDrawable(activity.resources, createSplash()),
+ "test",
+ ) {}
+ ),
+ BackgroundOnly(Color.YELLOW),
+ null,
+ )
+ }
+ }
+ }
+
+ @Test
+ fun taskContentView_scaled_roundRoundedCorners() {
+ screenshotRule.screenshotTest("taskContentView_scaledRoundedCorners") { activity ->
+ activity.actionBar?.hide()
+ createTaskContentView(activity).apply {
+ scaleX = 0.75f
+ scaleY = 0.3f
+ setState(TaskHeaderUiState.HideHeader, BackgroundOnly(Color.YELLOW), null)
+ }
+ }
+ }
+
+ private fun createTaskContentView(context: Context): TaskContentView {
+ val taskContentView =
+ LayoutInflater.from(context).inflate(R.layout.task_content_view, null, false)
+ as TaskContentView
+ taskContentView.cornerRadius = CORNER_RADIUS
+ return taskContentView
+ }
+
+ companion object {
+ @Parameters(name = "{0}")
+ @JvmStatic
+ fun getTestSpecs() =
+ DeviceEmulationSpec.forDisplays(
+ Displays.Phone,
+ isDarkTheme = false,
+ isLandscape = false,
+ )
+
+ const val CORNER_RADIUS = 56f
+ }
+}
diff --git a/quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/TaskHeaderViewScreenshotTest.kt b/quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/TaskHeaderViewScreenshotTest.kt
new file mode 100644
index 0000000000..e30554e647
--- /dev/null
+++ b/quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/TaskHeaderViewScreenshotTest.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.task.thumbnail
+
+import android.content.Context
+import android.graphics.drawable.BitmapDrawable
+import android.view.LayoutInflater
+import com.android.launcher3.R
+import com.android.quickstep.task.thumbnail.SplashHelper.createSplash
+import com.android.quickstep.views.TaskHeaderView
+import com.google.android.apps.nexuslauncher.imagecomparison.goldenpathmanager.ViewScreenshotGoldenPathManager
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
+import platform.test.screenshot.DeviceEmulationSpec
+import platform.test.screenshot.Displays
+import platform.test.screenshot.ViewScreenshotTestRule
+import platform.test.screenshot.getEmulatedDevicePathConfig
+
+/** Screenshot tests for [TaskHeaderView]. */
+@RunWith(ParameterizedAndroidJunit4::class)
+class TaskHeaderViewScreenshotTest(emulationSpec: DeviceEmulationSpec) {
+ @get:Rule
+ val screenshotRule =
+ ViewScreenshotTestRule(
+ emulationSpec,
+ ViewScreenshotGoldenPathManager(getEmulatedDevicePathConfig(emulationSpec)),
+ )
+
+ @Test
+ fun taskHeaderView_showHeader() {
+ screenshotRule.screenshotTest("taskHeaderView_showHeader") { activity ->
+ activity.actionBar?.hide()
+ createTaskHeaderView(activity).apply {
+ setState(
+ TaskHeaderUiState.ShowHeader(
+ TaskHeaderUiState.ThumbnailHeader(
+ BitmapDrawable(activity.resources, createSplash()),
+ "Example",
+ ) {}
+ )
+ )
+ }
+ }
+ }
+
+ private fun createTaskHeaderView(context: Context): TaskHeaderView {
+ val taskHeaderView =
+ LayoutInflater.from(context).inflate(R.layout.task_header_view, null, false)
+ as TaskHeaderView
+ return taskHeaderView
+ }
+
+ companion object {
+ @Parameters(name = "{0}")
+ @JvmStatic
+ fun getTestSpecs() =
+ DeviceEmulationSpec.forDisplays(
+ Displays.Tablet,
+ isDarkTheme = false,
+ isLandscape = true,
+ )
+ }
+}
diff --git a/quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewScreenshotTest.kt b/quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewScreenshotTest.kt
index 80b2c16e10..81b046adb3 100644
--- a/quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewScreenshotTest.kt
+++ b/quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewScreenshotTest.kt
@@ -16,21 +16,23 @@
package com.android.quickstep.task.thumbnail
import android.content.Context
-import android.graphics.Bitmap
-import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Matrix
-import android.graphics.Paint
import android.graphics.drawable.BitmapDrawable
+import android.platform.test.flag.junit.SetFlagsRule
import android.view.LayoutInflater
import android.view.Surface.ROTATION_0
-import androidx.core.graphics.set
+import com.android.launcher3.Flags
import com.android.launcher3.R
+import com.android.launcher3.util.rule.setFlags
+import com.android.quickstep.task.thumbnail.SplashHelper.createBitmap
+import com.android.quickstep.task.thumbnail.SplashHelper.createSplash
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.BackgroundOnly
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Snapshot
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.SnapshotSplash
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Uninitialized
import com.google.android.apps.nexuslauncher.imagecomparison.goldenpathmanager.ViewScreenshotGoldenPathManager
+import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -45,13 +47,20 @@ import platform.test.screenshot.getEmulatedDevicePathConfig
@RunWith(ParameterizedAndroidJunit4::class)
class TaskThumbnailViewScreenshotTest(emulationSpec: DeviceEmulationSpec) {
- @get:Rule
+ @get:Rule(order = 0) val setFlagsRule = SetFlagsRule()
+
+ @get:Rule(order = 1)
val screenshotRule =
ViewScreenshotTestRule(
emulationSpec,
ViewScreenshotGoldenPathManager(getEmulatedDevicePathConfig(emulationSpec)),
)
+ @Before
+ fun setUp() {
+ setFlagsRule.setFlags(false, Flags.FLAG_ENABLE_REFACTOR_TASK_CONTENT_VIEW)
+ }
+
@Test
fun taskThumbnailView_uninitializedByDefault() {
screenshotRule.screenshotTest("taskThumbnailView_uninitialized") { activity ->
@@ -90,23 +99,25 @@ class TaskThumbnailViewScreenshotTest(emulationSpec: DeviceEmulationSpec) {
}
@Test
- fun taskThumbnailView_liveTile_withoutHeader() {
+ fun taskThumbnailView_liveTile() {
screenshotRule.screenshotTest("taskThumbnailView_liveTile") { activity ->
activity.actionBar?.hide()
- createTaskThumbnailView(activity).apply {
- setState(TaskThumbnailUiState.LiveTile.WithoutHeader)
- }
+ createTaskThumbnailView(activity).apply { setState(TaskThumbnailUiState.LiveTile) }
}
}
@Test
- fun taskThumbnailView_image_withoutHeader() {
+ fun taskThumbnailView_image() {
screenshotRule.screenshotTest("taskThumbnailView_image") { activity ->
activity.actionBar?.hide()
createTaskThumbnailView(activity).apply {
setState(
SnapshotSplash(
- Snapshot.WithoutHeader(createBitmap(), ROTATION_0, Color.DKGRAY),
+ Snapshot(
+ createBitmap(VIEW_ENV_WIDTH, VIEW_ENV_HEIGHT),
+ ROTATION_0,
+ Color.DKGRAY,
+ ),
null,
)
)
@@ -115,14 +126,14 @@ class TaskThumbnailViewScreenshotTest(emulationSpec: DeviceEmulationSpec) {
}
@Test
- fun taskThumbnailView_image_withoutHeader_withImageMatrix() {
+ fun taskThumbnailView_image_withImageMatrix() {
screenshotRule.screenshotTest("taskThumbnailView_image_withMatrix") { activity ->
activity.actionBar?.hide()
createTaskThumbnailView(activity).apply {
val lessThanHeightMatchingAspectRatio = (VIEW_ENV_HEIGHT / 2) - 200
setState(
SnapshotSplash(
- Snapshot.WithoutHeader(
+ Snapshot(
createBitmap(
width = VIEW_ENV_WIDTH / 2,
height = lessThanHeightMatchingAspectRatio,
@@ -139,13 +150,17 @@ class TaskThumbnailViewScreenshotTest(emulationSpec: DeviceEmulationSpec) {
}
@Test
- fun taskThumbnailView_splash_withoutHeader() {
+ fun taskThumbnailView_splash() {
screenshotRule.screenshotTest("taskThumbnailView_partial_splash") { activity ->
activity.actionBar?.hide()
createTaskThumbnailView(activity).apply {
setState(
SnapshotSplash(
- Snapshot.WithoutHeader(createBitmap(), ROTATION_0, Color.DKGRAY),
+ Snapshot(
+ createBitmap(VIEW_ENV_WIDTH, VIEW_ENV_HEIGHT),
+ ROTATION_0,
+ Color.DKGRAY,
+ ),
BitmapDrawable(activity.resources, createSplash()),
)
)
@@ -155,14 +170,14 @@ class TaskThumbnailViewScreenshotTest(emulationSpec: DeviceEmulationSpec) {
}
@Test
- fun taskThumbnailView_splash_withoutHeader_withImageMatrix() {
+ fun taskThumbnailView_splash_withImageMatrix() {
screenshotRule.screenshotTest("taskThumbnailView_partial_splash_withMatrix") { activity ->
activity.actionBar?.hide()
createTaskThumbnailView(activity).apply {
val lessThanHeightMatchingAspectRatio = (VIEW_ENV_HEIGHT / 2) - 200
setState(
SnapshotSplash(
- Snapshot.WithoutHeader(
+ Snapshot(
createBitmap(
width = VIEW_ENV_WIDTH / 2,
height = lessThanHeightMatchingAspectRatio,
@@ -233,27 +248,6 @@ class TaskThumbnailViewScreenshotTest(emulationSpec: DeviceEmulationSpec) {
return taskThumbnailView
}
- private fun createSplash() = createBitmap(width = 20, height = 20, rectColorRotation = 1)
-
- private fun createBitmap(
- width: Int = VIEW_ENV_WIDTH,
- height: Int = VIEW_ENV_HEIGHT,
- rectColorRotation: Int = 0,
- ) =
- Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888).apply {
- Canvas(this).apply {
- val paint = Paint()
- paint.color = BITMAP_RECT_COLORS[rectColorRotation % 4]
- drawRect(0f, 0f, width / 2f, height / 2f, paint)
- paint.color = BITMAP_RECT_COLORS[(1 + rectColorRotation) % 4]
- drawRect(width / 2f, 0f, width.toFloat(), height / 2f, paint)
- paint.color = BITMAP_RECT_COLORS[(2 + rectColorRotation) % 4]
- drawRect(0f, height / 2f, width / 2f, height.toFloat(), paint)
- paint.color = BITMAP_RECT_COLORS[(3 + rectColorRotation) % 4]
- drawRect(width / 2f, height / 2f, width.toFloat(), height.toFloat(), paint)
- }
- }
-
companion object {
@Parameters(name = "{0}")
@JvmStatic
@@ -265,7 +259,6 @@ class TaskThumbnailViewScreenshotTest(emulationSpec: DeviceEmulationSpec) {
)
const val CORNER_RADIUS = 56f
- val BITMAP_RECT_COLORS = listOf(Color.GREEN, Color.RED, Color.BLUE, Color.CYAN)
const val VIEW_ENV_WIDTH = 1440
const val VIEW_ENV_HEIGHT = 3120
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/model/data/TaskViewItemInfoTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/model/data/TaskViewItemInfoTest.kt
index 6170551420..61c0a1b54b 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/model/data/TaskViewItemInfoTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/model/data/TaskViewItemInfoTest.kt
@@ -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(),
if (enableRefactorTaskThumbnail()) mock()
else mock(),
mock(),
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/ui/mapper/TaskUiStateMapperTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/ui/mapper/TaskUiStateMapperTest.kt
index cc658c7afc..b7c86ce900 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/ui/mapper/TaskUiStateMapperTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/ui/mapper/TaskUiStateMapperTest.kt
@@ -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)
diff --git a/quickstep/tests/src/com/android/quickstep/AspectRatioSystemShortcutTests.kt b/quickstep/tests/src/com/android/quickstep/AspectRatioSystemShortcutTests.kt
index 1e7c116f94..fa245da495 100644
--- a/quickstep/tests/src/com/android/quickstep/AspectRatioSystemShortcutTests.kt
+++ b/quickstep/tests/src/com/android/quickstep/AspectRatioSystemShortcutTests.kt
@@ -33,6 +33,7 @@ import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import androidx.test.platform.app.InstrumentationRegistry
import com.android.launcher3.AbstractFloatingView
import com.android.launcher3.AbstractFloatingViewHelper
+import com.android.launcher3.Flags.enableRefactorTaskContentView
import com.android.launcher3.Flags.enableRefactorTaskThumbnail
import com.android.launcher3.InvariantDeviceProfile
import com.android.launcher3.R
@@ -50,6 +51,7 @@ import com.android.quickstep.TaskViewTestDIHelpers.initializeRecentsDependencies
import com.android.quickstep.TaskViewTestDIHelpers.mockRecentsModel
import com.android.quickstep.orientation.LandscapePagedViewHandler
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.util.RecentsOrientedState
import com.android.quickstep.util.SingleTask
@@ -267,6 +269,11 @@ class AspectRatioSystemShortcutTests {
TaskContainer(
taskView,
task,
+ when {
+ enableRefactorTaskContentView() -> mock()
+ enableRefactorTaskThumbnail() -> mock()
+ else -> mock()
+ },
if (enableRefactorTaskThumbnail()) mock()
else mock(),
mock(),
diff --git a/quickstep/tests/src/com/android/quickstep/DesktopSystemShortcutTest.kt b/quickstep/tests/src/com/android/quickstep/DesktopSystemShortcutTest.kt
index 8b0c3d9821..b9392531f0 100644
--- a/quickstep/tests/src/com/android/quickstep/DesktopSystemShortcutTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/DesktopSystemShortcutTest.kt
@@ -29,6 +29,7 @@ import com.android.dx.mockito.inline.extended.StaticMockitoSession
import com.android.internal.R
import com.android.launcher3.AbstractFloatingView
import com.android.launcher3.AbstractFloatingViewHelper
+import com.android.launcher3.Flags.enableRefactorTaskContentView
import com.android.launcher3.Flags.enableRefactorTaskThumbnail
import com.android.launcher3.logging.StatsLogManager
import com.android.launcher3.logging.StatsLogManager.LauncherEvent
@@ -36,6 +37,7 @@ import com.android.launcher3.model.data.TaskViewItemInfo
import com.android.launcher3.util.SplitConfigurationOptions
import com.android.launcher3.util.TransformingTouchDelegate
import com.android.quickstep.TaskOverlayFactory.TaskOverlay
+import com.android.quickstep.task.thumbnail.TaskContentView
import com.android.quickstep.task.thumbnail.TaskThumbnailView
import com.android.quickstep.views.LauncherRecentsView
import com.android.quickstep.views.RecentsViewContainer
@@ -274,6 +276,11 @@ class DesktopSystemShortcutTest {
TaskContainer(
taskView,
task,
+ when {
+ enableRefactorTaskContentView() -> mock()
+ enableRefactorTaskThumbnail() -> mock()
+ else -> mock()
+ },
if (enableRefactorTaskThumbnail()) mock()
else mock(),
mock(),
diff --git a/quickstep/tests/src/com/android/quickstep/ExternalDisplaySystemShortcutTest.kt b/quickstep/tests/src/com/android/quickstep/ExternalDisplaySystemShortcutTest.kt
index 48b2ae3d3c..9b384318df 100644
--- a/quickstep/tests/src/com/android/quickstep/ExternalDisplaySystemShortcutTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/ExternalDisplaySystemShortcutTest.kt
@@ -29,6 +29,7 @@ import com.android.dx.mockito.inline.extended.StaticMockitoSession
import com.android.internal.R
import com.android.launcher3.AbstractFloatingView
import com.android.launcher3.AbstractFloatingViewHelper
+import com.android.launcher3.Flags.enableRefactorTaskContentView
import com.android.launcher3.Flags.enableRefactorTaskThumbnail
import com.android.launcher3.logging.StatsLogManager
import com.android.launcher3.logging.StatsLogManager.LauncherEvent
@@ -36,6 +37,7 @@ import com.android.launcher3.model.data.TaskViewItemInfo
import com.android.launcher3.util.SplitConfigurationOptions
import com.android.launcher3.util.TransformingTouchDelegate
import com.android.quickstep.TaskOverlayFactory.TaskOverlay
+import com.android.quickstep.task.thumbnail.TaskContentView
import com.android.quickstep.task.thumbnail.TaskThumbnailView
import com.android.quickstep.views.LauncherRecentsView
import com.android.quickstep.views.RecentsViewContainer
@@ -270,6 +272,11 @@ class ExternalDisplaySystemShortcutTest {
TaskContainer(
taskView,
task,
+ when {
+ enableRefactorTaskContentView() -> mock()
+ enableRefactorTaskThumbnail() -> mock()
+ else -> mock()
+ },
if (enableRefactorTaskThumbnail()) mock()
else mock(),
mock(),
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
index fe92cbfb76..d27e9a03de 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
@@ -73,16 +73,15 @@ public final class OverviewTask {
return getCombinedSplitTaskHeight();
}
- UiObject2 taskSnapshot1 = findObjectInTask((isDesktop() ? DESKTOP : DEFAULT).snapshotRes);
- return taskSnapshot1.getVisibleBounds().height();
+ return getTaskSnapshot(isDesktop() ? DESKTOP : DEFAULT).getVisibleBounds().height();
}
/**
* Calculates the visible height for split tasks, containing 2 snapshot tiles and a divider.
*/
private int getCombinedSplitTaskHeight() {
- UiObject2 taskSnapshot1 = findObjectInTask(SPLIT_TOP_OR_LEFT.snapshotRes);
- UiObject2 taskSnapshot2 = findObjectInTask(SPLIT_BOTTOM_OR_RIGHT.snapshotRes);
+ UiObject2 taskSnapshot1 = getTaskSnapshot(SPLIT_TOP_OR_LEFT);
+ UiObject2 taskSnapshot2 = getTaskSnapshot(SPLIT_BOTTOM_OR_RIGHT);
// If the split task is partly off screen, taskSnapshot1 can be invisible.
if (taskSnapshot1 == null) {
@@ -97,34 +96,6 @@ public final class OverviewTask {
return bottom - top;
}
- /**
- * Returns the width of the visible task, or the combined width of two tasks in split with a
- * divider between.
- */
- int getVisibleWidth() {
- if (isGrouped()) {
- return getCombinedSplitTaskWidth();
- }
-
- UiObject2 taskSnapshot1 = findObjectInTask(DEFAULT.snapshotRes);
- return taskSnapshot1.getVisibleBounds().width();
- }
-
- /**
- * Calculates the visible width for split tasks, containing 2 snapshot tiles and a divider.
- */
- private int getCombinedSplitTaskWidth() {
- UiObject2 taskSnapshot1 = findObjectInTask(SPLIT_TOP_OR_LEFT.snapshotRes);
- UiObject2 taskSnapshot2 = findObjectInTask(SPLIT_BOTTOM_OR_RIGHT.snapshotRes);
-
- int left = Math.min(
- taskSnapshot1.getVisibleBounds().left, taskSnapshot2.getVisibleBounds().left);
- int right = Math.max(
- taskSnapshot1.getVisibleBounds().right, taskSnapshot2.getVisibleBounds().right);
-
- return right - left;
- }
-
public int getTaskCenterX() {
return mTask.getVisibleCenter().x;
}
@@ -141,6 +112,26 @@ public final class OverviewTask {
return mTask;
}
+ /**
+ * Returns the task snapshot (thumbnail) for the given `OverviewTaskContainer`.
+ * If there are no `taskContentView`'s, then the `enableRefactorTaskContentView` feature flag is
+ * off, in that case fallback to the `snapshotViewRes` id.
+ */
+ private UiObject2 getTaskSnapshot(OverviewTaskContainer overviewTaskContainer) {
+ UiObject2 taskContentView = mTask.findObject(
+ mLauncher.getOverviewObjectSelector(overviewTaskContainer.taskContentViewRes));
+ if (taskContentView != null) {
+ BySelector snapshotSelector = mLauncher.getOverviewObjectSelector("snapshot");
+ UiObject2 snapshot = mTask.findObject(snapshotSelector);
+ if (snapshot != null) {
+ return snapshot;
+ }
+ }
+
+ return mTask.findObject(
+ mLauncher.getOverviewObjectSelector(overviewTaskContainer.snapshotViewRes));
+ }
+
/**
* Dismisses the task by swiping up.
*/
@@ -303,17 +294,13 @@ public final class OverviewTask {
}
}
- private UiObject2 findObjectInTask(String resName) {
- return mTask.findObject(mLauncher.getOverviewObjectSelector(resName));
- }
-
/**
* Returns whether the given String is contained in this Task's contentDescription. Also returns
* true if both Strings are null.
*/
public boolean containsContentDescription(String expected,
OverviewTaskContainer overviewTaskContainer) {
- String actual = findObjectInTask(overviewTaskContainer.snapshotRes).getContentDescription();
+ String actual = getTaskSnapshot(overviewTaskContainer).getContentDescription();
if (actual == null && expected == null) {
return true;
}
@@ -359,19 +346,25 @@ public final class OverviewTask {
*/
public enum OverviewTaskContainer {
// The main task when the task is not split.
- DEFAULT("snapshot", "icon"),
+ DEFAULT("task_content_view", "snapshot", "icon"),
// The first task in split task.
- SPLIT_TOP_OR_LEFT("snapshot", "icon"),
+ SPLIT_TOP_OR_LEFT("task_content_view", "snapshot", "icon"),
// The second task in split task.
- SPLIT_BOTTOM_OR_RIGHT("bottomright_snapshot", "bottomRight_icon"),
+ SPLIT_BOTTOM_OR_RIGHT("bottomright_task_content_view", "bottomright_snapshot",
+ "bottomRight_icon"),
// The desktop task.
- DESKTOP("background", "icon");
+ DESKTOP("background", "background", "icon");
- public final String snapshotRes;
+ public final String taskContentViewRes;
+ // TODO (b/409248525) Delete `snapshotViewRes` when cleaning up
+ // enableRefactorTaskContentView flag.
+ public final String snapshotViewRes;
public final String iconAppRes;
- OverviewTaskContainer(String snapshotRes, String iconAppRes) {
- this.snapshotRes = snapshotRes;
+ OverviewTaskContainer(String taskContentViewRes, String snapshotViewRes,
+ String iconAppRes) {
+ this.taskContentViewRes = taskContentViewRes;
+ this.snapshotViewRes = snapshotViewRes;
this.iconAppRes = iconAppRes;
}
}