Summaries receive a stability section index

This fixes an issue where the summary with no children wouldn't have its sort position stabilized, resulting in unintended move animations while the shade is open when child notifications were cancelled.

Bug: 241229236
Test: atest ShadeListBuilderTest
Change-Id: I742d68ce05e080f3b7a928247e46d8406f21eebb
This commit is contained in:
Jeff DeCew
2022-08-01 19:12:23 +00:00
parent dfb618d8f0
commit b0c2be9dca
4 changed files with 113 additions and 11 deletions

View File

@@ -73,7 +73,9 @@ public class Flags {
public static final UnreleasedFlag NOTIFICATION_DISMISSAL_FADE = new UnreleasedFlag(113, true); public static final UnreleasedFlag NOTIFICATION_DISMISSAL_FADE = new UnreleasedFlag(113, true);
// next id: 114 public static final UnreleasedFlag STABILITY_INDEX_FIX = new UnreleasedFlag(114, true);
// next id: 115
/***************************************/ /***************************************/
// 200 - keyguard/lockscreen // 200 - keyguard/lockscreen

View File

@@ -53,4 +53,8 @@ class NotifPipelineFlags @Inject constructor(
fun fullScreenIntentRequiresKeyguard(): Boolean = fun fullScreenIntentRequiresKeyguard(): Boolean =
featureFlags.isEnabled(Flags.FSI_REQUIRES_KEYGUARD) featureFlags.isEnabled(Flags.FSI_REQUIRES_KEYGUARD)
val isStabilityIndexFixEnabled: Boolean by lazy {
featureFlags.isEnabled(Flags.STABILITY_INDEX_FIX)
}
} }

View File

@@ -96,6 +96,7 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
// used exclusivly by ShadeListBuilder#notifySectionEntriesUpdated // used exclusivly by ShadeListBuilder#notifySectionEntriesUpdated
// TODO replace temp with collection pool for readability // TODO replace temp with collection pool for readability
private final ArrayList<ListEntry> mTempSectionMembers = new ArrayList<>(); private final ArrayList<ListEntry> mTempSectionMembers = new ArrayList<>();
private final NotifPipelineFlags mFlags;
private final boolean mAlwaysLogList; private final boolean mAlwaysLogList;
private List<ListEntry> mNotifList = new ArrayList<>(); private List<ListEntry> mNotifList = new ArrayList<>();
@@ -141,6 +142,7 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
) { ) {
mSystemClock = systemClock; mSystemClock = systemClock;
mLogger = logger; mLogger = logger;
mFlags = flags;
mAlwaysLogList = flags.isDevLoggingEnabled(); mAlwaysLogList = flags.isDevLoggingEnabled();
mInteractionTracker = interactionTracker; mInteractionTracker = interactionTracker;
mChoreographer = pipelineChoreographer; mChoreographer = pipelineChoreographer;
@@ -1036,29 +1038,43 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
/** /**
* Assign the index of each notification relative to the total order * Assign the index of each notification relative to the total order
*/ */
private static void assignIndexes(List<ListEntry> notifList) { private void assignIndexes(List<ListEntry> notifList) {
if (notifList.size() == 0) return; if (notifList.size() == 0) return;
NotifSection currentSection = requireNonNull(notifList.get(0).getSection()); NotifSection currentSection = requireNonNull(notifList.get(0).getSection());
int sectionMemberIndex = 0; int sectionMemberIndex = 0;
for (int i = 0; i < notifList.size(); i++) { for (int i = 0; i < notifList.size(); i++) {
ListEntry entry = notifList.get(i); final ListEntry entry = notifList.get(i);
NotifSection section = requireNonNull(entry.getSection()); NotifSection section = requireNonNull(entry.getSection());
if (section.getIndex() != currentSection.getIndex()) { if (section.getIndex() != currentSection.getIndex()) {
sectionMemberIndex = 0; sectionMemberIndex = 0;
currentSection = section; currentSection = section;
} }
if (mFlags.isStabilityIndexFixEnabled()) {
entry.getAttachState().setStableIndex(sectionMemberIndex++);
if (entry instanceof GroupEntry) {
final GroupEntry parent = (GroupEntry) entry;
final NotificationEntry summary = parent.getSummary();
if (summary != null) {
summary.getAttachState().setStableIndex(sectionMemberIndex++);
}
for (NotificationEntry child : parent.getChildren()) {
child.getAttachState().setStableIndex(sectionMemberIndex++);
}
}
} else {
// This old implementation uses the same index number for the group as the first
// child, and fails to assign an index to the summary. Remove once tested.
entry.getAttachState().setStableIndex(sectionMemberIndex); entry.getAttachState().setStableIndex(sectionMemberIndex);
if (entry instanceof GroupEntry) { if (entry instanceof GroupEntry) {
GroupEntry parent = (GroupEntry) entry; final GroupEntry parent = (GroupEntry) entry;
for (int j = 0; j < parent.getChildren().size(); j++) { for (NotificationEntry child : parent.getChildren()) {
entry = parent.getChildren().get(j); child.getAttachState().setStableIndex(sectionMemberIndex++);
entry.getAttachState().setStableIndex(sectionMemberIndex);
sectionMemberIndex++;
} }
} }
sectionMemberIndex++; sectionMemberIndex++;
} }
} }
}
private void freeEmptyGroups() { private void freeEmptyGroups() {
Trace.beginSection("ShadeListBuilder.freeEmptyGroups"); Trace.beginSection("ShadeListBuilder.freeEmptyGroups");

View File

@@ -38,6 +38,7 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy; import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
@@ -2112,6 +2113,85 @@ public class ShadeListBuilderTest extends SysuiTestCase {
); );
} }
@Test
public void groupRevertingToSummaryDoesNotRetainStablePositionWithLegacyIndexLogic() {
when(mNotifPipelineFlags.isStabilityIndexFixEnabled()).thenReturn(false);
// GIVEN a notification group is on screen
mStabilityManager.setAllowEntryReordering(false);
// WHEN the list is originally built with reordering disabled (and section changes allowed)
addNotif(0, PACKAGE_1).setRank(2);
addNotif(1, PACKAGE_1).setRank(3);
addGroupSummary(2, PACKAGE_1, "group").setRank(4);
addGroupChild(3, PACKAGE_1, "group").setRank(5);
addGroupChild(4, PACKAGE_1, "group").setRank(6);
dispatchBuild();
verifyBuiltList(
notif(0),
notif(1),
group(
summary(2),
child(3),
child(4)
)
);
// WHEN the notification summary rank increases and children removed
setNewRank(notif(2).entry, 1);
mEntrySet.remove(4);
mEntrySet.remove(3);
dispatchBuild();
// VERIFY the summary (incorrectly) moves to the top of the section where it is ranked,
// despite visual stability being active
verifyBuiltList(
notif(2),
notif(0),
notif(1)
);
}
@Test
public void groupRevertingToSummaryRetainsStablePosition() {
when(mNotifPipelineFlags.isStabilityIndexFixEnabled()).thenReturn(true);
// GIVEN a notification group is on screen
mStabilityManager.setAllowEntryReordering(false);
// WHEN the list is originally built with reordering disabled (and section changes allowed)
addNotif(0, PACKAGE_1).setRank(2);
addNotif(1, PACKAGE_1).setRank(3);
addGroupSummary(2, PACKAGE_1, "group").setRank(4);
addGroupChild(3, PACKAGE_1, "group").setRank(5);
addGroupChild(4, PACKAGE_1, "group").setRank(6);
dispatchBuild();
verifyBuiltList(
notif(0),
notif(1),
group(
summary(2),
child(3),
child(4)
)
);
// WHEN the notification summary rank increases and children removed
setNewRank(notif(2).entry, 1);
mEntrySet.remove(4);
mEntrySet.remove(3);
dispatchBuild();
// VERIFY the summary stays in the same location on rebuild
verifyBuiltList(
notif(0),
notif(1),
notif(2)
);
}
private static void setNewRank(NotificationEntry entry, int rank) { private static void setNewRank(NotificationEntry entry, int rank) {
entry.setRanking(new RankingBuilder(entry.getRanking()).setRank(rank).build()); entry.setRanking(new RankingBuilder(entry.getRanking()).setRank(rank).build());
} }