NotificationGroupManager.java revision 0b4aeab281d0bd18e67f245eeccbbc468f3065f1
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
17package com.android.systemui.statusbar.phone;
18
19import android.app.Notification;
20import android.service.notification.StatusBarNotification;
21
22import com.android.systemui.statusbar.ExpandableNotificationRow;
23import com.android.systemui.statusbar.NotificationData;
24import com.android.systemui.statusbar.StatusBarState;
25
26import java.util.HashMap;
27import java.util.HashSet;
28
29/**
30 * A class to handle notifications and their corresponding groups.
31 */
32public class NotificationGroupManager {
33
34    private final HashMap<String, NotificationGroup> mGroupMap = new HashMap<>();
35    private OnGroupChangeListener mListener;
36    private int mBarState = -1;
37
38    public void setOnGroupChangeListener(OnGroupChangeListener listener) {
39        mListener = listener;
40    }
41
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
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
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
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     */
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
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
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
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
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
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
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     */
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
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         */
228        void onGroupExpansionChanged(ExpandableNotificationRow changedRow, boolean expanded);
229
230        /**
231         * Children group policy has changed and children may no be prohibited or allowed.
232         */
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         */
241        void onGroupCreatedFromChildren(NotificationGroup group);
242    }
243}
244