Loading packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt +28 −1 Original line number Diff line number Diff line Loading @@ -64,6 +64,7 @@ import com.android.systemui.util.settings.SecureSettings 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 Loading Loading @@ -431,6 +432,12 @@ constructor( /** 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 Loading Loading @@ -587,6 +594,20 @@ constructor( } } coroutineScope.launch { shadeInteractor.isAnyFullyExpanded.collect { isAnyShadeFullyExpanded = it updateUserVisibility() } } coroutineScope.launch { keyguardInteractor.primaryBouncerShowing.collect { isPrimaryBouncerShowing = it updateUserVisibility() } } if (mediaControlsLockscreenShadeBugFix()) { coroutineScope.launch { shadeInteractor.shadeExpansion.collect { expansion -> Loading Loading @@ -638,6 +659,7 @@ constructor( communalShowing && isDreaming && isShadeExpanding onCommunalNotDreaming = communalShowing && !isDreaming updateDesiredLocation(forceNoAnimation = true) updateUserVisibility() } } } Loading Loading @@ -1290,7 +1312,8 @@ constructor( val shadeVisible = isLockScreenVisibleToUser() || isLockScreenShadeVisibleToUser() || isHomeScreenShadeVisibleToUser() isHomeScreenShadeVisibleToUser() || isGlanceableHubVisibleToUser() val mediaVisible = qsExpanded || hasActiveMediaOrRecommendation mediaCarouselController.mediaCarouselScrollHandler.visibleToUser = shadeVisible && mediaVisible Loading Loading @@ -1318,6 +1341,10 @@ constructor( statusBarStateController.isExpanded } private fun isGlanceableHubVisibleToUser(): Boolean { return isCommunalShowing && !isPrimaryBouncerShowing && !isAnyShadeFullyExpanded } companion object { /** Attached in expanded quick settings */ const val LOCATION_QS = 0 Loading packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt +73 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 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 Loading @@ -34,6 +35,7 @@ import com.android.systemui.flags.DisableSceneContainer 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 Loading Loading @@ -80,6 +82,8 @@ import org.mockito.Mockito.`when` as whenever 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 Loading Loading @@ -118,6 +122,7 @@ class MediaHierarchyManagerTest : SysuiTestCase() { 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() Loading @@ -137,8 +142,10 @@ class MediaHierarchyManagerTest : SysuiTestCase() { 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, Loading Loading @@ -573,6 +580,72 @@ class MediaHierarchyManagerTest : SysuiTestCase() { ) } @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 { Loading Loading
packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt +28 −1 Original line number Diff line number Diff line Loading @@ -64,6 +64,7 @@ import com.android.systemui.util.settings.SecureSettings 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 Loading Loading @@ -431,6 +432,12 @@ constructor( /** 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 Loading Loading @@ -587,6 +594,20 @@ constructor( } } coroutineScope.launch { shadeInteractor.isAnyFullyExpanded.collect { isAnyShadeFullyExpanded = it updateUserVisibility() } } coroutineScope.launch { keyguardInteractor.primaryBouncerShowing.collect { isPrimaryBouncerShowing = it updateUserVisibility() } } if (mediaControlsLockscreenShadeBugFix()) { coroutineScope.launch { shadeInteractor.shadeExpansion.collect { expansion -> Loading Loading @@ -638,6 +659,7 @@ constructor( communalShowing && isDreaming && isShadeExpanding onCommunalNotDreaming = communalShowing && !isDreaming updateDesiredLocation(forceNoAnimation = true) updateUserVisibility() } } } Loading Loading @@ -1290,7 +1312,8 @@ constructor( val shadeVisible = isLockScreenVisibleToUser() || isLockScreenShadeVisibleToUser() || isHomeScreenShadeVisibleToUser() isHomeScreenShadeVisibleToUser() || isGlanceableHubVisibleToUser() val mediaVisible = qsExpanded || hasActiveMediaOrRecommendation mediaCarouselController.mediaCarouselScrollHandler.visibleToUser = shadeVisible && mediaVisible Loading Loading @@ -1318,6 +1341,10 @@ constructor( statusBarStateController.isExpanded } private fun isGlanceableHubVisibleToUser(): Boolean { return isCommunalShowing && !isPrimaryBouncerShowing && !isAnyShadeFullyExpanded } companion object { /** Attached in expanded quick settings */ const val LOCATION_QS = 0 Loading
packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt +73 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 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 Loading @@ -34,6 +35,7 @@ import com.android.systemui.flags.DisableSceneContainer 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 Loading Loading @@ -80,6 +82,8 @@ import org.mockito.Mockito.`when` as whenever 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 Loading Loading @@ -118,6 +122,7 @@ class MediaHierarchyManagerTest : SysuiTestCase() { 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() Loading @@ -137,8 +142,10 @@ class MediaHierarchyManagerTest : SysuiTestCase() { 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, Loading Loading @@ -573,6 +580,72 @@ class MediaHierarchyManagerTest : SysuiTestCase() { ) } @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 { Loading