Merge "Fix bug with home panel dream" into main
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/HomeControlsComponentInteractorKosmos.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/HomeControlsComponentInteractorKosmos.kt
index efccf7a..e4ce6cb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/HomeControlsComponentInteractorKosmos.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/HomeControlsComponentInteractorKosmos.kt
@@ -17,7 +17,7 @@
 
 import com.android.systemui.controls.dagger.ControlsComponent
 import com.android.systemui.controls.management.ControlsListingController
-import com.android.systemui.controls.panels.AuthorizedPanelsRepository
+import com.android.systemui.controls.panels.authorizedPanelsRepository
 import com.android.systemui.controls.panels.selectedComponentRepository
 import com.android.systemui.dreams.homecontrols.domain.interactor.HomeControlsComponentInteractor
 import com.android.systemui.kosmos.Kosmos
@@ -38,4 +38,3 @@
 
 val Kosmos.controlsComponent by Kosmos.Fixture<ControlsComponent> { mock() }
 val Kosmos.controlsListingController by Kosmos.Fixture<ControlsListingController> { mock() }
-val Kosmos.authorizedPanelsRepository by Kosmos.Fixture<AuthorizedPanelsRepository> { mock() }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/HomeControlsComponentInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/HomeControlsComponentInteractorTest.kt
index ce74a90..6ad32cc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/HomeControlsComponentInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/HomeControlsComponentInteractorTest.kt
@@ -27,13 +27,14 @@
 import com.android.systemui.controls.dagger.ControlsComponent
 import com.android.systemui.controls.management.ControlsListingController
 import com.android.systemui.controls.panels.AuthorizedPanelsRepository
-import com.android.systemui.controls.panels.FakeSelectedComponentRepository
 import com.android.systemui.controls.panels.SelectedComponentRepository
+import com.android.systemui.controls.panels.authorizedPanelsRepository
 import com.android.systemui.controls.panels.selectedComponentRepository
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.dreams.homecontrols.domain.interactor.HomeControlsComponentInteractor
 import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.settings.fakeUserTracker
 import com.android.systemui.testKosmos
 import com.android.systemui.user.data.repository.FakeUserRepository
 import com.android.systemui.user.data.repository.fakeUserRepository
@@ -41,6 +42,9 @@
 import com.android.systemui.util.mockito.withArgCaptor
 import com.google.common.truth.Truth.assertThat
 import java.util.Optional
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
@@ -48,6 +52,7 @@
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
+@OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class HomeControlsComponentInteractorTest : SysuiTestCase() {
@@ -59,20 +64,20 @@
     private lateinit var authorizedPanelsRepository: AuthorizedPanelsRepository
     private lateinit var underTest: HomeControlsComponentInteractor
     private lateinit var userRepository: FakeUserRepository
-    private lateinit var selectedComponentRepository: FakeSelectedComponentRepository
+    private lateinit var selectedComponentRepository: SelectedComponentRepository
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
-        userRepository = kosmos.fakeUserRepository
-        userRepository.setUserInfos(listOf(PRIMARY_USER, ANOTHER_USER))
 
         controlsComponent = kosmos.controlsComponent
         authorizedPanelsRepository = kosmos.authorizedPanelsRepository
         controlsListingController = kosmos.controlsListingController
         selectedComponentRepository = kosmos.selectedComponentRepository
 
-        selectedComponentRepository.setCurrentUserHandle(PRIMARY_USER.userHandle)
+        userRepository = kosmos.fakeUserRepository
+        userRepository.setUserInfos(listOf(PRIMARY_USER, ANOTHER_USER))
+
         whenever(controlsComponent.getControlsListingController())
             .thenReturn(Optional.of(controlsListingController))
 
@@ -90,14 +95,13 @@
     fun testPanelComponentReturnsComponentNameForSelectedItemByUser() =
         with(kosmos) {
             testScope.runTest {
-                whenever(authorizedPanelsRepository.getAuthorizedPanels())
-                    .thenReturn(setOf(TEST_PACKAGE_PANEL))
-                userRepository.setSelectedUserInfo(PRIMARY_USER)
+                setActiveUser(PRIMARY_USER)
+                authorizedPanelsRepository.addAuthorizedPanels(setOf(TEST_PACKAGE))
                 selectedComponentRepository.setSelectedComponent(TEST_SELECTED_COMPONENT_PANEL)
                 val actualValue by collectLastValue(underTest.panelComponent)
                 assertThat(actualValue).isNull()
                 runServicesUpdate()
-                assertThat(actualValue).isEqualTo(TEST_COMPONENT_PANEL)
+                assertThat(actualValue).isEqualTo(TEST_COMPONENT)
             }
         }
 
@@ -105,16 +109,15 @@
     fun testPanelComponentReturnsComponentNameAsInitialValueWithoutServiceUpdate() =
         with(kosmos) {
             testScope.runTest {
-                whenever(authorizedPanelsRepository.getAuthorizedPanels())
-                    .thenReturn(setOf(TEST_PACKAGE_PANEL))
-                userRepository.setSelectedUserInfo(PRIMARY_USER)
+                setActiveUser(PRIMARY_USER)
+                authorizedPanelsRepository.addAuthorizedPanels(setOf(TEST_PACKAGE))
                 selectedComponentRepository.setSelectedComponent(TEST_SELECTED_COMPONENT_PANEL)
                 whenever(controlsListingController.getCurrentServices())
                     .thenReturn(
-                        listOf(ControlsServiceInfo(TEST_COMPONENT_PANEL, "panel", hasPanel = true))
+                        listOf(ControlsServiceInfo(TEST_COMPONENT, "panel", hasPanel = true))
                     )
                 val actualValue by collectLastValue(underTest.panelComponent)
-                assertThat(actualValue).isEqualTo(TEST_COMPONENT_PANEL)
+                assertThat(actualValue).isEqualTo(TEST_COMPONENT)
             }
         }
 
@@ -122,9 +125,8 @@
     fun testPanelComponentReturnsNullForHomeControlsThatDoesNotSupportPanel() =
         with(kosmos) {
             testScope.runTest {
-                whenever(authorizedPanelsRepository.getAuthorizedPanels())
-                    .thenReturn(setOf(TEST_PACKAGE_PANEL))
-                userRepository.setSelectedUserInfo(PRIMARY_USER)
+                setActiveUser(PRIMARY_USER)
+                authorizedPanelsRepository.addAuthorizedPanels(setOf(TEST_PACKAGE))
                 selectedComponentRepository.setSelectedComponent(TEST_SELECTED_COMPONENT_NON_PANEL)
                 val actualValue by collectLastValue(underTest.panelComponent)
                 assertThat(actualValue).isNull()
@@ -137,8 +139,8 @@
     fun testPanelComponentReturnsNullWhenPanelIsUnauthorized() =
         with(kosmos) {
             testScope.runTest {
-                whenever(authorizedPanelsRepository.getAuthorizedPanels()).thenReturn(setOf())
-                userRepository.setSelectedUserInfo(PRIMARY_USER)
+                setActiveUser(PRIMARY_USER)
+                authorizedPanelsRepository.removeAuthorizedPanels(setOf(TEST_PACKAGE))
                 selectedComponentRepository.setSelectedComponent(TEST_SELECTED_COMPONENT_PANEL)
                 val actualValue by collectLastValue(underTest.panelComponent)
                 assertThat(actualValue).isNull()
@@ -151,17 +153,24 @@
     fun testPanelComponentReturnsComponentNameForDifferentUsers() =
         with(kosmos) {
             testScope.runTest {
-                whenever(authorizedPanelsRepository.getAuthorizedPanels())
-                    .thenReturn(setOf(TEST_PACKAGE_PANEL))
-                userRepository.setSelectedUserInfo(ANOTHER_USER)
+                val actualValue by collectLastValue(underTest.panelComponent)
+
+                // Secondary user has non-panel selected.
+                setActiveUser(ANOTHER_USER)
                 selectedComponentRepository.setSelectedComponent(TEST_SELECTED_COMPONENT_NON_PANEL)
-                selectedComponentRepository.setCurrentUserHandle(ANOTHER_USER.userHandle)
+
+                // Primary user has panel selected.
+                setActiveUser(PRIMARY_USER)
+                authorizedPanelsRepository.addAuthorizedPanels(setOf(TEST_PACKAGE))
                 selectedComponentRepository.setSelectedComponent(TEST_SELECTED_COMPONENT_PANEL)
 
-                val actualValue by collectLastValue(underTest.panelComponent)
-                assertThat(actualValue).isNull()
                 runServicesUpdate()
-                assertThat(actualValue).isEqualTo(TEST_COMPONENT_PANEL)
+                assertThat(actualValue).isEqualTo(TEST_COMPONENT)
+
+                // Back to secondary user, should be null.
+                setActiveUser(ANOTHER_USER)
+                runServicesUpdate()
+                assertThat(actualValue).isNull()
             }
         }
 
@@ -169,8 +178,7 @@
     fun testPanelComponentReturnsNullWhenControlsComponentReturnsNullForListingController() =
         with(kosmos) {
             testScope.runTest {
-                whenever(authorizedPanelsRepository.getAuthorizedPanels())
-                    .thenReturn(setOf(TEST_PACKAGE_PANEL))
+                authorizedPanelsRepository.addAuthorizedPanels(setOf(TEST_PACKAGE))
                 whenever(controlsComponent.getControlsListingController())
                     .thenReturn(Optional.empty())
                 userRepository.setSelectedUserInfo(PRIMARY_USER)
@@ -182,11 +190,17 @@
 
     private fun runServicesUpdate(hasPanelBoolean: Boolean = true) {
         val listings =
-            listOf(ControlsServiceInfo(TEST_COMPONENT_PANEL, "panel", hasPanel = hasPanelBoolean))
+            listOf(ControlsServiceInfo(TEST_COMPONENT, "panel", hasPanel = hasPanelBoolean))
         val callback = withArgCaptor { verify(controlsListingController).addCallback(capture()) }
         callback.onServicesUpdated(listings)
     }
 
+    private suspend fun TestScope.setActiveUser(user: UserInfo) {
+        userRepository.setSelectedUserInfo(user)
+        kosmos.fakeUserTracker.set(listOf(user), 0)
+        runCurrent()
+    }
+
     private fun ControlsServiceInfo(
         componentName: ComponentName,
         label: CharSequence,
@@ -237,19 +251,9 @@
             )
         private const val TEST_PACKAGE = "pkg"
         private val TEST_COMPONENT = ComponentName(TEST_PACKAGE, "service")
-        private const val TEST_PACKAGE_PANEL = "pkg.panel"
-        private val TEST_COMPONENT_PANEL = ComponentName(TEST_PACKAGE_PANEL, "service")
         private val TEST_SELECTED_COMPONENT_PANEL =
-            SelectedComponentRepository.SelectedComponent(
-                TEST_PACKAGE_PANEL,
-                TEST_COMPONENT_PANEL,
-                true
-            )
+            SelectedComponentRepository.SelectedComponent(TEST_PACKAGE, TEST_COMPONENT, true)
         private val TEST_SELECTED_COMPONENT_NON_PANEL =
-            SelectedComponentRepository.SelectedComponent(
-                TEST_PACKAGE_PANEL,
-                TEST_COMPONENT_PANEL,
-                false
-            )
+            SelectedComponentRepository.SelectedComponent(TEST_PACKAGE, TEST_COMPONENT, false)
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamStartableTest.kt
index 6610e70..87b1bbb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamStartableTest.kt
@@ -32,6 +32,7 @@
 import com.android.systemui.controls.management.ControlsListingController
 import com.android.systemui.controls.panels.AuthorizedPanelsRepository
 import com.android.systemui.controls.panels.SelectedComponentRepository
+import com.android.systemui.controls.panels.authorizedPanelsRepository
 import com.android.systemui.controls.panels.selectedComponentRepository
 import com.android.systemui.dreams.homecontrols.domain.interactor.HomeControlsComponentInteractor
 import com.android.systemui.kosmos.applicationCoroutineScope
@@ -84,8 +85,7 @@
 
         userRepository.setUserInfos(listOf(PRIMARY_USER))
 
-        whenever(authorizedPanelsRepository.getAuthorizedPanels())
-            .thenReturn(setOf(TEST_PACKAGE_PANEL))
+        authorizedPanelsRepository.addAuthorizedPanels(setOf(TEST_PACKAGE_PANEL))
 
         whenever(controlsComponent.getControlsListingController())
             .thenReturn(Optional.of(controlsListingController))
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalPrefsRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalPrefsRepository.kt
index 85aeb4d..0e9b32f 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalPrefsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalPrefsRepository.kt
@@ -28,8 +28,8 @@
 import com.android.systemui.log.table.TableLogBuffer
 import com.android.systemui.log.table.logDiffsForTable
 import com.android.systemui.settings.UserFileManager
-import com.android.systemui.settings.UserFileManagerExt.observeSharedPreferences
 import com.android.systemui.user.data.repository.UserRepository
+import com.android.systemui.util.kotlin.SharedPreferencesExt.observe
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
@@ -97,8 +97,8 @@
         }
 
     private fun observeCtaDismissState(user: UserInfo): Flow<Boolean> =
-        userFileManager
-            .observeSharedPreferences(FILE_NAME, Context.MODE_PRIVATE, user.id)
+        getSharedPrefsForUser(user)
+            .observe(CTA_DISMISSED_STATE)
             // Emit at the start of collection to ensure we get an initial value
             .onStart { emit(Unit) }
             .map { getCtaDismissedState() }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/panels/AuthorizedPanelsRepository.kt b/packages/SystemUI/src/com/android/systemui/controls/panels/AuthorizedPanelsRepository.kt
index ae9c37a..b35bec4 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/panels/AuthorizedPanelsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/panels/AuthorizedPanelsRepository.kt
@@ -17,11 +17,16 @@
 
 package com.android.systemui.controls.panels
 
+import android.os.UserHandle
+import kotlinx.coroutines.flow.Flow
+
 /**
  * Repository for keeping track of which packages the panel has authorized to show control panels
  * (embedded activity).
  */
 interface AuthorizedPanelsRepository {
+    /** Exposes the authorized panels as a [Flow] for subscribing to updates */
+    fun observeAuthorizedPanels(user: UserHandle): Flow<Set<String>>
 
     /** A set of package names that the user has previously authorized to show panels. */
     fun getAuthorizedPanels(): Set<String>
diff --git a/packages/SystemUI/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImpl.kt
index 4e935df..7c2dae3 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImpl.kt
@@ -19,11 +19,16 @@
 
 import android.content.Context
 import android.content.SharedPreferences
+import android.os.UserHandle
 import com.android.systemui.res.R
 import com.android.systemui.settings.UserFileManager
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.statusbar.policy.DeviceControlsControllerImpl
+import com.android.systemui.util.kotlin.SharedPreferencesExt.observe
 import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
 
 class AuthorizedPanelsRepositoryImpl
 @Inject
@@ -33,19 +38,24 @@
     private val userTracker: UserTracker,
 ) : AuthorizedPanelsRepository {
 
+    override fun observeAuthorizedPanels(user: UserHandle): Flow<Set<String>> {
+        val prefs = instantiateSharedPrefs(user)
+        return prefs.observe(KEY).onStart { emit(Unit) }.map { getAuthorizedPanelsInternal(prefs) }
+    }
+
     override fun getAuthorizedPanels(): Set<String> {
-        return getAuthorizedPanelsInternal(instantiateSharedPrefs())
+        return getAuthorizedPanelsInternal(instantiateSharedPrefs(userTracker.userHandle))
     }
 
     override fun getPreferredPackages(): Set<String> =
         context.resources.getStringArray(R.array.config_controlsPreferredPackages).toSet()
 
     override fun addAuthorizedPanels(packageNames: Set<String>) {
-        addAuthorizedPanelsInternal(instantiateSharedPrefs(), packageNames)
+        addAuthorizedPanelsInternal(instantiateSharedPrefs(userTracker.userHandle), packageNames)
     }
 
     override fun removeAuthorizedPanels(packageNames: Set<String>) {
-        with(instantiateSharedPrefs()) {
+        with(instantiateSharedPrefs(userTracker.userHandle)) {
             val currentSet = getAuthorizedPanelsInternal(this)
             edit().putStringSet(KEY, currentSet - packageNames).apply()
         }
@@ -63,12 +73,12 @@
         sharedPreferences.edit().putStringSet(KEY, currentSet + packageNames).apply()
     }
 
-    private fun instantiateSharedPrefs(): SharedPreferences {
+    private fun instantiateSharedPrefs(user: UserHandle): SharedPreferences {
         val sharedPref =
             userFileManager.getSharedPreferences(
                 DeviceControlsControllerImpl.PREFS_CONTROLS_FILE,
                 Context.MODE_PRIVATE,
-                userTracker.userId,
+                user.identifier,
             )
 
         // We should add default packages when we've never run this
diff --git a/packages/SystemUI/src/com/android/systemui/controls/panels/SelectedComponentRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/panels/SelectedComponentRepositoryImpl.kt
index 0baa81a..9be04940 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/panels/SelectedComponentRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/panels/SelectedComponentRepositoryImpl.kt
@@ -20,21 +20,18 @@
 import android.content.Context
 import android.content.SharedPreferences
 import android.os.UserHandle
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.settings.UserFileManager
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.statusbar.policy.DeviceControlsControllerImpl
+import com.android.systemui.util.kotlin.SharedPreferencesExt.observe
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.flowOn
-import kotlinx.coroutines.launch
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
 
 @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
 @SysUISingleton
@@ -43,9 +40,7 @@
 constructor(
     private val userFileManager: UserFileManager,
     private val userTracker: UserTracker,
-    private val featureFlags: FeatureFlags,
-    @Background private val bgDispatcher: CoroutineDispatcher,
-    @Application private val applicationScope: CoroutineScope
+    @Background private val bgDispatcher: CoroutineDispatcher
 ) : SelectedComponentRepository {
 
     private companion object {
@@ -66,22 +61,11 @@
     override fun selectedComponentFlow(
         userHandle: UserHandle
     ): Flow<SelectedComponentRepository.SelectedComponent?> {
-        return conflatedCallbackFlow {
-                val sharedPreferencesByUserId = getSharedPreferencesForUser(userHandle.identifier)
-                val listener =
-                    SharedPreferences.OnSharedPreferenceChangeListener { _, key ->
-                        applicationScope.launch(bgDispatcher) {
-                            if (key == PREF_COMPONENT) {
-                                trySend(getSelectedComponent(userHandle))
-                            }
-                        }
-                    }
-                sharedPreferencesByUserId.registerOnSharedPreferenceChangeListener(listener)
-                send(getSelectedComponent(userHandle))
-                awaitClose {
-                    sharedPreferencesByUserId.unregisterOnSharedPreferenceChangeListener(listener)
-                }
-            }
+        val prefs = getSharedPreferencesForUser(userHandle.identifier)
+        return prefs
+            .observe(PREF_COMPONENT)
+            .onStart { emit(Unit) }
+            .map { getSelectedComponent(userHandle) }
             .flowOn(bgDispatcher)
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/domain/interactor/HomeControlsComponentInteractor.kt b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/domain/interactor/HomeControlsComponentInteractor.kt
index 91e0547..0cab10db 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/domain/interactor/HomeControlsComponentInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/domain/interactor/HomeControlsComponentInteractor.kt
@@ -47,24 +47,30 @@
 @Inject
 constructor(
     private val selectedComponentRepository: SelectedComponentRepository,
-    private val controlsComponent: ControlsComponent,
-    private val authorizedPanelsRepository: AuthorizedPanelsRepository,
+    controlsComponent: ControlsComponent,
+    authorizedPanelsRepository: AuthorizedPanelsRepository,
     userRepository: UserRepository,
     @Background private val bgScope: CoroutineScope
 ) {
-    private val controlsListingController =
+    private val controlsListingController: ControlsListingController? =
         controlsComponent.getControlsListingController().getOrNull()
 
     /** Gets the current user's selected panel, or null if there isn't one */
-    private val selectedItem: Flow<SelectedComponentRepository.SelectedComponent?> =
+    private val selectedPanel: Flow<SelectedComponentRepository.SelectedComponent?> =
         userRepository.selectedUserInfo
             .flatMapLatest { user ->
                 selectedComponentRepository.selectedComponentFlow(user.userHandle)
             }
             .map { if (it?.isPanel == true) it else null }
 
-    /** Gets all the available panels which are authorized by the user */
-    private fun allPanelItem(): Flow<List<PanelComponent>> {
+    /** Gets the current user's authorized panels */
+    private val allAuthorizedPanels: Flow<Set<String>> =
+        userRepository.selectedUserInfo.flatMapLatest { user ->
+            authorizedPanelsRepository.observeAuthorizedPanels(user.userHandle)
+        }
+
+    /** Gets all the available services from [ControlsListingController] */
+    private fun allAvailableServices(): Flow<List<ControlsServiceInfo>> {
         if (controlsListingController == null) {
             return emptyFlow()
         }
@@ -79,26 +85,38 @@
                 awaitClose { controlsListingController.removeCallback(listener) }
             }
             .onStart { emit(controlsListingController.getCurrentServices()) }
-            .map { serviceInfos ->
-                val authorizedPanels = authorizedPanelsRepository.getAuthorizedPanels()
-                serviceInfos.mapNotNull {
-                    val panelActivity = it.panelActivity
-                    if (it.componentName.packageName in authorizedPanels && panelActivity != null) {
-                        PanelComponent(it.componentName, panelActivity)
-                    } else {
-                        null
-                    }
+    }
+
+    /** Gets all panels which are available and authorized by the user */
+    private val allAvailableAndAuthorizedPanels: Flow<List<PanelComponent>> =
+        combine(
+            allAvailableServices(),
+            allAuthorizedPanels,
+        ) { serviceInfos, authorizedPanels ->
+            serviceInfos.mapNotNull {
+                val panelActivity = it.panelActivity
+                if (it.componentName.packageName in authorizedPanels && panelActivity != null) {
+                    PanelComponent(it.componentName, panelActivity)
+                } else {
+                    null
                 }
             }
-    }
+        }
+
     val panelComponent: StateFlow<ComponentName?> =
-        combine(allPanelItem(), selectedItem) { items, selected ->
+        combine(
+                allAvailableAndAuthorizedPanels,
+                selectedPanel,
+            ) { panels, selected ->
                 val item =
-                    items.firstOrNull { it.componentName == selected?.componentName }
-                        ?: items.firstOrNull()
+                    panels.firstOrNull { it.componentName == selected?.componentName }
+                        ?: panels.firstOrNull()
                 item?.panelActivity
             }
             .stateIn(bgScope, SharingStarted.WhileSubscribed(), null)
 
-    data class PanelComponent(val componentName: ComponentName, val panelActivity: ComponentName)
+    private data class PanelComponent(
+        val componentName: ComponentName,
+        val panelActivity: ComponentName,
+    )
 }
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserFileManagerExt.kt b/packages/SystemUI/src/com/android/systemui/settings/UserFileManagerExt.kt
deleted file mode 100644
index b09bfe2..0000000
--- a/packages/SystemUI/src/com/android/systemui/settings/UserFileManagerExt.kt
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.android.systemui.settings
-
-import android.annotation.UserIdInt
-import android.content.Context
-import android.content.SharedPreferences
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
-import kotlinx.coroutines.channels.awaitClose
-import kotlinx.coroutines.flow.Flow
-
-/** Extension functions for [UserFileManager]. */
-object UserFileManagerExt {
-
-    /** Returns a flow of [Unit] that is invoked each time the shared preference is updated. */
-    fun UserFileManager.observeSharedPreferences(
-        fileName: String,
-        @Context.PreferencesMode mode: Int,
-        @UserIdInt userId: Int
-    ): Flow<Unit> = conflatedCallbackFlow {
-        val sharedPrefs = getSharedPreferences(fileName, mode, userId)
-
-        val listener = SharedPreferences.OnSharedPreferenceChangeListener { _, _ -> trySend(Unit) }
-
-        sharedPrefs.registerOnSharedPreferenceChangeListener(listener)
-        awaitClose { sharedPrefs.unregisterOnSharedPreferenceChangeListener(listener) }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/SharedPreferencesExt.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/SharedPreferencesExt.kt
new file mode 100644
index 0000000..ab6a37b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/SharedPreferencesExt.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.kotlin
+
+import android.content.SharedPreferences
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.mapNotNull
+
+object SharedPreferencesExt {
+    /**
+     * Returns a flow of [Unit] that is invoked each time shared preference is updated.
+     *
+     * @param key Optional key to limit updates to a particular key.
+     */
+    fun SharedPreferences.observe(key: String? = null): Flow<Unit> =
+        conflatedCallbackFlow {
+                val listener =
+                    SharedPreferences.OnSharedPreferenceChangeListener { _, key -> trySend(key) }
+                registerOnSharedPreferenceChangeListener(listener)
+                awaitClose { unregisterOnSharedPreferenceChangeListener(listener) }
+            }
+            .mapNotNull { changedKey -> if ((key ?: changedKey) == changedKey) Unit else null }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
index c98d537..de455f63 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
@@ -34,11 +34,12 @@
 import com.android.systemui.controls.ControlsServiceInfo
 import com.android.systemui.controls.management.ControlsListingController
 import com.android.systemui.controls.panels.AuthorizedPanelsRepository
-import com.android.systemui.controls.panels.FakeSelectedComponentRepository
+import com.android.systemui.controls.panels.selectedComponentRepository
 import com.android.systemui.controls.ui.ControlsUiController
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.settings.UserFileManager
 import com.android.systemui.settings.UserTracker
+import com.android.systemui.testKosmos
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.time.FakeSystemClock
@@ -69,12 +70,13 @@
 import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
 import java.io.File
-import java.util.*
+import java.util.Optional
 import java.util.function.Consumer
 
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
 class ControlsControllerImplTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
 
     @Mock
     private lateinit var uiController: ControlsUiController
@@ -109,8 +111,6 @@
     private lateinit var listingCallbackCaptor:
             ArgumentCaptor<ControlsListingController.ControlsListingCallback>
 
-    private val preferredPanelRepository = FakeSelectedComponentRepository()
-
     private lateinit var delayableExecutor: FakeExecutor
     private lateinit var controller: ControlsControllerImpl
     private lateinit var canceller: DidRunRunnable
@@ -171,7 +171,7 @@
                 wrapper,
                 delayableExecutor,
                 uiController,
-                preferredPanelRepository,
+                kosmos.selectedComponentRepository,
                 bindingController,
                 listingController,
                 userFileManager,
@@ -225,7 +225,7 @@
                 mContext,
                 delayableExecutor,
                 uiController,
-                preferredPanelRepository,
+                kosmos.selectedComponentRepository,
                 bindingController,
                 listingController,
                 userFileManager,
@@ -245,7 +245,7 @@
                 mContext,
                 delayableExecutor,
                 uiController,
-                preferredPanelRepository,
+                kosmos.selectedComponentRepository,
                 bindingController,
                 listingController,
                 userFileManager,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImplTest.kt
index 4828ba3..18ce4a8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImplTest.kt
@@ -18,36 +18,40 @@
 package com.android.systemui.controls.panels
 
 import android.content.SharedPreferences
+import android.content.pm.UserInfo
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.res.R
+import com.android.systemui.settings.FakeUserTracker
 import com.android.systemui.settings.UserFileManager
-import com.android.systemui.settings.UserTracker
+import com.android.systemui.settings.fakeUserTracker
+import com.android.systemui.testKosmos
 import com.android.systemui.util.FakeSharedPreferences
-import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
 import java.io.File
+import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.MockitoAnnotations
 
 @RunWith(AndroidTestingRunner::class)
 @SmallTest
 class AuthorizedPanelsRepositoryImplTest : SysuiTestCase() {
+    val kosmos = testKosmos()
+    val testScope = kosmos.testScope
 
-    @Mock private lateinit var userTracker: UserTracker
+    private lateinit var userTracker: FakeUserTracker
 
     @Before
     fun setUp() {
-        MockitoAnnotations.initMocks(this)
         mContext.orCreateTestableResources.addOverride(
             R.array.config_controlsPreferredPackages,
             arrayOf<String>()
         )
-        whenever(userTracker.userId).thenReturn(0)
+        userTracker = kosmos.fakeUserTracker.apply { set(listOf(PRIMARY_USER, SECONDARY_USER), 0) }
     }
 
     @Test
@@ -91,7 +95,7 @@
         val repository = createRepository(fileManager)
 
         assertThat(repository.getAuthorizedPanels()).containsExactly(TEST_PACKAGE)
-        whenever(userTracker.userId).thenReturn(1)
+        userTracker.set(listOf(SECONDARY_USER), 0)
         assertThat(repository.getAuthorizedPanels()).isEmpty()
     }
 
@@ -127,6 +131,51 @@
         assertThat(sharedPrefs.getStringSet(KEY, null)).isEmpty()
     }
 
+    @Test
+    fun observeAuthorizedPanels() =
+        testScope.runTest {
+            val sharedPrefs = FakeSharedPreferences()
+            val fileManager = FakeUserFileManager(mapOf(0 to sharedPrefs))
+            val repository = createRepository(fileManager)
+
+            val authorizedPanels by
+                collectLastValue(repository.observeAuthorizedPanels(PRIMARY_USER.userHandle))
+            assertThat(authorizedPanels).isEmpty()
+
+            repository.addAuthorizedPanels(setOf(TEST_PACKAGE))
+            assertThat(authorizedPanels).containsExactly(TEST_PACKAGE)
+
+            repository.removeAuthorizedPanels(setOf(TEST_PACKAGE))
+            assertThat(authorizedPanels).isEmpty()
+        }
+
+    @Test
+    fun observeAuthorizedPanelsForAnotherUser() =
+        testScope.runTest {
+            val fileManager =
+                FakeUserFileManager(
+                    mapOf(
+                        0 to FakeSharedPreferences(),
+                        1 to FakeSharedPreferences(),
+                    )
+                )
+            val repository = createRepository(fileManager)
+
+            val authorizedPanels by
+                collectLastValue(repository.observeAuthorizedPanels(SECONDARY_USER.userHandle))
+            assertThat(authorizedPanels).isEmpty()
+
+            // Primary user is active, add authorized panels.
+            repository.addAuthorizedPanels(setOf(TEST_PACKAGE))
+            assertThat(authorizedPanels).isEmpty()
+
+            // Make secondary user active and add authorized panels again.
+            userTracker.set(listOf(PRIMARY_USER, SECONDARY_USER), 1)
+            assertThat(authorizedPanels).isEmpty()
+            repository.addAuthorizedPanels(setOf(TEST_PACKAGE))
+            assertThat(authorizedPanels).containsExactly(TEST_PACKAGE)
+        }
+
     private fun createRepository(userFileManager: UserFileManager): AuthorizedPanelsRepositoryImpl {
         return AuthorizedPanelsRepositoryImpl(mContext, userFileManager, userTracker)
     }
@@ -153,5 +202,9 @@
         private const val FILE_NAME = "controls_prefs"
         private const val KEY = "authorized_panels"
         private const val TEST_PACKAGE = "package"
+        private val PRIMARY_USER =
+            UserInfo(/* id= */ 0, /* name= */ "primary user", /* flags= */ UserInfo.FLAG_MAIN)
+        private val SECONDARY_USER =
+            UserInfo(/* id= */ 1, /* name= */ "secondary user", /* flags= */ 0)
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/panels/SelectedComponentRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/panels/SelectedComponentRepositoryTest.kt
index b463adf..a7e7ba9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/panels/SelectedComponentRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/panels/SelectedComponentRepositoryTest.kt
@@ -23,8 +23,6 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.settings.UserFileManager
@@ -74,7 +72,6 @@
     @Mock private lateinit var userTracker: UserTracker
     private lateinit var userFileManager: UserFileManager
 
-    private val featureFlags = FakeFeatureFlags()
     // under test
     private lateinit var repository: SelectedComponentRepository
 
@@ -95,11 +92,9 @@
                 )
             repository =
                 SelectedComponentRepositoryImpl(
-                    userFileManager,
-                    userTracker,
-                    featureFlags,
+                    userFileManager = userFileManager,
+                    userTracker = userTracker,
                     bgDispatcher = testDispatcher,
-                    applicationScope = applicationCoroutineScope
                 )
         }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/start/ControlsStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/start/ControlsStartableTest.kt
index bcef67e..94ea799 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/start/ControlsStartableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/start/ControlsStartableTest.kt
@@ -38,8 +38,8 @@
 import com.android.systemui.controls.dagger.ControlsComponent
 import com.android.systemui.controls.management.ControlsListingController
 import com.android.systemui.controls.panels.AuthorizedPanelsRepository
-import com.android.systemui.controls.panels.FakeSelectedComponentRepository
 import com.android.systemui.controls.panels.SelectedComponentRepository
+import com.android.systemui.controls.panels.selectedComponentRepository
 import com.android.systemui.controls.ui.SelectedItem
 import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.kosmos.testDispatcher
@@ -87,7 +87,7 @@
     @Mock private lateinit var userManager: UserManager
     @Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
 
-    private lateinit var preferredPanelsRepository: FakeSelectedComponentRepository
+    private lateinit var preferredPanelsRepository: SelectedComponentRepository
 
     private lateinit var fakeExecutor: FakeExecutor
 
@@ -99,7 +99,7 @@
         whenever(userTracker.userHandle).thenReturn(UserHandle.of(1))
 
         fakeExecutor = FakeExecutor(FakeSystemClock())
-        preferredPanelsRepository = FakeSelectedComponentRepository()
+        preferredPanelsRepository = kosmos.selectedComponentRepository
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt
index 36ae0c7..8f3813d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt
@@ -43,8 +43,8 @@
 import com.android.systemui.controls.management.ControlsListingController
 import com.android.systemui.controls.management.ControlsProviderSelectorActivity
 import com.android.systemui.controls.panels.AuthorizedPanelsRepository
-import com.android.systemui.controls.panels.FakeSelectedComponentRepository
 import com.android.systemui.controls.panels.SelectedComponentRepository
+import com.android.systemui.controls.panels.selectedComponentRepository
 import com.android.systemui.controls.settings.FakeControlsSettingsRepository
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.flags.FeatureFlags
@@ -53,6 +53,7 @@
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.statusbar.phone.SystemUIDialog
 import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.testKosmos
 import com.android.systemui.util.FakeSystemUIDialogController
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.any
@@ -85,6 +86,8 @@
 @RunWith(AndroidTestingRunner::class)
 @TestableLooper.RunWithLooper
 class ControlsUiControllerImplTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+
     @Mock lateinit var controlsController: ControlsController
     @Mock lateinit var controlsListingController: ControlsListingController
     @Mock lateinit var controlActionCoordinator: ControlActionCoordinator
@@ -100,7 +103,7 @@
     @Mock lateinit var packageManager: PackageManager
     @Mock lateinit var systemUIDialogFactory: SystemUIDialog.Factory
 
-    private val preferredPanelRepository = FakeSelectedComponentRepository()
+    private val preferredPanelRepository = kosmos.selectedComponentRepository
     private lateinit var fakeDialogController: FakeSystemUIDialogController
     private val uiExecutor = FakeExecutor(FakeSystemClock())
     private val bgExecutor = FakeExecutor(FakeSystemClock())
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryKosmos.kt
new file mode 100644
index 0000000..109e113
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryKosmos.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.controls.panels
+
+import android.content.applicationContext
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.settings.fakeUserFileManager
+import com.android.systemui.settings.fakeUserTracker
+
+var Kosmos.authorizedPanelsRepository: AuthorizedPanelsRepository by
+    Kosmos.Fixture {
+        AuthorizedPanelsRepositoryImpl(applicationContext, fakeUserFileManager, fakeUserTracker)
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/controls/panels/FakeSelectedComponentRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/controls/panels/FakeSelectedComponentRepository.kt
deleted file mode 100644
index a231212..0000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/controls/panels/FakeSelectedComponentRepository.kt
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.controls.panels
-
-import android.os.UserHandle
-import com.android.systemui.kosmos.Kosmos
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableStateFlow
-
-class FakeSelectedComponentRepository : SelectedComponentRepository {
-    private var shouldAddDefaultPanel: Boolean = true
-    private val _selectedComponentFlows =
-        mutableMapOf<UserHandle, MutableStateFlow<SelectedComponentRepository.SelectedComponent?>>()
-    private var currentUserHandle: UserHandle = UserHandle.of(0)
-
-    override fun selectedComponentFlow(
-        userHandle: UserHandle
-    ): Flow<SelectedComponentRepository.SelectedComponent?> {
-        // Return an existing flow for the user or create a new one
-        return _selectedComponentFlows.getOrPut(getUserHandle(userHandle)) {
-            MutableStateFlow(null)
-        }
-    }
-
-    override fun getSelectedComponent(
-        userHandle: UserHandle
-    ): SelectedComponentRepository.SelectedComponent? {
-        return _selectedComponentFlows[getUserHandle(userHandle)]?.value
-    }
-
-    override fun setSelectedComponent(
-        selectedComponent: SelectedComponentRepository.SelectedComponent
-    ) {
-        val flow = _selectedComponentFlows.getOrPut(currentUserHandle) { MutableStateFlow(null) }
-        flow.value = selectedComponent
-    }
-
-    override fun removeSelectedComponent() {
-        _selectedComponentFlows[currentUserHandle]?.value = null
-    }
-    override fun shouldAddDefaultComponent(): Boolean = shouldAddDefaultPanel
-
-    override fun setShouldAddDefaultComponent(shouldAdd: Boolean) {
-        shouldAddDefaultPanel = shouldAdd
-    }
-
-    fun setCurrentUserHandle(userHandle: UserHandle) {
-        currentUserHandle = userHandle
-    }
-    private fun getUserHandle(userHandle: UserHandle): UserHandle {
-        return if (userHandle == UserHandle.CURRENT) {
-            currentUserHandle
-        } else {
-            userHandle
-        }
-    }
-}
-
-val Kosmos.selectedComponentRepository by
-    Kosmos.Fixture<FakeSelectedComponentRepository> { FakeSelectedComponentRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/controls/panels/SelectedComponentRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/controls/panels/SelectedComponentRepositoryKosmos.kt
new file mode 100644
index 0000000..ee5b7e5
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/controls/panels/SelectedComponentRepositoryKosmos.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.controls.panels
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.settings.fakeUserFileManager
+import com.android.systemui.settings.fakeUserTracker
+
+var Kosmos.selectedComponentRepository: SelectedComponentRepository by
+    Kosmos.Fixture {
+        SelectedComponentRepositoryImpl(fakeUserFileManager, fakeUserTracker, testDispatcher)
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserFileManager.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserFileManager.kt
new file mode 100644
index 0000000..207c3f7
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserFileManager.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.settings
+
+import android.content.SharedPreferences
+import com.android.systemui.util.FakeSharedPreferences
+import java.io.File
+
+class FakeUserFileManager : UserFileManager {
+    private val sharedPreferences = mutableMapOf<SharedPrefKey, FakeSharedPreferences>()
+
+    override fun getFile(fileName: String, userId: Int): File {
+        throw UnsupportedOperationException("getFile not implemented in fake")
+    }
+
+    override fun getSharedPreferences(fileName: String, mode: Int, userId: Int): SharedPreferences {
+        val key = SharedPrefKey(fileName, mode, userId)
+        return sharedPreferences.getOrPut(key) { FakeSharedPreferences() }
+    }
+
+    private data class SharedPrefKey(val fileName: String, val mode: Int, val userId: Int)
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/UserFileManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/UserFileManagerKosmos.kt
new file mode 100644
index 0000000..4d7a40a
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/UserFileManagerKosmos.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.settings
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.fakeUserFileManager by Kosmos.Fixture { FakeUserFileManager() }
+var Kosmos.userFileManager: UserFileManager by Kosmos.Fixture { fakeUserFileManager }