From 7e7afac7c0cf36824924c9bd80c0b3aa741f8b8d Mon Sep 17 00:00:00 2001 From: Uwais Ashraf Date: Wed, 10 Jul 2024 18:50:42 +0000 Subject: [PATCH] Change drawing in TTV to use Views rather than Canvas Fix: 352332386 Test: Presubmit, Manual Flag: com.android.launcher3.enable_refactor_task_thumbnail Change-Id: Ia1b7b6dc3d093d431ce022e793f40e61cec38ac6 --- quickstep/res/layout/task.xml | 2 +- quickstep/res/layout/task_grouped.xml | 4 +- quickstep/res/layout/task_thumbnail.xml | 27 +++++- .../res/layout/task_thumbnail_deprecated.xml | 20 +++++ .../quickstep/task/thumbnail/LiveTileView.kt | 46 ++++++++++ .../task/thumbnail/TaskThumbnailView.kt | 86 ++++++++----------- .../quickstep/views/DesktopTaskView.kt | 35 ++++---- .../com/android/quickstep/views/TaskView.kt | 6 +- 8 files changed, 151 insertions(+), 75 deletions(-) create mode 100644 quickstep/res/layout/task_thumbnail_deprecated.xml create mode 100644 quickstep/src/com/android/quickstep/task/thumbnail/LiveTileView.kt diff --git a/quickstep/res/layout/task.xml b/quickstep/res/layout/task.xml index cc3b30e9dc..46c1332651 100644 --- a/quickstep/res/layout/task.xml +++ b/quickstep/res/layout/task.xml @@ -28,7 +28,7 @@ launcher:focusBorderColor="?androidprv:attr/materialColorOutline" launcher:hoverBorderColor="?androidprv:attr/materialColorPrimary"> - + diff --git a/quickstep/res/layout/task_grouped.xml b/quickstep/res/layout/task_grouped.xml index 87a0f7073f..708aa3cbd6 100644 --- a/quickstep/res/layout/task_grouped.xml +++ b/quickstep/res/layout/task_grouped.xml @@ -33,9 +33,9 @@ launcher:focusBorderColor="?androidprv:attr/materialColorOutline" launcher:hoverBorderColor="?androidprv:attr/materialColorPrimary"> - + - - \ No newline at end of file + android:layout_height="match_parent"> + + + + + + + + \ No newline at end of file diff --git a/quickstep/res/layout/task_thumbnail_deprecated.xml b/quickstep/res/layout/task_thumbnail_deprecated.xml new file mode 100644 index 0000000000..f1a3d62021 --- /dev/null +++ b/quickstep/res/layout/task_thumbnail_deprecated.xml @@ -0,0 +1,20 @@ + + \ No newline at end of file diff --git a/quickstep/src/com/android/quickstep/task/thumbnail/LiveTileView.kt b/quickstep/src/com/android/quickstep/task/thumbnail/LiveTileView.kt new file mode 100644 index 0000000000..45b368709a --- /dev/null +++ b/quickstep/src/com/android/quickstep/task/thumbnail/LiveTileView.kt @@ -0,0 +1,46 @@ +/* + * 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.Canvas +import android.graphics.Paint +import android.graphics.PorterDuff +import android.graphics.PorterDuffXfermode +import android.util.AttributeSet +import android.view.View + +class LiveTileView : View { + constructor(context: Context) : super(context) + + constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) + + constructor( + context: Context, + attrs: AttributeSet?, + defStyleAttr: Int, + ) : super(context, attrs, defStyleAttr) + + override fun onDraw(canvas: Canvas) { + canvas.drawRect(0f, 0f, measuredWidth.toFloat(), measuredHeight.toFloat(), CLEAR_PAINT) + } + + companion object { + private val CLEAR_PAINT = + Paint().apply { xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR) } + } +} diff --git a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt index 22d49c12fe..c71b9e74dc 100644 --- a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt +++ b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt @@ -18,17 +18,17 @@ package com.android.quickstep.task.thumbnail import android.content.Context import android.content.res.Configuration -import android.graphics.Canvas import android.graphics.Color import android.graphics.Outline -import android.graphics.Paint -import android.graphics.PorterDuff -import android.graphics.PorterDuffXfermode import android.graphics.Rect import android.util.AttributeSet import android.view.View import android.view.ViewOutlineProvider +import android.widget.FrameLayout +import android.widget.ImageView import androidx.annotation.ColorInt +import androidx.core.view.isVisible +import com.android.launcher3.R import com.android.launcher3.Utilities import com.android.launcher3.util.ViewPool import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.BackgroundOnly @@ -43,7 +43,7 @@ import com.android.systemui.shared.system.QuickStepContract import kotlinx.coroutines.MainScope import kotlinx.coroutines.launch -class TaskThumbnailView : View, ViewPool.Reusable { +class TaskThumbnailView : FrameLayout, ViewPool.Reusable { // TODO(b/335649589): Ideally create and obtain this from DI. This ViewModel should be scoped // to [TaskView], and also shared between [TaskView] and [TaskThumbnailView] // This is using a lazy for now because the dependencies cannot be obtained without DI. @@ -59,12 +59,12 @@ class TaskThumbnailView : View, ViewPool.Reusable { ) } - private var uiState: TaskThumbnailUiState = Uninitialized - private var inheritedScale: Float = 1f - private var dimProgress: Float = 0f + private val scrimView: View by lazy { findViewById(R.id.task_thumbnail_scrim) } + private val liveTileView: LiveTileView by lazy { findViewById(R.id.task_thumbnail_live_tile) } + private val thumbnail: ImageView by lazy { findViewById(R.id.task_thumbnail) } + + private var inheritedScale: Float = 1f - private val backgroundPaint = Paint(Paint.ANTI_ALIAS_FLAG) - private val scrimPaint = Paint().apply { color = Color.BLACK } private val _measuredBounds = Rect() private val measuredBounds: Rect get() { @@ -75,12 +75,12 @@ class TaskThumbnailView : View, ViewPool.Reusable { private var overviewCornerRadius: Float = TaskCornerRadius.get(context) private var fullscreenCornerRadius: Float = QuickStepContract.getWindowCornerRadius(context) - constructor(context: Context?) : super(context) + constructor(context: Context) : super(context) - constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) + constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) constructor( - context: Context?, + context: Context, attrs: AttributeSet?, defStyleAttr: Int, ) : super(context, attrs, defStyleAttr) @@ -90,15 +90,19 @@ class TaskThumbnailView : View, ViewPool.Reusable { // TODO(b/335396935) replace MainScope with shorter lifecycle. MainScope().launch { viewModel.uiState.collect { viewModelUiState -> - uiState = viewModelUiState - invalidate() + resetViews() + when (viewModelUiState) { + is Uninitialized -> {} + is LiveTile -> drawLiveWindow() + is Snapshot -> drawSnapshot(viewModelUiState) + is BackgroundOnly -> drawBackground(viewModelUiState.backgroundColor) + } } } MainScope().launch { viewModel.dimProgress.collect { dimProgress -> // TODO(b/348195366) Add fade in/out for scrim - this@TaskThumbnailView.dimProgress = dimProgress - invalidate() + scrimView.alpha = dimProgress * MAX_SCRIM_ALPHA } } MainScope().launch { viewModel.cornerRadiusProgress.collect { invalidateOutline() } } @@ -120,25 +124,6 @@ class TaskThumbnailView : View, ViewPool.Reusable { override fun onRecycle() { // Do nothing - uiState = Uninitialized - } - - override fun onDraw(canvas: Canvas) { - when (val uiStateVal = uiState) { - is Uninitialized -> drawBackgroundOnly(canvas, Color.BLACK) - is LiveTile -> drawTransparentUiState(canvas) - is Snapshot -> drawSnapshotState(canvas, uiStateVal) - is BackgroundOnly -> drawBackgroundOnly(canvas, uiStateVal.backgroundColor) - } - - if (dimProgress > 0) { - drawScrim(canvas) - } - } - - private fun drawBackgroundOnly(canvas: Canvas, @ColorInt backgroundColor: Int) { - backgroundPaint.color = backgroundColor - canvas.drawRect(measuredBounds, backgroundPaint) } override fun onConfigurationChanged(newConfig: Configuration?) { @@ -149,18 +134,25 @@ class TaskThumbnailView : View, ViewPool.Reusable { invalidateOutline() } - private fun drawTransparentUiState(canvas: Canvas) { - canvas.drawRect(measuredBounds, CLEAR_PAINT) + private fun resetViews() { + liveTileView.isVisible = false + thumbnail.isVisible = false + scrimView.alpha = 0f + setBackgroundColor(Color.BLACK) } - private fun drawSnapshotState(canvas: Canvas, snapshot: Snapshot) { - drawBackgroundOnly(canvas, snapshot.backgroundColor) - canvas.drawBitmap(snapshot.bitmap, snapshot.drawnRect, measuredBounds, null) + private fun drawBackground(@ColorInt background: Int) { + setBackgroundColor(background) } - private fun drawScrim(canvas: Canvas) { - scrimPaint.alpha = (dimProgress * MAX_SCRIM_ALPHA).toInt() - canvas.drawRect(measuredBounds, scrimPaint) + private fun drawLiveWindow() { + liveTileView.isVisible = true + } + + private fun drawSnapshot(snapshot: Snapshot) { + drawBackground(snapshot.backgroundColor) + thumbnail.setImageBitmap(snapshot.bitmap) + thumbnail.isVisible = true } private fun getCurrentCornerRadius() = @@ -170,9 +162,7 @@ class TaskThumbnailView : View, ViewPool.Reusable { fullscreenCornerRadius ) / inheritedScale - companion object { - private val CLEAR_PAINT = - Paint().apply { xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR) } - private const val MAX_SCRIM_ALPHA = (0.4f * 255).toInt() + private companion object { + const val MAX_SCRIM_ALPHA = 0.4f } } diff --git a/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt b/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt index 4333c8b905..9ce2277b14 100644 --- a/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt +++ b/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt @@ -24,6 +24,7 @@ import android.graphics.drawable.ShapeDrawable import android.graphics.drawable.shapes.RoundRectShape import android.util.AttributeSet import android.util.Log +import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.core.view.updateLayoutParams @@ -36,7 +37,6 @@ import com.android.launcher3.util.ViewPool import com.android.launcher3.util.rects.set import com.android.quickstep.BaseContainerInterface import com.android.quickstep.TaskOverlayFactory -import com.android.quickstep.task.thumbnail.TaskThumbnailView import com.android.quickstep.util.RecentsOrientedState import com.android.systemui.shared.recents.model.Task @@ -54,7 +54,7 @@ class DesktopTaskView @JvmOverloads constructor(context: Context, attrs: Attribu ViewPool( context, this, - R.layout.task_thumbnail, + R.layout.task_thumbnail_deprecated, VIEW_POOL_MAX_SIZE, VIEW_POOL_INITIAL_SIZE ) @@ -108,22 +108,21 @@ class DesktopTaskView @JvmOverloads constructor(context: Context, attrs: Attribu tasks.map { task -> val snapshotView = if (enableRefactorTaskThumbnail()) { - TaskThumbnailView(context) - } else { - taskThumbnailViewDeprecatedPool.view - } - .also { snapshotView -> - addView( - snapshotView, - // Add snapshotView to the front after initial views e.g. icon and - // background. - childCountAtInflation, - LayoutParams( - ViewGroup.LayoutParams.WRAP_CONTENT, - ViewGroup.LayoutParams.WRAP_CONTENT - ) - ) - } + LayoutInflater.from(context).inflate(R.layout.task_thumbnail, this, false) + } else { + taskThumbnailViewDeprecatedPool.view + } + + addView( + snapshotView, + // Add snapshotView to the front after initial views e.g. icon and + // background. + childCountAtInflation, + LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ) + ) TaskContainer( this, task, diff --git a/quickstep/src/com/android/quickstep/views/TaskView.kt b/quickstep/src/com/android/quickstep/views/TaskView.kt index 5c95aaa4c0..3209fab479 100644 --- a/quickstep/src/com/android/quickstep/views/TaskView.kt +++ b/quickstep/src/com/android/quickstep/views/TaskView.kt @@ -31,6 +31,7 @@ import android.util.AttributeSet import android.util.FloatProperty import android.util.Log import android.view.Display +import android.view.LayoutInflater import android.view.MotionEvent import android.view.View import android.view.View.OnClickListener @@ -665,9 +666,8 @@ constructor( if (enableRefactorTaskThumbnail()) { thumbnailViewDeprecated.visibility = GONE val indexOfSnapshotView = indexOfChild(thumbnailViewDeprecated) - TaskThumbnailView(context).apply { - layoutParams = thumbnailViewDeprecated.layoutParams - addView(this, indexOfSnapshotView) + LayoutInflater.from(context).inflate(R.layout.task_thumbnail, this, false).also { + addView(it, indexOfSnapshotView, thumbnailViewDeprecated.layoutParams) } } else { thumbnailViewDeprecated