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.systemui.dagger.SysUISingleton
20 import com.android.systemui.statusbar.notification.collection.GroupEntry
21 import com.android.systemui.statusbar.notification.collection.ListEntry
22 import com.android.systemui.statusbar.notification.collection.NotificationEntry
23 import com.android.systemui.statusbar.notification.collection.PipelineDumpable
24 import com.android.systemui.statusbar.notification.collection.PipelineDumper
25 import com.android.systemui.statusbar.notification.collection.ShadeListBuilder
26 import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderEntryListener
27 import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderGroupListener
28 import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderListListener
29 import com.android.app.tracing.traceSection
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<ListEntry>) {
50         traceSection("RenderStageManager.onRenderList") {
51             val viewRenderer = viewRenderer ?: return
52             viewRenderer.onRenderList(notifList)
53             dispatchOnAfterRenderList(viewRenderer, 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) = with(d) {
81         dump("viewRenderer", viewRenderer)
82         dump("onAfterRenderListListeners", onAfterRenderListListeners)
83         dump("onAfterRenderGroupListeners", onAfterRenderGroupListeners)
84         dump("onAfterRenderEntryListeners", onAfterRenderEntryListeners)
85     }
86 
87     private fun dispatchOnAfterRenderList(
88         viewRenderer: NotifViewRenderer,
89         entries: List<ListEntry>
90     ) {
91         traceSection("RenderStageManager.dispatchOnAfterRenderList") {
92             val stackController = viewRenderer.getStackController()
93             onAfterRenderListListeners.forEach { listener ->
94                 listener.onAfterRenderList(entries, stackController)
95             }
96         }
97     }
98 
99     private fun dispatchOnAfterRenderGroups(
100         viewRenderer: NotifViewRenderer,
101         entries: List<ListEntry>
102     ) {
103         traceSection("RenderStageManager.dispatchOnAfterRenderGroups") {
104             if (onAfterRenderGroupListeners.isEmpty()) {
105                 return
106             }
107             entries.asSequence().filterIsInstance<GroupEntry>().forEach { group ->
108                 val controller = viewRenderer.getGroupController(group)
109                 onAfterRenderGroupListeners.forEach { listener ->
110                     listener.onAfterRenderGroup(group, controller)
111                 }
112             }
113         }
114     }
115 
116     private fun dispatchOnAfterRenderEntries(
117         viewRenderer: NotifViewRenderer,
118         entries: List<ListEntry>
119     ) {
120         traceSection("RenderStageManager.dispatchOnAfterRenderEntries") {
121             if (onAfterRenderEntryListeners.isEmpty()) {
122                 return
123             }
124             entries.forEachNotificationEntry { entry ->
125                 val controller = viewRenderer.getRowController(entry)
126                 onAfterRenderEntryListeners.forEach { listener ->
127                     listener.onAfterRenderEntry(entry, controller)
128                 }
129             }
130         }
131     }
132 
133     /**
134      * Performs a forward, depth-first traversal of the list where the group's summary
135      * immediately precedes the group's children.
136      */
137     private inline fun List<ListEntry>.forEachNotificationEntry(
138         action: (NotificationEntry) -> Unit
139     ) {
140         forEach { entry ->
141             when (entry) {
142                 is NotificationEntry -> action(entry)
143                 is GroupEntry -> {
144                     action(entry.requireSummary)
145                     entry.children.forEach(action)
146                 }
147                 else -> error("Unhandled entry: $entry")
148             }
149         }
150     }
151 }
152