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