Merge changes I1169beb3,If7c7b737 into rvc-dev

* changes:
  Move preparation coordinator to after grouping
  Add listener hook before finalize filter.
This commit is contained in:
Kevin Han
2020-03-04 01:08:32 +00:00
committed by Android (Google) Code Review
12 changed files with 261 additions and 53 deletions

View File

@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.collection; 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.OnBeforeRenderListListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeSortListener; import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeSortListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeTransformGroupsListener; 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}) * 6. Top-level entries are assigned sections by NotifSections ({@link #setSections})
* 7. Top-level entries within the same section are sorted by NotifComparators * 7. Top-level entries within the same section are sorted by NotifComparators
* ({@link #setComparators}) * ({@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. OnBeforeRenderListListeners are fired ({@link #addOnBeforeRenderListListener})
* 9. The list is handed off to the view layer to be rendered * 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); 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 * Registers a filter with the pipeline to filter right before rendering the list (after
* pre-group filtering, grouping, promoting and sorting occurs). Filters are * 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 * true, the notification is removed from the pipeline (and no other filters are called on that
* notif). * notif).
*/ */
public void addPreRenderFilter(NotifFilter filter) { public void addFinalizeFilter(NotifFilter filter) {
mShadeListBuilder.addPreRenderFilter(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.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_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_FINALIZING;
import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_GROUPING; 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_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_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_RESETTING;
import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_SORTING; import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_SORTING;
import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_TRANSFORMING; 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.Dumpable;
import com.android.systemui.dump.DumpManager; 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.OnBeforeRenderListListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeSortListener; import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeSortListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeTransformGroupsListener; 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<NotifFilter> mNotifPreGroupFilters = new ArrayList<>();
private final List<NotifPromoter> mNotifPromoters = 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<NotifComparator> mNotifComparators = new ArrayList<>();
private final List<NotifSection> mNotifSections = new ArrayList<>(); private final List<NotifSection> mNotifSections = new ArrayList<>();
@@ -90,6 +91,8 @@ public class ShadeListBuilder implements Dumpable {
new ArrayList<>(); new ArrayList<>();
private final List<OnBeforeSortListener> mOnBeforeSortListeners = private final List<OnBeforeSortListener> mOnBeforeSortListeners =
new ArrayList<>(); new ArrayList<>();
private final List<OnBeforeFinalizeFilterListener> mOnBeforeFinalizeFilterListeners =
new ArrayList<>();
private final List<OnBeforeRenderListListener> mOnBeforeRenderListListeners = private final List<OnBeforeRenderListListener> mOnBeforeRenderListListeners =
new ArrayList<>(); new ArrayList<>();
@Nullable private OnRenderListListener mOnRenderListListener; @Nullable private OnRenderListListener mOnRenderListListener;
@@ -142,6 +145,13 @@ public class ShadeListBuilder implements Dumpable {
mOnBeforeSortListeners.add(listener); mOnBeforeSortListeners.add(listener);
} }
void addOnBeforeFinalizeFilterListener(OnBeforeFinalizeFilterListener listener) {
Assert.isMainThread();
mPipelineState.requireState(STATE_IDLE);
mOnBeforeFinalizeFilterListeners.add(listener);
}
void addOnBeforeRenderListListener(OnBeforeRenderListListener listener) { void addOnBeforeRenderListListener(OnBeforeRenderListListener listener) {
Assert.isMainThread(); Assert.isMainThread();
@@ -157,12 +167,12 @@ public class ShadeListBuilder implements Dumpable {
filter.setInvalidationListener(this::onPreGroupFilterInvalidated); filter.setInvalidationListener(this::onPreGroupFilterInvalidated);
} }
void addPreRenderFilter(NotifFilter filter) { void addFinalizeFilter(NotifFilter filter) {
Assert.isMainThread(); Assert.isMainThread();
mPipelineState.requireState(STATE_IDLE); mPipelineState.requireState(STATE_IDLE);
mNotifPreRenderFilters.add(filter); mNotifFinalizeFilters.add(filter);
filter.setInvalidationListener(this::onPreRenderFilterInvalidated); filter.setInvalidationListener(this::onFinalizeFilterInvalidated);
} }
void addPromoter(NotifPromoter promoter) { void addPromoter(NotifPromoter promoter) {
@@ -237,12 +247,12 @@ public class ShadeListBuilder implements Dumpable {
rebuildListIfBefore(STATE_SORTING); rebuildListIfBefore(STATE_SORTING);
} }
private void onPreRenderFilterInvalidated(NotifFilter filter) { private void onFinalizeFilterInvalidated(NotifFilter filter) {
Assert.isMainThread(); 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) { 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 // 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); dispatchOnBeforeFinalizeFilter(mReadOnlyNotifList);
filterNotifs(mNotifList, mNewNotifList, mNotifPreRenderFilters); mPipelineState.incrementTo(STATE_FINALIZE_FILTERING);
filterNotifs(mNotifList, mNewNotifList, mNotifFinalizeFilters);
applyNewNotifList(); applyNewNotifList();
pruneIncompleteGroups(mNotifList); 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) { private void dispatchOnBeforeRenderList(List<ListEntry> entries) {
for (int i = 0; i < mOnBeforeRenderListListeners.size(); i++) { for (int i = 0; i < mOnBeforeRenderListListeners.size(); i++) {
mOnBeforeRenderListListeners.get(i).onBeforeRenderList(entries); mOnBeforeRenderListListeners.get(i).onBeforeRenderList(entries);

View File

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

View File

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

View File

@@ -16,31 +16,39 @@
package com.android.systemui.statusbar.notification.collection.coordinator; package com.android.systemui.statusbar.notification.collection.coordinator;
import android.annotation.IntDef;
import android.os.RemoteException; import android.os.RemoteException;
import android.service.notification.StatusBarNotification; import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
import com.android.internal.statusbar.IStatusBarService; import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.statusbar.notification.collection.GroupEntry;
import com.android.systemui.statusbar.notification.collection.ListEntry;
import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl; import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl;
import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.ShadeListBuilder; import com.android.systemui.statusbar.notification.collection.ShadeListBuilder;
import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater; import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeFinalizeFilterListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.row.NotifInflationErrorManager; import com.android.systemui.statusbar.notification.row.NotifInflationErrorManager;
import java.util.ArrayList; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Objects;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
/** /**
* Kicks off notification inflation and view rebinding when a notification is added or updated. * Kicks off core notification inflation and view rebinding when a notification is added or updated.
* Aborts inflation when a notification is removed. * Aborts inflation when a notification is removed.
* *
* If a notification is not done inflating, this coordinator will filter the notification out * If a notification was uninflated, this coordinator will filter the notification out from the
* from the {@link ShadeListBuilder}. * {@link ShadeListBuilder} until it is inflated.
*/ */
@Singleton @Singleton
public class PreparationCoordinator implements Coordinator { public class PreparationCoordinator implements Coordinator {
@@ -49,7 +57,7 @@ public class PreparationCoordinator implements Coordinator {
private final PreparationCoordinatorLogger mLogger; private final PreparationCoordinatorLogger mLogger;
private final NotifInflater mNotifInflater; private final NotifInflater mNotifInflater;
private final NotifInflationErrorManager mNotifErrorManager; private final NotifInflationErrorManager mNotifErrorManager;
private final List<NotificationEntry> mPendingNotifications = new ArrayList<>(); private final Map<NotificationEntry, Integer> mInflationStates = new ArrayMap<>();
private final IStatusBarService mStatusBarService; private final IStatusBarService mStatusBarService;
@Inject @Inject
@@ -69,27 +77,44 @@ public class PreparationCoordinator implements Coordinator {
@Override @Override
public void attach(NotifPipeline pipeline) { public void attach(NotifPipeline pipeline) {
pipeline.addCollectionListener(mNotifCollectionListener); pipeline.addCollectionListener(mNotifCollectionListener);
pipeline.addPreRenderFilter(mNotifInflationErrorFilter); // Inflate after grouping/sorting since that affects what views to inflate.
pipeline.addPreRenderFilter(mNotifInflatingFilter); pipeline.addOnBeforeFinalizeFilterListener(mOnBeforeFinalizeFilterListener);
pipeline.addFinalizeFilter(mNotifInflationErrorFilter);
pipeline.addFinalizeFilter(mNotifInflatingFilter);
} }
private final NotifCollectionListener mNotifCollectionListener = new NotifCollectionListener() { private final NotifCollectionListener mNotifCollectionListener = new NotifCollectionListener() {
@Override @Override
public void onEntryAdded(NotificationEntry entry) { public void onEntryInit(NotificationEntry entry) {
inflateEntry(entry, "entryAdded"); mInflationStates.put(entry, STATE_UNINFLATED);
} }
@Override @Override
public void onEntryUpdated(NotificationEntry entry) { public void onEntryUpdated(NotificationEntry entry) {
rebind(entry, "entryUpdated"); @InflationState int state = getInflationState(entry);
if (state == STATE_INFLATED) {
mInflationStates.put(entry, STATE_INFLATED_INVALID);
} else if (state == STATE_ERROR) {
// Updated so maybe it won't error out now.
mInflationStates.put(entry, STATE_UNINFLATED);
}
} }
@Override @Override
public void onEntryRemoved(NotificationEntry entry, int reason) { public void onEntryRemoved(NotificationEntry entry, int reason) {
abortInflation(entry, "entryRemoved reason=" + reason); abortInflation(entry, "entryRemoved reason=" + reason);
} }
@Override
public void onEntryCleanUp(NotificationEntry entry) {
mInflationStates.remove(entry);
}
}; };
private final OnBeforeFinalizeFilterListener mOnBeforeFinalizeFilterListener =
entries -> inflateAllRequiredViews(entries);
private final NotifFilter mNotifInflationErrorFilter = new NotifFilter( private final NotifFilter mNotifInflationErrorFilter = new NotifFilter(
TAG + "InflationError") { TAG + "InflationError") {
/** /**
@@ -97,10 +122,7 @@ public class PreparationCoordinator implements Coordinator {
*/ */
@Override @Override
public boolean shouldFilterOut(NotificationEntry entry, long now) { public boolean shouldFilterOut(NotificationEntry entry, long now) {
if (mNotifErrorManager.hasInflationError(entry)) { return getInflationState(entry) == STATE_ERROR;
return true;
}
return false;
} }
}; };
@@ -110,7 +132,8 @@ public class PreparationCoordinator implements Coordinator {
*/ */
@Override @Override
public boolean shouldFilterOut(NotificationEntry entry, long now) { public boolean shouldFilterOut(NotificationEntry entry, long now) {
return mPendingNotifications.contains(entry); @InflationState int state = getInflationState(entry);
return (state != STATE_INFLATED) && (state != STATE_INFLATED_INVALID);
} }
}; };
@@ -119,7 +142,7 @@ public class PreparationCoordinator implements Coordinator {
@Override @Override
public void onInflationFinished(NotificationEntry entry) { public void onInflationFinished(NotificationEntry entry) {
mLogger.logNotifInflated(entry.getKey()); mLogger.logNotifInflated(entry.getKey());
mPendingNotifications.remove(entry); mInflationStates.put(entry, STATE_INFLATED);
mNotifInflatingFilter.invalidateList(); mNotifInflatingFilter.invalidateList();
} }
}; };
@@ -128,7 +151,7 @@ public class PreparationCoordinator implements Coordinator {
new NotifInflationErrorManager.NotifInflationErrorListener() { new NotifInflationErrorManager.NotifInflationErrorListener() {
@Override @Override
public void onNotifInflationError(NotificationEntry entry, Exception e) { public void onNotifInflationError(NotificationEntry entry, Exception e) {
mPendingNotifications.remove(entry); mInflationStates.put(entry, STATE_ERROR);
try { try {
final StatusBarNotification sbn = entry.getSbn(); final StatusBarNotification sbn = entry.getSbn();
// report notification inflation errors back up // report notification inflation errors back up
@@ -152,9 +175,41 @@ public class PreparationCoordinator implements Coordinator {
} }
}; };
private void inflateAllRequiredViews(List<ListEntry> entries) {
for (int i = 0, size = entries.size(); i < size; i++) {
ListEntry entry = entries.get(i);
if (entry instanceof GroupEntry) {
GroupEntry groupEntry = (GroupEntry) entry;
inflateNotifRequiredViews(groupEntry.getSummary());
List<NotificationEntry> children = groupEntry.getChildren();
for (int j = 0, groupSize = children.size(); j < groupSize; j++) {
inflateNotifRequiredViews(children.get(j));
}
} else {
NotificationEntry notifEntry = (NotificationEntry) entry;
inflateNotifRequiredViews(notifEntry);
}
}
}
private void inflateNotifRequiredViews(NotificationEntry entry) {
@InflationState int state = mInflationStates.get(entry);
switch (state) {
case STATE_UNINFLATED:
inflateEntry(entry, "entryAdded");
break;
case STATE_INFLATED_INVALID:
rebind(entry, "entryUpdated");
break;
case STATE_INFLATED:
case STATE_ERROR:
default:
// Nothing to do.
}
}
private void inflateEntry(NotificationEntry entry, String reason) { private void inflateEntry(NotificationEntry entry, String reason) {
abortInflation(entry, reason); abortInflation(entry, reason);
mPendingNotifications.add(entry);
mNotifInflater.inflateViews(entry); mNotifInflater.inflateViews(entry);
} }
@@ -165,6 +220,32 @@ public class PreparationCoordinator implements Coordinator {
private void abortInflation(NotificationEntry entry, String reason) { private void abortInflation(NotificationEntry entry, String reason) {
mLogger.logInflationAborted(entry.getKey(), reason); mLogger.logInflationAborted(entry.getKey(), reason);
entry.abortTask(); entry.abortTask();
mPendingNotifications.remove(entry);
} }
private @InflationState int getInflationState(NotificationEntry entry) {
Integer stateObj = mInflationStates.get(entry);
Objects.requireNonNull(stateObj,
"Asking state of a notification preparation coordinator doesn't know about");
return stateObj;
}
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"STATE_"},
value = {STATE_UNINFLATED, STATE_INFLATED_INVALID, STATE_INFLATED, STATE_ERROR})
@interface InflationState {}
/** The notification has never been inflated before. */
private static final int STATE_UNINFLATED = 0;
/** The notification is inflated. */
private static final int STATE_INFLATED = 1;
/**
* The notification is inflated, but its content may be out-of-date since the notification has
* been updated.
*/
private static final int STATE_INFLATED_INVALID = 2;
/** The notification errored out while inflating */
private static final int STATE_ERROR = -1;
} }

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_GROUPING = 4;
public static final int STATE_TRANSFORMING = 5; public static final int STATE_TRANSFORMING = 5;
public static final int STATE_SORTING = 6; 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; public static final int STATE_FINALIZING = 8;
@IntDef(prefix = { "STATE_" }, value = { @IntDef(prefix = { "STATE_" }, value = {
@@ -93,7 +93,7 @@ public class PipelineState {
STATE_GROUPING, STATE_GROUPING,
STATE_TRANSFORMING, STATE_TRANSFORMING,
STATE_SORTING, STATE_SORTING,
STATE_PRE_RENDER_FILTERING, STATE_FINALIZE_FILTERING,
STATE_FINALIZING, STATE_FINALIZING,
}) })
@Retention(RetentionPolicy.SOURCE) @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, { buffer.log(TAG, DEBUG, {
str1 = name str1 = name
int1 = pipelineState 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. * 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> { public abstract class NotifFilter extends Pluggable<NotifFilter> {
protected NotifFilter(String name) { protected NotifFilter(String name) {
@@ -37,7 +37,7 @@ public abstract class NotifFilter extends Pluggable<NotifFilter> {
* @param entry The entry in question. * @param entry The entry in question.
* If this filter is registered via {@link NotifPipeline#addPreGroupFilter}, * If this filter is registered via {@link NotifPipeline#addPreGroupFilter},
* this entry will not have any grouping nor sorting information. * 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. * this entry will have grouping and sorting information.
* @param now A timestamp in SystemClock.uptimeMillis that represents "now" for the purposes of * @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 * 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.SysuiTestCase;
import com.android.systemui.dump.DumpManager; import com.android.systemui.dump.DumpManager;
import com.android.systemui.statusbar.notification.collection.ShadeListBuilder.OnRenderListListener; 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.OnBeforeRenderListListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeSortListener; import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeSortListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeTransformGroupsListener; import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeTransformGroupsListener;
@@ -84,6 +85,7 @@ public class ShadeListBuilderTest extends SysuiTestCase {
@Mock private NotifCollection mNotifCollection; @Mock private NotifCollection mNotifCollection;
@Spy private OnBeforeTransformGroupsListener mOnBeforeTransformGroupsListener; @Spy private OnBeforeTransformGroupsListener mOnBeforeTransformGroupsListener;
@Spy private OnBeforeSortListener mOnBeforeSortListener; @Spy private OnBeforeSortListener mOnBeforeSortListener;
@Spy private OnBeforeFinalizeFilterListener mOnBeforeFinalizeFilterListener;
@Spy private OnBeforeRenderListListener mOnBeforeRenderListListener; @Spy private OnBeforeRenderListListener mOnBeforeRenderListListener;
@Spy private OnRenderListListener mOnRenderListListener = list -> mBuiltList = list; @Spy private OnRenderListListener mOnRenderListListener = list -> mBuiltList = list;
@@ -387,7 +389,7 @@ public class ShadeListBuilderTest extends SysuiTestCase {
NotifFilter preGroupFilter = spy(new PackageFilter(PACKAGE_2)); NotifFilter preGroupFilter = spy(new PackageFilter(PACKAGE_2));
NotifFilter preRenderFilter = spy(new PackageFilter(PACKAGE_2)); NotifFilter preRenderFilter = spy(new PackageFilter(PACKAGE_2));
mListBuilder.addPreGroupFilter(preGroupFilter); mListBuilder.addPreGroupFilter(preGroupFilter);
mListBuilder.addPreRenderFilter(preRenderFilter); mListBuilder.addFinalizeFilter(preRenderFilter);
// WHEN the pipeline is kicked off on a list of notifs // WHEN the pipeline is kicked off on a list of notifs
addNotif(0, PACKAGE_1); addNotif(0, PACKAGE_1);
@@ -423,7 +425,7 @@ public class ShadeListBuilderTest extends SysuiTestCase {
public void testPreRenderNotifsAreFiltered() { public void testPreRenderNotifsAreFiltered() {
// GIVEN a NotifFilter that filters out a specific package // GIVEN a NotifFilter that filters out a specific package
NotifFilter filter1 = spy(new PackageFilter(PACKAGE_2)); NotifFilter filter1 = spy(new PackageFilter(PACKAGE_2));
mListBuilder.addPreRenderFilter(filter1); mListBuilder.addFinalizeFilter(filter1);
// WHEN the pipeline is kicked off on a list of notifs // WHEN the pipeline is kicked off on a list of notifs
addNotif(0, PACKAGE_1); addNotif(0, PACKAGE_1);
@@ -454,7 +456,7 @@ public class ShadeListBuilderTest extends SysuiTestCase {
final String filterTag = "FILTER_ME"; final String filterTag = "FILTER_ME";
// GIVEN a NotifFilter that filters out notifications with a tag // GIVEN a NotifFilter that filters out notifications with a tag
NotifFilter filter1 = spy(new NotifFilterWithTag(filterTag)); NotifFilter filter1 = spy(new NotifFilterWithTag(filterTag));
mListBuilder.addPreRenderFilter(filter1); mListBuilder.addFinalizeFilter(filter1);
// WHEN the pipeline is kicked off on a list of notifs // WHEN the pipeline is kicked off on a list of notifs
addGroupChildWithTag(0, PACKAGE_2, GROUP_1, filterTag); addGroupChildWithTag(0, PACKAGE_2, GROUP_1, filterTag);
@@ -742,8 +744,9 @@ public class ShadeListBuilderTest extends SysuiTestCase {
mListBuilder.addOnBeforeSortListener(mOnBeforeSortListener); mListBuilder.addOnBeforeSortListener(mOnBeforeSortListener);
mListBuilder.setComparators(Collections.singletonList(comparator)); mListBuilder.setComparators(Collections.singletonList(comparator));
mListBuilder.setSections(Arrays.asList(section)); mListBuilder.setSections(Arrays.asList(section));
mListBuilder.addOnBeforeFinalizeFilterListener(mOnBeforeFinalizeFilterListener);
mListBuilder.addFinalizeFilter(preRenderFilter);
mListBuilder.addOnBeforeRenderListListener(mOnBeforeRenderListListener); mListBuilder.addOnBeforeRenderListListener(mOnBeforeRenderListListener);
mListBuilder.addPreRenderFilter(preRenderFilter);
// WHEN a few new notifs are added // WHEN a few new notifs are added
addNotif(0, PACKAGE_1); addNotif(0, PACKAGE_1);
@@ -763,6 +766,7 @@ public class ShadeListBuilderTest extends SysuiTestCase {
mOnBeforeSortListener, mOnBeforeSortListener,
section, section,
comparator, comparator,
mOnBeforeFinalizeFilterListener,
preRenderFilter, preRenderFilter,
mOnBeforeRenderListListener, mOnBeforeRenderListListener,
mOnRenderListListener); mOnRenderListListener);
@@ -777,6 +781,7 @@ public class ShadeListBuilderTest extends SysuiTestCase {
inOrder.verify(section, atLeastOnce()).isInSection(any(ListEntry.class)); inOrder.verify(section, atLeastOnce()).isInSection(any(ListEntry.class));
inOrder.verify(comparator, atLeastOnce()) inOrder.verify(comparator, atLeastOnce())
.compare(any(ListEntry.class), any(ListEntry.class)); .compare(any(ListEntry.class), any(ListEntry.class));
inOrder.verify(mOnBeforeFinalizeFilterListener).onBeforeFinalizeFilter(anyList());
inOrder.verify(preRenderFilter, atLeastOnce()) inOrder.verify(preRenderFilter, atLeastOnce())
.shouldFilterOut(any(NotificationEntry.class), anyLong()); .shouldFilterOut(any(NotificationEntry.class), anyLong());
inOrder.verify(mOnBeforeRenderListListener).onBeforeRenderList(anyList()); inOrder.verify(mOnBeforeRenderListListener).onBeforeRenderList(anyList());
@@ -1075,7 +1080,7 @@ public class ShadeListBuilderTest extends SysuiTestCase {
// GIVEN a PreRenderNotifFilter that gets invalidated during the finalizing stage // GIVEN a PreRenderNotifFilter that gets invalidated during the finalizing stage
NotifFilter filter = new PackageFilter(PACKAGE_5); NotifFilter filter = new PackageFilter(PACKAGE_5);
OnBeforeRenderListListener listener = (list) -> filter.invalidateList(); OnBeforeRenderListListener listener = (list) -> filter.invalidateList();
mListBuilder.addPreRenderFilter(filter); mListBuilder.addFinalizeFilter(filter);
mListBuilder.addOnBeforeRenderListListener(listener); mListBuilder.addOnBeforeRenderListListener(listener);
// WHEN we try to run the pipeline and the PreRenderFilter is invalidated // 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 // GIVEN a PreRenderFilter that gets invalidated during the grouping stage
NotifFilter filter = new PackageFilter(PACKAGE_5); NotifFilter filter = new PackageFilter(PACKAGE_5);
OnBeforeTransformGroupsListener listener = (list) -> filter.invalidateList(); OnBeforeTransformGroupsListener listener = (list) -> filter.invalidateList();
mListBuilder.addPreRenderFilter(filter); mListBuilder.addFinalizeFilter(filter);
mListBuilder.addOnBeforeTransformGroupsListener(listener); mListBuilder.addOnBeforeTransformGroupsListener(listener);
// WHEN we try to run the pipeline and the filter is invalidated // 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); ArgumentCaptor<NotifFilter> filterCaptor = ArgumentCaptor.forClass(NotifFilter.class);
mKeyguardCoordinator.attach(mNotifPipeline); mKeyguardCoordinator.attach(mNotifPipeline);
verify(mNotifPipeline, times(1)).addPreRenderFilter(filterCaptor.capture()); verify(mNotifPipeline, times(1)).addFinalizeFilter(filterCaptor.capture());
mKeyguardFilter = filterCaptor.getValue(); mKeyguardFilter = filterCaptor.getValue();
} }

View File

@@ -16,8 +16,8 @@
package com.android.systemui.statusbar.notification.collection.coordinator; package com.android.systemui.statusbar.notification.collection.coordinator;
import static junit.framework.Assert.assertTrue; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
@@ -35,13 +35,16 @@ import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl;
import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeFinalizeFilterListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.row.NotifInflationErrorManager; import com.android.systemui.statusbar.notification.row.NotifInflationErrorManager;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor; import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations;
@@ -54,14 +57,22 @@ public class PreparationCoordinatorTest extends SysuiTestCase {
private static final String TEST_MESSAGE = "TEST_MESSAGE"; private static final String TEST_MESSAGE = "TEST_MESSAGE";
private PreparationCoordinator mCoordinator; private PreparationCoordinator mCoordinator;
private NotifCollectionListener mCollectionListener;
private OnBeforeFinalizeFilterListener mBeforeFilterListener;
private NotifFilter mUninflatedFilter;
private NotifFilter mInflationErrorFilter; private NotifFilter mInflationErrorFilter;
private NotifInflaterImpl.InflationCallback mCallback;
private NotifInflationErrorManager mErrorManager; private NotifInflationErrorManager mErrorManager;
private NotificationEntry mEntry; private NotificationEntry mEntry;
private Exception mInflationError; private Exception mInflationError;
@Mock @Captor private ArgumentCaptor<NotifCollectionListener> mCollectionListenerCaptor;
private NotifPipeline mNotifPipeline; @Captor private ArgumentCaptor<OnBeforeFinalizeFilterListener> mBeforeFilterListenerCaptor;
@Captor private ArgumentCaptor<NotifInflaterImpl.InflationCallback> mCallbackCaptor;
@Mock private NotifPipeline mNotifPipeline;
@Mock private IStatusBarService mService; @Mock private IStatusBarService mService;
@Mock private NotifInflaterImpl mNotifInflater;
@Before @Before
public void setUp() { public void setUp() {
@@ -73,15 +84,28 @@ public class PreparationCoordinatorTest extends SysuiTestCase {
mCoordinator = new PreparationCoordinator( mCoordinator = new PreparationCoordinator(
mock(PreparationCoordinatorLogger.class), mock(PreparationCoordinatorLogger.class),
mock(NotifInflaterImpl.class), mNotifInflater,
mErrorManager, mErrorManager,
mService); mService);
ArgumentCaptor<NotifFilter> filterCaptor = ArgumentCaptor.forClass(NotifFilter.class); ArgumentCaptor<NotifFilter> filterCaptor = ArgumentCaptor.forClass(NotifFilter.class);
mCoordinator.attach(mNotifPipeline); mCoordinator.attach(mNotifPipeline);
verify(mNotifPipeline, times(2)).addPreRenderFilter(filterCaptor.capture()); verify(mNotifPipeline, times(2)).addFinalizeFilter(filterCaptor.capture());
List<NotifFilter> filters = filterCaptor.getAllValues(); List<NotifFilter> filters = filterCaptor.getAllValues();
mInflationErrorFilter = filters.get(0); mInflationErrorFilter = filters.get(0);
mUninflatedFilter = filters.get(1);
verify(mNotifPipeline).addCollectionListener(mCollectionListenerCaptor.capture());
mCollectionListener = mCollectionListenerCaptor.getValue();
verify(mNotifPipeline).addOnBeforeFinalizeFilterListener(
mBeforeFilterListenerCaptor.capture());
mBeforeFilterListener = mBeforeFilterListenerCaptor.getValue();
verify(mNotifInflater).setInflationCallback(mCallbackCaptor.capture());
mCallback = mCallbackCaptor.getValue();
mCollectionListener.onEntryInit(mEntry);
} }
@Test @Test
@@ -108,4 +132,42 @@ public class PreparationCoordinatorTest extends SysuiTestCase {
// THEN we filter it from the notification list. // THEN we filter it from the notification list.
assertTrue(mInflationErrorFilter.shouldFilterOut(mEntry, 0)); assertTrue(mInflationErrorFilter.shouldFilterOut(mEntry, 0));
} }
@Test
public void testInflatesNewNotification() {
// WHEN there is a new notification
mCollectionListener.onEntryAdded(mEntry);
mBeforeFilterListener.onBeforeFinalizeFilter(List.of(mEntry));
// THEN we inflate it
verify(mNotifInflater).inflateViews(mEntry);
// THEN we filter it out until it's done inflating.
assertTrue(mUninflatedFilter.shouldFilterOut(mEntry, 0));
}
@Test
public void testRebindsInflatedNotificationsOnUpdate() {
// GIVEN an inflated notification
mCallback.onInflationFinished(mEntry);
// WHEN notification is updated
mCollectionListener.onEntryUpdated(mEntry);
mBeforeFilterListener.onBeforeFinalizeFilter(List.of(mEntry));
// THEN we rebind it
verify(mNotifInflater).rebindViews(mEntry);
// THEN we do not filter it because it's not the first inflation.
assertFalse(mUninflatedFilter.shouldFilterOut(mEntry, 0));
}
@Test
public void testDoesntFilterInflatedNotifs() {
// WHEN a notification is inflated
mCallback.onInflationFinished(mEntry);
// THEN it isn't filtered from shade list
assertFalse(mUninflatedFilter.shouldFilterOut(mEntry, 0));
}
} }