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

Commit 590c5b8f authored by burakov's avatar burakov Committed by Danny Burakov
Browse files

[Dual Shade] Break the circular calculation of isFullWidthShade in AL.

When Dual Shade is off, isFullWidthShade is determined by the config
value (config_use_split_notification_shade). This value is defined as
`false` by default, but overriden to `true` on large screen devices in
landscape orientation.

In a landscape AL device, `config_use_split_notification_shade` becomes
`true` and causes isFullWidthShade to be `false` which would normally
show Split shade. However, since Split Shade is disabled on AL, the
device shows Dual Shade.

Notice how I wrote "when Dual Shade is off" above. Since that condition
is no longer met due to what happened in the last paragraph,
isFullWidthShade suddenly becomes `true`. This in turn causes it to
revert back to Single Shade, so now the condition "When Dual Shade is
off" is `true` again, and the cycle repeats.

This CL breaks that cycle, and renames `isSplitShadeEnabled` to
`isSplitShadeDisabled` in order to better convey that this config value
doesn't mean that Split Shade would be shown. The associated logic is
also arguably easier to follow as `isSplitShadeDisabled`.

Fix: 436926610
Test: Added unit tests.
Test: Existing unit tests pass.
Flag: com.android.systemui.scene_container
Change-Id: Ice43f55f4cd5e9336c372e33cd4f81dfa7f3e1fb
parent e5f57c74
Loading
Loading
Loading
Loading
+111 −0
Original line number Diff line number Diff line
@@ -17,19 +17,24 @@
package com.android.systemui.shade.domain.interactor

import android.content.testableContext
import android.provider.Settings
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
import com.android.systemui.display.data.repository.displayStateRepository
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.collectLastValue
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.res.R
import com.android.systemui.shade.data.repository.shadeRepository
import com.android.systemui.shade.shared.model.ShadeMode
import com.android.systemui.shared.settings.data.repository.fakeSecureSettingsRepository
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.runBlocking
import org.junit.Test
import org.junit.runner.RunWith

@@ -148,4 +153,110 @@ class ShadeModeInteractorImplTest : SysuiTestCase() {
            assertThat(shadeMode).isNotEqualTo(ShadeMode.Dual)
            assertThat(underTest.isDualShade).isFalse()
        }

    @Test
    fun isFullWidthShade_largeScreenPortrait() =
        kosmos.runTest {
            val isFullWidthShade by collectLastValue(underTest.isFullWidthShade)

            // Large screen portrait
            setupScreenConfig(wideScreen = true, legacyUseSplitShade = false)

            setupShadeConfig(dualShadeSettingEnabled = true, disableSplitShade = false)
            assertThat(isFullWidthShade).isFalse()

            setupShadeConfig(dualShadeSettingEnabled = true, disableSplitShade = true)
            assertThat(isFullWidthShade).isFalse()

            setupShadeConfig(dualShadeSettingEnabled = false, disableSplitShade = true)
            assertThat(isFullWidthShade).isTrue()

            setupShadeConfig(dualShadeSettingEnabled = false, disableSplitShade = false)
            assertThat(isFullWidthShade).isTrue()
        }

    @Test
    fun isFullWidthShade_largeScreenLandscape() =
        kosmos.runTest {
            val isFullWidthShade by collectLastValue(underTest.isFullWidthShade)

            // Large screen landscape
            setupScreenConfig(wideScreen = true, legacyUseSplitShade = true)

            setupShadeConfig(dualShadeSettingEnabled = true, disableSplitShade = false)
            assertThat(isFullWidthShade).isFalse()

            setupShadeConfig(dualShadeSettingEnabled = true, disableSplitShade = true)
            assertThat(isFullWidthShade).isFalse()

            setupShadeConfig(dualShadeSettingEnabled = false, disableSplitShade = true)
            assertThat(isFullWidthShade).isFalse()

            setupShadeConfig(dualShadeSettingEnabled = false, disableSplitShade = false)
            assertThat(isFullWidthShade).isFalse()
        }

    @Test
    fun isFullWidthShade_compactScreenPortrait() =
        kosmos.runTest {
            val isFullWidthShade by collectLastValue(underTest.isFullWidthShade)

            // Compact screen portrait
            setupScreenConfig(wideScreen = false, legacyUseSplitShade = false)

            setupShadeConfig(dualShadeSettingEnabled = true, disableSplitShade = false)
            assertThat(isFullWidthShade).isTrue()

            setupShadeConfig(dualShadeSettingEnabled = true, disableSplitShade = true)
            assertThat(isFullWidthShade).isTrue()

            setupShadeConfig(dualShadeSettingEnabled = false, disableSplitShade = true)
            assertThat(isFullWidthShade).isTrue()

            setupShadeConfig(dualShadeSettingEnabled = false, disableSplitShade = false)
            assertThat(isFullWidthShade).isTrue()
        }

    @Test
    fun isFullWidthShade_compactScreenLandscape() =
        kosmos.runTest {
            val isFullWidthShade by collectLastValue(underTest.isFullWidthShade)

            // Compact screen landscape
            setupScreenConfig(wideScreen = true, legacyUseSplitShade = false)

            setupShadeConfig(dualShadeSettingEnabled = true, disableSplitShade = false)
            assertThat(isFullWidthShade).isFalse()

            setupShadeConfig(dualShadeSettingEnabled = true, disableSplitShade = true)
            assertThat(isFullWidthShade).isFalse()

            setupShadeConfig(dualShadeSettingEnabled = false, disableSplitShade = true)
            assertThat(isFullWidthShade).isTrue()

            setupShadeConfig(dualShadeSettingEnabled = false, disableSplitShade = false)
            assertThat(isFullWidthShade).isTrue()
        }

    private fun Kosmos.setupScreenConfig(wideScreen: Boolean, legacyUseSplitShade: Boolean) {
        testableContext.orCreateTestableResources.apply {
            addOverride(R.bool.config_use_split_notification_shade, legacyUseSplitShade)
            addOverride(R.bool.config_use_large_screen_shade_header, legacyUseSplitShade)
        }
        fakeConfigurationRepository.onAnyConfigurationChange()
        shadeRepository.legacyUseSplitShade.value = legacyUseSplitShade
        shadeRepository.isWideScreen.value = wideScreen
    }

    private fun Kosmos.setupShadeConfig(
        dualShadeSettingEnabled: Boolean,
        disableSplitShade: Boolean,
    ) = runBlocking {
        fakeSecureSettingsRepository.setBoolean(Settings.Secure.DUAL_SHADE, dualShadeSettingEnabled)
        testableContext.orCreateTestableResources.addOverride(
            R.bool.config_disableSplitShade,
            disableSplitShade,
        )
        fakeConfigurationRepository.onAnyConfigurationChange()
    }
}
+1 −1
Original line number Diff line number Diff line
@@ -277,7 +277,7 @@ class ShadeStartableTest(flags: FlagsParameterization) : SysuiTestCase() {
            verify(notificationStackScrollLayoutController).setIsFullWidth(false)
            assertThat(scrimController.clipQsScrim).isFalse()
            assertThat(isWideScreen).isTrue()
            assertThat(legacyUseSplitShade).isFalse()
            assertThat(legacyUseSplitShade).isTrue()
        }

    private fun Kosmos.changeScene(
+12 −10
Original line number Diff line number Diff line
@@ -95,17 +95,19 @@ constructor(
            flowOf(false)
        }

    private val isSplitShadeEnabled: Boolean =
        !SceneContainerFlag.isEnabled ||
            !context.resources.getBoolean(R.bool.config_disableSplitShade)
    private val isSplitShadeDisabled: Boolean =
        SceneContainerFlag.isEnabled &&
            context.resources.getBoolean(R.bool.config_disableSplitShade)

    override val isFullWidthShade: StateFlow<Boolean> =
        isDualShadeSettingEnabled
            .flatMapLatest { isDualShadeSettingEnabled ->
                if (isDualShadeSettingEnabled) {
                if (isDualShadeSettingEnabled || isSplitShadeDisabled) {
                    // Dual Shade should be shown; derive the layout from the screen width.
                    Log.d(TAG, "Shade layout is derived from screen width")
                    repository.isWideScreen.map { !it }
                } else {
                    // Single/Split shade should be shown; derive the layout from the config.
                    Log.d(TAG, "Shade layout is derived from the legacy config")
                    repository.legacyUseSplitShade.map { !it }
                }
@@ -146,15 +148,15 @@ constructor(
                        "the setting is 'combined', and the device is a phone " +
                            "(in any orientation) or large screen in portrait"

                isSplitShadeEnabled ->
                    ShadeMode.Split to
                        "the setting is 'combined', split shade is enabled, " +
                            "and the device has a large screen in landscape orientation"

                else ->
                isSplitShadeDisabled ->
                    ShadeMode.Dual to
                        "the setting is 'combined', " +
                            "but split shade disabled and the device has a large screen"

                else ->
                    ShadeMode.Split to
                        "the setting is 'combined', split shade is enabled, " +
                            "and the device has a large screen in landscape orientation"
            }
        Log.d(TAG, "Shade mode is $newMode because $reason")
        return newMode
+8 −21
Original line number Diff line number Diff line
@@ -33,7 +33,6 @@ import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.shade.domain.interactor.ShadeDisplayStateInteractor
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shade.domain.interactor.ShadeModeInteractor
import com.android.systemui.shade.shared.model.ShadeMode
import com.android.systemui.shade.transition.ScrimShadeTransitionController
import com.android.systemui.statusbar.NotificationShadeDepthController
import com.android.systemui.statusbar.PulseExpansionHandler
@@ -46,8 +45,6 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
@@ -122,25 +119,15 @@ constructor(
        }

        applicationScope.launch {
            val shadeModeInteractor = shadeModeInteractorProvider.get()
            shadeModeInteractor.shadeMode
                .flatMapLatest { shadeMode ->
                    if (shadeMode is ShadeMode.Dual) {
                        flowOf(false)
                    } else {
            configurationRepository.onAnyConfigurationChange
                // Force initial collection.
                .onStart { emit(Unit) }
                .map {
                                // The configuration for 'shouldUseSplitNotificationShade' dictates
                                // the width of the shade in single/split shade modes.
                                splitShadeStateController.shouldUseSplitNotificationShade(
                                    context.resources
                                )
                    // The configuration for 'shouldUseSplitNotificationShade' dictates the width of
                    // the shade in single/split shade modes.
                    splitShadeStateController.shouldUseSplitNotificationShade(context.resources)
                }
                .distinctUntilChanged()
                    }
                }
                .collect { shadeRepository.legacyUseSplitShade.value = it }
        }
    }