1 /* 2 * Copyright (C) 2019 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; 18 19 import static com.android.systemui.statusbar.notification.NotificationUtils.logKey; 20 import static com.android.systemui.statusbar.notification.collection.NotifCollection.REASON_NOT_CANCELED; 21 import static com.android.systemui.statusbar.notification.collection.NotificationEntry.DismissState.NOT_DISMISSED; 22 23 import static java.util.Objects.requireNonNull; 24 25 import com.android.systemui.statusbar.NotificationInteractionTracker; 26 27 import java.util.Arrays; 28 import java.util.List; 29 30 /** 31 * Utility class for dumping the results of a {@link ShadeListBuilder} to a debug string. 32 */ 33 public class ListDumper { 34 35 /** 36 * Creates a debug string for a list of grouped notifications that will be printed 37 * in the order given in a tiered/tree structure. 38 * @param includeRecordKeeping whether to print out the Pluggables that caused the notification 39 * entry to be in its current state (ie: filter, lifeExtender) 40 */ dumpTree( List<ListEntry> entries, NotificationInteractionTracker interactionTracker, boolean includeRecordKeeping, String indent)41 public static String dumpTree( 42 List<ListEntry> entries, 43 NotificationInteractionTracker interactionTracker, 44 boolean includeRecordKeeping, 45 String indent) { 46 StringBuilder sb = new StringBuilder(); 47 final String childEntryIndent = indent + INDENT; 48 for (int topEntryIndex = 0; topEntryIndex < entries.size(); topEntryIndex++) { 49 ListEntry entry = entries.get(topEntryIndex); 50 dumpEntry(entry, 51 Integer.toString(topEntryIndex), 52 indent, 53 sb, 54 true, 55 includeRecordKeeping, 56 interactionTracker.hasUserInteractedWith(logKey(entry))); 57 if (entry instanceof GroupEntry) { 58 GroupEntry ge = (GroupEntry) entry; 59 NotificationEntry summary = ge.getSummary(); 60 if (summary != null) { 61 dumpEntry(summary, 62 topEntryIndex + ":*", 63 childEntryIndent, 64 sb, 65 true, 66 includeRecordKeeping, 67 interactionTracker.hasUserInteractedWith(logKey(summary))); 68 } 69 List<NotificationEntry> children = ge.getChildren(); 70 for (int childIndex = 0; childIndex < children.size(); childIndex++) { 71 NotificationEntry child = children.get(childIndex); 72 dumpEntry(child, 73 topEntryIndex + "." + childIndex, 74 childEntryIndent, 75 sb, 76 true, 77 includeRecordKeeping, 78 interactionTracker.hasUserInteractedWith(logKey(child))); 79 } 80 } 81 } 82 return sb.toString(); 83 } 84 85 /** 86 * Creates a debug string for a flat list of notifications 87 * @param includeRecordKeeping whether to print out the Pluggables that caused the notification 88 * entry to be in its current state (ie: filter, lifeExtender) 89 */ dumpList( List<NotificationEntry> entries, boolean includeRecordKeeping, String indent)90 public static String dumpList( 91 List<NotificationEntry> entries, 92 boolean includeRecordKeeping, 93 String indent) { 94 StringBuilder sb = new StringBuilder(); 95 for (int j = 0; j < entries.size(); j++) { 96 dumpEntry( 97 entries.get(j), 98 Integer.toString(j), 99 indent, 100 sb, 101 false, 102 includeRecordKeeping, 103 false); 104 } 105 return sb.toString(); 106 } 107 dumpEntry( ListEntry entry, String index, String indent, StringBuilder sb, boolean includeParent, boolean includeRecordKeeping, boolean hasBeenInteractedWith )108 private static void dumpEntry( 109 ListEntry entry, 110 String index, 111 String indent, 112 StringBuilder sb, 113 boolean includeParent, 114 boolean includeRecordKeeping, 115 boolean hasBeenInteractedWith 116 ) { 117 sb.append(indent) 118 .append("[").append(index).append("] ") 119 .append(index.length() == 1 ? " " : "") 120 .append(logKey(entry)); 121 122 if (includeParent) { 123 sb.append(" (parent=") 124 .append(logKey(entry.getParent())) 125 .append(")"); 126 127 NotificationEntry notifEntry = entry.getRepresentativeEntry(); 128 if (notifEntry != null) { 129 sb.append(" rank=") 130 .append(notifEntry.getRanking().getRank()); 131 } 132 } 133 134 if (entry.getSection() != null) { 135 sb.append(" section=") 136 .append(entry.getSection().getLabel()); 137 } 138 139 if (includeRecordKeeping) { 140 NotificationEntry notifEntry = requireNonNull(entry.getRepresentativeEntry()); 141 StringBuilder rksb = new StringBuilder(); 142 143 if (!notifEntry.mLifetimeExtenders.isEmpty()) { 144 String[] lifetimeExtenderNames = new String[notifEntry.mLifetimeExtenders.size()]; 145 for (int i = 0; i < lifetimeExtenderNames.length; i++) { 146 lifetimeExtenderNames[i] = notifEntry.mLifetimeExtenders.get(i).getName(); 147 } 148 rksb.append("lifetimeExtenders=") 149 .append(Arrays.toString(lifetimeExtenderNames)) 150 .append(" "); 151 } 152 153 if (!notifEntry.mDismissInterceptors.isEmpty()) { 154 String[] interceptorsNames = new String[notifEntry.mDismissInterceptors.size()]; 155 for (int i = 0; i < interceptorsNames.length; i++) { 156 interceptorsNames[i] = notifEntry.mDismissInterceptors.get(i).getName(); 157 } 158 rksb.append("dismissInterceptors=") 159 .append(Arrays.toString(interceptorsNames)) 160 .append(" "); 161 } 162 163 if (notifEntry.getExcludingFilter() != null) { 164 rksb.append("filter=") 165 .append(notifEntry.getExcludingFilter().getName()) 166 .append(" "); 167 } 168 169 if (notifEntry.getNotifPromoter() != null) { 170 rksb.append("promoter=") 171 .append(notifEntry.getNotifPromoter().getName()) 172 .append(" "); 173 } 174 175 if (notifEntry.mCancellationReason != REASON_NOT_CANCELED) { 176 rksb.append("cancellationReason=") 177 .append(notifEntry.mCancellationReason) 178 .append(" "); 179 } 180 181 if (notifEntry.getDismissState() != NOT_DISMISSED) { 182 rksb.append("dismissState=") 183 .append(notifEntry.getDismissState()) 184 .append(" "); 185 } 186 187 if (notifEntry.getAttachState().getSuppressedChanges().getParent() != null) { 188 rksb.append("suppressedParent=") 189 .append(logKey(notifEntry.getAttachState().getSuppressedChanges() 190 .getParent())) 191 .append(" "); 192 } 193 194 if (notifEntry.getAttachState().getSuppressedChanges().getSection() != null) { 195 rksb.append("suppressedSection=") 196 .append(notifEntry.getAttachState().getSuppressedChanges() 197 .getSection().getLabel()) 198 .append(" "); 199 } 200 201 if (hasBeenInteractedWith) { 202 rksb.append("interacted=yes "); 203 } 204 205 String rkString = rksb.toString(); 206 if (!rkString.isEmpty()) { 207 sb.append("\n\t") 208 .append(indent) 209 .append(rkString); 210 } 211 } 212 213 sb.append("\n"); 214 } 215 216 private static final String INDENT = " "; 217 } 218