• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2022 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.media.controls.ui.animation
18 
19 import android.animation.ValueAnimator
20 import android.graphics.Color
21 import android.platform.test.annotations.DisableFlags
22 import android.platform.test.annotations.EnableFlags
23 import android.testing.TestableLooper
24 import androidx.test.ext.junit.runners.AndroidJUnit4
25 import androidx.test.filters.SmallTest
26 import com.android.systemui.Flags
27 import com.android.systemui.SysuiTestCase
28 import com.android.systemui.media.controls.ui.view.GutsViewHolder
29 import com.android.systemui.media.controls.ui.view.MediaViewHolder
30 import com.android.systemui.monet.ColorScheme
31 import com.android.systemui.surfaceeffects.ripple.MultiRippleController
32 import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseController
33 import junit.framework.Assert.assertEquals
34 import org.junit.After
35 import org.junit.Before
36 import org.junit.Rule
37 import org.junit.Test
38 import org.junit.runner.RunWith
39 import org.mockito.ArgumentMatchers.anyInt
40 import org.mockito.Mock
41 import org.mockito.Mockito.never
42 import org.mockito.Mockito.times
43 import org.mockito.Mockito.verify
44 import org.mockito.Mockito.`when` as whenever
45 import org.mockito.junit.MockitoJUnit
46 
47 private const val DEFAULT_COLOR = Color.RED
48 private const val TARGET_COLOR = Color.BLUE
49 
50 @SmallTest
51 @RunWith(AndroidJUnit4::class)
52 @TestableLooper.RunWithLooper(setAsMainLooper = true)
53 class ColorSchemeTransitionTest : SysuiTestCase() {
54 
55     private interface ExtractCB : (ColorScheme) -> Int
56 
57     private interface ApplyCB : (Int) -> Unit
58 
59     private lateinit var colorTransition: AnimatingColorTransition
60     private lateinit var colorSchemeTransition: ColorSchemeTransition
61 
62     @Mock private lateinit var mockAnimatingTransition: AnimatingColorTransition
63     @Mock private lateinit var valueAnimator: ValueAnimator
64     @Mock private lateinit var colorScheme: ColorScheme
65     @Mock private lateinit var extractColor: ExtractCB
66     @Mock private lateinit var applyColor: ApplyCB
67 
68     private lateinit var animatingColorTransitionFactory: AnimatingColorTransitionFactory
69     @Mock private lateinit var mediaViewHolder: MediaViewHolder
70     @Mock private lateinit var gutsViewHolder: GutsViewHolder
71     @Mock private lateinit var multiRippleController: MultiRippleController
72     @Mock private lateinit var turbulenceNoiseController: TurbulenceNoiseController
73 
74     @JvmField @Rule val mockitoRule = MockitoJUnit.rule()
75 
76     @Before
77     fun setUp() {
78         whenever(mediaViewHolder.gutsViewHolder).thenReturn(gutsViewHolder)
79         animatingColorTransitionFactory = { _, _, _ -> mockAnimatingTransition }
80         whenever(extractColor.invoke(colorScheme)).thenReturn(TARGET_COLOR)
81 
82         colorSchemeTransition =
83             ColorSchemeTransition(
84                 context,
85                 mediaViewHolder,
86                 multiRippleController,
87                 turbulenceNoiseController,
88                 animatingColorTransitionFactory,
89             )
90 
91         colorTransition =
92             object : AnimatingColorTransition(DEFAULT_COLOR, extractColor, applyColor) {
93                 override fun buildAnimator(): ValueAnimator {
94                     return valueAnimator
95                 }
96             }
97     }
98 
99     @After fun tearDown() {}
100 
101     @Test
102     fun testColorTransition_nullColorScheme_keepsDefault() {
103         colorTransition.updateColorScheme(null)
104         verify(applyColor, times(1)).invoke(DEFAULT_COLOR)
105         verify(valueAnimator, never()).start()
106         assertEquals(DEFAULT_COLOR, colorTransition.sourceColor)
107         assertEquals(DEFAULT_COLOR, colorTransition.targetColor)
108     }
109 
110     @Test
111     fun testColorTransition_newColor_startsAnimation() {
112         colorTransition.updateColorScheme(colorScheme)
113         verify(applyColor, times(1)).invoke(DEFAULT_COLOR)
114         verify(valueAnimator, times(1)).start()
115         assertEquals(DEFAULT_COLOR, colorTransition.sourceColor)
116         assertEquals(TARGET_COLOR, colorTransition.targetColor)
117     }
118 
119     @Test
120     fun testColorTransition_sameColor_noAnimation() {
121         whenever(extractColor.invoke(colorScheme)).thenReturn(DEFAULT_COLOR)
122         colorTransition.updateColorScheme(colorScheme)
123         verify(valueAnimator, never()).start()
124         assertEquals(DEFAULT_COLOR, colorTransition.sourceColor)
125         assertEquals(DEFAULT_COLOR, colorTransition.targetColor)
126     }
127 
128     @Test
129     fun testColorTransition_colorAnimation_startValues() {
130         val expectedColor = DEFAULT_COLOR
131         whenever(valueAnimator.animatedFraction).thenReturn(0f)
132         colorTransition.updateColorScheme(colorScheme)
133         colorTransition.onAnimationUpdate(valueAnimator)
134 
135         assertEquals(expectedColor, colorTransition.currentColor)
136         assertEquals(expectedColor, colorTransition.sourceColor)
137         verify(applyColor, times(2)).invoke(expectedColor) // applied once in constructor
138     }
139 
140     @Test
141     fun testColorTransition_colorAnimation_endValues() {
142         val expectedColor = TARGET_COLOR
143         whenever(valueAnimator.animatedFraction).thenReturn(1f)
144         colorTransition.updateColorScheme(colorScheme)
145         colorTransition.onAnimationUpdate(valueAnimator)
146 
147         assertEquals(expectedColor, colorTransition.currentColor)
148         assertEquals(expectedColor, colorTransition.targetColor)
149         verify(applyColor).invoke(expectedColor)
150     }
151 
152     @Test
153     fun testColorTransition_colorAnimation_interpolatedMidpoint() {
154         val expectedColor = Color.rgb(186, 0, 186)
155         whenever(valueAnimator.animatedFraction).thenReturn(0.5f)
156         colorTransition.updateColorScheme(colorScheme)
157         colorTransition.onAnimationUpdate(valueAnimator)
158 
159         assertEquals(expectedColor, colorTransition.currentColor)
160         verify(applyColor).invoke(expectedColor)
161     }
162 
163     @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_A11Y_COLORS)
164     @Test
165     fun testColorSchemeTransition_update_legacy() {
166         colorSchemeTransition.updateColorScheme(colorScheme)
167         verify(mockAnimatingTransition, times(8)).updateColorScheme(colorScheme)
168         verify(gutsViewHolder).colorScheme = colorScheme
169     }
170 
171     @EnableFlags(Flags.FLAG_MEDIA_CONTROLS_A11Y_COLORS)
172     @Test
173     fun testColorSchemeTransition_update() {
174         colorSchemeTransition.updateColorScheme(colorScheme)
175         verify(mockAnimatingTransition, times(3)).updateColorScheme(colorScheme)
176         verify(gutsViewHolder).setColors(colorScheme)
177         verify(multiRippleController).updateColor(anyInt())
178         verify(turbulenceNoiseController).updateNoiseColor(anyInt())
179     }
180 }
181