• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.IMPORTANCE_DEFAULT;
20 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
21 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST;
22 
23 import static org.junit.Assert.assertEquals;
24 import static org.junit.Assert.assertFalse;
25 import static org.junit.Assert.assertTrue;
26 import static org.mockito.ArgumentMatchers.any;
27 import static org.mockito.ArgumentMatchers.eq;
28 import static org.mockito.Mockito.spy;
29 import static org.mockito.Mockito.times;
30 import static org.mockito.Mockito.verify;
31 import static org.mockito.Mockito.when;
32 
33 import android.app.Notification;
34 import android.app.NotificationChannel;
35 import android.app.NotificationManager;
36 import android.testing.AndroidTestingRunner;
37 
38 import androidx.annotation.Nullable;
39 import androidx.test.filters.SmallTest;
40 
41 import com.android.systemui.SysuiTestCase;
42 import com.android.systemui.plugins.statusbar.StatusBarStateController;
43 import com.android.systemui.statusbar.RankingBuilder;
44 import com.android.systemui.statusbar.SbnBuilder;
45 import com.android.systemui.statusbar.notification.collection.ListEntry;
46 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
47 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
48 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
49 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
50 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
51 import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
52 import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider;
53 import com.android.systemui.statusbar.notification.collection.render.NodeController;
54 import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController;
55 
56 import org.junit.Before;
57 import org.junit.Test;
58 import org.junit.runner.RunWith;
59 import org.mockito.ArgumentCaptor;
60 import org.mockito.Captor;
61 import org.mockito.Mock;
62 import org.mockito.MockitoAnnotations;
63 
64 import java.util.ArrayList;
65 import java.util.Arrays;
66 
67 @SmallTest
68 @RunWith(AndroidTestingRunner.class)
69 public class RankingCoordinatorTest extends SysuiTestCase {
70 
71     @Mock private StatusBarStateController mStatusBarStateController;
72     @Mock private HighPriorityProvider mHighPriorityProvider;
73     @Mock private SectionStyleProvider mSectionStyleProvider;
74     @Mock private NotifPipeline mNotifPipeline;
75     @Mock private NodeController mAlertingHeaderController;
76     @Mock private NodeController mSilentNodeController;
77     @Mock private SectionHeaderController mSilentHeaderController;
78 
79     @Captor private ArgumentCaptor<NotifFilter> mNotifFilterCaptor;
80 
81     private NotificationEntry mEntry;
82     private NotifFilter mCapturedSuspendedFilter;
83     private NotifFilter mCapturedDozingFilter;
84     private RankingCoordinator mRankingCoordinator;
85 
86     private NotifSectioner mAlertingSectioner;
87     private NotifSectioner mSilentSectioner;
88     private NotifSectioner mMinimizedSectioner;
89     private ArrayList<NotifSectioner> mSections = new ArrayList<>(3);
90 
91     @Before
setup()92     public void setup() {
93         MockitoAnnotations.initMocks(this);
94         mRankingCoordinator = new RankingCoordinator(
95                 mStatusBarStateController,
96                 mHighPriorityProvider,
97                 mSectionStyleProvider,
98                 mAlertingHeaderController,
99                 mSilentHeaderController,
100                 mSilentNodeController);
101         mEntry = spy(new NotificationEntryBuilder().build());
102         mEntry.setRanking(getRankingForUnfilteredNotif().build());
103 
104         mRankingCoordinator.attach(mNotifPipeline);
105         verify(mSectionStyleProvider).setMinimizedSections(any());
106         verify(mNotifPipeline, times(2)).addPreGroupFilter(mNotifFilterCaptor.capture());
107         mCapturedSuspendedFilter = mNotifFilterCaptor.getAllValues().get(0);
108         mCapturedDozingFilter = mNotifFilterCaptor.getAllValues().get(1);
109 
110         mAlertingSectioner = mRankingCoordinator.getAlertingSectioner();
111         mSilentSectioner = mRankingCoordinator.getSilentSectioner();
112         mMinimizedSectioner = mRankingCoordinator.getMinimizedSectioner();
113         mSections.addAll(Arrays.asList(mAlertingSectioner, mSilentSectioner, mMinimizedSectioner));
114     }
115 
116     @Test
testSilentHeaderClearableChildrenUpdate()117     public void testSilentHeaderClearableChildrenUpdate() {
118         ListEntry listEntry = new ListEntry(mEntry.getKey(), 0L) {
119             @Nullable
120             @Override
121             public NotificationEntry getRepresentativeEntry() {
122                 return mEntry;
123             }
124         };
125         setRankingAmbient(false);
126         setSbnClearable(true);
127         mSilentSectioner.onEntriesUpdated(Arrays.asList(listEntry));
128         verify(mSilentHeaderController).setClearSectionEnabled(eq(true));
129 
130         setSbnClearable(false);
131         mSilentSectioner.onEntriesUpdated(Arrays.asList(listEntry));
132         verify(mSilentHeaderController).setClearSectionEnabled(eq(false));
133     }
134 
135     @Test
testUnfilteredState()136     public void testUnfilteredState() {
137         // GIVEN no suppressed visual effects + app not suspended
138         mEntry.setRanking(getRankingForUnfilteredNotif().build());
139 
140         // THEN don't filter out the notification
141         assertFalse(mCapturedSuspendedFilter.shouldFilterOut(mEntry, 0));
142     }
143 
144     @Test
filterSuspended()145     public void filterSuspended() {
146         // GIVEN the notification's app is suspended
147         mEntry.setRanking(getRankingForUnfilteredNotif()
148                 .setSuspended(true)
149                 .build());
150 
151         // THEN filter out the notification
152         assertTrue(mCapturedSuspendedFilter.shouldFilterOut(mEntry, 0));
153     }
154 
155     @Test
filterDozingSuppressAmbient()156     public void filterDozingSuppressAmbient() {
157         // GIVEN should suppress ambient
158         mEntry.setRanking(getRankingForUnfilteredNotif()
159                 .setSuppressedVisualEffects(SUPPRESSED_EFFECT_AMBIENT)
160                 .build());
161 
162         // WHEN it's dozing (on ambient display)
163         when(mStatusBarStateController.isDozing()).thenReturn(true);
164 
165         // THEN filter out the notification
166         assertTrue(mCapturedDozingFilter.shouldFilterOut(mEntry, 0));
167 
168         // WHEN it's not dozing (showing the notification list)
169         when(mStatusBarStateController.isDozing()).thenReturn(false);
170 
171         // THEN don't filter out the notification
172         assertFalse(mCapturedDozingFilter.shouldFilterOut(mEntry, 0));
173     }
174 
175     @Test
filterDozingSuppressNotificationList()176     public void filterDozingSuppressNotificationList() {
177         // GIVEN should suppress from the notification list
178         mEntry.setRanking(getRankingForUnfilteredNotif()
179                 .setSuppressedVisualEffects(SUPPRESSED_EFFECT_NOTIFICATION_LIST)
180                 .build());
181 
182         // WHEN it's dozing (on ambient display)
183         when(mStatusBarStateController.isDozing()).thenReturn(true);
184 
185         // THEN don't filter out the notification
186         assertFalse(mCapturedDozingFilter.shouldFilterOut(mEntry, 0));
187 
188         // WHEN it's not dozing (showing the notification list)
189         when(mStatusBarStateController.isDozing()).thenReturn(false);
190 
191         // THEN filter out the notification
192         assertTrue(mCapturedDozingFilter.shouldFilterOut(mEntry, 0));
193     }
194 
195     @Test
testIncludeInSectionAlerting()196     public void testIncludeInSectionAlerting() {
197         // GIVEN the entry is high priority
198         when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(true);
199 
200         // THEN entry is in the alerting section
201         assertTrue(mAlertingSectioner.isInSection(mEntry));
202         assertFalse(mSilentSectioner.isInSection(mEntry));
203     }
204 
205     @Test
testIncludeInSectionSilent()206     public void testIncludeInSectionSilent() {
207         // GIVEN the entry isn't high priority
208         when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false);
209         setRankingAmbient(false);
210 
211         // THEN entry is in the silent section
212         assertFalse(mAlertingSectioner.isInSection(mEntry));
213         assertTrue(mSilentSectioner.isInSection(mEntry));
214     }
215 
216     @Test
testMinSection()217     public void testMinSection() {
218         when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false);
219         setRankingAmbient(true);
220         assertInSection(mEntry, mMinimizedSectioner);
221     }
222 
223     @Test
testSilentSection()224     public void testSilentSection() {
225         when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false);
226         setRankingAmbient(false);
227         assertInSection(mEntry, mSilentSectioner);
228     }
229 
230     @Test
testClearableSilentSection()231     public void testClearableSilentSection() {
232         when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false);
233         setSbnClearable(true);
234         setRankingAmbient(false);
235         mSilentSectioner.onEntriesUpdated(Arrays.asList(mEntry));
236         verify(mSilentHeaderController).setClearSectionEnabled(eq(true));
237     }
238 
239     @Test
testClearableMinimizedSection()240     public void testClearableMinimizedSection() {
241         when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false);
242         setSbnClearable(true);
243         setRankingAmbient(true);
244         mMinimizedSectioner.onEntriesUpdated(Arrays.asList(mEntry));
245         verify(mSilentHeaderController).setClearSectionEnabled(eq(true));
246     }
247 
248     @Test
testNotClearableSilentSection()249     public void testNotClearableSilentSection() {
250         setSbnClearable(false);
251         when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false);
252         setRankingAmbient(false);
253         mSilentSectioner.onEntriesUpdated(Arrays.asList(mEntry));
254         mMinimizedSectioner.onEntriesUpdated(Arrays.asList(mEntry));
255         mAlertingSectioner.onEntriesUpdated(Arrays.asList(mEntry));
256         verify(mSilentHeaderController, times(2)).setClearSectionEnabled(eq(false));
257     }
258 
259     @Test
testNotClearableMinimizedSection()260     public void testNotClearableMinimizedSection() {
261         setSbnClearable(false);
262         when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false);
263         setRankingAmbient(true);
264         mSilentSectioner.onEntriesUpdated(Arrays.asList(mEntry));
265         mMinimizedSectioner.onEntriesUpdated(Arrays.asList(mEntry));
266         mAlertingSectioner.onEntriesUpdated(Arrays.asList(mEntry));
267         verify(mSilentHeaderController, times(2)).setClearSectionEnabled(eq(false));
268     }
269 
assertInSection(NotificationEntry entry, NotifSectioner section)270     private void assertInSection(NotificationEntry entry, NotifSectioner section) {
271         for (NotifSectioner current: mSections) {
272             if (current == section) {
273                 assertTrue(current.isInSection(entry));
274             } else {
275                 assertFalse(current.isInSection(entry));
276             }
277         }
278     }
279 
getRankingForUnfilteredNotif()280     private RankingBuilder getRankingForUnfilteredNotif() {
281         return new RankingBuilder(mEntry.getRanking())
282                 .setChannel(new NotificationChannel("id", null, IMPORTANCE_DEFAULT))
283                 .setSuppressedVisualEffects(0)
284                 .setSuspended(false);
285     }
286 
setSbnClearable(boolean clearable)287     private void setSbnClearable(boolean clearable) {
288         mEntry.setSbn(new SbnBuilder(mEntry.getSbn())
289                 .setFlag(mContext, Notification.FLAG_NO_CLEAR, !clearable)
290                 .build());
291         assertEquals(clearable, mEntry.getSbn().isClearable());
292     }
293 
setRankingAmbient(boolean ambient)294     private void setRankingAmbient(boolean ambient) {
295         mEntry.setRanking(new RankingBuilder(mEntry.getRanking())
296                 .setImportance(ambient
297                         ? NotificationManager.IMPORTANCE_MIN
298                         : IMPORTANCE_DEFAULT)
299                 .build());
300         assertEquals(ambient, mEntry.getRanking().isAmbient());
301     }
302 }
303