• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.systemui.statusbar.notification.collection
17 
18 import com.android.systemui.dagger.SysUISingleton
19 import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderEntryListener
20 import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderGroupListener
21 import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderListListener
22 import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeFinalizeFilterListener
23 import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener
24 import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeSortListener
25 import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeTransformGroupsListener
26 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Invalidator
27 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifBundler
28 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator
29 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter
30 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter
31 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
32 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager
33 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
34 import com.android.systemui.statusbar.notification.collection.notifcollection.InternalNotifUpdater
35 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
36 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifDismissInterceptor
37 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender
38 import com.android.systemui.statusbar.notification.collection.render.RenderStageManager
39 import javax.inject.Inject
40 
41 /**
42  * The system that constructs the "shade list", the filtered, grouped, and sorted list of
43  * notifications that are currently being displayed to the user in the notification shade.
44  *
45  * The pipeline proceeds through a series of stages in order to produce the final list (see below).
46  * Each stage exposes hooks and listeners to allow other code to participate.
47  *
48  * This list differs from the canonical one we receive from system server in a few ways:
49  * - Filtered: Some notifications are filtered out. For example, we filter out notifications whose
50  *   views haven't been inflated yet. We also filter out some notifications if we're on the lock
51  *   screen and notifications for other users. To participate, see
52  *   [.addPreGroupFilter] and similar methods.
53  * - Grouped: Notifications that are part of the same group are clustered together into a single
54  *   GroupEntry. These groups are then transformed in order to remove children or completely split
55  *   them apart. To participate, see [.addPromoter].
56  * - Sorted: All top-level notifications are sorted. To participate, see
57  *   [.setSections] and [.setComparators]
58  *
59  * The exact order of all hooks is as follows:
60  *  0. Collection listeners are fired ([.addCollectionListener]).
61  *  1. Pre-group filters are fired on each notification ([.addPreGroupFilter]).
62  *  2. Initial grouping is performed (NotificationEntries will have their parents set
63  *     appropriately).
64  *  3. OnBeforeTransformGroupListeners are fired ([.addOnBeforeTransformGroupsListener])
65  *  4. NotifPromoters are called on each notification with a parent ([.addPromoter])
66  *  5. OnBeforeSortListeners are fired ([.addOnBeforeSortListener])
67  *  6. Top-level entries are assigned sections by NotifSections ([.setSections])
68  *  7. Top-level entries within the same section are sorted by NotifComparators ([.setComparators])
69  *  8. OnBeforeFinalizeFilterListeners are fired ([.addOnBeforeFinalizeFilterListener])
70  *  9. Finalize filters are fired on each notification ([.addFinalizeFilter])
71  *  10. OnBeforeRenderListListeners are fired ([.addOnBeforeRenderListListener])
72  *  11. The list is handed off to the view layer to be rendered
73  *  12. OnAfterRenderListListeners are fired ([.addOnAfterRenderListListener])
74  *  13. OnAfterRenderGroupListeners are fired ([.addOnAfterRenderGroupListener])
75  *  13. OnAfterRenderEntryListeners are fired ([.addOnAfterRenderEntryListener])
76  */
77 @SysUISingleton
78 class NotifPipeline @Inject constructor(
79     private val mNotifCollection: NotifCollection,
80     private val mShadeListBuilder: ShadeListBuilder,
81     private val mRenderStageManager: RenderStageManager
82 ) : CommonNotifCollection {
83     /**
84      * Returns the list of all known notifications, i.e. the notifications that are currently posted
85      * to the phone. In general, this tracks closely to the list maintained by NotificationManager,
86      * but it can diverge slightly due to lifetime extenders.
87      *
88      * The returned collection is read-only, unsorted, unfiltered, and ungrouped.
89      */
getAllNotifsnull90     override fun getAllNotifs(): Collection<NotificationEntry> {
91         return mNotifCollection.allNotifs
92     }
93 
addCollectionListenernull94     override fun addCollectionListener(listener: NotifCollectionListener) {
95         mNotifCollection.addCollectionListener(listener)
96     }
97 
removeCollectionListenernull98     override fun removeCollectionListener(listener: NotifCollectionListener) {
99         mNotifCollection.removeCollectionListener(listener)
100     }
101 
102     /**
103      * Returns the NotificationEntry associated with [key].
104      */
getEntrynull105     override fun getEntry(key: String): NotificationEntry? {
106         return mNotifCollection.getEntry(key)
107     }
108 
109     /**
110      * Registers a lifetime extender. Lifetime extenders can cause notifications that have been
111      * dismissed or retracted by system server to be temporarily retained in the collection.
112      */
addNotificationLifetimeExtendernull113     fun addNotificationLifetimeExtender(extender: NotifLifetimeExtender) {
114         mNotifCollection.addNotificationLifetimeExtender(extender)
115     }
116 
117     /**
118      * Registers a dismiss interceptor. Dismiss interceptors can cause notifications that have been
119      * dismissed by the user to be retained (won't send a dismissal to system server).
120      */
addNotificationDismissInterceptornull121     fun addNotificationDismissInterceptor(interceptor: NotifDismissInterceptor) {
122         mNotifCollection.addNotificationDismissInterceptor(interceptor)
123     }
124 
125     /**
126      * Registers a filter with the pipeline before grouping, promoting and sorting occurs. Filters
127      * are called on each notification in the order that they were registered. If any filter
128      * returns true, the notification is removed from the pipeline (and no other filters are
129      * called on that notif).
130      */
addPreGroupFilternull131     fun addPreGroupFilter(filter: NotifFilter) {
132         mShadeListBuilder.addPreGroupFilter(filter)
133     }
134 
135     /**
136      * Called after notifications have been filtered and after the initial grouping has been
137      * performed but before NotifPromoters have had a chance to promote children out of groups.
138      */
addOnBeforeTransformGroupsListenernull139     fun addOnBeforeTransformGroupsListener(listener: OnBeforeTransformGroupsListener) {
140         mShadeListBuilder.addOnBeforeTransformGroupsListener(listener)
141     }
142 
143     /**
144      * Registers a promoter with the pipeline. Promoters are able to promote child notifications to
145      * top-level, i.e. move a notification that would be a child of a group and make it appear
146      * ungrouped. Promoters are called on each child notification in the order that they are
147      * registered. If any promoter returns true, the notification is removed from the group (and no
148      * other promoters are called on it).
149      */
addPromoternull150     fun addPromoter(promoter: NotifPromoter) {
151         mShadeListBuilder.addPromoter(promoter)
152     }
153 
154     /**
155      * Called after notifs have been filtered and groups have been determined but before sections
156      * have been determined or the notifs have been sorted.
157      */
addOnBeforeSortListenernull158     fun addOnBeforeSortListener(listener: OnBeforeSortListener) {
159         mShadeListBuilder.addOnBeforeSortListener(listener)
160     }
161 
162     /**
163      * Sections that are used to sort top-level entries.  If two entries have the same section,
164      * NotifComparators are consulted. Sections from this list are called in order for each
165      * notification passed through the pipeline. The first NotifSection to return true for
166      * [NotifSectioner.isInSection] sets the entry as part of its Section.
167      */
setSectionsnull168     fun setSections(sections: List<NotifSectioner>) {
169         mShadeListBuilder.setSectioners(sections)
170     }
171 
172     /**
173      * NotifBundler that is used to determine whether a notification should be bundled according to
174      * classification.
175      */
setNotifBundlernull176     fun setNotifBundler(bundler: NotifBundler) {
177         mShadeListBuilder.setBundler(bundler)
178     }
179 
180     /**
181      * StabilityManager that is used to determine whether to suppress group and section changes.
182      * This should only be set once.
183      */
setVisualStabilityManagernull184     fun setVisualStabilityManager(notifStabilityManager: NotifStabilityManager) {
185         mShadeListBuilder.setNotifStabilityManager(notifStabilityManager)
186     }
187 
188     /**
189      * Comparators that are used to sort top-level entries that share the same section. The
190      * comparators are executed in order until one of them returns a non-zero result. If all return
191      * zero, the pipeline falls back to sorting by rank (and, failing that, Notification.when).
192      */
setComparatorsnull193     fun setComparators(comparators: List<NotifComparator>) {
194         mShadeListBuilder.setComparators(comparators)
195     }
196 
197     /**
198      * Called after notifs have been filtered once, grouped, and sorted but before the final
199      * filtering.
200      */
addOnBeforeFinalizeFilterListenernull201     fun addOnBeforeFinalizeFilterListener(listener: OnBeforeFinalizeFilterListener) {
202         mShadeListBuilder.addOnBeforeFinalizeFilterListener(listener)
203     }
204 
205     /**
206      * Registers a filter with the pipeline to filter right before rendering the list (after
207      * pre-group filtering, grouping, promoting and sorting occurs). Filters are
208      * called on each notification in the order that they were registered. If any filter returns
209      * true, the notification is removed from the pipeline (and no other filters are called on that
210      * notif).
211      */
addFinalizeFilternull212     fun addFinalizeFilter(filter: NotifFilter) {
213         mShadeListBuilder.addFinalizeFilter(filter)
214     }
215 
216     /**
217      * Called at the end of the pipeline after the notif list has been finalized but before it has
218      * been handed off to the view layer.
219      */
addOnBeforeRenderListListenernull220     fun addOnBeforeRenderListListener(listener: OnBeforeRenderListListener) {
221         mShadeListBuilder.addOnBeforeRenderListListener(listener)
222     }
223 
224     /** Registers an invalidator that can be used to invalidate the entire notif list. */
addPreRenderInvalidatornull225     fun addPreRenderInvalidator(invalidator: Invalidator) {
226         mShadeListBuilder.addPreRenderInvalidator(invalidator)
227     }
228 
229     /**
230      * Called at the end of the pipeline after the notif list has been handed off to the view layer.
231      */
addOnAfterRenderListListenernull232     fun addOnAfterRenderListListener(listener: OnAfterRenderListListener) {
233         mRenderStageManager.addOnAfterRenderListListener(listener)
234     }
235 
236     /**
237      * Called at the end of the pipeline after a group has been handed off to the view layer.
238      */
addOnAfterRenderGroupListenernull239     fun addOnAfterRenderGroupListener(listener: OnAfterRenderGroupListener) {
240         mRenderStageManager.addOnAfterRenderGroupListener(listener)
241     }
242 
243     /**
244      * Called at the end of the pipeline after an entry has been handed off to the view layer.
245      * This will be called for every top level entry, every group summary, and every group child.
246      */
addOnAfterRenderEntryListenernull247     fun addOnAfterRenderEntryListener(listener: OnAfterRenderEntryListener) {
248         mRenderStageManager.addOnAfterRenderEntryListener(listener)
249     }
250 
251     /**
252      * Get an object which can be used to update a notification (internally to the pipeline)
253      * in response to a user action.
254      *
255      * @param name the name of the component that will update notifiations
256      * @return an updater
257      */
getInternalNotifUpdaternull258     fun getInternalNotifUpdater(name: String?): InternalNotifUpdater {
259         return mNotifCollection.getInternalNotifUpdater(name)
260     }
261 }
262