1 package com.android.systemui.statusbar.notification 2 3 import android.view.View 4 import androidx.test.filters.SmallTest 5 import com.android.systemui.SysuiTestCase 6 import com.android.systemui.flags.FakeFeatureFlags 7 import com.android.systemui.flags.FeatureFlags 8 import com.android.systemui.flags.Flags 9 import com.android.systemui.util.mockito.mock 10 import com.android.systemui.util.mockito.whenever 11 import org.junit.Assert.assertEquals 12 import org.junit.Test 13 import org.junit.runner.RunWith 14 import org.junit.runners.JUnit4 15 import org.mockito.Mockito.atLeastOnce 16 import org.mockito.Mockito.times 17 import org.mockito.Mockito.verify 18 19 @SmallTest 20 @RunWith(JUnit4::class) 21 class RoundableTest : SysuiTestCase() { 22 private val targetView: View = mock() 23 private val featureFlags = FakeFeatureFlags() 24 private val roundable = FakeRoundable(targetView = targetView, featureFlags = featureFlags) 25 26 @Test 27 fun defaultConfig_shouldNotHaveRoundedCorner() { 28 // the expected default value for the roundness is top = 0f, bottom = 0f 29 assertEquals(0f, roundable.roundableState.topRoundness) 30 assertEquals(0f, roundable.roundableState.bottomRoundness) 31 assertEquals(false, roundable.hasRoundedCorner()) 32 } 33 34 @Test 35 fun applyRoundnessAndInvalidate_should_invalidate_targetView() { 36 roundable.applyRoundnessAndInvalidate() 37 38 verify(targetView, times(1)).invalidate() 39 } 40 41 @Test 42 fun requestTopRoundness_update_and_invalidate_targetView() { 43 roundable.requestTopRoundness(value = 1f, sourceType = SOURCE1) 44 45 assertEquals(1f, roundable.roundableState.topRoundness) 46 verify(targetView, times(1)).invalidate() 47 } 48 49 @Test 50 fun requestBottomRoundness_update_and_invalidate_targetView() { 51 roundable.requestBottomRoundness(value = 1f, sourceType = SOURCE1) 52 53 assertEquals(1f, roundable.roundableState.bottomRoundness) 54 verify(targetView, times(1)).invalidate() 55 } 56 57 @Test 58 fun requestRoundness_update_and_invalidate_targetView() { 59 roundable.requestRoundness(top = 1f, bottom = 1f, sourceType = SOURCE1) 60 61 assertEquals(1f, roundable.roundableState.topRoundness) 62 assertEquals(1f, roundable.roundableState.bottomRoundness) 63 verify(targetView, atLeastOnce()).invalidate() 64 } 65 66 @Test 67 fun requestRoundnessReset_update_and_invalidate_targetView() { 68 roundable.requestRoundness(1f, 1f, SOURCE1) 69 assertEquals(1f, roundable.roundableState.topRoundness) 70 assertEquals(1f, roundable.roundableState.bottomRoundness) 71 72 roundable.requestRoundnessReset(sourceType = SOURCE1) 73 74 assertEquals(0f, roundable.roundableState.topRoundness) 75 assertEquals(0f, roundable.roundableState.bottomRoundness) 76 verify(targetView, atLeastOnce()).invalidate() 77 } 78 79 @Test 80 fun hasRoundedCorner_return_true_ifRoundnessIsGreaterThenZero() { 81 roundable.requestRoundness(top = 1f, bottom = 1f, sourceType = SOURCE1) 82 assertEquals(true, roundable.hasRoundedCorner()) 83 84 roundable.requestRoundness(top = 1f, bottom = 0f, sourceType = SOURCE1) 85 assertEquals(true, roundable.hasRoundedCorner()) 86 87 roundable.requestRoundness(top = 0f, bottom = 1f, sourceType = SOURCE1) 88 assertEquals(true, roundable.hasRoundedCorner()) 89 90 roundable.requestRoundness(top = 0f, bottom = 0f, sourceType = SOURCE1) 91 assertEquals(false, roundable.hasRoundedCorner()) 92 } 93 94 @Test 95 fun roundness_take_maxValue_onMultipleSources_first_lower() { 96 roundable.requestRoundness(0.1f, 0.1f, SOURCE1) 97 assertEquals(0.1f, roundable.roundableState.topRoundness) 98 assertEquals(0.1f, roundable.roundableState.bottomRoundness) 99 100 roundable.requestRoundness(0.2f, 0.2f, SOURCE2) 101 // SOURCE1 has 0.1f - SOURCE2 has 0.2f 102 assertEquals(0.2f, roundable.roundableState.topRoundness) 103 assertEquals(0.2f, roundable.roundableState.bottomRoundness) 104 } 105 106 @Test 107 fun roundness_take_maxValue_onMultipleSources_first_higher() { 108 roundable.requestRoundness(0.5f, 0.5f, SOURCE1) 109 assertEquals(0.5f, roundable.roundableState.topRoundness) 110 assertEquals(0.5f, roundable.roundableState.bottomRoundness) 111 112 roundable.requestRoundness(0.1f, 0.1f, SOURCE2) 113 // SOURCE1 has 0.5f - SOURCE2 has 0.1f 114 assertEquals(0.5f, roundable.roundableState.topRoundness) 115 assertEquals(0.5f, roundable.roundableState.bottomRoundness) 116 } 117 118 @Test 119 fun roundness_take_maxValue_onMultipleSources_first_higher_second_step() { 120 roundable.requestRoundness(0.1f, 0.1f, SOURCE1) 121 assertEquals(0.1f, roundable.roundableState.topRoundness) 122 assertEquals(0.1f, roundable.roundableState.bottomRoundness) 123 124 roundable.requestRoundness(0.2f, 0.2f, SOURCE2) 125 // SOURCE1 has 0.1f - SOURCE2 has 0.2f 126 assertEquals(0.2f, roundable.roundableState.topRoundness) 127 assertEquals(0.2f, roundable.roundableState.bottomRoundness) 128 129 roundable.requestRoundness(0.3f, 0.3f, SOURCE1) 130 // SOURCE1 has 0.3f - SOURCE2 has 0.2f 131 assertEquals(0.3f, roundable.roundableState.topRoundness) 132 assertEquals(0.3f, roundable.roundableState.bottomRoundness) 133 } 134 135 @Test 136 fun roundness_take_maxValue_onMultipleSources_first_lower_second_step() { 137 roundable.requestRoundness(0.5f, 0.5f, SOURCE1) 138 assertEquals(0.5f, roundable.roundableState.topRoundness) 139 assertEquals(0.5f, roundable.roundableState.bottomRoundness) 140 141 roundable.requestRoundness(0.2f, 0.2f, SOURCE2) 142 // SOURCE1 has 0.5f - SOURCE2 has 0.2f 143 assertEquals(0.5f, roundable.roundableState.topRoundness) 144 assertEquals(0.5f, roundable.roundableState.bottomRoundness) 145 146 roundable.requestRoundness(0.1f, 0.1f, SOURCE1) 147 // SOURCE1 has 0.1f - SOURCE2 has 0.2f 148 assertEquals(0.2f, roundable.roundableState.topRoundness) 149 assertEquals(0.2f, roundable.roundableState.bottomRoundness) 150 } 151 152 @Test 153 fun getCornerRadii_radius_maxed_to_height() { 154 whenever(targetView.height).thenReturn(10) 155 featureFlags.set(Flags.IMPROVED_HUN_ANIMATIONS, true) 156 roundable.requestRoundness(1f, 1f, SOURCE1) 157 158 assertCornerRadiiEquals(5f, 5f) 159 } 160 161 @Test 162 fun getCornerRadii_topRadius_maxed_to_height() { 163 whenever(targetView.height).thenReturn(5) 164 featureFlags.set(Flags.IMPROVED_HUN_ANIMATIONS, true) 165 roundable.requestRoundness(1f, 0f, SOURCE1) 166 167 assertCornerRadiiEquals(5f, 0f) 168 } 169 170 @Test 171 fun getCornerRadii_bottomRadius_maxed_to_height() { 172 whenever(targetView.height).thenReturn(5) 173 featureFlags.set(Flags.IMPROVED_HUN_ANIMATIONS, true) 174 roundable.requestRoundness(0f, 1f, SOURCE1) 175 176 assertCornerRadiiEquals(0f, 5f) 177 } 178 179 @Test 180 fun getCornerRadii_radii_kept() { 181 whenever(targetView.height).thenReturn(100) 182 featureFlags.set(Flags.IMPROVED_HUN_ANIMATIONS, true) 183 roundable.requestRoundness(1f, 1f, SOURCE1) 184 185 assertCornerRadiiEquals(MAX_RADIUS, MAX_RADIUS) 186 } 187 188 private fun assertCornerRadiiEquals(top: Float, bottom: Float) { 189 assertEquals("topCornerRadius", top, roundable.topCornerRadius) 190 assertEquals("bottomCornerRadius", bottom, roundable.bottomCornerRadius) 191 } 192 193 class FakeRoundable( 194 targetView: View, 195 radius: Float = MAX_RADIUS, 196 featureFlags: FeatureFlags 197 ) : Roundable { 198 override val roundableState = 199 RoundableState( 200 targetView = targetView, 201 roundable = this, 202 maxRadius = radius, 203 featureFlags = featureFlags 204 ) 205 206 override val clipHeight: Int 207 get() = roundableState.targetView.height 208 } 209 210 companion object { 211 private const val MAX_RADIUS = 10f 212 private val SOURCE1 = SourceType.from("Source1") 213 private val SOURCE2 = SourceType.from("Source2") 214 } 215 } 216