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