Loading packages/SettingsLib/src/com/android/settingslib/notification/data/repository/FakeZenModeRepository.kt +20 −2 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import android.app.NotificationManager import android.provider.Settings import com.android.settingslib.notification.modes.TestModeBuilder import com.android.settingslib.notification.modes.ZenMode import java.time.Duration import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow Loading @@ -35,8 +36,7 @@ class FakeZenModeRepository : ZenModeRepository { override val globalZenMode: StateFlow<Int> get() = mutableZenMode.asStateFlow() private val mutableModesFlow: MutableStateFlow<List<ZenMode>> = MutableStateFlow(listOf(TestModeBuilder.EXAMPLE)) private val mutableModesFlow: MutableStateFlow<List<ZenMode>> = MutableStateFlow(listOf()) override val modes: Flow<List<ZenMode>> get() = mutableModesFlow.asStateFlow() Loading @@ -52,6 +52,10 @@ class FakeZenModeRepository : ZenModeRepository { mutableZenMode.value = zenMode } fun addModes(zenModes: List<ZenMode>) { mutableModesFlow.value += zenModes } fun addMode(id: String, active: Boolean = false) { mutableModesFlow.value += newMode(id, active) } Loading @@ -60,6 +64,20 @@ class FakeZenModeRepository : ZenModeRepository { mutableModesFlow.value = mutableModesFlow.value.filter { it.id != id } } override fun activateMode(zenMode: ZenMode, duration: Duration?) { activateMode(zenMode.id) } override fun deactivateMode(zenMode: ZenMode) { deactivateMode(zenMode.id) } fun activateMode(id: String) { val oldMode = mutableModesFlow.value.find { it.id == id } ?: return removeMode(id) mutableModesFlow.value += TestModeBuilder(oldMode).setActive(true).build() } fun deactivateMode(id: String) { val oldMode = mutableModesFlow.value.find { it.id == id } ?: return removeMode(id) Loading packages/SettingsLib/src/com/android/settingslib/notification/data/repository/ZenModeRepository.kt +13 −0 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ import android.provider.Settings import com.android.settingslib.flags.Flags import com.android.settingslib.notification.modes.ZenMode import com.android.settingslib.notification.modes.ZenModesBackend import java.time.Duration import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.channels.awaitClose Loading Loading @@ -57,6 +58,10 @@ interface ZenModeRepository { /** A list of all existing priority modes. */ val modes: Flow<List<ZenMode>> fun activateMode(zenMode: ZenMode, duration: Duration? = null) fun deactivateMode(zenMode: ZenMode) } @SuppressLint("SharedFlowCreation") Loading Loading @@ -178,4 +183,12 @@ class ZenModeRepositoryImpl( flowOf(emptyList()) } } override fun activateMode(zenMode: ZenMode, duration: Duration?) { backend.activateMode(zenMode, duration) } override fun deactivateMode(zenMode: ZenMode) { backend.deactivateMode(zenMode) } } packages/SettingsLib/src/com/android/settingslib/notification/modes/TestModeBuilder.java +7 −0 Original line number Diff line number Diff line Loading @@ -37,6 +37,13 @@ public class TestModeBuilder { private ZenModeConfig.ZenRule mConfigZenRule; public static final ZenMode EXAMPLE = new TestModeBuilder().build(); public static final ZenMode MANUAL_DND = ZenMode.manualDndMode( new AutomaticZenRule.Builder("Manual DND", Uri.parse("rule://dnd")) .setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_PRIORITY) .setZenPolicy(new ZenPolicy.Builder().disallowAllSounds().build()) .build(), true /* isActive */ ); public TestModeBuilder() { // Reasonable defaults Loading packages/SystemUI/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -539,6 +539,7 @@ android_library { "androidx.preference_preference", "androidx.appcompat_appcompat", "androidx.concurrent_concurrent-futures", "androidx.concurrent_concurrent-futures-ktx", "androidx.mediarouter_mediarouter", "androidx.palette_palette", "androidx.legacy_legacy-preference-v14", Loading packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModelTest.kt 0 → 100644 +164 −0 Original line number Diff line number Diff line /* * 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. */ @file:OptIn(ExperimentalCoroutinesApi::class) package com.android.systemui.statusbar.policy.ui.dialog.viewmodel import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.settingslib.notification.modes.TestModeBuilder import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope import com.android.systemui.statusbar.policy.data.repository.fakeZenModeRepository import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith @SmallTest @RunWith(AndroidJUnit4::class) class ModesDialogViewModelTest : SysuiTestCase() { private val kosmos = testKosmos() private val testScope = kosmos.testScope val repository = kosmos.fakeZenModeRepository val interactor = kosmos.zenModeInteractor val underTest = ModesDialogViewModel(context, interactor, kosmos.testDispatcher) @Test fun tiles_filtersOutDisabledModes() = testScope.runTest { val tiles by collectLastValue(underTest.tiles) repository.addModes( listOf( TestModeBuilder().setName("Disabled").setEnabled(false).build(), TestModeBuilder.MANUAL_DND, TestModeBuilder() .setName("Enabled") .setEnabled(true) .setManualInvocationAllowed(true) .build(), TestModeBuilder() .setName("Disabled with manual") .setEnabled(false) .setManualInvocationAllowed(true) .build(), )) runCurrent() assertThat(tiles?.size).isEqualTo(2) with(tiles?.elementAt(0)!!) { assertThat(this.text).isEqualTo("Manual DND") assertThat(this.subtext).isEqualTo("On") assertThat(this.enabled).isEqualTo(true) } with(tiles?.elementAt(1)!!) { assertThat(this.text).isEqualTo("Enabled") assertThat(this.subtext).isEqualTo("Off") assertThat(this.enabled).isEqualTo(false) } } @Test fun tiles_filtersOutInactiveModesWithoutManualInvocation() = testScope.runTest { val tiles by collectLastValue(underTest.tiles) repository.addModes( listOf( TestModeBuilder() .setName("Active without manual") .setActive(true) .setManualInvocationAllowed(false) .build(), TestModeBuilder() .setName("Active with manual") .setTriggerDescription("trigger description") .setActive(true) .setManualInvocationAllowed(true) .build(), TestModeBuilder() .setName("Inactive with manual") .setActive(false) .setManualInvocationAllowed(true) .build(), TestModeBuilder() .setName("Inactive without manual") .setActive(false) .setManualInvocationAllowed(false) .build(), )) runCurrent() assertThat(tiles?.size).isEqualTo(3) with(tiles?.elementAt(0)!!) { assertThat(this.text).isEqualTo("Active without manual") assertThat(this.subtext).isEqualTo("On") assertThat(this.enabled).isEqualTo(true) } with(tiles?.elementAt(1)!!) { assertThat(this.text).isEqualTo("Active with manual") assertThat(this.subtext).isEqualTo("trigger description") assertThat(this.enabled).isEqualTo(true) } with(tiles?.elementAt(2)!!) { assertThat(this.text).isEqualTo("Inactive with manual") assertThat(this.subtext).isEqualTo("Off") assertThat(this.enabled).isEqualTo(false) } } @Test fun onClick_togglesTileState() = testScope.runTest { val tiles by collectLastValue(underTest.tiles) val modeId = "id" repository.addModes( listOf( TestModeBuilder() .setId(modeId) .setName("Test") .setManualInvocationAllowed(true) .build() ) ) runCurrent() assertThat(tiles?.size).isEqualTo(1) assertThat(tiles?.elementAt(0)?.enabled).isFalse() // Trigger onClick tiles?.first()?.onClick?.let { it() } runCurrent() assertThat(tiles?.first()?.enabled).isTrue() // Trigger onClick tiles?.first()?.onClick?.let { it() } runCurrent() assertThat(tiles?.first()?.enabled).isFalse() } } Loading
packages/SettingsLib/src/com/android/settingslib/notification/data/repository/FakeZenModeRepository.kt +20 −2 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import android.app.NotificationManager import android.provider.Settings import com.android.settingslib.notification.modes.TestModeBuilder import com.android.settingslib.notification.modes.ZenMode import java.time.Duration import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow Loading @@ -35,8 +36,7 @@ class FakeZenModeRepository : ZenModeRepository { override val globalZenMode: StateFlow<Int> get() = mutableZenMode.asStateFlow() private val mutableModesFlow: MutableStateFlow<List<ZenMode>> = MutableStateFlow(listOf(TestModeBuilder.EXAMPLE)) private val mutableModesFlow: MutableStateFlow<List<ZenMode>> = MutableStateFlow(listOf()) override val modes: Flow<List<ZenMode>> get() = mutableModesFlow.asStateFlow() Loading @@ -52,6 +52,10 @@ class FakeZenModeRepository : ZenModeRepository { mutableZenMode.value = zenMode } fun addModes(zenModes: List<ZenMode>) { mutableModesFlow.value += zenModes } fun addMode(id: String, active: Boolean = false) { mutableModesFlow.value += newMode(id, active) } Loading @@ -60,6 +64,20 @@ class FakeZenModeRepository : ZenModeRepository { mutableModesFlow.value = mutableModesFlow.value.filter { it.id != id } } override fun activateMode(zenMode: ZenMode, duration: Duration?) { activateMode(zenMode.id) } override fun deactivateMode(zenMode: ZenMode) { deactivateMode(zenMode.id) } fun activateMode(id: String) { val oldMode = mutableModesFlow.value.find { it.id == id } ?: return removeMode(id) mutableModesFlow.value += TestModeBuilder(oldMode).setActive(true).build() } fun deactivateMode(id: String) { val oldMode = mutableModesFlow.value.find { it.id == id } ?: return removeMode(id) Loading
packages/SettingsLib/src/com/android/settingslib/notification/data/repository/ZenModeRepository.kt +13 −0 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ import android.provider.Settings import com.android.settingslib.flags.Flags import com.android.settingslib.notification.modes.ZenMode import com.android.settingslib.notification.modes.ZenModesBackend import java.time.Duration import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.channels.awaitClose Loading Loading @@ -57,6 +58,10 @@ interface ZenModeRepository { /** A list of all existing priority modes. */ val modes: Flow<List<ZenMode>> fun activateMode(zenMode: ZenMode, duration: Duration? = null) fun deactivateMode(zenMode: ZenMode) } @SuppressLint("SharedFlowCreation") Loading Loading @@ -178,4 +183,12 @@ class ZenModeRepositoryImpl( flowOf(emptyList()) } } override fun activateMode(zenMode: ZenMode, duration: Duration?) { backend.activateMode(zenMode, duration) } override fun deactivateMode(zenMode: ZenMode) { backend.deactivateMode(zenMode) } }
packages/SettingsLib/src/com/android/settingslib/notification/modes/TestModeBuilder.java +7 −0 Original line number Diff line number Diff line Loading @@ -37,6 +37,13 @@ public class TestModeBuilder { private ZenModeConfig.ZenRule mConfigZenRule; public static final ZenMode EXAMPLE = new TestModeBuilder().build(); public static final ZenMode MANUAL_DND = ZenMode.manualDndMode( new AutomaticZenRule.Builder("Manual DND", Uri.parse("rule://dnd")) .setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_PRIORITY) .setZenPolicy(new ZenPolicy.Builder().disallowAllSounds().build()) .build(), true /* isActive */ ); public TestModeBuilder() { // Reasonable defaults Loading
packages/SystemUI/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -539,6 +539,7 @@ android_library { "androidx.preference_preference", "androidx.appcompat_appcompat", "androidx.concurrent_concurrent-futures", "androidx.concurrent_concurrent-futures-ktx", "androidx.mediarouter_mediarouter", "androidx.palette_palette", "androidx.legacy_legacy-preference-v14", Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModelTest.kt 0 → 100644 +164 −0 Original line number Diff line number Diff line /* * 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. */ @file:OptIn(ExperimentalCoroutinesApi::class) package com.android.systemui.statusbar.policy.ui.dialog.viewmodel import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.settingslib.notification.modes.TestModeBuilder import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope import com.android.systemui.statusbar.policy.data.repository.fakeZenModeRepository import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith @SmallTest @RunWith(AndroidJUnit4::class) class ModesDialogViewModelTest : SysuiTestCase() { private val kosmos = testKosmos() private val testScope = kosmos.testScope val repository = kosmos.fakeZenModeRepository val interactor = kosmos.zenModeInteractor val underTest = ModesDialogViewModel(context, interactor, kosmos.testDispatcher) @Test fun tiles_filtersOutDisabledModes() = testScope.runTest { val tiles by collectLastValue(underTest.tiles) repository.addModes( listOf( TestModeBuilder().setName("Disabled").setEnabled(false).build(), TestModeBuilder.MANUAL_DND, TestModeBuilder() .setName("Enabled") .setEnabled(true) .setManualInvocationAllowed(true) .build(), TestModeBuilder() .setName("Disabled with manual") .setEnabled(false) .setManualInvocationAllowed(true) .build(), )) runCurrent() assertThat(tiles?.size).isEqualTo(2) with(tiles?.elementAt(0)!!) { assertThat(this.text).isEqualTo("Manual DND") assertThat(this.subtext).isEqualTo("On") assertThat(this.enabled).isEqualTo(true) } with(tiles?.elementAt(1)!!) { assertThat(this.text).isEqualTo("Enabled") assertThat(this.subtext).isEqualTo("Off") assertThat(this.enabled).isEqualTo(false) } } @Test fun tiles_filtersOutInactiveModesWithoutManualInvocation() = testScope.runTest { val tiles by collectLastValue(underTest.tiles) repository.addModes( listOf( TestModeBuilder() .setName("Active without manual") .setActive(true) .setManualInvocationAllowed(false) .build(), TestModeBuilder() .setName("Active with manual") .setTriggerDescription("trigger description") .setActive(true) .setManualInvocationAllowed(true) .build(), TestModeBuilder() .setName("Inactive with manual") .setActive(false) .setManualInvocationAllowed(true) .build(), TestModeBuilder() .setName("Inactive without manual") .setActive(false) .setManualInvocationAllowed(false) .build(), )) runCurrent() assertThat(tiles?.size).isEqualTo(3) with(tiles?.elementAt(0)!!) { assertThat(this.text).isEqualTo("Active without manual") assertThat(this.subtext).isEqualTo("On") assertThat(this.enabled).isEqualTo(true) } with(tiles?.elementAt(1)!!) { assertThat(this.text).isEqualTo("Active with manual") assertThat(this.subtext).isEqualTo("trigger description") assertThat(this.enabled).isEqualTo(true) } with(tiles?.elementAt(2)!!) { assertThat(this.text).isEqualTo("Inactive with manual") assertThat(this.subtext).isEqualTo("Off") assertThat(this.enabled).isEqualTo(false) } } @Test fun onClick_togglesTileState() = testScope.runTest { val tiles by collectLastValue(underTest.tiles) val modeId = "id" repository.addModes( listOf( TestModeBuilder() .setId(modeId) .setName("Test") .setManualInvocationAllowed(true) .build() ) ) runCurrent() assertThat(tiles?.size).isEqualTo(1) assertThat(tiles?.elementAt(0)?.enabled).isFalse() // Trigger onClick tiles?.first()?.onClick?.let { it() } runCurrent() assertThat(tiles?.first()?.enabled).isTrue() // Trigger onClick tiles?.first()?.onClick?.let { it() } runCurrent() assertThat(tiles?.first()?.enabled).isFalse() } }