1 /* 2 * Copyright (C) 2019 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.notification.collection; 18 19 import static android.app.Notification.CATEGORY_ALARM; 20 import static android.app.Notification.CATEGORY_CALL; 21 import static android.app.Notification.CATEGORY_EVENT; 22 import static android.app.Notification.CATEGORY_MESSAGE; 23 import static android.app.Notification.CATEGORY_REMINDER; 24 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT; 25 26 import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking; 27 import static com.android.systemui.statusbar.NotificationEntryHelper.modifySbn; 28 29 import static org.junit.Assert.assertEquals; 30 import static org.junit.Assert.assertFalse; 31 import static org.junit.Assert.assertTrue; 32 import static org.mockito.Mockito.mock; 33 34 import android.app.ActivityManager; 35 import android.app.Notification; 36 import android.app.NotificationChannel; 37 import android.app.PendingIntent; 38 import android.app.Person; 39 import android.content.Intent; 40 import android.graphics.drawable.Icon; 41 import android.media.session.MediaSession; 42 import android.os.Bundle; 43 import android.os.UserHandle; 44 import android.service.notification.NotificationListenerService.Ranking; 45 import android.service.notification.SnoozeCriterion; 46 import android.service.notification.StatusBarNotification; 47 import android.testing.AndroidTestingRunner; 48 49 import androidx.test.filters.SmallTest; 50 51 import com.android.systemui.R; 52 import com.android.systemui.SysuiTestCase; 53 import com.android.systemui.statusbar.RankingBuilder; 54 import com.android.systemui.statusbar.SbnBuilder; 55 import com.android.systemui.util.time.FakeSystemClock; 56 57 import org.junit.Before; 58 import org.junit.Test; 59 import org.junit.runner.RunWith; 60 61 import java.util.ArrayList; 62 63 @SmallTest 64 @RunWith(AndroidTestingRunner.class) 65 public class NotificationEntryTest extends SysuiTestCase { 66 private static final String TEST_PACKAGE_NAME = "test"; 67 private static final int TEST_UID = 0; 68 private static final int UID_NORMAL = 123; 69 private static final NotificationChannel NOTIFICATION_CHANNEL = 70 new NotificationChannel("id", "name", NotificationChannel.USER_LOCKED_IMPORTANCE); 71 72 private int mId; 73 74 private NotificationEntry mEntry; 75 private final FakeSystemClock mClock = new FakeSystemClock(); 76 77 @Before setup()78 public void setup() { 79 Notification.Builder n = new Notification.Builder(mContext, "") 80 .setSmallIcon(R.drawable.ic_person) 81 .setContentTitle("Title") 82 .setContentText("Text"); 83 84 mEntry = new NotificationEntryBuilder() 85 .setPkg(TEST_PACKAGE_NAME) 86 .setOpPkg(TEST_PACKAGE_NAME) 87 .setUid(TEST_UID) 88 .setId(mId++) 89 .setNotification(n.build()) 90 .setUser(new UserHandle(ActivityManager.getCurrentUser())) 91 .build(); 92 } 93 94 @Test testIsExemptFromDndVisualSuppression_foreground()95 public void testIsExemptFromDndVisualSuppression_foreground() { 96 mEntry.getSbn().getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE; 97 98 assertTrue(mEntry.isExemptFromDndVisualSuppression()); 99 assertFalse(mEntry.shouldSuppressAmbient()); 100 } 101 102 @Test testIsExemptFromDndVisualSuppression_media()103 public void testIsExemptFromDndVisualSuppression_media() { 104 Notification.Builder n = new Notification.Builder(mContext, "") 105 .setStyle(new Notification.MediaStyle() 106 .setMediaSession(mock(MediaSession.Token.class))) 107 .setSmallIcon(R.drawable.ic_person) 108 .setContentTitle("Title") 109 .setContentText("Text"); 110 NotificationEntry e1 = new NotificationEntryBuilder() 111 .setNotification(n.build()) 112 .build(); 113 114 assertTrue(e1.isExemptFromDndVisualSuppression()); 115 assertFalse(e1.shouldSuppressAmbient()); 116 } 117 118 @Test testIsExemptFromDndVisualSuppression_system()119 public void testIsExemptFromDndVisualSuppression_system() { 120 mEntry.mIsSystemNotification = true; 121 122 assertTrue(mEntry.isExemptFromDndVisualSuppression()); 123 assertFalse(mEntry.shouldSuppressAmbient()); 124 } 125 126 @Test testIsNotExemptFromDndVisualSuppression_hiddenCategories()127 public void testIsNotExemptFromDndVisualSuppression_hiddenCategories() { 128 NotificationEntry entry = new NotificationEntryBuilder() 129 .setUid(UID_NORMAL) 130 .build(); 131 entry.mIsSystemNotification = true; 132 modifyRanking(entry).setSuppressedVisualEffects(SUPPRESSED_EFFECT_AMBIENT).build(); 133 134 modifySbn(entry) 135 .setNotification( 136 new Notification.Builder(mContext, "").setCategory(CATEGORY_CALL).build()) 137 .build(); 138 assertFalse(entry.isExemptFromDndVisualSuppression()); 139 assertTrue(entry.shouldSuppressAmbient()); 140 141 modifySbn(entry) 142 .setNotification( 143 new Notification.Builder(mContext, "") 144 .setCategory(CATEGORY_REMINDER) 145 .build()) 146 .build(); 147 assertFalse(entry.isExemptFromDndVisualSuppression()); 148 149 modifySbn(entry) 150 .setNotification( 151 new Notification.Builder(mContext, "").setCategory(CATEGORY_ALARM).build()) 152 .build(); 153 assertFalse(entry.isExemptFromDndVisualSuppression()); 154 155 modifySbn(entry) 156 .setNotification( 157 new Notification.Builder(mContext, "").setCategory(CATEGORY_EVENT).build()) 158 .build(); 159 assertFalse(entry.isExemptFromDndVisualSuppression()); 160 161 modifySbn(entry) 162 .setNotification( 163 new Notification.Builder(mContext, "") 164 .setCategory(CATEGORY_MESSAGE) 165 .build()) 166 .build(); 167 assertFalse(entry.isExemptFromDndVisualSuppression()); 168 } 169 170 @Test testCreateNotificationDataEntry_RankingUpdate()171 public void testCreateNotificationDataEntry_RankingUpdate() { 172 StatusBarNotification sbn = new SbnBuilder().build(); 173 sbn.getNotification().actions = 174 new Notification.Action[]{createContextualAction("appGeneratedAction")}; 175 176 ArrayList<Notification.Action> systemGeneratedSmartActions = 177 createActions("systemGeneratedAction"); 178 179 SnoozeCriterion snoozeCriterion = new SnoozeCriterion("id", "explanation", "confirmation"); 180 ArrayList<SnoozeCriterion> snoozeCriterions = new ArrayList<>(); 181 snoozeCriterions.add(snoozeCriterion); 182 183 Ranking ranking = new RankingBuilder() 184 .setKey(sbn.getKey()) 185 .setSmartActions(systemGeneratedSmartActions) 186 .setChannel(NOTIFICATION_CHANNEL) 187 .setUserSentiment(Ranking.USER_SENTIMENT_NEGATIVE) 188 .setSnoozeCriteria(snoozeCriterions) 189 .build(); 190 191 NotificationEntry entry = 192 new NotificationEntry(sbn, ranking, mClock.uptimeMillis()); 193 194 assertEquals(systemGeneratedSmartActions, entry.getSmartActions()); 195 assertEquals(NOTIFICATION_CHANNEL, entry.getChannel()); 196 assertEquals(Ranking.USER_SENTIMENT_NEGATIVE, entry.getUserSentiment()); 197 assertEquals(snoozeCriterions, entry.getSnoozeCriteria()); 198 } 199 200 @Test notificationDataEntry_testIsLastMessageFromReply()201 public void notificationDataEntry_testIsLastMessageFromReply() { 202 Person.Builder person = new Person.Builder() 203 .setName("name") 204 .setKey("abc") 205 .setUri("uri") 206 .setBot(true); 207 208 // EXTRA_MESSAGING_PERSON is the same Person as the sender in last message in EXTRA_MESSAGES 209 Bundle bundle = new Bundle(); 210 bundle.putParcelable(Notification.EXTRA_MESSAGING_PERSON, person.build()); 211 Bundle[] messagesBundle = new Bundle[]{new Notification.MessagingStyle.Message( 212 "text", 0, person.build()).toBundle()}; 213 bundle.putParcelableArray(Notification.EXTRA_MESSAGES, messagesBundle); 214 215 Notification notification = new Notification.Builder(mContext, "test") 216 .addExtras(bundle) 217 .build(); 218 219 NotificationEntry entry = new NotificationEntryBuilder() 220 .setPkg("pkg") 221 .setOpPkg("pkg") 222 .setTag("tag") 223 .setNotification(notification) 224 .setUser(mContext.getUser()) 225 .setOverrideGroupKey("") 226 .build(); 227 entry.setHasSentReply(); 228 229 assertTrue(entry.isLastMessageFromReply()); 230 } 231 createContextualAction(String title)232 private Notification.Action createContextualAction(String title) { 233 return new Notification.Action.Builder( 234 Icon.createWithResource(getContext(), android.R.drawable.sym_def_app_icon), 235 title, 236 PendingIntent.getBroadcast(getContext(), 0, new Intent("Action"), 0)) 237 .setContextual(true) 238 .build(); 239 } 240 createAction(String title)241 private Notification.Action createAction(String title) { 242 return new Notification.Action.Builder( 243 Icon.createWithResource(getContext(), android.R.drawable.sym_def_app_icon), 244 title, 245 PendingIntent.getBroadcast(getContext(), 0, new Intent("Action"), 0)).build(); 246 } 247 createActions(String... titles)248 private ArrayList<Notification.Action> createActions(String... titles) { 249 ArrayList<Notification.Action> actions = new ArrayList<>(); 250 for (String title : titles) { 251 actions.add(createAction(title)); 252 } 253 return actions; 254 } 255 } 256