Add listener hook before finalize filter.

Add a hook so coordinators can listen before the FinalizeFilter
stage (previously named preRenderFilter and renamed in this CL b/c
OnBeforePreRenderFilter sounds somewhat awkward).

This'll be helpful for moving inflation-related filtering after
sort/group stage.

Bug: 112656837
Test: atest ShadeListBuilderTest
Change-Id: If7c7b7375b664ddcd0bb75b0937070a03db9b0e3
This commit is contained in:
Kevin Han
2020-01-22 12:56:25 -08:00
parent 6d03d973c0
commit 2265301d53
12 changed files with 96 additions and 31 deletions

View File

@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.collection;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeFinalizeFilterListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeSortListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeTransformGroupsListener;
@@ -63,7 +64,7 @@ import javax.inject.Singleton;
* 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})
* 8. Finalize filters are fired on each notification ({@link #addFinalizeFilter})
* 9. OnBeforeRenderListListeners are fired ({@link #addOnBeforeRenderListListener})
* 9. The list is handed off to the view layer to be rendered
*/
@@ -168,6 +169,14 @@ public class NotifPipeline implements CommonNotifCollection {
mShadeListBuilder.setComparators(comparators);
}
/**
* Called after notifs have been filtered once, grouped, and sorted but before the final
* filtering.
*/
public void addOnBeforeFinalizeFilterListener(OnBeforeFinalizeFilterListener listener) {
mShadeListBuilder.addOnBeforeFinalizeFilterListener(listener);
}
/**
* Registers a filter with the pipeline to filter right before rendering the list (after
* pre-group filtering, grouping, promoting and sorting occurs). Filters are
@@ -175,8 +184,8 @@ public class NotifPipeline implements CommonNotifCollection {
* true, the notification is removed from the pipeline (and no other filters are called on that
* notif).
*/
public void addPreRenderFilter(NotifFilter filter) {
mShadeListBuilder.addPreRenderFilter(filter);
public void addFinalizeFilter(NotifFilter filter) {
mShadeListBuilder.addFinalizeFilter(filter);
}
/**

View File

@@ -18,11 +18,11 @@ package com.android.systemui.statusbar.notification.collection;
import static com.android.systemui.statusbar.notification.collection.GroupEntry.ROOT_ENTRY;
import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_BUILD_STARTED;
import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_FINALIZE_FILTERING;
import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_FINALIZING;
import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_GROUPING;
import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_IDLE;
import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_PRE_GROUP_FILTERING;
import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_PRE_RENDER_FILTERING;
import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_RESETTING;
import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_SORTING;
import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_TRANSFORMING;
@@ -36,6 +36,7 @@ import androidx.annotation.NonNull;
import com.android.systemui.Dumpable;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeFinalizeFilterListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeSortListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeTransformGroupsListener;
@@ -82,7 +83,7 @@ public class ShadeListBuilder implements Dumpable {
private final List<NotifFilter> mNotifPreGroupFilters = new ArrayList<>();
private final List<NotifPromoter> mNotifPromoters = new ArrayList<>();
private final List<NotifFilter> mNotifPreRenderFilters = new ArrayList<>();
private final List<NotifFilter> mNotifFinalizeFilters = new ArrayList<>();
private final List<NotifComparator> mNotifComparators = new ArrayList<>();
private final List<NotifSection> mNotifSections = new ArrayList<>();
@@ -90,6 +91,8 @@ public class ShadeListBuilder implements Dumpable {
new ArrayList<>();
private final List<OnBeforeSortListener> mOnBeforeSortListeners =
new ArrayList<>();
private final List<OnBeforeFinalizeFilterListener> mOnBeforeFinalizeFilterListeners =
new ArrayList<>();
private final List<OnBeforeRenderListListener> mOnBeforeRenderListListeners =
new ArrayList<>();
@Nullable private OnRenderListListener mOnRenderListListener;
@@ -142,6 +145,13 @@ public class ShadeListBuilder implements Dumpable {
mOnBeforeSortListeners.add(listener);
}
void addOnBeforeFinalizeFilterListener(OnBeforeFinalizeFilterListener listener) {
Assert.isMainThread();
mPipelineState.requireState(STATE_IDLE);
mOnBeforeFinalizeFilterListeners.add(listener);
}
void addOnBeforeRenderListListener(OnBeforeRenderListListener listener) {
Assert.isMainThread();
@@ -157,12 +167,12 @@ public class ShadeListBuilder implements Dumpable {
filter.setInvalidationListener(this::onPreGroupFilterInvalidated);
}
void addPreRenderFilter(NotifFilter filter) {
void addFinalizeFilter(NotifFilter filter) {
Assert.isMainThread();
mPipelineState.requireState(STATE_IDLE);
mNotifPreRenderFilters.add(filter);
filter.setInvalidationListener(this::onPreRenderFilterInvalidated);
mNotifFinalizeFilters.add(filter);
filter.setInvalidationListener(this::onFinalizeFilterInvalidated);
}
void addPromoter(NotifPromoter promoter) {
@@ -237,12 +247,12 @@ public class ShadeListBuilder implements Dumpable {
rebuildListIfBefore(STATE_SORTING);
}
private void onPreRenderFilterInvalidated(NotifFilter filter) {
private void onFinalizeFilterInvalidated(NotifFilter filter) {
Assert.isMainThread();
mLogger.logPreRenderFilterInvalidated(filter.getName(), mPipelineState.getState());
mLogger.logFinalizeFilterInvalidated(filter.getName(), mPipelineState.getState());
rebuildListIfBefore(STATE_PRE_RENDER_FILTERING);
rebuildListIfBefore(STATE_FINALIZE_FILTERING);
}
private void onNotifComparatorInvalidated(NotifComparator comparator) {
@@ -298,8 +308,9 @@ public class ShadeListBuilder implements Dumpable {
// 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.
mPipelineState.incrementTo(STATE_PRE_RENDER_FILTERING);
filterNotifs(mNotifList, mNewNotifList, mNotifPreRenderFilters);
dispatchOnBeforeFinalizeFilter(mReadOnlyNotifList);
mPipelineState.incrementTo(STATE_FINALIZE_FILTERING);
filterNotifs(mNotifList, mNewNotifList, mNotifFinalizeFilters);
applyNewNotifList();
pruneIncompleteGroups(mNotifList);
@@ -772,6 +783,12 @@ public class ShadeListBuilder implements Dumpable {
}
}
private void dispatchOnBeforeFinalizeFilter(List<ListEntry> entries) {
for (int i = 0; i < mOnBeforeFinalizeFilterListeners.size(); i++) {
mOnBeforeFinalizeFilterListeners.get(i).onBeforeFinalizeFilter(entries);
}
}
private void dispatchOnBeforeRenderList(List<ListEntry> entries) {
for (int i = 0; i < mOnBeforeRenderListListeners.size(); i++) {
mOnBeforeRenderListListeners.get(i).onBeforeRenderList(entries);

View File

@@ -77,7 +77,7 @@ public class BubbleCoordinator implements Coordinator {
public void attach(NotifPipeline pipeline) {
mNotifPipeline = pipeline;
mNotifPipeline.addNotificationDismissInterceptor(mDismissInterceptor);
mNotifPipeline.addPreRenderFilter(mNotifFilter);
mNotifPipeline.addFinalizeFilter(mNotifFilter);
mBubbleController.addNotifCallback(mNotifCallback);
}

View File

@@ -87,7 +87,7 @@ public class KeyguardCoordinator implements Coordinator {
@Override
public void attach(NotifPipeline pipeline) {
setupInvalidateNotifListCallbacks();
pipeline.addPreRenderFilter(mNotifFilter);
pipeline.addFinalizeFilter(mNotifFilter);
}
private final NotifFilter mNotifFilter = new NotifFilter(TAG) {

View File

@@ -69,8 +69,8 @@ public class PreparationCoordinator implements Coordinator {
@Override
public void attach(NotifPipeline pipeline) {
pipeline.addCollectionListener(mNotifCollectionListener);
pipeline.addPreRenderFilter(mNotifInflationErrorFilter);
pipeline.addPreRenderFilter(mNotifInflatingFilter);
pipeline.addFinalizeFilter(mNotifInflationErrorFilter);
pipeline.addFinalizeFilter(mNotifInflatingFilter);
}
private final NotifCollectionListener mNotifCollectionListener = new NotifCollectionListener() {

View File

@@ -0,0 +1,34 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.statusbar.notification.collection.listbuilder;
import com.android.systemui.statusbar.notification.collection.ListEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import java.util.List;
/** See {@link NotifPipeline#addOnBeforeFinalizeFilterListener(OnBeforeFinalizeFilterListener)} */
public interface OnBeforeFinalizeFilterListener {
/**
* Called after the notif list has been filtered, grouped, and sorted but before they are
* filtered one last time before rendering.
*
* @param entries The current list of top-level entries. Note that this is a live view into the
* current list and will change whenever the pipeline is rerun.
*/
void onBeforeFinalizeFilter(List<ListEntry> entries);
}

View File

@@ -82,7 +82,7 @@ public class PipelineState {
public static final int STATE_GROUPING = 4;
public static final int STATE_TRANSFORMING = 5;
public static final int STATE_SORTING = 6;
public static final int STATE_PRE_RENDER_FILTERING = 7;
public static final int STATE_FINALIZE_FILTERING = 7;
public static final int STATE_FINALIZING = 8;
@IntDef(prefix = { "STATE_" }, value = {
@@ -93,7 +93,7 @@ public class PipelineState {
STATE_GROUPING,
STATE_TRANSFORMING,
STATE_SORTING,
STATE_PRE_RENDER_FILTERING,
STATE_FINALIZE_FILTERING,
STATE_FINALIZING,
})
@Retention(RetentionPolicy.SOURCE)

View File

@@ -87,12 +87,12 @@ class ShadeListBuilderLogger @Inject constructor(
})
}
fun logPreRenderFilterInvalidated(name: String, pipelineState: Int) {
fun logFinalizeFilterInvalidated(name: String, pipelineState: Int) {
buffer.log(TAG, DEBUG, {
str1 = name
int1 = pipelineState
}, {
"""Pre-render NotifFilter "$str1" invalidated; pipeline state is $int1"""
"""Finalize NotifFilter "$str1" invalidated; pipeline state is $int1"""
})
}

View File

@@ -21,7 +21,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry;
/**
* Pluggable for participating in notif filtering.
* See {@link NotifPipeline#addPreGroupFilter} and {@link NotifPipeline#addPreRenderFilter}.
* See {@link NotifPipeline#addPreGroupFilter} and {@link NotifPipeline#addFinalizeFilter}.
*/
public abstract class NotifFilter extends Pluggable<NotifFilter> {
protected NotifFilter(String name) {
@@ -37,7 +37,7 @@ public abstract class NotifFilter extends Pluggable<NotifFilter> {
* @param entry The entry in question.
* If this filter is registered via {@link NotifPipeline#addPreGroupFilter},
* this entry will not have any grouping nor sorting information.
* If this filter is registered via {@link NotifPipeline#addPreRenderFilter},
* If this filter is registered via {@link NotifPipeline#addFinalizeFilter},
* this entry will have grouping and sorting information.
* @param now A timestamp in SystemClock.uptimeMillis that represents "now" for the purposes of
* pipeline execution. This value will be the same for all pluggable calls made

View File

@@ -42,6 +42,7 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.statusbar.notification.collection.ShadeListBuilder.OnRenderListListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeFinalizeFilterListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeSortListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeTransformGroupsListener;
@@ -84,6 +85,7 @@ public class ShadeListBuilderTest extends SysuiTestCase {
@Mock private NotifCollection mNotifCollection;
@Spy private OnBeforeTransformGroupsListener mOnBeforeTransformGroupsListener;
@Spy private OnBeforeSortListener mOnBeforeSortListener;
@Spy private OnBeforeFinalizeFilterListener mOnBeforeFinalizeFilterListener;
@Spy private OnBeforeRenderListListener mOnBeforeRenderListListener;
@Spy private OnRenderListListener mOnRenderListListener = list -> mBuiltList = list;
@@ -387,7 +389,7 @@ public class ShadeListBuilderTest extends SysuiTestCase {
NotifFilter preGroupFilter = spy(new PackageFilter(PACKAGE_2));
NotifFilter preRenderFilter = spy(new PackageFilter(PACKAGE_2));
mListBuilder.addPreGroupFilter(preGroupFilter);
mListBuilder.addPreRenderFilter(preRenderFilter);
mListBuilder.addFinalizeFilter(preRenderFilter);
// WHEN the pipeline is kicked off on a list of notifs
addNotif(0, PACKAGE_1);
@@ -423,7 +425,7 @@ public class ShadeListBuilderTest extends SysuiTestCase {
public void testPreRenderNotifsAreFiltered() {
// GIVEN a NotifFilter that filters out a specific package
NotifFilter filter1 = spy(new PackageFilter(PACKAGE_2));
mListBuilder.addPreRenderFilter(filter1);
mListBuilder.addFinalizeFilter(filter1);
// WHEN the pipeline is kicked off on a list of notifs
addNotif(0, PACKAGE_1);
@@ -454,7 +456,7 @@ public class ShadeListBuilderTest extends SysuiTestCase {
final String filterTag = "FILTER_ME";
// GIVEN a NotifFilter that filters out notifications with a tag
NotifFilter filter1 = spy(new NotifFilterWithTag(filterTag));
mListBuilder.addPreRenderFilter(filter1);
mListBuilder.addFinalizeFilter(filter1);
// WHEN the pipeline is kicked off on a list of notifs
addGroupChildWithTag(0, PACKAGE_2, GROUP_1, filterTag);
@@ -742,8 +744,9 @@ public class ShadeListBuilderTest extends SysuiTestCase {
mListBuilder.addOnBeforeSortListener(mOnBeforeSortListener);
mListBuilder.setComparators(Collections.singletonList(comparator));
mListBuilder.setSections(Arrays.asList(section));
mListBuilder.addOnBeforeFinalizeFilterListener(mOnBeforeFinalizeFilterListener);
mListBuilder.addFinalizeFilter(preRenderFilter);
mListBuilder.addOnBeforeRenderListListener(mOnBeforeRenderListListener);
mListBuilder.addPreRenderFilter(preRenderFilter);
// WHEN a few new notifs are added
addNotif(0, PACKAGE_1);
@@ -763,6 +766,7 @@ public class ShadeListBuilderTest extends SysuiTestCase {
mOnBeforeSortListener,
section,
comparator,
mOnBeforeFinalizeFilterListener,
preRenderFilter,
mOnBeforeRenderListListener,
mOnRenderListListener);
@@ -777,6 +781,7 @@ public class ShadeListBuilderTest extends SysuiTestCase {
inOrder.verify(section, atLeastOnce()).isInSection(any(ListEntry.class));
inOrder.verify(comparator, atLeastOnce())
.compare(any(ListEntry.class), any(ListEntry.class));
inOrder.verify(mOnBeforeFinalizeFilterListener).onBeforeFinalizeFilter(anyList());
inOrder.verify(preRenderFilter, atLeastOnce())
.shouldFilterOut(any(NotificationEntry.class), anyLong());
inOrder.verify(mOnBeforeRenderListListener).onBeforeRenderList(anyList());
@@ -1075,7 +1080,7 @@ public class ShadeListBuilderTest extends SysuiTestCase {
// GIVEN a PreRenderNotifFilter that gets invalidated during the finalizing stage
NotifFilter filter = new PackageFilter(PACKAGE_5);
OnBeforeRenderListListener listener = (list) -> filter.invalidateList();
mListBuilder.addPreRenderFilter(filter);
mListBuilder.addFinalizeFilter(filter);
mListBuilder.addOnBeforeRenderListListener(listener);
// WHEN we try to run the pipeline and the PreRenderFilter is invalidated
@@ -1090,7 +1095,7 @@ public class ShadeListBuilderTest extends SysuiTestCase {
// GIVEN a PreRenderFilter that gets invalidated during the grouping stage
NotifFilter filter = new PackageFilter(PACKAGE_5);
OnBeforeTransformGroupsListener listener = (list) -> filter.invalidateList();
mListBuilder.addPreRenderFilter(filter);
mListBuilder.addFinalizeFilter(filter);
mListBuilder.addOnBeforeTransformGroupsListener(listener);
// WHEN we try to run the pipeline and the filter is invalidated

View File

@@ -88,7 +88,7 @@ public class KeyguardCoordinatorTest extends SysuiTestCase {
ArgumentCaptor<NotifFilter> filterCaptor = ArgumentCaptor.forClass(NotifFilter.class);
mKeyguardCoordinator.attach(mNotifPipeline);
verify(mNotifPipeline, times(1)).addPreRenderFilter(filterCaptor.capture());
verify(mNotifPipeline, times(1)).addFinalizeFilter(filterCaptor.capture());
mKeyguardFilter = filterCaptor.getValue();
}

View File

@@ -79,7 +79,7 @@ public class PreparationCoordinatorTest extends SysuiTestCase {
ArgumentCaptor<NotifFilter> filterCaptor = ArgumentCaptor.forClass(NotifFilter.class);
mCoordinator.attach(mNotifPipeline);
verify(mNotifPipeline, times(2)).addPreRenderFilter(filterCaptor.capture());
verify(mNotifPipeline, times(2)).addFinalizeFilter(filterCaptor.capture());
List<NotifFilter> filters = filterCaptor.getAllValues();
mInflationErrorFilter = filters.get(0);
}