Loading packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorImplTest.kt +17 −24 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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) } } packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractor.kt +35 −8 Original line number Diff line number Diff line Loading @@ -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 /** Loading Loading @@ -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 { Loading packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorKosmos.kt +43 −0 Original line number Diff line number Diff line Loading @@ -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) } } Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorImplTest.kt +17 −24 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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) } }
packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractor.kt +35 −8 Original line number Diff line number Diff line Loading @@ -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 /** Loading Loading @@ -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 { Loading
packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorKosmos.kt +43 −0 Original line number Diff line number Diff line Loading @@ -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) } }