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

Commit 9dbdcb91 authored by Nicolo' Mazzucato's avatar Nicolo' Mazzucato
Browse files

Fix per-display flags propagation in flexiglass

Flags were only kept up to date for the default display, causing elements relying on them to broken on external displays (e.g. taskbar visibility)

Bug: 362719719
Bug: 417921986
Test: SceneContainerStartableTest
Flag: com.android.systemui.shade_window_goes_around
Change-Id: I970015c26cf43d542780b1eb09884769fe52b975
parent 25af1134
Loading
Loading
Loading
Loading
+47 −2
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import android.os.PowerManager
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.provider.Settings
import android.view.Display
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.ObservableTransitionState
@@ -61,6 +62,7 @@ import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteract
import com.android.systemui.deviceentry.shared.model.DeviceUnlockStatus
import com.android.systemui.deviceentry.shared.model.FailedFaceAuthenticationStatus
import com.android.systemui.deviceentry.shared.model.SuccessFaceAuthenticationStatus
import com.android.systemui.display.data.repository.displayRepository
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.haptics.msdl.fakeMSDLPlayer
import com.android.systemui.haptics.vibratorHelper
@@ -88,6 +90,7 @@ import com.android.systemui.kosmos.collectLastValue
import com.android.systemui.kosmos.runCurrent
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testScope
import com.android.systemui.model.fakeSysUIStatePerDisplayRepository
import com.android.systemui.model.sysUiState
import com.android.systemui.power.data.repository.fakePowerRepository
import com.android.systemui.power.data.repository.powerRepository
@@ -103,6 +106,7 @@ import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.model.Overlays
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.shade.domain.interactor.disableDualShade
import com.android.systemui.shade.domain.interactor.enableDualShade
import com.android.systemui.shade.domain.interactor.shadeInteractor
@@ -128,6 +132,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runCurrent
@@ -162,7 +167,6 @@ class SceneContainerStartableTest : SysuiTestCase() {
    private val bouncerInteractor by lazy { kosmos.bouncerInteractor }
    private val faceAuthRepository by lazy { kosmos.fakeDeviceEntryFaceAuthRepository }
    private val bouncerRepository by lazy { kosmos.fakeKeyguardBouncerRepository }
    private val sysUiState = kosmos.sysUiState
    private val falsingCollector = mock<FalsingCollector>().also { kosmos.falsingCollector = it }
    private val vibratorHelper = mock<VibratorHelper>().also { kosmos.vibratorHelper = it }
    private val fakeSceneDataSource = kosmos.fakeSceneDataSource
@@ -174,6 +178,9 @@ class SceneContainerStartableTest : SysuiTestCase() {
    private val msdlPlayer = kosmos.fakeMSDLPlayer
    private val authInteractionProperties = AuthInteractionProperties()
    private val mockActivityTransitionAnimator = mock<ActivityTransitionAnimator>()
    private val sysuiStateRepository = kosmos.fakeSysUIStatePerDisplayRepository
    private val sysUiState = sysuiStateRepository[Display.DEFAULT_DISPLAY]!!
    private val secondaryDisplaySysUIState = sysuiStateRepository[SECONDARY_DISPLAY]!!

    private lateinit var underTest: SceneContainerStartable

@@ -183,7 +190,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
        whenever(kosmos.keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean()))
            .thenReturn(true)
        kosmos.activityTransitionAnimator = mockActivityTransitionAnimator

        runBlocking { kosmos.displayRepository.addDisplay(Display.DEFAULT_DISPLAY) }
        underTest = kosmos.sceneContainerStartable
    }

@@ -1163,14 +1170,48 @@ class SceneContainerStartableTest : SysuiTestCase() {
                    sceneInteractor.changeScene(sceneKey, "reason")
                    runCurrent()
                    verify(sysUiState, times(index)).commitUpdate()
                    verify(secondaryDisplaySysUIState, never()).commitUpdate()

                    fakeSceneDataSource.unpause(expectedScene = sceneKey)
                    runCurrent()
                    verify(sysUiState, times(index)).commitUpdate()
                    verify(secondaryDisplaySysUIState, never()).commitUpdate()

                    transitionStateFlow.value = ObservableTransitionState.Idle(sceneKey)
                    runCurrent()
                    verify(sysUiState, times(index + 1)).commitUpdate()
                    verify(secondaryDisplaySysUIState, never()).commitUpdate()
                }
        }

    @Test
    fun hydrateSystemUiState_onSecondaryDisplay() =
        testScope.runTest {
            val transitionStateFlow = prepareState()
            kosmos.displayRepository.addDisplay(SECONDARY_DISPLAY)
            kosmos.fakeShadeDisplaysRepository.setPendingDisplayId(SECONDARY_DISPLAY)
            underTest.start()
            runCurrent()
            clearInvocations(secondaryDisplaySysUIState)

            listOf(Scenes.Gone, Scenes.Lockscreen, Scenes.Gone, Scenes.Shade, Scenes.QuickSettings)
                .forEachIndexed { index, sceneKey ->
                    if (sceneKey == Scenes.Gone) {
                        updateFingerprintAuthStatus(isSuccess = true)
                        runCurrent()
                    }
                    fakeSceneDataSource.pause()
                    sceneInteractor.changeScene(sceneKey, "reason")
                    runCurrent()
                    verify(secondaryDisplaySysUIState, times(index)).commitUpdate()

                    fakeSceneDataSource.unpause(expectedScene = sceneKey)
                    runCurrent()
                    verify(secondaryDisplaySysUIState, times(index)).commitUpdate()

                    transitionStateFlow.value = ObservableTransitionState.Idle(sceneKey)
                    runCurrent()
                    verify(secondaryDisplaySysUIState, times(index + 1)).commitUpdate()
                }
        }

@@ -3028,4 +3069,8 @@ class SceneContainerStartableTest : SysuiTestCase() {
            )
        }
    }

    private companion object {
        const val SECONDARY_DISPLAY = Display.DEFAULT_DISPLAY + 1
    }
}
+3 −0
Original line number Diff line number Diff line
@@ -42,6 +42,9 @@ import kotlinx.coroutines.flow.StateFlow
/**
 * A plugin for [SysUiState] that provides overrides for certain state flags that must be pulled
 * from the scene framework when that framework is enabled.
 *
 * Note that those flags only apply to the display id containing the shade window, as defined by
 * [com.android.systemui.shade.domain.interactor.ShadeDisplaysInteractor.displayId]
 */
interface SceneContainerPlugin {
    /**
+7 −0
Original line number Diff line number Diff line
@@ -94,4 +94,11 @@ class StateChange {
            )
        })"""
    }

    companion object {
        /** Creates a [StateChange] from a list of pairs. */
        fun from(iterable: Iterable<Pair<Long, Boolean>>): StateChange {
            return StateChange().apply { iterable.forEach { (bit, state) -> setFlag(bit, state) } }
        }
    }
}
+30 −15
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.systemui.scene.domain.startable

import android.app.StatusBarManager
import android.view.Display
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.compose.animation.scene.OverlayKey
import com.android.compose.animation.scene.SceneKey
@@ -33,6 +34,7 @@ import com.android.systemui.bouncer.domain.interactor.SimBouncerInteractor
import com.android.systemui.bouncer.shared.logging.BouncerUiEvent
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.classifier.FalsingCollectorActual
import com.android.systemui.common.domain.interactor.SysUIStateDisplaysInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
@@ -48,8 +50,8 @@ import com.android.systemui.keyguard.domain.interactor.WindowManagerLockscreenVi
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.model.SceneContainerPlugin
import com.android.systemui.model.SceneContainerPluginImpl
import com.android.systemui.model.StateChange
import com.android.systemui.model.SysUiState
import com.android.systemui.model.updateFlags
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.FalsingManager.FalsingBeliefListener
import com.android.systemui.power.domain.interactor.PowerInteractor
@@ -67,8 +69,10 @@ import com.android.systemui.scene.shared.logger.SceneLogger
import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.shared.model.SceneFamilies
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.domain.interactor.ShadeDisplaysInteractor
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shade.domain.interactor.ShadeModeInteractor
import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.statusbar.VibratorHelper
@@ -122,7 +126,6 @@ constructor(
    private val deviceUnlockedInteractor: DeviceUnlockedInteractor,
    private val bouncerInteractor: BouncerInteractor,
    private val keyguardInteractor: KeyguardInteractor,
    private val sysUiState: SysUiState,
    private val sceneLogger: SceneLogger,
    @FalsingCollectorActual private val falsingCollector: FalsingCollector,
    private val falsingManager: FalsingManager,
@@ -150,12 +153,21 @@ constructor(
    private val shadeModeInteractor: ShadeModeInteractor,
    @SceneFrameworkTableLog private val tableLogBuffer: TableLogBuffer,
    private val trustInteractor: TrustInteractor,
    private val sysuiStateInteractor: SysUIStateDisplaysInteractor,
    private val shadeDisplaysInteractor: Lazy<ShadeDisplaysInteractor>,
) : CoreStartable {
    private val centralSurfaces: CentralSurfaces?
        get() = centralSurfacesOptLazy.get().getOrNull()

    private val authInteractionProperties = AuthInteractionProperties()

    private val shadePendingDisplayId: Flow<Int> =
        if (ShadeWindowGoesAround.isEnabled) {
            shadeDisplaysInteractor.get().pendingDisplayId
        } else {
            flowOf(Display.DEFAULT_DISPLAY)
        }

    override fun start() {
        if (SceneContainerFlag.isEnabled) {
            sceneLogger.logFrameworkEnabled(isEnabled = true)
@@ -759,24 +771,27 @@ constructor(
                        .distinctUntilChanged(),
                    sceneInteractor.isVisible,
                    occlusionInteractor.invisibleDueToOcclusion,
                ) { idleState, isVisible, invisibleDueToOcclusion ->
                    shadePendingDisplayId,
                ) { idleState, isVisible, invisibleDueToOcclusion, displayId ->
                    displayId to
                        SceneContainerPlugin.SceneContainerPluginState(
                            scene = idleState.currentScene,
                            overlays = idleState.currentOverlays,
                        isVisible = isVisible,
                            invisibleDueToOcclusion = invisibleDueToOcclusion,
                            isVisible = isVisible,
                        )
                }
                .map { sceneContainerPluginState ->
                .map { (displayId, sceneContainerPluginState) ->
                    displayId to
                        SceneContainerPluginImpl.EvaluatorByFlag.map { (flag, evaluator) ->
                            flag to evaluator(sceneContainerPluginState)
                        }
                        .toMap()
                }
                .distinctUntilChanged()
                .collect { flags ->
                    sysUiState.updateFlags(
                        *(flags.entries.map { (key, value) -> key to value }).toTypedArray()
                .collect { (displayId: Int, flagMap) ->
                    sysuiStateInteractor.setFlagsExclusivelyToDisplay(
                        targetDisplayId = displayId,
                        stateChanges = StateChange.from(flagMap),
                    )
                }
        }
+3 −1
Original line number Diff line number Diff line
@@ -39,7 +39,9 @@ val Kosmos.sysUiStateFactory by Fixture {
}

val Kosmos.fakeSysUIStatePerDisplayRepository by Fixture {
    FakePerDisplayRepository<SysUiState>().apply { add(Display.DEFAULT_DISPLAY, sysUiState) }
    FakePerDisplayRepository<SysUiState>(defaultIfAbsent = { sysUiStateFactory.create(it) }).apply {
        add(Display.DEFAULT_DISPLAY, sysUiState)
    }
}

val Kosmos.sysuiStateInteractor by Fixture {
Loading