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.coordinator;
18 
19 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
20 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST;
21 
22 import static org.junit.Assert.assertFalse;
23 import static org.junit.Assert.assertTrue;
24 import static org.mockito.ArgumentMatchers.eq;
25 import static org.mockito.Mockito.times;
26 import static org.mockito.Mockito.verify;
27 import static org.mockito.Mockito.when;
28 
29 import android.app.Notification;
30 import android.service.notification.StatusBarNotification;
31 import android.testing.AndroidTestingRunner;
32 
33 import androidx.annotation.Nullable;
34 import androidx.test.filters.SmallTest;
35 
36 import com.android.systemui.SysuiTestCase;
37 import com.android.systemui.plugins.statusbar.StatusBarStateController;
38 import com.android.systemui.statusbar.RankingBuilder;
39 import com.android.systemui.statusbar.notification.collection.ListEntry;
40 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
41 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
42 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
43 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
44 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
45 import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
46 import com.android.systemui.statusbar.notification.collection.render.NodeController;
47 import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController;
48 
49 import org.junit.Before;
50 import org.junit.Test;
51 import org.junit.runner.RunWith;
52 import org.mockito.ArgumentCaptor;
53 import org.mockito.Captor;
54 import org.mockito.Mock;
55 import org.mockito.Mockito;
56 import org.mockito.MockitoAnnotations;
57 
58 import java.util.Arrays;
59 
60 @SmallTest
61 @RunWith(AndroidTestingRunner.class)
62 public class RankingCoordinatorTest extends SysuiTestCase {
63 
64     @Mock private StatusBarStateController mStatusBarStateController;
65     @Mock private HighPriorityProvider mHighPriorityProvider;
66     @Mock private NotifPipeline mNotifPipeline;
67     @Mock private NodeController mAlertingHeaderController;
68     @Mock private NodeController mSilentNodeController;
69     @Mock private SectionHeaderController mSilentHeaderController;
70 
71     @Captor private ArgumentCaptor<NotifFilter> mNotifFilterCaptor;
72 
73     private NotificationEntry mEntry;
74     private NotifFilter mCapturedSuspendedFilter;
75     private NotifFilter mCapturedDozingFilter;
76 
77     private NotifSectioner mAlertingSectioner;
78     private NotifSectioner mSilentSectioner;
79 
80     @Before
setup()81     public void setup() {
82         MockitoAnnotations.initMocks(this);
83         RankingCoordinator rankingCoordinator = new RankingCoordinator(
84                 mStatusBarStateController, mHighPriorityProvider, mAlertingHeaderController,
85                 mSilentHeaderController, mSilentNodeController);
86         mEntry = new NotificationEntryBuilder().build();
87 
88         rankingCoordinator.attach(mNotifPipeline);
89         verify(mNotifPipeline, times(2)).addPreGroupFilter(mNotifFilterCaptor.capture());
90         mCapturedSuspendedFilter = mNotifFilterCaptor.getAllValues().get(0);
91         mCapturedDozingFilter = mNotifFilterCaptor.getAllValues().get(1);
92 
93         mAlertingSectioner = rankingCoordinator.getAlertingSectioner();
94         mSilentSectioner = rankingCoordinator.getSilentSectioner();
95     }
96 
97     @Test
testSilentHeaderClearableChildrenUpdate()98     public void testSilentHeaderClearableChildrenUpdate() {
99         StatusBarNotification sbn = Mockito.mock(StatusBarNotification.class);
100         Mockito.doReturn("key").when(sbn).getKey();
101         Mockito.doReturn(Mockito.mock(Notification.class)).when(sbn).getNotification();
102         NotificationEntry entry = new NotificationEntryBuilder().setSbn(sbn).build();
103         ListEntry listEntry = new ListEntry("key", 0L) {
104             @Nullable
105             @Override
106             public NotificationEntry getRepresentativeEntry() {
107                 return entry;
108             }
109         };
110         Mockito.doReturn(true).when(sbn).isClearable();
111         mSilentSectioner.onEntriesUpdated(Arrays.asList(listEntry));
112         verify(mSilentHeaderController).setClearSectionEnabled(eq(true));
113 
114         Mockito.doReturn(false).when(sbn).isClearable();
115         mSilentSectioner.onEntriesUpdated(Arrays.asList(listEntry));
116         verify(mSilentHeaderController).setClearSectionEnabled(eq(false));
117     }
118 
119     @Test
testUnfilteredState()120     public void testUnfilteredState() {
121         // GIVEN no suppressed visual effects + app not suspended
122         mEntry.setRanking(getRankingForUnfilteredNotif().build());
123 
124         // THEN don't filter out the notification
125         assertFalse(mCapturedSuspendedFilter.shouldFilterOut(mEntry, 0));
126     }
127 
128     @Test
filterSuspended()129     public void filterSuspended() {
130         // GIVEN the notification's app is suspended
131         mEntry.setRanking(getRankingForUnfilteredNotif()
132                 .setSuspended(true)
133                 .build());
134 
135         // THEN filter out the notification
136         assertTrue(mCapturedSuspendedFilter.shouldFilterOut(mEntry, 0));
137     }
138 
139     @Test
filterDozingSuppressAmbient()140     public void filterDozingSuppressAmbient() {
141         // GIVEN should suppress ambient
142         mEntry.setRanking(getRankingForUnfilteredNotif()
143                 .setSuppressedVisualEffects(SUPPRESSED_EFFECT_AMBIENT)
144                 .build());
145 
146         // WHEN it's dozing (on ambient display)
147         when(mStatusBarStateController.isDozing()).thenReturn(true);
148 
149         // THEN filter out the notification
150         assertTrue(mCapturedDozingFilter.shouldFilterOut(mEntry, 0));
151 
152         // WHEN it's not dozing (showing the notification list)
153         when(mStatusBarStateController.isDozing()).thenReturn(false);
154 
155         // THEN don't filter out the notification
156         assertFalse(mCapturedDozingFilter.shouldFilterOut(mEntry, 0));
157     }
158 
159     @Test
filterDozingSuppressNotificationList()160     public void filterDozingSuppressNotificationList() {
161         // GIVEN should suppress from the notification list
162         mEntry.setRanking(getRankingForUnfilteredNotif()
163                 .setSuppressedVisualEffects(SUPPRESSED_EFFECT_NOTIFICATION_LIST)
164                 .build());
165 
166         // WHEN it's dozing (on ambient display)
167         when(mStatusBarStateController.isDozing()).thenReturn(true);
168 
169         // THEN don't filter out the notification
170         assertFalse(mCapturedDozingFilter.shouldFilterOut(mEntry, 0));
171 
172         // WHEN it's not dozing (showing the notification list)
173         when(mStatusBarStateController.isDozing()).thenReturn(false);
174 
175         // THEN filter out the notification
176         assertTrue(mCapturedDozingFilter.shouldFilterOut(mEntry, 0));
177     }
178 
179     @Test
testIncludeInSectionAlerting()180     public void testIncludeInSectionAlerting() {
181         // GIVEN the entry is high priority
182         when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(true);
183 
184         // THEN entry is in the alerting section
185         assertTrue(mAlertingSectioner.isInSection(mEntry));
186         assertFalse(mSilentSectioner.isInSection(mEntry));
187     }
188 
189     @Test
testIncludeInSectionSilent()190     public void testIncludeInSectionSilent() {
191         // GIVEN the entry isn't high priority
192         when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false);
193 
194         // THEN entry is in the silent section
195         assertFalse(mAlertingSectioner.isInSection(mEntry));
196         assertTrue(mSilentSectioner.isInSection(mEntry));
197     }
198 
getRankingForUnfilteredNotif()199     private RankingBuilder getRankingForUnfilteredNotif() {
200         return new RankingBuilder()
201                 .setKey(mEntry.getKey())
202                 .setSuppressedVisualEffects(0)
203                 .setSuspended(false);
204     }
205 }
206