1 /* 2 * Copyright (C) 2015 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.phone; 18 19 import android.app.Notification; 20 import android.service.notification.StatusBarNotification; 21 22 import com.android.systemui.statusbar.ExpandableNotificationRow; 23 import com.android.systemui.statusbar.NotificationData; 24 import com.android.systemui.statusbar.StatusBarState; 25 26 import java.util.HashMap; 27 import java.util.HashSet; 28 29 /** 30 * A class to handle notifications and their corresponding groups. 31 */ 32 public class NotificationGroupManager { 33 34 private final HashMap<String, NotificationGroup> mGroupMap = new HashMap<>(); 35 private OnGroupChangeListener mListener; 36 private int mBarState = -1; 37 setOnGroupChangeListener(OnGroupChangeListener listener)38 public void setOnGroupChangeListener(OnGroupChangeListener listener) { 39 mListener = listener; 40 } 41 isGroupExpanded(StatusBarNotification sbn)42 public boolean isGroupExpanded(StatusBarNotification sbn) { 43 NotificationGroup group = mGroupMap.get(sbn.getGroupKey()); 44 if (group == null) { 45 return false; 46 } 47 return group.expanded; 48 } 49 setGroupExpanded(StatusBarNotification sbn, boolean expanded)50 public void setGroupExpanded(StatusBarNotification sbn, boolean expanded) { 51 NotificationGroup group = mGroupMap.get(sbn.getGroupKey()); 52 if (group == null) { 53 return; 54 } 55 setGroupExpanded(group, expanded); 56 } 57 setGroupExpanded(NotificationGroup group, boolean expanded)58 private void setGroupExpanded(NotificationGroup group, boolean expanded) { 59 group.expanded = expanded; 60 if (group.summary != null) { 61 mListener.onGroupExpansionChanged(group.summary.row, expanded); 62 } 63 } 64 onEntryRemoved(NotificationData.Entry removed)65 public void onEntryRemoved(NotificationData.Entry removed) { 66 onEntryRemovedInternal(removed, removed.notification); 67 } 68 69 /** 70 * An entry was removed. 71 * 72 * @param removed the removed entry 73 * @param sbn the notification the entry has, which doesn't need to be the same as it's internal 74 * notification 75 */ onEntryRemovedInternal(NotificationData.Entry removed, final StatusBarNotification sbn)76 private void onEntryRemovedInternal(NotificationData.Entry removed, 77 final StatusBarNotification sbn) { 78 Notification notif = sbn.getNotification(); 79 String groupKey = sbn.getGroupKey(); 80 final NotificationGroup group = mGroupMap.get(groupKey); 81 if (group == null) { 82 // When an app posts 2 different notifications as summary of the same group, then a 83 // cancellation of the first notification removes this group. 84 // This situation is not supported and we will not allow such notifications anymore in 85 // the close future. See b/23676310 for reference. 86 return; 87 } 88 if (notif.isGroupSummary()) { 89 group.summary = null; 90 } else { 91 group.children.remove(removed); 92 } 93 if (group.children.isEmpty()) { 94 if (group.summary == null) { 95 mGroupMap.remove(groupKey); 96 } else { 97 if (group.expanded) { 98 // only the summary is left. Change it to unexpanded in a few ms. We do this to 99 // avoid raceconditions 100 removed.row.post(new Runnable() { 101 @Override 102 public void run() { 103 if (group.children.isEmpty()) { 104 setGroupExpanded(sbn, false); 105 } 106 } 107 }); 108 } else { 109 group.summary.row.updateExpandButton(); 110 } 111 } 112 } 113 } 114 onEntryAdded(NotificationData.Entry added)115 public void onEntryAdded(NotificationData.Entry added) { 116 StatusBarNotification sbn = added.notification; 117 Notification notif = sbn.getNotification(); 118 String groupKey = sbn.getGroupKey(); 119 NotificationGroup group = mGroupMap.get(groupKey); 120 if (group == null) { 121 group = new NotificationGroup(); 122 mGroupMap.put(groupKey, group); 123 } 124 if (notif.isGroupSummary()) { 125 group.summary = added; 126 group.expanded = added.row.areChildrenExpanded(); 127 if (!group.children.isEmpty()) { 128 mListener.onGroupCreatedFromChildren(group); 129 } 130 } else { 131 group.children.add(added); 132 if (group.summary != null && group.children.size() == 1 && !group.expanded) { 133 group.summary.row.updateExpandButton(); 134 } 135 } 136 } 137 onEntryUpdated(NotificationData.Entry entry, StatusBarNotification oldNotification)138 public void onEntryUpdated(NotificationData.Entry entry, 139 StatusBarNotification oldNotification) { 140 if (mGroupMap.get(oldNotification.getGroupKey()) != null) { 141 onEntryRemovedInternal(entry, oldNotification); 142 } 143 onEntryAdded(entry); 144 } 145 isVisible(StatusBarNotification sbn)146 public boolean isVisible(StatusBarNotification sbn) { 147 if (!sbn.getNotification().isGroupChild()) { 148 return true; 149 } 150 NotificationGroup group = mGroupMap.get(sbn.getGroupKey()); 151 if (group != null && (group.expanded || group.summary == null)) { 152 return true; 153 } 154 return false; 155 } 156 hasGroupChildren(StatusBarNotification sbn)157 public boolean hasGroupChildren(StatusBarNotification sbn) { 158 if (areGroupsProhibited()) { 159 return false; 160 } 161 if (!sbn.getNotification().isGroupSummary()) { 162 return false; 163 } 164 NotificationGroup group = mGroupMap.get(sbn.getGroupKey()); 165 if (group == null) { 166 return false; 167 } 168 return !group.children.isEmpty(); 169 } 170 setStatusBarState(int newState)171 public void setStatusBarState(int newState) { 172 if (mBarState == newState) { 173 return; 174 } 175 boolean prohibitedBefore = areGroupsProhibited(); 176 mBarState = newState; 177 boolean nowProhibited = areGroupsProhibited(); 178 if (nowProhibited != prohibitedBefore) { 179 if (nowProhibited) { 180 for (NotificationGroup group : mGroupMap.values()) { 181 if (group.expanded) { 182 setGroupExpanded(group, false); 183 } 184 } 185 } 186 mListener.onGroupsProhibitedChanged(); 187 } 188 } 189 areGroupsProhibited()190 private boolean areGroupsProhibited() { 191 return mBarState == StatusBarState.KEYGUARD; 192 } 193 194 /** 195 * @return whether a given notification is a child in a group which has a summary 196 */ isChildInGroupWithSummary(StatusBarNotification sbn)197 public boolean isChildInGroupWithSummary(StatusBarNotification sbn) { 198 if (!sbn.getNotification().isGroupChild()) { 199 return false; 200 } 201 NotificationGroup group = mGroupMap.get(sbn.getGroupKey()); 202 if (group == null || group.summary == null) { 203 return false; 204 } 205 return true; 206 } 207 getGroupSummary(StatusBarNotification sbn)208 public ExpandableNotificationRow getGroupSummary(StatusBarNotification sbn) { 209 NotificationGroup group = mGroupMap.get(sbn.getGroupKey()); 210 return group == null ? null 211 : group.summary == null ? null 212 : group.summary.row; 213 } 214 215 public static class NotificationGroup { 216 public final HashSet<NotificationData.Entry> children = new HashSet<>(); 217 public NotificationData.Entry summary; 218 public boolean expanded; 219 } 220 221 public interface OnGroupChangeListener { 222 /** 223 * The expansion of a group has changed. 224 * 225 * @param changedRow the row for which the expansion has changed, which is also the summary 226 * @param expanded a boolean indicating the new expanded state 227 */ onGroupExpansionChanged(ExpandableNotificationRow changedRow, boolean expanded)228 void onGroupExpansionChanged(ExpandableNotificationRow changedRow, boolean expanded); 229 230 /** 231 * Children group policy has changed and children may no be prohibited or allowed. 232 */ onGroupsProhibitedChanged()233 void onGroupsProhibitedChanged(); 234 235 /** 236 * A group of children just received a summary notification and should therefore become 237 * children of it. 238 * 239 * @param group the group created 240 */ onGroupCreatedFromChildren(NotificationGroup group)241 void onGroupCreatedFromChildren(NotificationGroup group); 242 } 243 } 244