Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java +7 −0 Original line number Diff line number Diff line Loading @@ -99,6 +99,13 @@ public class ListDumper { .append(")"); } if (entry.mNotifSection != null) { sb.append(" sectionIndex=") .append(entry.getSection()) .append(" sectionName=") .append(entry.mNotifSection.getName()); } if (includeRecordKeeping) { NotificationEntry notifEntry = entry.getRepresentativeEntry(); StringBuilder rksb = new StringBuilder(); Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java +5 −1 Original line number Diff line number Diff line Loading @@ -18,6 +18,8 @@ package com.android.systemui.statusbar.notification.collection; import android.annotation.Nullable; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection; /** * Abstract superclass for top-level entries, i.e. things that can appear in the final notification * list shown to users. In practice, this means either GroupEntries or NotificationEntries. Loading @@ -27,7 +29,9 @@ public abstract class ListEntry { @Nullable private GroupEntry mParent; @Nullable private GroupEntry mPreviousParent; private int mSection; @Nullable NotifSection mNotifSection; private int mSection = -1; int mFirstAddedIteration = -1; ListEntry(String key) { Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java +9 −10 Original line number Diff line number Diff line Loading @@ -22,7 +22,7 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.OnBefo import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.SectionsProvider; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender; Loading @@ -48,7 +48,7 @@ import javax.inject.Singleton; * GroupEntry. These groups are then transformed in order to remove children or completely split * them apart. To participate, see {@link #addPromoter}. * - Sorted: All top-level notifications are sorted. To participate, see * {@link #setSectionsProvider} and {@link #setComparators} * {@link #setSections} and {@link #setComparators} * * The exact order of all hooks is as follows: * 0. Collection listeners are fired ({@link #addCollectionListener}). Loading @@ -58,7 +58,7 @@ import javax.inject.Singleton; * 3. OnBeforeTransformGroupListeners are fired ({@link #addOnBeforeTransformGroupsListener}) * 4. NotifPromoters are called on each notification with a parent ({@link #addPromoter}) * 5. OnBeforeSortListeners are fired ({@link #addOnBeforeSortListener}) * 6. SectionsProvider is called on each top-level entry in the list ({@link #setSectionsProvider}) * 6. Top-level entries are assigned sections by NotifSections ({@link #setSections}) * 7. Top-level entries within the same section are sorted by NotifComparators * ({@link #setComparators}) * 8. Pre-render filters are fired on each notification ({@link #addPreRenderFilter}) Loading Loading @@ -142,14 +142,13 @@ public class NotifPipeline { } /** * Assigns sections to each top-level entry, where a section is simply an integer. Sections are * the primary metric by which top-level entries are sorted; NotifComparators are only consulted * when two entries are in the same section. The pipeline doesn't assign any particular meaning * to section IDs -- from it's perspective they're just numbers and it sorts them by a simple * numerical comparison. * Sections that are used to sort top-level entries. If two entries have the same section, * NotifComparators are consulted. Sections from this list are called in order for each * notification passed through the pipeline. The first NotifSection to return true for * {@link NotifSection#isInSection(ListEntry)} sets the entry as part of its Section. */ public void setSectionsProvider(SectionsProvider provider) { mShadeListBuilder.setSectionsProvider(provider); public void setSections(List<NotifSection> sections) { mShadeListBuilder.setSections(sections); } /** Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java +65 −22 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ import static com.android.systemui.statusbar.notification.collection.listbuilder import android.annotation.MainThread; import android.annotation.Nullable; import android.util.ArrayMap; import android.util.Pair; import com.android.systemui.DumpController; import com.android.systemui.Dumpable; Loading @@ -40,7 +41,7 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.Pipeli import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.SectionsProvider; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection; import com.android.systemui.statusbar.notification.collection.notifcollection.CollectionReadyForBuildListener; import com.android.systemui.statusbar.notification.logging.NotifEvent; import com.android.systemui.statusbar.notification.logging.NotifLog; Loading Loading @@ -82,7 +83,7 @@ public class ShadeListBuilder implements Dumpable { private final List<NotifPromoter> mNotifPromoters = new ArrayList<>(); private final List<NotifFilter> mNotifPreRenderFilters = new ArrayList<>(); private final List<NotifComparator> mNotifComparators = new ArrayList<>(); private SectionsProvider mSectionsProvider = new DefaultSectionsProvider(); private final List<NotifSection> mNotifSections = new ArrayList<>(); private final List<OnBeforeTransformGroupsListener> mOnBeforeTransformGroupsListeners = new ArrayList<>(); Loading Loading @@ -170,12 +171,15 @@ public class ShadeListBuilder implements Dumpable { promoter.setInvalidationListener(this::onPromoterInvalidated); } void setSectionsProvider(SectionsProvider provider) { void setSections(List<NotifSection> sections) { Assert.isMainThread(); mPipelineState.requireState(STATE_IDLE); mSectionsProvider = provider; provider.setInvalidationListener(this::onSectionsProviderInvalidated); mNotifSections.clear(); for (NotifSection section : sections) { mNotifSections.add(section); section.setInvalidationListener(this::onNotifSectionInvalidated); } } void setComparators(List<NotifComparator> comparators) { Loading Loading @@ -230,12 +234,12 @@ public class ShadeListBuilder implements Dumpable { rebuildListIfBefore(STATE_TRANSFORMING); } private void onSectionsProviderInvalidated(SectionsProvider provider) { private void onNotifSectionInvalidated(NotifSection section) { Assert.isMainThread(); mNotifLog.log(NotifEvent.SECTIONS_PROVIDER_INVALIDATED, String.format( "Sections provider \"%s\" invalidated; pipeline state is %d", provider.getName(), mNotifLog.log(NotifEvent.SECTION_INVALIDATED, String.format( "Section \"%s\" invalidated; pipeline state is %d", section.getName(), mPipelineState.getState())); rebuildListIfBefore(STATE_SORTING); Loading Loading @@ -318,7 +322,7 @@ public class ShadeListBuilder implements Dumpable { sortList(); // Step 6: Filter out entries after pre-group filtering, grouping, promoting and sorting // Now filters can see grouping information to determine whether to filter or not // Now filters can see grouping information to determine whether to filter or not. mPipelineState.incrementTo(STATE_PRE_RENDER_FILTERING); filterNotifs(mNotifList, mNewNotifList, mNotifPreRenderFilters); applyNewNotifList(); Loading Loading @@ -580,6 +584,8 @@ public class ShadeListBuilder implements Dumpable { * filtered out during any of the filtering steps. */ private void annulAddition(ListEntry entry) { entry.setSection(-1); entry.mNotifSection = null; entry.setParent(null); if (entry.mFirstAddedIteration == mIterationCount) { entry.mFirstAddedIteration = -1; Loading @@ -589,11 +595,12 @@ public class ShadeListBuilder implements Dumpable { private void sortList() { // Assign sections to top-level elements and sort their children for (ListEntry entry : mNotifList) { entry.setSection(mSectionsProvider.getSection(entry)); Pair<NotifSection, Integer> sectionWithIndex = applySections(entry); if (entry instanceof GroupEntry) { GroupEntry parent = (GroupEntry) entry; for (NotificationEntry child : parent.getChildren()) { child.setSection(0); child.mNotifSection = sectionWithIndex.first; child.setSection(sectionWithIndex.second); } parent.sortChildren(sChildComparator); } Loading Loading @@ -754,6 +761,45 @@ public class ShadeListBuilder implements Dumpable { return null; } private Pair<NotifSection, Integer> applySections(ListEntry entry) { final Pair<NotifSection, Integer> sectionWithIndex = findSection(entry); final NotifSection section = sectionWithIndex.first; final Integer sectionIndex = sectionWithIndex.second; if (section != entry.mNotifSection) { if (entry.mNotifSection == null) { mNotifLog.log(NotifEvent.SECTION_CHANGED, String.format( "%s: sectioned by '%s' [index=%d].", entry.getKey(), section.getName(), sectionIndex)); } else { mNotifLog.log(NotifEvent.SECTION_CHANGED, String.format( "%s: section changed: '%s' [index=%d] -> '%s [index=%d]'.", entry.getKey(), entry.mNotifSection, entry.getSection(), section, sectionIndex)); } entry.mNotifSection = section; entry.setSection(sectionIndex); } return sectionWithIndex; } private Pair<NotifSection, Integer> findSection(ListEntry entry) { for (int i = 0; i < mNotifSections.size(); i++) { NotifSection sectioner = mNotifSections.get(i); if (sectioner.isInSection(entry)) { return new Pair<>(sectioner, i); } } return new Pair<>(sDefaultSection, mNotifSections.size()); } private void rebuildListIfBefore(@PipelineState.StateName int state) { mPipelineState.requireIsBefore(state); if (mPipelineState.is(STATE_IDLE)) { Loading Loading @@ -803,16 +849,13 @@ public class ShadeListBuilder implements Dumpable { void onRenderList(List<ListEntry> entries); } private static class DefaultSectionsProvider extends SectionsProvider { DefaultSectionsProvider() { super("DefaultSectionsProvider"); } private static final NotifSection sDefaultSection = new NotifSection("DefaultSection") { @Override public int getSection(ListEntry entry) { return 0; } public boolean isInSection(ListEntry entry) { return true; } }; private static final String TAG = "NotifListBuilderImpl"; Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/Coordinator.java +5 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.notification.collection.coordinator; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable; /** Loading @@ -28,4 +29,8 @@ public interface Coordinator { * Coordinators should register their listeners and {@link Pluggable}s to the pipeline. */ void attach(NotifPipeline pipeline); default NotifSection getSection() { return null; } } Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java +7 −0 Original line number Diff line number Diff line Loading @@ -99,6 +99,13 @@ public class ListDumper { .append(")"); } if (entry.mNotifSection != null) { sb.append(" sectionIndex=") .append(entry.getSection()) .append(" sectionName=") .append(entry.mNotifSection.getName()); } if (includeRecordKeeping) { NotificationEntry notifEntry = entry.getRepresentativeEntry(); StringBuilder rksb = new StringBuilder(); Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java +5 −1 Original line number Diff line number Diff line Loading @@ -18,6 +18,8 @@ package com.android.systemui.statusbar.notification.collection; import android.annotation.Nullable; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection; /** * Abstract superclass for top-level entries, i.e. things that can appear in the final notification * list shown to users. In practice, this means either GroupEntries or NotificationEntries. Loading @@ -27,7 +29,9 @@ public abstract class ListEntry { @Nullable private GroupEntry mParent; @Nullable private GroupEntry mPreviousParent; private int mSection; @Nullable NotifSection mNotifSection; private int mSection = -1; int mFirstAddedIteration = -1; ListEntry(String key) { Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java +9 −10 Original line number Diff line number Diff line Loading @@ -22,7 +22,7 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.OnBefo import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.SectionsProvider; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender; Loading @@ -48,7 +48,7 @@ import javax.inject.Singleton; * GroupEntry. These groups are then transformed in order to remove children or completely split * them apart. To participate, see {@link #addPromoter}. * - Sorted: All top-level notifications are sorted. To participate, see * {@link #setSectionsProvider} and {@link #setComparators} * {@link #setSections} and {@link #setComparators} * * The exact order of all hooks is as follows: * 0. Collection listeners are fired ({@link #addCollectionListener}). Loading @@ -58,7 +58,7 @@ import javax.inject.Singleton; * 3. OnBeforeTransformGroupListeners are fired ({@link #addOnBeforeTransformGroupsListener}) * 4. NotifPromoters are called on each notification with a parent ({@link #addPromoter}) * 5. OnBeforeSortListeners are fired ({@link #addOnBeforeSortListener}) * 6. SectionsProvider is called on each top-level entry in the list ({@link #setSectionsProvider}) * 6. Top-level entries are assigned sections by NotifSections ({@link #setSections}) * 7. Top-level entries within the same section are sorted by NotifComparators * ({@link #setComparators}) * 8. Pre-render filters are fired on each notification ({@link #addPreRenderFilter}) Loading Loading @@ -142,14 +142,13 @@ public class NotifPipeline { } /** * Assigns sections to each top-level entry, where a section is simply an integer. Sections are * the primary metric by which top-level entries are sorted; NotifComparators are only consulted * when two entries are in the same section. The pipeline doesn't assign any particular meaning * to section IDs -- from it's perspective they're just numbers and it sorts them by a simple * numerical comparison. * Sections that are used to sort top-level entries. If two entries have the same section, * NotifComparators are consulted. Sections from this list are called in order for each * notification passed through the pipeline. The first NotifSection to return true for * {@link NotifSection#isInSection(ListEntry)} sets the entry as part of its Section. */ public void setSectionsProvider(SectionsProvider provider) { mShadeListBuilder.setSectionsProvider(provider); public void setSections(List<NotifSection> sections) { mShadeListBuilder.setSections(sections); } /** Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java +65 −22 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ import static com.android.systemui.statusbar.notification.collection.listbuilder import android.annotation.MainThread; import android.annotation.Nullable; import android.util.ArrayMap; import android.util.Pair; import com.android.systemui.DumpController; import com.android.systemui.Dumpable; Loading @@ -40,7 +41,7 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.Pipeli import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.SectionsProvider; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection; import com.android.systemui.statusbar.notification.collection.notifcollection.CollectionReadyForBuildListener; import com.android.systemui.statusbar.notification.logging.NotifEvent; import com.android.systemui.statusbar.notification.logging.NotifLog; Loading Loading @@ -82,7 +83,7 @@ public class ShadeListBuilder implements Dumpable { private final List<NotifPromoter> mNotifPromoters = new ArrayList<>(); private final List<NotifFilter> mNotifPreRenderFilters = new ArrayList<>(); private final List<NotifComparator> mNotifComparators = new ArrayList<>(); private SectionsProvider mSectionsProvider = new DefaultSectionsProvider(); private final List<NotifSection> mNotifSections = new ArrayList<>(); private final List<OnBeforeTransformGroupsListener> mOnBeforeTransformGroupsListeners = new ArrayList<>(); Loading Loading @@ -170,12 +171,15 @@ public class ShadeListBuilder implements Dumpable { promoter.setInvalidationListener(this::onPromoterInvalidated); } void setSectionsProvider(SectionsProvider provider) { void setSections(List<NotifSection> sections) { Assert.isMainThread(); mPipelineState.requireState(STATE_IDLE); mSectionsProvider = provider; provider.setInvalidationListener(this::onSectionsProviderInvalidated); mNotifSections.clear(); for (NotifSection section : sections) { mNotifSections.add(section); section.setInvalidationListener(this::onNotifSectionInvalidated); } } void setComparators(List<NotifComparator> comparators) { Loading Loading @@ -230,12 +234,12 @@ public class ShadeListBuilder implements Dumpable { rebuildListIfBefore(STATE_TRANSFORMING); } private void onSectionsProviderInvalidated(SectionsProvider provider) { private void onNotifSectionInvalidated(NotifSection section) { Assert.isMainThread(); mNotifLog.log(NotifEvent.SECTIONS_PROVIDER_INVALIDATED, String.format( "Sections provider \"%s\" invalidated; pipeline state is %d", provider.getName(), mNotifLog.log(NotifEvent.SECTION_INVALIDATED, String.format( "Section \"%s\" invalidated; pipeline state is %d", section.getName(), mPipelineState.getState())); rebuildListIfBefore(STATE_SORTING); Loading Loading @@ -318,7 +322,7 @@ public class ShadeListBuilder implements Dumpable { sortList(); // Step 6: Filter out entries after pre-group filtering, grouping, promoting and sorting // Now filters can see grouping information to determine whether to filter or not // Now filters can see grouping information to determine whether to filter or not. mPipelineState.incrementTo(STATE_PRE_RENDER_FILTERING); filterNotifs(mNotifList, mNewNotifList, mNotifPreRenderFilters); applyNewNotifList(); Loading Loading @@ -580,6 +584,8 @@ public class ShadeListBuilder implements Dumpable { * filtered out during any of the filtering steps. */ private void annulAddition(ListEntry entry) { entry.setSection(-1); entry.mNotifSection = null; entry.setParent(null); if (entry.mFirstAddedIteration == mIterationCount) { entry.mFirstAddedIteration = -1; Loading @@ -589,11 +595,12 @@ public class ShadeListBuilder implements Dumpable { private void sortList() { // Assign sections to top-level elements and sort their children for (ListEntry entry : mNotifList) { entry.setSection(mSectionsProvider.getSection(entry)); Pair<NotifSection, Integer> sectionWithIndex = applySections(entry); if (entry instanceof GroupEntry) { GroupEntry parent = (GroupEntry) entry; for (NotificationEntry child : parent.getChildren()) { child.setSection(0); child.mNotifSection = sectionWithIndex.first; child.setSection(sectionWithIndex.second); } parent.sortChildren(sChildComparator); } Loading Loading @@ -754,6 +761,45 @@ public class ShadeListBuilder implements Dumpable { return null; } private Pair<NotifSection, Integer> applySections(ListEntry entry) { final Pair<NotifSection, Integer> sectionWithIndex = findSection(entry); final NotifSection section = sectionWithIndex.first; final Integer sectionIndex = sectionWithIndex.second; if (section != entry.mNotifSection) { if (entry.mNotifSection == null) { mNotifLog.log(NotifEvent.SECTION_CHANGED, String.format( "%s: sectioned by '%s' [index=%d].", entry.getKey(), section.getName(), sectionIndex)); } else { mNotifLog.log(NotifEvent.SECTION_CHANGED, String.format( "%s: section changed: '%s' [index=%d] -> '%s [index=%d]'.", entry.getKey(), entry.mNotifSection, entry.getSection(), section, sectionIndex)); } entry.mNotifSection = section; entry.setSection(sectionIndex); } return sectionWithIndex; } private Pair<NotifSection, Integer> findSection(ListEntry entry) { for (int i = 0; i < mNotifSections.size(); i++) { NotifSection sectioner = mNotifSections.get(i); if (sectioner.isInSection(entry)) { return new Pair<>(sectioner, i); } } return new Pair<>(sDefaultSection, mNotifSections.size()); } private void rebuildListIfBefore(@PipelineState.StateName int state) { mPipelineState.requireIsBefore(state); if (mPipelineState.is(STATE_IDLE)) { Loading Loading @@ -803,16 +849,13 @@ public class ShadeListBuilder implements Dumpable { void onRenderList(List<ListEntry> entries); } private static class DefaultSectionsProvider extends SectionsProvider { DefaultSectionsProvider() { super("DefaultSectionsProvider"); } private static final NotifSection sDefaultSection = new NotifSection("DefaultSection") { @Override public int getSection(ListEntry entry) { return 0; } public boolean isInSection(ListEntry entry) { return true; } }; private static final String TAG = "NotifListBuilderImpl"; Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/Coordinator.java +5 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.notification.collection.coordinator; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable; /** Loading @@ -28,4 +29,8 @@ public interface Coordinator { * Coordinators should register their listeners and {@link Pluggable}s to the pipeline. */ void attach(NotifPipeline pipeline); default NotifSection getSection() { return null; } }