1 /*
2  * 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.statusbar.notification.collection.GroupEntry
20 import com.android.systemui.statusbar.notification.collection.ListEntry
21 import com.android.systemui.statusbar.notification.collection.NotificationEntry
22 import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection
23 import com.android.systemui.util.traceSection
24 
25 /**
26  * Converts a notif list (the output of the ShadeListBuilder) into a NodeSpec, an abstract
27  * representation of which views should be present in the shade. This spec will later be consumed
28  * by the ViewDiffer, which will add and remove views until the shade matches the spec. Up until
29  * this point, the pipeline has dealt with pure data representations of notifications (in the
30  * form of NotificationEntries). In this step, NotificationEntries finally become associated with
31  * the views that will represent them. In addition, we add in any non-notification views that also
32  * need to present in the shade, notably the section headers.
33  */
34 class NodeSpecBuilder(
35     private val viewBarn: NotifViewBarn
36 ) {
37     fun buildNodeSpec(
38         rootController: NodeController,
39         notifList: List<ListEntry>
40     ): NodeSpec = traceSection("NodeSpecBuilder.buildNodeSpec") {
41         val root = NodeSpecImpl(null, rootController)
42         var currentSection: NotifSection? = null
43         val prevSections = mutableSetOf<NotifSection?>()
44 
45         for (entry in notifList) {
46             val section = entry.section!!
47 
48             if (prevSections.contains(section)) {
49                 throw java.lang.RuntimeException("Section ${section.label} has been duplicated")
50             }
51 
52             // If this notif begins a new section, first add the section's header view
53             if (section != currentSection) {
54                 section.headerController?.let { headerController ->
55                     root.children.add(NodeSpecImpl(root, headerController))
56                 }
57                 prevSections.add(currentSection)
58                 currentSection = section
59             }
60 
61             // Finally, add the actual notif node!
62             root.children.add(buildNotifNode(root, entry))
63         }
64 
65         return root
66     }
67 
68     private fun buildNotifNode(parent: NodeSpec, entry: ListEntry): NodeSpec = when (entry) {
69         is NotificationEntry -> NodeSpecImpl(parent, viewBarn.requireView(entry))
70         is GroupEntry -> NodeSpecImpl(parent, viewBarn.requireView(checkNotNull(entry.summary)))
71                 .apply { entry.children.forEach { children.add(buildNotifNode(this, it)) } }
72         else -> throw RuntimeException("Unexpected entry: $entry")
73     }
74 }
75