• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2021 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 
17 package com.android.systemui.statusbar.notification.collection.render
18 
19 import com.android.app.tracing.traceSection
20 import com.android.systemui.dagger.SysUISingleton
21 import com.android.systemui.statusbar.notification.collection.GroupEntry
22 import com.android.systemui.statusbar.notification.collection.PipelineEntry
23 import com.android.systemui.statusbar.notification.collection.NotificationEntry
24 import com.android.systemui.statusbar.notification.collection.PipelineDumpable
25 import com.android.systemui.statusbar.notification.collection.PipelineDumper
26 import com.android.systemui.statusbar.notification.collection.ShadeListBuilder
27 import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderEntryListener
28 import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderGroupListener
29 import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderListListener
30 import javax.inject.Inject
31 
32 /**
33  * The class which is part of the pipeline which guarantees a consistent that coordinators get a
34  * consistent interface to the view system regardless of the [NotifViewRenderer] implementation
35  * provided to [setViewRenderer].
36  */
37 @SysUISingleton
38 class RenderStageManager @Inject constructor() : PipelineDumpable {
39     private val onAfterRenderListListeners = mutableListOf<OnAfterRenderListListener>()
40     private val onAfterRenderGroupListeners = mutableListOf<OnAfterRenderGroupListener>()
41     private val onAfterRenderEntryListeners = mutableListOf<OnAfterRenderEntryListener>()
42     private var viewRenderer: NotifViewRenderer? = null
43 
44     /** Attach this stage to the rest of the pipeline */
45     fun attach(listBuilder: ShadeListBuilder) {
46         listBuilder.setOnRenderListListener(::onRenderList)
47     }
48 
49     private fun onRenderList(notifList: List<PipelineEntry>) {
50         traceSection("RenderStageManager.onRenderList") {
51             val viewRenderer = viewRenderer ?: return
52             viewRenderer.onRenderList(notifList)
53             dispatchOnAfterRenderList(notifList)
54             dispatchOnAfterRenderGroups(viewRenderer, notifList)
55             dispatchOnAfterRenderEntries(viewRenderer, notifList)
56             viewRenderer.onDispatchComplete()
57         }
58     }
59 
60     /** Provides this class with the view rendering implementation. */
61     fun setViewRenderer(renderer: NotifViewRenderer) {
62         viewRenderer = renderer
63     }
64 
65     /** Adds a listener that will get a single callback after rendering the list. */
66     fun addOnAfterRenderListListener(listener: OnAfterRenderListListener) {
67         onAfterRenderListListeners.add(listener)
68     }
69 
70     /** Adds a listener that will get a callback for each group rendered. */
71     fun addOnAfterRenderGroupListener(listener: OnAfterRenderGroupListener) {
72         onAfterRenderGroupListeners.add(listener)
73     }
74 
75     /** Adds a listener that will get a callback for each entry rendered. */
76     fun addOnAfterRenderEntryListener(listener: OnAfterRenderEntryListener) {
77         onAfterRenderEntryListeners.add(listener)
78     }
79 
80     override fun dumpPipeline(d: PipelineDumper) =
81         with(d) {
82             dump("viewRenderer", viewRenderer)
83             dump("onAfterRenderListListeners", onAfterRenderListListeners)
84             dump("onAfterRenderGroupListeners", onAfterRenderGroupListeners)
85             dump("onAfterRenderEntryListeners", onAfterRenderEntryListeners)
86         }
87 
88     private fun dispatchOnAfterRenderList(entries: List<PipelineEntry>) {
89         traceSection("RenderStageManager.dispatchOnAfterRenderList") {
90             onAfterRenderListListeners.forEach { listener -> listener.onAfterRenderList(entries) }
91         }
92     }
93 
94     private fun dispatchOnAfterRenderGroups(
95         viewRenderer: NotifViewRenderer,
96         entries: List<PipelineEntry>,
97     ) {
98         traceSection("RenderStageManager.dispatchOnAfterRenderGroups") {
99             if (onAfterRenderGroupListeners.isEmpty()) {
100                 return
101             }
102             entries.asSequence().filterIsInstance<GroupEntry>().forEach { group ->
103                 val controller = viewRenderer.getGroupController(group)
104                 onAfterRenderGroupListeners.forEach { listener ->
105                     listener.onAfterRenderGroup(group, controller)
106                 }
107             }
108         }
109     }
110 
111     private fun dispatchOnAfterRenderEntries(
112         viewRenderer: NotifViewRenderer,
113         entries: List<PipelineEntry>,
114     ) {
115         traceSection("RenderStageManager.dispatchOnAfterRenderEntries") {
116             if (onAfterRenderEntryListeners.isEmpty()) {
117                 return
118             }
119             entries.forEachNotificationEntry { entry ->
120                 val controller = viewRenderer.getRowController(entry)
121                 onAfterRenderEntryListeners.forEach { listener ->
122                     listener.onAfterRenderEntry(entry, controller)
123                 }
124             }
125         }
126     }
127 
128     /**
129      * Performs a forward, depth-first traversal of the list where the group's summary immediately
130      * precedes the group's children.
131      */
132     private inline fun List<PipelineEntry>.forEachNotificationEntry(
133         action: (NotificationEntry) -> Unit
134     ) {
135         forEach { entry ->
136             when (entry) {
137                 is NotificationEntry -> action(entry)
138                 is GroupEntry -> {
139                     action(entry.requireSummary)
140                     entry.children.forEach(action)
141                 }
142                 else -> error("Unhandled entry: $entry")
143             }
144         }
145     }
146 }
147