Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt +7 −4 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import android.provider.DeviceConfig import com.android.internal.annotations.VisibleForTesting import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NOTIFICATIONS_USE_PEOPLE_FILTERING import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_FOREGROUND_SERVICE import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_HEADS_UP import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_MEDIA_CONTROLS import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_PEOPLE Loading Loading @@ -52,12 +53,14 @@ class NotificationSectionsFeatureManager @Inject constructor( fun getNotificationBuckets(): IntArray { return when { isFilteringEnabled() && isMediaControlsEnabled() -> intArrayOf(BUCKET_HEADS_UP, BUCKET_MEDIA_CONTROLS, BUCKET_PEOPLE, BUCKET_ALERTING, BUCKET_SILENT) intArrayOf(BUCKET_HEADS_UP, BUCKET_FOREGROUND_SERVICE, BUCKET_MEDIA_CONTROLS, BUCKET_PEOPLE, BUCKET_ALERTING, BUCKET_SILENT) !isFilteringEnabled() && isMediaControlsEnabled() -> intArrayOf(BUCKET_HEADS_UP, BUCKET_MEDIA_CONTROLS, BUCKET_ALERTING, BUCKET_SILENT) intArrayOf(BUCKET_HEADS_UP, BUCKET_FOREGROUND_SERVICE, BUCKET_MEDIA_CONTROLS, BUCKET_ALERTING, BUCKET_SILENT) isFilteringEnabled() && !isMediaControlsEnabled() -> intArrayOf(BUCKET_HEADS_UP, BUCKET_PEOPLE, BUCKET_ALERTING, BUCKET_SILENT) intArrayOf(BUCKET_HEADS_UP, BUCKET_FOREGROUND_SERVICE, BUCKET_PEOPLE, BUCKET_ALERTING, BUCKET_SILENT) NotificationUtils.useNewInterruptionModel(context) -> intArrayOf(BUCKET_ALERTING, BUCKET_SILENT) else -> Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt +3 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ import com.android.systemui.statusbar.notification.collection.provider.HighPrior import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_NON_PERSON import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_FOREGROUND_SERVICE import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_HEADS_UP import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_PEOPLE import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_SILENT Loading Loading @@ -162,6 +163,8 @@ open class NotificationRankingManager @Inject constructor( val isMedia = isImportantMedia(entry) val isSystemMax = entry.isSystemMax() return when { entry.sbn.notification.isForegroundService && entry.sbn.notification.isColorized -> BUCKET_FOREGROUND_SERVICE usePeopleFiltering && entry.getPeopleNotificationType() != TYPE_NON_PERSON -> BUCKET_PEOPLE isHeadsUp || isMedia || isSystemMax || entry.isHighPriority() -> Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java +28 −35 Original line number Diff line number Diff line Loading @@ -29,12 +29,10 @@ import android.content.Intent; import android.provider.Settings; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.R; import com.android.systemui.media.KeyguardMediaController; import com.android.systemui.media.MediaHierarchyManager; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.StatusBarState; Loading Loading @@ -329,8 +327,6 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section boolean peopleNotifsPresent = false; int currentMediaControlsIdx = -1; // Currently, just putting media controls in the front and incrementing the position based // on the number of heads-up notifs. int mediaControlsTarget = usingMediaControls ? 0 : -1; int currentIncomingHeaderIdx = -1; int incomingHeaderTarget = -1; Loading Loading @@ -408,6 +404,11 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section mediaControlsTarget++; } break; case BUCKET_FOREGROUND_SERVICE: if (mediaControlsTarget != -1) { mediaControlsTarget++; } break; case BUCKET_PEOPLE: mLogger.logPosition(i, "Conversation"); peopleNotifsPresent = true; Loading Loading @@ -488,7 +489,8 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section adjustHeaderVisibilityAndPosition( peopleHeaderTarget, mPeopleHubView, currentPeopleHeaderIdx); adjustViewPosition(mediaControlsTarget, mMediaControlsView, currentMediaControlsIdx); adjustViewPosition(incomingHeaderTarget, mIncomingHeader, currentIncomingHeaderIdx); adjustHeaderVisibilityAndPosition(incomingHeaderTarget, mIncomingHeader, currentIncomingHeaderIdx); mLogger.logStr("Final order:"); Loading @@ -508,45 +510,29 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section private void adjustHeaderVisibilityAndPosition( int targetPosition, StackScrollerDecorView header, int currentPosition) { if (targetPosition == -1) { if (currentPosition != -1) { mParent.removeView(header); } } else { 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. if (header.getTransientContainer() != null) { header.getTransientContainer().removeTransientView(header); header.setTransientContainer(null); } adjustViewPosition(targetPosition, header, currentPosition); if (targetPosition != -1 && currentPosition == -1) { header.setContentVisible(true); mParent.addView(header, targetPosition); } else { mParent.changeViewPosition(header, targetPosition); } } } private void adjustViewPosition(int targetPosition, ExpandableView header, int currentPosition) { private void adjustViewPosition(int targetPosition, ExpandableView view, int currentPosition) { if (targetPosition == -1) { if (currentPosition != -1) { mParent.removeView(header); mParent.removeView(view); } } else { 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. if (header.getTransientContainer() != null) { header.getTransientContainer().removeTransientView(header); header.setTransientContainer(null); if (view.getTransientContainer() != null) { view.getTransientContainer().removeTransientView(view); view.setTransientContainer(null); } mParent.addView(header, targetPosition); mParent.addView(view, targetPosition); } else { mParent.changeViewPosition(header, targetPosition); mParent.changeViewPosition(view, targetPosition); } } } Loading Loading @@ -640,6 +626,11 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section return mMediaControlsView; } @VisibleForTesting ExpandableView getIncomingHeaderView() { return mIncomingHeader; } @VisibleForTesting void setPeopleHubVisible(boolean visible) { mPeopleHubVisible = visible; Loading Loading @@ -685,6 +676,7 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section @Retention(SOURCE) @IntDef(prefix = { "BUCKET_" }, value = { BUCKET_HEADS_UP, BUCKET_FOREGROUND_SERVICE, BUCKET_MEDIA_CONTROLS, BUCKET_PEOPLE, BUCKET_ALERTING, Loading @@ -692,8 +684,9 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section }) public @interface PriorityBucket {} public static final int BUCKET_HEADS_UP = 0; public static final int BUCKET_MEDIA_CONTROLS = 1; public static final int BUCKET_PEOPLE = 2; public static final int BUCKET_ALERTING = 3; public static final int BUCKET_SILENT = 4; public static final int BUCKET_FOREGROUND_SERVICE = 1; public static final int BUCKET_MEDIA_CONTROLS = 2; public static final int BUCKET_PEOPLE = 3; public static final int BUCKET_ALERTING = 4; public static final int BUCKET_SILENT = 5; } packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java +211 −11 Original line number Diff line number Diff line Loading @@ -19,15 +19,19 @@ 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_FOREGROUND_SERVICE; 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.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.RETURNS_DEEP_STUBS; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; Loading Loading @@ -62,6 +66,9 @@ import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; import java.util.ArrayList; import java.util.List; @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) Loading @@ -84,10 +91,23 @@ public class NotificationSectionsManagerTest extends SysuiTestCase { @Before public void setUp() { when(mSectionsFeatureManager.getNumberOfBuckets()).thenReturn(2); when(mNotificationRowComponent.getActivatableNotificationViewController()).thenReturn( mActivatableNotificationViewController ); when(mSectionsFeatureManager.getNumberOfBuckets()).thenAnswer( invocation -> { int count = 2; if (mSectionsFeatureManager.isFilteringEnabled()) { count = 5; } if (mSectionsFeatureManager.isMediaControlsEnabled()) { if (!mSectionsFeatureManager.isFilteringEnabled()) { count = 5; } else { count += 1; } } return count; }); when(mNotificationRowComponent.getActivatableNotificationViewController()) .thenReturn(mActivatableNotificationViewController); mSectionsManager = new NotificationSectionsManager( mActivityStarterDelegate, Loading @@ -104,6 +124,7 @@ public class NotificationSectionsManagerTest extends SysuiTestCase { mSectionsManager.initialize(mNssl, LayoutInflater.from(mContext)); when(mNssl.indexOfChild(any(View.class))).thenReturn(-1); when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE); } @Test(expected = IllegalStateException.class) Loading Loading @@ -338,6 +359,58 @@ public class NotificationSectionsManagerTest extends SysuiTestCase { verify(mNssl).changeViewPosition(mSectionsManager.getPeopleHeaderView(), 0); } @Test public void testPeopleFiltering_HunWhilePeopleVisible() { enablePeopleFiltering(); setupMockStack( ChildType.PEOPLE_HEADER, ChildType.HEADS_UP, ChildType.PERSON, ChildType.ALERTING_HEADER, ChildType.GENTLE_HEADER, ChildType.GENTLE ); mSectionsManager.updateSectionBoundaries(); verifyMockStack( ChildType.INCOMING_HEADER, ChildType.HEADS_UP, ChildType.PEOPLE_HEADER, ChildType.PERSON, ChildType.GENTLE_HEADER, ChildType.GENTLE ); } @Test public void testPeopleFiltering_Fsn() { enablePeopleFiltering(); setupMockStack( ChildType.INCOMING_HEADER, ChildType.HEADS_UP, ChildType.PEOPLE_HEADER, ChildType.FSN, ChildType.PERSON, ChildType.ALERTING, ChildType.GENTLE ); mSectionsManager.updateSectionBoundaries(); verifyMockStack( ChildType.INCOMING_HEADER, ChildType.HEADS_UP, ChildType.FSN, ChildType.PEOPLE_HEADER, ChildType.PERSON, ChildType.ALERTING_HEADER, ChildType.ALERTING, ChildType.GENTLE_HEADER, ChildType.GENTLE ); } @Test public void testMediaControls_AddWhenEnterKeyguard() { enableMediaControls(); Loading @@ -358,30 +431,28 @@ public class NotificationSectionsManagerTest extends SysuiTestCase { enableMediaControls(); // GIVEN a stack that doesn't include media controls but includes HEADS_UP setStackState(ChildType.HEADS_UP, ChildType.ALERTING, ChildType.GENTLE_HEADER, setupMockStack(ChildType.HEADS_UP, ChildType.ALERTING, ChildType.GENTLE_HEADER, ChildType.GENTLE); // WHEN we go back to the keyguard when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD); mSectionsManager.updateSectionBoundaries(); // Then the media controls are added after HEADS_UP verify(mNssl).addView(mSectionsManager.getMediaControlsView(), 1); verifyMockStack(ChildType.HEADS_UP, ChildType.MEDIA_CONTROLS, ChildType.ALERTING, ChildType.GENTLE); } private void enablePeopleFiltering() { when(mSectionsFeatureManager.isFilteringEnabled()).thenReturn(true); when(mSectionsFeatureManager.getNumberOfBuckets()).thenReturn(4); } private void enableMediaControls() { when(mSectionsFeatureManager.isMediaControlsEnabled()).thenReturn(true); when(mSectionsFeatureManager.getNumberOfBuckets()).thenReturn(4); } private enum ChildType { MEDIA_CONTROLS, PEOPLE_HEADER, ALERTING_HEADER, GENTLE_HEADER, HEADS_UP, PERSON, ALERTING, GENTLE, OTHER INCOMING_HEADER, MEDIA_CONTROLS, PEOPLE_HEADER, ALERTING_HEADER, GENTLE_HEADER, HEADS_UP, FSN, PERSON, ALERTING, GENTLE, OTHER } private void setStackState(ChildType... children) { Loading @@ -389,6 +460,9 @@ public class NotificationSectionsManagerTest extends SysuiTestCase { for (int i = 0; i < children.length; i++) { View child; switch (children[i]) { case INCOMING_HEADER: child = mSectionsManager.getIncomingHeaderView(); break; case MEDIA_CONTROLS: child = mSectionsManager.getMediaControlsView(); break; Loading @@ -404,6 +478,9 @@ public class NotificationSectionsManagerTest extends SysuiTestCase { case HEADS_UP: child = mockNotification(BUCKET_HEADS_UP); break; case FSN: child = mockNotification(BUCKET_FOREGROUND_SERVICE); break; case PERSON: child = mockNotification(BUCKET_PEOPLE); break; Loading Loading @@ -434,4 +511,127 @@ public class NotificationSectionsManagerTest extends SysuiTestCase { when(notifRow.getParent()).thenReturn(mNssl); return notifRow; } private void verifyMockStack(ChildType... expected) { final List<ChildType> actual = new ArrayList<>(); int childCount = mNssl.getChildCount(); for (int i = 0; i < childCount; i++) { View child = mNssl.getChildAt(i); if (child == mSectionsManager.getIncomingHeaderView()) { actual.add(ChildType.INCOMING_HEADER); continue; } if (child == mSectionsManager.getMediaControlsView()) { actual.add(ChildType.MEDIA_CONTROLS); continue; } if (child == mSectionsManager.getPeopleHeaderView()) { actual.add(ChildType.PEOPLE_HEADER); continue; } if (child == mSectionsManager.getAlertingHeaderView()) { actual.add(ChildType.ALERTING_HEADER); continue; } if (child == mSectionsManager.getGentleHeaderView()) { actual.add(ChildType.GENTLE_HEADER); continue; } if (child instanceof ExpandableNotificationRow) { switch (((ExpandableNotificationRow) child).getEntry().getBucket()) { case BUCKET_HEADS_UP: actual.add(ChildType.HEADS_UP); break; case BUCKET_FOREGROUND_SERVICE: actual.add(ChildType.FSN); break; case BUCKET_PEOPLE: actual.add(ChildType.PERSON); break; case BUCKET_ALERTING: actual.add(ChildType.ALERTING); break; case BUCKET_SILENT: actual.add(ChildType.GENTLE); break; default: actual.add(ChildType.OTHER); break; } continue; } actual.add(ChildType.OTHER); } assertThat(actual).containsExactly((Object[]) expected).inOrder(); } private void setupMockStack(ChildType... childTypes) { final List<View> children = new ArrayList<>(); when(mNssl.getChildCount()).thenAnswer(invocation -> children.size()); when(mNssl.getChildAt(anyInt())) .thenAnswer(invocation -> children.get(invocation.getArgument(0))); when(mNssl.indexOfChild(any())) .thenAnswer(invocation -> children.indexOf(invocation.getArgument(0))); doAnswer(invocation -> { View child = invocation.getArgument(0); int index = invocation.getArgument(1); children.add(index, child); return null; }).when(mNssl).addView(any(), anyInt()); doAnswer(invocation -> { View child = invocation.getArgument(0); children.remove(child); return null; }).when(mNssl).removeView(any()); doAnswer(invocation -> { View child = invocation.getArgument(0); int newIndex = invocation.getArgument(1); children.remove(child); children.add(newIndex, child); return null; }).when(mNssl).changeViewPosition(any(), anyInt()); for (ChildType childType : childTypes) { View child; switch (childType) { case INCOMING_HEADER: child = mSectionsManager.getIncomingHeaderView(); break; case MEDIA_CONTROLS: child = mSectionsManager.getMediaControlsView(); break; case PEOPLE_HEADER: child = mSectionsManager.getPeopleHeaderView(); break; case ALERTING_HEADER: child = mSectionsManager.getAlertingHeaderView(); break; case GENTLE_HEADER: child = mSectionsManager.getGentleHeaderView(); break; case HEADS_UP: child = mockNotification(BUCKET_HEADS_UP); break; case FSN: child = mockNotification(BUCKET_FOREGROUND_SERVICE); 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: " + childType); } children.add(child); } } } Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt +7 −4 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import android.provider.DeviceConfig import com.android.internal.annotations.VisibleForTesting import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NOTIFICATIONS_USE_PEOPLE_FILTERING import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_FOREGROUND_SERVICE import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_HEADS_UP import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_MEDIA_CONTROLS import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_PEOPLE Loading Loading @@ -52,12 +53,14 @@ class NotificationSectionsFeatureManager @Inject constructor( fun getNotificationBuckets(): IntArray { return when { isFilteringEnabled() && isMediaControlsEnabled() -> intArrayOf(BUCKET_HEADS_UP, BUCKET_MEDIA_CONTROLS, BUCKET_PEOPLE, BUCKET_ALERTING, BUCKET_SILENT) intArrayOf(BUCKET_HEADS_UP, BUCKET_FOREGROUND_SERVICE, BUCKET_MEDIA_CONTROLS, BUCKET_PEOPLE, BUCKET_ALERTING, BUCKET_SILENT) !isFilteringEnabled() && isMediaControlsEnabled() -> intArrayOf(BUCKET_HEADS_UP, BUCKET_MEDIA_CONTROLS, BUCKET_ALERTING, BUCKET_SILENT) intArrayOf(BUCKET_HEADS_UP, BUCKET_FOREGROUND_SERVICE, BUCKET_MEDIA_CONTROLS, BUCKET_ALERTING, BUCKET_SILENT) isFilteringEnabled() && !isMediaControlsEnabled() -> intArrayOf(BUCKET_HEADS_UP, BUCKET_PEOPLE, BUCKET_ALERTING, BUCKET_SILENT) intArrayOf(BUCKET_HEADS_UP, BUCKET_FOREGROUND_SERVICE, BUCKET_PEOPLE, BUCKET_ALERTING, BUCKET_SILENT) NotificationUtils.useNewInterruptionModel(context) -> intArrayOf(BUCKET_ALERTING, BUCKET_SILENT) else -> Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt +3 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ import com.android.systemui.statusbar.notification.collection.provider.HighPrior import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_NON_PERSON import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_FOREGROUND_SERVICE import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_HEADS_UP import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_PEOPLE import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_SILENT Loading Loading @@ -162,6 +163,8 @@ open class NotificationRankingManager @Inject constructor( val isMedia = isImportantMedia(entry) val isSystemMax = entry.isSystemMax() return when { entry.sbn.notification.isForegroundService && entry.sbn.notification.isColorized -> BUCKET_FOREGROUND_SERVICE usePeopleFiltering && entry.getPeopleNotificationType() != TYPE_NON_PERSON -> BUCKET_PEOPLE isHeadsUp || isMedia || isSystemMax || entry.isHighPriority() -> Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java +28 −35 Original line number Diff line number Diff line Loading @@ -29,12 +29,10 @@ import android.content.Intent; import android.provider.Settings; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.R; import com.android.systemui.media.KeyguardMediaController; import com.android.systemui.media.MediaHierarchyManager; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.StatusBarState; Loading Loading @@ -329,8 +327,6 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section boolean peopleNotifsPresent = false; int currentMediaControlsIdx = -1; // Currently, just putting media controls in the front and incrementing the position based // on the number of heads-up notifs. int mediaControlsTarget = usingMediaControls ? 0 : -1; int currentIncomingHeaderIdx = -1; int incomingHeaderTarget = -1; Loading Loading @@ -408,6 +404,11 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section mediaControlsTarget++; } break; case BUCKET_FOREGROUND_SERVICE: if (mediaControlsTarget != -1) { mediaControlsTarget++; } break; case BUCKET_PEOPLE: mLogger.logPosition(i, "Conversation"); peopleNotifsPresent = true; Loading Loading @@ -488,7 +489,8 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section adjustHeaderVisibilityAndPosition( peopleHeaderTarget, mPeopleHubView, currentPeopleHeaderIdx); adjustViewPosition(mediaControlsTarget, mMediaControlsView, currentMediaControlsIdx); adjustViewPosition(incomingHeaderTarget, mIncomingHeader, currentIncomingHeaderIdx); adjustHeaderVisibilityAndPosition(incomingHeaderTarget, mIncomingHeader, currentIncomingHeaderIdx); mLogger.logStr("Final order:"); Loading @@ -508,45 +510,29 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section private void adjustHeaderVisibilityAndPosition( int targetPosition, StackScrollerDecorView header, int currentPosition) { if (targetPosition == -1) { if (currentPosition != -1) { mParent.removeView(header); } } else { 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. if (header.getTransientContainer() != null) { header.getTransientContainer().removeTransientView(header); header.setTransientContainer(null); } adjustViewPosition(targetPosition, header, currentPosition); if (targetPosition != -1 && currentPosition == -1) { header.setContentVisible(true); mParent.addView(header, targetPosition); } else { mParent.changeViewPosition(header, targetPosition); } } } private void adjustViewPosition(int targetPosition, ExpandableView header, int currentPosition) { private void adjustViewPosition(int targetPosition, ExpandableView view, int currentPosition) { if (targetPosition == -1) { if (currentPosition != -1) { mParent.removeView(header); mParent.removeView(view); } } else { 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. if (header.getTransientContainer() != null) { header.getTransientContainer().removeTransientView(header); header.setTransientContainer(null); if (view.getTransientContainer() != null) { view.getTransientContainer().removeTransientView(view); view.setTransientContainer(null); } mParent.addView(header, targetPosition); mParent.addView(view, targetPosition); } else { mParent.changeViewPosition(header, targetPosition); mParent.changeViewPosition(view, targetPosition); } } } Loading Loading @@ -640,6 +626,11 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section return mMediaControlsView; } @VisibleForTesting ExpandableView getIncomingHeaderView() { return mIncomingHeader; } @VisibleForTesting void setPeopleHubVisible(boolean visible) { mPeopleHubVisible = visible; Loading Loading @@ -685,6 +676,7 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section @Retention(SOURCE) @IntDef(prefix = { "BUCKET_" }, value = { BUCKET_HEADS_UP, BUCKET_FOREGROUND_SERVICE, BUCKET_MEDIA_CONTROLS, BUCKET_PEOPLE, BUCKET_ALERTING, Loading @@ -692,8 +684,9 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section }) public @interface PriorityBucket {} public static final int BUCKET_HEADS_UP = 0; public static final int BUCKET_MEDIA_CONTROLS = 1; public static final int BUCKET_PEOPLE = 2; public static final int BUCKET_ALERTING = 3; public static final int BUCKET_SILENT = 4; public static final int BUCKET_FOREGROUND_SERVICE = 1; public static final int BUCKET_MEDIA_CONTROLS = 2; public static final int BUCKET_PEOPLE = 3; public static final int BUCKET_ALERTING = 4; public static final int BUCKET_SILENT = 5; }
packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java +211 −11 Original line number Diff line number Diff line Loading @@ -19,15 +19,19 @@ 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_FOREGROUND_SERVICE; 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.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.RETURNS_DEEP_STUBS; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; Loading Loading @@ -62,6 +66,9 @@ import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; import java.util.ArrayList; import java.util.List; @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) Loading @@ -84,10 +91,23 @@ public class NotificationSectionsManagerTest extends SysuiTestCase { @Before public void setUp() { when(mSectionsFeatureManager.getNumberOfBuckets()).thenReturn(2); when(mNotificationRowComponent.getActivatableNotificationViewController()).thenReturn( mActivatableNotificationViewController ); when(mSectionsFeatureManager.getNumberOfBuckets()).thenAnswer( invocation -> { int count = 2; if (mSectionsFeatureManager.isFilteringEnabled()) { count = 5; } if (mSectionsFeatureManager.isMediaControlsEnabled()) { if (!mSectionsFeatureManager.isFilteringEnabled()) { count = 5; } else { count += 1; } } return count; }); when(mNotificationRowComponent.getActivatableNotificationViewController()) .thenReturn(mActivatableNotificationViewController); mSectionsManager = new NotificationSectionsManager( mActivityStarterDelegate, Loading @@ -104,6 +124,7 @@ public class NotificationSectionsManagerTest extends SysuiTestCase { mSectionsManager.initialize(mNssl, LayoutInflater.from(mContext)); when(mNssl.indexOfChild(any(View.class))).thenReturn(-1); when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE); } @Test(expected = IllegalStateException.class) Loading Loading @@ -338,6 +359,58 @@ public class NotificationSectionsManagerTest extends SysuiTestCase { verify(mNssl).changeViewPosition(mSectionsManager.getPeopleHeaderView(), 0); } @Test public void testPeopleFiltering_HunWhilePeopleVisible() { enablePeopleFiltering(); setupMockStack( ChildType.PEOPLE_HEADER, ChildType.HEADS_UP, ChildType.PERSON, ChildType.ALERTING_HEADER, ChildType.GENTLE_HEADER, ChildType.GENTLE ); mSectionsManager.updateSectionBoundaries(); verifyMockStack( ChildType.INCOMING_HEADER, ChildType.HEADS_UP, ChildType.PEOPLE_HEADER, ChildType.PERSON, ChildType.GENTLE_HEADER, ChildType.GENTLE ); } @Test public void testPeopleFiltering_Fsn() { enablePeopleFiltering(); setupMockStack( ChildType.INCOMING_HEADER, ChildType.HEADS_UP, ChildType.PEOPLE_HEADER, ChildType.FSN, ChildType.PERSON, ChildType.ALERTING, ChildType.GENTLE ); mSectionsManager.updateSectionBoundaries(); verifyMockStack( ChildType.INCOMING_HEADER, ChildType.HEADS_UP, ChildType.FSN, ChildType.PEOPLE_HEADER, ChildType.PERSON, ChildType.ALERTING_HEADER, ChildType.ALERTING, ChildType.GENTLE_HEADER, ChildType.GENTLE ); } @Test public void testMediaControls_AddWhenEnterKeyguard() { enableMediaControls(); Loading @@ -358,30 +431,28 @@ public class NotificationSectionsManagerTest extends SysuiTestCase { enableMediaControls(); // GIVEN a stack that doesn't include media controls but includes HEADS_UP setStackState(ChildType.HEADS_UP, ChildType.ALERTING, ChildType.GENTLE_HEADER, setupMockStack(ChildType.HEADS_UP, ChildType.ALERTING, ChildType.GENTLE_HEADER, ChildType.GENTLE); // WHEN we go back to the keyguard when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD); mSectionsManager.updateSectionBoundaries(); // Then the media controls are added after HEADS_UP verify(mNssl).addView(mSectionsManager.getMediaControlsView(), 1); verifyMockStack(ChildType.HEADS_UP, ChildType.MEDIA_CONTROLS, ChildType.ALERTING, ChildType.GENTLE); } private void enablePeopleFiltering() { when(mSectionsFeatureManager.isFilteringEnabled()).thenReturn(true); when(mSectionsFeatureManager.getNumberOfBuckets()).thenReturn(4); } private void enableMediaControls() { when(mSectionsFeatureManager.isMediaControlsEnabled()).thenReturn(true); when(mSectionsFeatureManager.getNumberOfBuckets()).thenReturn(4); } private enum ChildType { MEDIA_CONTROLS, PEOPLE_HEADER, ALERTING_HEADER, GENTLE_HEADER, HEADS_UP, PERSON, ALERTING, GENTLE, OTHER INCOMING_HEADER, MEDIA_CONTROLS, PEOPLE_HEADER, ALERTING_HEADER, GENTLE_HEADER, HEADS_UP, FSN, PERSON, ALERTING, GENTLE, OTHER } private void setStackState(ChildType... children) { Loading @@ -389,6 +460,9 @@ public class NotificationSectionsManagerTest extends SysuiTestCase { for (int i = 0; i < children.length; i++) { View child; switch (children[i]) { case INCOMING_HEADER: child = mSectionsManager.getIncomingHeaderView(); break; case MEDIA_CONTROLS: child = mSectionsManager.getMediaControlsView(); break; Loading @@ -404,6 +478,9 @@ public class NotificationSectionsManagerTest extends SysuiTestCase { case HEADS_UP: child = mockNotification(BUCKET_HEADS_UP); break; case FSN: child = mockNotification(BUCKET_FOREGROUND_SERVICE); break; case PERSON: child = mockNotification(BUCKET_PEOPLE); break; Loading Loading @@ -434,4 +511,127 @@ public class NotificationSectionsManagerTest extends SysuiTestCase { when(notifRow.getParent()).thenReturn(mNssl); return notifRow; } private void verifyMockStack(ChildType... expected) { final List<ChildType> actual = new ArrayList<>(); int childCount = mNssl.getChildCount(); for (int i = 0; i < childCount; i++) { View child = mNssl.getChildAt(i); if (child == mSectionsManager.getIncomingHeaderView()) { actual.add(ChildType.INCOMING_HEADER); continue; } if (child == mSectionsManager.getMediaControlsView()) { actual.add(ChildType.MEDIA_CONTROLS); continue; } if (child == mSectionsManager.getPeopleHeaderView()) { actual.add(ChildType.PEOPLE_HEADER); continue; } if (child == mSectionsManager.getAlertingHeaderView()) { actual.add(ChildType.ALERTING_HEADER); continue; } if (child == mSectionsManager.getGentleHeaderView()) { actual.add(ChildType.GENTLE_HEADER); continue; } if (child instanceof ExpandableNotificationRow) { switch (((ExpandableNotificationRow) child).getEntry().getBucket()) { case BUCKET_HEADS_UP: actual.add(ChildType.HEADS_UP); break; case BUCKET_FOREGROUND_SERVICE: actual.add(ChildType.FSN); break; case BUCKET_PEOPLE: actual.add(ChildType.PERSON); break; case BUCKET_ALERTING: actual.add(ChildType.ALERTING); break; case BUCKET_SILENT: actual.add(ChildType.GENTLE); break; default: actual.add(ChildType.OTHER); break; } continue; } actual.add(ChildType.OTHER); } assertThat(actual).containsExactly((Object[]) expected).inOrder(); } private void setupMockStack(ChildType... childTypes) { final List<View> children = new ArrayList<>(); when(mNssl.getChildCount()).thenAnswer(invocation -> children.size()); when(mNssl.getChildAt(anyInt())) .thenAnswer(invocation -> children.get(invocation.getArgument(0))); when(mNssl.indexOfChild(any())) .thenAnswer(invocation -> children.indexOf(invocation.getArgument(0))); doAnswer(invocation -> { View child = invocation.getArgument(0); int index = invocation.getArgument(1); children.add(index, child); return null; }).when(mNssl).addView(any(), anyInt()); doAnswer(invocation -> { View child = invocation.getArgument(0); children.remove(child); return null; }).when(mNssl).removeView(any()); doAnswer(invocation -> { View child = invocation.getArgument(0); int newIndex = invocation.getArgument(1); children.remove(child); children.add(newIndex, child); return null; }).when(mNssl).changeViewPosition(any(), anyInt()); for (ChildType childType : childTypes) { View child; switch (childType) { case INCOMING_HEADER: child = mSectionsManager.getIncomingHeaderView(); break; case MEDIA_CONTROLS: child = mSectionsManager.getMediaControlsView(); break; case PEOPLE_HEADER: child = mSectionsManager.getPeopleHeaderView(); break; case ALERTING_HEADER: child = mSectionsManager.getAlertingHeaderView(); break; case GENTLE_HEADER: child = mSectionsManager.getGentleHeaderView(); break; case HEADS_UP: child = mockNotification(BUCKET_HEADS_UP); break; case FSN: child = mockNotification(BUCKET_FOREGROUND_SERVICE); 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: " + childType); } children.add(child); } } }