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 @file:OptIn(ExperimentalCoroutinesApi::class) 18 19 package com.android.systemui.dreams.ui.viewmodel 20 21 import android.platform.test.annotations.DisableFlags 22 import android.platform.test.annotations.EnableFlags 23 import androidx.test.ext.junit.runners.AndroidJUnit4 24 import androidx.test.filters.SmallTest 25 import com.android.compose.animation.scene.Swipe 26 import com.android.compose.animation.scene.UserActionResult 27 import com.android.systemui.SysuiTestCase 28 import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository 29 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel 30 import com.android.systemui.communal.domain.interactor.setCommunalAvailable 31 import com.android.systemui.coroutines.collectLastValue 32 import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor 33 import com.android.systemui.flags.EnableSceneContainer 34 import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository 35 import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus 36 import com.android.systemui.kosmos.testScope 37 import com.android.systemui.lifecycle.activateIn 38 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest 39 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest 40 import com.android.systemui.power.domain.interactor.powerInteractor 41 import com.android.systemui.scene.domain.interactor.sceneInteractor 42 import com.android.systemui.scene.shared.model.Overlays 43 import com.android.systemui.scene.shared.model.Scenes 44 import com.android.systemui.scene.shared.model.TransitionKeys.ToSplitShade 45 import com.android.systemui.shade.data.repository.fakeShadeRepository 46 import com.android.systemui.shade.shared.flag.DualShade 47 import com.android.systemui.shade.shared.model.ShadeMode 48 import com.android.systemui.testKosmos 49 import com.google.common.truth.Truth.assertThat 50 import kotlin.test.Test 51 import kotlinx.coroutines.ExperimentalCoroutinesApi 52 import kotlinx.coroutines.test.TestScope 53 import kotlinx.coroutines.test.runCurrent 54 import kotlinx.coroutines.test.runTest 55 import org.junit.Before 56 import org.junit.runner.RunWith 57 58 @SmallTest 59 @RunWith(AndroidJUnit4::class) 60 @EnableSceneContainer 61 class DreamUserActionsViewModelTest : SysuiTestCase() { 62 63 private val kosmos = testKosmos() 64 private val testScope = kosmos.testScope 65 66 private lateinit var underTest: DreamUserActionsViewModel 67 68 @Before setUpnull69 fun setUp() { 70 underTest = kosmos.dreamUserActionsViewModel 71 underTest.activateIn(testScope) 72 } 73 74 @Test 75 @DisableFlags(DualShade.FLAG_NAME) actions_communalNotAvailable_singleShadenull76 fun actions_communalNotAvailable_singleShade() = 77 testScope.runTest { 78 kosmos.setCommunalAvailable(false) 79 80 val actions by collectLastValue(underTest.actions) 81 82 setUpState( 83 isShadeTouchable = true, 84 isDeviceUnlocked = false, 85 shadeMode = ShadeMode.Single, 86 ) 87 assertThat(actions).isNotEmpty() 88 assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Bouncer)) 89 assertThat(actions?.get(Swipe.Down)).isEqualTo(UserActionResult(Scenes.Shade)) 90 assertThat(actions?.get(Swipe.Start)).isNull() 91 assertThat(actions?.get(Swipe.End)).isNull() 92 93 setUpState( 94 isShadeTouchable = false, 95 isDeviceUnlocked = false, 96 shadeMode = ShadeMode.Single, 97 ) 98 assertThat(actions).isEmpty() 99 100 setUpState( 101 isShadeTouchable = true, 102 isDeviceUnlocked = true, 103 shadeMode = ShadeMode.Single, 104 ) 105 assertThat(actions).isNotEmpty() 106 assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Gone)) 107 assertThat(actions?.get(Swipe.Down)).isEqualTo(UserActionResult(Scenes.Shade)) 108 assertThat(actions?.get(Swipe.Start)).isNull() 109 assertThat(actions?.get(Swipe.End)).isNull() 110 } 111 112 @Test 113 @DisableFlags(DualShade.FLAG_NAME) actions_communalNotAvailable_splitShadenull114 fun actions_communalNotAvailable_splitShade() = 115 testScope.runTest { 116 kosmos.setCommunalAvailable(false) 117 118 val actions by collectLastValue(underTest.actions) 119 120 setUpState( 121 isShadeTouchable = true, 122 isDeviceUnlocked = false, 123 shadeMode = ShadeMode.Split, 124 ) 125 assertThat(actions).isNotEmpty() 126 assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Bouncer)) 127 assertThat(actions?.get(Swipe.Down)) 128 .isEqualTo(UserActionResult(Scenes.Shade, ToSplitShade)) 129 assertThat(actions?.get(Swipe.Start)).isNull() 130 assertThat(actions?.get(Swipe.End)).isNull() 131 132 setUpState( 133 isShadeTouchable = false, 134 isDeviceUnlocked = false, 135 shadeMode = ShadeMode.Split, 136 ) 137 assertThat(actions).isEmpty() 138 139 setUpState( 140 isShadeTouchable = true, 141 isDeviceUnlocked = true, 142 shadeMode = ShadeMode.Split, 143 ) 144 assertThat(actions).isNotEmpty() 145 assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Gone)) 146 assertThat(actions?.get(Swipe.Down)) 147 .isEqualTo(UserActionResult(Scenes.Shade, ToSplitShade)) 148 assertThat(actions?.get(Swipe.Start)).isNull() 149 assertThat(actions?.get(Swipe.End)).isNull() 150 } 151 152 @Test 153 @EnableFlags(DualShade.FLAG_NAME) actions_communalNotAvailable_dualShadenull154 fun actions_communalNotAvailable_dualShade() = 155 testScope.runTest { 156 kosmos.setCommunalAvailable(false) 157 158 val actions by collectLastValue(underTest.actions) 159 160 setUpState( 161 isShadeTouchable = true, 162 isDeviceUnlocked = false, 163 shadeMode = ShadeMode.Dual, 164 ) 165 assertThat(actions).isNotEmpty() 166 assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Bouncer)) 167 assertThat(actions?.get(Swipe.Down)) 168 .isEqualTo(UserActionResult.ShowOverlay(Overlays.NotificationsShade)) 169 assertThat(actions?.get(Swipe.Start)).isNull() 170 assertThat(actions?.get(Swipe.End)).isNull() 171 172 setUpState( 173 isShadeTouchable = false, 174 isDeviceUnlocked = false, 175 shadeMode = ShadeMode.Dual, 176 ) 177 assertThat(actions).isEmpty() 178 179 setUpState(isShadeTouchable = true, isDeviceUnlocked = true, shadeMode = ShadeMode.Dual) 180 assertThat(actions).isNotEmpty() 181 assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Gone)) 182 assertThat(actions?.get(Swipe.Down)) 183 .isEqualTo(UserActionResult.ShowOverlay(Overlays.NotificationsShade)) 184 assertThat(actions?.get(Swipe.Start)).isNull() 185 assertThat(actions?.get(Swipe.End)).isNull() 186 } 187 188 @Test 189 @DisableFlags(DualShade.FLAG_NAME) actions_communalAvailable_singleShadenull190 fun actions_communalAvailable_singleShade() = 191 testScope.runTest { 192 kosmos.setCommunalAvailable(true) 193 194 val actions by collectLastValue(underTest.actions) 195 196 setUpState( 197 isShadeTouchable = true, 198 isDeviceUnlocked = false, 199 shadeMode = ShadeMode.Single, 200 ) 201 assertThat(actions).isNotEmpty() 202 assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Bouncer)) 203 assertThat(actions?.get(Swipe.Down)).isEqualTo(UserActionResult(Scenes.Shade)) 204 assertThat(actions?.get(Swipe.Start)).isEqualTo(UserActionResult(Scenes.Communal)) 205 assertThat(actions?.get(Swipe.End)).isNull() 206 207 setUpState( 208 isShadeTouchable = false, 209 isDeviceUnlocked = false, 210 shadeMode = ShadeMode.Single, 211 ) 212 assertThat(actions).isEmpty() 213 214 setUpState( 215 isShadeTouchable = true, 216 isDeviceUnlocked = true, 217 shadeMode = ShadeMode.Single, 218 ) 219 assertThat(actions).isNotEmpty() 220 assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Gone)) 221 assertThat(actions?.get(Swipe.Down)).isEqualTo(UserActionResult(Scenes.Shade)) 222 assertThat(actions?.get(Swipe.Start)).isEqualTo(UserActionResult(Scenes.Communal)) 223 assertThat(actions?.get(Swipe.End)).isNull() 224 } 225 226 @Test 227 @DisableFlags(DualShade.FLAG_NAME) actions_communalAvailable_splitShadenull228 fun actions_communalAvailable_splitShade() = 229 testScope.runTest { 230 kosmos.setCommunalAvailable(true) 231 232 val actions by collectLastValue(underTest.actions) 233 234 setUpState( 235 isShadeTouchable = true, 236 isDeviceUnlocked = false, 237 shadeMode = ShadeMode.Split, 238 ) 239 assertThat(actions).isNotEmpty() 240 assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Bouncer)) 241 assertThat(actions?.get(Swipe.Down)) 242 .isEqualTo(UserActionResult(Scenes.Shade, ToSplitShade)) 243 assertThat(actions?.get(Swipe.Start)).isEqualTo(UserActionResult(Scenes.Communal)) 244 assertThat(actions?.get(Swipe.End)).isNull() 245 246 setUpState( 247 isShadeTouchable = false, 248 isDeviceUnlocked = false, 249 shadeMode = ShadeMode.Split, 250 ) 251 assertThat(actions).isEmpty() 252 253 setUpState( 254 isShadeTouchable = true, 255 isDeviceUnlocked = true, 256 shadeMode = ShadeMode.Split, 257 ) 258 assertThat(actions).isNotEmpty() 259 assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Gone)) 260 assertThat(actions?.get(Swipe.Down)) 261 .isEqualTo(UserActionResult(Scenes.Shade, ToSplitShade)) 262 assertThat(actions?.get(Swipe.Start)).isEqualTo(UserActionResult(Scenes.Communal)) 263 assertThat(actions?.get(Swipe.End)).isNull() 264 } 265 266 @Test 267 @EnableFlags(DualShade.FLAG_NAME) actions_communalAvailable_dualShadenull268 fun actions_communalAvailable_dualShade() = 269 testScope.runTest { 270 kosmos.setCommunalAvailable(true) 271 272 val actions by collectLastValue(underTest.actions) 273 274 setUpState( 275 isShadeTouchable = true, 276 isDeviceUnlocked = false, 277 shadeMode = ShadeMode.Dual, 278 ) 279 assertThat(actions).isNotEmpty() 280 assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Bouncer)) 281 assertThat(actions?.get(Swipe.Down)) 282 .isEqualTo(UserActionResult.ShowOverlay(Overlays.NotificationsShade)) 283 assertThat(actions?.get(Swipe.Start)).isEqualTo(UserActionResult(Scenes.Communal)) 284 assertThat(actions?.get(Swipe.End)).isNull() 285 286 setUpState( 287 isShadeTouchable = false, 288 isDeviceUnlocked = false, 289 shadeMode = ShadeMode.Dual, 290 ) 291 assertThat(actions).isEmpty() 292 293 setUpState(isShadeTouchable = true, isDeviceUnlocked = true, shadeMode = ShadeMode.Dual) 294 assertThat(actions).isNotEmpty() 295 assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Gone)) 296 assertThat(actions?.get(Swipe.Down)) 297 .isEqualTo(UserActionResult.ShowOverlay(Overlays.NotificationsShade)) 298 assertThat(actions?.get(Swipe.Start)).isEqualTo(UserActionResult(Scenes.Communal)) 299 assertThat(actions?.get(Swipe.End)).isNull() 300 } 301 TestScopenull302 private fun TestScope.setUpState( 303 isShadeTouchable: Boolean, 304 isDeviceUnlocked: Boolean, 305 shadeMode: ShadeMode, 306 ) { 307 if (isShadeTouchable) { 308 kosmos.powerInteractor.setAwakeForTest() 309 } else { 310 kosmos.powerInteractor.setAsleepForTest() 311 } 312 313 if (isDeviceUnlocked) { 314 unlockDevice() 315 } else { 316 lockDevice() 317 } 318 319 if (shadeMode == ShadeMode.Dual) { 320 assertThat(DualShade.isEnabled).isTrue() 321 } else { 322 assertThat(DualShade.isEnabled).isFalse() 323 kosmos.fakeShadeRepository.setShadeLayoutWide(shadeMode == ShadeMode.Split) 324 } 325 runCurrent() 326 } 327 lockDevicenull328 private fun TestScope.lockDevice() { 329 val deviceUnlockStatus by 330 collectLastValue(kosmos.deviceUnlockedInteractor.deviceUnlockStatus) 331 332 kosmos.fakeAuthenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) 333 assertThat(deviceUnlockStatus?.isUnlocked).isFalse() 334 kosmos.sceneInteractor.changeScene(Scenes.Lockscreen, "reason") 335 runCurrent() 336 } 337 unlockDevicenull338 private fun TestScope.unlockDevice() { 339 val deviceUnlockStatus by 340 collectLastValue(kosmos.deviceUnlockedInteractor.deviceUnlockStatus) 341 342 kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus( 343 SuccessFingerprintAuthenticationStatus(0, true) 344 ) 345 assertThat(deviceUnlockStatus?.isUnlocked).isTrue() 346 kosmos.sceneInteractor.changeScene(Scenes.Gone, "reason") 347 runCurrent() 348 } 349 } 350