Home | History | Annotate | Download | only in phone
      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 
     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