mirror of
https://github.com/LawnchairLauncher/lawnchair.git
synced 2026-03-04 18:06:48 +00:00
Merge "Change drawing in TTV to use Views rather than Canvas" into main
This commit is contained in:
committed by
Android (Google) Code Review
commit
2d73c5ef1b
@@ -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) }
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user