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

Commit 30ea4560 authored by burakov's avatar burakov Committed by Danny Burakov
Browse files

[Dual Shade] Allow the new Dual Shade setting to enable Dual Shade.

Temporarily, we allow the dual_shade aconfig flag to enable Dual Shade
in addition to the setting (i.e. enabling either the setting or flag
will enable the feature). This helps avoid breaking the many unit tests
that still rely on the flag. Once all code references to the flag have
been removed, we'll remove the flag as well.

BONUS: Adds convenience methods in Kosmos for enabling each of the shade
modes.

Test: Unit tests still pass.
Flag: com.android.systemui.scene_container
Bug: 388793191
Change-Id: I6928f2625b175ba25a3be2e1a31a4f8e8ef32ee1
parent 6e500c4c
Loading
Loading
Loading
Loading
+17 −24
Original line number Diff line number Diff line
@@ -16,15 +16,11 @@

package com.android.systemui.shade.domain.interactor

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.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
import com.android.systemui.shade.data.repository.shadeRepository
import com.android.systemui.shade.shared.flag.DualShade
import com.android.systemui.shade.shared.model.ShadeMode
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
@@ -48,82 +44,79 @@ class ShadeModeInteractorImplTest : SysuiTestCase() {
    }

    @Test
    @DisableFlags(DualShade.FLAG_NAME)
    fun legacyShadeMode_narrowScreen_singleShade() =
        testScope.runTest {
            val shadeMode by collectLastValue(underTest.shadeMode)
            kosmos.shadeRepository.setShadeLayoutWide(false)
            kosmos.enableSingleShade()

            assertThat(shadeMode).isEqualTo(ShadeMode.Single)
        }

    @Test
    @DisableFlags(DualShade.FLAG_NAME)
    fun legacyShadeMode_wideScreen_splitShade() =
        testScope.runTest {
            val shadeMode by collectLastValue(underTest.shadeMode)
            kosmos.shadeRepository.setShadeLayoutWide(true)
            kosmos.enableSplitShade()

            assertThat(shadeMode).isEqualTo(ShadeMode.Split)
        }

    @Test
    @EnableFlags(DualShade.FLAG_NAME)
    fun shadeMode_wideScreen_isDual() =
        testScope.runTest {
            val shadeMode by collectLastValue(underTest.shadeMode)
            kosmos.shadeRepository.setShadeLayoutWide(true)
            kosmos.enableDualShade(wideLayout = true)

            assertThat(shadeMode).isEqualTo(ShadeMode.Dual)
        }

    @Test
    @EnableFlags(DualShade.FLAG_NAME)
    fun shadeMode_narrowScreen_isDual() =
        testScope.runTest {
            val shadeMode by collectLastValue(underTest.shadeMode)
            kosmos.shadeRepository.setShadeLayoutWide(false)
            kosmos.enableDualShade(wideLayout = false)

            assertThat(shadeMode).isEqualTo(ShadeMode.Dual)
        }

    @Test
    @EnableFlags(DualShade.FLAG_NAME)
    fun isDualShade_flagEnabled_true() =
    fun isDualShade_settingEnabled_returnsTrue() =
        testScope.runTest {
            // Initiate collection.
            // TODO(b/391578667): Add a test case for user switching once the bug is fixed.
            val shadeMode by collectLastValue(underTest.shadeMode)
            kosmos.enableDualShade()

            assertThat(shadeMode).isEqualTo(ShadeMode.Dual)
            assertThat(underTest.isDualShade).isTrue()
        }

    @Test
    @DisableFlags(DualShade.FLAG_NAME)
    fun isDualShade_flagDisabled_false() =
    fun isDualShade_settingDisabled_returnsFalse() =
        testScope.runTest {
            // Initiate collection.
            val shadeMode by collectLastValue(underTest.shadeMode)
            kosmos.disableDualShade()

            assertThat(shadeMode).isNotEqualTo(ShadeMode.Dual)
            assertThat(underTest.isDualShade).isFalse()
        }

    @Test
    fun getTopEdgeSplitFraction_narrowScreen_splitInHalf() =
        testScope.runTest {
            // Ensure isShadeLayoutWide is collected.
            val isShadeLayoutWide by collectLastValue(underTest.isShadeLayoutWide)
            kosmos.shadeRepository.setShadeLayoutWide(false)
            val shadeMode by collectLastValue(underTest.shadeMode)
            kosmos.enableDualShade(wideLayout = false)

            assertThat(shadeMode).isEqualTo(ShadeMode.Dual)
            assertThat(underTest.getTopEdgeSplitFraction()).isEqualTo(0.5f)
        }

    @Test
    fun getTopEdgeSplitFraction_wideScreen_splitInHalf() =
        testScope.runTest {
            // Ensure isShadeLayoutWide is collected.
            val isShadeLayoutWide by collectLastValue(underTest.isShadeLayoutWide)
            kosmos.shadeRepository.setShadeLayoutWide(true)
            val shadeMode by collectLastValue(underTest.shadeMode)
            kosmos.enableDualShade(wideLayout = true)

            assertThat(shadeMode).isEqualTo(ShadeMode.Dual)
            assertThat(underTest.getTopEdgeSplitFraction()).isEqualTo(0.5f)
        }
}
+35 −8
Original line number Diff line number Diff line
@@ -16,17 +16,20 @@

package com.android.systemui.shade.domain.interactor

import android.provider.Settings
import androidx.annotation.FloatRange
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.shade.shared.flag.DualShade
import com.android.systemui.shade.shared.model.ShadeMode
import com.android.systemui.shared.settings.data.repository.SecureSettingsRepository
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.stateIn

/**
@@ -76,29 +79,53 @@ interface ShadeModeInteractor {

class ShadeModeInteractorImpl
@Inject
constructor(@Application applicationScope: CoroutineScope, repository: ShadeRepository) :
    ShadeModeInteractor {
constructor(
    @Application applicationScope: CoroutineScope,
    repository: ShadeRepository,
    secureSettingsRepository: SecureSettingsRepository,
) : ShadeModeInteractor {

    private val isDualShadeEnabled: Flow<Boolean> =
        secureSettingsRepository.boolSetting(
            Settings.Secure.DUAL_SHADE,
            defaultValue = DUAL_SHADE_ENABLED_DEFAULT,
        )

    override val isShadeLayoutWide: StateFlow<Boolean> = repository.isShadeLayoutWide

    override val shadeMode: StateFlow<ShadeMode> =
        isShadeLayoutWide
            .map(this::determineShadeMode)
        combine(isDualShadeEnabled, repository.isShadeLayoutWide, ::determineShadeMode)
            .stateIn(
                applicationScope,
                SharingStarted.Eagerly,
                initialValue = determineShadeMode(isShadeLayoutWide.value),
                initialValue =
                    determineShadeMode(
                        isDualShadeEnabled = DUAL_SHADE_ENABLED_DEFAULT,
                        isShadeLayoutWide = repository.isShadeLayoutWide.value,
                    ),
            )

    @FloatRange(from = 0.0, to = 1.0) override fun getTopEdgeSplitFraction(): Float = 0.5f

    private fun determineShadeMode(isShadeLayoutWide: Boolean): ShadeMode {
    private fun determineShadeMode(
        isDualShadeEnabled: Boolean,
        isShadeLayoutWide: Boolean,
    ): ShadeMode {
        return when {
            isDualShadeEnabled ||
                // TODO(b/388793191): This ensures that the dual_shade aconfig flag can also enable
                //  Dual Shade, to avoid breaking unit tests. Remove this once all references to the
                //  flag are removed.
                DualShade.isEnabled -> ShadeMode.Dual
            isShadeLayoutWide -> ShadeMode.Split
            else -> ShadeMode.Single
        }
    }

    companion object {
        /* Whether the Dual Shade setting is enabled by default. */
        private const val DUAL_SHADE_ENABLED_DEFAULT = false
    }
}

class ShadeModeInteractorEmptyImpl @Inject constructor() : ShadeModeInteractor {
+43 −0
Original line number Diff line number Diff line
@@ -16,14 +16,57 @@

package com.android.systemui.shade.domain.interactor

import android.provider.Settings
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.testScope
import com.android.systemui.shade.data.repository.fakeShadeRepository
import com.android.systemui.shade.data.repository.shadeRepository
import com.android.systemui.shared.settings.data.repository.secureSettingsRepository
import kotlinx.coroutines.launch

val Kosmos.shadeModeInteractor by Fixture {
    ShadeModeInteractorImpl(
        applicationScope = applicationCoroutineScope,
        repository = shadeRepository,
        secureSettingsRepository = secureSettingsRepository,
    )
}

// TODO(b/391578667): Make this user-aware once supported by FakeSecureSettingsRepository.
/**
 * Enables the Dual Shade setting, and (optionally) sets the shade layout to be wide (`true`)
 * or narrow (`false`).
 *
 * In a wide layout, notifications and quick settings shades each take up only half the screen
 * width. In a narrow layout, they each take up the entire screen width.
 */
fun Kosmos.enableDualShade(wideLayout: Boolean? = null) {
    testScope.launch {
        secureSettingsRepository.setInt(Settings.Secure.DUAL_SHADE, 1)

        if (wideLayout != null) {
            fakeShadeRepository.setShadeLayoutWide(wideLayout)
        }
    }
}

// TODO(b/391578667): Make this user-aware once supported by FakeSecureSettingsRepository.
fun Kosmos.disableDualShade() {
    testScope.launch { secureSettingsRepository.setInt(Settings.Secure.DUAL_SHADE, 0) }
}

fun Kosmos.enableSingleShade() {
    testScope.launch {
        disableDualShade()
        fakeShadeRepository.setShadeLayoutWide(false)
    }
}

fun Kosmos.enableSplitShade() {
    testScope.launch {
        disableDualShade()
        fakeShadeRepository.setShadeLayoutWide(true)
    }
}