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

Commit c6a29959 authored by Danny Burakov's avatar Danny Burakov Committed by Android (Google) Code Review
Browse files

Merge "[Dual Shade] Allow the new Dual Shade setting to enable Dual Shade." into main

parents 71cbd55f 30ea4560
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)
    }
}