1 /* 2 * Copyright (C) 2023 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.keyguard.domain.interactor 18 19 import androidx.test.ext.junit.runners.AndroidJUnit4 20 import androidx.test.filters.SmallTest 21 import com.android.systemui.RoboPilotTest 22 import com.android.systemui.SysuiTestCase 23 import com.android.systemui.coroutines.collectValues 24 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository 25 import com.android.systemui.keyguard.shared.model.KeyguardState 26 import com.android.systemui.keyguard.shared.model.TransitionState 27 import com.android.systemui.keyguard.shared.model.TransitionStep 28 import com.android.systemui.util.mockito.whenever 29 import junit.framework.Assert.assertEquals 30 import kotlinx.coroutines.flow.MutableStateFlow 31 import kotlinx.coroutines.test.TestScope 32 import kotlinx.coroutines.test.runCurrent 33 import kotlinx.coroutines.test.runTest 34 import org.junit.Before 35 import org.junit.Test 36 import org.junit.runner.RunWith 37 import org.mockito.Mock 38 import org.mockito.MockitoAnnotations.initMocks 39 40 @SmallTest 41 @RoboPilotTest 42 @RunWith(AndroidJUnit4::class) 43 @kotlinx.coroutines.ExperimentalCoroutinesApi 44 class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { 45 46 private lateinit var underTest: WindowManagerLockscreenVisibilityInteractor 47 48 @Mock private lateinit var surfaceBehindInteractor: KeyguardSurfaceBehindInteractor 49 @Mock 50 private lateinit var fromLockscreenTransitionInteractor: FromLockscreenTransitionInteractor 51 @Mock 52 private lateinit var fromPrimaryBouncerTransitionInteractor: 53 FromPrimaryBouncerTransitionInteractor 54 55 private val lockscreenSurfaceVisibilityFlow = MutableStateFlow<Boolean?>(false) 56 private val primaryBouncerSurfaceVisibilityFlow = MutableStateFlow<Boolean?>(false) 57 private val surfaceBehindIsAnimatingFlow = MutableStateFlow(false) 58 59 private val testScope = TestScope() 60 61 private lateinit var keyguardInteractor: KeyguardInteractor 62 private lateinit var transitionRepository: FakeKeyguardTransitionRepository 63 private lateinit var transitionInteractor: KeyguardTransitionInteractor 64 65 @Before setUpnull66 fun setUp() { 67 initMocks(this) 68 69 whenever(fromLockscreenTransitionInteractor.surfaceBehindVisibility) 70 .thenReturn(lockscreenSurfaceVisibilityFlow) 71 whenever(fromPrimaryBouncerTransitionInteractor.surfaceBehindVisibility) 72 .thenReturn(primaryBouncerSurfaceVisibilityFlow) 73 whenever(surfaceBehindInteractor.isAnimatingSurface) 74 .thenReturn(surfaceBehindIsAnimatingFlow) 75 76 transitionRepository = FakeKeyguardTransitionRepository() 77 78 transitionInteractor = 79 KeyguardTransitionInteractorFactory.create( 80 scope = testScope.backgroundScope, 81 repository = transitionRepository, 82 ) 83 .also { keyguardInteractor = it.keyguardInteractor } 84 .keyguardTransitionInteractor 85 86 underTest = 87 WindowManagerLockscreenVisibilityInteractor( 88 keyguardInteractor = keyguardInteractor, 89 transitionInteractor = transitionInteractor, 90 surfaceBehindInteractor = surfaceBehindInteractor, 91 fromLockscreenTransitionInteractor, 92 fromPrimaryBouncerTransitionInteractor, 93 ) 94 } 95 96 @Test surfaceBehindVisibility_switchesToCorrectFlownull97 fun surfaceBehindVisibility_switchesToCorrectFlow() = 98 testScope.runTest { 99 val values by collectValues(underTest.surfaceBehindVisibility) 100 101 // Start on LOCKSCREEN. 102 transitionRepository.sendTransitionStep( 103 TransitionStep( 104 transitionState = TransitionState.STARTED, 105 from = KeyguardState.AOD, 106 to = KeyguardState.LOCKSCREEN, 107 ) 108 ) 109 110 runCurrent() 111 112 transitionRepository.sendTransitionStep( 113 TransitionStep( 114 transitionState = TransitionState.FINISHED, 115 from = KeyguardState.AOD, 116 to = KeyguardState.LOCKSCREEN, 117 ) 118 ) 119 120 runCurrent() 121 122 assertEquals( 123 listOf( 124 false, // We should start with the surface invisible on LOCKSCREEN. 125 ), 126 values 127 ) 128 129 val lockscreenSpecificSurfaceVisibility = true 130 lockscreenSurfaceVisibilityFlow.emit(lockscreenSpecificSurfaceVisibility) 131 transitionRepository.sendTransitionStep( 132 TransitionStep( 133 transitionState = TransitionState.STARTED, 134 from = KeyguardState.LOCKSCREEN, 135 to = KeyguardState.GONE, 136 ) 137 ) 138 139 runCurrent() 140 141 // We started a transition from LOCKSCREEN, we should be using the value emitted by the 142 // lockscreenSurfaceVisibilityFlow. 143 assertEquals( 144 listOf( 145 false, 146 lockscreenSpecificSurfaceVisibility, 147 ), 148 values 149 ) 150 151 // Go back to LOCKSCREEN, since we won't emit 'true' twice in a row. 152 transitionRepository.sendTransitionStep( 153 TransitionStep( 154 transitionState = TransitionState.STARTED, 155 from = KeyguardState.GONE, 156 to = KeyguardState.LOCKSCREEN, 157 ) 158 ) 159 runCurrent() 160 transitionRepository.sendTransitionStep( 161 TransitionStep( 162 transitionState = TransitionState.FINISHED, 163 from = KeyguardState.GONE, 164 to = KeyguardState.LOCKSCREEN, 165 ) 166 ) 167 runCurrent() 168 169 assertEquals( 170 listOf( 171 false, 172 lockscreenSpecificSurfaceVisibility, 173 false, // FINISHED (LOCKSCREEN) 174 ), 175 values 176 ) 177 178 val bouncerSpecificVisibility = true 179 primaryBouncerSurfaceVisibilityFlow.emit(bouncerSpecificVisibility) 180 transitionRepository.sendTransitionStep( 181 TransitionStep( 182 transitionState = TransitionState.STARTED, 183 from = KeyguardState.PRIMARY_BOUNCER, 184 to = KeyguardState.GONE, 185 ) 186 ) 187 188 runCurrent() 189 190 // We started a transition from PRIMARY_BOUNCER, we should be using the value emitted by 191 // the 192 // primaryBouncerSurfaceVisibilityFlow. 193 assertEquals( 194 listOf( 195 false, 196 lockscreenSpecificSurfaceVisibility, 197 false, 198 bouncerSpecificVisibility, 199 ), 200 values 201 ) 202 } 203 204 @Test testUsingGoingAwayAnimation_duringTransitionToGonenull205 fun testUsingGoingAwayAnimation_duringTransitionToGone() = 206 testScope.runTest { 207 val values by collectValues(underTest.usingKeyguardGoingAwayAnimation) 208 209 // Start on LOCKSCREEN. 210 transitionRepository.sendTransitionStep( 211 TransitionStep( 212 transitionState = TransitionState.STARTED, 213 from = KeyguardState.AOD, 214 to = KeyguardState.LOCKSCREEN, 215 ) 216 ) 217 runCurrent() 218 transitionRepository.sendTransitionStep( 219 TransitionStep( 220 transitionState = TransitionState.FINISHED, 221 from = KeyguardState.AOD, 222 to = KeyguardState.LOCKSCREEN, 223 ) 224 ) 225 runCurrent() 226 227 assertEquals( 228 listOf( 229 false, // Not using the animation when we're just sitting on LOCKSCREEN. 230 ), 231 values 232 ) 233 234 surfaceBehindIsAnimatingFlow.emit(true) 235 runCurrent() 236 transitionRepository.sendTransitionStep( 237 TransitionStep( 238 transitionState = TransitionState.FINISHED, 239 from = KeyguardState.LOCKSCREEN, 240 to = KeyguardState.GONE, 241 ) 242 ) 243 runCurrent() 244 245 assertEquals( 246 listOf( 247 false, 248 true, // Still true when we're FINISHED -> GONE, since we're still animating. 249 ), 250 values 251 ) 252 253 surfaceBehindIsAnimatingFlow.emit(false) 254 runCurrent() 255 256 assertEquals( 257 listOf( 258 false, 259 true, 260 false, // False once the animation ends. 261 ), 262 values 263 ) 264 } 265 266 @Test testNotUsingGoingAwayAnimation_evenWhenAnimating_ifStateIsNotGonenull267 fun testNotUsingGoingAwayAnimation_evenWhenAnimating_ifStateIsNotGone() = 268 testScope.runTest { 269 val values by collectValues(underTest.usingKeyguardGoingAwayAnimation) 270 271 // Start on LOCKSCREEN. 272 transitionRepository.sendTransitionStep( 273 TransitionStep( 274 transitionState = TransitionState.STARTED, 275 from = KeyguardState.AOD, 276 to = KeyguardState.LOCKSCREEN, 277 ) 278 ) 279 runCurrent() 280 transitionRepository.sendTransitionStep( 281 TransitionStep( 282 transitionState = TransitionState.FINISHED, 283 from = KeyguardState.AOD, 284 to = KeyguardState.LOCKSCREEN, 285 ) 286 ) 287 runCurrent() 288 289 assertEquals( 290 listOf( 291 false, // Not using the animation when we're just sitting on LOCKSCREEN. 292 ), 293 values 294 ) 295 296 surfaceBehindIsAnimatingFlow.emit(true) 297 runCurrent() 298 transitionRepository.sendTransitionStep( 299 TransitionStep( 300 transitionState = TransitionState.STARTED, 301 from = KeyguardState.LOCKSCREEN, 302 to = KeyguardState.GONE, 303 ) 304 ) 305 runCurrent() 306 307 assertEquals( 308 listOf( 309 false, 310 true, // We're happily animating while transitioning to gone. 311 ), 312 values 313 ) 314 315 // Oh no, we're still surfaceBehindAnimating=true, but no longer transitioning to GONE. 316 transitionRepository.sendTransitionStep( 317 TransitionStep( 318 transitionState = TransitionState.STARTED, 319 from = KeyguardState.LOCKSCREEN, 320 to = KeyguardState.AOD, 321 ) 322 ) 323 runCurrent() 324 325 assertEquals( 326 listOf( 327 false, 328 true, 329 false, // Despite the animator still running, this should be false. 330 ), 331 values 332 ) 333 334 surfaceBehindIsAnimatingFlow.emit(false) 335 runCurrent() 336 337 assertEquals( 338 listOf( 339 false, 340 true, 341 false, // The animator ending should have no effect. 342 ), 343 values 344 ) 345 } 346 347 @Test lockscreenVisibility_visibleWhenGonenull348 fun lockscreenVisibility_visibleWhenGone() = 349 testScope.runTest { 350 val values by collectValues(underTest.lockscreenVisibility) 351 352 // Start on LOCKSCREEN. 353 transitionRepository.sendTransitionStep( 354 TransitionStep( 355 transitionState = TransitionState.STARTED, 356 from = KeyguardState.AOD, 357 to = KeyguardState.LOCKSCREEN, 358 ) 359 ) 360 runCurrent() 361 362 transitionRepository.sendTransitionStep( 363 TransitionStep( 364 transitionState = TransitionState.FINISHED, 365 from = KeyguardState.AOD, 366 to = KeyguardState.LOCKSCREEN, 367 ) 368 ) 369 runCurrent() 370 371 assertEquals( 372 listOf( 373 true, // Unsurprisingly, we should start with the lockscreen visible on 374 // LOCKSCREEN. 375 ), 376 values 377 ) 378 379 transitionRepository.sendTransitionStep( 380 TransitionStep( 381 transitionState = TransitionState.STARTED, 382 from = KeyguardState.LOCKSCREEN, 383 to = KeyguardState.GONE, 384 ) 385 ) 386 runCurrent() 387 388 assertEquals( 389 listOf( 390 true, // Lockscreen remains visible while we're transitioning to GONE. 391 ), 392 values 393 ) 394 395 transitionRepository.sendTransitionStep( 396 TransitionStep( 397 transitionState = TransitionState.FINISHED, 398 from = KeyguardState.LOCKSCREEN, 399 to = KeyguardState.GONE, 400 ) 401 ) 402 runCurrent() 403 404 assertEquals( 405 listOf( 406 true, 407 false, // Once we're fully GONE, the lockscreen should not be visible. 408 ), 409 values 410 ) 411 } 412 } 413