Merge changes I65e6ad57,I0335e4d0 into main
* changes:
Remove listener in destroy() that caused a memory leak.
Reuse TTV, recreating deps to get faster load times, lower mem usage
diff --git a/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt b/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt
index 44b8b8d..f2b9976 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 @@
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 @@
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 e7416ec..eb9c047 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.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 @@
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 @@
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 9253dbf..c82ed9a 100644
--- a/quickstep/src/com/android/quickstep/task/util/TaskOverlayHelper.kt
+++ b/quickstep/src/com/android/quickstep/task/util/TaskOverlayHelper.kt
@@ -17,6 +17,7 @@
package com.android.quickstep.task.util
import android.util.Log
+import android.view.View.OnLayoutChangeListener
import com.android.quickstep.TaskOverlayFactory
import com.android.quickstep.recents.di.RecentsDependencies
import com.android.quickstep.recents.di.get
@@ -41,31 +42,35 @@
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
get() = uiState as Enabled
+ private val snapshotLayoutChangeListener = OnLayoutChangeListener { _, _, _, _, _, _, _, _, _ ->
+ (uiState as? Enabled)?.let { initOverlay(it) }
+ }
+
fun getThumbnailMatrix() = getThumbnailPositionState().matrix
private fun getThumbnailPositionState() =
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
@@ -76,9 +81,7 @@
}
}
.launchIn(overlayInitializedScope)
- overlay.snapshotView.addOnLayoutChangeListener { _, _, _, _, _, _, _, _, _ ->
- (uiState as? Enabled)?.let { initOverlay(it) }
- }
+ overlay.snapshotView.addOnLayoutChangeListener(snapshotLayoutChangeListener)
}
private fun initOverlay(enabledState: Enabled) {
@@ -96,6 +99,7 @@
fun destroy() {
overlayInitializedScope.cancel()
uiState = Disabled
+ overlay.snapshotView.removeOnLayoutChangeListener(snapshotLayoutChangeListener)
reset()
}
diff --git a/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt b/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt
index 8c854e7..6b145bd 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.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.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 @@
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 @@
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 @@
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 @@
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 6cb7741..dc5ffee 100644
--- a/quickstep/src/com/android/quickstep/views/TaskContainer.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskContainer.kt
@@ -157,12 +157,13 @@
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 cc64dba..28ecf96 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.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 @@
@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 @@
iconView,
TransformingTouchDelegate(iconView.asView()),
stagePosition,
- digitalWellBeingToast,
+ findViewById(digitalWellbeingBannerId)!!,
findViewById(showWindowViewId)!!,
taskOverlayFactory,
)