Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java +124 −69 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ 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 android.annotation.IntDef; Loading Loading @@ -100,14 +102,11 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section private boolean mInitialized = false; private SectionHeaderView mGentleHeader; private boolean mGentleHeaderVisible; @Nullable private View.OnClickListener mOnClearGentleNotifsClickListener; private SectionHeaderView mAlertingHeader; private boolean mAlertingHeaderVisible; private PeopleHubView mPeopleHubView; private boolean mPeopleHeaderVisible; private boolean mPeopleHubVisible = false; @Nullable private Subscription mPeopleHubSubscription; Loading Loading @@ -231,88 +230,135 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section 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 usingPeopleFiltering = mSectionsFeatureManager.isFilteringEnabled(); boolean peopleNotifsPresent = false; int currentPeopleHeaderIdx = -1; int peopleHeaderTarget = -1; int currentAlertingHeaderIdx = -1; int alertingHeaderTarget = -1; int currentGentleHeaderIdx = -1; int gentleHeaderTarget = -1; int viewCount = 0; int lastNotifIndex = 0; if (showHeaders) { final int childCount = mParent.getChildCount(); for (int i = 0; i < childCount; i++) { View child = mParent.getChildAt(i); if (child.getVisibility() == View.GONE || !(child instanceof ExpandableNotificationRow)) { // Track the existing positions of the headers if (child == mPeopleHubView) { currentPeopleHeaderIdx = i; continue; } if (child == mAlertingHeader) { currentAlertingHeaderIdx = i; continue; } if (child == mGentleHeader) { currentGentleHeaderIdx = i; continue; } if (!(child instanceof ExpandableNotificationRow)) { continue; } 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: if (peopleHeaderTarget == -1) { peopleNotifsPresent = true; peopleHeaderTarget = viewCount; viewCount++; 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 (usingPeopleFiltering && alertingHeaderTarget == -1) { alertingHeaderTarget = viewCount; viewCount++; 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 (gentleHeaderTarget == -1) { gentleHeaderTarget = viewCount; viewCount++; 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"); } viewCount++; } if (usingPeopleFiltering && mPeopleHubVisible && peopleHeaderTarget == -1) { 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; alertingHeaderTarget++; gentleHeaderTarget++; } else if (gentleHeaderTarget != -1) { peopleHeaderTarget = gentleHeaderTarget; gentleHeaderTarget++; } else { // Put it at the end of the list. peopleHeaderTarget = viewCount; } peopleHeaderTarget = lastNotifIndex; } } // Allow swiping the people header if the section is empty mPeopleHubView.setCanSwipe(mPeopleHubVisible && !peopleNotifsPresent); // Add headers in reverse order to preserve indices adjustHeaderVisibilityAndPosition( gentleHeaderTarget, mGentleHeader, currentGentleHeaderIdx); adjustHeaderVisibilityAndPosition( alertingHeaderTarget, mAlertingHeader, currentAlertingHeaderIdx); adjustHeaderVisibilityAndPosition( peopleHeaderTarget, mPeopleHubView, currentPeopleHeaderIdx); mPeopleHeaderVisible = adjustHeaderVisibilityAndPosition( peopleHeaderTarget, mPeopleHubView, mPeopleHeaderVisible); mAlertingHeaderVisible = adjustHeaderVisibilityAndPosition( alertingHeaderTarget, mAlertingHeader, mAlertingHeaderVisible); mGentleHeaderVisible = adjustHeaderVisibilityAndPosition( gentleHeaderTarget, mGentleHeader, mGentleHeaderVisible); // Update headers to reflect state of section contents mGentleHeader.setAreThereDismissableGentleNotifs( mParent.hasActiveClearableNotifications(ROWS_GENTLE)); mPeopleHubView.setCanSwipe(showHeaders && mPeopleHubVisible && !peopleNotifsPresent); if (peopleHeaderTarget != currentPeopleHeaderIdx) { mPeopleHubView.resetTranslation(); } } private boolean adjustHeaderVisibilityAndPosition( int targetIndex, StackScrollerDecorView header, boolean isCurrentlyVisible) { if (targetIndex == -1) { if (isCurrentlyVisible) { private void adjustHeaderVisibilityAndPosition( int targetPosition, StackScrollerDecorView header, int currentPosition) { if (targetPosition == -1) { if (currentPosition != -1) { mParent.removeView(header); } return false; } else { if (header instanceof SwipeableView) { ((SwipeableView) header).resetTranslation(); } if (!isCurrentlyVisible) { if (currentPosition == -1) { // 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 // automatically when the view's intro animation starts, but it's a fragile link. Loading @@ -321,11 +367,10 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section header.setTransientContainer(null); } header.setContentVisible(true); mParent.addView(header, targetIndex); } else if (mParent.indexOfChild(header) != targetIndex) { mParent.changeViewPosition(header, targetIndex); mParent.addView(header, targetPosition); } else { mParent.changeViewPosition(header, targetPosition); } return true; } } Loading Loading @@ -400,10 +445,20 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section @VisibleForTesting SectionHeaderView getGentleHeaderView() { ExpandableView getGentleHeaderView() { return mGentleHeader; } @VisibleForTesting ExpandableView getAlertingHeaderView() { return mAlertingHeader; } @VisibleForTesting ExpandableView getPeopleHeaderView() { return mPeopleHubView; } private final ConfigurationListener mConfigurationListener = new ConfigurationListener() { @Override public void onLocaleListChanged() { Loading packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java +129 −47 Original line number Diff line number Diff line Loading @@ -19,6 +19,8 @@ package com.android.systemui.statusbar.notification.stack; 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_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 org.mockito.ArgumentMatchers.any; Loading Loading @@ -107,7 +109,7 @@ public class NotificationSectionsManagerTest extends SysuiTestCase { @Test public void testInsertHeader() { // 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 mSectionsManager.updateSectionBoundaries(); Loading @@ -119,11 +121,15 @@ public class NotificationSectionsManagerTest extends SysuiTestCase { @Test public void testRemoveHeader() { // 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(); // 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); mSectionsManager.updateSectionBoundaries(); Loading @@ -134,7 +140,7 @@ public class NotificationSectionsManagerTest extends SysuiTestCase { @Test public void testDoNothingIfHeaderAlreadyRemoved() { // 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 mSectionsManager.updateSectionBoundaries(); Loading @@ -147,19 +153,19 @@ public class NotificationSectionsManagerTest extends SysuiTestCase { public void testMoveHeaderForward() { // GIVEN a stack that originally had a header between the HI and LO sections setStackState( ChildType.HIPRI, ChildType.HIPRI, ChildType.HIPRI, ChildType.LOPRI); ChildType.ALERTING, ChildType.ALERTING, ChildType.ALERTING, ChildType.GENTLE); mSectionsManager.updateSectionBoundaries(); // WHEN the LO section moves forward setStackState( ChildType.HIPRI, ChildType.HIPRI, ChildType.LOPRI, ChildType.HEADER, ChildType.LOPRI); ChildType.ALERTING, ChildType.ALERTING, ChildType.GENTLE, ChildType.GENTLE_HEADER, ChildType.GENTLE); mSectionsManager.updateSectionBoundaries(); // THEN the LO section header is also moved forward Loading @@ -170,19 +176,19 @@ public class NotificationSectionsManagerTest extends SysuiTestCase { public void testMoveHeaderBackward() { // GIVEN a stack that originally had a header between the HI and LO sections setStackState( ChildType.HIPRI, ChildType.LOPRI, ChildType.LOPRI, ChildType.LOPRI); ChildType.ALERTING, ChildType.GENTLE, ChildType.GENTLE, ChildType.GENTLE); mSectionsManager.updateSectionBoundaries(); // WHEN the LO section moves backward setStackState( ChildType.HIPRI, ChildType.HEADER, ChildType.HIPRI, ChildType.HIPRI, ChildType.LOPRI); ChildType.ALERTING, ChildType.GENTLE_HEADER, ChildType.ALERTING, ChildType.ALERTING, ChildType.GENTLE); mSectionsManager.updateSectionBoundaries(); // THEN the LO section header is also moved backward (with appropriate index shifting) Loading @@ -193,14 +199,14 @@ public class NotificationSectionsManagerTest extends SysuiTestCase { public void testHeaderRemovedFromTransientParent() { // GIVEN a stack where the header is animating away setStackState( ChildType.HIPRI, ChildType.LOPRI, ChildType.LOPRI, ChildType.LOPRI); ChildType.ALERTING, ChildType.GENTLE, ChildType.GENTLE, ChildType.GENTLE); mSectionsManager.updateSectionBoundaries(); setStackState( ChildType.HIPRI, ChildType.HEADER); ChildType.ALERTING, ChildType.GENTLE_HEADER); mSectionsManager.updateSectionBoundaries(); clearInvocations(mNssl); Loading @@ -209,8 +215,8 @@ public class NotificationSectionsManagerTest extends SysuiTestCase { // WHEN the LO section reappears setStackState( ChildType.HIPRI, ChildType.LOPRI); ChildType.ALERTING, ChildType.GENTLE); mSectionsManager.updateSectionBoundaries(); // THEN the header is first removed from the transient parent before being added to the Loading @@ -223,7 +229,7 @@ public class NotificationSectionsManagerTest extends SysuiTestCase { public void testHeaderNotShownOnLockscreen() { // GIVEN a stack of HI and LO notifs on the lockscreen 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 mSectionsManager.updateSectionBoundaries(); Loading @@ -236,7 +242,7 @@ public class NotificationSectionsManagerTest extends SysuiTestCase { public void testHeaderShownWhenEnterLockscreen() { // GIVEN a stack of HI and LO notifs on the lockscreen 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(); // WHEN we unlock Loading @@ -250,37 +256,104 @@ public class NotificationSectionsManagerTest extends SysuiTestCase { @Test public void testHeaderHiddenWhenEnterLockscreen() { // GIVEN a stack of HI and LO notifs on the shade when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE_LOCKED); setStackState(ChildType.HIPRI, ChildType.HIPRI, ChildType.HIPRI, ChildType.LOPRI); mSectionsManager.updateSectionBoundaries(); setStackState(ChildType.ALERTING, ChildType.GENTLE_HEADER, ChildType.GENTLE); // WHEN we go back to the keyguard when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD); mSectionsManager.updateSectionBoundaries(); // Then the section header is removed verify(mNssl).removeView(eq(mSectionsManager.getGentleHeaderView())); verify(mNssl).removeView(mSectionsManager.getGentleHeaderView()); } @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 enum ChildType { HEADER, HIPRI, LOPRI } 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) { when(mNssl.getChildCount()).thenReturn(children.length); for (int i = 0; i < children.length; i++) { View child; 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(); break; case HIPRI: case LOPRI: ExpandableNotificationRow notifRow = mock(ExpandableNotificationRow.class, RETURNS_DEEP_STUBS); when(notifRow.getVisibility()).thenReturn(View.VISIBLE); when(notifRow.getEntry().getBucket()).thenReturn( children[i] == ChildType.HIPRI ? BUCKET_ALERTING : BUCKET_SILENT); when(notifRow.getParent()).thenReturn(mNssl); child = notifRow; case HEADS_UP: child = mockNotification(BUCKET_HEADS_UP); break; case PERSON: child = mockNotification(BUCKET_PEOPLE); break; case ALERTING: child = mockNotification(BUCKET_ALERTING); 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; default: throw new RuntimeException("Unknown ChildType: " + children[i]); Loading @@ -289,4 +362,13 @@ public class NotificationSectionsManagerTest extends SysuiTestCase { 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; } } Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java +124 −69 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ 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 android.annotation.IntDef; Loading Loading @@ -100,14 +102,11 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section private boolean mInitialized = false; private SectionHeaderView mGentleHeader; private boolean mGentleHeaderVisible; @Nullable private View.OnClickListener mOnClearGentleNotifsClickListener; private SectionHeaderView mAlertingHeader; private boolean mAlertingHeaderVisible; private PeopleHubView mPeopleHubView; private boolean mPeopleHeaderVisible; private boolean mPeopleHubVisible = false; @Nullable private Subscription mPeopleHubSubscription; Loading Loading @@ -231,88 +230,135 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section 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 usingPeopleFiltering = mSectionsFeatureManager.isFilteringEnabled(); boolean peopleNotifsPresent = false; int currentPeopleHeaderIdx = -1; int peopleHeaderTarget = -1; int currentAlertingHeaderIdx = -1; int alertingHeaderTarget = -1; int currentGentleHeaderIdx = -1; int gentleHeaderTarget = -1; int viewCount = 0; int lastNotifIndex = 0; if (showHeaders) { final int childCount = mParent.getChildCount(); for (int i = 0; i < childCount; i++) { View child = mParent.getChildAt(i); if (child.getVisibility() == View.GONE || !(child instanceof ExpandableNotificationRow)) { // Track the existing positions of the headers if (child == mPeopleHubView) { currentPeopleHeaderIdx = i; continue; } if (child == mAlertingHeader) { currentAlertingHeaderIdx = i; continue; } if (child == mGentleHeader) { currentGentleHeaderIdx = i; continue; } if (!(child instanceof ExpandableNotificationRow)) { continue; } 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: if (peopleHeaderTarget == -1) { peopleNotifsPresent = true; peopleHeaderTarget = viewCount; viewCount++; 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 (usingPeopleFiltering && alertingHeaderTarget == -1) { alertingHeaderTarget = viewCount; viewCount++; 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 (gentleHeaderTarget == -1) { gentleHeaderTarget = viewCount; viewCount++; 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"); } viewCount++; } if (usingPeopleFiltering && mPeopleHubVisible && peopleHeaderTarget == -1) { 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; alertingHeaderTarget++; gentleHeaderTarget++; } else if (gentleHeaderTarget != -1) { peopleHeaderTarget = gentleHeaderTarget; gentleHeaderTarget++; } else { // Put it at the end of the list. peopleHeaderTarget = viewCount; } peopleHeaderTarget = lastNotifIndex; } } // Allow swiping the people header if the section is empty mPeopleHubView.setCanSwipe(mPeopleHubVisible && !peopleNotifsPresent); // Add headers in reverse order to preserve indices adjustHeaderVisibilityAndPosition( gentleHeaderTarget, mGentleHeader, currentGentleHeaderIdx); adjustHeaderVisibilityAndPosition( alertingHeaderTarget, mAlertingHeader, currentAlertingHeaderIdx); adjustHeaderVisibilityAndPosition( peopleHeaderTarget, mPeopleHubView, currentPeopleHeaderIdx); mPeopleHeaderVisible = adjustHeaderVisibilityAndPosition( peopleHeaderTarget, mPeopleHubView, mPeopleHeaderVisible); mAlertingHeaderVisible = adjustHeaderVisibilityAndPosition( alertingHeaderTarget, mAlertingHeader, mAlertingHeaderVisible); mGentleHeaderVisible = adjustHeaderVisibilityAndPosition( gentleHeaderTarget, mGentleHeader, mGentleHeaderVisible); // Update headers to reflect state of section contents mGentleHeader.setAreThereDismissableGentleNotifs( mParent.hasActiveClearableNotifications(ROWS_GENTLE)); mPeopleHubView.setCanSwipe(showHeaders && mPeopleHubVisible && !peopleNotifsPresent); if (peopleHeaderTarget != currentPeopleHeaderIdx) { mPeopleHubView.resetTranslation(); } } private boolean adjustHeaderVisibilityAndPosition( int targetIndex, StackScrollerDecorView header, boolean isCurrentlyVisible) { if (targetIndex == -1) { if (isCurrentlyVisible) { private void adjustHeaderVisibilityAndPosition( int targetPosition, StackScrollerDecorView header, int currentPosition) { if (targetPosition == -1) { if (currentPosition != -1) { mParent.removeView(header); } return false; } else { if (header instanceof SwipeableView) { ((SwipeableView) header).resetTranslation(); } if (!isCurrentlyVisible) { if (currentPosition == -1) { // 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 // automatically when the view's intro animation starts, but it's a fragile link. Loading @@ -321,11 +367,10 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section header.setTransientContainer(null); } header.setContentVisible(true); mParent.addView(header, targetIndex); } else if (mParent.indexOfChild(header) != targetIndex) { mParent.changeViewPosition(header, targetIndex); mParent.addView(header, targetPosition); } else { mParent.changeViewPosition(header, targetPosition); } return true; } } Loading Loading @@ -400,10 +445,20 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section @VisibleForTesting SectionHeaderView getGentleHeaderView() { ExpandableView getGentleHeaderView() { return mGentleHeader; } @VisibleForTesting ExpandableView getAlertingHeaderView() { return mAlertingHeader; } @VisibleForTesting ExpandableView getPeopleHeaderView() { return mPeopleHubView; } private final ConfigurationListener mConfigurationListener = new ConfigurationListener() { @Override public void onLocaleListChanged() { Loading
packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java +129 −47 Original line number Diff line number Diff line Loading @@ -19,6 +19,8 @@ package com.android.systemui.statusbar.notification.stack; 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_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 org.mockito.ArgumentMatchers.any; Loading Loading @@ -107,7 +109,7 @@ public class NotificationSectionsManagerTest extends SysuiTestCase { @Test public void testInsertHeader() { // 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 mSectionsManager.updateSectionBoundaries(); Loading @@ -119,11 +121,15 @@ public class NotificationSectionsManagerTest extends SysuiTestCase { @Test public void testRemoveHeader() { // 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(); // 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); mSectionsManager.updateSectionBoundaries(); Loading @@ -134,7 +140,7 @@ public class NotificationSectionsManagerTest extends SysuiTestCase { @Test public void testDoNothingIfHeaderAlreadyRemoved() { // 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 mSectionsManager.updateSectionBoundaries(); Loading @@ -147,19 +153,19 @@ public class NotificationSectionsManagerTest extends SysuiTestCase { public void testMoveHeaderForward() { // GIVEN a stack that originally had a header between the HI and LO sections setStackState( ChildType.HIPRI, ChildType.HIPRI, ChildType.HIPRI, ChildType.LOPRI); ChildType.ALERTING, ChildType.ALERTING, ChildType.ALERTING, ChildType.GENTLE); mSectionsManager.updateSectionBoundaries(); // WHEN the LO section moves forward setStackState( ChildType.HIPRI, ChildType.HIPRI, ChildType.LOPRI, ChildType.HEADER, ChildType.LOPRI); ChildType.ALERTING, ChildType.ALERTING, ChildType.GENTLE, ChildType.GENTLE_HEADER, ChildType.GENTLE); mSectionsManager.updateSectionBoundaries(); // THEN the LO section header is also moved forward Loading @@ -170,19 +176,19 @@ public class NotificationSectionsManagerTest extends SysuiTestCase { public void testMoveHeaderBackward() { // GIVEN a stack that originally had a header between the HI and LO sections setStackState( ChildType.HIPRI, ChildType.LOPRI, ChildType.LOPRI, ChildType.LOPRI); ChildType.ALERTING, ChildType.GENTLE, ChildType.GENTLE, ChildType.GENTLE); mSectionsManager.updateSectionBoundaries(); // WHEN the LO section moves backward setStackState( ChildType.HIPRI, ChildType.HEADER, ChildType.HIPRI, ChildType.HIPRI, ChildType.LOPRI); ChildType.ALERTING, ChildType.GENTLE_HEADER, ChildType.ALERTING, ChildType.ALERTING, ChildType.GENTLE); mSectionsManager.updateSectionBoundaries(); // THEN the LO section header is also moved backward (with appropriate index shifting) Loading @@ -193,14 +199,14 @@ public class NotificationSectionsManagerTest extends SysuiTestCase { public void testHeaderRemovedFromTransientParent() { // GIVEN a stack where the header is animating away setStackState( ChildType.HIPRI, ChildType.LOPRI, ChildType.LOPRI, ChildType.LOPRI); ChildType.ALERTING, ChildType.GENTLE, ChildType.GENTLE, ChildType.GENTLE); mSectionsManager.updateSectionBoundaries(); setStackState( ChildType.HIPRI, ChildType.HEADER); ChildType.ALERTING, ChildType.GENTLE_HEADER); mSectionsManager.updateSectionBoundaries(); clearInvocations(mNssl); Loading @@ -209,8 +215,8 @@ public class NotificationSectionsManagerTest extends SysuiTestCase { // WHEN the LO section reappears setStackState( ChildType.HIPRI, ChildType.LOPRI); ChildType.ALERTING, ChildType.GENTLE); mSectionsManager.updateSectionBoundaries(); // THEN the header is first removed from the transient parent before being added to the Loading @@ -223,7 +229,7 @@ public class NotificationSectionsManagerTest extends SysuiTestCase { public void testHeaderNotShownOnLockscreen() { // GIVEN a stack of HI and LO notifs on the lockscreen 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 mSectionsManager.updateSectionBoundaries(); Loading @@ -236,7 +242,7 @@ public class NotificationSectionsManagerTest extends SysuiTestCase { public void testHeaderShownWhenEnterLockscreen() { // GIVEN a stack of HI and LO notifs on the lockscreen 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(); // WHEN we unlock Loading @@ -250,37 +256,104 @@ public class NotificationSectionsManagerTest extends SysuiTestCase { @Test public void testHeaderHiddenWhenEnterLockscreen() { // GIVEN a stack of HI and LO notifs on the shade when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE_LOCKED); setStackState(ChildType.HIPRI, ChildType.HIPRI, ChildType.HIPRI, ChildType.LOPRI); mSectionsManager.updateSectionBoundaries(); setStackState(ChildType.ALERTING, ChildType.GENTLE_HEADER, ChildType.GENTLE); // WHEN we go back to the keyguard when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD); mSectionsManager.updateSectionBoundaries(); // Then the section header is removed verify(mNssl).removeView(eq(mSectionsManager.getGentleHeaderView())); verify(mNssl).removeView(mSectionsManager.getGentleHeaderView()); } @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 enum ChildType { HEADER, HIPRI, LOPRI } 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) { when(mNssl.getChildCount()).thenReturn(children.length); for (int i = 0; i < children.length; i++) { View child; 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(); break; case HIPRI: case LOPRI: ExpandableNotificationRow notifRow = mock(ExpandableNotificationRow.class, RETURNS_DEEP_STUBS); when(notifRow.getVisibility()).thenReturn(View.VISIBLE); when(notifRow.getEntry().getBucket()).thenReturn( children[i] == ChildType.HIPRI ? BUCKET_ALERTING : BUCKET_SILENT); when(notifRow.getParent()).thenReturn(mNssl); child = notifRow; case HEADS_UP: child = mockNotification(BUCKET_HEADS_UP); break; case PERSON: child = mockNotification(BUCKET_PEOPLE); break; case ALERTING: child = mockNotification(BUCKET_ALERTING); 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; default: throw new RuntimeException("Unknown ChildType: " + children[i]); Loading @@ -289,4 +362,13 @@ public class NotificationSectionsManagerTest extends SysuiTestCase { 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; } }