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