Reuse TTV, recreating deps to get faster load times, lower mem usage

Use a ViewPool for DesktopTaskView for same reason.

Fix: 369590189
Flag: com.android.launcher3.enable_refactor_task_thumbnail
Test: Manually looking at slow-mo videos of home to overview
Change-Id: I0335e4d0eea4841145815244bbbec19541f31c73
This commit is contained in:
Uwais Ashraf
2024-10-24 22:53:19 +00:00
parent 18b3171980
commit 6f61228a6b
6 changed files with 73 additions and 38 deletions
@@ -113,6 +113,10 @@ class RecentsDependencies private constructor(private val appContext: Context) {
instance =
factory?.invoke(extras) as T ?: createDependency(modelClass, scopeId, extras)
scope[modelClass.simpleName] = instance!!
log(
"instance of $modelClass" +
" (${instance.hashCode()}) added to scope ${scope.scopeId}"
)
}
}
return instance!!
@@ -148,6 +152,13 @@ class RecentsDependencies private constructor(private val appContext: Context) {
fun getScope(scopeId: RecentsScopeId): RecentsDependenciesScope =
scopes[scopeId] ?: createScope(scopeId)
fun removeScope(scope: Any) {
val scopeId: RecentsScopeId = scope as? RecentsScopeId ?: scope.hashCode().toString()
scopes[scopeId]?.close()
scopes.remove(scopeId)
log("Scope $scopeId removed")
}
// TODO(b/353912757): Create a factory so we can prevent this method of growing indefinitely.
// Each class should be responsible for providing a factory function to create a new instance.
@Suppress("UNCHECKED_CAST")
@@ -32,6 +32,7 @@ import com.android.launcher3.R
import com.android.launcher3.Utilities
import com.android.launcher3.util.ViewPool
import com.android.quickstep.recents.di.RecentsDependencies
import com.android.quickstep.recents.di.get
import com.android.quickstep.recents.di.inject
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.BackgroundOnly
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.LiveTile
@@ -54,7 +55,7 @@ import kotlinx.coroutines.flow.onEach
class TaskThumbnailView : ConstraintLayout, ViewPool.Reusable {
private val viewData: TaskThumbnailViewData by RecentsDependencies.inject(this)
private val viewModel: TaskThumbnailViewModel by RecentsDependencies.inject(this)
private lateinit var viewModel: TaskThumbnailViewModel
private lateinit var viewAttachedScope: CoroutineScope
@@ -91,6 +92,7 @@ class TaskThumbnailView : ConstraintLayout, ViewPool.Reusable {
super.onAttachedToWindow()
viewAttachedScope =
CoroutineScope(SupervisorJob() + Dispatchers.Main + CoroutineName("TaskThumbnailView"))
viewModel = RecentsDependencies.get(this)
viewModel.uiState
.onEach { viewModelUiState ->
Log.d(TAG, "viewModelUiState changed from $uiState to: $viewModelUiState")
@@ -41,14 +41,7 @@ class TaskOverlayHelper(val task: Task, val overlay: TaskOverlayFactory.TaskOver
private lateinit var overlayInitializedScope: CoroutineScope
private var uiState: TaskOverlayUiState = Disabled
private val viewModel: TaskOverlayViewModel by lazy {
TaskOverlayViewModel(
task = task,
recentsViewData = RecentsDependencies.get(),
getThumbnailPositionUseCase = RecentsDependencies.get(),
recentTasksRepository = RecentsDependencies.get()
)
}
private lateinit var viewModel: TaskOverlayViewModel
// TODO(b/331753115): TaskOverlay should listen for state changes and react.
val enabledState: Enabled
@@ -60,12 +53,19 @@ class TaskOverlayHelper(val task: Task, val overlay: TaskOverlayFactory.TaskOver
viewModel.getThumbnailPositionState(
overlay.snapshotView.width,
overlay.snapshotView.height,
overlay.snapshotView.isLayoutRtl
overlay.snapshotView.isLayoutRtl,
)
fun init() {
overlayInitializedScope =
CoroutineScope(SupervisorJob() + Dispatchers.Main + CoroutineName("TaskOverlayHelper"))
viewModel =
TaskOverlayViewModel(
task = task,
recentsViewData = RecentsDependencies.get(),
getThumbnailPositionUseCase = RecentsDependencies.get(),
recentTasksRepository = RecentsDependencies.get(),
)
viewModel.overlayState
.onEach {
uiState = it
@@ -25,7 +25,6 @@ import android.graphics.drawable.shapes.RoundRectShape
import android.util.AttributeSet
import android.util.Log
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import androidx.core.content.res.ResourcesCompat
import androidx.core.view.updateLayoutParams
@@ -40,6 +39,7 @@ 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
@@ -53,14 +53,29 @@ class DesktopTaskView @JvmOverloads constructor(context: Context, attrs: Attribu
override fun computeTaskCornerRadius(context: Context) =
computeWindowCornerRadius(context)
}
private val taskThumbnailViewDeprecatedPool =
ViewPool<TaskThumbnailViewDeprecated>(
context,
this,
R.layout.task_thumbnail_deprecated,
VIEW_POOL_MAX_SIZE,
VIEW_POOL_INITIAL_SIZE,
)
if (!enableRefactorTaskThumbnail()) {
ViewPool<TaskThumbnailViewDeprecated>(
context,
this,
R.layout.task_thumbnail_deprecated,
VIEW_POOL_MAX_SIZE,
VIEW_POOL_INITIAL_SIZE,
)
} else null
private val taskThumbnailViewPool =
if (enableRefactorTaskThumbnail()) {
ViewPool<TaskThumbnailView>(
context,
this,
R.layout.task_thumbnail,
VIEW_POOL_MAX_SIZE,
VIEW_POOL_INITIAL_SIZE,
)
} else null
private val tempPointF = PointF()
private val tempRect = Rect()
private lateinit var backgroundView: View
@@ -117,9 +132,9 @@ class DesktopTaskView @JvmOverloads constructor(context: Context, attrs: Attribu
tasks.map { task ->
val snapshotView =
if (enableRefactorTaskThumbnail()) {
LayoutInflater.from(context).inflate(R.layout.task_thumbnail, this, false)
taskThumbnailViewPool!!.view
} else {
taskThumbnailViewDeprecatedPool.view
taskThumbnailViewDeprecatedPool!!.view
}
addView(
@@ -148,9 +163,11 @@ class DesktopTaskView @JvmOverloads constructor(context: Context, attrs: Attribu
super.onRecycle()
visibility = VISIBLE
taskContainers.forEach {
if (!enableRefactorTaskThumbnail()) {
removeView(it.thumbnailViewDeprecated)
taskThumbnailViewDeprecatedPool.recycle(it.thumbnailViewDeprecated)
removeView(it.snapshotView)
if (enableRefactorTaskThumbnail()) {
taskThumbnailViewPool!!.recycle(it.thumbnailView)
} else {
taskThumbnailViewDeprecatedPool!!.recycle(it.thumbnailViewDeprecated)
}
}
}
@@ -299,7 +316,7 @@ class DesktopTaskView @JvmOverloads constructor(context: Context, attrs: Attribu
companion object {
private const val TAG = "DesktopTaskView"
private const val DEBUG = false
private const val VIEW_POOL_MAX_SIZE = 10
private const val VIEW_POOL_MAX_SIZE = 5
// As DesktopTaskView is inflated in background, use initialSize=0 to avoid initPool.
private const val VIEW_POOL_INITIAL_SIZE = 0
@@ -157,12 +157,13 @@ class TaskContainer(
fun destroy() {
digitalWellBeingToast?.destroy()
if (enableRefactorTaskThumbnail()) {
taskView.removeView(thumbnailView)
}
snapshotView.scaleX = 1f
snapshotView.scaleY = 1f
overlay.destroy()
if (enableRefactorTaskThumbnail()) {
RecentsDependencies.getInstance().removeScope(snapshotView)
RecentsDependencies.getInstance().removeScope(this)
}
}
fun bindThumbnailView() {
@@ -82,6 +82,7 @@ import com.android.quickstep.TaskViewUtils
import com.android.quickstep.orientation.RecentsPagedOrientationHandler
import com.android.quickstep.recents.di.RecentsDependencies
import com.android.quickstep.recents.di.get
import com.android.quickstep.task.thumbnail.TaskThumbnailView
import com.android.quickstep.task.viewmodel.TaskViewModel
import com.android.quickstep.util.ActiveGestureErrorDetector
import com.android.quickstep.util.ActiveGestureLog
@@ -723,20 +724,23 @@ constructor(
@StagePosition stagePosition: Int,
taskOverlayFactory: TaskOverlayFactory,
): TaskContainer {
val thumbnailViewDeprecated: TaskThumbnailViewDeprecated = findViewById(thumbnailViewId)!!
val existingThumbnailView: View = findViewById(thumbnailViewId)!!
val snapshotView =
if (enableRefactorTaskThumbnail()) {
thumbnailViewDeprecated.visibility = GONE
val indexOfSnapshotView = indexOfChild(thumbnailViewDeprecated)
LayoutInflater.from(context).inflate(R.layout.task_thumbnail, this, false).also {
it.id = thumbnailViewId
addView(it, indexOfSnapshotView, thumbnailViewDeprecated.layoutParams)
when {
!enableRefactorTaskThumbnail() -> existingThumbnailView
existingThumbnailView is TaskThumbnailView -> existingThumbnailView
else -> {
val indexOfSnapshotView = indexOfChild(existingThumbnailView)
LayoutInflater.from(context)
.inflate(R.layout.task_thumbnail, this, false)
.also {
it.id = thumbnailViewId
addView(it, indexOfSnapshotView, existingThumbnailView.layoutParams)
removeView(existingThumbnailView)
}
}
} else {
thumbnailViewDeprecated
}
val iconView = getOrInflateIconView(iconViewId)
val digitalWellBeingToast = findViewById<DigitalWellBeingToast>(digitalWellbeingBannerId)!!
return TaskContainer(
this,
task,
@@ -744,7 +748,7 @@ constructor(
iconView,
TransformingTouchDelegate(iconView.asView()),
stagePosition,
digitalWellBeingToast,
findViewById(digitalWellbeingBannerId)!!,
findViewById(showWindowViewId)!!,
taskOverlayFactory,
)