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.statusbar.notification.NotificationSectionsFeatureManager 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.listbuilder.NotifSection 24 import com.android.systemui.statusbar.notification.collection.provider.SectionHeaderVisibilityProvider 25 import com.android.systemui.util.Compile 26 import com.android.app.tracing.traceSection 27 28 /** 29 * Converts a notif list (the output of the ShadeListBuilder) into a NodeSpec, an abstract 30 * representation of which views should be present in the shade. This spec will later be consumed 31 * by the ViewDiffer, which will add and remove views until the shade matches the spec. Up until 32 * this point, the pipeline has dealt with pure data representations of notifications (in the 33 * form of NotificationEntries). In this step, NotificationEntries finally become associated with 34 * the views that will represent them. In addition, we add in any non-notification views that also 35 * need to present in the shade, notably the section headers. 36 */ 37 class NodeSpecBuilder( 38 private val mediaContainerController: MediaContainerController, 39 private val sectionsFeatureManager: NotificationSectionsFeatureManager, 40 private val sectionHeaderVisibilityProvider: SectionHeaderVisibilityProvider, 41 private val viewBarn: NotifViewBarn, 42 private val logger: NodeSpecBuilderLogger 43 ) { 44 private var lastSections = setOf<NotifSection?>() 45 46 fun buildNodeSpec( 47 rootController: NodeController, 48 notifList: List<ListEntry> 49 ): NodeSpec = traceSection("NodeSpecBuilder.buildNodeSpec") { 50 val root = NodeSpecImpl(null, rootController) 51 52 // The media container should be added as the first child of the root node 53 // TODO: Perhaps the node spec building process should be more of a pipeline of its own? 54 if (sectionsFeatureManager.isMediaControlsEnabled()) { 55 root.children.add(NodeSpecImpl(root, mediaContainerController)) 56 } 57 58 var currentSection: NotifSection? = null 59 val prevSections = mutableSetOf<NotifSection?>() 60 val showHeaders = sectionHeaderVisibilityProvider.sectionHeadersVisible 61 val sectionOrder = mutableListOf<NotifSection?>() 62 val sectionHeaders = mutableMapOf<NotifSection?, NodeController?>() 63 val sectionCounts = mutableMapOf<NotifSection?, Int>() 64 65 for (entry in notifList) { 66 val section = entry.section!! 67 if (prevSections.contains(section)) { 68 throw java.lang.RuntimeException("Section ${section.label} has been duplicated") 69 } 70 71 // If this notif begins a new section, first add the section's header view 72 if (section != currentSection) { 73 if (section.headerController != currentSection?.headerController && showHeaders) { 74 section.headerController?.let { headerController -> 75 root.children.add(NodeSpecImpl(root, headerController)) 76 if (Compile.IS_DEBUG) { 77 sectionHeaders[section] = headerController 78 } 79 } 80 } 81 prevSections.add(currentSection) 82 currentSection = section 83 if (Compile.IS_DEBUG) { 84 sectionOrder.add(section) 85 } 86 } 87 88 // Finally, add the actual notif node! 89 root.children.add(buildNotifNode(root, entry)) 90 if (Compile.IS_DEBUG) { 91 sectionCounts[section] = sectionCounts.getOrDefault(section, 0) + 1 92 } 93 } 94 95 if (Compile.IS_DEBUG) { 96 logger.logBuildNodeSpec(lastSections, sectionHeaders, sectionCounts, sectionOrder) 97 lastSections = sectionCounts.keys 98 } 99 100 return@traceSection root 101 } 102 103 private fun buildNotifNode(parent: NodeSpec, entry: ListEntry): NodeSpec = when (entry) { 104 is NotificationEntry -> NodeSpecImpl(parent, viewBarn.requireNodeController(entry)) 105 is GroupEntry -> 106 NodeSpecImpl(parent, viewBarn.requireNodeController(checkNotNull(entry.summary))) 107 .apply { entry.children.forEach { children.add(buildNotifNode(this, it)) } } 108 else -> throw RuntimeException("Unexpected entry: $entry") 109 } 110 } 111