1 /* 2 * Copyright (C) 2018 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 package com.android.car.notification; 17 18 import android.annotation.NonNull; 19 import android.annotation.Nullable; 20 import android.app.Notification; 21 import android.os.Bundle; 22 23 import java.util.ArrayList; 24 import java.util.List; 25 26 /** 27 * Data structure representing a notification card in car. 28 * A notification group can hold either: 29 * <ol> 30 * <li>One notification with no group summary notification</li> 31 * <li>One group summary notification with no child notifications</li> 32 * <li>A group of notifications with a group summary notification</li> 33 * </ol> 34 */ 35 public class NotificationGroup { 36 37 private String mGroupKey; 38 private final List<AlertEntry> mNotifications = new ArrayList<>(); 39 @Nullable 40 private List<String> mChildTitles; 41 @Nullable 42 private AlertEntry mGroupSummaryNotification; 43 44 private boolean mIsHeader; 45 private boolean mIsFooter; 46 NotificationGroup()47 public NotificationGroup() { 48 } 49 NotificationGroup(AlertEntry alertEntry)50 public NotificationGroup(AlertEntry alertEntry) { 51 addNotification(alertEntry); 52 } 53 addNotification(AlertEntry alertEntry)54 public void addNotification(AlertEntry alertEntry) { 55 assertSameGroupKey(alertEntry.getStatusBarNotification().getGroupKey()); 56 mNotifications.add(alertEntry); 57 } 58 setGroupSummaryNotification(AlertEntry groupSummaryNotification)59 void setGroupSummaryNotification(AlertEntry groupSummaryNotification) { 60 assertSameGroupKey(groupSummaryNotification.getStatusBarNotification().getGroupKey()); 61 mGroupSummaryNotification = groupSummaryNotification; 62 } 63 setGroupKey(@onNull String groupKey)64 void setGroupKey(@NonNull String groupKey) { 65 mGroupKey = groupKey; 66 } 67 68 /** 69 * Returns the group key of this notification group. 70 * 71 * <p> {@code null} will be returned if the group key has not been set yet. 72 */ 73 @Nullable getGroupKey()74 public String getGroupKey() { 75 return mGroupKey; 76 } 77 78 /** 79 * Returns the count of how many child notifications (excluding the group summary notification) 80 * this notification group has. 81 */ getChildCount()82 public int getChildCount() { 83 return mNotifications.size(); 84 } 85 86 /** 87 * Returns true when it has a group summary notification and >1 child notifications 88 */ isGroup()89 public boolean isGroup() { 90 return mGroupSummaryNotification != null && getChildCount() > 1; 91 } 92 93 /** 94 * Return true if the header is set to be displayed. 95 */ isHeader()96 public boolean isHeader() { 97 return mIsHeader; 98 } 99 100 /** 101 * Set this to true if a header needs to be displayed with a title and a clear all button. 102 */ setHeader(boolean header)103 public void setHeader(boolean header) { 104 mIsHeader = header; 105 } 106 107 /** 108 * Return true if the header is set to be displayed. 109 */ isFooter()110 public boolean isFooter() { 111 return mIsFooter; 112 } 113 114 /** 115 * Set this to true if a footer needs to be displayed with a clear all button. 116 */ setFooter(boolean footer)117 public void setFooter(boolean footer) { 118 mIsFooter = footer; 119 } 120 121 /** 122 * Returns true if this group is not a header or footer and all of the notifications it holds 123 * are dismissible by user action. 124 */ isDismissible()125 public boolean isDismissible() { 126 127 if (mIsHeader || mIsFooter) { 128 return false; 129 } 130 131 for (AlertEntry notification : mNotifications) { 132 boolean isForeground = 133 (notification.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE) 134 != 0; 135 if (isForeground || notification.getStatusBarNotification().isOngoing()) { 136 return false; 137 } 138 } 139 return true; 140 } 141 142 /** 143 * Returns the list of the child notifications. 144 */ getChildNotifications()145 public List<AlertEntry> getChildNotifications() { 146 return mNotifications; 147 } 148 149 /** 150 * Returns the group summary notification. 151 */ 152 @Nullable getGroupSummaryNotification()153 public AlertEntry getGroupSummaryNotification() { 154 return mGroupSummaryNotification; 155 } 156 157 /** 158 * Sets the list of child notification titles. 159 */ setChildTitles(List<String> childTitles)160 public void setChildTitles(List<String> childTitles) { 161 mChildTitles = childTitles; 162 } 163 164 /** 165 * Returns the list of child notification titles. 166 */ 167 @Nullable getChildTitles()168 public List<String> getChildTitles() { 169 return mChildTitles; 170 } 171 172 /** 173 * Generates the list of the child notification titles for a group summary notification. 174 */ generateChildTitles()175 public List<String> generateChildTitles() { 176 List<String> titles = new ArrayList<>(); 177 178 for (AlertEntry notification : mNotifications) { 179 Bundle extras = notification.getNotification().extras; 180 if (extras.containsKey(Notification.EXTRA_TITLE)) { 181 titles.add(extras.getString(Notification.EXTRA_TITLE)); 182 183 } else if (extras.containsKey(Notification.EXTRA_TITLE_BIG)) { 184 titles.add(extras.getString(Notification.EXTRA_TITLE_BIG)); 185 186 } else if (extras.containsKey(Notification.EXTRA_MESSAGES)) { 187 List<Notification.MessagingStyle.Message> messages = 188 Notification.MessagingStyle.Message.getMessagesFromBundleArray( 189 extras.getParcelableArray(Notification.EXTRA_MESSAGES)); 190 Notification.MessagingStyle.Message lastMessage = messages.get(messages.size() - 1); 191 titles.add(lastMessage.getSenderPerson().getName().toString()); 192 193 } else if (extras.containsKey(Notification.EXTRA_SUB_TEXT)) { 194 titles.add(extras.getString(Notification.EXTRA_SUB_TEXT)); 195 } 196 } 197 198 return titles; 199 } 200 201 /** 202 * Returns a single notification that represents this NotificationGroup: 203 * 204 * <p> If the NotificationGroup is a valid grouped notification or has no child notifications, 205 * the group summary notification is returned. 206 * 207 * <p> If the NotificationGroup has only 1 child notification, 208 * or has more than 1 child notifications without a valid group summary, 209 * the first child notification is returned. 210 * 211 * @return the notification that represents this NotificationGroup 212 */ getSingleNotification()213 public AlertEntry getSingleNotification() { 214 if (isGroup() || getChildCount() == 0) { 215 return getGroupSummaryNotification(); 216 217 } else { 218 return mNotifications.get(0); 219 } 220 } 221 getNotificationForSorting()222 AlertEntry getNotificationForSorting() { 223 if (mGroupSummaryNotification != null) { 224 return getGroupSummaryNotification(); 225 } 226 return getSingleNotification(); 227 } 228 assertSameGroupKey(String groupKey)229 private void assertSameGroupKey(String groupKey) { 230 if (mGroupKey == null) { 231 setGroupKey(groupKey); 232 } else if (!mGroupKey.equals(groupKey)) { 233 throw new IllegalStateException( 234 "Group key mismatch when adding a notification to a group. " + 235 "mGroupKey: " + mGroupKey + "; groupKey:" + groupKey); 236 } 237 } 238 } 239