Merge "Disable communal hub if lockscreen widgets are disabled" into main
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
index ff53ff2..378a1e4 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
@@ -34,6 +34,7 @@
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.stateIn
 
@@ -43,23 +44,21 @@
 @Inject
 constructor(
     @Application private val applicationScope: CoroutineScope,
-    private val viewModel: LockscreenSceneViewModel,
+    viewModel: LockscreenSceneViewModel,
     private val lockscreenContent: Lazy<LockscreenContent>,
 ) : ComposableScene {
     override val key = SceneKey.Lockscreen
 
     override val destinationScenes: StateFlow<Map<UserAction, SceneModel>> =
-        viewModel.upDestinationSceneKey
-            .map { pageKey ->
-                destinationScenes(up = pageKey, left = viewModel.leftDestinationSceneKey)
-            }
+        combine(viewModel.upDestinationSceneKey, viewModel.leftDestinationSceneKey, ::Pair)
+            .map { (upKey, leftKey) -> destinationScenes(up = upKey, left = leftKey) }
             .stateIn(
                 scope = applicationScope,
                 started = SharingStarted.Eagerly,
                 initialValue =
                     destinationScenes(
                         up = viewModel.upDestinationSceneKey.value,
-                        left = viewModel.leftDestinationSceneKey,
+                        left = viewModel.leftDestinationSceneKey.value,
                     )
             )
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt
index bd9ca30..b4e2eab 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt
@@ -16,26 +16,18 @@
 
 package com.android.systemui.communal.data.repository
 
-import android.content.pm.UserInfo
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.communal.shared.model.CommunalSceneKey
 import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.flags.Flags
-import com.android.systemui.flags.fakeFeatureFlagsClassic
-import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.scene.data.repository.sceneContainerRepository
 import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
 import com.android.systemui.scene.shared.model.SceneKey
 import com.android.systemui.scene.shared.model.SceneModel
 import com.android.systemui.testKosmos
-import com.android.systemui.user.data.repository.FakeUserRepository
-import com.android.systemui.user.data.repository.fakeUserRepository
-import com.android.systemui.util.settings.FakeSettings
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.test.runTest
@@ -48,37 +40,20 @@
 class CommunalRepositoryImplTest : SysuiTestCase() {
     private lateinit var underTest: CommunalRepositoryImpl
 
-    private lateinit var secureSettings: FakeSettings
-    private lateinit var userRepository: FakeUserRepository
-
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val sceneContainerRepository = kosmos.sceneContainerRepository
 
     @Before
     fun setUp() {
-        secureSettings = FakeSettings()
-        userRepository = kosmos.fakeUserRepository
-
-        val listOfUserInfo = listOf(MAIN_USER_INFO)
-        userRepository.setUserInfos(listOfUserInfo)
-
-        kosmos.fakeFeatureFlagsClassic.apply { set(Flags.COMMUNAL_SERVICE_ENABLED, true) }
-        mSetFlagsRule.enableFlags(FLAG_COMMUNAL_HUB)
-
         underTest = createRepositoryImpl(false)
     }
 
     private fun createRepositoryImpl(sceneContainerEnabled: Boolean): CommunalRepositoryImpl {
         return CommunalRepositoryImpl(
             testScope.backgroundScope,
-            testScope.backgroundScope,
-            kosmos.testDispatcher,
-            kosmos.fakeFeatureFlagsClassic,
             kosmos.fakeSceneContainerFlags.apply { enabled = sceneContainerEnabled },
             sceneContainerRepository,
-            kosmos.fakeUserRepository,
-            secureSettings,
         )
     }
 
@@ -159,29 +134,4 @@
             assertThat(transitionState)
                 .isEqualTo(ObservableCommunalTransitionState.Idle(CommunalSceneKey.DEFAULT))
         }
-
-    @Test
-    fun communalEnabledState_false_whenGlanceableHubSettingFalse() =
-        testScope.runTest {
-            userRepository.setSelectedUserInfo(MAIN_USER_INFO)
-            secureSettings.putIntForUser(GLANCEABLE_HUB_ENABLED, 0, MAIN_USER_INFO.id)
-
-            val communalEnabled by collectLastValue(underTest.communalEnabledState)
-            assertThat(communalEnabled).isFalse()
-        }
-
-    @Test
-    fun communalEnabledState_true_whenGlanceableHubSettingTrue() =
-        testScope.runTest {
-            userRepository.setSelectedUserInfo(MAIN_USER_INFO)
-            secureSettings.putIntForUser(GLANCEABLE_HUB_ENABLED, 1, MAIN_USER_INFO.id)
-
-            val communalEnabled by collectLastValue(underTest.communalEnabledState)
-            assertThat(communalEnabled).isTrue()
-        }
-
-    companion object {
-        private const val GLANCEABLE_HUB_ENABLED = "glanceable_hub_enabled"
-        private val MAIN_USER_INFO = UserInfo(0, "primary", UserInfo.FLAG_MAIN)
-    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt
new file mode 100644
index 0000000..0aca16d
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt
@@ -0,0 +1,153 @@
+/*
+ * 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.communal.data.repository
+
+import android.app.admin.DevicePolicyManager
+import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE
+import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL
+import android.app.admin.devicePolicyManager
+import android.content.Intent
+import android.content.pm.UserInfo
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.broadcastDispatcher
+import com.android.systemui.communal.data.model.DisabledReason
+import com.android.systemui.communal.data.repository.CommunalSettingsRepositoryImpl.Companion.GLANCEABLE_HUB_ENABLED
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED
+import com.android.systemui.flags.fakeFeatureFlagsClassic
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.nullable
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.settings.fakeSettings
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.eq
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class CommunalSettingsRepositoryImplTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private lateinit var underTest: CommunalSettingsRepository
+
+    @Before
+    fun setUp() {
+        kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
+        setKeyguardFeaturesDisabled(PRIMARY_USER, KEYGUARD_DISABLE_FEATURES_NONE)
+        setKeyguardFeaturesDisabled(SECONDARY_USER, KEYGUARD_DISABLE_FEATURES_NONE)
+        underTest = kosmos.communalSettingsRepository
+    }
+
+    @EnableFlags(FLAG_COMMUNAL_HUB)
+    @Test
+    fun secondaryUserIsInvalid() =
+        testScope.runTest {
+            val enabledState by collectLastValue(underTest.getEnabledState(SECONDARY_USER))
+
+            assertThat(enabledState?.enabled).isFalse()
+            assertThat(enabledState).containsExactly(DisabledReason.DISABLED_REASON_INVALID_USER)
+        }
+
+    @EnableFlags(FLAG_COMMUNAL_HUB)
+    @Test
+    fun classicFlagIsDisabled() =
+        testScope.runTest {
+            kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, false)
+            val enabledState by collectLastValue(underTest.getEnabledState(PRIMARY_USER))
+            assertThat(enabledState?.enabled).isFalse()
+            assertThat(enabledState).containsExactly(DisabledReason.DISABLED_REASON_FLAG)
+        }
+
+    @DisableFlags(FLAG_COMMUNAL_HUB)
+    @Test
+    fun communalHubFlagIsDisabled() =
+        testScope.runTest {
+            val enabledState by collectLastValue(underTest.getEnabledState(PRIMARY_USER))
+            assertThat(enabledState?.enabled).isFalse()
+            assertThat(enabledState).containsExactly(DisabledReason.DISABLED_REASON_FLAG)
+        }
+
+    @EnableFlags(FLAG_COMMUNAL_HUB)
+    @Test
+    fun hubIsDisabledByUser() =
+        testScope.runTest {
+            kosmos.fakeSettings.putIntForUser(GLANCEABLE_HUB_ENABLED, 0, PRIMARY_USER.id)
+            val enabledState by collectLastValue(underTest.getEnabledState(PRIMARY_USER))
+            assertThat(enabledState?.enabled).isFalse()
+            assertThat(enabledState).containsExactly(DisabledReason.DISABLED_REASON_USER_SETTING)
+
+            kosmos.fakeSettings.putIntForUser(GLANCEABLE_HUB_ENABLED, 1, SECONDARY_USER.id)
+            assertThat(enabledState?.enabled).isFalse()
+
+            kosmos.fakeSettings.putIntForUser(GLANCEABLE_HUB_ENABLED, 1, PRIMARY_USER.id)
+            assertThat(enabledState?.enabled).isTrue()
+        }
+
+    @EnableFlags(FLAG_COMMUNAL_HUB)
+    @Test
+    fun hubIsDisabledByDevicePolicy() =
+        testScope.runTest {
+            val enabledState by collectLastValue(underTest.getEnabledState(PRIMARY_USER))
+            assertThat(enabledState?.enabled).isTrue()
+
+            setKeyguardFeaturesDisabled(PRIMARY_USER, KEYGUARD_DISABLE_WIDGETS_ALL)
+            assertThat(enabledState?.enabled).isFalse()
+            assertThat(enabledState).containsExactly(DisabledReason.DISABLED_REASON_DEVICE_POLICY)
+        }
+
+    @EnableFlags(FLAG_COMMUNAL_HUB)
+    @Test
+    fun hubIsDisabledByUserAndDevicePolicy() =
+        testScope.runTest {
+            val enabledState by collectLastValue(underTest.getEnabledState(PRIMARY_USER))
+            assertThat(enabledState?.enabled).isTrue()
+
+            kosmos.fakeSettings.putIntForUser(GLANCEABLE_HUB_ENABLED, 0, PRIMARY_USER.id)
+            setKeyguardFeaturesDisabled(PRIMARY_USER, KEYGUARD_DISABLE_WIDGETS_ALL)
+
+            assertThat(enabledState?.enabled).isFalse()
+            assertThat(enabledState)
+                .containsExactly(
+                    DisabledReason.DISABLED_REASON_DEVICE_POLICY,
+                    DisabledReason.DISABLED_REASON_USER_SETTING,
+                )
+        }
+
+    private fun setKeyguardFeaturesDisabled(user: UserInfo, disabledFlags: Int) {
+        whenever(kosmos.devicePolicyManager.getKeyguardDisabledFeatures(nullable(), eq(user.id)))
+            .thenReturn(disabledFlags)
+        kosmos.broadcastDispatcher.sendIntentToMatchingReceiversOnly(
+            context,
+            Intent(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
+        )
+    }
+
+    private companion object {
+        val PRIMARY_USER =
+            UserInfo(/* id= */ 0, /* name= */ "primary user", /* flags= */ UserInfo.FLAG_MAIN)
+        val SECONDARY_USER = UserInfo(/* id= */ 1, /* name= */ "secondary user", /* flags= */ 0)
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorCommunalDisabledTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorCommunalDisabledTest.kt
index 6a3fc2a..824733b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorCommunalDisabledTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorCommunalDisabledTest.kt
@@ -19,6 +19,7 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.communal.data.repository.FakeCommunalRepository
 import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository
@@ -59,7 +60,7 @@
         widgetRepository = kosmos.fakeCommunalWidgetRepository
         keyguardRepository = kosmos.fakeKeyguardRepository
 
-        communalRepository.setIsCommunalEnabled(false)
+        mSetFlagsRule.disableFlags(FLAG_COMMUNAL_HUB)
 
         underTest = kosmos.communalInteractor
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
index c5485c5..3ac19e4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
@@ -23,6 +23,7 @@
 import android.widget.RemoteViews
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.communal.data.repository.FakeCommunalMediaRepository
 import com.android.systemui.communal.data.repository.FakeCommunalPrefsRepository
@@ -41,6 +42,8 @@
 import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState
 import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.Flags
+import com.android.systemui.flags.fakeFeatureFlagsClassic
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 import com.android.systemui.kosmos.testScope
@@ -109,12 +112,19 @@
         whenever(secondaryUser.isMain).thenReturn(false)
         userRepository.setUserInfos(listOf(mainUser, secondaryUser))
 
+        kosmos.fakeFeatureFlagsClassic.set(Flags.COMMUNAL_SERVICE_ENABLED, true)
+        mSetFlagsRule.enableFlags(FLAG_COMMUNAL_HUB)
+
         underTest = kosmos.communalInteractor
     }
 
     @Test
     fun communalEnabled_true() =
-        testScope.runTest { assertThat(underTest.isCommunalEnabled).isTrue() }
+        testScope.runTest {
+            userRepository.setSelectedUserInfo(mainUser)
+            runCurrent()
+            assertThat(underTest.isCommunalEnabled).isTrue()
+        }
 
     @Test
     fun isCommunalAvailable_storageUnlockedAndMainUser_true() =
@@ -125,7 +135,6 @@
             keyguardRepository.setIsEncryptedOrLockdown(false)
             userRepository.setSelectedUserInfo(mainUser)
             keyguardRepository.setKeyguardShowing(true)
-            communalRepository.setCommunalEnabledState(true)
 
             assertThat(isAvailable).isTrue()
         }
@@ -139,7 +148,6 @@
             keyguardRepository.setIsEncryptedOrLockdown(true)
             userRepository.setSelectedUserInfo(mainUser)
             keyguardRepository.setKeyguardShowing(true)
-            communalRepository.setCommunalEnabledState(true)
 
             assertThat(isAvailable).isFalse()
         }
@@ -153,7 +161,6 @@
             keyguardRepository.setIsEncryptedOrLockdown(false)
             userRepository.setSelectedUserInfo(secondaryUser)
             keyguardRepository.setKeyguardShowing(true)
-            communalRepository.setCommunalEnabledState(true)
 
             assertThat(isAvailable).isFalse()
         }
@@ -167,7 +174,6 @@
             keyguardRepository.setIsEncryptedOrLockdown(false)
             userRepository.setSelectedUserInfo(mainUser)
             keyguardRepository.setDreaming(true)
-            communalRepository.setCommunalEnabledState(true)
 
             assertThat(isAvailable).isTrue()
         }
@@ -175,13 +181,14 @@
     @Test
     fun isCommunalAvailable_communalDisabled_false() =
         testScope.runTest {
+            mSetFlagsRule.disableFlags(FLAG_COMMUNAL_HUB)
+
             val isAvailable by collectLastValue(underTest.isCommunalAvailable)
             assertThat(isAvailable).isFalse()
 
             keyguardRepository.setIsEncryptedOrLockdown(false)
             userRepository.setSelectedUserInfo(mainUser)
             keyguardRepository.setKeyguardShowing(true)
-            communalRepository.setCommunalEnabledState(false)
 
             assertThat(isAvailable).isFalse()
         }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt
index 6c87e0f..ceb7fac 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt
@@ -22,12 +22,15 @@
 import android.provider.Settings.Secure.HUB_MODE_TUTORIAL_STARTED
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.communal.data.repository.FakeCommunalRepository
 import com.android.systemui.communal.data.repository.FakeCommunalTutorialRepository
 import com.android.systemui.communal.data.repository.fakeCommunalRepository
 import com.android.systemui.communal.data.repository.fakeCommunalTutorialRepository
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.Flags
+import com.android.systemui.flags.fakeFeatureFlagsClassic
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 import com.android.systemui.kosmos.testScope
@@ -35,11 +38,13 @@
 import com.android.systemui.user.data.repository.FakeUserRepository
 import com.android.systemui.user.data.repository.fakeUserRepository
 import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 
+@OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class CommunalTutorialInteractorTest : SysuiTestCase() {
@@ -62,6 +67,8 @@
         userRepository = kosmos.fakeUserRepository
 
         userRepository.setUserInfos(listOf(MAIN_USER_INFO))
+        kosmos.fakeFeatureFlagsClassic.set(Flags.COMMUNAL_SERVICE_ENABLED, true)
+        mSetFlagsRule.enableFlags(FLAG_COMMUNAL_HUB)
 
         underTest = kosmos.communalTutorialInteractor
     }
@@ -127,6 +134,7 @@
         testScope.runTest {
             val tutorialSettingState by
                 collectLastValue(communalTutorialRepository.tutorialSettingState)
+            userRepository.setSelectedUserInfo(MAIN_USER_INFO)
             communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED)
 
             communalRepository.setIsCommunalHubShowing(true)
@@ -139,6 +147,7 @@
         testScope.runTest {
             val tutorialSettingState by
                 collectLastValue(communalTutorialRepository.tutorialSettingState)
+            userRepository.setSelectedUserInfo(MAIN_USER_INFO)
             communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_STARTED)
 
             communalRepository.setIsCommunalHubShowing(true)
@@ -151,6 +160,7 @@
         testScope.runTest {
             val tutorialSettingState by
                 collectLastValue(communalTutorialRepository.tutorialSettingState)
+            userRepository.setSelectedUserInfo(MAIN_USER_INFO)
             communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
 
             communalRepository.setIsCommunalHubShowing(true)
@@ -163,6 +173,7 @@
         testScope.runTest {
             val tutorialSettingState by
                 collectLastValue(communalTutorialRepository.tutorialSettingState)
+            userRepository.setSelectedUserInfo(MAIN_USER_INFO)
             communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED)
 
             communalRepository.setIsCommunalHubShowing(false)
@@ -175,6 +186,7 @@
         testScope.runTest {
             val tutorialSettingState by
                 collectLastValue(communalTutorialRepository.tutorialSettingState)
+            userRepository.setSelectedUserInfo(MAIN_USER_INFO)
             communalRepository.setIsCommunalHubShowing(true)
             communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_STARTED)
 
@@ -188,6 +200,7 @@
         testScope.runTest {
             val tutorialSettingState by
                 collectLastValue(communalTutorialRepository.tutorialSettingState)
+            userRepository.setSelectedUserInfo(MAIN_USER_INFO)
             communalRepository.setIsCommunalHubShowing(true)
             communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
 
@@ -198,14 +211,11 @@
 
     private suspend fun setCommunalAvailable(available: Boolean) {
         if (available) {
-            communalRepository.setIsCommunalEnabled(true)
-            communalRepository.setCommunalEnabledState(true)
             keyguardRepository.setIsEncryptedOrLockdown(false)
             userRepository.setSelectedUserInfo(MAIN_USER_INFO)
             keyguardRepository.setKeyguardShowing(true)
         } else {
-            communalRepository.setIsCommunalEnabled(false)
-            communalRepository.setCommunalEnabledState(false)
+            keyguardRepository.setIsEncryptedOrLockdown(true)
         }
     }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
index 73d3091..f70b6a5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
@@ -22,12 +22,12 @@
 import android.widget.RemoteViews
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.communal.data.repository.FakeCommunalMediaRepository
 import com.android.systemui.communal.data.repository.FakeCommunalTutorialRepository
 import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository
 import com.android.systemui.communal.data.repository.fakeCommunalMediaRepository
-import com.android.systemui.communal.data.repository.fakeCommunalRepository
 import com.android.systemui.communal.data.repository.fakeCommunalTutorialRepository
 import com.android.systemui.communal.data.repository.fakeCommunalWidgetRepository
 import com.android.systemui.communal.domain.interactor.communalInteractor
@@ -37,6 +37,8 @@
 import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
 import com.android.systemui.communal.ui.viewmodel.CommunalViewModel.Companion.POPUP_AUTO_HIDE_TIMEOUT_MS
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED
+import com.android.systemui.flags.fakeFeatureFlagsClassic
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 import com.android.systemui.kosmos.testScope
@@ -92,7 +94,8 @@
         mediaRepository = kosmos.fakeCommunalMediaRepository
         userRepository = kosmos.fakeUserRepository
 
-        kosmos.fakeCommunalRepository.setCommunalEnabledState(true)
+        kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
+        mSetFlagsRule.enableFlags(FLAG_COMMUNAL_HUB)
 
         underTest =
             CommunalViewModel(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt
index 032d76f..8488843 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt
@@ -19,12 +19,15 @@
 import android.content.pm.UserInfo
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.communal.data.repository.fakeCommunalRepository
+import com.android.systemui.communal.data.repository.CommunalSettingsRepositoryImpl.Companion.GLANCEABLE_HUB_ENABLED
 import com.android.systemui.communal.data.repository.fakeCommunalWidgetRepository
 import com.android.systemui.communal.domain.interactor.communalInteractor
 import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.Flags
+import com.android.systemui.flags.fakeFeatureFlagsClassic
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.kosmos.testDispatcher
@@ -33,6 +36,7 @@
 import com.android.systemui.user.data.repository.fakeUserRepository
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.settings.fakeSettings
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.MutableSharedFlow
@@ -62,6 +66,8 @@
     fun setUp() {
         MockitoAnnotations.initMocks(this)
         kosmos.fakeUserRepository.setUserInfos(listOf(MAIN_USER_INFO))
+        kosmos.fakeFeatureFlagsClassic.set(Flags.COMMUNAL_SERVICE_ENABLED, true)
+        mSetFlagsRule.enableFlags(FLAG_COMMUNAL_HUB)
 
         appWidgetIdToRemove = MutableSharedFlow()
         whenever(appWidgetHost.appWidgetIdToRemove).thenReturn(appWidgetIdToRemove)
@@ -169,7 +175,8 @@
             fakeKeyguardRepository.setIsEncryptedOrLockdown(false)
             fakeUserRepository.setSelectedUserInfo(MAIN_USER_INFO)
             fakeKeyguardRepository.setKeyguardShowing(true)
-            fakeCommunalRepository.setCommunalEnabledState(available)
+            val settingsValue = if (available) 1 else 0
+            fakeSettings.putIntForUser(GLANCEABLE_HUB_ENABLED, settingsValue, MAIN_USER_INFO.id)
         }
 
     private companion object {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
index 4595fbf..7261723 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
@@ -18,22 +18,28 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
+import android.content.pm.UserInfo
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
-import com.android.systemui.communal.data.repository.fakeCommunalRepository
-import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
+import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED
+import com.android.systemui.flags.fakeFeatureFlagsClassic
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.scene.shared.model.SceneKey
 import com.android.systemui.scene.shared.model.SceneModel
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationsPlaceholderViewModel
 import com.android.systemui.testKosmos
+import com.android.systemui.user.data.repository.fakeUserRepository
 import com.android.systemui.util.mockito.mock
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -49,9 +55,7 @@
     private val testScope = kosmos.testScope
     private val sceneInteractor by lazy { kosmos.sceneInteractor }
 
-    private val underTest by lazy {
-        createLockscreenSceneViewModel()
-    }
+    private val underTest by lazy { createLockscreenSceneViewModel() }
 
     @Test
     fun upTransitionSceneKey_canSwipeToUnlock_gone() =
@@ -80,29 +84,37 @@
             assertThat(upTransitionSceneKey).isEqualTo(SceneKey.Bouncer)
         }
 
+    @EnableFlags(FLAG_COMMUNAL_HUB)
     @Test
     fun leftTransitionSceneKey_communalIsEnabled_communal() =
         testScope.runTest {
-            kosmos.fakeCommunalRepository.setIsCommunalEnabled(true)
-            val underTest = createLockscreenSceneViewModel()
-
-            assertThat(underTest.leftDestinationSceneKey).isEqualTo(SceneKey.Communal)
+            with(kosmos.fakeUserRepository) {
+                setUserInfos(listOf(PRIMARY_USER))
+                setSelectedUserInfo(PRIMARY_USER)
+            }
+            kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
+            val leftDestinationSceneKey by collectLastValue(underTest.leftDestinationSceneKey)
+            assertThat(leftDestinationSceneKey).isEqualTo(SceneKey.Communal)
         }
 
+    @DisableFlags(FLAG_COMMUNAL_HUB)
     @Test
     fun leftTransitionSceneKey_communalIsDisabled_null() =
         testScope.runTest {
-            kosmos.fakeCommunalRepository.setIsCommunalEnabled(false)
-            val underTest = createLockscreenSceneViewModel()
-
-            assertThat(underTest.leftDestinationSceneKey).isNull()
+            with(kosmos.fakeUserRepository) {
+                setUserInfos(listOf(PRIMARY_USER))
+                setSelectedUserInfo(PRIMARY_USER)
+            }
+            kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, false)
+            val leftDestinationSceneKey by collectLastValue(underTest.leftDestinationSceneKey)
+            assertThat(leftDestinationSceneKey).isNull()
         }
 
     private fun createLockscreenSceneViewModel(): LockscreenSceneViewModel {
         return LockscreenSceneViewModel(
             applicationScope = testScope.backgroundScope,
             deviceEntryInteractor = kosmos.deviceEntryInteractor,
-            communalInteractor = kosmos.communalInteractor,
+            communalSettingsInteractor = kosmos.communalSettingsInteractor,
             longPress =
                 KeyguardLongPressViewModel(
                     interactor = mock(),
@@ -110,4 +122,9 @@
             notifications = kosmos.notificationsPlaceholderViewModel,
         )
     }
+
+    private companion object {
+        val PRIMARY_USER =
+            UserInfo(/* id= */ 0, /* name= */ "primary user", /* flags= */ UserInfo.FLAG_MAIN)
+    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index 9d3f0d6..006f429 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -40,7 +40,7 @@
 import com.android.systemui.bouncer.ui.viewmodel.bouncerViewModel
 import com.android.systemui.classifier.domain.interactor.falsingInteractor
 import com.android.systemui.classifier.falsingCollector
-import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
@@ -130,7 +130,7 @@
     private val sceneInteractor by lazy { kosmos.sceneInteractor }
     private val authenticationInteractor by lazy { kosmos.authenticationInteractor }
     private val deviceEntryInteractor by lazy { kosmos.deviceEntryInteractor }
-    private val communalInteractor by lazy { kosmos.communalInteractor }
+    private val communalSettingsInteractor by lazy { kosmos.communalSettingsInteractor }
 
     private val transitionState by lazy {
         MutableStateFlow<ObservableTransitionState>(
@@ -155,7 +155,7 @@
         LockscreenSceneViewModel(
             applicationScope = testScope.backgroundScope,
             deviceEntryInteractor = deviceEntryInteractor,
-            communalInteractor = communalInteractor,
+            communalSettingsInteractor = communalSettingsInteractor,
             longPress =
                 KeyguardLongPressViewModel(
                     interactor = mock(),
diff --git a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
index dc07c1b..0bad33b 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
@@ -20,6 +20,7 @@
 import com.android.systemui.communal.data.repository.CommunalMediaRepositoryModule
 import com.android.systemui.communal.data.repository.CommunalPrefsRepositoryModule
 import com.android.systemui.communal.data.repository.CommunalRepositoryModule
+import com.android.systemui.communal.data.repository.CommunalSettingsRepositoryModule
 import com.android.systemui.communal.data.repository.CommunalTutorialRepositoryModule
 import com.android.systemui.communal.data.repository.CommunalWidgetRepositoryModule
 import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
@@ -36,6 +37,7 @@
             CommunalWidgetRepositoryModule::class,
             CommunalDatabaseModule::class,
             CommunalPrefsRepositoryModule::class,
+            CommunalSettingsRepositoryModule::class,
         ]
 )
 interface CommunalModule {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalEnabledState.kt b/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalEnabledState.kt
new file mode 100644
index 0000000..83a5bdb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalEnabledState.kt
@@ -0,0 +1,69 @@
+/*
+ * 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.communal.data.model
+
+import com.android.systemui.log.table.Diffable
+import com.android.systemui.log.table.TableRowLogger
+import java.util.EnumSet
+
+/** Reasons that communal is disabled, primarily for logging. */
+enum class DisabledReason(val loggingString: String) {
+    /** Communal should be disabled due to invalid current user */
+    DISABLED_REASON_INVALID_USER("invalidUser"),
+    /** Communal should be disabled due to the flag being off */
+    DISABLED_REASON_FLAG("flag"),
+    /** Communal should be disabled because the user has turned off the setting */
+    DISABLED_REASON_USER_SETTING("userSetting"),
+    /** Communal is disabled by the device policy app */
+    DISABLED_REASON_DEVICE_POLICY("devicePolicy"),
+}
+
+/**
+ * Model representing the reasons communal hub should be disabled. Allows logging reasons separately
+ * for debugging.
+ */
+@JvmInline
+value class CommunalEnabledState(
+    private val disabledReasons: EnumSet<DisabledReason> =
+        EnumSet.noneOf(DisabledReason::class.java)
+) : Diffable<CommunalEnabledState>, Set<DisabledReason> by disabledReasons {
+
+    /** Creates [CommunalEnabledState] with a single reason for being disabled */
+    constructor(reason: DisabledReason) : this(EnumSet.of(reason))
+
+    /** Checks if there are any reasons communal should be disabled. If none, returns true. */
+    val enabled: Boolean
+        get() = isEmpty()
+
+    override fun logDiffs(prevVal: CommunalEnabledState, row: TableRowLogger) {
+        for (reason in DisabledReason.entries) {
+            val newVal = contains(reason)
+            if (newVal != prevVal.contains(reason)) {
+                row.logChange(
+                    columnName = reason.loggingString,
+                    value = newVal,
+                )
+            }
+        }
+    }
+
+    override fun logFull(row: TableRowLogger) {
+        for (reason in DisabledReason.entries) {
+            row.logChange(columnName = reason.loggingString, value = contains(reason))
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt
index addd880..4a06585f5 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt
@@ -16,22 +16,14 @@
 
 package com.android.systemui.communal.data.repository
 
-import com.android.systemui.Flags.communalHub
 import com.android.systemui.communal.shared.model.CommunalSceneKey
 import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState
 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.FeatureFlagsClassic
-import com.android.systemui.flags.Flags
 import com.android.systemui.scene.data.repository.SceneContainerRepository
 import com.android.systemui.scene.shared.flag.SceneContainerFlags
 import com.android.systemui.scene.shared.model.SceneKey
-import com.android.systemui.user.data.repository.UserRepository
-import com.android.systemui.util.settings.SecureSettings
-import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
 import javax.inject.Inject
-import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
@@ -39,26 +31,13 @@
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.flow.filterNotNull
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.onStart
-import kotlinx.coroutines.flow.shareIn
 import kotlinx.coroutines.flow.stateIn
-import kotlinx.coroutines.withContext
 
 /** Encapsulates the state of communal mode. */
 interface CommunalRepository {
-    /** Whether communal features are enabled. */
-    val isCommunalEnabled: Boolean
-
-    /**
-     * A {@link StateFlow} that tracks whether communal hub is enabled (it can be disabled in
-     * settings).
-     */
-    val communalEnabledState: StateFlow<Boolean>
-
     /** Whether the communal hub is showing. */
     val isCommunalHubShowing: Flow<Boolean>
 
@@ -87,37 +66,11 @@
 class CommunalRepositoryImpl
 @Inject
 constructor(
-    @Application private val applicationScope: CoroutineScope,
     @Background backgroundScope: CoroutineScope,
-    @Background private val backgroundDispatcher: CoroutineDispatcher,
-    private val featureFlagsClassic: FeatureFlagsClassic,
     sceneContainerFlags: SceneContainerFlags,
     sceneContainerRepository: SceneContainerRepository,
-    userRepository: UserRepository,
-    private val secureSettings: SecureSettings
 ) : CommunalRepository {
 
-    private val communalEnabledSettingState: Flow<Boolean> =
-        userRepository.selectedUserInfo
-            .flatMapLatest { userInfo -> observeSettings(userInfo.id) }
-            .shareIn(scope = applicationScope, started = SharingStarted.WhileSubscribed())
-
-    override val communalEnabledState: StateFlow<Boolean> =
-        if (featureFlagsClassic.isEnabled(Flags.COMMUNAL_SERVICE_ENABLED) && communalHub()) {
-            communalEnabledSettingState
-                .filterNotNull()
-                .stateIn(
-                    scope = applicationScope,
-                    started = SharingStarted.Eagerly,
-                    initialValue = true
-                )
-        } else {
-            MutableStateFlow(false)
-        }
-
-    override val isCommunalEnabled: Boolean
-        get() = communalEnabledState.value
-
     private val _desiredScene: MutableStateFlow<CommunalSceneKey> =
         MutableStateFlow(CommunalSceneKey.DEFAULT)
     override val desiredScene: StateFlow<CommunalSceneKey> = _desiredScene.asStateFlow()
@@ -153,26 +106,4 @@
         } else {
             desiredScene.map { sceneKey -> sceneKey == CommunalSceneKey.Communal }
         }
-
-    private fun observeSettings(userId: Int): Flow<Boolean> =
-        secureSettings
-            .observerFlow(
-                userId = userId,
-                names =
-                    arrayOf(
-                        GLANCEABLE_HUB_ENABLED,
-                    )
-            )
-            // Force an update
-            .onStart { emit(Unit) }
-            .map { readFromSettings(userId) }
-
-    private suspend fun readFromSettings(userId: Int): Boolean =
-        withContext(backgroundDispatcher) {
-            secureSettings.getIntForUser(GLANCEABLE_HUB_ENABLED, 1, userId) == 1
-        }
-
-    companion object {
-        private const val GLANCEABLE_HUB_ENABLED = "glanceable_hub_enabled"
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
new file mode 100644
index 0000000..201b049
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
@@ -0,0 +1,126 @@
+/*
+ * 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.communal.data.repository
+
+import android.app.admin.DevicePolicyManager
+import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL
+import android.content.IntentFilter
+import android.content.pm.UserInfo
+import com.android.systemui.Flags.communalHub
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.communal.data.model.CommunalEnabledState
+import com.android.systemui.communal.data.model.DisabledReason
+import com.android.systemui.communal.data.model.DisabledReason.DISABLED_REASON_DEVICE_POLICY
+import com.android.systemui.communal.data.model.DisabledReason.DISABLED_REASON_FLAG
+import com.android.systemui.communal.data.model.DisabledReason.DISABLED_REASON_INVALID_USER
+import com.android.systemui.communal.data.model.DisabledReason.DISABLED_REASON_USER_SETTING
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.flags.Flags
+import com.android.systemui.util.kotlin.emitOnStart
+import com.android.systemui.util.settings.SecureSettings
+import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
+import java.util.EnumSet
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
+
+interface CommunalSettingsRepository {
+    /** A [CommunalEnabledState] for the specified user. */
+    fun getEnabledState(user: UserInfo): Flow<CommunalEnabledState>
+}
+
+@SysUISingleton
+class CommunalSettingsRepositoryImpl
+@Inject
+constructor(
+    @Background private val bgDispatcher: CoroutineDispatcher,
+    private val featureFlagsClassic: FeatureFlagsClassic,
+    private val secureSettings: SecureSettings,
+    private val broadcastDispatcher: BroadcastDispatcher,
+    private val devicePolicyManager: DevicePolicyManager,
+) : CommunalSettingsRepository {
+
+    private val flagEnabled: Boolean by lazy {
+        featureFlagsClassic.isEnabled(Flags.COMMUNAL_SERVICE_ENABLED) && communalHub()
+    }
+
+    override fun getEnabledState(user: UserInfo): Flow<CommunalEnabledState> {
+        if (!user.isMain) {
+            return flowOf(CommunalEnabledState(DISABLED_REASON_INVALID_USER))
+        }
+        if (!flagEnabled) {
+            return flowOf(CommunalEnabledState(DISABLED_REASON_FLAG))
+        }
+        return combine(
+                getEnabledByUser(user).mapToReason(DISABLED_REASON_USER_SETTING),
+                getAllowedByDevicePolicy(user).mapToReason(DISABLED_REASON_DEVICE_POLICY),
+            ) { reasons ->
+                reasons.filterNotNull()
+            }
+            .map { reasons ->
+                if (reasons.isEmpty()) {
+                    EnumSet.noneOf(DisabledReason::class.java)
+                } else {
+                    EnumSet.copyOf(reasons)
+                }
+            }
+            .map { reasons -> CommunalEnabledState(reasons) }
+            .flowOn(bgDispatcher)
+    }
+
+    private fun getEnabledByUser(user: UserInfo): Flow<Boolean> =
+        secureSettings
+            .observerFlow(userId = user.id, names = arrayOf(GLANCEABLE_HUB_ENABLED))
+            // Force an update
+            .onStart { emit(Unit) }
+            .map {
+                secureSettings.getIntForUser(
+                    GLANCEABLE_HUB_ENABLED,
+                    ENABLED_SETTING_DEFAULT,
+                    user.id,
+                ) == 1
+            }
+
+    private fun getAllowedByDevicePolicy(user: UserInfo): Flow<Boolean> =
+        broadcastDispatcher
+            .broadcastFlow(
+                filter =
+                    IntentFilter(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
+                user = user.userHandle
+            )
+            .emitOnStart()
+            .map { devicePolicyManager.areKeyguardWidgetsAllowed(user.id) }
+
+    companion object {
+        const val GLANCEABLE_HUB_ENABLED = "glanceable_hub_enabled"
+        private const val ENABLED_SETTING_DEFAULT = 1
+    }
+}
+
+private fun DevicePolicyManager.areKeyguardWidgetsAllowed(userId: Int): Boolean =
+    (getKeyguardDisabledFeatures(null, userId) and KEYGUARD_DISABLE_WIDGETS_ALL) == 0
+
+private fun Flow<Boolean>.mapToReason(reason: DisabledReason) = map { enabled ->
+    if (enabled) null else reason
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryModule.kt
new file mode 100644
index 0000000..a931d3f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryModule.kt
@@ -0,0 +1,26 @@
+/*
+ * 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.communal.data.repository
+
+import dagger.Binds
+import dagger.Module
+
+@Module
+interface CommunalSettingsRepositoryModule {
+    @Binds
+    fun communalSettingsRepository(impl: CommunalSettingsRepositoryImpl): CommunalSettingsRepository
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
index 950ac3c..23f590e 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -42,7 +42,6 @@
 import com.android.systemui.log.table.TableLogBuffer
 import com.android.systemui.log.table.logDiffsForTable
 import com.android.systemui.smartspace.data.repository.SmartspaceRepository
-import com.android.systemui.user.data.repository.UserRepository
 import com.android.systemui.util.kotlin.BooleanFlowOperators.and
 import com.android.systemui.util.kotlin.BooleanFlowOperators.not
 import com.android.systemui.util.kotlin.BooleanFlowOperators.or
@@ -74,8 +73,8 @@
     private val communalPrefsRepository: CommunalPrefsRepository,
     mediaRepository: CommunalMediaRepository,
     smartspaceRepository: SmartspaceRepository,
-    userRepository: UserRepository,
     keyguardInteractor: KeyguardInteractor,
+    private val communalSettingsInteractor: CommunalSettingsInteractor,
     private val appWidgetHost: CommunalAppWidgetHost,
     private val editWidgetsActivityStarter: EditWidgetsActivityStarter,
     @CommunalLog logBuffer: LogBuffer,
@@ -90,13 +89,12 @@
 
     /** Whether communal features are enabled. */
     val isCommunalEnabled: Boolean
-        get() = communalRepository.isCommunalEnabled
+        get() = communalSettingsInteractor.isCommunalEnabled.value
 
     /** Whether communal features are enabled and available. */
     val isCommunalAvailable: Flow<Boolean> =
         and(
-                communalRepository.communalEnabledState,
-                userRepository.selectedUserInfo.map { it.isMain },
+                communalSettingsInteractor.isCommunalEnabled,
                 not(keyguardInteractor.isEncryptedOrLockdown),
                 or(keyguardInteractor.isKeyguardVisible, keyguardInteractor.isDreaming)
             )
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt
new file mode 100644
index 0000000..0b096ce
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt
@@ -0,0 +1,58 @@
+/*
+ * 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.communal.domain.interactor
+
+import com.android.systemui.communal.data.model.CommunalEnabledState
+import com.android.systemui.communal.data.repository.CommunalSettingsRepository
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.log.dagger.CommunalTableLog
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logDiffsForTable
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
+class CommunalSettingsInteractor
+@Inject
+constructor(
+    @Background private val bgScope: CoroutineScope,
+    private val repository: CommunalSettingsRepository,
+    userInteractor: SelectedUserInteractor,
+    @CommunalTableLog tableLogBuffer: TableLogBuffer,
+) {
+    /** Whether or not communal is enabled for the currently selected user. */
+    val isCommunalEnabled: StateFlow<Boolean> =
+        userInteractor.selectedUserInfo
+            .flatMapLatest { user -> repository.getEnabledState(user) }
+            .logDiffsForTable(
+                tableLogBuffer = tableLogBuffer,
+                columnPrefix = "disabledReason",
+                initialValue = CommunalEnabledState()
+            )
+            .map { model -> model.enabled }
+            // Start this eagerly since the value is accessed synchronously in many places.
+            .stateIn(scope = bgScope, started = SharingStarted.Eagerly, initialValue = false)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt
index 1404ee2..25dfc02 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt
@@ -28,17 +28,18 @@
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.Job
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.emptyFlow
 import kotlinx.coroutines.flow.filterNotNull
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.flow.transformWhile
 import kotlinx.coroutines.launch
 
 /** Encapsulates business-logic related to communal tutorial state. */
@@ -51,6 +52,7 @@
     private val communalTutorialRepository: CommunalTutorialRepository,
     keyguardInteractor: KeyguardInteractor,
     private val communalRepository: CommunalRepository,
+    private val communalSettingsInteractor: CommunalSettingsInteractor,
     communalInteractor: CommunalInteractor,
     @CommunalTableLog tableLogBuffer: TableLogBuffer,
 ) {
@@ -110,20 +112,24 @@
         return null
     }
 
-    private var job: Job? = null
     private fun listenForTransitionToUpdateTutorialState() {
-        if (!communalRepository.isCommunalEnabled) {
-            return
-        }
-        job =
-            scope.launch {
-                tutorialStateToUpdate.collect {
-                    communalTutorialRepository.setTutorialState(it)
-                    if (it == Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED) {
-                        job?.cancel()
+        scope.launch {
+            communalSettingsInteractor.isCommunalEnabled
+                .flatMapLatest { enabled ->
+                    if (!enabled) {
+                        emptyFlow()
+                    } else {
+                        tutorialStateToUpdate
                     }
                 }
-            }
+                .transformWhile { tutorialState ->
+                    emit(tutorialState)
+                    tutorialState != Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED
+                }
+                .collect { tutorialState ->
+                    communalTutorialRepository.setTutorialState(tutorialState)
+                }
+        }
     }
 
     init {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
index 2b28a71..fa18557 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
@@ -16,7 +16,7 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
-import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
@@ -36,7 +36,7 @@
 constructor(
     @Application applicationScope: CoroutineScope,
     deviceEntryInteractor: DeviceEntryInteractor,
-    communalInteractor: CommunalInteractor,
+    communalSettingsInteractor: CommunalSettingsInteractor,
     val longPress: KeyguardLongPressViewModel,
     val notifications: NotificationsPlaceholderViewModel,
 ) {
@@ -55,10 +55,12 @@
     }
 
     /** The key of the scene we should switch to when swiping left. */
-    val leftDestinationSceneKey: SceneKey? =
-        if (communalInteractor.isCommunalEnabled) {
-            SceneKey.Communal
-        } else {
-            null
-        }
+    val leftDestinationSceneKey: StateFlow<SceneKey?> =
+        communalSettingsInteractor.isCommunalEnabled
+            .map { available -> if (available) SceneKey.Communal else null }
+            .stateIn(
+                scope = applicationScope,
+                started = SharingStarted.WhileSubscribed(),
+                initialValue = null,
+            )
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt
index 523414c..35e0271 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt
@@ -1155,7 +1155,7 @@
                 onLockscreen && isTransformingToFullShadeAndInQQS() -> LOCATION_QQS
                 // TODO(b/311234666): revisit logic once interactions between the hub and
                 //  shade/keyguard state are finalized
-                isCommunalShowing && communalInteractor.isCommunalEnabled -> LOCATION_COMMUNAL_HUB
+                isCommunalShowing -> LOCATION_COMMUNAL_HUB
                 onLockscreen && allowMediaPlayerOnLockScreen -> LOCATION_LOCKSCREEN
                 else -> LOCATION_QQS
             }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
index 1c7cc00..df845f5 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
@@ -35,6 +35,7 @@
 import com.android.systemui.util.kotlin.collectFlow
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOf
 
 /**
  * Controller that's responsible for the glanceable hub container view and its touch handling.
@@ -105,13 +106,9 @@
      */
     private var shadeShowing = false
 
-    /** Returns true if the glanceable hub is enabled and the container view can be created. */
-    fun isEnabled(): Boolean {
-        return communalInteractor.isCommunalEnabled && isComposeAvailable()
-    }
-
-    /** Returns a {@link StateFlow} that tracks whether communal hub is available. */
-    fun communalAvailable(): Flow<Boolean> = communalInteractor.isCommunalAvailable
+    /** Returns a flow that tracks whether communal hub is available. */
+    fun communalAvailable(): Flow<Boolean> =
+        if (isComposeAvailable()) communalInteractor.isCommunalAvailable else flowOf(false)
 
     /**
      * Creates the container view containing the glanceable hub UI.
@@ -127,9 +124,6 @@
     /** Override for testing. */
     @VisibleForTesting
     internal fun initView(containerView: View): View {
-        if (!isEnabled()) {
-            throw RuntimeException("Glanceable hub is not enabled")
-        }
         if (communalContainerView != null) {
             throw RuntimeException("Communal view has already been initialized")
         }
diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/SelectedUserInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/SelectedUserInteractor.kt
index 0fb4b43..38b381a 100644
--- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/SelectedUserInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/SelectedUserInteractor.kt
@@ -1,6 +1,7 @@
 package com.android.systemui.user.domain.interactor
 
 import android.annotation.UserIdInt
+import android.content.pm.UserInfo
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.Flags.refactorGetCurrentUser
 import com.android.systemui.dagger.SysUISingleton
@@ -16,6 +17,9 @@
     /** Flow providing the ID of the currently selected user. */
     val selectedUser = repository.selectedUserInfo.map { it.id }.distinctUntilChanged()
 
+    /** Flow providing the [UserInfo] of the currently selected user. */
+    val selectedUserInfo = repository.selectedUserInfo
+
     /**
      * Returns the ID of the currently-selected user.
      *
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
index 9517f82..1dc5f7d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
@@ -103,8 +103,6 @@
             )
         testableLooper = TestableLooper.get(this)
 
-        communalRepository.setIsCommunalEnabled(true)
-
         whenever(keyguardTransitionInteractor.isFinishedInStateWhere(any()))
             .thenReturn(bouncerShowingFlow)
         whenever(shadeInteractor.isAnyFullyExpanded).thenReturn(shadeShowingFlow)
@@ -125,36 +123,6 @@
     }
 
     @Test
-    fun isEnabled_communalEnabled_returnsTrue() {
-        communalRepository.setIsCommunalEnabled(true)
-
-        assertThat(underTest.isEnabled()).isTrue()
-    }
-
-    @Test
-    fun isEnabled_communalDisabled_returnsFalse() {
-        communalRepository.setIsCommunalEnabled(false)
-
-        assertThat(underTest.isEnabled()).isFalse()
-    }
-
-    @Test
-    fun initView_notEnabled_throwsException() {
-        communalRepository.setIsCommunalEnabled(false)
-
-        underTest =
-            GlanceableHubContainerController(
-                communalInteractor,
-                communalViewModel,
-                keyguardTransitionInteractor,
-                shadeInteractor,
-                powerManager,
-            )
-
-        assertThrows(RuntimeException::class.java) { underTest.initView(context) }
-    }
-
-    @Test
     fun initView_calledTwice_throwsException() {
         underTest =
             GlanceableHubContainerController(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 22b05be..248ed24 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -488,7 +488,8 @@
             return
         }
 
-        whenever(mGlanceableHubContainerController.isEnabled()).thenReturn(true)
+        whenever(mGlanceableHubContainerController.communalAvailable())
+            .thenReturn(MutableStateFlow(true))
 
         val mockCommunalView = mock(View::class.java)
         whenever(mGlanceableHubContainerController.initView(any<Context>()))
@@ -513,7 +514,6 @@
             return
         }
 
-        whenever(mGlanceableHubContainerController.isEnabled()).thenReturn(false)
         whenever(mGlanceableHubContainerController.communalAvailable())
             .thenReturn(MutableStateFlow(false))
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryKosmos.kt
new file mode 100644
index 0000000..5485f79
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryKosmos.kt
@@ -0,0 +1,35 @@
+/*
+ * 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.communal.data.repository
+
+import android.app.admin.devicePolicyManager
+import com.android.systemui.broadcast.broadcastDispatcher
+import com.android.systemui.flags.featureFlagsClassic
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.util.settings.fakeSettings
+
+val Kosmos.communalSettingsRepository: CommunalSettingsRepository by
+    Kosmos.Fixture {
+        CommunalSettingsRepositoryImpl(
+            bgDispatcher = testDispatcher,
+            featureFlagsClassic = featureFlagsClassic,
+            secureSettings = fakeSettings,
+            broadcastDispatcher = broadcastDispatcher,
+            devicePolicyManager = devicePolicyManager,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt
index cccd908..ae7d877 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt
@@ -16,7 +16,6 @@
 @OptIn(ExperimentalCoroutinesApi::class)
 class FakeCommunalRepository(
     applicationScope: CoroutineScope,
-    override var isCommunalEnabled: Boolean = true,
     override val desiredScene: MutableStateFlow<CommunalSceneKey> =
         MutableStateFlow(CommunalSceneKey.DEFAULT),
 ) : CommunalRepository {
@@ -40,21 +39,10 @@
         _transitionState.value = transitionState
     }
 
-    fun setIsCommunalEnabled(value: Boolean) {
-        isCommunalEnabled = value
-    }
-
     private val _isCommunalHubShowing: MutableStateFlow<Boolean> = MutableStateFlow(false)
     override val isCommunalHubShowing: Flow<Boolean> = _isCommunalHubShowing
 
     fun setIsCommunalHubShowing(isCommunalHubShowing: Boolean) {
         _isCommunalHubShowing.value = isCommunalHubShowing
     }
-
-    private val _communalEnabledState: MutableStateFlow<Boolean> = MutableStateFlow(false)
-    override val communalEnabledState: StateFlow<Boolean> = _communalEnabledState
-
-    fun setCommunalEnabledState(enabled: Boolean) {
-        _communalEnabledState.value = enabled
-    }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
index c47f020..f7e9a11 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
@@ -27,7 +27,6 @@
 import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.log.logcatLogBuffer
 import com.android.systemui.smartspace.data.repository.smartspaceRepository
-import com.android.systemui.user.data.repository.userRepository
 import com.android.systemui.util.mockito.mock
 
 val Kosmos.communalInteractor by Fixture {
@@ -38,12 +37,12 @@
         mediaRepository = communalMediaRepository,
         communalPrefsRepository = communalPrefsRepository,
         smartspaceRepository = smartspaceRepository,
-        userRepository = userRepository,
         appWidgetHost = mock(),
         keyguardInteractor = keyguardInteractor,
         editWidgetsActivityStarter = editWidgetsActivityStarter,
         logBuffer = logcatLogBuffer("CommunalInteractor"),
         tableLogBuffer = mock(),
+        communalSettingsInteractor = communalSettingsInteractor,
     )
 }
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractorKosmos.kt
new file mode 100644
index 0000000..b4773f6
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractorKosmos.kt
@@ -0,0 +1,33 @@
+/*
+ * 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.communal.domain.interactor
+
+import com.android.systemui.communal.data.repository.communalSettingsRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.user.domain.interactor.selectedUserInteractor
+import com.android.systemui.util.mockito.mock
+
+val Kosmos.communalSettingsInteractor by Fixture {
+    CommunalSettingsInteractor(
+        bgScope = applicationCoroutineScope,
+        repository = communalSettingsRepository,
+        userInteractor = selectedUserInteractor,
+        tableLogBuffer = mock(),
+    )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorKosmos.kt
index 9776b43..00fdced 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorKosmos.kt
@@ -31,6 +31,7 @@
             keyguardInteractor = keyguardInteractor,
             communalRepository = communalRepository,
             communalInteractor = communalInteractor,
+            communalSettingsInteractor = communalSettingsInteractor,
             tableLogBuffer = mock(),
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettingsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettingsKosmos.kt
new file mode 100644
index 0000000..bcb5848
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettingsKosmos.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.util.settings
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+
+val Kosmos.fakeSettings: FakeSettings by Fixture { FakeSettings() }