1 /* 2 * 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 package com.android.systemui.statusbar.phone 17 18 import android.service.notification.StatusBarNotification 19 import android.testing.AndroidTestingRunner 20 import android.testing.TestableLooper.RunWithLooper 21 import androidx.test.filters.SmallTest 22 import com.android.systemui.SysuiTestCase 23 import com.android.systemui.statusbar.StatusBarIconView 24 import com.android.systemui.statusbar.StatusBarIconView.STATE_DOT 25 import com.android.systemui.statusbar.StatusBarIconView.STATE_HIDDEN 26 import junit.framework.Assert.assertEquals 27 import junit.framework.Assert.assertFalse 28 import junit.framework.Assert.assertTrue 29 import org.junit.Test 30 import org.junit.runner.RunWith 31 import org.mockito.Mockito.mock 32 import org.mockito.Mockito.`when` as whenever 33 34 /** 35 * Tests for {@link NotificationIconContainer}. 36 */ 37 @SmallTest 38 @RunWith(AndroidTestingRunner::class) 39 @RunWithLooper 40 class NotificationIconContainerTest : SysuiTestCase() { 41 42 private val iconContainer = NotificationIconContainer(context, /* attrs= */ null) 43 44 @Test 45 fun calculateWidthFor_zeroIcons_widthIsZero() { 46 assertEquals(/* expected= */ iconContainer.calculateWidthFor(/* numIcons= */ 0f), 47 /* actual= */ 0f) 48 } 49 50 @Test 51 fun calculateWidthFor_oneIcon_widthForOneIcon() { 52 iconContainer.setActualPaddingStart(10f) 53 iconContainer.setActualPaddingEnd(10f) 54 iconContainer.setIconSize(10) 55 56 assertEquals(/* expected= */ iconContainer.calculateWidthFor(/* numIcons= */ 1f), 57 /* actual= */ 30f) 58 } 59 60 @Test 61 fun calculateWidthFor_fourIcons_widthForFourIcons() { 62 iconContainer.setActualPaddingStart(10f) 63 iconContainer.setActualPaddingEnd(10f) 64 iconContainer.setIconSize(10) 65 66 assertEquals(/* expected= */ iconContainer.calculateWidthFor(/* numIcons= */ 4f), 67 /* actual= */ 60f) 68 } 69 70 @Test 71 fun calculateWidthFor_fiveIcons_widthForFourIcons() { 72 iconContainer.setActualPaddingStart(10f) 73 iconContainer.setActualPaddingEnd(10f) 74 iconContainer.setIconSize(10) 75 assertEquals(/* expected= */ iconContainer.calculateWidthFor(/* numIcons= */ 5f), 76 /* actual= */ 60f) 77 } 78 79 @Test 80 fun calculateIconXTranslations_shortShelfOneIcon_atCorrectXWithoutOverflowDot() { 81 iconContainer.setActualPaddingStart(10f) 82 iconContainer.setActualPaddingEnd(10f) 83 iconContainer.setIconSize(10) 84 85 val icon = mockStatusBarIcon() 86 iconContainer.addView(icon) 87 assertEquals(1, iconContainer.childCount) 88 89 val iconState = iconContainer.getIconState(icon) 90 iconState.iconAppearAmount = 1f 91 92 val width = iconContainer.calculateWidthFor(/* numIcons= */ 1f) 93 iconContainer.setActualLayoutWidth(width.toInt()) 94 95 iconContainer.calculateIconXTranslations() 96 assertEquals(10f, iconState.xTranslation) 97 assertFalse(iconContainer.areIconsOverflowing()) 98 } 99 100 @Test 101 fun calculateIconXTranslations_shortShelfFourIcons_atCorrectXWithoutOverflowDot() { 102 iconContainer.setActualPaddingStart(10f) 103 iconContainer.setActualPaddingEnd(10f) 104 iconContainer.setIconSize(10) 105 106 val iconOne = mockStatusBarIcon() 107 val iconTwo = mockStatusBarIcon() 108 val iconThree = mockStatusBarIcon() 109 val iconFour = mockStatusBarIcon() 110 111 iconContainer.addView(iconOne) 112 iconContainer.addView(iconTwo) 113 iconContainer.addView(iconThree) 114 iconContainer.addView(iconFour) 115 assertEquals(4, iconContainer.childCount) 116 117 val width = iconContainer.calculateWidthFor(/* numIcons= */ 4f) 118 iconContainer.setActualLayoutWidth(width.toInt()) 119 120 iconContainer.calculateIconXTranslations() 121 assertEquals(10f, iconContainer.getIconState(iconOne).xTranslation) 122 assertEquals(20f, iconContainer.getIconState(iconTwo).xTranslation) 123 assertEquals(30f, iconContainer.getIconState(iconThree).xTranslation) 124 assertEquals(40f, iconContainer.getIconState(iconFour).xTranslation) 125 126 assertFalse(iconContainer.areIconsOverflowing()) 127 } 128 129 @Test 130 fun calculateIconXTranslations_shortShelfFiveIcons_atCorrectXWithOverflowDot() { 131 iconContainer.setActualPaddingStart(10f) 132 iconContainer.setActualPaddingEnd(10f) 133 iconContainer.setIconSize(10) 134 135 val iconOne = mockStatusBarIcon() 136 val iconTwo = mockStatusBarIcon() 137 val iconThree = mockStatusBarIcon() 138 val iconFour = mockStatusBarIcon() 139 val iconFive = mockStatusBarIcon() 140 141 iconContainer.addView(iconOne) 142 iconContainer.addView(iconTwo) 143 iconContainer.addView(iconThree) 144 iconContainer.addView(iconFour) 145 iconContainer.addView(iconFive) 146 assertEquals(5, iconContainer.childCount) 147 148 val width = iconContainer.calculateWidthFor(/* numIcons= */ 5f) 149 iconContainer.setActualLayoutWidth(width.toInt()) 150 151 iconContainer.calculateIconXTranslations() 152 assertEquals(10f, iconContainer.getIconState(iconOne).xTranslation) 153 assertEquals(20f, iconContainer.getIconState(iconTwo).xTranslation) 154 assertEquals(30f, iconContainer.getIconState(iconThree).xTranslation) 155 assertTrue(iconContainer.areIconsOverflowing()) 156 } 157 158 @Test 159 fun calculateIconXTranslations_givenWidthEnoughForThreeIcons_atCorrectXWithoutOverflowDot() { 160 iconContainer.setActualPaddingStart(0f) 161 iconContainer.setActualPaddingEnd(0f) 162 iconContainer.setActualLayoutWidth(30) 163 iconContainer.setIconSize(10) 164 165 val iconOne = mockStatusBarIcon() 166 val iconTwo = mockStatusBarIcon() 167 val iconThree = mockStatusBarIcon() 168 169 iconContainer.addView(iconOne) 170 iconContainer.addView(iconTwo) 171 iconContainer.addView(iconThree) 172 assertEquals(3, iconContainer.childCount) 173 174 iconContainer.calculateIconXTranslations() 175 assertEquals(0f, iconContainer.getIconState(iconOne).xTranslation) 176 assertEquals(10f, iconContainer.getIconState(iconTwo).xTranslation) 177 assertEquals(20f, iconContainer.getIconState(iconThree).xTranslation) 178 assertFalse(iconContainer.areIconsOverflowing()) 179 } 180 181 @Test 182 fun calculateIconXTranslations_givenWidthNotEnoughForFourIcons_atCorrectXWithOverflowDot() { 183 iconContainer.setActualPaddingStart(0f) 184 iconContainer.setActualPaddingEnd(0f) 185 iconContainer.setActualLayoutWidth(35) 186 iconContainer.setIconSize(10) 187 188 val iconOne = mockStatusBarIcon() 189 val iconTwo = mockStatusBarIcon() 190 val iconThree = mockStatusBarIcon() 191 val iconFour = mockStatusBarIcon() 192 193 iconContainer.addView(iconOne) 194 iconContainer.addView(iconTwo) 195 iconContainer.addView(iconThree) 196 iconContainer.addView(iconFour) 197 assertEquals(4, iconContainer.childCount) 198 199 iconContainer.calculateIconXTranslations() 200 assertEquals(0f, iconContainer.getIconState(iconOne).xTranslation) 201 assertEquals(10f, iconContainer.getIconState(iconTwo).xTranslation) 202 assertEquals(STATE_DOT, iconContainer.getIconState(iconThree).visibleState) 203 assertEquals(STATE_HIDDEN, iconContainer.getIconState(iconFour).visibleState) 204 assertTrue(iconContainer.areIconsOverflowing()) 205 } 206 207 @Test 208 fun shouldForceOverflow_appearingAboveSpeedBump_true() { 209 val forceOverflow = iconContainer.shouldForceOverflow( 210 /* i= */ 1, 211 /* speedBumpIndex= */ 0, 212 /* iconAppearAmount= */ 1f, 213 /* maxVisibleIcons= */ 5 214 ) 215 assertTrue(forceOverflow) 216 } 217 218 @Test 219 fun shouldForceOverflow_moreThanMaxVisible_true() { 220 val forceOverflow = iconContainer.shouldForceOverflow( 221 /* i= */ 10, 222 /* speedBumpIndex= */ 11, 223 /* iconAppearAmount= */ 0f, 224 /* maxVisibleIcons= */ 5 225 ) 226 assertTrue(forceOverflow) 227 } 228 229 @Test 230 fun shouldForceOverflow_belowSpeedBumpAndLessThanMaxVisible_false() { 231 val forceOverflow = iconContainer.shouldForceOverflow( 232 /* i= */ 0, 233 /* speedBumpIndex= */ 11, 234 /* iconAppearAmount= */ 0f, 235 /* maxVisibleIcons= */ 5 236 ) 237 assertFalse(forceOverflow) 238 } 239 240 @Test 241 fun isOverflowing_lastChildXLessThanLayoutEnd_false() { 242 val isOverflowing = iconContainer.isOverflowing( 243 /* isLastChild= */ true, 244 /* translationX= */ 0f, 245 /* layoutEnd= */ 10f, 246 /* iconSize= */ 2f, 247 ) 248 assertFalse(isOverflowing) 249 } 250 251 252 @Test 253 fun isOverflowing_lastChildXEqualToLayoutEnd_true() { 254 val isOverflowing = iconContainer.isOverflowing( 255 /* isLastChild= */ true, 256 /* translationX= */ 10f, 257 /* layoutEnd= */ 10f, 258 /* iconSize= */ 2f, 259 ) 260 assertTrue(isOverflowing) 261 } 262 263 @Test 264 fun isOverflowing_lastChildXGreaterThanDotX_true() { 265 val isOverflowing = iconContainer.isOverflowing( 266 /* isLastChild= */ true, 267 /* translationX= */ 9f, 268 /* layoutEnd= */ 10f, 269 /* iconSize= */ 2f, 270 ) 271 assertTrue(isOverflowing) 272 } 273 274 @Test 275 fun isOverflowing_lastChildXGreaterThanLayoutEnd_true() { 276 val isOverflowing = iconContainer.isOverflowing( 277 /* isLastChild= */ true, 278 /* translationX= */ 20f, 279 /* layoutEnd= */ 10f, 280 /* iconSize= */ 2f, 281 ) 282 assertTrue(isOverflowing) 283 } 284 285 @Test 286 fun isOverflowing_notLastChildXLessThanDotX_false() { 287 val isOverflowing = iconContainer.isOverflowing( 288 /* isLastChild= */ false, 289 /* translationX= */ 0f, 290 /* layoutEnd= */ 10f, 291 /* iconSize= */ 2f, 292 ) 293 assertFalse(isOverflowing) 294 } 295 296 @Test 297 fun isOverflowing_notLastChildXGreaterThanDotX_true() { 298 val isOverflowing = iconContainer.isOverflowing( 299 /* isLastChild= */ false, 300 /* translationX= */ 20f, 301 /* layoutEnd= */ 10f, 302 /* iconSize= */ 2f, 303 ) 304 assertTrue(isOverflowing) 305 } 306 307 @Test 308 fun isOverflowing_notLastChildXEqualToDotX_true() { 309 val isOverflowing = iconContainer.isOverflowing( 310 /* isLastChild= */ false, 311 /* translationX= */ 8f, 312 /* layoutEnd= */ 10f, 313 /* iconSize= */ 2f, 314 ) 315 assertTrue(isOverflowing) 316 } 317 318 private fun mockStatusBarIcon(): StatusBarIconView { 319 val iconView = mock(StatusBarIconView::class.java) 320 whenever(iconView.width).thenReturn(10) 321 322 val icon = mock(android.graphics.drawable.Icon::class.java) 323 whenever(iconView.sourceIcon).thenReturn(icon) 324 325 val sbn = mock(StatusBarNotification::class.java) 326 whenever(sbn.groupKey).thenReturn("groupKey") 327 whenever(iconView.notification).thenReturn(sbn) 328 return iconView 329 } 330 }