Fixed leak and no clock carousel (1/3)

Previously, ClockRegistry is bond to activity lifecycle and it's not stable with unregister call followed by register in a short period of time.

Remove the register/unregister ClockRegistry from ClockRegistryProvider (used in activity), and move to app level singleton. Register ClockRegistry listeners when app started and cleaned up when app ended.

Bug: 285348630
Bug: 285321790
Test: resume/destroy WPP then launch LS tab
Test: rotate foldable inner screen
Test: run profiler check no related leaks
Change-Id: I5b9d8eba17b3164a3da5f81058c5c6c90f6a2272
diff --git a/src/com/android/customization/module/CustomizationInjector.kt b/src/com/android/customization/module/CustomizationInjector.kt
index b3400bc..8b0d90f 100644
--- a/src/com/android/customization/module/CustomizationInjector.kt
+++ b/src/com/android/customization/module/CustomizationInjector.kt
@@ -18,7 +18,6 @@
 import android.content.Context
 import androidx.activity.ComponentActivity
 import androidx.fragment.app.FragmentActivity
-import androidx.lifecycle.LifecycleOwner
 import com.android.customization.model.theme.OverlayManagerCompat
 import com.android.customization.model.theme.ThemeBundleProvider
 import com.android.customization.model.theme.ThemeManager
@@ -48,13 +47,12 @@
         context: Context,
     ): KeyguardQuickAffordancePickerInteractor
 
-    fun getClockRegistry(context: Context, lifecycleOwner: LifecycleOwner): ClockRegistry
+    fun getClockRegistry(context: Context): ClockRegistry?
 
     fun getClockPickerInteractor(context: Context): ClockPickerInteractor
 
     fun getClockSectionViewModel(
         context: Context,
-        lifecycleOwner: LifecycleOwner
     ): ClockSectionViewModel
 
     fun getColorPickerInteractor(
@@ -77,6 +75,5 @@
         context: Context,
         wallpaperColorsViewModel: WallpaperColorsViewModel,
         clockViewFactory: ClockViewFactory,
-        lifecycleOwner: LifecycleOwner,
     ): ClockSettingsViewModel.Factory
 }
diff --git a/src/com/android/customization/module/ThemePickerInjector.kt b/src/com/android/customization/module/ThemePickerInjector.kt
index 6c747d7..4988c6b 100644
--- a/src/com/android/customization/module/ThemePickerInjector.kt
+++ b/src/com/android/customization/module/ThemePickerInjector.kt
@@ -196,23 +196,19 @@
 
     override fun getSnapshotRestorers(
         context: Context,
-        lifecycleOwner: LifecycleOwner
     ): Map<Int, SnapshotRestorer> {
-        return super<WallpaperPicker2Injector>.getSnapshotRestorers(context, lifecycleOwner)
-            .toMutableMap()
-            .apply {
-                this[KEY_QUICK_AFFORDANCE_SNAPSHOT_RESTORER] =
-                    getKeyguardQuickAffordanceSnapshotRestorer(context)
-                this[KEY_WALLPAPER_SNAPSHOT_RESTORER] = getWallpaperSnapshotRestorer(context)
-                this[KEY_NOTIFICATIONS_SNAPSHOT_RESTORER] =
-                    getNotificationsSnapshotRestorer(context)
-                this[KEY_DARK_MODE_SNAPSHOT_RESTORER] = getDarkModeSnapshotRestorer(context)
-                this[KEY_THEMED_ICON_SNAPSHOT_RESTORER] = getThemedIconSnapshotRestorer(context)
-                this[KEY_APP_GRID_SNAPSHOT_RESTORER] = getGridSnapshotRestorer(context)
-                this[KEY_COLOR_PICKER_SNAPSHOT_RESTORER] =
-                    getColorPickerSnapshotRestorer(context, getWallpaperColorsViewModel())
-                this[KEY_CLOCKS_SNAPSHOT_RESTORER] = getClockPickerSnapshotRestorer(context)
-            }
+        return super<WallpaperPicker2Injector>.getSnapshotRestorers(context).toMutableMap().apply {
+            this[KEY_QUICK_AFFORDANCE_SNAPSHOT_RESTORER] =
+                getKeyguardQuickAffordanceSnapshotRestorer(context)
+            this[KEY_WALLPAPER_SNAPSHOT_RESTORER] = getWallpaperSnapshotRestorer(context)
+            this[KEY_NOTIFICATIONS_SNAPSHOT_RESTORER] = getNotificationsSnapshotRestorer(context)
+            this[KEY_DARK_MODE_SNAPSHOT_RESTORER] = getDarkModeSnapshotRestorer(context)
+            this[KEY_THEMED_ICON_SNAPSHOT_RESTORER] = getThemedIconSnapshotRestorer(context)
+            this[KEY_APP_GRID_SNAPSHOT_RESTORER] = getGridSnapshotRestorer(context)
+            this[KEY_COLOR_PICKER_SNAPSHOT_RESTORER] =
+                getColorPickerSnapshotRestorer(context, getWallpaperColorsViewModel())
+            this[KEY_CLOCKS_SNAPSHOT_RESTORER] = getClockPickerSnapshotRestorer(context)
+        }
     }
 
     override fun getCustomizationPreferences(context: Context): CustomizationPreferences {
@@ -347,7 +343,7 @@
                 .also { notificationsSnapshotRestorer = it }
     }
 
-    override fun getClockRegistry(context: Context, lifecycleOwner: LifecycleOwner): ClockRegistry {
+    override fun getClockRegistry(context: Context): ClockRegistry {
         return (clockRegistryProvider
                 ?: ClockRegistryProvider(
                         context = context.applicationContext,
@@ -356,7 +352,7 @@
                         backgroundDispatcher = Dispatchers.IO,
                     )
                     .also { clockRegistryProvider = it })
-            .getForOwner(lifecycleOwner)
+            .get()
     }
 
     override fun getClockPickerInteractor(
@@ -367,9 +363,8 @@
             ?: ClockPickerInteractor(
                     repository =
                         ClockPickerRepositoryImpl(
-                            secureSettingsRepository = getSecureSettingsRepository(context),
-                            // TODO (b/285978251): remove second argument once b/285348630 is fixed
-                            registry = getClockRegistry(context, context as LifecycleOwner),
+                            secureSettingsRepository = getSecureSettingsRepository(appContext),
+                            registry = getClockRegistry(appContext),
                             scope = getApplicationCoroutineScope(),
                             mainDispatcher = Dispatchers.Main,
                         ),
@@ -380,10 +375,12 @@
 
     override fun getClockSectionViewModel(
         context: Context,
-        lifecycleOwner: LifecycleOwner
     ): ClockSectionViewModel {
         return clockSectionViewModel
-            ?: ClockSectionViewModel(context.applicationContext, getClockPickerInteractor(context))
+            ?: ClockSectionViewModel(
+                    context.applicationContext,
+                    getClockPickerInteractor(context.applicationContext)
+                )
                 .also { clockSectionViewModel = it }
     }
 
@@ -404,10 +401,7 @@
                     ScreenSizeCalculator.getInstance()
                         .getScreenSize(activity.windowManager.defaultDisplay),
                     WallpaperManager.getInstance(activity.applicationContext),
-                    getClockRegistry(
-                        context = activity.applicationContext,
-                        lifecycleOwner = activity,
-                    ),
+                    getClockRegistry(activity.applicationContext),
                 )
                 .also {
                     clockViewFactories[activityHashCode] = it
@@ -521,7 +515,6 @@
         context: Context,
         wallpaperColorsViewModel: WallpaperColorsViewModel,
         clockViewFactory: ClockViewFactory,
-        lifecycleOwner: LifecycleOwner,
     ): ClockSettingsViewModel.Factory {
         return clockSettingsViewModelFactory
             ?: ClockSettingsViewModel.Factory(
diff --git a/src/com/android/customization/picker/clock/data/repository/ClockRegistryProvider.kt b/src/com/android/customization/picker/clock/data/repository/ClockRegistryProvider.kt
index 52c3c4e..652ffdd 100644
--- a/src/com/android/customization/picker/clock/data/repository/ClockRegistryProvider.kt
+++ b/src/com/android/customization/picker/clock/data/repository/ClockRegistryProvider.kt
@@ -19,8 +19,6 @@
 import android.content.ComponentName
 import android.content.Context
 import android.view.LayoutInflater
-import androidx.lifecycle.DefaultLifecycleObserver
-import androidx.lifecycle.LifecycleOwner
 import com.android.systemui.plugins.Plugin
 import com.android.systemui.plugins.PluginManager
 import com.android.systemui.shared.clocks.ClockRegistry
@@ -45,50 +43,28 @@
     private val mainDispatcher: CoroutineDispatcher,
     private val backgroundDispatcher: CoroutineDispatcher,
 ) {
-    private val lifecycleOwners = mutableSetOf<Int>()
-    private val pluginManager: PluginManager by lazy { createPluginManager(context) }
     private val clockRegistry: ClockRegistry by lazy {
         ClockRegistry(
-                context,
-                pluginManager,
-                coroutineScope,
-                mainDispatcher,
-                backgroundDispatcher,
-                isEnabled = true,
-                handleAllUsers = false,
-                DefaultClockProvider(context, LayoutInflater.from(context), context.resources),
-                keepAllLoaded = true,
-                subTag = "Picker",
-            )
-            .apply { registerListeners() }
-    }
-
-    fun getForOwner(lifecycleOwner: LifecycleOwner): ClockRegistry {
-        registerLifecycleOwner(lifecycleOwner)
-        return clockRegistry
-    }
-
-    private fun registerLifecycleOwner(lifecycleOwner: LifecycleOwner) {
-        lifecycleOwners.add(lifecycleOwner.hashCode())
-
-        lifecycleOwner.lifecycle.addObserver(
-            object : DefaultLifecycleObserver {
-                override fun onDestroy(owner: LifecycleOwner) {
-                    super.onDestroy(owner)
-                    unregisterLifecycleOwner(owner)
-                }
-            }
+            context,
+            createPluginManager(context),
+            coroutineScope,
+            mainDispatcher,
+            backgroundDispatcher,
+            isEnabled = true,
+            handleAllUsers = false,
+            DefaultClockProvider(context, LayoutInflater.from(context), context.resources),
+            keepAllLoaded = true,
+            subTag = "Picker",
         )
     }
 
-    private fun unregisterLifecycleOwner(lifecycleOwner: LifecycleOwner) {
-        lifecycleOwners.remove(lifecycleOwner.hashCode())
-
-        if (lifecycleOwners.isEmpty()) {
-            clockRegistry.unregisterListeners()
-        }
+    init {
+        // Listeners in ClockRegistry get cleaned up when app ended
+        clockRegistry.registerListeners()
     }
 
+    fun get() = clockRegistry
+
     private fun createPluginManager(context: Context): PluginManager {
         val privilegedPlugins = listOf<String>()
         val isDebugDevice = true
diff --git a/src/com/android/customization/picker/clock/ui/fragment/ClockCustomDemoFragment.kt b/src/com/android/customization/picker/clock/ui/fragment/ClockCustomDemoFragment.kt
index 6203e24..f138d6a 100644
--- a/src/com/android/customization/picker/clock/ui/fragment/ClockCustomDemoFragment.kt
+++ b/src/com/android/customization/picker/clock/ui/fragment/ClockCustomDemoFragment.kt
@@ -37,7 +37,6 @@
         clockRegistry =
             (InjectorProvider.getInjector() as ThemePickerInjector).getClockRegistry(
                 requireContext(),
-                this
             )
         val listInUse = clockRegistry.getClocks().filter { "NOT_IN_USE" !in it.clockId }
 
diff --git a/src/com/android/customization/picker/clock/ui/fragment/ClockSettingsFragment.kt b/src/com/android/customization/picker/clock/ui/fragment/ClockSettingsFragment.kt
index d19b33c..e476d6a 100644
--- a/src/com/android/customization/picker/clock/ui/fragment/ClockSettingsFragment.kt
+++ b/src/com/android/customization/picker/clock/ui/fragment/ClockSettingsFragment.kt
@@ -123,7 +123,6 @@
                         context,
                         injector.getWallpaperColorsViewModel(),
                         injector.getClockViewFactory(activity),
-                        activity,
                     ),
                 )
                 .get(),