Destroy dep tree on RecentsView detached. This prevents leaks.

Scenario 1 was already working, this CL fixes scenario 2 and leaves scenario 3 broken.

3 Scenarios:
Scenario 1 (occurs in real usage):
Launcher instance 1 init
Launcher instance 2 init
Launcher instance 1 destroy
Launcher instance 2 destroy

Scenario 2 (occurs in testStressPressHome):
Launcher instance 1 init
Launcher instance 1 destroy
Launcher instance 2 init
Launcher instance 2 destroy

Scenario 3 (does not currently happen):
Launcher instance 1 init
Launcher instance 2 init
Launcher instance 2 destroy
Launcher instance 1 destroy

Fix: 381160061
Flag: com.android.launcher3.enable_refactor_task_thumbnail
Test: TaplStartLauncherViaGestureTests#testStressPressHome
Change-Id: I7c82ce97e7a7f0698b9adad38f59e4555f0d1bdc
diff --git a/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt b/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt
index b78e214..53bacd0 100644
--- a/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt
+++ b/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt
@@ -253,13 +253,17 @@
         private const val DEFAULT_SCOPE_ID = "RecentsDependencies::GlobalScope"
         private const val TAG = "RecentsDependencies"
         private const val DEBUG = false
+        private var activeRecentsCount = 0
 
         @Volatile private lateinit var instance: RecentsDependencies
 
         fun initialize(view: View): RecentsDependencies = initialize(view.context)
 
         fun initialize(context: Context): RecentsDependencies {
-            synchronized(this) { instance = RecentsDependencies(context.applicationContext) }
+            synchronized(this) {
+                activeRecentsCount++
+                instance = RecentsDependencies(context.applicationContext)
+            }
             return instance
         }
 
@@ -273,9 +277,29 @@
             return instance
         }
 
+        @JvmStatic
         fun destroy() {
-            instance.scopes.clear()
-            instance.startDefaultScope(instance.appContext)
+            // When Launcher Activity restarts, the old view's RecentsView.onDetachedFromWindow
+            // happens after the new view's creation. This means that destroy can be called after a
+            // new initialisation. This check prevents a newly initialised tree from being
+            // destroyed. Ideally we would have 1 instance of the dependency tree for each
+            // RecentsView.
+            //
+            // This check is sufficient to avoid a leak of the dependency tree after the Activity is
+            // destroyed while also allowing Launcher auto-restarts (production behaviour) to easily
+            // reinitialise the dependency tree.
+            //
+            // TODO(b/353917593): Better lifecycle decisions will be implemented in this bug or when
+            //  replacing with Dagger (b/371370483).
+            activeRecentsCount--
+            if (activeRecentsCount == 0) {
+                instance.scopes.clear()
+            } else {
+                instance.log(
+                    "RecentsDependencies was not destroyed. " +
+                        "There is still an active RecentsView instance."
+                )
+            }
         }
     }
 }
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 3b46367..b965d94 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -1236,6 +1236,7 @@
         reset();
         if (enableRefactorTaskThumbnail()) {
             mHelper.onDetachedFromWindow();
+            RecentsDependencies.destroy();
         }
     }