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