Fix dot/icon overlap in notification shelf
This bug was caused by an off-by-one error where
isOverflowing = iconX > overflowX
should have been
isOverflowing = iconX >= overflowX
As a result, the icon that should have been hidden
by the dot was considered "not overflowing"
(iconX=overflowX) so it remained visible.
This change corrects the definition of isOverflowing;
refactors isOverflowing and forceOverflow out of
calculateIconXTranslations; adds tests.
Bug: 232056690
Test: NotificationIconContainer
Test: add notifs to short shelf until overflow
swipe notifs away until short shelf empty
(no dot/icon overlap)
Test: swipe down on ls notif then let go
swipe down on ls notif to go to full shade
add notifs/swipe away notifs in full shade
scroll in full shade with long shelf
(no regressions)
Change-Id: Ie270c55f6c9d27b5d0fee73c22fedb5335760179
This commit is contained in:
@@ -423,6 +423,21 @@ public class NotificationIconContainer extends ViewGroup {
|
||||
+ getActualPaddingEnd();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
boolean shouldForceOverflow(int i, int speedBumpIndex, float iconAppearAmount,
|
||||
int maxVisibleIcons) {
|
||||
return speedBumpIndex != -1 && i >= speedBumpIndex
|
||||
&& iconAppearAmount > 0.0f || i >= maxVisibleIcons;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
boolean isOverflowing(boolean isLastChild, float translationX, float layoutEnd,
|
||||
float iconSize) {
|
||||
// Layout end, as used here, does not include padding end.
|
||||
final float overflowX = isLastChild ? layoutEnd : layoutEnd - iconSize;
|
||||
return translationX >= overflowX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the horizontal translations for each notification based on how much the icons
|
||||
* are inserted into the notification container.
|
||||
@@ -448,26 +463,26 @@ public class NotificationIconContainer extends ViewGroup {
|
||||
if (mFirstVisibleIconState == null) {
|
||||
mFirstVisibleIconState = iconState;
|
||||
}
|
||||
boolean forceOverflow = mSpeedBumpIndex != -1 && i >= mSpeedBumpIndex
|
||||
&& iconState.iconAppearAmount > 0.0f || i >= maxVisibleIcons;
|
||||
boolean isLastChild = i == childCount - 1;
|
||||
float drawingScale = mOnLockScreen && view instanceof StatusBarIconView
|
||||
? ((StatusBarIconView) view).getIconScaleIncreased()
|
||||
: 1f;
|
||||
iconState.visibleState = iconState.hidden
|
||||
? StatusBarIconView.STATE_HIDDEN
|
||||
: StatusBarIconView.STATE_ICON;
|
||||
|
||||
final float overflowDotX = layoutEnd - mIconSize;
|
||||
boolean isOverflowing = translationX > overflowDotX;
|
||||
final boolean forceOverflow = shouldForceOverflow(i, mSpeedBumpIndex,
|
||||
iconState.iconAppearAmount, maxVisibleIcons);
|
||||
final boolean isOverflowing = forceOverflow || isOverflowing(
|
||||
/* isLastChild= */ i == childCount - 1, translationX, layoutEnd, mIconSize);
|
||||
|
||||
if (firstOverflowIndex == -1 && (forceOverflow || isOverflowing)) {
|
||||
firstOverflowIndex = isLastChild && !forceOverflow ? i - 1 : i;
|
||||
// First icon to overflow.
|
||||
if (firstOverflowIndex == -1 && isOverflowing) {
|
||||
firstOverflowIndex = i;
|
||||
mVisualOverflowStart = layoutEnd - mIconSize;
|
||||
if (forceOverflow || mIsStaticLayout) {
|
||||
mVisualOverflowStart = Math.min(translationX, mVisualOverflowStart);
|
||||
}
|
||||
}
|
||||
final float drawingScale = mOnLockScreen && view instanceof StatusBarIconView
|
||||
? ((StatusBarIconView) view).getIconScaleIncreased()
|
||||
: 1f;
|
||||
translationX += iconState.iconAppearAmount * view.getWidth() * drawingScale;
|
||||
}
|
||||
mNumDots = 0;
|
||||
|
||||
@@ -153,6 +153,106 @@ class NotificationIconContainerTest : SysuiTestCase() {
|
||||
assertTrue(iconContainer.hasOverflow())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldForceOverflow_appearingAboveSpeedBump_true() {
|
||||
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= */ 0f,
|
||||
/* 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_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)
|
||||
|
||||
Reference in New Issue
Block a user