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

Commit b26d2514 authored by Nicolo' Mazzucato's avatar Nicolo' Mazzucato
Browse files

Fix part of dual shade insets flicker when window moves to a different display

When the shade moves to a different display there is a 3 frames flicker where the shade header jumps around.

This cl addresses the first frame flicker, that had wrong shade header position as the displayCutout and cornerRadius StateFlows were being re-created at every root view recomposition with the wrong initial values.

With this cl:
- The flows are created once, outside the composable
- corner radius is updated every time the shade moves to a different display (e.g. external displays normally don't have any corner radius, and this was triggering a wrong offset)

The remaining 2 frame flickers will be addressed in a follow up.

Bug: 362719719
Bug: 417960167
Flag: com.android.systemui.scene_container
Test: Move shade window between displays with dual shade enabled -> Check the first frame flicker doesn't happen
Change-Id: I6b8f8f5c1d44f2e4b996c55faac7cbdc21072640
parent 3ce3461d
Loading
Loading
Loading
Loading
+4 −2
Original line number Diff line number Diff line
@@ -34,11 +34,13 @@ val LocalScreenCornerRadius = staticCompositionLocalOf { 0.dp }
@Composable
fun ScreenDecorProvider(
    displayCutout: StateFlow<DisplayCutout>,
    screenCornerRadius: Float,
    screenCornerRadius: StateFlow<Float>,
    content: @Composable () -> Unit,
) {
    val cutout by displayCutout.collectAsStateWithLifecycle()
    val screenCornerRadiusDp = with(LocalDensity.current) { screenCornerRadius.toDp() }
    val screenCornerRadiusPx by screenCornerRadius.collectAsStateWithLifecycle()

    val screenCornerRadiusDp = with(LocalDensity.current) { screenCornerRadiusPx.toDp() }

    CompositionLocalProvider(
        LocalScreenCornerRadius provides screenCornerRadiusDp,
+3 −0
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@ import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import android.view.WindowInsets
import com.android.systemui.common.ui.data.repository.ConfigurationRepository
import com.android.systemui.qs.ui.adapter.QSSceneAdapter
import com.android.systemui.scene.shared.model.SceneContainerConfig
import com.android.systemui.scene.shared.model.SceneDataSourceDelegator
@@ -31,6 +32,7 @@ class SceneWindowRootView(context: Context, attrs: AttributeSet?) : WindowRootVi
        scenes: Set<Scene>,
        overlays: Set<Overlay>,
        layoutInsetController: LayoutInsetsController,
        configurationRepository: ConfigurationRepository,
        sceneDataSourceDelegator: SceneDataSourceDelegator,
        qsSceneAdapter: Provider<QSSceneAdapter>,
        sceneJankMonitorFactory: SceneJankMonitor.Factory,
@@ -53,6 +55,7 @@ class SceneWindowRootView(context: Context, attrs: AttributeSet?) : WindowRootVi
                super.setVisibility(if (isVisible) View.VISIBLE else View.INVISIBLE)
            },
            dataSourceDelegator = sceneDataSourceDelegator,
            configurationRepository = configurationRepository,
            qsSceneAdapter = qsSceneAdapter,
            sceneJankMonitorFactory = sceneJankMonitorFactory,
            tintedIconManagerFactory = tintedIconManagerFactory,
+54 −21
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ import com.android.internal.policy.ScreenDecorationsUtils
import com.android.systemui.common.ui.compose.windowinsets.CutoutLocation
import com.android.systemui.common.ui.compose.windowinsets.DisplayCutout
import com.android.systemui.common.ui.compose.windowinsets.ScreenDecorProvider
import com.android.systemui.common.ui.data.repository.ConfigurationRepository
import com.android.systemui.compose.modifiers.sysUiResTagContainer
import com.android.systemui.initOnBackPressedDispatcherOwner
import com.android.systemui.lifecycle.WindowLifecycleState
@@ -76,6 +77,7 @@ object SceneWindowRootViewBinder {
        overlays: Set<Overlay>,
        onVisibilityChangedInternal: (isVisible: Boolean) -> Unit,
        dataSourceDelegator: SceneDataSourceDelegator,
        configurationRepository: ConfigurationRepository,
        qsSceneAdapter: Provider<QSSceneAdapter>,
        sceneJankMonitorFactory: SceneJankMonitor.Factory,
        tintedIconManagerFactory: TintedIconManager.Factory,
@@ -132,6 +134,7 @@ object SceneWindowRootViewBinder {
                                containerConfig = containerConfig,
                                sceneJankMonitorFactory = sceneJankMonitorFactory,
                                tintedIconManagerFactory = tintedIconManagerFactory,
                                configurationRepository = configurationRepository,
                            )
                            .also { it.id = R.id.scene_container_root_composable }
                    )
@@ -155,6 +158,7 @@ object SceneWindowRootViewBinder {
                            viewModelFactory =
                                viewModel.dualShadeEducationalTooltipsViewModelFactory,
                            windowInsets = windowInsets,
                            configurationRepository = configurationRepository,
                        )
                    )

@@ -177,16 +181,19 @@ object SceneWindowRootViewBinder {
        overlayByKey: Map<OverlayKey, Overlay>,
        dataSourceDelegator: SceneDataSourceDelegator,
        qsSceneAdapter: Provider<QSSceneAdapter>,
        configurationRepository: ConfigurationRepository,
        containerConfig: SceneContainerConfig,
        sceneJankMonitorFactory: SceneJankMonitor.Factory,
        tintedIconManagerFactory: TintedIconManager.Factory,
    ): View {
        val displayCutout = displayCutoutFromWindowInsets(scope, context, windowInsets)
        val cornerRadius = cornerRadius(scope, configurationRepository, context)
        return ComposeView(context).apply {
            setContent {
                PlatformTheme {
                    ScreenDecorProvider(
                        displayCutout = displayCutoutFromWindowInsets(scope, context, windowInsets),
                        screenCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context),
                        displayCutout = displayCutout,
                        screenCornerRadius = cornerRadius,
                    ) {
                        WithStatusIconContext(tintedIconManagerFactory = tintedIconManagerFactory) {
                            SceneContainer(
@@ -212,7 +219,10 @@ object SceneWindowRootViewBinder {
        context: Context,
        viewModelFactory: DualShadeEducationalTooltipsViewModel.Factory,
        windowInsets: StateFlow<WindowInsets?>,
        configurationRepository: ConfigurationRepository,
    ): View {
        val displayCutout = displayCutoutFromWindowInsets(scope, context, windowInsets)
        val cornerRadius = cornerRadius(scope, configurationRepository, context)
        return ComposeView(context).apply {
            layoutParams =
                FrameLayout.LayoutParams(
@@ -222,8 +232,8 @@ object SceneWindowRootViewBinder {
            setContent {
                PlatformTheme {
                    ScreenDecorProvider(
                        displayCutout = displayCutoutFromWindowInsets(scope, context, windowInsets),
                        screenCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context),
                        displayCutout = displayCutout,
                        screenCornerRadius = cornerRadius,
                    ) {
                        DualShadeEducationalTooltips(viewModelFactory = viewModelFactory)
                    }
@@ -232,6 +242,23 @@ object SceneWindowRootViewBinder {
        }
    }

    /**
     * The corner radius can chang every time the window is moved to another display. This keeps it
     * up to date.
     */
    private fun cornerRadius(
        scope: CoroutineScope,
        configurationRepository: ConfigurationRepository,
        context: Context,
    ): StateFlow<Float> =
        configurationRepository.onMovedToDisplay
            .map { ScreenDecorationsUtils.getWindowCornerRadius(context) }
            .stateIn(
                scope,
                SharingStarted.WhileSubscribed(),
                ScreenDecorationsUtils.getWindowCornerRadius(context),
            )

    // TODO(b/298525212): remove once Compose exposes window inset bounds.
    private fun displayCutoutFromWindowInsets(
        scope: CoroutineScope,
@@ -239,8 +266,15 @@ object SceneWindowRootViewBinder {
        windowInsets: StateFlow<WindowInsets?>,
    ): StateFlow<DisplayCutout> =
        windowInsets
            .map {
                val boundingRect = it?.displayCutout?.boundingRectTop
            .map { it.toCutout(context) }
            .stateIn(
                scope,
                SharingStarted.WhileSubscribed(),
                initialValue = windowInsets.value.toCutout(context),
            )

    private fun WindowInsets?.toCutout(context: Context): DisplayCutout {
        val boundingRect = this?.displayCutout?.boundingRectTop
        val width = boundingRect?.let { boundingRect.right - boundingRect.left } ?: 0
        val left = boundingRect?.left?.toDp(context) ?: 0.dp
        val top = boundingRect?.top?.toDp(context) ?: 0.dp
@@ -253,10 +287,9 @@ object SceneWindowRootViewBinder {
                right >= getDisplayWidth(context) -> CutoutLocation.RIGHT
                else -> CutoutLocation.CENTER
            }
                val viewDisplayCutout = it?.displayCutout
                DisplayCutout(left, top, right, bottom, location, viewDisplayCutout)
        val viewDisplayCutout = this?.displayCutout
        return DisplayCutout(left, top, right, bottom, location, viewDisplayCutout)
    }
            .stateIn(scope, SharingStarted.WhileSubscribed(), DisplayCutout())

    // TODO(b/298525212): remove once Compose exposes window inset bounds.
    private fun getDisplayWidth(context: Context): Dp {
+3 −0
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import com.android.keyguard.logging.ScrimLogger
import com.android.systemui.battery.BatteryMeterView
import com.android.systemui.battery.BatteryMeterViewController
import com.android.systemui.biometrics.AuthRippleView
import com.android.systemui.common.ui.data.repository.ConfigurationRepository
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlags
@@ -101,6 +102,7 @@ abstract class ShadeViewProviderModule {
            choreographer: Choreographer?,
            @Main mainDispatcher: CoroutineDispatcher,
            tintedIconManagerFactory: TintedIconManager.Factory,
            @ShadeDisplayAware configurationRepository: ConfigurationRepository,
        ): WindowRootView {
            return if (SceneContainerFlag.isEnabled) {
                checkNoSceneDuplicates(scenesProvider.get())
@@ -121,6 +123,7 @@ abstract class ShadeViewProviderModule {
                    scenes = scenesProvider.get(),
                    overlays = overlaysProvider.get(),
                    layoutInsetController = layoutInsetController,
                    configurationRepository = configurationRepository,
                    sceneDataSourceDelegator = sceneDataSourceDelegator.get(),
                    qsSceneAdapter = qsSceneAdapter,
                    sceneJankMonitorFactory = sceneJankMonitorFactory,