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.systemui.statusbar.phone;
18 
19 import static org.junit.Assert.assertFalse;
20 import static org.junit.Assert.assertTrue;
21 import static org.mockito.Mockito.doReturn;
22 import static org.mockito.Mockito.mock;
23 import static org.mockito.Mockito.spy;
24 import static org.mockito.Mockito.times;
25 import static org.mockito.Mockito.verify;
26 import static org.mockito.Mockito.when;
27 
28 import android.app.Notification;
29 import android.service.notification.StatusBarNotification;
30 import android.testing.AndroidTestingRunner;
31 import android.testing.TestableLooper;
32 
33 import androidx.test.filters.SmallTest;
34 
35 import com.android.systemui.SysuiTestCase;
36 import com.android.systemui.plugins.statusbar.StatusBarStateController;
37 import com.android.systemui.statusbar.notification.NotificationEntryListener;
38 import com.android.systemui.statusbar.notification.NotificationEntryManager;
39 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
40 import com.android.systemui.statusbar.policy.HeadsUpManager;
41 
42 import org.junit.Before;
43 import org.junit.Rule;
44 import org.junit.Test;
45 import org.junit.runner.RunWith;
46 import org.mockito.ArgumentCaptor;
47 import org.mockito.Captor;
48 import org.mockito.Mock;
49 import org.mockito.junit.MockitoJUnit;
50 import org.mockito.junit.MockitoRule;
51 
52 import java.util.HashMap;
53 
54 @SmallTest
55 @RunWith(AndroidTestingRunner.class)
56 @TestableLooper.RunWithLooper
57 public class NotificationGroupAlertTransferHelperTest extends SysuiTestCase {
58     @Rule public MockitoRule rule = MockitoJUnit.rule();
59 
60     private NotificationGroupAlertTransferHelper mGroupAlertTransferHelper;
61     private NotificationGroupManager mGroupManager;
62     private HeadsUpManager mHeadsUpManager;
63     @Mock private NotificationEntryManager mNotificationEntryManager;
64     @Captor
65     private ArgumentCaptor<NotificationEntryListener> mListenerCaptor;
66     private NotificationEntryListener mNotificationEntryListener;
67     private final HashMap<String, NotificationEntry> mPendingEntries = new HashMap<>();
68     private final NotificationGroupTestHelper mGroupTestHelper =
69             new NotificationGroupTestHelper(mContext);
70 
71 
72     @Before
setup()73     public void setup() {
74         mHeadsUpManager = new HeadsUpManager(mContext) {};
75 
76         when(mNotificationEntryManager.getPendingNotificationsIterator())
77                 .thenReturn(mPendingEntries.values());
78 
79         mGroupManager = new NotificationGroupManager(mock(StatusBarStateController.class));
80         mDependency.injectTestDependency(NotificationGroupManager.class, mGroupManager);
81         mGroupManager.setHeadsUpManager(mHeadsUpManager);
82 
83         mGroupAlertTransferHelper = new NotificationGroupAlertTransferHelper();
84         mGroupAlertTransferHelper.setHeadsUpManager(mHeadsUpManager);
85 
86         mGroupAlertTransferHelper.bind(mNotificationEntryManager, mGroupManager);
87         verify(mNotificationEntryManager).addNotificationEntryListener(mListenerCaptor.capture());
88         mNotificationEntryListener = mListenerCaptor.getValue();
89         mHeadsUpManager.addListener(mGroupAlertTransferHelper);
90     }
91 
92     @Test
testSuppressedSummaryHeadsUpTransfersToChild()93     public void testSuppressedSummaryHeadsUpTransfersToChild() {
94         NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
95         mHeadsUpManager.showNotification(summaryEntry);
96         NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
97 
98         // Summary will be suppressed because there is only one child.
99         mGroupManager.onEntryAdded(summaryEntry);
100         mGroupManager.onEntryAdded(childEntry);
101 
102         // A suppressed summary should transfer its alert state to the child.
103         assertFalse(mHeadsUpManager.isAlerting(summaryEntry.key));
104         assertTrue(mHeadsUpManager.isAlerting(childEntry.key));
105     }
106 
107     @Test
testSuppressedSummaryHeadsUpTransfersToChildButBackAgain()108     public void testSuppressedSummaryHeadsUpTransfersToChildButBackAgain() {
109         NotificationEntry summaryEntry =
110                 mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
111         NotificationEntry childEntry =
112                 mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
113         NotificationEntry childEntry2 =
114                 mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
115         mHeadsUpManager.showNotification(summaryEntry);
116         // Trigger a transfer of alert state from summary to child.
117         mGroupManager.onEntryAdded(summaryEntry);
118         mGroupManager.onEntryAdded(childEntry);
119 
120         // Add second child notification so that summary is no longer suppressed.
121         mPendingEntries.put(childEntry2.key, childEntry2);
122         mNotificationEntryListener.onPendingEntryAdded(childEntry2);
123         mGroupManager.onEntryAdded(childEntry2);
124 
125         // The alert state should transfer back to the summary as there is now more than one
126         // child and the summary should no longer be suppressed.
127         assertTrue(mHeadsUpManager.isAlerting(summaryEntry.key));
128         assertFalse(mHeadsUpManager.isAlerting(childEntry.key));
129     }
130 
131     @Test
testSuppressedSummaryHeadsUpDoesntTransferBackOnDozingChanged()132     public void testSuppressedSummaryHeadsUpDoesntTransferBackOnDozingChanged() {
133         NotificationEntry summaryEntry =
134                 mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
135         NotificationEntry childEntry =
136                 mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
137         NotificationEntry childEntry2 =
138                 mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
139         mHeadsUpManager.showNotification(summaryEntry);
140         // Trigger a transfer of alert state from summary to child.
141         mGroupManager.onEntryAdded(summaryEntry);
142         mGroupManager.onEntryAdded(childEntry);
143 
144         // Set dozing to true.
145         mGroupAlertTransferHelper.onDozingChanged(true);
146 
147         // Add second child notification so that summary is no longer suppressed.
148         mPendingEntries.put(childEntry2.key, childEntry2);
149         mNotificationEntryListener.onPendingEntryAdded(childEntry2);
150         mGroupManager.onEntryAdded(childEntry2);
151 
152         // Dozing changed so no reason to re-alert summary.
153         assertFalse(mHeadsUpManager.isAlerting(summaryEntry.key));
154     }
155 
156     @Test
testSuppressedSummaryHeadsUpTransferDoesNotAlertChildIfUninflated()157     public void testSuppressedSummaryHeadsUpTransferDoesNotAlertChildIfUninflated() {
158         NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
159         mHeadsUpManager.showNotification(summaryEntry);
160         NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
161         when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
162             .thenReturn(false);
163 
164         mGroupManager.onEntryAdded(summaryEntry);
165         mGroupManager.onEntryAdded(childEntry);
166 
167         // Alert is immediately removed from summary, but we do not show child yet either as its
168         // content is not inflated.
169         assertFalse(mHeadsUpManager.isAlerting(summaryEntry.key));
170         assertFalse(mHeadsUpManager.isAlerting(childEntry.key));
171         assertTrue(mGroupAlertTransferHelper.isAlertTransferPending(childEntry));
172     }
173 
174     @Test
testSuppressedSummaryHeadsUpTransferAlertsChildOnInflation()175     public void testSuppressedSummaryHeadsUpTransferAlertsChildOnInflation() {
176         NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
177         mHeadsUpManager.showNotification(summaryEntry);
178         NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
179         when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
180             .thenReturn(false);
181 
182         mGroupManager.onEntryAdded(summaryEntry);
183         mGroupManager.onEntryAdded(childEntry);
184 
185         when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
186             .thenReturn(true);
187         mNotificationEntryListener.onEntryReinflated(childEntry);
188 
189         // Alert is immediately removed from summary, and we show child as its content is inflated.
190         assertFalse(mHeadsUpManager.isAlerting(summaryEntry.key));
191         assertTrue(mHeadsUpManager.isAlerting(childEntry.key));
192     }
193 
194     @Test
testSuppressedSummaryHeadsUpTransferBackAbortsChildInflation()195     public void testSuppressedSummaryHeadsUpTransferBackAbortsChildInflation() {
196         NotificationEntry summaryEntry =
197                 mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
198         NotificationEntry childEntry =
199                 mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
200         when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
201             .thenReturn(false);
202         NotificationEntry childEntry2 =
203                 mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
204         mHeadsUpManager.showNotification(summaryEntry);
205         // Trigger a transfer of alert state from summary to child.
206         mGroupManager.onEntryAdded(summaryEntry);
207         mGroupManager.onEntryAdded(childEntry);
208 
209         // Add second child notification so that summary is no longer suppressed.
210         mPendingEntries.put(childEntry2.key, childEntry2);
211         mNotificationEntryListener.onPendingEntryAdded(childEntry2);
212         mGroupManager.onEntryAdded(childEntry2);
213 
214         // Child entry finishes its inflation.
215         when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
216             .thenReturn(true);
217         mNotificationEntryListener.onEntryReinflated(childEntry);
218 
219         verify(childEntry.getRow(), times(1)).freeContentViewWhenSafe(mHeadsUpManager
220             .getContentFlag());
221         assertFalse(mHeadsUpManager.isAlerting(childEntry.key));
222     }
223 
224     @Test
testCleanUpPendingAlertInfo()225     public void testCleanUpPendingAlertInfo() {
226         NotificationEntry summaryEntry =
227                 mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
228         NotificationEntry childEntry =
229                 mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
230         when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
231             .thenReturn(false);
232         mHeadsUpManager.showNotification(summaryEntry);
233         // Trigger a transfer of alert state from summary to child.
234         mGroupManager.onEntryAdded(summaryEntry);
235         mGroupManager.onEntryAdded(childEntry);
236 
237         mNotificationEntryListener.onEntryRemoved(childEntry, null, false);
238 
239         assertFalse(mGroupAlertTransferHelper.isAlertTransferPending(childEntry));
240     }
241 
242     @Test
testUpdateGroupChangeDoesNotTransfer()243     public void testUpdateGroupChangeDoesNotTransfer() {
244         NotificationEntry summaryEntry =
245                 mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
246         NotificationEntry childEntry =
247                 mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
248         when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
249             .thenReturn(false);
250         mHeadsUpManager.showNotification(summaryEntry);
251         // Trigger a transfer of alert state from summary to child.
252         mGroupManager.onEntryAdded(summaryEntry);
253         mGroupManager.onEntryAdded(childEntry);
254 
255         // Notify that entry changed groups.
256         StatusBarNotification oldNotification = childEntry.notification;
257         StatusBarNotification newSbn = spy(childEntry.notification.clone());
258         doReturn("other_group").when(newSbn).getGroupKey();
259         childEntry.notification = newSbn;
260         mGroupManager.onEntryUpdated(childEntry, oldNotification);
261 
262         assertFalse(mGroupAlertTransferHelper.isAlertTransferPending(childEntry));
263     }
264 
265     @Test
testUpdateChildToSummaryDoesNotTransfer()266     public void testUpdateChildToSummaryDoesNotTransfer() {
267         NotificationEntry summaryEntry =
268                 mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
269         NotificationEntry childEntry =
270                 mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
271         when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
272             .thenReturn(false);
273         mHeadsUpManager.showNotification(summaryEntry);
274         // Trigger a transfer of alert state from summary to child.
275         mGroupManager.onEntryAdded(summaryEntry);
276         mGroupManager.onEntryAdded(childEntry);
277 
278         // Update that child to a summary.
279         StatusBarNotification oldNotification = childEntry.notification;
280         childEntry.notification = mGroupTestHelper.createSummaryNotification(
281                 Notification.GROUP_ALERT_SUMMARY).notification;
282         mGroupManager.onEntryUpdated(childEntry, oldNotification);
283 
284         assertFalse(mGroupAlertTransferHelper.isAlertTransferPending(childEntry));
285     }
286 }
287