Merge "Update section header placement logic" into rvc-dev

This commit is contained in:
TreeHugger Robot
2020-02-28 11:46:28 +00:00
committed by Android (Google) Code Review
2 changed files with 255 additions and 118 deletions

View File

@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification.stack; package com.android.systemui.statusbar.notification.stack;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_GENTLE;
import static java.lang.annotation.RetentionPolicy.SOURCE; import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.annotation.IntDef; import android.annotation.IntDef;
@@ -100,14 +102,11 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section
private boolean mInitialized = false; private boolean mInitialized = false;
private SectionHeaderView mGentleHeader; private SectionHeaderView mGentleHeader;
private boolean mGentleHeaderVisible;
@Nullable private View.OnClickListener mOnClearGentleNotifsClickListener; @Nullable private View.OnClickListener mOnClearGentleNotifsClickListener;
private SectionHeaderView mAlertingHeader; private SectionHeaderView mAlertingHeader;
private boolean mAlertingHeaderVisible;
private PeopleHubView mPeopleHubView; private PeopleHubView mPeopleHubView;
private boolean mPeopleHeaderVisible;
private boolean mPeopleHubVisible = false; private boolean mPeopleHubVisible = false;
@Nullable private Subscription mPeopleHubSubscription; @Nullable private Subscription mPeopleHubSubscription;
@@ -231,88 +230,135 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section
return; return;
} }
// The overall strategy here is to iterate over the current children of mParent, looking
// for where the sections headers are currently positioned, and where each section begins.
// Then, once we find the start of a new section, we track that position as the "target" for
// the section header, adjusted for the case where existing headers are in front of that
// target, but won't be once they are moved / removed after the pass has completed.
final boolean showHeaders = mStatusBarStateController.getState() != StatusBarState.KEYGUARD; final boolean showHeaders = mStatusBarStateController.getState() != StatusBarState.KEYGUARD;
final boolean usingPeopleFiltering = mSectionsFeatureManager.isFilteringEnabled(); final boolean usingPeopleFiltering = mSectionsFeatureManager.isFilteringEnabled();
boolean peopleNotifsPresent = false; boolean peopleNotifsPresent = false;
int currentPeopleHeaderIdx = -1;
int peopleHeaderTarget = -1; int peopleHeaderTarget = -1;
int currentAlertingHeaderIdx = -1;
int alertingHeaderTarget = -1; int alertingHeaderTarget = -1;
int currentGentleHeaderIdx = -1;
int gentleHeaderTarget = -1; int gentleHeaderTarget = -1;
int viewCount = 0; int lastNotifIndex = 0;
if (showHeaders) { final int childCount = mParent.getChildCount();
final int childCount = mParent.getChildCount(); for (int i = 0; i < childCount; i++) {
for (int i = 0; i < childCount; i++) { View child = mParent.getChildAt(i);
View child = mParent.getChildAt(i);
if (child.getVisibility() == View.GONE // Track the existing positions of the headers
|| !(child instanceof ExpandableNotificationRow)) { if (child == mPeopleHubView) {
continue; currentPeopleHeaderIdx = i;
} continue;
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
switch (row.getEntry().getBucket()) {
case BUCKET_PEOPLE:
if (peopleHeaderTarget == -1) {
peopleNotifsPresent = true;
peopleHeaderTarget = viewCount;
viewCount++;
}
break;
case BUCKET_ALERTING:
if (usingPeopleFiltering && alertingHeaderTarget == -1) {
alertingHeaderTarget = viewCount;
viewCount++;
}
break;
case BUCKET_SILENT:
if (gentleHeaderTarget == -1) {
gentleHeaderTarget = viewCount;
viewCount++;
}
break;
}
viewCount++;
} }
if (usingPeopleFiltering && mPeopleHubVisible && peopleHeaderTarget == -1) { if (child == mAlertingHeader) {
// Insert the people header even if there are no people visible, in order to show currentAlertingHeaderIdx = i;
// the hub. Put it directly above the next header. continue;
if (alertingHeaderTarget != -1) { }
peopleHeaderTarget = alertingHeaderTarget; if (child == mGentleHeader) {
alertingHeaderTarget++; currentGentleHeaderIdx = i;
gentleHeaderTarget++; continue;
} else if (gentleHeaderTarget != -1) { }
peopleHeaderTarget = gentleHeaderTarget;
gentleHeaderTarget++; if (!(child instanceof ExpandableNotificationRow)) {
} else { continue;
// Put it at the end of the list. }
peopleHeaderTarget = viewCount; lastNotifIndex = i;
} ExpandableNotificationRow row = (ExpandableNotificationRow) child;
// Once we enter a new section, calculate the target position for the header.
switch (row.getEntry().getBucket()) {
case BUCKET_HEADS_UP:
break;
case BUCKET_PEOPLE:
peopleNotifsPresent = true;
if (showHeaders && peopleHeaderTarget == -1) {
peopleHeaderTarget = i;
// Offset the target if there are other headers before this that will be
// moved.
if (currentPeopleHeaderIdx != -1) {
peopleHeaderTarget--;
}
if (currentAlertingHeaderIdx != -1) {
peopleHeaderTarget--;
}
if (currentGentleHeaderIdx != -1) {
peopleHeaderTarget--;
}
}
break;
case BUCKET_ALERTING:
if (showHeaders && usingPeopleFiltering && alertingHeaderTarget == -1) {
alertingHeaderTarget = i;
// Offset the target if there are other headers before this that will be
// moved.
if (currentAlertingHeaderIdx != -1) {
alertingHeaderTarget--;
}
if (currentGentleHeaderIdx != -1) {
alertingHeaderTarget--;
}
}
break;
case BUCKET_SILENT:
if (showHeaders && gentleHeaderTarget == -1) {
gentleHeaderTarget = i;
// Offset the target if there are other headers before this that will be
// moved.
if (currentGentleHeaderIdx != -1) {
gentleHeaderTarget--;
}
}
break;
default:
throw new IllegalStateException("Cannot find section bucket for view");
}
}
if (showHeaders && usingPeopleFiltering && mPeopleHubVisible && peopleHeaderTarget == -1) {
// Insert the people header even if there are no people visible, in order to show
// the hub. Put it directly above the next header.
if (alertingHeaderTarget != -1) {
peopleHeaderTarget = alertingHeaderTarget;
} else if (gentleHeaderTarget != -1) {
peopleHeaderTarget = gentleHeaderTarget;
} else {
// Put it at the end of the list.
peopleHeaderTarget = lastNotifIndex;
} }
} }
// Allow swiping the people header if the section is empty // Add headers in reverse order to preserve indices
mPeopleHubView.setCanSwipe(mPeopleHubVisible && !peopleNotifsPresent); adjustHeaderVisibilityAndPosition(
gentleHeaderTarget, mGentleHeader, currentGentleHeaderIdx);
adjustHeaderVisibilityAndPosition(
alertingHeaderTarget, mAlertingHeader, currentAlertingHeaderIdx);
adjustHeaderVisibilityAndPosition(
peopleHeaderTarget, mPeopleHubView, currentPeopleHeaderIdx);
mPeopleHeaderVisible = adjustHeaderVisibilityAndPosition( // Update headers to reflect state of section contents
peopleHeaderTarget, mPeopleHubView, mPeopleHeaderVisible); mGentleHeader.setAreThereDismissableGentleNotifs(
mAlertingHeaderVisible = adjustHeaderVisibilityAndPosition( mParent.hasActiveClearableNotifications(ROWS_GENTLE));
alertingHeaderTarget, mAlertingHeader, mAlertingHeaderVisible); mPeopleHubView.setCanSwipe(showHeaders && mPeopleHubVisible && !peopleNotifsPresent);
mGentleHeaderVisible = adjustHeaderVisibilityAndPosition( if (peopleHeaderTarget != currentPeopleHeaderIdx) {
gentleHeaderTarget, mGentleHeader, mGentleHeaderVisible); mPeopleHubView.resetTranslation();
}
} }
private boolean adjustHeaderVisibilityAndPosition( private void adjustHeaderVisibilityAndPosition(
int targetIndex, StackScrollerDecorView header, boolean isCurrentlyVisible) { int targetPosition, StackScrollerDecorView header, int currentPosition) {
if (targetIndex == -1) { if (targetPosition == -1) {
if (isCurrentlyVisible) { if (currentPosition != -1) {
mParent.removeView(header); mParent.removeView(header);
} }
return false;
} else { } else {
if (header instanceof SwipeableView) { if (currentPosition == -1) {
((SwipeableView) header).resetTranslation();
}
if (!isCurrentlyVisible) {
// If the header is animating away, it will still have a parent, so detach it first // If the header is animating away, it will still have a parent, so detach it first
// TODO: We should really cancel the active animations here. This will happen // TODO: We should really cancel the active animations here. This will happen
// automatically when the view's intro animation starts, but it's a fragile link. // automatically when the view's intro animation starts, but it's a fragile link.
@@ -321,11 +367,10 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section
header.setTransientContainer(null); header.setTransientContainer(null);
} }
header.setContentVisible(true); header.setContentVisible(true);
mParent.addView(header, targetIndex); mParent.addView(header, targetPosition);
} else if (mParent.indexOfChild(header) != targetIndex) { } else {
mParent.changeViewPosition(header, targetIndex); mParent.changeViewPosition(header, targetPosition);
} }
return true;
} }
} }
@@ -400,10 +445,20 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section
@VisibleForTesting @VisibleForTesting
SectionHeaderView getGentleHeaderView() { ExpandableView getGentleHeaderView() {
return mGentleHeader; return mGentleHeader;
} }
@VisibleForTesting
ExpandableView getAlertingHeaderView() {
return mAlertingHeader;
}
@VisibleForTesting
ExpandableView getPeopleHeaderView() {
return mPeopleHubView;
}
private final ConfigurationListener mConfigurationListener = new ConfigurationListener() { private final ConfigurationListener mConfigurationListener = new ConfigurationListener() {
@Override @Override
public void onLocaleListChanged() { public void onLocaleListChanged() {

View File

@@ -19,6 +19,8 @@ package com.android.systemui.statusbar.notification.stack;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING; import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING;
import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_HEADS_UP;
import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_PEOPLE;
import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_SILENT; import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_SILENT;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
@@ -107,7 +109,7 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
@Test @Test
public void testInsertHeader() { public void testInsertHeader() {
// GIVEN a stack with HI and LO rows but no section headers // GIVEN a stack with HI and LO rows but no section headers
setStackState(ChildType.HIPRI, ChildType.HIPRI, ChildType.HIPRI, ChildType.LOPRI); setStackState(ChildType.ALERTING, ChildType.ALERTING, ChildType.ALERTING, ChildType.GENTLE);
// WHEN we update the section headers // WHEN we update the section headers
mSectionsManager.updateSectionBoundaries(); mSectionsManager.updateSectionBoundaries();
@@ -119,11 +121,15 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
@Test @Test
public void testRemoveHeader() { public void testRemoveHeader() {
// GIVEN a stack that originally had a header between the HI and LO sections // GIVEN a stack that originally had a header between the HI and LO sections
setStackState(ChildType.HIPRI, ChildType.HIPRI, ChildType.LOPRI); setStackState(ChildType.ALERTING, ChildType.ALERTING, ChildType.GENTLE);
mSectionsManager.updateSectionBoundaries(); mSectionsManager.updateSectionBoundaries();
// WHEN the last LO row is replaced with a HI row // WHEN the last LO row is replaced with a HI row
setStackState(ChildType.HIPRI, ChildType.HIPRI, ChildType.HEADER, ChildType.HIPRI); setStackState(
ChildType.ALERTING,
ChildType.ALERTING,
ChildType.GENTLE_HEADER,
ChildType.ALERTING);
clearInvocations(mNssl); clearInvocations(mNssl);
mSectionsManager.updateSectionBoundaries(); mSectionsManager.updateSectionBoundaries();
@@ -134,7 +140,7 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
@Test @Test
public void testDoNothingIfHeaderAlreadyRemoved() { public void testDoNothingIfHeaderAlreadyRemoved() {
// GIVEN a stack with only HI rows // GIVEN a stack with only HI rows
setStackState(ChildType.HIPRI, ChildType.HIPRI, ChildType.HIPRI); setStackState(ChildType.ALERTING, ChildType.ALERTING, ChildType.ALERTING);
// WHEN we update the sections headers // WHEN we update the sections headers
mSectionsManager.updateSectionBoundaries(); mSectionsManager.updateSectionBoundaries();
@@ -147,19 +153,19 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
public void testMoveHeaderForward() { public void testMoveHeaderForward() {
// GIVEN a stack that originally had a header between the HI and LO sections // GIVEN a stack that originally had a header between the HI and LO sections
setStackState( setStackState(
ChildType.HIPRI, ChildType.ALERTING,
ChildType.HIPRI, ChildType.ALERTING,
ChildType.HIPRI, ChildType.ALERTING,
ChildType.LOPRI); ChildType.GENTLE);
mSectionsManager.updateSectionBoundaries(); mSectionsManager.updateSectionBoundaries();
// WHEN the LO section moves forward // WHEN the LO section moves forward
setStackState( setStackState(
ChildType.HIPRI, ChildType.ALERTING,
ChildType.HIPRI, ChildType.ALERTING,
ChildType.LOPRI, ChildType.GENTLE,
ChildType.HEADER, ChildType.GENTLE_HEADER,
ChildType.LOPRI); ChildType.GENTLE);
mSectionsManager.updateSectionBoundaries(); mSectionsManager.updateSectionBoundaries();
// THEN the LO section header is also moved forward // THEN the LO section header is also moved forward
@@ -170,19 +176,19 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
public void testMoveHeaderBackward() { public void testMoveHeaderBackward() {
// GIVEN a stack that originally had a header between the HI and LO sections // GIVEN a stack that originally had a header between the HI and LO sections
setStackState( setStackState(
ChildType.HIPRI, ChildType.ALERTING,
ChildType.LOPRI, ChildType.GENTLE,
ChildType.LOPRI, ChildType.GENTLE,
ChildType.LOPRI); ChildType.GENTLE);
mSectionsManager.updateSectionBoundaries(); mSectionsManager.updateSectionBoundaries();
// WHEN the LO section moves backward // WHEN the LO section moves backward
setStackState( setStackState(
ChildType.HIPRI, ChildType.ALERTING,
ChildType.HEADER, ChildType.GENTLE_HEADER,
ChildType.HIPRI, ChildType.ALERTING,
ChildType.HIPRI, ChildType.ALERTING,
ChildType.LOPRI); ChildType.GENTLE);
mSectionsManager.updateSectionBoundaries(); mSectionsManager.updateSectionBoundaries();
// THEN the LO section header is also moved backward (with appropriate index shifting) // THEN the LO section header is also moved backward (with appropriate index shifting)
@@ -193,14 +199,14 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
public void testHeaderRemovedFromTransientParent() { public void testHeaderRemovedFromTransientParent() {
// GIVEN a stack where the header is animating away // GIVEN a stack where the header is animating away
setStackState( setStackState(
ChildType.HIPRI, ChildType.ALERTING,
ChildType.LOPRI, ChildType.GENTLE,
ChildType.LOPRI, ChildType.GENTLE,
ChildType.LOPRI); ChildType.GENTLE);
mSectionsManager.updateSectionBoundaries(); mSectionsManager.updateSectionBoundaries();
setStackState( setStackState(
ChildType.HIPRI, ChildType.ALERTING,
ChildType.HEADER); ChildType.GENTLE_HEADER);
mSectionsManager.updateSectionBoundaries(); mSectionsManager.updateSectionBoundaries();
clearInvocations(mNssl); clearInvocations(mNssl);
@@ -209,8 +215,8 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
// WHEN the LO section reappears // WHEN the LO section reappears
setStackState( setStackState(
ChildType.HIPRI, ChildType.ALERTING,
ChildType.LOPRI); ChildType.GENTLE);
mSectionsManager.updateSectionBoundaries(); mSectionsManager.updateSectionBoundaries();
// THEN the header is first removed from the transient parent before being added to the // THEN the header is first removed from the transient parent before being added to the
@@ -223,7 +229,7 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
public void testHeaderNotShownOnLockscreen() { public void testHeaderNotShownOnLockscreen() {
// GIVEN a stack of HI and LO notifs on the lockscreen // GIVEN a stack of HI and LO notifs on the lockscreen
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD); when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
setStackState(ChildType.HIPRI, ChildType.HIPRI, ChildType.HIPRI, ChildType.LOPRI); setStackState(ChildType.ALERTING, ChildType.ALERTING, ChildType.ALERTING, ChildType.GENTLE);
// WHEN we update the section headers // WHEN we update the section headers
mSectionsManager.updateSectionBoundaries(); mSectionsManager.updateSectionBoundaries();
@@ -236,7 +242,7 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
public void testHeaderShownWhenEnterLockscreen() { public void testHeaderShownWhenEnterLockscreen() {
// GIVEN a stack of HI and LO notifs on the lockscreen // GIVEN a stack of HI and LO notifs on the lockscreen
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD); when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
setStackState(ChildType.HIPRI, ChildType.HIPRI, ChildType.HIPRI, ChildType.LOPRI); setStackState(ChildType.ALERTING, ChildType.ALERTING, ChildType.ALERTING, ChildType.GENTLE);
mSectionsManager.updateSectionBoundaries(); mSectionsManager.updateSectionBoundaries();
// WHEN we unlock // WHEN we unlock
@@ -250,37 +256,104 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
@Test @Test
public void testHeaderHiddenWhenEnterLockscreen() { public void testHeaderHiddenWhenEnterLockscreen() {
// GIVEN a stack of HI and LO notifs on the shade // GIVEN a stack of HI and LO notifs on the shade
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE_LOCKED); setStackState(ChildType.ALERTING, ChildType.GENTLE_HEADER, ChildType.GENTLE);
setStackState(ChildType.HIPRI, ChildType.HIPRI, ChildType.HIPRI, ChildType.LOPRI);
mSectionsManager.updateSectionBoundaries();
// WHEN we go back to the keyguard // WHEN we go back to the keyguard
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD); when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
mSectionsManager.updateSectionBoundaries(); mSectionsManager.updateSectionBoundaries();
// Then the section header is removed // Then the section header is removed
verify(mNssl).removeView(eq(mSectionsManager.getGentleHeaderView())); verify(mNssl).removeView(mSectionsManager.getGentleHeaderView());
} }
private enum ChildType { HEADER, HIPRI, LOPRI } @Test
public void testPeopleFiltering_addHeadersFromShowingOnlyGentle() {
enablePeopleFiltering();
setStackState(
ChildType.GENTLE_HEADER,
ChildType.PERSON,
ChildType.ALERTING,
ChildType.GENTLE);
mSectionsManager.updateSectionBoundaries();
verify(mNssl).changeViewPosition(mSectionsManager.getGentleHeaderView(), 2);
verify(mNssl).addView(mSectionsManager.getAlertingHeaderView(), 1);
verify(mNssl).addView(mSectionsManager.getPeopleHeaderView(), 0);
}
@Test
public void testPeopleFiltering_addAllHeaders() {
enablePeopleFiltering();
setStackState(
ChildType.PERSON,
ChildType.ALERTING,
ChildType.GENTLE);
mSectionsManager.updateSectionBoundaries();
verify(mNssl).addView(mSectionsManager.getGentleHeaderView(), 2);
verify(mNssl).addView(mSectionsManager.getAlertingHeaderView(), 1);
verify(mNssl).addView(mSectionsManager.getPeopleHeaderView(), 0);
}
@Test
public void testPeopleFiltering_moveAllHeaders() {
enablePeopleFiltering();
setStackState(
ChildType.PEOPLE_HEADER,
ChildType.ALERTING_HEADER,
ChildType.GENTLE_HEADER,
ChildType.PERSON,
ChildType.ALERTING,
ChildType.GENTLE);
mSectionsManager.updateSectionBoundaries();
verify(mNssl).changeViewPosition(mSectionsManager.getGentleHeaderView(), 4);
verify(mNssl).changeViewPosition(mSectionsManager.getAlertingHeaderView(), 2);
verify(mNssl).changeViewPosition(mSectionsManager.getPeopleHeaderView(), 0);
}
private void enablePeopleFiltering() {
when(mSectionsFeatureManager.isFilteringEnabled()).thenReturn(true);
when(mSectionsFeatureManager.getNumberOfBuckets()).thenReturn(4);
}
private enum ChildType {
PEOPLE_HEADER, ALERTING_HEADER, GENTLE_HEADER, HEADS_UP, PERSON, ALERTING, GENTLE, OTHER
}
private void setStackState(ChildType... children) { private void setStackState(ChildType... children) {
when(mNssl.getChildCount()).thenReturn(children.length); when(mNssl.getChildCount()).thenReturn(children.length);
for (int i = 0; i < children.length; i++) { for (int i = 0; i < children.length; i++) {
View child; View child;
switch (children[i]) { switch (children[i]) {
case HEADER: case PEOPLE_HEADER:
child = mSectionsManager.getPeopleHeaderView();
break;
case ALERTING_HEADER:
child = mSectionsManager.getAlertingHeaderView();
break;
case GENTLE_HEADER:
child = mSectionsManager.getGentleHeaderView(); child = mSectionsManager.getGentleHeaderView();
break; break;
case HIPRI: case HEADS_UP:
case LOPRI: child = mockNotification(BUCKET_HEADS_UP);
ExpandableNotificationRow notifRow = mock(ExpandableNotificationRow.class, break;
RETURNS_DEEP_STUBS); case PERSON:
when(notifRow.getVisibility()).thenReturn(View.VISIBLE); child = mockNotification(BUCKET_PEOPLE);
when(notifRow.getEntry().getBucket()).thenReturn( break;
children[i] == ChildType.HIPRI ? BUCKET_ALERTING : BUCKET_SILENT); case ALERTING:
when(notifRow.getParent()).thenReturn(mNssl); child = mockNotification(BUCKET_ALERTING);
child = notifRow; break;
case GENTLE:
child = mockNotification(BUCKET_SILENT);
break;
case OTHER:
child = mock(View.class);
when(child.getVisibility()).thenReturn(View.VISIBLE);
when(child.getParent()).thenReturn(mNssl);
break; break;
default: default:
throw new RuntimeException("Unknown ChildType: " + children[i]); throw new RuntimeException("Unknown ChildType: " + children[i]);
@@ -289,4 +362,13 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
when(mNssl.indexOfChild(child)).thenReturn(i); when(mNssl.indexOfChild(child)).thenReturn(i);
} }
} }
private View mockNotification(int bucket) {
ExpandableNotificationRow notifRow = mock(ExpandableNotificationRow.class,
RETURNS_DEEP_STUBS);
when(notifRow.getVisibility()).thenReturn(View.VISIBLE);
when(notifRow.getEntry().getBucket()).thenReturn(bucket);
when(notifRow.getParent()).thenReturn(mNssl);
return notifRow;
}
} }