NotificationGroupManager.java revision e73ad216d322d0e7002d1ce2e59caf23030dbf5b
125fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek/* 225fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek * Copyright (C) 2015 The Android Open Source Project 325fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek * 425fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek * Licensed under the Apache License, Version 2.0 (the "License"); 525fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek * you may not use this file except in compliance with the License. 625fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek * You may obtain a copy of the License at 725fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek * 825fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek * http://www.apache.org/licenses/LICENSE-2.0 925fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek * 1025fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek * Unless required by applicable law or agreed to in writing, software 1125fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek * distributed under the License is distributed on an "AS IS" BASIS, 1225fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1325fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek * See the License for the specific language governing permissions and 1425fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek * limitations under the License 1525fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek */ 1625fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek 1725fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinekpackage com.android.systemui.statusbar.phone; 1825fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek 1925fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinekimport android.app.Notification; 2025fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinekimport android.service.notification.StatusBarNotification; 2125fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek 2225fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinekimport com.android.systemui.statusbar.ExpandableNotificationRow; 2325fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinekimport com.android.systemui.statusbar.NotificationData; 2425fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek 2525fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinekimport java.util.HashMap; 2625fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinekimport java.util.HashSet; 2725fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek 2825fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek/** 2925fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek * A class to handle notifications and their corresponding groups. 3025fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek */ 3125fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinekpublic class NotificationGroupManager { 3225fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek 3325fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek private final HashMap<String, NotificationGroup> mGroupMap = new HashMap<>(); 3425fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek private OnGroupChangeListener mListener; 3525fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek private int mBarState = -1; 3625fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek 3725fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek public void setOnGroupChangeListener(OnGroupChangeListener listener) { 3825fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek mListener = listener; 3925fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek } 4025fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek 4125fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek public boolean isGroupExpanded(StatusBarNotification sbn) { 4225fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek NotificationGroup group = mGroupMap.get(sbn.getGroupKey()); 4325fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek if (group == null) { 4425fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek return false; 4525fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek } 4625fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek return group.expanded; 4725fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek } 4825fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek 4925fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek public void setGroupExpanded(StatusBarNotification sbn, boolean expanded) { 5025fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek NotificationGroup group = mGroupMap.get(sbn.getGroupKey()); 5125fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek if (group == null) { 5225fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek return; 5325fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek } 5425fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek setGroupExpanded(group, expanded); 5525fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek } 5625fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek 5725fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek private void setGroupExpanded(NotificationGroup group, boolean expanded) { 5825fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek group.expanded = expanded; 5925fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek if (group.summary != null) { 6025fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek mListener.onGroupExpansionChanged(group.summary.row, expanded); 6125fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek } 6225fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek } 6325fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek 6425fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek public void onEntryRemoved(NotificationData.Entry removed) { 6525fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek onEntryRemovedInternal(removed, removed.notification); 6625fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek } 6725fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek 6825fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek /** 6925fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek * An entry was removed. 7025fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek * 7125fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek * @param removed the removed entry 7225fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek * @param sbn the notification the entry has, which doesn't need to be the same as it's internal 7325fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek * notification 7425fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek */ 7525fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek private void onEntryRemovedInternal(NotificationData.Entry removed, 7625fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek final StatusBarNotification sbn) { 7725fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek Notification notif = sbn.getNotification(); 7825fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek String groupKey = sbn.getGroupKey(); 7925fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek final NotificationGroup group = mGroupMap.get(groupKey); 800b4aeab281d0bd18e67f245eeccbbc468f3065f1Selim Cinek if (group == null) { 810b4aeab281d0bd18e67f245eeccbbc468f3065f1Selim Cinek // When an app posts 2 different notifications as summary of the same group, then a 820b4aeab281d0bd18e67f245eeccbbc468f3065f1Selim Cinek // cancellation of the first notification removes this group. 830b4aeab281d0bd18e67f245eeccbbc468f3065f1Selim Cinek // This situation is not supported and we will not allow such notifications anymore in 840b4aeab281d0bd18e67f245eeccbbc468f3065f1Selim Cinek // the close future. See b/23676310 for reference. 850b4aeab281d0bd18e67f245eeccbbc468f3065f1Selim Cinek return; 860b4aeab281d0bd18e67f245eeccbbc468f3065f1Selim Cinek } 87e73ad216d322d0e7002d1ce2e59caf23030dbf5bSelim Cinek if (notif.isGroupChild()) { 8825fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek group.children.remove(removed); 89e73ad216d322d0e7002d1ce2e59caf23030dbf5bSelim Cinek } else { 90e73ad216d322d0e7002d1ce2e59caf23030dbf5bSelim Cinek group.summary = null; 9125fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek } 9225fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek if (group.children.isEmpty()) { 9325fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek if (group.summary == null) { 9425fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek mGroupMap.remove(groupKey); 9583bc78338437a460076a4b5778ded38440ac3501Selim Cinek } else if (!group.expanded) { 9683bc78338437a460076a4b5778ded38440ac3501Selim Cinek group.summary.row.updateNotificationHeader(); 9725fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek } 9825fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek } 9925fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek } 10025fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek 10125fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek public void onEntryAdded(NotificationData.Entry added) { 10225fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek StatusBarNotification sbn = added.notification; 10325fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek Notification notif = sbn.getNotification(); 10425fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek String groupKey = sbn.getGroupKey(); 10525fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek NotificationGroup group = mGroupMap.get(groupKey); 10625fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek if (group == null) { 10725fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek group = new NotificationGroup(); 10825fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek mGroupMap.put(groupKey, group); 10925fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek } 110e73ad216d322d0e7002d1ce2e59caf23030dbf5bSelim Cinek if (notif.isGroupChild()) { 111e73ad216d322d0e7002d1ce2e59caf23030dbf5bSelim Cinek group.children.add(added); 112e73ad216d322d0e7002d1ce2e59caf23030dbf5bSelim Cinek if (group.summary != null && group.children.size() == 1 && !group.expanded) { 113e73ad216d322d0e7002d1ce2e59caf23030dbf5bSelim Cinek group.summary.row.updateNotificationHeader(); 114e73ad216d322d0e7002d1ce2e59caf23030dbf5bSelim Cinek } 115e73ad216d322d0e7002d1ce2e59caf23030dbf5bSelim Cinek } else { 11625fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek group.summary = added; 117b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek group.expanded = added.row.areChildrenExpanded(); 11825fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek if (!group.children.isEmpty()) { 11925fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek mListener.onGroupCreatedFromChildren(group); 12025fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek } 12125fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek } 12225fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek } 12325fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek 12425fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek public void onEntryUpdated(NotificationData.Entry entry, 12525fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek StatusBarNotification oldNotification) { 12625fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek if (mGroupMap.get(oldNotification.getGroupKey()) != null) { 12725fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek onEntryRemovedInternal(entry, oldNotification); 12825fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek } 12925fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek onEntryAdded(entry); 13025fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek } 13125fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek 13225fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek public boolean isVisible(StatusBarNotification sbn) { 13325fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek if (!sbn.getNotification().isGroupChild()) { 13425fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek return true; 13525fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek } 13625fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek NotificationGroup group = mGroupMap.get(sbn.getGroupKey()); 137acf52ba54d5d64f3dad3dc5c7f9275216512f226Selim Cinek if (group != null && (group.expanded || group.summary == null)) { 13825fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek return true; 13925fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek } 14025fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek return false; 14125fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek } 14225fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek 14325fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek public boolean hasGroupChildren(StatusBarNotification sbn) { 14425fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek if (!sbn.getNotification().isGroupSummary()) { 14525fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek return false; 14625fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek } 14725fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek NotificationGroup group = mGroupMap.get(sbn.getGroupKey()); 14825fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek if (group == null) { 14925fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek return false; 15025fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek } 15125fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek return !group.children.isEmpty(); 15225fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek } 15325fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek 15425fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek /** 15525fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek * @return whether a given notification is a child in a group which has a summary 15625fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek */ 15725fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek public boolean isChildInGroupWithSummary(StatusBarNotification sbn) { 15825fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek if (!sbn.getNotification().isGroupChild()) { 15925fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek return false; 16025fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek } 16125fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek NotificationGroup group = mGroupMap.get(sbn.getGroupKey()); 16225fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek if (group == null || group.summary == null) { 16325fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek return false; 16425fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek } 16525fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek return true; 16625fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek } 16725fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek 168263398f0175efc8bc8c965473f9565a693a0a0e0Selim Cinek /** 169263398f0175efc8bc8c965473f9565a693a0a0e0Selim Cinek * @return whether a given notification is a summary in a group which has children 170263398f0175efc8bc8c965473f9565a693a0a0e0Selim Cinek */ 171263398f0175efc8bc8c965473f9565a693a0a0e0Selim Cinek public boolean isSummaryOfGroup(StatusBarNotification sbn) { 172e73ad216d322d0e7002d1ce2e59caf23030dbf5bSelim Cinek if (!sbn.getNotification().isGroupSummary()) { 173263398f0175efc8bc8c965473f9565a693a0a0e0Selim Cinek return false; 174263398f0175efc8bc8c965473f9565a693a0a0e0Selim Cinek } 175263398f0175efc8bc8c965473f9565a693a0a0e0Selim Cinek NotificationGroup group = mGroupMap.get(sbn.getGroupKey()); 176263398f0175efc8bc8c965473f9565a693a0a0e0Selim Cinek if (group == null) { 177263398f0175efc8bc8c965473f9565a693a0a0e0Selim Cinek return false; 178263398f0175efc8bc8c965473f9565a693a0a0e0Selim Cinek } 179263398f0175efc8bc8c965473f9565a693a0a0e0Selim Cinek return !group.children.isEmpty(); 180263398f0175efc8bc8c965473f9565a693a0a0e0Selim Cinek } 181263398f0175efc8bc8c965473f9565a693a0a0e0Selim Cinek 18225fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek public ExpandableNotificationRow getGroupSummary(StatusBarNotification sbn) { 18325fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek NotificationGroup group = mGroupMap.get(sbn.getGroupKey()); 18425fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek return group == null ? null 18525fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek : group.summary == null ? null 18625fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek : group.summary.row; 18725fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek } 18825fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek 18983bc78338437a460076a4b5778ded38440ac3501Selim Cinek public void onEntryHeadsUped(NotificationData.Entry headsUp) { 19083bc78338437a460076a4b5778ded38440ac3501Selim Cinek // TODO: handle this nicely 19183bc78338437a460076a4b5778ded38440ac3501Selim Cinek } 19283bc78338437a460076a4b5778ded38440ac3501Selim Cinek 19383bc78338437a460076a4b5778ded38440ac3501Selim Cinek public void toggleGroupExpansion(StatusBarNotification sbn) { 19483bc78338437a460076a4b5778ded38440ac3501Selim Cinek NotificationGroup group = mGroupMap.get(sbn.getGroupKey()); 19583bc78338437a460076a4b5778ded38440ac3501Selim Cinek if (group == null) { 19683bc78338437a460076a4b5778ded38440ac3501Selim Cinek return; 19783bc78338437a460076a4b5778ded38440ac3501Selim Cinek } 19883bc78338437a460076a4b5778ded38440ac3501Selim Cinek setGroupExpanded(group, !group.expanded); 19983bc78338437a460076a4b5778ded38440ac3501Selim Cinek } 20083bc78338437a460076a4b5778ded38440ac3501Selim Cinek 20125fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek public static class NotificationGroup { 20225fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek public final HashSet<NotificationData.Entry> children = new HashSet<>(); 20325fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek public NotificationData.Entry summary; 20425fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek public boolean expanded; 20525fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek } 20625fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek 20725fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek public interface OnGroupChangeListener { 20825fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek /** 20925fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek * The expansion of a group has changed. 21025fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek * 21125fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek * @param changedRow the row for which the expansion has changed, which is also the summary 21225fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek * @param expanded a boolean indicating the new expanded state 21325fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek */ 21425fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek void onGroupExpansionChanged(ExpandableNotificationRow changedRow, boolean expanded); 21525fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek 21625fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek /** 21725fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek * A group of children just received a summary notification and should therefore become 21825fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek * children of it. 21925fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek * 22025fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek * @param group the group created 22125fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek */ 22225fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek void onGroupCreatedFromChildren(NotificationGroup group); 22325fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek } 22425fd4e2be731fe893685faa48828d8fa4526cb1aSelim Cinek} 225