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

Commit 7edaa84e authored by Nicolo' Mazzucato's avatar Nicolo' Mazzucato
Browse files

Fix SceneContainer SysUI state override for multiple displays

This makes sure that we're only overriding flags for the display the shade is at. For all other displays, shade related flags in SysUIState are supposed to be false.

This holds as long as we're not having a SceneContainer per display.

Bug: 362719719
Bug: 398011576
Test: SceneContainerPluginTest
Flag: com.android.systemui.shade_window_goes_around
Change-Id: Ib07a5fa7469160fe591f1e87ea8332bdedacc87d
parent 82e14c21
Loading
Loading
Loading
Loading
+93 −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.model

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
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.shared.model.fakeSceneDataSource
import com.android.systemui.shade.data.repository.fakeShadeDisplaysRepository
import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
import org.junit.runner.RunWith

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

    private val shadeDisplayRepository = kosmos.fakeShadeDisplaysRepository
    private val sceneDataSource = kosmos.fakeSceneDataSource

    private val underTest = kosmos.sceneContainerPlugin

    @Test
    @EnableFlags(Flags.FLAG_SHADE_WINDOW_GOES_AROUND)
    fun flagValueOverride_differentDisplayId_alwaysFalse() {
        sceneDataSource.changeScene(Scenes.Shade)

        shadeDisplayRepository.setDisplayId(1)

        assertThat(
                underTest.flagValueOverride(
                    flag = SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE,
                    displayId = 2,
                )
            )
            .isFalse()
    }

    @Test
    @EnableFlags(Flags.FLAG_SHADE_WINDOW_GOES_AROUND)
    fun flagValueOverride_sameDisplayId_returnsTrue() {
        sceneDataSource.changeScene(Scenes.Shade)

        shadeDisplayRepository.setDisplayId(1)

        assertThat(
                underTest.flagValueOverride(
                    flag = SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE,
                    displayId = 1,
                )
            )
            .isTrue()
    }

    @Test
    @DisableFlags(Flags.FLAG_SHADE_WINDOW_GOES_AROUND)
    fun flagValueOverride_differentDisplayId_shadeGoesAroundFlagOff_returnsTrue() {
        sceneDataSource.changeScene(Scenes.Shade)

        shadeDisplayRepository.setDisplayId(1)

        assertThat(
                underTest.flagValueOverride(
                    flag = SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE,
                    displayId = 2,
                )
            )
            .isTrue()
    }
}
+15 −1
Original line number Diff line number Diff line
@@ -25,6 +25,8 @@ import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.data.repository.ShadeDisplaysRepository
import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround
import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING
import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_COMMUNAL_HUB_SHOWING
import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED
@@ -35,6 +37,7 @@ import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_B
import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags
import dagger.Lazy
import javax.inject.Inject
import kotlinx.coroutines.flow.StateFlow

/**
 * A plugin for [SysUiState] that provides overrides for certain state flags that must be pulled
@@ -46,17 +49,28 @@ class SceneContainerPlugin
constructor(
    private val sceneInteractor: Lazy<SceneInteractor>,
    private val occlusionInteractor: Lazy<SceneContainerOcclusionInteractor>,
    private val shadeDisplaysRepository: Lazy<ShadeDisplaysRepository>,
) {

    private val shadeDisplayId: StateFlow<Int> by lazy { shadeDisplaysRepository.get().displayId }

    /**
     * Returns an override value for the given [flag] or `null` if the scene framework isn't enabled
     * or if the flag value doesn't need to be overridden.
     */
    fun flagValueOverride(@SystemUiStateFlags flag: Long): Boolean? {
    fun flagValueOverride(@SystemUiStateFlags flag: Long, displayId: Int): Boolean? {
        if (!SceneContainerFlag.isEnabled) {
            return null
        }

        if (ShadeWindowGoesAround.isEnabled && shadeDisplayId.value != displayId) {
            // The shade is in another display. All flags related to the shade container will map to
            // false on other displays now.
            //
            // Note that this assumes there is only one SceneContainer and it is only on the shade
            // window display. If there will be more, this will need to be revisited
            return false
        }
        val transitionState = sceneInteractor.get().transitionState.value
        val idleTransitionStateOrNull = transitionState as? ObservableTransitionState.Idle
        val invisibleDueToOcclusion = occlusionInteractor.get().invisibleDueToOcclusion.value
+2 −1
Original line number Diff line number Diff line
@@ -132,7 +132,8 @@ constructor(
    /** Methods to this call can be chained together before calling [.commitUpdate]. */
    override fun setFlag(@SystemUiStateFlags flag: Long, enabled: Boolean): SysUiState {
        var enabled = enabled
        val overrideOrNull = sceneContainerPlugin?.flagValueOverride(flag)
        val overrideOrNull =
            sceneContainerPlugin?.flagValueOverride(flag = flag, displayId = displayId)
        if (overrideOrNull != null && enabled != overrideOrNull) {
            if (SysUiState.DEBUG) {
                Log.d(
+2 −0
Original line number Diff line number Diff line
@@ -20,10 +20,12 @@ import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.scene.domain.interactor.sceneContainerOcclusionInteractor
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.shade.data.repository.fakeShadeDisplaysRepository

val Kosmos.sceneContainerPlugin by Fixture {
    SceneContainerPlugin(
        sceneInteractor = { sceneInteractor },
        occlusionInteractor = { sceneContainerOcclusionInteractor },
        shadeDisplaysRepository = { fakeShadeDisplaysRepository },
    )
}