1 /* 2 * Copyright (C) 2020 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 android.view.View 20 import java.lang.RuntimeException 21 import java.lang.StringBuilder 22 23 /** 24 * A controller that represents a single unit of addable/removable view(s) in the notification 25 * shade. Some nodes are just a single view (such as a header), while some might involve many views 26 * (such as a notification row). 27 * 28 * It's possible for nodes to support having child nodes (for example, some notification rows 29 * contain other notification rows). If so, they must implement all of the child-related methods 30 * below. 31 */ 32 interface NodeController { 33 34 /** A string that uniquely(ish) represents the node in the tree. Used for debugging. */ 35 val nodeLabel: String 36 37 val view: View 38 39 fun getChildAt(index: Int): View? { 40 throw RuntimeException("Not supported") 41 } 42 43 fun getChildCount(): Int = 0 44 45 /** Called to add a child to this view */ 46 fun addChildAt(child: NodeController, index: Int) { 47 throw RuntimeException("Not supported") 48 } 49 50 /** Called to move one of this view's current children to a new position */ 51 fun moveChildTo(child: NodeController, index: Int) { 52 throw RuntimeException("Not supported") 53 } 54 55 /** Called to remove one of this view's current children */ 56 fun removeChild(child: NodeController, isTransfer: Boolean) { 57 throw RuntimeException("Not supported") 58 } 59 60 /** Called when this view has been added */ 61 fun onViewAdded() {} 62 63 /** Called when this view has been moved */ 64 fun onViewMoved() {} 65 66 /** Called when this view has been removed */ 67 fun onViewRemoved() {} 68 69 /** 70 * Called before removing a node from its parent 71 * 72 * If returned true, the ShadeViewDiffer won't detach this row and the view system is 73 * responsible for ensuring the row is in eventually removed from the parent. 74 * 75 * @return false to opt out from this feature 76 */ 77 fun offerToKeepInParentForAnimation(): Boolean 78 79 /** 80 * Called before a node is reattached. Removes the view from its parent 81 * if it was flagged to be kept before. 82 * 83 * @return whether it did a removal 84 */ 85 fun removeFromParentIfKeptForAnimation(): Boolean 86 87 /** Called when a node is being reattached */ 88 fun resetKeepInParentForAnimation() 89 } 90 91 /** 92 * Used to specify the tree of [NodeController]s that currently make up the shade. 93 */ 94 interface NodeSpec { 95 val parent: NodeSpec? 96 val controller: NodeController 97 val children: List<NodeSpec> 98 } 99 100 class NodeSpecImpl( 101 override val parent: NodeSpec?, 102 override val controller: NodeController 103 ) : NodeSpec { 104 override val children = mutableListOf<NodeSpec>() 105 } 106 107 /** 108 * Converts a tree spec to human-readable string, for dumping purposes. 109 */ 110 fun treeSpecToStr(tree: NodeSpec): String { 111 return StringBuilder().also { treeSpecToStrHelper(tree, it, "") }.toString() 112 } 113 114 private fun treeSpecToStrHelper(tree: NodeSpec, sb: StringBuilder, indent: String) { 115 sb.append("$indent{${tree.controller.nodeLabel}}\n") 116 if (tree.children.isNotEmpty()) { 117 val childIndent = "$indent " 118 for (child in tree.children) { 119 treeSpecToStrHelper(child, sb, childIndent) 120 } 121 } 122 } 123