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.SysUITestModule 22 import com.android.systemui.SysuiTestCase 23 import com.android.systemui.TestMocksModule 24 import com.android.systemui.biometrics.domain.BiometricsDomainLayerModule 25 import com.android.systemui.coroutines.collectValues 26 import com.android.systemui.dagger.SysUISingleton 27 import com.android.systemui.keyguard.data.repository.FakeKeyguardSurfaceBehindRepository 28 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository 29 import com.android.systemui.keyguard.data.repository.InWindowLauncherUnlockAnimationRepository 30 import com.android.systemui.keyguard.shared.model.KeyguardState 31 import com.android.systemui.keyguard.shared.model.TransitionState 32 import com.android.systemui.keyguard.shared.model.TransitionStep 33 import com.android.systemui.keyguard.util.mockTopActivityClassName 34 import com.android.systemui.shared.system.ActivityManagerWrapper 35 import com.android.systemui.user.domain.UserDomainLayerModule 36 import dagger.BindsInstance 37 import dagger.Component 38 import junit.framework.Assert.assertEquals 39 import kotlinx.coroutines.test.TestScope 40 import kotlinx.coroutines.test.runCurrent 41 import kotlinx.coroutines.test.runTest 42 import org.junit.Before 43 import org.junit.Test 44 import org.junit.runner.RunWith 45 import org.mockito.Mock 46 import org.mockito.MockitoAnnotations 47 48 @SmallTest 49 @RunWith(AndroidJUnit4::class) 50 @kotlinx.coroutines.ExperimentalCoroutinesApi 51 class InWindowLauncherUnlockAnimationInteractorTest : SysuiTestCase() { 52 private lateinit var underTest: InWindowLauncherUnlockAnimationInteractor 53 54 private lateinit var testComponent: TestComponent 55 private lateinit var testScope: TestScope 56 private lateinit var transitionRepository: FakeKeyguardTransitionRepository 57 @Mock private lateinit var activityManagerWrapper: ActivityManagerWrapper 58 59 private val launcherClassName = "launcher" 60 61 @Before setUpnull62 fun setUp() { 63 MockitoAnnotations.initMocks(this) 64 65 testComponent = 66 DaggerInWindowLauncherUnlockAnimationInteractorTest_TestComponent.factory() 67 .create( 68 test = this, 69 mocks = 70 TestMocksModule( 71 activityManagerWrapper = activityManagerWrapper, 72 ), 73 ) 74 underTest = testComponent.underTest 75 testScope = testComponent.testScope 76 transitionRepository = testComponent.transitionRepository 77 78 activityManagerWrapper.mockTopActivityClassName(launcherClassName) 79 } 80 81 @Test testTransitioningToGoneWithInWindowAnimation_trueIfTopActivityIsLauncher_andTransitioningToGonenull82 fun testTransitioningToGoneWithInWindowAnimation_trueIfTopActivityIsLauncher_andTransitioningToGone() = 83 testScope.runTest { 84 val values by collectValues(underTest.transitioningToGoneWithInWindowAnimation) 85 runCurrent() 86 87 assertEquals( 88 listOf( 89 false, // False by default. 90 ), 91 values 92 ) 93 94 // Put launcher on top 95 testComponent.inWindowLauncherUnlockAnimationRepository.setLauncherActivityClass( 96 launcherClassName 97 ) 98 activityManagerWrapper.mockTopActivityClassName(launcherClassName) 99 runCurrent() 100 101 // Should still be false since we're not transitioning to GONE. 102 assertEquals( 103 listOf( 104 false, // False by default. 105 ), 106 values 107 ) 108 109 transitionRepository.sendTransitionStep( 110 TransitionStep( 111 transitionState = TransitionState.STARTED, 112 from = KeyguardState.LOCKSCREEN, 113 to = KeyguardState.GONE, 114 ) 115 ) 116 runCurrent() 117 118 assertEquals( 119 listOf( 120 false, 121 true, // -> GONE + launcher is behind 122 ), 123 values 124 ) 125 126 activityManagerWrapper.mockTopActivityClassName("not_launcher") 127 transitionRepository.sendTransitionStep( 128 TransitionStep( 129 transitionState = TransitionState.RUNNING, 130 from = KeyguardState.LOCKSCREEN, 131 to = KeyguardState.GONE, 132 ) 133 ) 134 runCurrent() 135 136 assertEquals( 137 listOf( 138 false, 139 true, // Top activity should be sampled, if it changes midway it should not 140 // matter. 141 ), 142 values 143 ) 144 145 transitionRepository.sendTransitionStep( 146 TransitionStep( 147 transitionState = TransitionState.FINISHED, 148 from = KeyguardState.LOCKSCREEN, 149 to = KeyguardState.GONE, 150 ) 151 ) 152 runCurrent() 153 154 assertEquals( 155 listOf( 156 false, 157 true, 158 false, // False once we're not transitioning anymore. 159 ), 160 values 161 ) 162 } 163 164 @Test testTransitioningToGoneWithInWindowAnimation_falseIfTopActivityIsLauncherPartwayThroughnull165 fun testTransitioningToGoneWithInWindowAnimation_falseIfTopActivityIsLauncherPartwayThrough() = 166 testScope.runTest { 167 val values by collectValues(underTest.transitioningToGoneWithInWindowAnimation) 168 runCurrent() 169 170 assertEquals( 171 listOf( 172 false, // False by default. 173 ), 174 values 175 ) 176 177 // Put not launcher on top 178 testComponent.inWindowLauncherUnlockAnimationRepository.setLauncherActivityClass( 179 launcherClassName 180 ) 181 activityManagerWrapper.mockTopActivityClassName("not_launcher") 182 runCurrent() 183 184 assertEquals( 185 listOf( 186 false, 187 ), 188 values 189 ) 190 191 transitionRepository.sendTransitionStep( 192 TransitionStep( 193 transitionState = TransitionState.STARTED, 194 from = KeyguardState.LOCKSCREEN, 195 to = KeyguardState.GONE, 196 ) 197 ) 198 runCurrent() 199 200 assertEquals( 201 listOf( 202 false, 203 ), 204 values 205 ) 206 207 activityManagerWrapper.mockTopActivityClassName(launcherClassName) 208 transitionRepository.sendTransitionStep( 209 TransitionStep( 210 transitionState = TransitionState.RUNNING, 211 from = KeyguardState.LOCKSCREEN, 212 to = KeyguardState.GONE, 213 ) 214 ) 215 runCurrent() 216 217 assertEquals( 218 listOf( 219 false, 220 ), 221 values 222 ) 223 224 transitionRepository.sendTransitionStep( 225 TransitionStep( 226 transitionState = TransitionState.FINISHED, 227 from = KeyguardState.LOCKSCREEN, 228 to = KeyguardState.GONE, 229 ) 230 ) 231 runCurrent() 232 233 assertEquals( 234 listOf( 235 false, 236 ), 237 values 238 ) 239 } 240 241 @Test testTransitioningToGoneWithInWindowAnimation_falseIfTopActivityIsLauncherWhileNotTransitioningToGonenull242 fun testTransitioningToGoneWithInWindowAnimation_falseIfTopActivityIsLauncherWhileNotTransitioningToGone() = 243 testScope.runTest { 244 val values by collectValues(underTest.transitioningToGoneWithInWindowAnimation) 245 runCurrent() 246 247 assertEquals( 248 listOf( 249 false, // False by default. 250 ), 251 values 252 ) 253 254 // Put launcher on top 255 testComponent.inWindowLauncherUnlockAnimationRepository.setLauncherActivityClass( 256 launcherClassName 257 ) 258 activityManagerWrapper.mockTopActivityClassName(launcherClassName) 259 runCurrent() 260 261 assertEquals( 262 listOf( 263 false, 264 ), 265 values 266 ) 267 268 transitionRepository.sendTransitionStep( 269 TransitionStep( 270 transitionState = TransitionState.STARTED, 271 from = KeyguardState.AOD, 272 to = KeyguardState.LOCKSCREEN, 273 ) 274 ) 275 runCurrent() 276 277 assertEquals( 278 listOf( 279 false, 280 ), 281 values 282 ) 283 } 284 285 @Test testShouldStartInWindowAnimation_trueOnceSurfaceAvailable_falseWhenTransitionEndsnull286 fun testShouldStartInWindowAnimation_trueOnceSurfaceAvailable_falseWhenTransitionEnds() = 287 testScope.runTest { 288 val values by collectValues(underTest.shouldStartInWindowAnimation) 289 runCurrent() 290 291 assertEquals( 292 listOf( 293 false, // False by default. 294 ), 295 values 296 ) 297 298 // Put Launcher on top and begin transitioning to GONE. 299 testComponent.inWindowLauncherUnlockAnimationRepository.setLauncherActivityClass( 300 launcherClassName 301 ) 302 activityManagerWrapper.mockTopActivityClassName(launcherClassName) 303 transitionRepository.sendTransitionStep( 304 TransitionStep( 305 transitionState = TransitionState.STARTED, 306 from = KeyguardState.LOCKSCREEN, 307 to = KeyguardState.GONE, 308 ) 309 ) 310 runCurrent() 311 312 assertEquals( 313 listOf( 314 false, 315 ), 316 values 317 ) 318 319 testComponent.surfaceBehindRepository.setSurfaceRemoteAnimationTargetAvailable(true) 320 runCurrent() 321 322 assertEquals( 323 listOf( 324 false, 325 true, // The surface is now available, so we should start the animation. 326 ), 327 values 328 ) 329 330 transitionRepository.sendTransitionStep( 331 TransitionStep( 332 transitionState = TransitionState.FINISHED, 333 from = KeyguardState.LOCKSCREEN, 334 to = KeyguardState.GONE, 335 ) 336 ) 337 runCurrent() 338 339 assertEquals( 340 listOf( 341 false, 342 true, 343 false, 344 ), 345 values 346 ) 347 } 348 349 @Test testShouldStartInWindowAnimation_neverTrueIfSurfaceNotAvailablenull350 fun testShouldStartInWindowAnimation_neverTrueIfSurfaceNotAvailable() = 351 testScope.runTest { 352 val values by collectValues(underTest.shouldStartInWindowAnimation) 353 runCurrent() 354 355 assertEquals( 356 listOf( 357 false, // False by default. 358 ), 359 values 360 ) 361 362 // Put Launcher on top and begin transitioning to GONE. 363 testComponent.inWindowLauncherUnlockAnimationRepository.setLauncherActivityClass( 364 launcherClassName 365 ) 366 activityManagerWrapper.mockTopActivityClassName(launcherClassName) 367 transitionRepository.sendTransitionStep( 368 TransitionStep( 369 transitionState = TransitionState.STARTED, 370 from = KeyguardState.LOCKSCREEN, 371 to = KeyguardState.GONE, 372 ) 373 ) 374 transitionRepository.sendTransitionStep( 375 TransitionStep( 376 transitionState = TransitionState.FINISHED, 377 from = KeyguardState.LOCKSCREEN, 378 to = KeyguardState.GONE, 379 ) 380 ) 381 runCurrent() 382 383 assertEquals( 384 listOf( 385 false, 386 ), 387 values 388 ) 389 } 390 391 @Test testShouldStartInWindowAnimation_falseIfSurfaceAvailable_afterTransitionInterruptednull392 fun testShouldStartInWindowAnimation_falseIfSurfaceAvailable_afterTransitionInterrupted() = 393 testScope.runTest { 394 val values by collectValues(underTest.shouldStartInWindowAnimation) 395 runCurrent() 396 397 assertEquals( 398 listOf( 399 false, // False by default. 400 ), 401 values 402 ) 403 404 // Put Launcher on top and begin transitioning to GONE. 405 testComponent.inWindowLauncherUnlockAnimationRepository.setLauncherActivityClass( 406 launcherClassName 407 ) 408 activityManagerWrapper.mockTopActivityClassName(launcherClassName) 409 transitionRepository.sendTransitionStep( 410 TransitionStep( 411 transitionState = TransitionState.STARTED, 412 from = KeyguardState.LOCKSCREEN, 413 to = KeyguardState.GONE, 414 ) 415 ) 416 transitionRepository.sendTransitionStep( 417 TransitionStep( 418 transitionState = TransitionState.CANCELED, 419 from = KeyguardState.LOCKSCREEN, 420 to = KeyguardState.GONE, 421 ) 422 ) 423 transitionRepository.sendTransitionStep( 424 TransitionStep( 425 transitionState = TransitionState.STARTED, 426 from = KeyguardState.GONE, 427 to = KeyguardState.AOD, 428 ) 429 ) 430 testComponent.surfaceBehindRepository.setSurfaceRemoteAnimationTargetAvailable(true) 431 runCurrent() 432 433 assertEquals( 434 listOf( 435 false, 436 ), 437 values 438 ) 439 } 440 441 @SysUISingleton 442 @Component( 443 modules = 444 [ 445 SysUITestModule::class, 446 BiometricsDomainLayerModule::class, 447 UserDomainLayerModule::class, 448 ] 449 ) 450 interface TestComponent { 451 val underTest: InWindowLauncherUnlockAnimationInteractor 452 val testScope: TestScope 453 val transitionRepository: FakeKeyguardTransitionRepository 454 val surfaceBehindRepository: FakeKeyguardSurfaceBehindRepository 455 val inWindowLauncherUnlockAnimationRepository: InWindowLauncherUnlockAnimationRepository 456 457 @Component.Factory 458 interface Factory { createnull459 fun create( 460 @BindsInstance test: SysuiTestCase, 461 mocks: TestMocksModule, 462 ): TestComponent 463 } 464 } 465 } 466