Loading packages/SettingsLib/src/com/android/settingslib/notification/data/repository/FakeZenModeRepository.kt +27 −2 Original line number Diff line number Diff line Loading @@ -18,6 +18,9 @@ package com.android.settingslib.notification.data.repository import android.app.NotificationManager import android.provider.Settings import com.android.settingslib.notification.modes.TestModeBuilder import com.android.settingslib.notification.modes.ZenMode import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow Loading @@ -32,6 +35,11 @@ class FakeZenModeRepository : ZenModeRepository { override val globalZenMode: StateFlow<Int> get() = mutableZenMode.asStateFlow() private val mutableModesFlow: MutableStateFlow<List<ZenMode>> = MutableStateFlow(listOf(TestModeBuilder.EXAMPLE)) override val modes: Flow<List<ZenMode>> get() = mutableModesFlow.asStateFlow() init { updateNotificationPolicy() } Loading @@ -43,6 +51,20 @@ class FakeZenModeRepository : ZenModeRepository { fun updateZenMode(zenMode: Int) { mutableZenMode.value = zenMode } fun addMode(id: String, active: Boolean = false) { mutableModesFlow.value += newMode(id, active) } fun removeMode(id: String) { mutableModesFlow.value = mutableModesFlow.value.filter { it.id != id } } fun deactivateMode(id: String) { val oldMode = mutableModesFlow.value.find { it.id == id } ?: return removeMode(id) mutableModesFlow.value += TestModeBuilder(oldMode).setActive(false).build() } } fun FakeZenModeRepository.updateNotificationPolicy( Loading @@ -61,5 +83,8 @@ fun FakeZenModeRepository.updateNotificationPolicy( suppressedVisualEffects, state, priorityConversationSenders, ) ) )) private fun newMode(id: String, active: Boolean = false): ZenMode { return TestModeBuilder().setId(id).setName("Mode $id").setActive(active).build() } packages/SettingsLib/src/com/android/settingslib/notification/data/repository/ZenModeRepository.kt +54 −1 Original line number Diff line number Diff line Loading @@ -18,18 +18,26 @@ package com.android.settingslib.notification.data.repository import android.app.NotificationManager import android.content.BroadcastReceiver import android.content.ContentResolver import android.content.Context import android.content.Intent import android.content.IntentFilter import android.database.ContentObserver import android.os.Handler 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 kotlin.coroutines.CoroutineContext import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onStart Loading @@ -44,11 +52,16 @@ interface ZenModeRepository { /** @see NotificationManager.getZenMode */ val globalZenMode: StateFlow<Int?> /** A list of all existing priority modes. */ val modes: Flow<List<ZenMode>> } class ZenModeRepositoryImpl( private val context: Context, private val notificationManager: NotificationManager, private val backend: ZenModesBackend, private val contentResolver: ContentResolver, val scope: CoroutineScope, val backgroundCoroutineContext: CoroutineContext, // This is nullable just to simplify testing, since SettingsLib doesn't have a good way Loading Loading @@ -87,7 +100,6 @@ class ZenModeRepositoryImpl( .let { if (Flags.volumePanelBroadcastFix()) { it.flowOn(backgroundCoroutineContext) .stateIn(scope, SharingStarted.WhileSubscribed(), null) } else { it.shareIn( started = SharingStarted.WhileSubscribed(), Loading Loading @@ -121,4 +133,45 @@ class ZenModeRepositoryImpl( .onStart { emit(mapper()) } .flowOn(backgroundCoroutineContext) .stateIn(scope, SharingStarted.WhileSubscribed(), null) private val zenConfigChanged by lazy { if (android.app.Flags.modesUi()) { callbackFlow { // emit an initial value trySend(Unit) val observer = object : ContentObserver(backgroundHandler) { override fun onChange(selfChange: Boolean) { trySend(Unit) } } contentResolver.registerContentObserver( Settings.Global.getUriFor(Settings.Global.ZEN_MODE), /* notifyForDescendants= */ false, observer) contentResolver.registerContentObserver( Settings.Global.getUriFor(Settings.Global.ZEN_MODE_CONFIG_ETAG), /* notifyForDescendants= */ false, observer) awaitClose { contentResolver.unregisterContentObserver(observer) } } .flowOn(backgroundCoroutineContext) } else { flowOf(Unit) } } override val modes: Flow<List<ZenMode>> by lazy { if (android.app.Flags.modesUi()) { zenConfigChanged .map { backend.modes } .distinctUntilChanged() .flowOn(backgroundCoroutineContext) } else { flowOf(emptyList()) } } } packages/SettingsLib/src/com/android/settingslib/notification/modes/TestModeBuilder.java 0 → 100644 +176 −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. */ package com.android.settingslib.notification.modes; import android.app.AutomaticZenRule; import android.app.NotificationManager; import android.content.ComponentName; import android.net.Uri; import android.service.notification.Condition; import android.service.notification.ZenDeviceEffects; import android.service.notification.ZenModeConfig; import android.service.notification.ZenPolicy; import androidx.annotation.DrawableRes; import androidx.annotation.Nullable; import java.util.Random; public class TestModeBuilder { private String mId; private AutomaticZenRule mRule; private ZenModeConfig.ZenRule mConfigZenRule; public static final ZenMode EXAMPLE = new TestModeBuilder().build(); public TestModeBuilder() { // Reasonable defaults int id = new Random().nextInt(1000); mId = "rule_" + id; mRule = new AutomaticZenRule.Builder("Test Rule #" + id, Uri.parse("rule://" + id)) .setPackage("some_package") .setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_PRIORITY) .setZenPolicy(new ZenPolicy.Builder().disallowAllSounds().build()) .build(); mConfigZenRule = new ZenModeConfig.ZenRule(); mConfigZenRule.enabled = true; mConfigZenRule.pkg = "some_package"; } public TestModeBuilder(ZenMode previous) { mId = previous.getId(); mRule = previous.getRule(); mConfigZenRule = new ZenModeConfig.ZenRule(); mConfigZenRule.enabled = previous.getRule().isEnabled(); mConfigZenRule.pkg = previous.getRule().getPackageName(); setActive(previous.isActive()); } public TestModeBuilder setId(String id) { mId = id; return this; } public TestModeBuilder setAzr(AutomaticZenRule rule) { mRule = rule; mConfigZenRule.pkg = rule.getPackageName(); mConfigZenRule.conditionId = rule.getConditionId(); mConfigZenRule.enabled = rule.isEnabled(); return this; } public TestModeBuilder setConfigZenRule(ZenModeConfig.ZenRule configZenRule) { mConfigZenRule = configZenRule; return this; } public TestModeBuilder setName(String name) { mRule.setName(name); mConfigZenRule.name = name; return this; } public TestModeBuilder setPackage(String pkg) { mRule.setPackageName(pkg); mConfigZenRule.pkg = pkg; return this; } public TestModeBuilder setOwner(ComponentName owner) { mRule.setOwner(owner); mConfigZenRule.component = owner; return this; } public TestModeBuilder setConfigurationActivity(ComponentName configActivity) { mRule.setConfigurationActivity(configActivity); mConfigZenRule.configurationActivity = configActivity; return this; } public TestModeBuilder setConditionId(Uri conditionId) { mRule.setConditionId(conditionId); mConfigZenRule.conditionId = conditionId; return this; } public TestModeBuilder setType(@AutomaticZenRule.Type int type) { mRule.setType(type); mConfigZenRule.type = type; return this; } public TestModeBuilder setInterruptionFilter( @NotificationManager.InterruptionFilter int interruptionFilter) { mRule.setInterruptionFilter(interruptionFilter); mConfigZenRule.zenMode = NotificationManager.zenModeFromInterruptionFilter( interruptionFilter, NotificationManager.INTERRUPTION_FILTER_PRIORITY); return this; } public TestModeBuilder setZenPolicy(@Nullable ZenPolicy policy) { mRule.setZenPolicy(policy); mConfigZenRule.zenPolicy = policy; return this; } public TestModeBuilder setDeviceEffects(@Nullable ZenDeviceEffects deviceEffects) { mRule.setDeviceEffects(deviceEffects); mConfigZenRule.zenDeviceEffects = deviceEffects; return this; } public TestModeBuilder setEnabled(boolean enabled) { mRule.setEnabled(enabled); mConfigZenRule.enabled = enabled; return this; } public TestModeBuilder setManualInvocationAllowed(boolean allowed) { mRule.setManualInvocationAllowed(allowed); mConfigZenRule.allowManualInvocation = allowed; return this; } public TestModeBuilder setTriggerDescription(@Nullable String triggerDescription) { mRule.setTriggerDescription(triggerDescription); mConfigZenRule.triggerDescription = triggerDescription; return this; } public TestModeBuilder setIconResId(@DrawableRes int iconResId) { mRule.setIconResId(iconResId); return this; } public TestModeBuilder setActive(boolean active) { if (active) { mConfigZenRule.enabled = true; mConfigZenRule.condition = new Condition(mRule.getConditionId(), "...", Condition.STATE_TRUE); } else { mConfigZenRule.condition = null; } return this; } public ZenMode build() { return new ZenMode(mId, mRule, mConfigZenRule); } } packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/data/repository/ZenModeRepositoryTest.kt +66 −0 Original line number Diff line number Diff line Loading @@ -18,13 +18,18 @@ package com.android.settingslib.notification.data.repository import android.app.NotificationManager import android.content.BroadcastReceiver import android.content.ContentResolver import android.content.Context import android.content.Intent import android.database.ContentObserver import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.provider.Settings.Global import androidx.test.filters.SmallTest import com.android.settingslib.flags.Flags import com.android.settingslib.notification.modes.TestModeBuilder import com.android.settingslib.notification.modes.ZenMode import com.android.settingslib.notification.modes.ZenModesBackend import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.launchIn Loading @@ -36,6 +41,7 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.ArgumentMatchers.eq import org.mockito.Captor import org.mockito.Mock import org.mockito.Mockito.any Loading @@ -52,8 +58,16 @@ class ZenModeRepositoryTest { @Mock private lateinit var notificationManager: NotificationManager @Mock private lateinit var zenModesBackend: ZenModesBackend @Mock private lateinit var contentResolver: ContentResolver @Captor private lateinit var receiverCaptor: ArgumentCaptor<BroadcastReceiver> @Captor private lateinit var zenModeObserverCaptor: ArgumentCaptor<ContentObserver> @Captor private lateinit var zenConfigObserverCaptor: ArgumentCaptor<ContentObserver> private lateinit var underTest: ZenModeRepository private val testScope: TestScope = TestScope() Loading @@ -66,6 +80,8 @@ class ZenModeRepositoryTest { ZenModeRepositoryImpl( context, notificationManager, zenModesBackend, contentResolver, testScope.backgroundScope, testScope.testScheduler, backgroundHandler = null, Loading Loading @@ -128,11 +144,61 @@ class ZenModeRepositoryTest { } } @EnableFlags(android.app.Flags.FLAG_MODES_UI) @Test fun modesListEmitsOnSettingsChange() { testScope.runTest { val values = mutableListOf<List<ZenMode>>() val modes1 = listOf(TestModeBuilder().setId("One").build()) `when`(zenModesBackend.modes).thenReturn(modes1) underTest.modes.onEach { values.add(it) }.launchIn(backgroundScope) runCurrent() // zen mode change triggers update val modes2 = listOf(TestModeBuilder().setId("Two").build()) `when`(zenModesBackend.modes).thenReturn(modes2) triggerZenModeSettingUpdate() runCurrent() // zen config change also triggers update val modes3 = listOf(TestModeBuilder().setId("Three").build()) `when`(zenModesBackend.modes).thenReturn(modes3) triggerZenConfigSettingUpdate() runCurrent() // setting update with no list change doesn't trigger update triggerZenModeSettingUpdate() runCurrent() assertThat(values).containsExactly(modes1, modes2, modes3).inOrder() } } private fun triggerIntent(action: String) { verify(context).registerReceiver(receiverCaptor.capture(), any(), any(), any()) receiverCaptor.value.onReceive(context, Intent(action)) } private fun triggerZenModeSettingUpdate() { verify(contentResolver) .registerContentObserver( eq(Global.getUriFor(Global.ZEN_MODE)), eq(false), zenModeObserverCaptor.capture(), ) zenModeObserverCaptor.value.onChange(false) } private fun triggerZenConfigSettingUpdate() { verify(contentResolver) .registerContentObserver( eq(Global.getUriFor(Global.ZEN_MODE_CONFIG_ETAG)), eq(false), zenConfigObserverCaptor.capture(), ) zenConfigObserverCaptor.value.onChange(false) } private companion object { val testPolicy1 = NotificationManager.Policy( Loading packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt +13 −12 Original line number Diff line number Diff line Loading @@ -20,9 +20,6 @@ import android.app.Flags import android.os.UserHandle import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS import android.provider.Settings.Global.ZEN_MODE_NO_INTERRUPTIONS import android.provider.Settings.Global.ZEN_MODE_OFF import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.settingslib.notification.data.repository.FakeZenModeRepository Loading Loading @@ -65,24 +62,28 @@ class ModesTileDataInteractorTest : SysuiTestCase() { @EnableFlags(Flags.FLAG_MODES_UI) @Test fun dataMatchesTheRepository() = runTest { fun isActivatedWhenModesChange() = runTest { val dataList: List<ModesTileModel> by collectValues(underTest.tileData(TEST_USER, flowOf(DataUpdateTrigger.InitialRequest))) runCurrent() assertThat(dataList.map { it.isActivated }).containsExactly(false).inOrder() // Enable zen mode zenModeRepository.updateZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS) // Add active mode zenModeRepository.addMode(id = "One", active = true) runCurrent() assertThat(dataList.map { it.isActivated }).containsExactly(false, true).inOrder() // Change zen mode: it's still enabled, so this shouldn't cause another emission zenModeRepository.updateZenMode(ZEN_MODE_NO_INTERRUPTIONS) // Add another mode: state hasn't changed, so this shouldn't cause another emission zenModeRepository.addMode(id = "Two", active = true) runCurrent() assertThat(dataList.map { it.isActivated }).containsExactly(false, true).inOrder() // Disable zen mode zenModeRepository.updateZenMode(ZEN_MODE_OFF) // Remove a mode and disable the other zenModeRepository.removeMode("One") runCurrent() assertThat(dataList.map { it.isActivated }).containsExactly(false, true, false) zenModeRepository.deactivateMode("Two") runCurrent() assertThat(dataList.map { it.isActivated }).containsExactly(false, true, false).inOrder() } private companion object { Loading Loading
packages/SettingsLib/src/com/android/settingslib/notification/data/repository/FakeZenModeRepository.kt +27 −2 Original line number Diff line number Diff line Loading @@ -18,6 +18,9 @@ package com.android.settingslib.notification.data.repository import android.app.NotificationManager import android.provider.Settings import com.android.settingslib.notification.modes.TestModeBuilder import com.android.settingslib.notification.modes.ZenMode import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow Loading @@ -32,6 +35,11 @@ class FakeZenModeRepository : ZenModeRepository { override val globalZenMode: StateFlow<Int> get() = mutableZenMode.asStateFlow() private val mutableModesFlow: MutableStateFlow<List<ZenMode>> = MutableStateFlow(listOf(TestModeBuilder.EXAMPLE)) override val modes: Flow<List<ZenMode>> get() = mutableModesFlow.asStateFlow() init { updateNotificationPolicy() } Loading @@ -43,6 +51,20 @@ class FakeZenModeRepository : ZenModeRepository { fun updateZenMode(zenMode: Int) { mutableZenMode.value = zenMode } fun addMode(id: String, active: Boolean = false) { mutableModesFlow.value += newMode(id, active) } fun removeMode(id: String) { mutableModesFlow.value = mutableModesFlow.value.filter { it.id != id } } fun deactivateMode(id: String) { val oldMode = mutableModesFlow.value.find { it.id == id } ?: return removeMode(id) mutableModesFlow.value += TestModeBuilder(oldMode).setActive(false).build() } } fun FakeZenModeRepository.updateNotificationPolicy( Loading @@ -61,5 +83,8 @@ fun FakeZenModeRepository.updateNotificationPolicy( suppressedVisualEffects, state, priorityConversationSenders, ) ) )) private fun newMode(id: String, active: Boolean = false): ZenMode { return TestModeBuilder().setId(id).setName("Mode $id").setActive(active).build() }
packages/SettingsLib/src/com/android/settingslib/notification/data/repository/ZenModeRepository.kt +54 −1 Original line number Diff line number Diff line Loading @@ -18,18 +18,26 @@ package com.android.settingslib.notification.data.repository import android.app.NotificationManager import android.content.BroadcastReceiver import android.content.ContentResolver import android.content.Context import android.content.Intent import android.content.IntentFilter import android.database.ContentObserver import android.os.Handler 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 kotlin.coroutines.CoroutineContext import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onStart Loading @@ -44,11 +52,16 @@ interface ZenModeRepository { /** @see NotificationManager.getZenMode */ val globalZenMode: StateFlow<Int?> /** A list of all existing priority modes. */ val modes: Flow<List<ZenMode>> } class ZenModeRepositoryImpl( private val context: Context, private val notificationManager: NotificationManager, private val backend: ZenModesBackend, private val contentResolver: ContentResolver, val scope: CoroutineScope, val backgroundCoroutineContext: CoroutineContext, // This is nullable just to simplify testing, since SettingsLib doesn't have a good way Loading Loading @@ -87,7 +100,6 @@ class ZenModeRepositoryImpl( .let { if (Flags.volumePanelBroadcastFix()) { it.flowOn(backgroundCoroutineContext) .stateIn(scope, SharingStarted.WhileSubscribed(), null) } else { it.shareIn( started = SharingStarted.WhileSubscribed(), Loading Loading @@ -121,4 +133,45 @@ class ZenModeRepositoryImpl( .onStart { emit(mapper()) } .flowOn(backgroundCoroutineContext) .stateIn(scope, SharingStarted.WhileSubscribed(), null) private val zenConfigChanged by lazy { if (android.app.Flags.modesUi()) { callbackFlow { // emit an initial value trySend(Unit) val observer = object : ContentObserver(backgroundHandler) { override fun onChange(selfChange: Boolean) { trySend(Unit) } } contentResolver.registerContentObserver( Settings.Global.getUriFor(Settings.Global.ZEN_MODE), /* notifyForDescendants= */ false, observer) contentResolver.registerContentObserver( Settings.Global.getUriFor(Settings.Global.ZEN_MODE_CONFIG_ETAG), /* notifyForDescendants= */ false, observer) awaitClose { contentResolver.unregisterContentObserver(observer) } } .flowOn(backgroundCoroutineContext) } else { flowOf(Unit) } } override val modes: Flow<List<ZenMode>> by lazy { if (android.app.Flags.modesUi()) { zenConfigChanged .map { backend.modes } .distinctUntilChanged() .flowOn(backgroundCoroutineContext) } else { flowOf(emptyList()) } } }
packages/SettingsLib/src/com/android/settingslib/notification/modes/TestModeBuilder.java 0 → 100644 +176 −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. */ package com.android.settingslib.notification.modes; import android.app.AutomaticZenRule; import android.app.NotificationManager; import android.content.ComponentName; import android.net.Uri; import android.service.notification.Condition; import android.service.notification.ZenDeviceEffects; import android.service.notification.ZenModeConfig; import android.service.notification.ZenPolicy; import androidx.annotation.DrawableRes; import androidx.annotation.Nullable; import java.util.Random; public class TestModeBuilder { private String mId; private AutomaticZenRule mRule; private ZenModeConfig.ZenRule mConfigZenRule; public static final ZenMode EXAMPLE = new TestModeBuilder().build(); public TestModeBuilder() { // Reasonable defaults int id = new Random().nextInt(1000); mId = "rule_" + id; mRule = new AutomaticZenRule.Builder("Test Rule #" + id, Uri.parse("rule://" + id)) .setPackage("some_package") .setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_PRIORITY) .setZenPolicy(new ZenPolicy.Builder().disallowAllSounds().build()) .build(); mConfigZenRule = new ZenModeConfig.ZenRule(); mConfigZenRule.enabled = true; mConfigZenRule.pkg = "some_package"; } public TestModeBuilder(ZenMode previous) { mId = previous.getId(); mRule = previous.getRule(); mConfigZenRule = new ZenModeConfig.ZenRule(); mConfigZenRule.enabled = previous.getRule().isEnabled(); mConfigZenRule.pkg = previous.getRule().getPackageName(); setActive(previous.isActive()); } public TestModeBuilder setId(String id) { mId = id; return this; } public TestModeBuilder setAzr(AutomaticZenRule rule) { mRule = rule; mConfigZenRule.pkg = rule.getPackageName(); mConfigZenRule.conditionId = rule.getConditionId(); mConfigZenRule.enabled = rule.isEnabled(); return this; } public TestModeBuilder setConfigZenRule(ZenModeConfig.ZenRule configZenRule) { mConfigZenRule = configZenRule; return this; } public TestModeBuilder setName(String name) { mRule.setName(name); mConfigZenRule.name = name; return this; } public TestModeBuilder setPackage(String pkg) { mRule.setPackageName(pkg); mConfigZenRule.pkg = pkg; return this; } public TestModeBuilder setOwner(ComponentName owner) { mRule.setOwner(owner); mConfigZenRule.component = owner; return this; } public TestModeBuilder setConfigurationActivity(ComponentName configActivity) { mRule.setConfigurationActivity(configActivity); mConfigZenRule.configurationActivity = configActivity; return this; } public TestModeBuilder setConditionId(Uri conditionId) { mRule.setConditionId(conditionId); mConfigZenRule.conditionId = conditionId; return this; } public TestModeBuilder setType(@AutomaticZenRule.Type int type) { mRule.setType(type); mConfigZenRule.type = type; return this; } public TestModeBuilder setInterruptionFilter( @NotificationManager.InterruptionFilter int interruptionFilter) { mRule.setInterruptionFilter(interruptionFilter); mConfigZenRule.zenMode = NotificationManager.zenModeFromInterruptionFilter( interruptionFilter, NotificationManager.INTERRUPTION_FILTER_PRIORITY); return this; } public TestModeBuilder setZenPolicy(@Nullable ZenPolicy policy) { mRule.setZenPolicy(policy); mConfigZenRule.zenPolicy = policy; return this; } public TestModeBuilder setDeviceEffects(@Nullable ZenDeviceEffects deviceEffects) { mRule.setDeviceEffects(deviceEffects); mConfigZenRule.zenDeviceEffects = deviceEffects; return this; } public TestModeBuilder setEnabled(boolean enabled) { mRule.setEnabled(enabled); mConfigZenRule.enabled = enabled; return this; } public TestModeBuilder setManualInvocationAllowed(boolean allowed) { mRule.setManualInvocationAllowed(allowed); mConfigZenRule.allowManualInvocation = allowed; return this; } public TestModeBuilder setTriggerDescription(@Nullable String triggerDescription) { mRule.setTriggerDescription(triggerDescription); mConfigZenRule.triggerDescription = triggerDescription; return this; } public TestModeBuilder setIconResId(@DrawableRes int iconResId) { mRule.setIconResId(iconResId); return this; } public TestModeBuilder setActive(boolean active) { if (active) { mConfigZenRule.enabled = true; mConfigZenRule.condition = new Condition(mRule.getConditionId(), "...", Condition.STATE_TRUE); } else { mConfigZenRule.condition = null; } return this; } public ZenMode build() { return new ZenMode(mId, mRule, mConfigZenRule); } }
packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/data/repository/ZenModeRepositoryTest.kt +66 −0 Original line number Diff line number Diff line Loading @@ -18,13 +18,18 @@ package com.android.settingslib.notification.data.repository import android.app.NotificationManager import android.content.BroadcastReceiver import android.content.ContentResolver import android.content.Context import android.content.Intent import android.database.ContentObserver import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.provider.Settings.Global import androidx.test.filters.SmallTest import com.android.settingslib.flags.Flags import com.android.settingslib.notification.modes.TestModeBuilder import com.android.settingslib.notification.modes.ZenMode import com.android.settingslib.notification.modes.ZenModesBackend import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.launchIn Loading @@ -36,6 +41,7 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.ArgumentMatchers.eq import org.mockito.Captor import org.mockito.Mock import org.mockito.Mockito.any Loading @@ -52,8 +58,16 @@ class ZenModeRepositoryTest { @Mock private lateinit var notificationManager: NotificationManager @Mock private lateinit var zenModesBackend: ZenModesBackend @Mock private lateinit var contentResolver: ContentResolver @Captor private lateinit var receiverCaptor: ArgumentCaptor<BroadcastReceiver> @Captor private lateinit var zenModeObserverCaptor: ArgumentCaptor<ContentObserver> @Captor private lateinit var zenConfigObserverCaptor: ArgumentCaptor<ContentObserver> private lateinit var underTest: ZenModeRepository private val testScope: TestScope = TestScope() Loading @@ -66,6 +80,8 @@ class ZenModeRepositoryTest { ZenModeRepositoryImpl( context, notificationManager, zenModesBackend, contentResolver, testScope.backgroundScope, testScope.testScheduler, backgroundHandler = null, Loading Loading @@ -128,11 +144,61 @@ class ZenModeRepositoryTest { } } @EnableFlags(android.app.Flags.FLAG_MODES_UI) @Test fun modesListEmitsOnSettingsChange() { testScope.runTest { val values = mutableListOf<List<ZenMode>>() val modes1 = listOf(TestModeBuilder().setId("One").build()) `when`(zenModesBackend.modes).thenReturn(modes1) underTest.modes.onEach { values.add(it) }.launchIn(backgroundScope) runCurrent() // zen mode change triggers update val modes2 = listOf(TestModeBuilder().setId("Two").build()) `when`(zenModesBackend.modes).thenReturn(modes2) triggerZenModeSettingUpdate() runCurrent() // zen config change also triggers update val modes3 = listOf(TestModeBuilder().setId("Three").build()) `when`(zenModesBackend.modes).thenReturn(modes3) triggerZenConfigSettingUpdate() runCurrent() // setting update with no list change doesn't trigger update triggerZenModeSettingUpdate() runCurrent() assertThat(values).containsExactly(modes1, modes2, modes3).inOrder() } } private fun triggerIntent(action: String) { verify(context).registerReceiver(receiverCaptor.capture(), any(), any(), any()) receiverCaptor.value.onReceive(context, Intent(action)) } private fun triggerZenModeSettingUpdate() { verify(contentResolver) .registerContentObserver( eq(Global.getUriFor(Global.ZEN_MODE)), eq(false), zenModeObserverCaptor.capture(), ) zenModeObserverCaptor.value.onChange(false) } private fun triggerZenConfigSettingUpdate() { verify(contentResolver) .registerContentObserver( eq(Global.getUriFor(Global.ZEN_MODE_CONFIG_ETAG)), eq(false), zenConfigObserverCaptor.capture(), ) zenConfigObserverCaptor.value.onChange(false) } private companion object { val testPolicy1 = NotificationManager.Policy( Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt +13 −12 Original line number Diff line number Diff line Loading @@ -20,9 +20,6 @@ import android.app.Flags import android.os.UserHandle import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS import android.provider.Settings.Global.ZEN_MODE_NO_INTERRUPTIONS import android.provider.Settings.Global.ZEN_MODE_OFF import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.settingslib.notification.data.repository.FakeZenModeRepository Loading Loading @@ -65,24 +62,28 @@ class ModesTileDataInteractorTest : SysuiTestCase() { @EnableFlags(Flags.FLAG_MODES_UI) @Test fun dataMatchesTheRepository() = runTest { fun isActivatedWhenModesChange() = runTest { val dataList: List<ModesTileModel> by collectValues(underTest.tileData(TEST_USER, flowOf(DataUpdateTrigger.InitialRequest))) runCurrent() assertThat(dataList.map { it.isActivated }).containsExactly(false).inOrder() // Enable zen mode zenModeRepository.updateZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS) // Add active mode zenModeRepository.addMode(id = "One", active = true) runCurrent() assertThat(dataList.map { it.isActivated }).containsExactly(false, true).inOrder() // Change zen mode: it's still enabled, so this shouldn't cause another emission zenModeRepository.updateZenMode(ZEN_MODE_NO_INTERRUPTIONS) // Add another mode: state hasn't changed, so this shouldn't cause another emission zenModeRepository.addMode(id = "Two", active = true) runCurrent() assertThat(dataList.map { it.isActivated }).containsExactly(false, true).inOrder() // Disable zen mode zenModeRepository.updateZenMode(ZEN_MODE_OFF) // Remove a mode and disable the other zenModeRepository.removeMode("One") runCurrent() assertThat(dataList.map { it.isActivated }).containsExactly(false, true, false) zenModeRepository.deactivateMode("Two") runCurrent() assertThat(dataList.map { it.isActivated }).containsExactly(false, true, false).inOrder() } private companion object { Loading