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 
17 package com.android.settings.applications;
18 
19 import static android.text.format.DateUtils.DAY_IN_MILLIS;
20 
21 import static com.android.settings.applications.AppStateNotificationBridge.FILTER_APP_NOTIFICATION_BLOCKED;
22 import static com.android.settings.applications.AppStateNotificationBridge.FILTER_APP_NOTIFICATION_FREQUENCY;
23 import static com.android.settings.applications.AppStateNotificationBridge.FILTER_APP_NOTIFICATION_RECENCY;
24 import static com.android.settings.applications.AppStateNotificationBridge.FREQUENCY_NOTIFICATION_COMPARATOR;
25 import static com.android.settings.applications.AppStateNotificationBridge.RECENT_NOTIFICATION_COMPARATOR;
26 
27 import static com.google.common.truth.Truth.assertThat;
28 
29 import static org.junit.Assert.assertFalse;
30 import static org.junit.Assert.assertTrue;
31 import static org.mockito.ArgumentMatchers.any;
32 import static org.mockito.ArgumentMatchers.anyInt;
33 import static org.mockito.ArgumentMatchers.anyLong;
34 import static org.mockito.ArgumentMatchers.anyString;
35 import static org.mockito.ArgumentMatchers.eq;
36 import static org.mockito.Mockito.mock;
37 import static org.mockito.Mockito.verify;
38 import static org.mockito.Mockito.when;
39 
40 import android.app.usage.IUsageStatsManager;
41 import android.app.usage.UsageEvents;
42 import android.app.usage.UsageEvents.Event;
43 import android.content.Context;
44 import android.content.pm.ApplicationInfo;
45 import android.content.pm.UserInfo;
46 import android.os.Looper;
47 import android.os.Parcel;
48 import android.os.RemoteException;
49 import android.os.UserHandle;
50 import android.os.UserManager;
51 import android.widget.CompoundButton;
52 import android.widget.Switch;
53 
54 import com.android.settings.R;
55 import com.android.settings.applications.AppStateNotificationBridge.NotificationsSentState;
56 import com.android.settings.notification.NotificationBackend;
57 import com.android.settingslib.applications.ApplicationsState;
58 import com.android.settingslib.applications.ApplicationsState.AppEntry;
59 
60 import org.junit.Before;
61 import org.junit.Test;
62 import org.junit.runner.RunWith;
63 import org.mockito.Mock;
64 import org.mockito.MockitoAnnotations;
65 import org.robolectric.RobolectricTestRunner;
66 import org.robolectric.RuntimeEnvironment;
67 
68 import java.util.ArrayList;
69 import java.util.Arrays;
70 import java.util.List;
71 import java.util.Map;
72 
73 @RunWith(RobolectricTestRunner.class)
74 public class AppStateNotificationBridgeTest {
75 
76     private static String PKG1 = "pkg1";
77     private static String PKG2 = "pkg2";
78 
79     @Mock
80     private ApplicationsState.Session mSession;
81     @Mock
82     private ApplicationsState mState;
83     @Mock
84     private IUsageStatsManager mUsageStats;
85     @Mock
86     private UserManager mUserManager;
87     @Mock
88     private NotificationBackend mBackend;
89     private Context mContext;
90     private AppStateNotificationBridge mBridge;
91 
92     @Before
setUp()93     public void setUp() {
94         MockitoAnnotations.initMocks(this);
95         when(mState.newSession(any())).thenReturn(mSession);
96         when(mState.getBackgroundLooper()).thenReturn(mock(Looper.class));
97         when(mBackend.getNotificationsBanned(anyString(), anyInt())).thenReturn(true);
98         when(mBackend.enableSwitch(any(), any())).thenReturn(true);
99         // most tests assume no work profile
100         when(mUserManager.getProfileIdsWithDisabled(anyInt())).thenReturn(new int[]{});
101         mContext = RuntimeEnvironment.application.getApplicationContext();
102 
103         mBridge = new AppStateNotificationBridge(mContext, mState,
104                 mock(AppStateBaseBridge.Callback.class), mUsageStats, mUserManager, mBackend);
105     }
106 
getMockAppEntry(String pkg)107     private AppEntry getMockAppEntry(String pkg) {
108         AppEntry entry = mock(AppEntry.class);
109         entry.info = mock(ApplicationInfo.class);
110         entry.info.packageName = pkg;
111         return entry;
112     }
113 
getUsageEvents(List<Event> events)114     private UsageEvents getUsageEvents(List<Event> events) {
115         UsageEvents usageEvents = new UsageEvents(events, new String[] {PKG1, PKG2});
116         Parcel parcel = Parcel.obtain();
117         parcel.setDataPosition(0);
118         usageEvents.writeToParcel(parcel, 0);
119         parcel.setDataPosition(0);
120         return UsageEvents.CREATOR.createFromParcel(parcel);
121     }
122 
123     @Test
testGetAggregatedUsageEvents_noEvents()124     public void testGetAggregatedUsageEvents_noEvents() throws Exception {
125         when(mUsageStats.queryEventsForUser(anyLong(), anyLong(), anyInt(), anyString()))
126                 .thenReturn(mock(UsageEvents.class));
127 
128         assertThat(mBridge.getAggregatedUsageEvents()).isEmpty();
129     }
130 
131     @Test
testGetAggregatedUsageEvents_onlyNotificationEvents()132     public void testGetAggregatedUsageEvents_onlyNotificationEvents() throws Exception {
133         List<Event> events = new ArrayList<>();
134         Event good = new Event();
135         good.mEventType = Event.NOTIFICATION_INTERRUPTION;
136         good.mPackage = PKG1;
137         good.mTimeStamp = 1;
138         events.add(good);
139         Event bad = new Event();
140         bad.mEventType = Event.CHOOSER_ACTION;
141         bad.mPackage = PKG1;
142         bad.mTimeStamp = 2;
143         events.add(bad);
144 
145         UsageEvents usageEvents = getUsageEvents(events);
146         when(mUsageStats.queryEventsForUser(anyLong(), anyLong(), anyInt(), anyString()))
147                 .thenReturn(usageEvents);
148 
149         Map<String, NotificationsSentState> map = mBridge.getAggregatedUsageEvents();
150         assertThat(map.get(AppStateNotificationBridge.getKey(0, PKG1)).sentCount).isEqualTo(1);
151     }
152 
153     @Test
testGetAggregatedUsageEvents_multipleEventsAgg()154     public void testGetAggregatedUsageEvents_multipleEventsAgg() throws Exception {
155         List<Event> events = new ArrayList<>();
156         Event good = new Event();
157         good.mEventType = Event.NOTIFICATION_INTERRUPTION;
158         good.mPackage = PKG1;
159         good.mTimeStamp = 6;
160         events.add(good);
161         Event good1 = new Event();
162         good1.mEventType = Event.NOTIFICATION_INTERRUPTION;
163         good1.mPackage = PKG1;
164         good1.mTimeStamp = 1;
165         events.add(good1);
166 
167         UsageEvents usageEvents = getUsageEvents(events);
168         when(mUsageStats.queryEventsForUser(anyLong(), anyLong(), anyInt(), anyString()))
169                 .thenReturn(usageEvents);
170 
171         Map<String, NotificationsSentState> map  = mBridge.getAggregatedUsageEvents();
172         assertThat(map.get(AppStateNotificationBridge.getKey(0, PKG1)).sentCount).isEqualTo(2);
173         assertThat(map.get(AppStateNotificationBridge.getKey(0, PKG1)).lastSent).isEqualTo(6);
174     }
175 
176     @Test
testGetAggregatedUsageEvents_multiplePkgs()177     public void testGetAggregatedUsageEvents_multiplePkgs() throws Exception {
178         List<Event> events = new ArrayList<>();
179         Event good = new Event();
180         good.mEventType = Event.NOTIFICATION_INTERRUPTION;
181         good.mPackage = PKG1;
182         good.mTimeStamp = 6;
183         events.add(good);
184         Event good1 = new Event();
185         good1.mEventType = Event.NOTIFICATION_INTERRUPTION;
186         good1.mPackage = PKG2;
187         good1.mTimeStamp = 1;
188         events.add(good1);
189 
190         UsageEvents usageEvents = getUsageEvents(events);
191         when(mUsageStats.queryEventsForUser(anyLong(), anyLong(), anyInt(), anyString()))
192                 .thenReturn(usageEvents);
193 
194         Map<String, NotificationsSentState> map
195                 = mBridge.getAggregatedUsageEvents();
196         assertThat(map.get(AppStateNotificationBridge.getKey(0, PKG1)).sentCount).isEqualTo(1);
197         assertThat(map.get(AppStateNotificationBridge.getKey(0, PKG2)).sentCount).isEqualTo(1);
198         assertThat(map.get(AppStateNotificationBridge.getKey(0, PKG1)).lastSent).isEqualTo(6);
199         assertThat(map.get(AppStateNotificationBridge.getKey(0, PKG2)).lastSent).isEqualTo(1);
200     }
201 
202     @Test
testLoadAllExtraInfo_noEvents()203     public void testLoadAllExtraInfo_noEvents() throws RemoteException {
204         when(mUsageStats.queryEventsForUser(anyLong(), anyLong(), anyInt(), anyString()))
205                 .thenReturn(mock(UsageEvents.class));
206         ArrayList<AppEntry> apps = new ArrayList<>();
207         apps.add(getMockAppEntry(PKG1));
208         when(mSession.getAllApps()).thenReturn(apps);
209 
210         mBridge.loadAllExtraInfo();
211         // extra info should exist and blocked status should be populated
212         assertThat(apps.get(0).extraInfo).isNotNull();
213         verify(mBackend).getNotificationsBanned(PKG1, 0);
214         // but the recent/frequent counts should be 0 so they don't appear on those screens
215         assertThat(((NotificationsSentState) apps.get(0).extraInfo).avgSentDaily).isEqualTo(0);
216         assertThat(((NotificationsSentState) apps.get(0).extraInfo).lastSent).isEqualTo(0);
217     }
218 
219     @Test
testLoadAllExtraInfo_multipleEventsAgg()220     public void testLoadAllExtraInfo_multipleEventsAgg() throws RemoteException {
221         List<Event> events = new ArrayList<>();
222         for (int i = 0; i < 7; i++) {
223             Event good = new Event();
224             good.mEventType = Event.NOTIFICATION_INTERRUPTION;
225             good.mPackage = PKG1;
226             good.mTimeStamp = i;
227             events.add(good);
228         }
229 
230         UsageEvents usageEvents = getUsageEvents(events);
231         when(mUsageStats.queryEventsForUser(anyLong(), anyLong(), anyInt(), anyString()))
232                 .thenReturn(usageEvents);
233 
234         ArrayList<AppEntry> apps = new ArrayList<>();
235         apps.add(getMockAppEntry(PKG1));
236         when(mSession.getAllApps()).thenReturn(apps);
237 
238         mBridge.loadAllExtraInfo();
239         assertThat(((NotificationsSentState) apps.get(0).extraInfo).sentCount).isEqualTo(7);
240         assertThat(((NotificationsSentState) apps.get(0).extraInfo).lastSent).isEqualTo(6);
241         assertThat(((NotificationsSentState) apps.get(0).extraInfo).avgSentDaily).isEqualTo(1);
242         assertThat(((NotificationsSentState) apps.get(0).extraInfo).avgSentWeekly).isEqualTo(0);
243         assertThat(((NotificationsSentState) apps.get(0).extraInfo).blocked).isTrue();
244         assertThat(((NotificationsSentState) apps.get(0).extraInfo).blockable).isTrue();
245     }
246 
247     @Test
testLoadAllExtraInfo_multiplePkgs()248     public void testLoadAllExtraInfo_multiplePkgs() throws RemoteException {
249         List<Event> events = new ArrayList<>();
250         for (int i = 0; i < 8; i++) {
251             Event good = new Event();
252             good.mEventType = Event.NOTIFICATION_INTERRUPTION;
253             good.mPackage = PKG1;
254             good.mTimeStamp = i;
255             events.add(good);
256         }
257         Event good1 = new Event();
258         good1.mEventType = Event.NOTIFICATION_INTERRUPTION;
259         good1.mPackage = PKG2;
260         good1.mTimeStamp = 1;
261         events.add(good1);
262 
263         UsageEvents usageEvents = getUsageEvents(events);
264         when(mUsageStats.queryEventsForUser(anyLong(), anyLong(), anyInt(), anyString()))
265                 .thenReturn(usageEvents);
266 
267         ArrayList<AppEntry> apps = new ArrayList<>();
268         apps.add(getMockAppEntry(PKG1));
269         apps.add(getMockAppEntry(PKG2));
270         when(mSession.getAllApps()).thenReturn(apps);
271 
272         mBridge.loadAllExtraInfo();
273         assertThat(((NotificationsSentState) apps.get(0).extraInfo).sentCount).isEqualTo(8);
274         assertThat(((NotificationsSentState) apps.get(0).extraInfo).lastSent).isEqualTo(7);
275         assertThat(((NotificationsSentState) apps.get(0).extraInfo).avgSentWeekly).isEqualTo(0);
276         assertThat(((NotificationsSentState) apps.get(0).extraInfo).avgSentDaily).isEqualTo(1);
277 
278         assertThat(((NotificationsSentState) apps.get(1).extraInfo).sentCount).isEqualTo(1);
279         assertThat(((NotificationsSentState) apps.get(1).extraInfo).lastSent).isEqualTo(1);
280         assertThat(((NotificationsSentState) apps.get(1).extraInfo).avgSentWeekly).isEqualTo(1);
281         assertThat(((NotificationsSentState) apps.get(1).extraInfo).avgSentDaily).isEqualTo(0);
282     }
283 
284     @Test
testLoadAllExtraInfo_multipleUsers()285     public void testLoadAllExtraInfo_multipleUsers() throws RemoteException {
286         // has work profile
287         when(mUserManager.getProfiles(anyInt())).thenReturn(Arrays.asList(
288                 new UserInfo(1, "", UserInfo.FLAG_MANAGED_PROFILE | UserInfo.FLAG_PROFILE)));
289         mBridge = new AppStateNotificationBridge(mContext, mState,
290                 mock(AppStateBaseBridge.Callback.class), mUsageStats, mUserManager, mBackend);
291 
292         List<Event> eventsProfileOwner = new ArrayList<>();
293         for (int i = 0; i < 8; i++) {
294             Event good = new Event();
295             good.mEventType = Event.NOTIFICATION_INTERRUPTION;
296             good.mPackage = PKG1;
297             good.mTimeStamp = i;
298             eventsProfileOwner.add(good);
299         }
300 
301         List<Event> eventsProfile = new ArrayList<>();
302         for (int i = 0; i < 4; i++) {
303             Event good = new Event();
304             good.mEventType = Event.NOTIFICATION_INTERRUPTION;
305             good.mPackage = PKG1;
306             good.mTimeStamp = i;
307             eventsProfile.add(good);
308         }
309 
310         UsageEvents usageEventsOwner = getUsageEvents(eventsProfileOwner);
311         when(mUsageStats.queryEventsForUser(anyLong(), anyLong(), eq(0), anyString()))
312                 .thenReturn(usageEventsOwner);
313 
314         UsageEvents usageEventsProfile = getUsageEvents(eventsProfile);
315         when(mUsageStats.queryEventsForUser(anyLong(), anyLong(), eq(1), anyString()))
316                 .thenReturn(usageEventsProfile);
317 
318         ArrayList<AppEntry> apps = new ArrayList<>();
319         AppEntry owner = getMockAppEntry(PKG1);
320         owner.info.uid = 1;
321         apps.add(owner);
322 
323         AppEntry profile = getMockAppEntry(PKG1);
324         profile.info.uid = UserHandle.PER_USER_RANGE + 1;
325         apps.add(profile);
326         when(mSession.getAllApps()).thenReturn(apps);
327 
328         mBridge.loadAllExtraInfo();
329 
330         assertThat(((NotificationsSentState) apps.get(0).extraInfo).sentCount).isEqualTo(8);
331         assertThat(((NotificationsSentState) apps.get(0).extraInfo).lastSent).isEqualTo(7);
332         assertThat(((NotificationsSentState) apps.get(0).extraInfo).avgSentWeekly).isEqualTo(0);
333         assertThat(((NotificationsSentState) apps.get(0).extraInfo).avgSentDaily).isEqualTo(1);
334 
335         assertThat(((NotificationsSentState) apps.get(1).extraInfo).sentCount).isEqualTo(4);
336         assertThat(((NotificationsSentState) apps.get(1).extraInfo).lastSent).isEqualTo(3);
337         assertThat(((NotificationsSentState) apps.get(1).extraInfo).avgSentWeekly).isEqualTo(4);
338         assertThat(((NotificationsSentState) apps.get(1).extraInfo).avgSentDaily).isEqualTo(1);
339     }
340 
341     @Test
testUpdateExtraInfo_noEvents()342     public void testUpdateExtraInfo_noEvents() throws RemoteException {
343         when(mUsageStats.queryEventsForPackageForUser(
344                 anyLong(), anyLong(), anyInt(), anyString(), anyString()))
345                 .thenReturn(mock(UsageEvents.class));
346         AppEntry entry = getMockAppEntry(PKG1);
347 
348         mBridge.updateExtraInfo(entry, "", 0);
349         assertThat(entry.extraInfo).isNull();
350     }
351 
352     @Test
testUpdateExtraInfo_multipleEventsAgg()353     public void testUpdateExtraInfo_multipleEventsAgg() throws RemoteException {
354         List<Event> events = new ArrayList<>();
355         for (int i = 0; i < 13; i++) {
356             Event good = new Event();
357             good.mEventType = Event.NOTIFICATION_INTERRUPTION;
358             good.mPackage = PKG1;
359             good.mTimeStamp = i;
360             events.add(good);
361         }
362 
363         UsageEvents usageEvents = getUsageEvents(events);
364         when(mUsageStats.queryEventsForPackageForUser(
365                 anyLong(), anyLong(), anyInt(), anyString(), anyString())).thenReturn(usageEvents);
366 
367         AppEntry entry = getMockAppEntry(PKG1);
368         mBridge.updateExtraInfo(entry, "", 0);
369 
370         assertThat(((NotificationsSentState) entry.extraInfo).sentCount).isEqualTo(13);
371         assertThat(((NotificationsSentState) entry.extraInfo).lastSent).isEqualTo(12);
372         assertThat(((NotificationsSentState) entry.extraInfo).avgSentDaily).isEqualTo(2);
373         assertThat(((NotificationsSentState) entry.extraInfo).avgSentWeekly).isEqualTo(0);
374         assertThat(((NotificationsSentState) entry.extraInfo).blocked).isTrue();
375         assertThat(((NotificationsSentState) entry.extraInfo).blockable).isTrue();
376     }
377 
378     @Test
testSummary_recency()379     public void testSummary_recency() {
380         NotificationsSentState neverSent = new NotificationsSentState();
381         NotificationsSentState sent = new NotificationsSentState();
382         sent.lastSent = System.currentTimeMillis() - (2 * DAY_IN_MILLIS);
383 
384         assertThat(AppStateNotificationBridge.getSummary(
385                 mContext, neverSent, R.id.sort_order_recent_notification)).isEqualTo(
386                         mContext.getString(R.string.notifications_sent_never));
387         assertThat(AppStateNotificationBridge.getSummary(
388                 mContext, sent, R.id.sort_order_recent_notification).toString()).contains("2");
389     }
390 
391     @Test
testSummary_frequency()392     public void testSummary_frequency() {
393         NotificationsSentState sentRarely = new NotificationsSentState();
394         sentRarely.avgSentWeekly = 1;
395         NotificationsSentState sentOften = new NotificationsSentState();
396         sentOften.avgSentDaily = 8;
397 
398         assertThat(AppStateNotificationBridge.getSummary(
399                 mContext, sentRarely, R.id.sort_order_frequent_notification).toString())
400                 .contains("1");
401         assertThat(AppStateNotificationBridge.getSummary(
402                 mContext, sentRarely, R.id.sort_order_frequent_notification).toString())
403                 .contains("notification ");
404         assertThat(AppStateNotificationBridge.getSummary(
405                 mContext, sentRarely, R.id.sort_order_frequent_notification).toString())
406                 .contains("week");
407         assertThat(AppStateNotificationBridge.getSummary(
408                 mContext, sentOften, R.id.sort_order_frequent_notification).toString())
409                 .contains("8");
410         assertThat(AppStateNotificationBridge.getSummary(
411                 mContext, sentOften, R.id.sort_order_frequent_notification).toString())
412                 .contains("notifications");
413         assertThat(AppStateNotificationBridge.getSummary(
414                 mContext, sentOften, R.id.sort_order_frequent_notification).toString())
415                 .contains("day");
416     }
417 
418     @Test
testSummary_alpha()419     public void testSummary_alpha() {
420         NotificationsSentState sentRarely = new NotificationsSentState();
421         sentRarely.avgSentWeekly = 1;
422         assertThat(AppStateNotificationBridge.getSummary(
423                 mContext, sentRarely, R.id.sort_order_alpha).toString())
424                 .isEqualTo("");
425     }
426 
427     @Test
testFilterRecency()428     public void testFilterRecency() {
429         NotificationsSentState allowState = new NotificationsSentState();
430         allowState.lastSent = 1;
431         AppEntry allow = mock(AppEntry.class);
432         allow.extraInfo = allowState;
433 
434 
435         assertTrue(FILTER_APP_NOTIFICATION_RECENCY.filterApp(allow));
436 
437         NotificationsSentState denyState = new NotificationsSentState();
438         denyState.lastSent = 0;
439         AppEntry deny = mock(AppEntry.class);
440         deny.extraInfo = denyState;
441 
442         assertFalse(FILTER_APP_NOTIFICATION_RECENCY.filterApp(deny));
443     }
444 
445     @Test
testFilterFrequency()446     public void testFilterFrequency() {
447         NotificationsSentState allowState = new NotificationsSentState();
448         allowState.sentCount = 1;
449         AppEntry allow = mock(AppEntry.class);
450         allow.extraInfo = allowState;
451 
452         assertTrue(FILTER_APP_NOTIFICATION_FREQUENCY.filterApp(allow));
453 
454         NotificationsSentState denyState = new NotificationsSentState();
455         denyState.sentCount = 0;
456         AppEntry deny = mock(AppEntry.class);
457         deny.extraInfo = denyState;
458 
459         assertFalse(FILTER_APP_NOTIFICATION_FREQUENCY.filterApp(deny));
460     }
461 
462     @Test
testFilterBlocked()463     public void testFilterBlocked() {
464         NotificationsSentState allowState = new NotificationsSentState();
465         allowState.blocked = true;
466         AppEntry allow = mock(AppEntry.class);
467         allow.extraInfo = allowState;
468 
469         assertTrue(FILTER_APP_NOTIFICATION_BLOCKED.filterApp(allow));
470 
471         NotificationsSentState denyState = new NotificationsSentState();
472         denyState.blocked = false;
473         AppEntry deny = mock(AppEntry.class);
474         deny.extraInfo = denyState;
475 
476         assertFalse(FILTER_APP_NOTIFICATION_BLOCKED.filterApp(deny));
477     }
478 
479     @Test
testComparators_nullsNoCrash()480     public void testComparators_nullsNoCrash() {
481         List<AppEntry> entries = new ArrayList<>();
482         AppEntry a = mock(AppEntry.class);
483         a.label = "1";
484         AppEntry b = mock(AppEntry.class);
485         b.label = "2";
486         entries.add(a);
487         entries.add(b);
488 
489         entries.sort(RECENT_NOTIFICATION_COMPARATOR);
490         entries.sort(FREQUENCY_NOTIFICATION_COMPARATOR);
491     }
492 
493     @Test
testRecencyComparator()494     public void testRecencyComparator() {
495         List<AppEntry> entries = new ArrayList<>();
496 
497         NotificationsSentState earlier = new NotificationsSentState();
498         earlier.lastSent = 1;
499         AppEntry earlyEntry = mock(AppEntry.class);
500         earlyEntry.extraInfo = earlier;
501         entries.add(earlyEntry);
502 
503         NotificationsSentState later = new NotificationsSentState();
504         later.lastSent = 8;
505         AppEntry lateEntry = mock(AppEntry.class);
506         lateEntry.extraInfo = later;
507         entries.add(lateEntry);
508 
509         entries.sort(RECENT_NOTIFICATION_COMPARATOR);
510 
511         assertThat(entries).containsExactly(lateEntry, earlyEntry);
512     }
513 
514     @Test
testFrequencyComparator()515     public void testFrequencyComparator() {
516         List<AppEntry> entries = new ArrayList<>();
517 
518         NotificationsSentState notFrequentWeekly = new NotificationsSentState();
519         notFrequentWeekly.sentCount = 2;
520         AppEntry notFrequentWeeklyEntry = mock(AppEntry.class);
521         notFrequentWeeklyEntry.extraInfo = notFrequentWeekly;
522         entries.add(notFrequentWeeklyEntry);
523 
524         NotificationsSentState notFrequentDaily = new NotificationsSentState();
525         notFrequentDaily.sentCount = 7;
526         AppEntry notFrequentDailyEntry = mock(AppEntry.class);
527         notFrequentDailyEntry.extraInfo = notFrequentDaily;
528         entries.add(notFrequentDailyEntry);
529 
530         NotificationsSentState veryFrequentWeekly = new NotificationsSentState();
531         veryFrequentWeekly.sentCount = 6;
532         AppEntry veryFrequentWeeklyEntry = mock(AppEntry.class);
533         veryFrequentWeeklyEntry.extraInfo = veryFrequentWeekly;
534         entries.add(veryFrequentWeeklyEntry);
535 
536         NotificationsSentState veryFrequentDaily = new NotificationsSentState();
537         veryFrequentDaily.sentCount = 19;
538         AppEntry veryFrequentDailyEntry = mock(AppEntry.class);
539         veryFrequentDailyEntry.extraInfo = veryFrequentDaily;
540         entries.add(veryFrequentDailyEntry);
541 
542         entries.sort(FREQUENCY_NOTIFICATION_COMPARATOR);
543 
544         assertThat(entries).containsExactly(veryFrequentDailyEntry, notFrequentDailyEntry,
545                 veryFrequentWeeklyEntry, notFrequentWeeklyEntry);
546     }
547 
548     @Test
testSwitchOnChangeListener()549     public void testSwitchOnChangeListener() {
550         Switch toggle = mock(Switch.class);
551         when(toggle.isChecked()).thenReturn(true);
552         when(toggle.isEnabled()).thenReturn(true);
553 
554         AppEntry entry = mock(AppEntry.class);
555         entry.info = new ApplicationInfo();
556         entry.info.packageName = "pkg";
557         entry.info.uid = 1356;
558         entry.extraInfo = new NotificationsSentState();
559 
560         CompoundButton.OnCheckedChangeListener listener = mBridge.getSwitchOnCheckedListener(entry);
561         listener.onCheckedChanged(toggle, false);
562 
563         verify(mBackend).setNotificationsEnabledForPackage(
564                 entry.info.packageName, entry.info.uid, false);
565         assertThat(((NotificationsSentState) entry.extraInfo).blocked).isTrue();
566     }
567 
568     @Test
testSwitchViews_nullDoesNotCrash()569     public void testSwitchViews_nullDoesNotCrash() {
570         AppStateNotificationBridge.enableSwitch(null);
571         AppStateNotificationBridge.checkSwitch(null);
572     }
573 }
574