1 /* <lambda>null2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settingslib.notification.data.repository 18 19 import android.app.NotificationManager 20 import android.content.BroadcastReceiver 21 import android.content.ContentResolver 22 import android.content.Context 23 import android.content.Intent 24 import android.database.ContentObserver 25 import android.os.Parcelable 26 import android.platform.test.annotations.EnableFlags 27 import android.platform.test.flag.junit.SetFlagsRule 28 import android.provider.Settings.Global 29 import androidx.test.filters.SmallTest 30 import com.android.settingslib.notification.modes.TestModeBuilder 31 import com.android.settingslib.notification.modes.ZenMode 32 import com.android.settingslib.notification.modes.ZenModesBackend 33 import com.google.common.truth.Truth.assertThat 34 import kotlinx.coroutines.ExperimentalCoroutinesApi 35 import kotlinx.coroutines.flow.launchIn 36 import kotlinx.coroutines.flow.onEach 37 import kotlinx.coroutines.test.TestScope 38 import kotlinx.coroutines.test.runCurrent 39 import kotlinx.coroutines.test.runTest 40 import org.junit.Before 41 import org.junit.Rule 42 import org.junit.Test 43 import org.junit.runner.RunWith 44 import org.mockito.ArgumentCaptor 45 import org.mockito.ArgumentMatchers.eq 46 import org.mockito.Captor 47 import org.mockito.Mock 48 import org.mockito.Mockito.any 49 import org.mockito.Mockito.verify 50 import org.mockito.Mockito.`when` 51 import org.mockito.MockitoAnnotations 52 import org.robolectric.RobolectricTestRunner 53 54 @OptIn(ExperimentalCoroutinesApi::class) 55 @RunWith(RobolectricTestRunner::class) 56 @SmallTest 57 class ZenModeRepositoryTest { 58 @get:Rule val setFlagsRule = SetFlagsRule() 59 60 @Mock private lateinit var context: Context 61 62 @Mock private lateinit var notificationManager: NotificationManager 63 64 @Mock private lateinit var zenModesBackend: ZenModesBackend 65 66 @Mock private lateinit var contentResolver: ContentResolver 67 68 @Captor private lateinit var receiverCaptor: ArgumentCaptor<BroadcastReceiver> 69 70 @Captor private lateinit var zenModeObserverCaptor: ArgumentCaptor<ContentObserver> 71 72 @Captor private lateinit var zenConfigObserverCaptor: ArgumentCaptor<ContentObserver> 73 74 private lateinit var underTest: ZenModeRepository 75 76 private val testScope: TestScope = TestScope() 77 78 @Before 79 fun setup() { 80 MockitoAnnotations.initMocks(this) 81 82 underTest = 83 ZenModeRepositoryImpl( 84 context, 85 notificationManager, 86 zenModesBackend, 87 contentResolver, 88 testScope.backgroundScope, 89 testScope.testScheduler, 90 backgroundHandler = null, 91 ) 92 } 93 94 @EnableFlags(android.app.Flags.FLAG_MODES_API) 95 @Test 96 fun consolidatedPolicyChanges_repositoryEmits_flagsOn() { 97 testScope.runTest { 98 val values = mutableListOf<NotificationManager.Policy?>() 99 `when`(notificationManager.consolidatedNotificationPolicy).thenReturn(testPolicy1) 100 underTest.consolidatedNotificationPolicy 101 .onEach { values.add(it) } 102 .launchIn(backgroundScope) 103 runCurrent() 104 105 `when`(notificationManager.consolidatedNotificationPolicy).thenReturn(testPolicy2) 106 triggerIntent(NotificationManager.ACTION_CONSOLIDATED_NOTIFICATION_POLICY_CHANGED) 107 runCurrent() 108 109 assertThat(values).containsExactly(null, testPolicy1, testPolicy2).inOrder() 110 } 111 } 112 113 @EnableFlags(android.app.Flags.FLAG_MODES_API) 114 @Test 115 fun consolidatedPolicyChanges_repositoryEmitsFromExtras() { 116 testScope.runTest { 117 val values = mutableListOf<NotificationManager.Policy?>() 118 `when`(notificationManager.consolidatedNotificationPolicy).thenReturn(testPolicy1) 119 underTest.consolidatedNotificationPolicy 120 .onEach { values.add(it) } 121 .launchIn(backgroundScope) 122 runCurrent() 123 124 triggerIntent( 125 NotificationManager.ACTION_CONSOLIDATED_NOTIFICATION_POLICY_CHANGED, 126 extras = mapOf(NotificationManager.EXTRA_NOTIFICATION_POLICY to testPolicy2)) 127 runCurrent() 128 129 assertThat(values).containsExactly(null, testPolicy1, testPolicy2).inOrder() 130 } 131 } 132 133 @Test 134 fun zenModeChanges_repositoryEmits() { 135 testScope.runTest { 136 val values = mutableListOf<Int?>() 137 `when`(notificationManager.zenMode).thenReturn(Global.ZEN_MODE_OFF) 138 underTest.globalZenMode.onEach { values.add(it) }.launchIn(backgroundScope) 139 runCurrent() 140 141 `when`(notificationManager.zenMode).thenReturn(Global.ZEN_MODE_ALARMS) 142 triggerIntent(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED) 143 runCurrent() 144 145 assertThat(values) 146 .containsExactly(null, Global.ZEN_MODE_OFF, Global.ZEN_MODE_ALARMS) 147 .inOrder() 148 } 149 } 150 151 @EnableFlags(android.app.Flags.FLAG_MODES_UI) 152 @Test 153 fun modesListEmitsOnSettingsChange() { 154 testScope.runTest { 155 val values = mutableListOf<List<ZenMode>>() 156 val modes1 = listOf(TestModeBuilder().setId("One").build()) 157 `when`(zenModesBackend.modes).thenReturn(modes1) 158 underTest.modes.onEach { values.add(it) }.launchIn(backgroundScope) 159 runCurrent() 160 161 // zen mode change triggers update 162 val modes2 = listOf(TestModeBuilder().setId("Two").build()) 163 `when`(zenModesBackend.modes).thenReturn(modes2) 164 triggerZenModeSettingUpdate() 165 runCurrent() 166 167 // zen config change also triggers update 168 val modes3 = listOf(TestModeBuilder().setId("Three").build()) 169 `when`(zenModesBackend.modes).thenReturn(modes3) 170 triggerZenConfigSettingUpdate() 171 runCurrent() 172 173 // setting update with no list change doesn't trigger update 174 triggerZenModeSettingUpdate() 175 runCurrent() 176 177 assertThat(values).containsExactly(modes1, modes2, modes3).inOrder() 178 } 179 } 180 181 @EnableFlags(android.app.Flags.FLAG_MODES_UI) 182 @Test 183 fun getModes_returnsModes() { 184 val modesList = listOf(TestModeBuilder().setId("One").build()) 185 `when`(zenModesBackend.modes).thenReturn(modesList) 186 187 assertThat(underTest.getModes()).isEqualTo(modesList) 188 } 189 190 private fun triggerIntent(action: String, extras: Map<String, Parcelable>? = null) { 191 verify(context).registerReceiver(receiverCaptor.capture(), any(), any(), any()) 192 val intent = Intent(action) 193 if (extras?.isNotEmpty() == true) { 194 extras.forEach { (key, value) -> intent.putExtra(key, value) } 195 } 196 receiverCaptor.value.onReceive(context, intent) 197 } 198 199 private fun triggerZenModeSettingUpdate() { 200 verify(contentResolver) 201 .registerContentObserver( 202 eq(Global.getUriFor(Global.ZEN_MODE)), 203 eq(false), 204 zenModeObserverCaptor.capture(), 205 ) 206 zenModeObserverCaptor.value.onChange(false) 207 } 208 209 private fun triggerZenConfigSettingUpdate() { 210 verify(contentResolver) 211 .registerContentObserver( 212 eq(Global.getUriFor(Global.ZEN_MODE_CONFIG_ETAG)), 213 eq(false), 214 zenConfigObserverCaptor.capture(), 215 ) 216 zenConfigObserverCaptor.value.onChange(false) 217 } 218 219 private companion object { 220 val testPolicy1 = 221 NotificationManager.Policy( 222 /* priorityCategories = */ 1, 223 /* priorityCallSenders =*/ 1, 224 /* priorityMessageSenders = */ 1, 225 ) 226 val testPolicy2 = 227 NotificationManager.Policy( 228 /* priorityCategories = */ 2, 229 /* priorityCallSenders =*/ 2, 230 /* priorityMessageSenders = */ 2, 231 ) 232 } 233 } 234