From 6f61228a6bc8bebc55c9fbbf44da8abc8fbb38a3 Mon Sep 17 00:00:00 2001 From: Uwais Ashraf Date: Thu, 24 Oct 2024 22:53:19 +0000 Subject: [PATCH] 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 --- .../recents/di/RecentsDependencies.kt | 11 +++++ .../task/thumbnail/TaskThumbnailView.kt | 4 +- .../quickstep/task/util/TaskOverlayHelper.kt | 18 ++++---- .../quickstep/views/DesktopTaskView.kt | 45 +++++++++++++------ .../android/quickstep/views/TaskContainer.kt | 7 +-- .../com/android/quickstep/views/TaskView.kt | 26 ++++++----- 6 files changed, 73 insertions(+), 38 deletions(-) diff --git a/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt b/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt index 44b8b8dbf0..f2b99765af 100644 --- a/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt +++ b/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt @@ -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") diff --git a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt index e7416eca3d..eb9c04709e 100644 --- a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt +++ b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt @@ -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") diff --git a/quickstep/src/com/android/quickstep/task/util/TaskOverlayHelper.kt b/quickstep/src/com/android/quickstep/task/util/TaskOverlayHelper.kt index 9253dbf945..92ac4adae8 100644 --- a/quickstep/src/com/android/quickstep/task/util/TaskOverlayHelper.kt +++ b/quickstep/src/com/android/quickstep/task/util/TaskOverlayHelper.kt @@ -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 diff --git a/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt b/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt index 8c854e7617..6b145bd30d 100644 --- a/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt +++ b/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt @@ -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( - context, - this, - R.layout.task_thumbnail_deprecated, - VIEW_POOL_MAX_SIZE, - VIEW_POOL_INITIAL_SIZE, - ) + if (!enableRefactorTaskThumbnail()) { + ViewPool( + context, + this, + R.layout.task_thumbnail_deprecated, + VIEW_POOL_MAX_SIZE, + VIEW_POOL_INITIAL_SIZE, + ) + } else null + + private val taskThumbnailViewPool = + if (enableRefactorTaskThumbnail()) { + ViewPool( + 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 diff --git a/quickstep/src/com/android/quickstep/views/TaskContainer.kt b/quickstep/src/com/android/quickstep/views/TaskContainer.kt index 6cb7741020..dc5ffeee12 100644 --- a/quickstep/src/com/android/quickstep/views/TaskContainer.kt +++ b/quickstep/src/com/android/quickstep/views/TaskContainer.kt @@ -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() { diff --git a/quickstep/src/com/android/quickstep/views/TaskView.kt b/quickstep/src/com/android/quickstep/views/TaskView.kt index cc64dbae98..28ecf9648b 100644 --- a/quickstep/src/com/android/quickstep/views/TaskView.kt +++ b/quickstep/src/com/android/quickstep/views/TaskView.kt @@ -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(digitalWellbeingBannerId)!! return TaskContainer( this, task, @@ -744,7 +748,7 @@ constructor( iconView, TransformingTouchDelegate(iconView.asView()), stagePosition, - digitalWellBeingToast, + findViewById(digitalWellbeingBannerId)!!, findViewById(showWindowViewId)!!, taskOverlayFactory, )