1 package com.android.systemui.dreams 2 3 import android.animation.Animator 4 import android.animation.AnimatorSet 5 import android.animation.ValueAnimator 6 import android.testing.AndroidTestingRunner 7 import android.view.View 8 import androidx.test.filters.SmallTest 9 import com.android.systemui.SysuiTestCase 10 import com.android.systemui.complication.ComplicationHostViewController 11 import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel 12 import com.android.systemui.log.core.FakeLogBuffer 13 import com.android.systemui.statusbar.BlurUtils 14 import com.android.systemui.statusbar.policy.ConfigurationController 15 import com.android.systemui.util.mockito.argumentCaptor 16 import com.android.systemui.util.mockito.mock 17 import com.android.systemui.util.mockito.whenever 18 import kotlinx.coroutines.Dispatchers 19 import kotlinx.coroutines.runBlocking 20 import org.junit.Assert.assertTrue 21 import org.junit.Before 22 import org.junit.Test 23 import org.junit.runner.RunWith 24 import org.mockito.ArgumentCaptor 25 import org.mockito.Mock 26 import org.mockito.Mockito.atLeastOnce 27 import org.mockito.Mockito.never 28 import org.mockito.Mockito.times 29 import org.mockito.Mockito.verify 30 import org.mockito.MockitoAnnotations 31 32 @SmallTest 33 @RunWith(AndroidTestingRunner::class) 34 class DreamOverlayAnimationsControllerTest : SysuiTestCase() { 35 36 companion object { 37 private const val DREAM_BLUR_RADIUS = 50 38 private const val DREAM_IN_BLUR_ANIMATION_DURATION = 1L 39 private const val DREAM_IN_COMPLICATIONS_ANIMATION_DURATION = 3L 40 private const val DREAM_IN_TRANSLATION_Y_DISTANCE = 6 41 private const val DREAM_IN_TRANSLATION_Y_DURATION = 7L 42 } 43 44 @Mock private lateinit var mockAnimator: AnimatorSet 45 @Mock private lateinit var blurUtils: BlurUtils 46 @Mock private lateinit var hostViewController: ComplicationHostViewController 47 @Mock private lateinit var statusBarViewController: DreamOverlayStatusBarViewController 48 @Mock private lateinit var stateController: DreamOverlayStateController 49 @Mock private lateinit var configController: ConfigurationController 50 @Mock private lateinit var transitionViewModel: DreamingToLockscreenTransitionViewModel 51 private val logBuffer = FakeLogBuffer.Factory.create() 52 private lateinit var controller: DreamOverlayAnimationsController 53 54 @Before 55 fun setUp() { 56 MockitoAnnotations.initMocks(this) 57 controller = 58 DreamOverlayAnimationsController( 59 blurUtils, 60 hostViewController, 61 statusBarViewController, 62 stateController, 63 DREAM_BLUR_RADIUS, 64 transitionViewModel, 65 configController, 66 DREAM_IN_BLUR_ANIMATION_DURATION, 67 DREAM_IN_COMPLICATIONS_ANIMATION_DURATION, 68 DREAM_IN_TRANSLATION_Y_DISTANCE, 69 DREAM_IN_TRANSLATION_Y_DURATION, 70 logBuffer 71 ) 72 73 val mockView: View = mock() 74 whenever(mockView.resources).thenReturn(mContext.resources) 75 76 runBlocking(Dispatchers.Main.immediate) { controller.init(mockView) } 77 } 78 79 @Test 80 fun testExitAnimationUpdatesState() { 81 controller.startExitAnimations(animatorBuilder = { mockAnimator }) 82 83 verify(stateController).setExitAnimationsRunning(true) 84 85 val captor = argumentCaptor<Animator.AnimatorListener>() 86 verify(mockAnimator, atLeastOnce()).addListener(captor.capture()) 87 88 captor.allValues.forEach { it.onAnimationEnd(mockAnimator) } 89 verify(stateController).setExitAnimationsRunning(false) 90 } 91 92 @Test 93 fun testWakeUpSetsExitAnimationsRunning() { 94 controller.wakeUp() 95 96 verify(stateController).setExitAnimationsRunning(true) 97 } 98 99 @Test 100 fun testWakeUpAfterStartWillCancel() { 101 val mockStartAnimator: AnimatorSet = mock() 102 103 controller.startEntryAnimations(false, animatorBuilder = { mockStartAnimator }) 104 105 verify(mockStartAnimator, never()).cancel() 106 107 controller.wakeUp() 108 109 // Verify that we cancelled the start animator in favor of the exit 110 // animator. 111 verify(mockStartAnimator, times(1)).cancel() 112 } 113 114 @Test 115 fun testEntryAnimations_translatesUpwards() { 116 val mockStartAnimator: AnimatorSet = mock() 117 118 controller.startEntryAnimations( 119 /* downwards= */ false, 120 animatorBuilder = { mockStartAnimator } 121 ) 122 123 val animatorCaptor = ArgumentCaptor.forClass(Animator::class.java) 124 verify(mockStartAnimator).playTogether(animatorCaptor.capture()) 125 126 // Check if there's a ValueAnimator starting at the expected Y distance. 127 val animators: List<ValueAnimator> = animatorCaptor.allValues as List<ValueAnimator> 128 assertTrue( 129 animators.any { 130 // Call setCurrentFraction so the animated value jumps to the initial value. 131 it.setCurrentFraction(0f) 132 it.animatedValue == DREAM_IN_TRANSLATION_Y_DISTANCE.toFloat() 133 } 134 ) 135 } 136 137 @Test 138 fun testEntryAnimations_translatesDownwards() { 139 val mockStartAnimator: AnimatorSet = mock() 140 141 controller.startEntryAnimations( 142 /* downwards= */ true, 143 animatorBuilder = { mockStartAnimator } 144 ) 145 146 val animatorCaptor = ArgumentCaptor.forClass(Animator::class.java) 147 verify(mockStartAnimator).playTogether(animatorCaptor.capture()) 148 149 // Check if there's a ValueAnimator starting at the expected Y distance. 150 val animators: List<ValueAnimator> = animatorCaptor.allValues as List<ValueAnimator> 151 assertTrue( 152 animators.any { 153 // Call setCurrentFraction so the animated value jumps to the initial value. 154 it.setCurrentFraction(0f) 155 it.animatedValue == -DREAM_IN_TRANSLATION_Y_DISTANCE.toFloat() 156 } 157 ) 158 } 159 } 160