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