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