1 /* 2 * 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.systemui.statusbar.notification.emptyshade.ui.viewmodel 18 19 import android.app.Flags 20 import android.app.NotificationManager.Policy 21 import android.content.res.Configuration 22 import android.os.LocaleList 23 import android.platform.test.annotations.DisableFlags 24 import android.platform.test.annotations.EnableFlags 25 import android.platform.test.flag.junit.FlagsParameterization 26 import android.provider.Settings 27 import android.service.notification.ZenPolicy.VISUAL_EFFECT_NOTIFICATION_LIST 28 import androidx.test.filters.SmallTest 29 import com.android.settingslib.notification.data.repository.updateNotificationPolicy 30 import com.android.settingslib.notification.modes.TestModeBuilder 31 import com.android.systemui.SysuiTestCase 32 import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository 33 import com.android.systemui.coroutines.collectLastValue 34 import com.android.systemui.flags.andSceneContainer 35 import com.android.systemui.kosmos.testScope 36 import com.android.systemui.shared.settings.data.repository.fakeSecureSettingsRepository 37 import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository 38 import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix 39 import com.android.systemui.statusbar.policy.data.repository.zenModeRepository 40 import com.android.systemui.testKosmos 41 import com.google.common.truth.Truth.assertThat 42 import java.util.Locale 43 import kotlinx.coroutines.test.runCurrent 44 import kotlinx.coroutines.test.runTest 45 import org.junit.After 46 import org.junit.Before 47 import org.junit.Test 48 import org.junit.runner.RunWith 49 import platform.test.runner.parameterized.ParameterizedAndroidJunit4 50 import platform.test.runner.parameterized.Parameters 51 52 @RunWith(ParameterizedAndroidJunit4::class) 53 @SmallTest 54 class EmptyShadeViewModelTest(flags: FlagsParameterization) : SysuiTestCase() { 55 private val kosmos = testKosmos() 56 private val testScope = kosmos.testScope 57 private val zenModeRepository = kosmos.zenModeRepository 58 private val activeNotificationListRepository = kosmos.activeNotificationListRepository 59 private val fakeSecureSettingsRepository = kosmos.fakeSecureSettingsRepository 60 private val fakeConfigurationRepository = kosmos.fakeConfigurationRepository 61 62 /** Backup of the current locales, to be restored at the end of the test if they are changed. */ 63 private lateinit var originalLocales: LocaleList 64 <lambda>null65 private val underTest by lazy { kosmos.emptyShadeViewModel } 66 67 companion object { 68 @JvmStatic 69 @Parameters(name = "{0}") getParamsnull70 fun getParams(): List<FlagsParameterization> { 71 return FlagsParameterization.allCombinationsOf().andSceneContainer() 72 } 73 } 74 75 init { 76 mSetFlagsRule.setFlagsParameterization(flags) 77 } 78 79 @Before setUpnull80 fun setUp() { 81 originalLocales = context.resources.configuration.locales 82 updateLocales(LocaleList(Locale.US)) 83 } 84 85 @After tearDownnull86 fun tearDown() { 87 // Make sure we restore the original locale even if a test fails after changing it 88 updateLocales(originalLocales) 89 } 90 91 @Test areNotificationsHiddenInShade_truenull92 fun areNotificationsHiddenInShade_true() = 93 testScope.runTest { 94 val hidden by collectLastValue(underTest.areNotificationsHiddenInShade) 95 96 zenModeRepository.updateNotificationPolicy( 97 suppressedVisualEffects = Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST 98 ) 99 zenModeRepository.updateZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) 100 runCurrent() 101 102 assertThat(hidden).isTrue() 103 } 104 105 @Test areNotificationsHiddenInShade_falsenull106 fun areNotificationsHiddenInShade_false() = 107 testScope.runTest { 108 val hidden by collectLastValue(underTest.areNotificationsHiddenInShade) 109 110 zenModeRepository.updateNotificationPolicy( 111 suppressedVisualEffects = Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST 112 ) 113 zenModeRepository.updateZenMode(Settings.Global.ZEN_MODE_OFF) 114 runCurrent() 115 116 assertThat(hidden).isFalse() 117 } 118 119 @Test hasFilteredOutSeenNotifications_truenull120 fun hasFilteredOutSeenNotifications_true() = 121 testScope.runTest { 122 val hasFilteredNotifs by collectLastValue(underTest.hasFilteredOutSeenNotifications) 123 124 activeNotificationListRepository.hasFilteredOutSeenNotifications.value = true 125 runCurrent() 126 127 assertThat(hasFilteredNotifs).isTrue() 128 } 129 130 @Test hasFilteredOutSeenNotifications_falsenull131 fun hasFilteredOutSeenNotifications_false() = 132 testScope.runTest { 133 val hasFilteredNotifs by collectLastValue(underTest.hasFilteredOutSeenNotifications) 134 135 activeNotificationListRepository.hasFilteredOutSeenNotifications.value = false 136 runCurrent() 137 138 assertThat(hasFilteredNotifs).isFalse() 139 } 140 141 @Test 142 @EnableFlags(ModesEmptyShadeFix.FLAG_NAME) 143 @DisableFlags(Flags.FLAG_MODES_UI) text_changesWhenNotifsHiddenInShadenull144 fun text_changesWhenNotifsHiddenInShade() = 145 testScope.runTest { 146 val text by collectLastValue(underTest.text) 147 148 zenModeRepository.updateNotificationPolicy( 149 suppressedVisualEffects = Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST 150 ) 151 zenModeRepository.updateZenMode(Settings.Global.ZEN_MODE_OFF) 152 runCurrent() 153 154 assertThat(text).isEqualTo("No notifications") 155 156 zenModeRepository.updateNotificationPolicy( 157 suppressedVisualEffects = Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST 158 ) 159 zenModeRepository.updateZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) 160 runCurrent() 161 162 assertThat(text).isEqualTo("Notifications paused by Do Not Disturb") 163 } 164 165 @Test 166 @EnableFlags(ModesEmptyShadeFix.FLAG_NAME, Flags.FLAG_MODES_UI) text_changesWhenLocaleChangesnull167 fun text_changesWhenLocaleChanges() = 168 testScope.runTest { 169 val text by collectLastValue(underTest.text) 170 171 zenModeRepository.updateNotificationPolicy( 172 suppressedVisualEffects = Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST 173 ) 174 zenModeRepository.updateZenMode(Settings.Global.ZEN_MODE_OFF) 175 runCurrent() 176 177 assertThat(text).isEqualTo("No notifications") 178 179 updateLocales(LocaleList(Locale.GERMAN)) 180 runCurrent() 181 182 assertThat(text).isEqualTo("Keine Benachrichtigungen") 183 184 // Make sure we restore the original locales 185 updateLocales(originalLocales) 186 } 187 188 @Test 189 @EnableFlags(ModesEmptyShadeFix.FLAG_NAME, Flags.FLAG_MODES_UI) text_reflectsModesHidingNotificationsnull190 fun text_reflectsModesHidingNotifications() = 191 testScope.runTest { 192 val text by collectLastValue(underTest.text) 193 194 assertThat(text).isEqualTo("No notifications") 195 196 zenModeRepository.addMode( 197 TestModeBuilder() 198 .setId("Do not disturb") 199 .setName("Do not disturb") 200 .setActive(true) 201 .setVisualEffect(VISUAL_EFFECT_NOTIFICATION_LIST, /* allowed= */ false) 202 .build() 203 ) 204 runCurrent() 205 assertThat(text).isEqualTo("Notifications paused by Do not disturb") 206 207 zenModeRepository.addMode( 208 TestModeBuilder() 209 .setId("Work") 210 .setName("Work") 211 .setActive(true) 212 .setVisualEffect(VISUAL_EFFECT_NOTIFICATION_LIST, /* allowed= */ false) 213 .build() 214 ) 215 runCurrent() 216 assertThat(text).isEqualTo("Notifications paused by Do not disturb and one other mode") 217 218 zenModeRepository.addMode( 219 TestModeBuilder() 220 .setId("Gym") 221 .setName("Gym") 222 .setActive(true) 223 .setVisualEffect(VISUAL_EFFECT_NOTIFICATION_LIST, /* allowed= */ false) 224 .build() 225 ) 226 runCurrent() 227 assertThat(text).isEqualTo("Notifications paused by Do not disturb and 2 other modes") 228 229 zenModeRepository.deactivateMode("Do not disturb") 230 zenModeRepository.deactivateMode("Work") 231 runCurrent() 232 assertThat(text).isEqualTo("Notifications paused by Gym") 233 } 234 235 @Test 236 @EnableFlags(ModesEmptyShadeFix.FLAG_NAME) footer_isVisibleWhenSeenNotifsAreFilteredOutnull237 fun footer_isVisibleWhenSeenNotifsAreFilteredOut() = 238 testScope.runTest { 239 val footerVisible by collectLastValue(underTest.footer.isVisible) 240 241 activeNotificationListRepository.hasFilteredOutSeenNotifications.value = false 242 runCurrent() 243 244 assertThat(footerVisible).isFalse() 245 246 activeNotificationListRepository.hasFilteredOutSeenNotifications.value = true 247 runCurrent() 248 249 assertThat(footerVisible).isTrue() 250 } 251 252 @Test 253 @EnableFlags(ModesEmptyShadeFix.FLAG_NAME, Flags.FLAG_MODES_UI) onClick_whenHistoryDisabled_leadsToSettingsPagenull254 fun onClick_whenHistoryDisabled_leadsToSettingsPage() = 255 testScope.runTest { 256 val onClick by collectLastValue(underTest.onClick) 257 runCurrent() 258 259 fakeSecureSettingsRepository.setInt(Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 0) 260 261 assertThat(onClick?.targetIntent?.action) 262 .isEqualTo(Settings.ACTION_NOTIFICATION_SETTINGS) 263 assertThat(onClick?.backStack).isEmpty() 264 } 265 266 @Test 267 @EnableFlags(ModesEmptyShadeFix.FLAG_NAME, Flags.FLAG_MODES_UI) onClick_whenHistoryEnabled_leadsToHistoryPagenull268 fun onClick_whenHistoryEnabled_leadsToHistoryPage() = 269 testScope.runTest { 270 val onClick by collectLastValue(underTest.onClick) 271 runCurrent() 272 273 fakeSecureSettingsRepository.setInt(Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 1) 274 275 assertThat(onClick?.targetIntent?.action) 276 .isEqualTo(Settings.ACTION_NOTIFICATION_HISTORY) 277 assertThat(onClick?.backStack?.map { it.action }) 278 .containsExactly(Settings.ACTION_NOTIFICATION_SETTINGS) 279 } 280 281 @Test 282 @EnableFlags(ModesEmptyShadeFix.FLAG_NAME, Flags.FLAG_MODES_UI) onClick_whenOneModeHidingNotifications_leadsToModeSettingsnull283 fun onClick_whenOneModeHidingNotifications_leadsToModeSettings() = 284 testScope.runTest { 285 val onClick by collectLastValue(underTest.onClick) 286 runCurrent() 287 288 zenModeRepository.addMode( 289 TestModeBuilder() 290 .setId("ID") 291 .setActive(true) 292 .setVisualEffect(VISUAL_EFFECT_NOTIFICATION_LIST, /* allowed= */ false) 293 .build() 294 ) 295 runCurrent() 296 297 assertThat(onClick?.targetIntent?.action) 298 .isEqualTo(Settings.ACTION_AUTOMATIC_ZEN_RULE_SETTINGS) 299 assertThat( 300 onClick?.targetIntent?.extras?.getString(Settings.EXTRA_AUTOMATIC_ZEN_RULE_ID) 301 ) 302 .isEqualTo("ID") 303 assertThat(onClick?.backStack?.map { it.action }) 304 .containsExactly(Settings.ACTION_ZEN_MODE_SETTINGS) 305 } 306 307 @Test 308 @EnableFlags(ModesEmptyShadeFix.FLAG_NAME, Flags.FLAG_MODES_UI) onClick_whenMultipleModesHidingNotifications_leadsToGeneralModesSettingsnull309 fun onClick_whenMultipleModesHidingNotifications_leadsToGeneralModesSettings() = 310 testScope.runTest { 311 val onClick by collectLastValue(underTest.onClick) 312 runCurrent() 313 314 zenModeRepository.addMode( 315 TestModeBuilder() 316 .setActive(true) 317 .setVisualEffect(VISUAL_EFFECT_NOTIFICATION_LIST, /* allowed= */ false) 318 .build() 319 ) 320 zenModeRepository.addMode( 321 TestModeBuilder() 322 .setActive(true) 323 .setVisualEffect(VISUAL_EFFECT_NOTIFICATION_LIST, /* allowed= */ false) 324 .build() 325 ) 326 runCurrent() 327 328 assertThat(onClick?.targetIntent?.action).isEqualTo(Settings.ACTION_ZEN_MODE_SETTINGS) 329 assertThat(onClick?.backStack).isEmpty() 330 } 331 updateLocalesnull332 private fun updateLocales(locales: LocaleList) { 333 val configuration = Configuration() 334 configuration.setLocales(locales) 335 context.resources.updateConfiguration(configuration, context.resources.displayMetrics) 336 fakeConfigurationRepository.onConfigurationChange(configuration) 337 } 338 } 339