Merge "Uprank colorized FSNs over Conversations" into rvc-dev

This commit is contained in:
Steve Elliott
2020-05-13 17:27:32 +00:00
committed by Android (Google) Code Review
4 changed files with 249 additions and 50 deletions

View File

@@ -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
@@ -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 ->

View File

@@ -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
@@ -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() ->

View File

@@ -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;
@@ -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;
@@ -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;
@@ -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:");
@@ -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);
}
header.setContentVisible(true);
mParent.addView(header, targetPosition);
} else {
mParent.changeViewPosition(header, targetPosition);
}
adjustViewPosition(targetPosition, header, currentPosition);
if (targetPosition != -1 && currentPosition == -1) {
header.setContentVisible(true);
}
}
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);
}
}
}
@@ -640,6 +626,11 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section
return mMediaControlsView;
}
@VisibleForTesting
ExpandableView getIncomingHeaderView() {
return mIncomingHeader;
}
@VisibleForTesting
void setPeopleHubVisible(boolean visible) {
mPeopleHubVisible = visible;
@@ -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,
@@ -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;
}

View File

@@ -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;
@@ -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)
@@ -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,
@@ -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)
@@ -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();
@@ -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) {
@@ -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;
@@ -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;
@@ -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);
}
}
}