Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 55c7e006 authored by Darrell Shi's avatar Darrell Shi
Browse files

Use new signal for mediaHostVisible

This change uses a different signal for isMediaHostVisible when
media_controls_in_compose is enabled. This prevents a crash when going
to glanceable hub and scene container is enabled.

It also moves the mediaHost initialization code into a startable rather
than in the constructor of a view model.

Test: atest CommunalViewModelTest
Test: atest CommunalMediaStartableTest
Fix: 441564893
Flag: com.android.systemui.media_controls_in_compose
Change-Id: I1d0d795c7d1d4dc50c063b1ae8701b592878368e
parent 3e4d4369
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -213,7 +213,6 @@ import com.android.systemui.media.remedia.shared.flag.MediaControlsInComposeFlag
import com.android.systemui.media.remedia.ui.compose.Media
import com.android.systemui.media.remedia.ui.compose.MediaPresentationStyle
import com.android.systemui.res.R
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.statusbar.phone.SystemUIDialogFactory
import kotlin.math.max
import kotlin.math.min
@@ -1867,7 +1866,7 @@ private fun Umo(
                }
            }
    ) {
        if (SceneContainerFlag.isEnabled || MediaControlsInComposeFlag.isEnabled) {
        if (MediaControlsInComposeFlag.isEnabled) {
            Media(
                viewModelFactory = viewModel.mediaViewModelFactory,
                presentationStyle = MediaPresentationStyle.Large,
@@ -2205,7 +2204,8 @@ class Dimensions(val context: Context, val config: Configuration) {
    val emptyStatePadding: Dp
        get() {
            return if (
                WindowSizeUtils.getWindowSizeCategory(context) == WindowSizeUtils.WindowSizeCategory.MOBILE_PORTRAIT
                WindowSizeUtils.getWindowSizeCategory(context) ==
                    WindowSizeUtils.WindowSizeCategory.MOBILE_PORTRAIT
            ) {
                24.adjustedDp
            } else {
+63 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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

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_MEDIA_CONTROLS_IN_COMPOSE
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.runTest
import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
import com.android.systemui.media.controls.ui.view.MediaHost
import com.android.systemui.testKosmos
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.any
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.verify

@SmallTest
@RunWith(AndroidJUnit4::class)
class CommunalMediaStartableTest : SysuiTestCase() {
    private val kosmos = testKosmos()

    private val mediaHost = mock<MediaHost>()

    private val Kosmos.underTest by
        Kosmos.Fixture { CommunalMediaStartable(communalSettingsInteractor, mediaHost) }

    @DisableFlags(FLAG_MEDIA_CONTROLS_IN_COMPOSE)
    @Test
    fun onStart_mediaControlsInComposeDisabled_initializeMediaHostForCommunal() =
        kosmos.runTest {
            underTest.start()
            verify(mediaHost).init(MediaHierarchyManager.LOCATION_COMMUNAL_HUB)
        }

    @EnableFlags(FLAG_MEDIA_CONTROLS_IN_COMPOSE)
    @Test
    fun onStart_mediaControlsInComposeEnabled_doNotInitializeMediaHost() =
        kosmos.runTest {
            underTest.start()
            verify(mediaHost, never()).init(any())
        }
}
+41 −21
Original line number Diff line number Diff line
@@ -31,7 +31,9 @@ import com.android.systemui.Flags.FLAG_COMMUNAL_RESPONSIVE_GRID
import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_DIRECT_EDIT_MODE
import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2
import com.android.systemui.Flags.FLAG_HUB_EDIT_MODE_TRANSITION
import com.android.systemui.Flags.FLAG_MEDIA_CONTROLS_IN_COMPOSE
import com.android.systemui.Flags.FLAG_NOTIFICATION_SHADE_BLUR
import com.android.systemui.Flags.FLAG_SCENE_CONTAINER
import com.android.systemui.SysuiTestCase
import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
import com.android.systemui.classifier.domain.interactor.falsingInteractor
@@ -74,6 +76,7 @@ import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.keyguard.ui.transitions.blurConfig
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.advanceTimeBy
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.backgroundScope
@@ -85,10 +88,11 @@ import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.media.controls.domain.pipeline.interactor.mediaCarouselInteractor
import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
import com.android.systemui.media.controls.shared.model.MediaData
import com.android.systemui.media.controls.ui.controller.mediaCarouselController
import com.android.systemui.media.controls.ui.view.MediaCarouselScrollHandler
import com.android.systemui.media.controls.ui.view.MediaHost
import com.android.systemui.media.remedia.data.repository.mediaPipelineRepository
import com.android.systemui.media.remedia.ui.viewmodel.factory.mediaViewModelFactory
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
import com.android.systemui.power.domain.interactor.powerInteractor
@@ -135,7 +139,7 @@ class CommunalViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {

    private val kosmos = testKosmos()

    private lateinit var underTest: CommunalViewModel
    private val Kosmos.underTest by Kosmos.Fixture { createViewModel() }

    init {
        mSetFlagsRule.setFlagsParameterization(flags)
@@ -152,8 +156,6 @@ class CommunalViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
            .thenReturn(mediaCarouselScrollHandler)

        kosmos.powerInteractor.setAwakeForTest()

        underTest = createViewModel()
    }

    private fun createViewModel(): CommunalViewModel {
@@ -182,13 +184,6 @@ class CommunalViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
        )
    }

    @Test
    fun init_initsMediaHost() =
        kosmos.runTest {
            // MediaHost is initialized as soon as the class is created.
            verify(mediaHost).init(MediaHierarchyManager.LOCATION_COMMUNAL_HUB)
        }

    @Test
    fun tutorial_tutorialNotCompletedAndKeyguardVisible_showTutorialContent() =
        kosmos.runTest {
@@ -207,7 +202,7 @@ class CommunalViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
        }

    @Test
    @DisableFlags(FLAG_GLANCEABLE_HUB_V2)
    @DisableFlags(FLAG_GLANCEABLE_HUB_V2, FLAG_MEDIA_CONTROLS_IN_COMPOSE, FLAG_SCENE_CONTAINER)
    fun ordering_smartspaceBeforeUmoBeforeWidgetsBeforeCtaTile() =
        kosmos.runTest {
            fakeCommunalTutorialRepository.setTutorialSettingState(
@@ -249,7 +244,12 @@ class CommunalViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {

    /** TODO(b/378171351): Handle ongoing content in responsive grid. */
    @Test
    @DisableFlags(FLAG_COMMUNAL_RESPONSIVE_GRID, FLAG_GLANCEABLE_HUB_V2)
    @DisableFlags(
        FLAG_COMMUNAL_RESPONSIVE_GRID,
        FLAG_GLANCEABLE_HUB_V2,
        FLAG_MEDIA_CONTROLS_IN_COMPOSE,
        FLAG_SCENE_CONTAINER,
    )
    fun ongoingContent_umoAndOneTimer_sizedAppropriately() =
        kosmos.runTest {
            // Widgets available.
@@ -287,7 +287,12 @@ class CommunalViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {

    /** TODO(b/378171351): Handle ongoing content in responsive grid. */
    @Test
    @DisableFlags(FLAG_COMMUNAL_RESPONSIVE_GRID, FLAG_GLANCEABLE_HUB_V2)
    @DisableFlags(
        FLAG_COMMUNAL_RESPONSIVE_GRID,
        FLAG_GLANCEABLE_HUB_V2,
        FLAG_MEDIA_CONTROLS_IN_COMPOSE,
        FLAG_SCENE_CONTAINER,
    )
    fun ongoingContent_umoAndTwoTimers_sizedAppropriately() =
        kosmos.runTest {
            // Widgets available.
@@ -334,6 +339,19 @@ class CommunalViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {

    @Test
    @DisableFlags(FLAG_GLANCEABLE_HUB_V2)
    @EnableFlags(FLAG_MEDIA_CONTROLS_IN_COMPOSE)
    fun communalContent_mediaHostVisible_mediaControlsInCompose_umoIncluded() =
        kosmos.runTest {
            mediaPipelineRepository.addCurrentUserMediaEntry(MediaData().copy(active = true))
            fakeCommunalMediaRepository.mediaActive()

            val communalContent by collectLastValue(underTest.communalContent)
            assertThat(communalContent?.size).isEqualTo(2)
            assertThat(communalContent?.get(0)).isInstanceOf(CommunalContentModel.Umo::class.java)
        }

    @Test
    @DisableFlags(FLAG_GLANCEABLE_HUB_V2, FLAG_MEDIA_CONTROLS_IN_COMPOSE, FLAG_SCENE_CONTAINER)
    fun communalContent_mediaHostVisible_umoIncluded() =
        kosmos.runTest {
            // Media playing.
@@ -345,8 +363,8 @@ class CommunalViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
        }

    @Test
    @DisableFlags(FLAG_GLANCEABLE_HUB_V2)
    fun communalContent_mediaHostVisible_umoExcluded() =
    @DisableFlags(FLAG_GLANCEABLE_HUB_V2, FLAG_MEDIA_CONTROLS_IN_COMPOSE, FLAG_SCENE_CONTAINER)
    fun communalContent_mediaHostNotVisible_umoExcluded() =
        kosmos.runTest {
            whenever(mediaHost.visible).thenReturn(false)
            mediaHost.updateViewVisibility()
@@ -356,7 +374,7 @@ class CommunalViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
            val communalContent by collectLastValue(underTest.communalContent)
            assertThat(communalContent?.size).isEqualTo(1)
            assertThat(communalContent?.get(0))
                .isInstanceOf(CommunalContentModel.CtaTileInViewMode::class.java)
                .isNotInstanceOf(CommunalContentModel.Umo::class.java)
        }

    @Test
@@ -873,6 +891,7 @@ class CommunalViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
        }

    @Test
    @DisableFlags(FLAG_MEDIA_CONTROLS_IN_COMPOSE, FLAG_SCENE_CONTAINER)
    fun communalContent_readTriggersUmoVisibilityUpdate() =
        kosmos.runTest {
            verify(mediaHost, never()).updateViewVisibility()
@@ -897,7 +916,8 @@ class CommunalViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
        }

    @Test
    fun onTapWidget_logEvent() {
    fun onTapWidget_logEvent() =
        kosmos.runTest {
            underTest.onTapWidget(ComponentName("test_pkg", "test_cls"), rank = 10)
            verify(metricsLogger).logTapWidget("test_pkg/test_cls", rank = 10)
        }
+55 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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

import com.android.systemui.CoreStartable
import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
import com.android.systemui.media.controls.ui.view.MediaHost
import com.android.systemui.media.controls.ui.view.MediaHostState
import com.android.systemui.media.dagger.MediaModule
import com.android.systemui.media.remedia.shared.flag.MediaControlsInComposeFlag
import javax.inject.Inject
import javax.inject.Named

@SysUISingleton
class CommunalMediaStartable
@Inject
constructor(
    private val communalSettingsInteractor: CommunalSettingsInteractor,
    @Named(MediaModule.COMMUNAL_HUB) private val mediaHost: MediaHost,
) : CoreStartable {
    override fun start() {
        if (MediaControlsInComposeFlag.isEnabled) {
            return
        }

        // Initialize our media host for the UMO. This only needs to happen once and must be done
        // before the MediaHierarchyManager attempts to move the UMO to the hub.
        with(mediaHost) {
            expansion = MediaHostState.EXPANDED
            expandedMatchesParentHeight = true
            // When V2 is enabled, only show active media to match lock screen, non-resumable media,
            // which can persist for up to 2 days.
            showsOnlyActiveMedia = communalSettingsInteractor.isV2FlagEnabled()
            falsingProtectionNeeded = false
            disableScrolling = true
            init(MediaHierarchyManager.LOCATION_COMMUNAL_HUB)
        }
    }
}
+6 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.systemui.communal.dagger
import com.android.systemui.CoreStartable
import com.android.systemui.communal.CommunalBackupRestoreStartable
import com.android.systemui.communal.CommunalDreamStartable
import com.android.systemui.communal.CommunalMediaStartable
import com.android.systemui.communal.CommunalMetricsStartable
import com.android.systemui.communal.CommunalOngoingContentStartable
import com.android.systemui.communal.CommunalSceneStartable
@@ -49,6 +50,11 @@ interface CommunalStartableModule {
    @ClassKey(CommunalDreamStartable::class)
    fun bindCommunalDreamStartable(impl: CommunalDreamStartable): CoreStartable

    @Binds
    @IntoMap
    @ClassKey(CommunalMediaStartable::class)
    fun bindCommunalMediaStartable(impl: CommunalMediaStartable): CoreStartable

    @Binds
    @IntoMap
    @PerUser
Loading