diff options
17 files changed, 334 insertions, 150 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt index 5925819f27a7..03660e92a44f 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt @@ -24,9 +24,11 @@ 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.internal.R import com.android.settingslib.notification.modes.ZenIconLoader import com.android.systemui.SysuiTestCase import com.android.systemui.common.shared.model.asIcon +import com.android.systemui.coroutines.collectLastValue import com.android.systemui.coroutines.collectValues import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope @@ -60,9 +62,11 @@ class ModesTileDataInteractorTest : SysuiTestCase() { @Before fun setUp() { context.orCreateTestableResources.apply { - addOverride(com.android.internal.R.drawable.ic_zen_mode_type_bedtime, BEDTIME_DRAWABLE) - addOverride(com.android.internal.R.drawable.ic_zen_mode_type_driving, DRIVING_DRAWABLE) + addOverride(MODES_DRAWABLE_ID, MODES_DRAWABLE) + addOverride(R.drawable.ic_zen_mode_type_bedtime, BEDTIME_DRAWABLE) + addOverride(R.drawable.ic_zen_mode_type_driving, DRIVING_DRAWABLE) } + // TODO: b/360399800 - Remove; ZenIconLoader should always use direct executor for tests ZenIconLoader.setInstance(ZenIconLoader(MoreExecutors.newDirectExecutorService())) } @@ -128,28 +132,34 @@ class ModesTileDataInteractorTest : SysuiTestCase() { @Test @EnableFlags(Flags.FLAG_MODES_UI, Flags.FLAG_MODES_UI_ICONS) - fun changesIconWhenActiveModesChange() = + fun tileData_iconsFlagEnabled_changesIconWhenActiveModesChange() = testScope.runTest { - val dataList: List<ModesTileModel> by - collectValues( + val tileData by + collectLastValue( underTest.tileData(TEST_USER, flowOf(DataUpdateTrigger.InitialRequest)) ) + + // Tile starts with the generic Modes icon. runCurrent() - assertThat(dataList.map { it.icon }).containsExactly(null).inOrder() + assertThat(tileData?.icon).isEqualTo(MODES_ICON) + assertThat(tileData?.iconResId).isEqualTo(MODES_DRAWABLE_ID) - // Add an inactive mode: state hasn't changed, so this shouldn't cause another emission + // Add an inactive mode -> Still modes icon zenModeRepository.addMode(id = "Mode", active = false) runCurrent() - assertThat(dataList.map { it.icon }).containsExactly(null).inOrder() + assertThat(tileData?.icon).isEqualTo(MODES_ICON) + assertThat(tileData?.iconResId).isEqualTo(MODES_DRAWABLE_ID) - // Add an active mode: icon should be the mode icon + // Add an active mode: icon should be the mode icon. No iconResId, because we don't + // really know that it's a system icon. zenModeRepository.addMode( id = "Bedtime", type = AutomaticZenRule.TYPE_BEDTIME, active = true ) runCurrent() - assertThat(dataList.map { it.icon }).containsExactly(null, BEDTIME_ICON).inOrder() + assertThat(tileData?.icon).isEqualTo(BEDTIME_ICON) + assertThat(tileData?.iconResId).isNull() // Add another, less-prioritized mode: icon should remain the first mode icon zenModeRepository.addMode( @@ -158,29 +168,58 @@ class ModesTileDataInteractorTest : SysuiTestCase() { active = true ) runCurrent() - assertThat(dataList.map { it.icon }) - .containsExactly(null, BEDTIME_ICON, BEDTIME_ICON) - .inOrder() + assertThat(tileData?.icon).isEqualTo(BEDTIME_ICON) + assertThat(tileData?.iconResId).isNull() - // Deactivate more important mode: icon should be the less important, still active mode. + // Deactivate more important mode: icon should be the less important, still active mode zenModeRepository.deactivateMode("Bedtime") runCurrent() - assertThat(dataList.map { it.icon }) - .containsExactly(null, BEDTIME_ICON, BEDTIME_ICON, DRIVING_ICON) - .inOrder() + assertThat(tileData?.icon).isEqualTo(DRIVING_ICON) + assertThat(tileData?.iconResId).isNull() - // Deactivate remaining mode: no icon + // Deactivate remaining mode: back to the default modes icon zenModeRepository.deactivateMode("Driving") runCurrent() - assertThat(dataList.map { it.icon }) - .containsExactly(null, BEDTIME_ICON, BEDTIME_ICON, DRIVING_ICON, null) - .inOrder() + assertThat(tileData?.icon).isEqualTo(MODES_ICON) + assertThat(tileData?.iconResId).isEqualTo(MODES_DRAWABLE_ID) + } + + @Test + @EnableFlags(Flags.FLAG_MODES_UI) + @DisableFlags(Flags.FLAG_MODES_UI_ICONS) + fun tileData_iconsFlagDisabled_hasPriorityModesIcon() = + testScope.runTest { + val tileData by + collectLastValue( + underTest.tileData(TEST_USER, flowOf(DataUpdateTrigger.InitialRequest)) + ) + + runCurrent() + assertThat(tileData?.icon).isEqualTo(MODES_ICON) + assertThat(tileData?.iconResId).isEqualTo(MODES_DRAWABLE_ID) + + // Activate a Mode -> Icon doesn't change. + zenModeRepository.addMode(id = "Mode", active = true) + runCurrent() + assertThat(tileData?.icon).isEqualTo(MODES_ICON) + assertThat(tileData?.iconResId).isEqualTo(MODES_DRAWABLE_ID) + + zenModeRepository.deactivateMode(id = "Mode") + runCurrent() + assertThat(tileData?.icon).isEqualTo(MODES_ICON) + assertThat(tileData?.iconResId).isEqualTo(MODES_DRAWABLE_ID) } private companion object { val TEST_USER = UserHandle.of(1)!! + + val MODES_DRAWABLE_ID = com.android.systemui.res.R.drawable.qs_dnd_icon_off + + val MODES_DRAWABLE = TestStubDrawable("modes_icon") val BEDTIME_DRAWABLE = TestStubDrawable("bedtime") val DRIVING_DRAWABLE = TestStubDrawable("driving") + + val MODES_ICON = MODES_DRAWABLE.asIcon() val BEDTIME_ICON = BEDTIME_DRAWABLE.asIcon() val DRIVING_ICON = DRIVING_DRAWABLE.asIcon() } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorTest.kt index 4b7564981855..cd5812710292 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorTest.kt @@ -16,12 +16,14 @@ package com.android.systemui.qs.tiles.impl.modes.domain.interactor +import android.graphics.drawable.TestStubDrawable import android.platform.test.annotations.EnableFlags 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.animation.Expandable +import com.android.systemui.common.shared.model.asIcon import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject import com.android.systemui.qs.tiles.base.actions.qsTileIntentUserInputHandler import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx @@ -54,10 +56,7 @@ class ModesTileUserActionInteractorTest : SysuiTestCase() { fun handleClick_active() = runTest { val expandable = mock<Expandable>() underTest.handleInput( - QSTileInputTestKtx.click( - data = ModesTileModel(true, listOf("DND")), - expandable = expandable - ) + QSTileInputTestKtx.click(data = modelOf(true, listOf("DND")), expandable = expandable) ) verify(mockDialogDelegate).showDialog(eq(expandable)) @@ -67,10 +66,7 @@ class ModesTileUserActionInteractorTest : SysuiTestCase() { fun handleClick_inactive() = runTest { val expandable = mock<Expandable>() underTest.handleInput( - QSTileInputTestKtx.click( - data = ModesTileModel(false, emptyList()), - expandable = expandable - ) + QSTileInputTestKtx.click(data = modelOf(false, emptyList()), expandable = expandable) ) verify(mockDialogDelegate).showDialog(eq(expandable)) @@ -78,7 +74,7 @@ class ModesTileUserActionInteractorTest : SysuiTestCase() { @Test fun handleLongClick_active() = runTest { - underTest.handleInput(QSTileInputTestKtx.longClick(ModesTileModel(true, listOf("DND")))) + underTest.handleInput(QSTileInputTestKtx.longClick(modelOf(true, listOf("DND")))) QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput { assertThat(it.intent.action).isEqualTo(Settings.ACTION_ZEN_MODE_SETTINGS) @@ -87,10 +83,14 @@ class ModesTileUserActionInteractorTest : SysuiTestCase() { @Test fun handleLongClick_inactive() = runTest { - underTest.handleInput(QSTileInputTestKtx.longClick(ModesTileModel(false, emptyList()))) + underTest.handleInput(QSTileInputTestKtx.longClick(modelOf(false, emptyList()))) QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput { assertThat(it.intent.action).isEqualTo(Settings.ACTION_ZEN_MODE_SETTINGS) } } + + private fun modelOf(isActivated: Boolean, activeModeNames: List<String>): ModesTileModel { + return ModesTileModel(isActivated, activeModeNames, TestStubDrawable("icon").asIcon(), 123) + } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapperTest.kt index a41f15d8ff82..f7bdcb8086ef 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapperTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapperTest.kt @@ -18,11 +18,12 @@ package com.android.systemui.qs.tiles.impl.modes.ui import android.app.Flags import android.graphics.drawable.TestStubDrawable +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.common.shared.model.Icon +import com.android.systemui.common.shared.model.asIcon import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesTileModel import com.android.systemui.qs.tiles.viewmodel.QSTileConfigTestBuilder import com.android.systemui.qs.tiles.viewmodel.QSTileState @@ -58,47 +59,88 @@ class ModesTileMapperTest : SysuiTestCase() { @Test fun inactiveState() { - val model = ModesTileModel(isActivated = false, activeModes = emptyList()) + val icon = TestStubDrawable("res123").asIcon() + val model = + ModesTileModel( + isActivated = false, + activeModes = emptyList(), + icon = icon, + ) val state = underTest.map(config, model) assertThat(state.activationState).isEqualTo(QSTileState.ActivationState.INACTIVE) - assertThat(state.iconRes).isEqualTo(R.drawable.qs_dnd_icon_off) + assertThat(state.icon()).isEqualTo(icon) assertThat(state.secondaryLabel).isEqualTo("No active modes") } @Test fun activeState_oneMode() { - val model = ModesTileModel(isActivated = true, activeModes = listOf("DND")) + val icon = TestStubDrawable("res123").asIcon() + val model = + ModesTileModel( + isActivated = true, + activeModes = listOf("DND"), + icon = icon, + ) val state = underTest.map(config, model) assertThat(state.activationState).isEqualTo(QSTileState.ActivationState.ACTIVE) - assertThat(state.iconRes).isEqualTo(R.drawable.qs_dnd_icon_on) + assertThat(state.icon()).isEqualTo(icon) assertThat(state.secondaryLabel).isEqualTo("DND is active") } @Test fun activeState_multipleModes() { + val icon = TestStubDrawable("res123").asIcon() val model = - ModesTileModel(isActivated = true, activeModes = listOf("Mode 1", "Mode 2", "Mode 3")) + ModesTileModel( + isActivated = true, + activeModes = listOf("Mode 1", "Mode 2", "Mode 3"), + icon = icon, + ) val state = underTest.map(config, model) assertThat(state.activationState).isEqualTo(QSTileState.ActivationState.ACTIVE) - assertThat(state.iconRes).isEqualTo(R.drawable.qs_dnd_icon_on) + assertThat(state.icon()).isEqualTo(icon) assertThat(state.secondaryLabel).isEqualTo("3 modes are active") } @Test @EnableFlags(Flags.FLAG_MODES_UI_ICONS) - fun activeState_withIcon() { - val icon = Icon.Resource(1234, contentDescription = null) - val model = ModesTileModel(isActivated = true, activeModes = listOf("DND"), icon = icon) + fun state_withEnabledFlag_noIconResId() { + val icon = TestStubDrawable("res123").asIcon() + val model = + ModesTileModel( + isActivated = false, + activeModes = emptyList(), + icon = icon, + iconResId = 123 // Should not be populated, but is ignored even if present + ) val state = underTest.map(config, model) + assertThat(state.icon()).isEqualTo(icon) assertThat(state.iconRes).isNull() + } + + @Test + @DisableFlags(Flags.FLAG_MODES_UI_ICONS) + fun state_withDisabledFlag_includesIconResId() { + val icon = TestStubDrawable("res123").asIcon() + val model = + ModesTileModel( + isActivated = false, + activeModes = emptyList(), + icon = icon, + iconResId = 123 + ) + + val state = underTest.map(config, model) + assertThat(state.icon()).isEqualTo(icon) + assertThat(state.iconRes).isEqualTo(123) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt index 20d3a7b6b7b5..cb743bc732f0 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt @@ -220,33 +220,37 @@ class ZenModeInteractorTest : SysuiTestCase() { } @Test - fun mainActiveMode_returnsMainActiveMode() = + fun activeModes_computesMainActiveMode() = testScope.runTest { - val mainActiveMode by collectLastValue(underTest.mainActiveMode) + val activeModes by collectLastValue(underTest.activeModes) zenModeRepository.addMode(id = "Bedtime", type = AutomaticZenRule.TYPE_BEDTIME) zenModeRepository.addMode(id = "Other", type = AutomaticZenRule.TYPE_OTHER) runCurrent() - assertThat(mainActiveMode).isNull() + assertThat(activeModes?.modeNames).hasSize(0) + assertThat(activeModes?.mainMode).isNull() zenModeRepository.activateMode("Other") runCurrent() - assertThat(mainActiveMode).isNotNull() - assertThat(mainActiveMode!!.id).isEqualTo("Other") + assertThat(activeModes?.modeNames).containsExactly("Mode Other") + assertThat(activeModes?.mainMode?.name).isEqualTo("Mode Other") zenModeRepository.activateMode("Bedtime") runCurrent() - assertThat(mainActiveMode).isNotNull() - assertThat(mainActiveMode!!.id).isEqualTo("Bedtime") + assertThat(activeModes?.modeNames) + .containsExactly("Mode Bedtime", "Mode Other") + .inOrder() + assertThat(activeModes?.mainMode?.name).isEqualTo("Mode Bedtime") zenModeRepository.deactivateMode("Other") runCurrent() - assertThat(mainActiveMode).isNotNull() - assertThat(mainActiveMode!!.id).isEqualTo("Bedtime") + assertThat(activeModes?.modeNames).containsExactly("Mode Bedtime") + assertThat(activeModes?.mainMode?.name).isEqualTo("Mode Bedtime") zenModeRepository.deactivateMode("Bedtime") runCurrent() - assertThat(mainActiveMode).isNull() + assertThat(activeModes?.modeNames).hasSize(0) + assertThat(activeModes?.mainMode).isNull() } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModelTest.kt index 33f379d020b7..468e6822d1e1 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModelTest.kt @@ -69,6 +69,7 @@ class ModesDialogViewModelTest : SysuiTestCase() { @Before fun setUp() { + // TODO: b/360399800 - Remove; ZenIconLoader should always use direct executor for tests ZenIconLoader.setInstance(ZenIconLoader(MoreExecutors.newDirectExecutorService())) } diff --git a/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt b/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt index 3cdb57318e8d..aef5f1f422d1 100644 --- a/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt +++ b/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt @@ -38,5 +38,5 @@ sealed class Icon { } /** Creates [Icon.Loaded] for a given drawable with an optional [contentDescription]. */ -fun Drawable.asIcon(contentDescription: ContentDescription? = null): Icon = +fun Drawable.asIcon(contentDescription: ContentDescription? = null): Icon.Loaded = Icon.Loaded(this, contentDescription) diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt index 3f18fc2066eb..664951d199a7 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt @@ -20,10 +20,12 @@ import android.app.Flags import android.content.Context import android.os.UserHandle import com.android.app.tracing.coroutines.flow.map +import com.android.systemui.common.shared.model.asIcon import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesTileModel +import com.android.systemui.res.R import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher @@ -52,18 +54,32 @@ constructor( */ fun tileData() = zenModeInteractor.activeModes - .map { modes -> - ModesTileModel( - isActivated = modes.isNotEmpty(), - icon = - if (Flags.modesApi() && Flags.modesUi() && Flags.modesUiIcons()) - zenModeInteractor.getActiveModeIcon(modes) - else null, - activeModes = modes.map { it.name } - ) + .map { activeModes -> + val modesIconResId = R.drawable.qs_dnd_icon_off + + if (usesModeIcons()) { + val mainModeDrawable = activeModes.mainMode?.icon?.drawable + val iconResId = if (mainModeDrawable == null) modesIconResId else null + + ModesTileModel( + isActivated = activeModes.isAnyActive(), + icon = (mainModeDrawable ?: context.getDrawable(modesIconResId)!!).asIcon(), + iconResId = iconResId, + activeModes = activeModes.modeNames + ) + } else { + ModesTileModel( + isActivated = activeModes.isAnyActive(), + icon = context.getDrawable(modesIconResId)!!.asIcon(), + iconResId = modesIconResId, + activeModes = activeModes.modeNames + ) + } } .flowOn(bgDispatcher) .distinctUntilChanged() override fun availability(user: UserHandle): Flow<Boolean> = flowOf(Flags.modesUi()) + + private fun usesModeIcons() = Flags.modesApi() && Flags.modesUi() && Flags.modesUiIcons() } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/model/ModesTileModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/model/ModesTileModel.kt index 904ff3aaad26..db4812342050 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/model/ModesTileModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/model/ModesTileModel.kt @@ -21,5 +21,12 @@ import com.android.systemui.common.shared.model.Icon data class ModesTileModel( val isActivated: Boolean, val activeModes: List<String>, - val icon: Icon? = null + val icon: Icon.Loaded, + + /** + * Resource id corresponding to [icon]. Will only be present if it's know to correspond to a + * resource with a known id in SystemUI (such as resources from `android.R`, + * `com.android.internal.R`, or `com.android.systemui.res` itself). + */ + val iconResId: Int? = null ) diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt index 83c3335ebffb..7f571b135fc8 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt @@ -16,11 +16,9 @@ package com.android.systemui.qs.tiles.impl.modes.ui -import android.app.Flags import android.content.res.Resources import android.icu.text.MessageFormat import android.widget.Button -import com.android.systemui.common.shared.model.asIcon import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesTileModel @@ -38,15 +36,10 @@ constructor( ) : QSTileDataToStateMapper<ModesTileModel> { override fun map(config: QSTileConfig, data: ModesTileModel): QSTileState = QSTileState.build(resources, theme, config.uiConfig) { - if (Flags.modesApi() && Flags.modesUi() && Flags.modesUiIcons() && data.icon != null) { - icon = { data.icon } - } else { - val iconRes = - if (data.isActivated) R.drawable.qs_dnd_icon_on else R.drawable.qs_dnd_icon_off - val icon = resources.getDrawable(iconRes, theme).asIcon() - this.iconRes = iconRes - this.icon = { icon } + if (!android.app.Flags.modesUiIcons()) { + iconRes = data.iconResId } + icon = { data.icon } activationState = if (data.isActivated) { QSTileState.ActivationState.ACTIVE diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java index 05bd1a7676ae..a9b886fbb073 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -42,9 +42,9 @@ import android.text.format.DateFormat; import android.util.Log; import android.view.View; +import androidx.annotation.NonNull; import androidx.lifecycle.Observer; -import com.android.settingslib.notification.modes.ZenMode; import com.android.systemui.Flags; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.qualifiers.DisplayId; @@ -80,6 +80,8 @@ import com.android.systemui.statusbar.policy.SensorPrivacyController; import com.android.systemui.statusbar.policy.UserInfoController; import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor; +import com.android.systemui.statusbar.policy.domain.model.ActiveZenModes; +import com.android.systemui.statusbar.policy.domain.model.ZenModeInfo; import com.android.systemui.util.RingerModeTracker; import com.android.systemui.util.kotlin.JavaAdapter; import com.android.systemui.util.time.DateFormatUtil; @@ -362,8 +364,8 @@ public class PhoneStatusBarPolicy if (usesModeIcons()) { // Note that we're not fully replacing ZenModeController with ZenModeInteractor, so // we listen for the extra event here but still add the ZMC callback. - mJavaAdapter.alwaysCollectFlow(mZenModeInteractor.getMainActiveMode(), - this::onActiveModeChanged); + mJavaAdapter.alwaysCollectFlow(mZenModeInteractor.getActiveModes(), + this::onActiveModesChanged); } mZenController.addCallback(mZenControllerCallback); if (!Flags.statusBarScreenSharingChips()) { @@ -395,20 +397,21 @@ public class PhoneStatusBarPolicy () -> mResources.getString(R.string.accessibility_managed_profile)); } - private void onActiveModeChanged(@Nullable ZenMode mode) { + private void onActiveModesChanged(@NonNull ActiveZenModes activeModes) { if (!usesModeIcons()) { Log.wtf(TAG, "onActiveModeChanged shouldn't be called if MODES_UI_ICONS is disabled"); return; } - boolean visible = mode != null; - if (visible) { - // TODO: b/360399800 - Get the resource id, package, and cached drawable from the mode; - // this is a shortcut for testing. - String resPackage = mode.getIconKey().resPackage(); - int iconResId = mode.getIconKey().resId(); - mIconController.setResourceIcon(mSlotZen, resPackage, iconResId, - /* preloadedIcon= */ null, mode.getName()); + ZenModeInfo mainActiveMode = activeModes.getMainMode(); + boolean visible = mainActiveMode != null; + + if (visible) { + mIconController.setResourceIcon(mSlotZen, + mainActiveMode.getIcon().key().resPackage(), + mainActiveMode.getIcon().key().resId(), + mainActiveMode.getIcon().drawable(), + mainActiveMode.getName()); } if (visible != mZenVisible) { mIconController.setIconVisibility(mSlotZen, visible); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt index a67b47a9a0c9..e87916ef6bcd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt @@ -23,16 +23,20 @@ import android.provider.Settings.Secure.ZEN_DURATION_PROMPT import android.util.Log import androidx.concurrent.futures.await import com.android.settingslib.notification.data.repository.ZenModeRepository +import com.android.settingslib.notification.modes.ZenIcon import com.android.settingslib.notification.modes.ZenIconLoader import com.android.settingslib.notification.modes.ZenMode -import com.android.systemui.common.shared.model.Icon -import com.android.systemui.common.shared.model.asIcon +import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.shared.notifications.data.repository.NotificationSettingsRepository +import com.android.systemui.statusbar.policy.domain.model.ActiveZenModes +import com.android.systemui.statusbar.policy.domain.model.ZenModeInfo import java.time.Duration import javax.inject.Inject +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map /** @@ -45,6 +49,7 @@ constructor( private val context: Context, private val zenModeRepository: ZenModeRepository, private val notificationSettingsRepository: NotificationSettingsRepository, + @Background private val bgDispatcher: CoroutineDispatcher, ) { private val iconLoader: ZenIconLoader = ZenIconLoader.getInstance() @@ -76,34 +81,24 @@ constructor( val modes: Flow<List<ZenMode>> = zenModeRepository.modes - val activeModes: Flow<List<ZenMode>> = - modes.map { modes -> modes.filter { mode -> mode.isActive } }.distinctUntilChanged() - - /** Flow returning the most prioritized of the active modes, if any. */ - val mainActiveMode: Flow<ZenMode?> = - activeModes.map { modes -> getMainActiveMode(modes) }.distinctUntilChanged() - - /** - * Given the list of modes (which may include zero or more currently active modes), returns the - * most prioritized of the active modes, if any. - */ - private fun getMainActiveMode(modes: List<ZenMode>): ZenMode? { - return modes.sortedWith(ZenMode.PRIORITIZING_COMPARATOR).firstOrNull { it.isActive } - } - - suspend fun getModeIcon(mode: ZenMode): Icon { - return iconLoader.getIcon(context, mode).await().drawable().asIcon() - } + /** Flow returning the currently active mode(s), if any. */ + val activeModes: Flow<ActiveZenModes> = + modes + .map { modes -> + val activeModesList = + modes + .filter { mode -> mode.isActive } + .sortedWith(ZenMode.PRIORITIZING_COMPARATOR) + val mainActiveMode = + activeModesList.firstOrNull()?.let { ZenModeInfo(it.name, getModeIcon(it)) } + + ActiveZenModes(activeModesList.map { m -> m.name }, mainActiveMode) + } + .flowOn(bgDispatcher) + .distinctUntilChanged() - /** - * Given the list of modes (which may include zero or more currently active modes), returns an - * icon representing the active mode, if any (or, if multiple modes are active, to the most - * prioritized one). This icon is suitable for use in the status bar or lockscreen (uses the - * standard DND icon for implicit modes, instead of the launcher icon of the associated - * package). - */ - suspend fun getActiveModeIcon(modes: List<ZenMode>): Icon? { - return getMainActiveMode(modes)?.let { m -> getModeIcon(m) } + suspend fun getModeIcon(mode: ZenMode): ZenIcon { + return iconLoader.getIcon(context, mode).await() } fun activateMode(zenMode: ZenMode) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/model/ActiveZenModes.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/model/ActiveZenModes.kt new file mode 100644 index 000000000000..569e517d6ed5 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/model/ActiveZenModes.kt @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.policy.domain.model + +import com.android.settingslib.notification.modes.ZenMode + +/** + * Represents the list of [ZenMode] instances that are currently active. + * + * @property modeNames Names of all the active modes, sorted by their priority. + * @property mainMode The most prioritized active mode, if any modes active. Guaranteed to be + * non-null if [modeNames] is not empty. + */ +data class ActiveZenModes(val modeNames: List<String>, val mainMode: ZenModeInfo?) { + fun isAnyActive(): Boolean = modeNames.isNotEmpty() +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/model/ZenModeInfo.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/model/ZenModeInfo.kt new file mode 100644 index 000000000000..5004f4c21371 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/model/ZenModeInfo.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.policy.domain.model + +import com.android.settingslib.notification.modes.ZenIcon +import com.android.settingslib.notification.modes.ZenMode + +/** Name and icon of a [ZenMode] */ +data class ZenModeInfo(val name: String, val icon: ZenIcon) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModel.kt index be90bec03e52..841071347c08 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModel.kt @@ -23,6 +23,7 @@ import android.provider.Settings.ACTION_AUTOMATIC_ZEN_RULE_SETTINGS import android.provider.Settings.EXTRA_AUTOMATIC_ZEN_RULE_ID import com.android.settingslib.notification.modes.EnableZenModeDialog import com.android.settingslib.notification.modes.ZenMode +import com.android.systemui.common.shared.model.asIcon import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.qs.tiles.dialog.QSZenModeDialogMetricsLogger @@ -88,7 +89,7 @@ constructor( modesList.map { mode -> ModeTileViewModel( id = mode.id, - icon = zenModeInteractor.getModeIcon(mode), + icon = zenModeInteractor.getModeIcon(mode).drawable().asIcon(), text = mode.name, subtext = getTileSubtext(mode), enabled = mode.isActive, diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ModesTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ModesTileTest.kt index 19735e29834e..dadfb37fd59d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ModesTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ModesTileTest.kt @@ -25,9 +25,11 @@ import android.testing.TestableLooper.RunWithLooper import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.logging.MetricsLogger -import com.android.settingslib.notification.data.repository.FakeZenModeRepository +import com.android.settingslib.notification.modes.ZenIconLoader import com.android.systemui.SysuiTestCase import com.android.systemui.classifier.FalsingManagerFake +import com.android.systemui.kosmos.testDispatcher +import com.android.systemui.kosmos.testScope import com.android.systemui.plugins.ActivityStarter import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.qs.QSHost @@ -41,25 +43,25 @@ import com.android.systemui.qs.tiles.viewmodel.QSTileConfigProvider import com.android.systemui.qs.tiles.viewmodel.QSTileConfigTestBuilder import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig import com.android.systemui.res.R -import com.android.systemui.shared.notifications.data.repository.NotificationSettingsRepository -import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor +import com.android.systemui.statusbar.policy.data.repository.zenModeRepository +import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor import com.android.systemui.statusbar.policy.ui.dialog.ModesDialogDelegate +import com.android.systemui.testKosmos import com.android.systemui.util.mockito.any import com.android.systemui.util.settings.FakeSettings import com.android.systemui.util.settings.SecureSettings import com.google.common.truth.Truth.assertThat +import com.google.common.util.concurrent.MoreExecutors import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.TestScope -import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.After import org.junit.Before +import org.junit.BeforeClass import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.MockitoAnnotations -import org.mockito.kotlin.mock import org.mockito.kotlin.whenever @OptIn(ExperimentalCoroutinesApi::class) @@ -68,6 +70,18 @@ import org.mockito.kotlin.whenever @RunWith(AndroidJUnit4::class) @RunWithLooper(setAsMainLooper = true) class ModesTileTest : SysuiTestCase() { + private val kosmos = testKosmos() + private val testScope = kosmos.testScope + private val testDispatcher = kosmos.testDispatcher + + companion object { + @BeforeClass + @JvmStatic + fun setup() { + // TODO: b/360399800 - Remove; ZenIconLoader should always use direct executor for tests + ZenIconLoader.setInstance(ZenIconLoader(MoreExecutors.newDirectExecutorService())) + } + } @Mock private lateinit var qsHost: QSHost @@ -85,17 +99,10 @@ class ModesTileTest : SysuiTestCase() { @Mock private lateinit var dialogDelegate: ModesDialogDelegate - private val testDispatcher = UnconfinedTestDispatcher() - private val testScope = TestScope(testDispatcher) - private val inputHandler = FakeQSTileIntentUserInputHandler() - private val zenModeRepository = FakeZenModeRepository() + private val zenModeRepository = kosmos.zenModeRepository private val tileDataInteractor = - ModesTileDataInteractor( - context, - ZenModeInteractor(context, zenModeRepository, mock<NotificationSettingsRepository>()), - testDispatcher - ) + ModesTileDataInteractor(context, kosmos.zenModeInteractor, testDispatcher) private val mapper = ModesTileMapper( context.orCreateTestableResources diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt index 76dc65cbc915..8d6f50f86b1f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt @@ -35,12 +35,14 @@ import android.testing.TestableLooper.RunWithLooper import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.settingslib.notification.modes.TestModeBuilder +import com.android.settingslib.notification.modes.ZenIconLoader import com.android.systemui.Flags import com.android.systemui.SysuiTestCase import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor.PendingDisplay import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor.State +import com.android.systemui.kosmos.testScope import com.android.systemui.privacy.PrivacyItemController import com.android.systemui.privacy.logging.PrivacyLogger import com.android.systemui.screenrecord.RecordingController @@ -69,14 +71,14 @@ import com.android.systemui.util.kotlin.JavaAdapter import com.android.systemui.util.mockito.capture import com.android.systemui.util.time.DateFormatUtil import com.android.systemui.util.time.FakeSystemClock +import com.google.common.util.concurrent.MoreExecutors import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.test.TestScope -import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before +import org.junit.BeforeClass import org.junit.Test import org.junit.runner.RunWith import org.mockito.Answers @@ -112,6 +114,12 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() { private const val SCREEN_RECORD_SLOT = "screen_record" private const val CONNECTED_DISPLAY_SLOT = "connected_display" private const val MANAGED_PROFILE_SLOT = "managed_profile" + + @BeforeClass + @JvmStatic + fun setup() { + ZenIconLoader.setInstance(ZenIconLoader(MoreExecutors.newDirectExecutorService())) + } } @Mock private lateinit var iconController: StatusBarIconController @@ -145,7 +153,7 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() { private lateinit var alarmCallbackCaptor: ArgumentCaptor<NextAlarmController.NextAlarmChangeCallback> - private val testScope = TestScope(UnconfinedTestDispatcher()) + private val testScope = kosmos.testScope private val fakeConnectedDisplayStateProvider = FakeConnectedDisplayStateProvider() private val zenModeController = FakeZenModeController() @@ -249,7 +257,7 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() { statusBarPolicy.init() clearInvocations(iconController) - fakeConnectedDisplayStateProvider.emit(State.CONNECTED) + fakeConnectedDisplayStateProvider.setState(State.CONNECTED) runCurrent() verify(iconController).setIconVisibility(CONNECTED_DISPLAY_SLOT, true) @@ -261,7 +269,8 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() { statusBarPolicy.init() clearInvocations(iconController) - fakeConnectedDisplayStateProvider.emit(State.DISCONNECTED) + fakeConnectedDisplayStateProvider.setState(State.DISCONNECTED) + runCurrent() verify(iconController).setIconVisibility(CONNECTED_DISPLAY_SLOT, false) } @@ -272,9 +281,12 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() { statusBarPolicy.init() clearInvocations(iconController) - fakeConnectedDisplayStateProvider.emit(State.CONNECTED) - fakeConnectedDisplayStateProvider.emit(State.DISCONNECTED) - fakeConnectedDisplayStateProvider.emit(State.CONNECTED) + fakeConnectedDisplayStateProvider.setState(State.CONNECTED) + runCurrent() + fakeConnectedDisplayStateProvider.setState(State.DISCONNECTED) + runCurrent() + fakeConnectedDisplayStateProvider.setState(State.CONNECTED) + runCurrent() inOrder(iconController).apply { verify(iconController).setIconVisibility(CONNECTED_DISPLAY_SLOT, true) @@ -289,7 +301,8 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() { statusBarPolicy.init() clearInvocations(iconController) - fakeConnectedDisplayStateProvider.emit(State.CONNECTED_SECURE) + fakeConnectedDisplayStateProvider.setState(State.CONNECTED_SECURE) + runCurrent() verify(iconController).setIconVisibility(CONNECTED_DISPLAY_SLOT, true) } @@ -390,7 +403,7 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() { } @Test - @EnableFlags(android.app.Flags.FLAG_MODES_UI_ICONS) + @EnableFlags(android.app.Flags.FLAG_MODES_UI, android.app.Flags.FLAG_MODES_UI_ICONS) fun zenModeInteractorActiveModeChanged_showsModeIcon() = testScope.runTest { statusBarPolicy.init() @@ -403,8 +416,8 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() { .setName("Bedtime Mode") .setType(AutomaticZenRule.TYPE_BEDTIME) .setActive(true) - .setPackage("some.package") - .setIconResId(123) + .setPackage(mContext.packageName) + .setIconResId(android.R.drawable.ic_lock_lock) .build(), TestModeBuilder() .setId("other") @@ -412,7 +425,7 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() { .setType(AutomaticZenRule.TYPE_OTHER) .setActive(true) .setPackage(SystemZenRules.PACKAGE_ANDROID) - .setIconResId(456) + .setIconResId(android.R.drawable.ic_media_play) .build(), ) ) @@ -422,9 +435,9 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() { verify(iconController) .setResourceIcon( eq(ZEN_SLOT), - eq("some.package"), - eq(123), - eq(null), + eq(mContext.packageName), + eq(android.R.drawable.ic_lock_lock), + any(), // non-null eq("Bedtime Mode") ) @@ -432,7 +445,13 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() { runCurrent() verify(iconController) - .setResourceIcon(eq(ZEN_SLOT), eq(null), eq(456), eq(null), eq("Other Mode")) + .setResourceIcon( + eq(ZEN_SLOT), + eq(null), + eq(android.R.drawable.ic_media_play), + any(), // non-null + eq("Other Mode") + ) zenModeRepository.deactivateMode("other") runCurrent() @@ -441,7 +460,7 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() { } @Test - @EnableFlags(android.app.Flags.FLAG_MODES_UI_ICONS) + @EnableFlags(android.app.Flags.FLAG_MODES_UI, android.app.Flags.FLAG_MODES_UI_ICONS) fun zenModeControllerOnGlobalZenChanged_doesNotUpdateDndIcon() { statusBarPolicy.init() reset(iconController) @@ -529,9 +548,11 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() { } private class FakeConnectedDisplayStateProvider : ConnectedDisplayInteractor { - private val flow = MutableSharedFlow<State>() + private val flow = MutableStateFlow(State.DISCONNECTED) - suspend fun emit(value: State) = flow.emit(value) + fun setState(value: State) { + flow.value = value + } override val connectedDisplayState: Flow<State> get() = flow diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorKosmos.kt index 66be7e7a7a7e..f1f876c19c0a 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorKosmos.kt @@ -19,6 +19,7 @@ package com.android.systemui.statusbar.policy.domain.interactor import android.content.testableContext import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture +import com.android.systemui.kosmos.testDispatcher import com.android.systemui.shared.notifications.data.repository.notificationSettingsRepository import com.android.systemui.statusbar.policy.data.repository.zenModeRepository @@ -27,5 +28,6 @@ val Kosmos.zenModeInteractor by Fixture { context = testableContext, zenModeRepository = zenModeRepository, notificationSettingsRepository = notificationSettingsRepository, + bgDispatcher = testDispatcher, ) } |