/* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.statusbar.phone import android.service.notification.StatusBarNotification import android.testing.TestableLooper.RunWithLooper import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.StatusBarIconView import com.android.systemui.statusbar.StatusBarIconView.STATE_DOT import com.android.systemui.statusbar.StatusBarIconView.STATE_HIDDEN import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor import junit.framework.Assert.assertEquals import junit.framework.Assert.assertFalse import junit.framework.Assert.assertTrue import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito.mock import org.mockito.Mockito.`when` as whenever /** Tests for {@link NotificationIconContainer}. */ @SmallTest @RunWith(AndroidJUnit4::class) @RunWithLooper class NotificationIconContainerTest : SysuiTestCase() { private val iconContainer = NotificationIconContainer(context, /* attrs= */ null) @Test fun calculateWidthFor_zeroIcons_widthIsZero() { assertEquals( /* expected= */ iconContainer.calculateWidthFor(/* numIcons= */ 0f), /* actual= */ 0f ) } @Test fun calculateWidthFor_oneIcon_widthForOneIcon() { iconContainer.setActualPaddingStart(10f) iconContainer.setActualPaddingEnd(10f) iconContainer.setIconSize(10) assertEquals( /* expected= */ iconContainer.calculateWidthFor(/* numIcons= */ 1f), /* actual= */ 30f ) } @Test fun calculateWidthFor_fourIcons_widthForFourIcons() { iconContainer.setActualPaddingStart(10f) iconContainer.setActualPaddingEnd(10f) iconContainer.setIconSize(10) assertEquals( /* expected= */ iconContainer.calculateWidthFor(/* numIcons= */ 4f), /* actual= */ 60f ) } @Test fun calculateWidthFor_fiveIcons_widthForFourIcons() { mSetFlagsRule.disableFlags(NotificationIconContainerRefactor.FLAG_NAME) iconContainer.setActualPaddingStart(10f) iconContainer.setActualPaddingEnd(10f) iconContainer.setIconSize(10) assertEquals( /* expected= */ iconContainer.calculateWidthFor(/* numIcons= */ 5f), /* actual= */ 60f ) } @Test fun calculateIconXTranslations_shortShelfOneIcon_atCorrectXWithoutOverflowDot() { iconContainer.setActualPaddingStart(10f) iconContainer.setActualPaddingEnd(10f) iconContainer.setIconSize(10) val icon = mockStatusBarIcon() iconContainer.addView(icon) assertEquals(1, iconContainer.childCount) val iconState = iconContainer.getIconState(icon) iconState.iconAppearAmount = 1f val width = iconContainer.calculateWidthFor(/* numIcons= */ 1f) iconContainer.setActualLayoutWidth(width.toInt()) iconContainer.calculateIconXTranslations() assertEquals(10f, iconState.xTranslation) assertFalse(iconContainer.areIconsOverflowing()) } @Test fun calculateIconXTranslations_shortShelfFourIcons_atCorrectXWithoutOverflowDot() { iconContainer.setActualPaddingStart(10f) iconContainer.setActualPaddingEnd(10f) iconContainer.setIconSize(10) val iconOne = mockStatusBarIcon() val iconTwo = mockStatusBarIcon() val iconThree = mockStatusBarIcon() val iconFour = mockStatusBarIcon() iconContainer.addView(iconOne) iconContainer.addView(iconTwo) iconContainer.addView(iconThree) iconContainer.addView(iconFour) assertEquals(4, iconContainer.childCount) val width = iconContainer.calculateWidthFor(/* numIcons= */ 4f) iconContainer.setActualLayoutWidth(width.toInt()) iconContainer.calculateIconXTranslations() assertEquals(10f, iconContainer.getIconState(iconOne).xTranslation) assertEquals(20f, iconContainer.getIconState(iconTwo).xTranslation) assertEquals(30f, iconContainer.getIconState(iconThree).xTranslation) assertEquals(40f, iconContainer.getIconState(iconFour).xTranslation) assertFalse(iconContainer.areIconsOverflowing()) } @Test fun calculateIconXTranslations_shortShelfFiveIcons_atCorrectXWithOverflowDot() { iconContainer.setActualPaddingStart(10f) iconContainer.setActualPaddingEnd(10f) iconContainer.setIconSize(10) val iconOne = mockStatusBarIcon() val iconTwo = mockStatusBarIcon() val iconThree = mockStatusBarIcon() val iconFour = mockStatusBarIcon() val iconFive = mockStatusBarIcon() iconContainer.addView(iconOne) iconContainer.addView(iconTwo) iconContainer.addView(iconThree) iconContainer.addView(iconFour) iconContainer.addView(iconFive) assertEquals(5, iconContainer.childCount) val width = iconContainer.calculateWidthFor(/* numIcons= */ 4f) iconContainer.setActualLayoutWidth(width.toInt()) iconContainer.calculateIconXTranslations() assertEquals(10f, iconContainer.getIconState(iconOne).xTranslation) assertEquals(20f, iconContainer.getIconState(iconTwo).xTranslation) assertEquals(30f, iconContainer.getIconState(iconThree).xTranslation) assertTrue(iconContainer.areIconsOverflowing()) } @Test fun calculateIconXTranslations_givenWidthEnoughForThreeIcons_atCorrectXWithoutOverflowDot() { iconContainer.setActualPaddingStart(0f) iconContainer.setActualPaddingEnd(0f) iconContainer.setActualLayoutWidth(30) iconContainer.setIconSize(10) val iconOne = mockStatusBarIcon() val iconTwo = mockStatusBarIcon() val iconThree = mockStatusBarIcon() iconContainer.addView(iconOne) iconContainer.addView(iconTwo) iconContainer.addView(iconThree) assertEquals(3, iconContainer.childCount) iconContainer.calculateIconXTranslations() assertEquals(0f, iconContainer.getIconState(iconOne).xTranslation) assertEquals(10f, iconContainer.getIconState(iconTwo).xTranslation) assertEquals(20f, iconContainer.getIconState(iconThree).xTranslation) assertFalse(iconContainer.areIconsOverflowing()) } @Test fun calculateIconXTranslations_givenWidthNotEnoughForFourIcons_atCorrectXWithOverflowDot() { iconContainer.setActualPaddingStart(0f) iconContainer.setActualPaddingEnd(0f) iconContainer.setActualLayoutWidth(35) iconContainer.setIconSize(10) val iconOne = mockStatusBarIcon() val iconTwo = mockStatusBarIcon() val iconThree = mockStatusBarIcon() val iconFour = mockStatusBarIcon() iconContainer.addView(iconOne) iconContainer.addView(iconTwo) iconContainer.addView(iconThree) iconContainer.addView(iconFour) assertEquals(4, iconContainer.childCount) iconContainer.calculateIconXTranslations() assertEquals(0f, iconContainer.getIconState(iconOne).xTranslation) assertEquals(10f, iconContainer.getIconState(iconTwo).xTranslation) assertEquals(STATE_DOT, iconContainer.getIconState(iconThree).visibleState) assertEquals(STATE_HIDDEN, iconContainer.getIconState(iconFour).visibleState) assertTrue(iconContainer.areIconsOverflowing()) } @Test fun shouldForceOverflow_appearingAboveSpeedBump_true() { mSetFlagsRule.disableFlags(NotificationIconContainerRefactor.FLAG_NAME) val forceOverflow = iconContainer.shouldForceOverflow( /* i= */ 1, /* speedBumpIndex= */ 0, /* iconAppearAmount= */ 1f, /* maxVisibleIcons= */ 5 ) assertTrue(forceOverflow) } @Test fun shouldForceOverflow_moreThanMaxVisible_true() { val forceOverflow = iconContainer.shouldForceOverflow( /* i= */ 10, /* speedBumpIndex= */ 11, /* iconAppearAmount= */ 0.1f, /* maxVisibleIcons= */ 5 ) assertTrue(forceOverflow) } @Test fun shouldForceOverflow_belowSpeedBumpAndLessThanMaxVisible_false() { val forceOverflow = iconContainer.shouldForceOverflow( /* i= */ 0, /* speedBumpIndex= */ 11, /* iconAppearAmount= */ 0f, /* maxVisibleIcons= */ 5 ) assertFalse(forceOverflow) } @Test fun isOverflowing_lastChildXLessThanLayoutEnd_false() { val isOverflowing = iconContainer.isOverflowing( /* isLastChild= */ true, /* translationX= */ 0f, /* layoutEnd= */ 10f, /* iconSize= */ 2f, ) assertFalse(isOverflowing) } @Test fun isOverflowing_lastChildXEqualToLayoutEnd_true() { val isOverflowing = iconContainer.isOverflowing( /* isLastChild= */ true, /* translationX= */ 10f, /* layoutEnd= */ 10f, /* iconSize= */ 2f, ) assertTrue(isOverflowing) } @Test fun isOverflowing_lastChildXGreaterThanDotX_true() { val isOverflowing = iconContainer.isOverflowing( /* isLastChild= */ true, /* translationX= */ 9f, /* layoutEnd= */ 10f, /* iconSize= */ 2f, ) assertTrue(isOverflowing) } @Test fun isOverflowing_lastChildXGreaterThanLayoutEnd_true() { val isOverflowing = iconContainer.isOverflowing( /* isLastChild= */ true, /* translationX= */ 20f, /* layoutEnd= */ 10f, /* iconSize= */ 2f, ) assertTrue(isOverflowing) } @Test fun isOverflowing_notLastChildXLessThanDotX_false() { val isOverflowing = iconContainer.isOverflowing( /* isLastChild= */ false, /* translationX= */ 0f, /* layoutEnd= */ 10f, /* iconSize= */ 2f, ) assertFalse(isOverflowing) } @Test fun isOverflowing_notLastChildXGreaterThanDotX_true() { val isOverflowing = iconContainer.isOverflowing( /* isLastChild= */ false, /* translationX= */ 20f, /* layoutEnd= */ 10f, /* iconSize= */ 2f, ) assertTrue(isOverflowing) } @Test fun isOverflowing_notLastChildXEqualToDotX_true() { val isOverflowing = iconContainer.isOverflowing( /* isLastChild= */ false, /* translationX= */ 8f, /* layoutEnd= */ 10f, /* iconSize= */ 2f, ) assertTrue(isOverflowing) } private fun mockStatusBarIcon(): StatusBarIconView { val iconView = mock(StatusBarIconView::class.java) whenever(iconView.width).thenReturn(10) val icon = mock(android.graphics.drawable.Icon::class.java) whenever(iconView.sourceIcon).thenReturn(icon) val sbn = mock(StatusBarNotification::class.java) whenever(sbn.groupKey).thenReturn("groupKey") whenever(iconView.notification).thenReturn(sbn) return iconView } }