Update media carousel visibility for glanceable hub.

This changelist accounts for the glanceable hub presence for determining
whether the media carousel is visible. The visibility state is
responsible for ensuring the carousel is properly configured when shown.

Test: atest testCommunalLocationVisibilityWithShadeShowing
Test: atest testCommunalLocationVisibilityWithPrimaryBouncerShowing
Flag: EXEMPT bugfix
Fixes: 364488347
Change-Id: I97a8e70bcdb68a8740eab15df2cb14f5a94b15f6
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
index a9d2a54..38cea5b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
@@ -64,6 +64,7 @@
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.flow.collectLatest
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
@@ -431,6 +432,12 @@
     /** Is the communal UI showing */
     private var isCommunalShowing: Boolean = false
 
+    /** Is the primary bouncer showing */
+    private var isPrimaryBouncerShowing: Boolean = false
+
+    /** Is either shade or QS fully expanded */
+    private var isAnyShadeFullyExpanded: Boolean = false
+
     /** Is the communal UI showing and not dreaming */
     private var onCommunalNotDreaming: Boolean = false
 
@@ -587,6 +594,20 @@
             }
         }
 
+        coroutineScope.launch {
+            shadeInteractor.isAnyFullyExpanded.collect {
+                isAnyShadeFullyExpanded = it
+                updateUserVisibility()
+            }
+        }
+
+        coroutineScope.launch {
+            keyguardInteractor.primaryBouncerShowing.collect {
+                isPrimaryBouncerShowing = it
+                updateUserVisibility()
+            }
+        }
+
         if (mediaControlsLockscreenShadeBugFix()) {
             coroutineScope.launch {
                 shadeInteractor.shadeExpansion.collect { expansion ->
@@ -638,6 +659,7 @@
                         communalShowing && isDreaming && isShadeExpanding
                     onCommunalNotDreaming = communalShowing && !isDreaming
                     updateDesiredLocation(forceNoAnimation = true)
+                    updateUserVisibility()
                 }
         }
     }
@@ -1290,7 +1312,8 @@
         val shadeVisible =
             isLockScreenVisibleToUser() ||
                 isLockScreenShadeVisibleToUser() ||
-                isHomeScreenShadeVisibleToUser()
+                isHomeScreenShadeVisibleToUser() ||
+                isGlanceableHubVisibleToUser()
         val mediaVisible = qsExpanded || hasActiveMediaOrRecommendation
         mediaCarouselController.mediaCarouselScrollHandler.visibleToUser =
             shadeVisible && mediaVisible
@@ -1318,6 +1341,10 @@
             statusBarStateController.isExpanded
     }
 
+    private fun isGlanceableHubVisibleToUser(): Boolean {
+        return isCommunalShowing && !isPrimaryBouncerShowing && !isAnyShadeFullyExpanded
+    }
+
     companion object {
         /** Attached in expanded quick settings */
         const val LOCATION_QS = 0
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt
index 2370bca..0508c2c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt
@@ -25,6 +25,7 @@
 import androidx.test.filters.SmallTest
 import com.android.keyguard.KeyguardViewController
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.bouncer.data.repository.keyguardBouncerRepository
 import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository
 import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.communal.ui.viewmodel.communalTransitionViewModel
@@ -34,6 +35,7 @@
 import com.android.systemui.keyguard.WakefulnessLifecycle
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.keyguardRepository
 import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.kosmos.testScope
@@ -80,6 +82,8 @@
 import org.mockito.junit.MockitoJUnit
 import org.mockito.kotlin.any
 import org.mockito.kotlin.anyOrNull
+import org.mockito.kotlin.atLeastOnce
+import org.mockito.kotlin.lastValue
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
@@ -118,6 +122,7 @@
     private lateinit var mediaHierarchyManager: MediaHierarchyManager
     private lateinit var isQsBypassingShade: MutableStateFlow<Boolean>
     private lateinit var shadeExpansion: MutableStateFlow<Float>
+    private lateinit var anyShadeExpanded: MutableStateFlow<Boolean>
     private lateinit var mediaFrame: ViewGroup
     private val configurationController = FakeConfigurationController()
     private val settings = FakeSettings()
@@ -137,8 +142,10 @@
         whenever(mediaCarouselController.mediaFrame).thenReturn(mediaFrame)
         isQsBypassingShade = MutableStateFlow(false)
         shadeExpansion = MutableStateFlow(0f)
+        anyShadeExpanded = MutableStateFlow(false)
         whenever(shadeInteractor.isQsBypassingShade).thenReturn(isQsBypassingShade)
         whenever(shadeInteractor.shadeExpansion).thenReturn(shadeExpansion)
+        whenever(shadeInteractor.isAnyFullyExpanded).thenReturn(anyShadeExpanded)
         mediaHierarchyManager =
             MediaHierarchyManager(
                 context,
@@ -574,6 +581,72 @@
         }
 
     @Test
+    fun testCommunalLocationVisibilityWithShadeShowing() =
+        testScope.runTest {
+            whenever(mediaDataManager.hasActiveMediaOrRecommendation()).thenReturn(true)
+            keyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.GLANCEABLE_HUB,
+                testScope = testScope,
+            )
+            kosmos.fakeCommunalSceneRepository.changeScene(CommunalScenes.Communal)
+            runCurrent()
+            verify(mediaCarouselController)
+                .onDesiredLocationChanged(
+                    eq(MediaHierarchyManager.LOCATION_COMMUNAL_HUB),
+                    nullable(),
+                    eq(false),
+                    anyLong(),
+                    anyLong()
+                )
+
+            val captor = ArgumentCaptor.forClass(Boolean::class.java)
+            verify(mediaCarouselScrollHandler, atLeastOnce()).visibleToUser = captor.capture()
+
+            assertThat(captor.lastValue).isTrue()
+
+            clearInvocations(mediaCarouselScrollHandler)
+            anyShadeExpanded.value = true
+            runCurrent()
+            verify(mediaCarouselScrollHandler, atLeastOnce()).visibleToUser = captor.capture()
+
+            assertThat(captor.lastValue).isFalse()
+        }
+
+    @Test
+    fun testCommunalLocationVisibilityWithPrimaryBouncerShowing() =
+        testScope.runTest {
+            whenever(mediaDataManager.hasActiveMediaOrRecommendation()).thenReturn(true)
+            keyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.GLANCEABLE_HUB,
+                testScope = testScope,
+            )
+            kosmos.fakeCommunalSceneRepository.changeScene(CommunalScenes.Communal)
+            runCurrent()
+            verify(mediaCarouselController)
+                .onDesiredLocationChanged(
+                    eq(MediaHierarchyManager.LOCATION_COMMUNAL_HUB),
+                    nullable(),
+                    eq(false),
+                    anyLong(),
+                    anyLong()
+                )
+
+            val captor = ArgumentCaptor.forClass(Boolean::class.java)
+            verify(mediaCarouselScrollHandler, atLeastOnce()).visibleToUser = captor.capture()
+
+            assertThat(captor.lastValue).isTrue()
+
+            clearInvocations(mediaCarouselScrollHandler)
+            kosmos.keyguardBouncerRepository.setPrimaryShow(true)
+            runCurrent()
+            verify(mediaCarouselScrollHandler, atLeastOnce()).visibleToUser = captor.capture()
+
+            assertThat(captor.lastValue).isFalse()
+        }
+
+    @Test
     fun testCommunalLocation_showsOverLockscreen() =
         testScope.runTest {
             // Device is on lock screen.