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