Merge "Change drawing in TTV to use Views rather than Canvas" into main

This commit is contained in:
Treehugger Robot
2024-07-11 15:03:18 +00:00
committed by Android (Google) Code Review
8 changed files with 151 additions and 75 deletions

View File

@@ -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) }
}
}

View File

@@ -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
}
}

View File

@@ -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<TaskThumbnailViewDeprecated>(
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,

View File

@@ -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
@@ -666,9 +667,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