blob: 1f5759357d8ef8abd24fc737047c6ee887de4475 [file] [log] [blame]
Selim Cinek8a9308d2017-08-24 09:31:08 -07001/*
2 * Copyright (C) 2017 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.
Jason Monke59dc402018-08-16 12:05:01 -04006 * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 *
Selim Cinek8a9308d2017-08-24 09:31:08 -07007 * Unless required by applicable law or agreed to in writing, software
8 * distributed under the License is distributed on an "AS IS" BASIS,
9 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 * See the License for the specific language governing permissions and
11 * limitations under the License
12 */
13
Rohan Shah20790b82018-07-02 17:21:04 -070014package com.android.systemui.statusbar.notification.stack;
Selim Cinek8a9308d2017-08-24 09:31:08 -070015
Gus Prevase2d6f042018-10-17 15:25:30 -040016import static android.provider.Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL;
17
Jason Monke59dc402018-08-16 12:05:01 -040018import static junit.framework.Assert.assertEquals;
19import static junit.framework.Assert.assertNotNull;
Gus Prevas99ba4ba2018-10-01 16:40:23 -040020import static junit.framework.Assert.assertNull;
21
Jason Monk297c04e2018-08-23 17:16:59 -040022import static org.junit.Assert.assertFalse;
23import static org.junit.Assert.assertTrue;
Rohan Shah524cf7b2018-03-15 14:40:02 -070024import static org.mockito.ArgumentMatchers.any;
25import static org.mockito.ArgumentMatchers.anyBoolean;
Jason Monke59dc402018-08-16 12:05:01 -040026import static org.mockito.ArgumentMatchers.anyInt;
Will Brockmane718d582019-01-17 16:38:38 -050027import static org.mockito.ArgumentMatchers.argThat;
Selim Cinekd60ef9e2018-05-16 16:01:05 -070028import static org.mockito.ArgumentMatchers.eq;
Gus Prevase2d6f042018-10-17 15:25:30 -040029import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
Evan Lairdfec77662018-09-18 16:56:50 -040030import static org.mockito.Mockito.atLeastOnce;
Jason Monke59dc402018-08-16 12:05:01 -040031import static org.mockito.Mockito.clearInvocations;
Rohan Shah524cf7b2018-03-15 14:40:02 -070032import static org.mockito.Mockito.doNothing;
Selim Cinek8a9308d2017-08-24 09:31:08 -070033import static org.mockito.Mockito.mock;
Rohan Shah524cf7b2018-03-15 14:40:02 -070034import static org.mockito.Mockito.reset;
35import static org.mockito.Mockito.spy;
Julia Reynolds9cf17562018-03-14 09:49:35 -040036import static org.mockito.Mockito.verify;
Selim Cinek8a9308d2017-08-24 09:31:08 -070037import static org.mockito.Mockito.when;
38
Will Brockmane718d582019-01-17 16:38:38 -050039import android.metrics.LogMaker;
Gus Prevase2d6f042018-10-17 15:25:30 -040040import android.provider.Settings;
Gus Prevas99ba4ba2018-10-01 16:40:23 -040041import android.view.View;
Selim Cinek8a9308d2017-08-24 09:31:08 -070042
Brett Chabot84151d92019-02-27 15:37:59 -080043import androidx.test.annotation.UiThreadTest;
44import androidx.test.filters.SmallTest;
45import androidx.test.runner.AndroidJUnit4;
46
Will Brockmane718d582019-01-17 16:38:38 -050047import com.android.internal.logging.MetricsLogger;
48import com.android.internal.logging.nano.MetricsProto;
Jason Monk297c04e2018-08-23 17:16:59 -040049import com.android.systemui.Dependency;
Rohan Shah524cf7b2018-03-15 14:40:02 -070050import com.android.systemui.ExpandHelper;
Jason Monk297c04e2018-08-23 17:16:59 -040051import com.android.systemui.InitController;
Julia Reynolds9cf17562018-03-14 09:49:35 -040052import com.android.systemui.R;
Selim Cinek8a9308d2017-08-24 09:31:08 -070053import com.android.systemui.SysuiTestCase;
Will Brockmane718d582019-01-17 16:38:38 -050054import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
Beverly8fdb5332019-02-04 14:29:49 -050055import com.android.systemui.plugins.statusbar.StatusBarStateController;
Selim Cinek34518f62019-02-28 19:41:18 -080056import com.android.systemui.statusbar.AmbientPulseManager;
Julia Reynolds9cf17562018-03-14 09:49:35 -040057import com.android.systemui.statusbar.EmptyShadeView;
Jason Monke59dc402018-08-16 12:05:01 -040058import com.android.systemui.statusbar.NotificationPresenter;
Jason Monk297c04e2018-08-23 17:16:59 -040059import com.android.systemui.statusbar.NotificationRemoteInputManager;
60import com.android.systemui.statusbar.NotificationShelf;
61import com.android.systemui.statusbar.RemoteInputController;
62import com.android.systemui.statusbar.StatusBarState;
Jason Monke59dc402018-08-16 12:05:01 -040063import com.android.systemui.statusbar.notification.NotificationEntryManager;
Ned Burnsf81c4c42019-01-07 14:10:43 -050064import com.android.systemui.statusbar.notification.collection.NotificationData;
65import com.android.systemui.statusbar.notification.collection.NotificationEntry;
Jason Monke59dc402018-08-16 12:05:01 -040066import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
Rohan Shah20790b82018-07-02 17:21:04 -070067import com.android.systemui.statusbar.notification.row.FooterView;
68import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
Rohan Shah524cf7b2018-03-15 14:40:02 -070069import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
70import com.android.systemui.statusbar.phone.NotificationGroupManager;
Selim Cinek8a9308d2017-08-24 09:31:08 -070071import com.android.systemui.statusbar.phone.ScrimController;
Jason Monk297c04e2018-08-23 17:16:59 -040072import com.android.systemui.statusbar.phone.ShadeController;
Selim Cinek8a9308d2017-08-24 09:31:08 -070073import com.android.systemui.statusbar.phone.StatusBar;
Jason Monke59dc402018-08-16 12:05:01 -040074import com.android.systemui.statusbar.phone.StatusBarTest.TestableNotificationEntryManager;
75
Gus Prevase2d6f042018-10-17 15:25:30 -040076import org.junit.After;
Selim Cinek8a9308d2017-08-24 09:31:08 -070077import org.junit.Assert;
78import org.junit.Before;
Rohan Shah524cf7b2018-03-15 14:40:02 -070079import org.junit.Rule;
Selim Cinek8a9308d2017-08-24 09:31:08 -070080import org.junit.Test;
81import org.junit.runner.RunWith;
Jason Monke59dc402018-08-16 12:05:01 -040082import org.mockito.ArgumentCaptor;
Will Brockmane718d582019-01-17 16:38:38 -050083import org.mockito.ArgumentMatcher;
Rohan Shah524cf7b2018-03-15 14:40:02 -070084import org.mockito.Mock;
85import org.mockito.junit.MockitoJUnit;
86import org.mockito.junit.MockitoRule;
Selim Cinek8a9308d2017-08-24 09:31:08 -070087
Jason Monk297c04e2018-08-23 17:16:59 -040088import java.util.ArrayList;
89
Rohan Shah524cf7b2018-03-15 14:40:02 -070090/**
91 * Tests for {@link NotificationStackScrollLayout}.
92 */
Selim Cinek8a9308d2017-08-24 09:31:08 -070093@SmallTest
94@RunWith(AndroidJUnit4.class)
95public class NotificationStackScrollLayoutTest extends SysuiTestCase {
96
Will Brockmane718d582019-01-17 16:38:38 -050097 private NotificationStackScrollLayout mStackScroller; // Normally test this
98 private NotificationStackScrollLayout mStackScrollerInternal; // See explanation below
Rohan Shah524cf7b2018-03-15 14:40:02 -070099
100 @Rule public MockitoRule mockito = MockitoJUnit.rule();
101 @Mock private StatusBar mBar;
Jason Monk1fd3fc32018-08-14 17:20:09 -0400102 @Mock private StatusBarStateController mBarState;
Rohan Shah524cf7b2018-03-15 14:40:02 -0700103 @Mock private HeadsUpManagerPhone mHeadsUpManager;
104 @Mock private NotificationBlockingHelperManager mBlockingHelperManager;
105 @Mock private NotificationGroupManager mGroupManager;
106 @Mock private ExpandHelper mExpandHelper;
107 @Mock private EmptyShadeView mEmptyShadeView;
Jason Monke59dc402018-08-16 12:05:01 -0400108 @Mock private NotificationData mNotificationData;
109 @Mock private NotificationRemoteInputManager mRemoteInputManager;
110 @Mock private RemoteInputController mRemoteInputController;
Will Brockmane718d582019-01-17 16:38:38 -0500111 @Mock private MetricsLogger mMetricsLogger;
Lucas Dupin00be88f2019-01-03 17:50:52 -0800112 @Mock private NotificationRoundnessManager mNotificationRoundnessManager;
Jason Monke59dc402018-08-16 12:05:01 -0400113 private TestableNotificationEntryManager mEntryManager;
Gus Prevase2d6f042018-10-17 15:25:30 -0400114 private int mOriginalInterruptionModelSetting;
Selim Cinek8a9308d2017-08-24 09:31:08 -0700115
Will Brockmane718d582019-01-17 16:38:38 -0500116
Selim Cinek8a9308d2017-08-24 09:31:08 -0700117 @Before
118 @UiThreadTest
119 public void setUp() throws Exception {
Rohan Shah524cf7b2018-03-15 14:40:02 -0700120 // Inject dependencies before initializing the layout
121 mDependency.injectTestDependency(
122 NotificationBlockingHelperManager.class,
123 mBlockingHelperManager);
Jason Monk1fd3fc32018-08-14 17:20:09 -0400124 mDependency.injectTestDependency(StatusBarStateController.class, mBarState);
Will Brockmane718d582019-01-17 16:38:38 -0500125 mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
Jason Monke59dc402018-08-16 12:05:01 -0400126 mDependency.injectTestDependency(NotificationRemoteInputManager.class,
127 mRemoteInputManager);
Jason Monk297c04e2018-08-23 17:16:59 -0400128 mDependency.injectMockDependency(ShadeController.class);
Jason Monke59dc402018-08-16 12:05:01 -0400129 when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController);
130
Gus Prevas92586462019-01-04 16:06:12 -0500131 mEntryManager = new TestableNotificationEntryManager(mContext);
Jason Monk297c04e2018-08-23 17:16:59 -0400132 mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
133 Dependency.get(InitController.class).executePostInitTasks();
Gus Prevas8621bd22018-12-20 15:04:25 -0500134 mEntryManager.setUpForTest(mock(NotificationPresenter.class), null, mHeadsUpManager,
Kevina5ff1fa2018-08-21 16:35:48 -0700135 mNotificationData);
Rohan Shah524cf7b2018-03-15 14:40:02 -0700136
Jason Monk297c04e2018-08-23 17:16:59 -0400137
138 NotificationShelf notificationShelf = mock(NotificationShelf.class);
Will Brockmane718d582019-01-17 16:38:38 -0500139
140 // The actual class under test. You may need to work with this class directly when
141 // testing anonymous class members of mStackScroller, like mMenuEventListener,
142 // which refer to members of NotificationStackScrollLayout. The spy
143 // holds a copy of the CUT's instances of these classes, so they still refer to the CUT's
144 // member variables, not the spy's member variables.
145 mStackScrollerInternal = new NotificationStackScrollLayout(getContext(), null,
Selim Cinek34518f62019-02-28 19:41:18 -0800146 true /* allowLongPress */, mNotificationRoundnessManager,
147 new AmbientPulseManager(mContext));
Will Brockmane718d582019-01-17 16:38:38 -0500148 mStackScroller = spy(mStackScrollerInternal);
Rohan Shah524cf7b2018-03-15 14:40:02 -0700149 mStackScroller.setShelf(notificationShelf);
Selim Cinek8a9308d2017-08-24 09:31:08 -0700150 mStackScroller.setStatusBar(mBar);
151 mStackScroller.setScrimController(mock(ScrimController.class));
Rohan Shah524cf7b2018-03-15 14:40:02 -0700152 mStackScroller.setHeadsUpManager(mHeadsUpManager);
153 mStackScroller.setGroupManager(mGroupManager);
154 mStackScroller.setEmptyShadeView(mEmptyShadeView);
155
156 // Stub out functionality that isn't necessary to test.
157 doNothing().when(mBar)
158 .executeRunnableDismissingKeyguard(any(Runnable.class),
159 any(Runnable.class),
160 anyBoolean(),
161 anyBoolean(),
162 anyBoolean());
163 doNothing().when(mGroupManager).collapseAllGroups();
164 doNothing().when(mExpandHelper).cancelImmediately();
165 doNothing().when(notificationShelf).setAnimationsEnabled(anyBoolean());
Lucas Dupin7fc9dc12019-01-03 09:19:43 -0800166 doNothing().when(notificationShelf).fadeInTranslating();
Gus Prevase2d6f042018-10-17 15:25:30 -0400167
168 mOriginalInterruptionModelSetting = Settings.Secure.getInt(mContext.getContentResolver(),
169 NOTIFICATION_NEW_INTERRUPTION_MODEL, 0);
170 Settings.Secure.putInt(mContext.getContentResolver(),
171 NOTIFICATION_NEW_INTERRUPTION_MODEL, 1);
172 }
173
174 @After
175 public void tearDown() {
176 Settings.Secure.putInt(mContext.getContentResolver(),
177 NOTIFICATION_NEW_INTERRUPTION_MODEL, mOriginalInterruptionModelSetting);
Selim Cinek8a9308d2017-08-24 09:31:08 -0700178 }
179
180 @Test
181 public void testNotDimmedOnKeyguard() {
Jason Monk1fd3fc32018-08-14 17:20:09 -0400182 when(mBarState.getState()).thenReturn(StatusBarState.SHADE);
Selim Cinek8a9308d2017-08-24 09:31:08 -0700183 mStackScroller.setDimmed(true /* dimmed */, false /* animate */);
184 mStackScroller.setDimmed(true /* dimmed */, true /* animate */);
Jason Monk297c04e2018-08-23 17:16:59 -0400185 assertFalse(mStackScroller.isDimmed());
Selim Cinek8a9308d2017-08-24 09:31:08 -0700186 }
187
Lucas Dupin0cd882f2018-01-30 12:19:49 -0800188 @Test
189 public void testAntiBurnInOffset() {
190 final int burnInOffset = 30;
191 mStackScroller.setAntiBurnInOffsetX(burnInOffset);
192 mStackScroller.setDark(false /* dark */, false /* animated */, null /* touch */);
193 Assert.assertEquals(0 /* expected */, mStackScroller.getTranslationX(), 0.01 /* delta */);
194 mStackScroller.setDark(true /* dark */, false /* animated */, null /* touch */);
195 Assert.assertEquals(burnInOffset /* expected */, mStackScroller.getTranslationX(),
196 0.01 /* delta */);
197 }
198
Julia Reynolds9cf17562018-03-14 09:49:35 -0400199 @Test
Julia Reynolds34f14962018-05-03 12:40:20 +0000200 public void updateEmptyView_dndSuppressing() {
201 when(mEmptyShadeView.willBeGone()).thenReturn(true);
202 when(mBar.areNotificationsHidden()).thenReturn(true);
203
204 mStackScroller.updateEmptyShadeView(true);
205
206 verify(mEmptyShadeView).setText(R.string.dnd_suppressing_shade_text);
207 }
208
209 @Test
210 public void updateEmptyView_dndNotSuppressing() {
211 mStackScroller.setEmptyShadeView(mEmptyShadeView);
212 when(mEmptyShadeView.willBeGone()).thenReturn(true);
213 when(mBar.areNotificationsHidden()).thenReturn(false);
214
215 mStackScroller.updateEmptyShadeView(true);
216
217 verify(mEmptyShadeView).setText(R.string.empty_shade_text);
218 }
219
220 @Test
221 public void updateEmptyView_noNotificationsToDndSuppressing() {
222 mStackScroller.setEmptyShadeView(mEmptyShadeView);
223 when(mEmptyShadeView.willBeGone()).thenReturn(true);
224 when(mBar.areNotificationsHidden()).thenReturn(false);
225 mStackScroller.updateEmptyShadeView(true);
226 verify(mEmptyShadeView).setText(R.string.empty_shade_text);
227
228 when(mBar.areNotificationsHidden()).thenReturn(true);
229 mStackScroller.updateEmptyShadeView(true);
230 verify(mEmptyShadeView).setText(R.string.dnd_suppressing_shade_text);
231 }
232
233 @Test
Rohan Shah524cf7b2018-03-15 14:40:02 -0700234 @UiThreadTest
235 public void testSetExpandedHeight_blockingHelperManagerReceivedCallbacks() {
236 mStackScroller.setExpandedHeight(0f);
237 verify(mBlockingHelperManager).setNotificationShadeExpanded(0f);
238 reset(mBlockingHelperManager);
239
240 mStackScroller.setExpandedHeight(100f);
241 verify(mBlockingHelperManager).setNotificationShadeExpanded(100f);
Julia Reynolds9cf17562018-03-14 09:49:35 -0400242 }
Julia Reynoldsed1c9af2018-03-21 15:21:09 -0400243
244 @Test
245 public void manageNotifications_visible() {
246 FooterView view = mock(FooterView.class);
247 mStackScroller.setFooterView(view);
248 when(view.willBeGone()).thenReturn(true);
Julia Reynoldsed1c9af2018-03-21 15:21:09 -0400249
250 mStackScroller.updateFooterView(true, false);
251
Selim Cinekd60ef9e2018-05-16 16:01:05 -0700252 verify(view).setVisible(eq(true), anyBoolean());
253 verify(view).setSecondaryVisible(eq(false), anyBoolean());
Julia Reynoldsed1c9af2018-03-21 15:21:09 -0400254 }
255
256 @Test
257 public void clearAll_visible() {
258 FooterView view = mock(FooterView.class);
259 mStackScroller.setFooterView(view);
260 when(view.willBeGone()).thenReturn(true);
Julia Reynoldsed1c9af2018-03-21 15:21:09 -0400261
262 mStackScroller.updateFooterView(true, true);
263
Selim Cinekd60ef9e2018-05-16 16:01:05 -0700264 verify(view).setVisible(eq(true), anyBoolean());
265 verify(view).setSecondaryVisible(eq(true), anyBoolean());
Julia Reynoldsed1c9af2018-03-21 15:21:09 -0400266 }
Jason Monke59dc402018-08-16 12:05:01 -0400267
268 @Test
269 public void testInflateFooterView() {
270 mStackScroller.inflateFooterView();
271 ArgumentCaptor<FooterView> captor = ArgumentCaptor.forClass(FooterView.class);
272 verify(mStackScroller).setFooterView(captor.capture());
273
274 assertNotNull(captor.getValue().findViewById(R.id.manage_text).hasOnClickListeners());
275 assertNotNull(captor.getValue().findViewById(R.id.dismiss_text).hasOnClickListeners());
276 }
277
278 @Test
279 public void testUpdateFooter_noNotifications() {
280 setBarStateForTest(StatusBarState.SHADE);
281 assertEquals(0, mNotificationData.getActiveNotifications().size());
282
283 mStackScroller.updateFooter();
Jason Monk297c04e2018-08-23 17:16:59 -0400284 verify(mStackScroller, atLeastOnce()).updateFooterView(false, false);
Jason Monke59dc402018-08-16 12:05:01 -0400285 }
286
287 @Test
288 public void testUpdateFooter_remoteInput() {
289 setBarStateForTest(StatusBarState.SHADE);
Ned Burnsf81c4c42019-01-07 14:10:43 -0500290 ArrayList<NotificationEntry> entries = new ArrayList<>();
291 entries.add(mock(NotificationEntry.class));
Jason Monke59dc402018-08-16 12:05:01 -0400292 when(mNotificationData.getActiveNotifications()).thenReturn(entries);
293
294 ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
295 when(row.canViewBeDismissed()).thenReturn(true);
296 when(mStackScroller.getChildCount()).thenReturn(1);
297 when(mStackScroller.getChildAt(anyInt())).thenReturn(row);
298 when(mRemoteInputController.isRemoteInputActive()).thenReturn(true);
299
300 mStackScroller.updateFooter();
301 verify(mStackScroller).updateFooterView(false, true);
302 }
303
304 @Test
305 public void testUpdateFooter_oneClearableNotification() {
306 setBarStateForTest(StatusBarState.SHADE);
Ned Burnsf81c4c42019-01-07 14:10:43 -0500307 ArrayList<NotificationEntry> entries = new ArrayList<>();
308 entries.add(mock(NotificationEntry.class));
Jason Monke59dc402018-08-16 12:05:01 -0400309 when(mNotificationData.getActiveNotifications()).thenReturn(entries);
310
311 ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
312 when(row.canViewBeDismissed()).thenReturn(true);
313 when(mStackScroller.getChildCount()).thenReturn(1);
314 when(mStackScroller.getChildAt(anyInt())).thenReturn(row);
315
316 mStackScroller.updateFooter();
317 verify(mStackScroller).updateFooterView(true, true);
318 }
319
320 @Test
321 public void testUpdateFooter_oneNonClearableNotification() {
322 setBarStateForTest(StatusBarState.SHADE);
Ned Burnsf81c4c42019-01-07 14:10:43 -0500323 ArrayList<NotificationEntry> entries = new ArrayList<>();
324 entries.add(mock(NotificationEntry.class));
Jason Monk297c04e2018-08-23 17:16:59 -0400325 when(mEntryManager.getNotificationData().getActiveNotifications()).thenReturn(entries);
326 assertTrue(mEntryManager.getNotificationData().getActiveNotifications().size() != 0);
Jason Monke59dc402018-08-16 12:05:01 -0400327
328 mStackScroller.updateFooter();
329 verify(mStackScroller).updateFooterView(true, false);
330 }
331
332 @Test
333 public void testUpdateFooter_atEnd() {
334 // add footer
335 mStackScroller.inflateFooterView();
336
337 // add notification
338 ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
Ned Burnsf81c4c42019-01-07 14:10:43 -0500339 NotificationEntry entry = mock(NotificationEntry.class);
Evan Laird94492852018-10-25 13:43:01 -0400340 when(row.getEntry()).thenReturn(entry);
341 when(entry.isClearable()).thenReturn(true);
Jason Monke59dc402018-08-16 12:05:01 -0400342 mStackScroller.addContainerView(row);
343
344 mStackScroller.onUpdateRowStates();
345
346 // move footer to end
347 verify(mStackScroller).changeViewPosition(any(FooterView.class), eq(-1 /* end */));
348 }
349
350 @Test
Gus Prevase2d6f042018-10-17 15:25:30 -0400351 public void testUpdateGapIndex_allHighPriority() {
352 when(mStackScroller.getChildCount()).thenReturn(3);
353 for (int i = 0; i < 3; i++) {
354 ExpandableNotificationRow row = mock(ExpandableNotificationRow.class,
355 RETURNS_DEEP_STUBS);
356 String key = Integer.toString(i);
357 when(row.getStatusBarNotification().getKey()).thenReturn(key);
Gus Prevascaed15c2019-01-18 14:19:51 -0500358 when(row.getEntry().isHighPriority()).thenReturn(true);
Gus Prevase2d6f042018-10-17 15:25:30 -0400359 when(mStackScroller.getChildAt(i)).thenReturn(row);
360 }
361
362 mStackScroller.updateSectionBoundaries();
363 assertEquals(-1, mStackScroller.getSectionBoundaryIndex(0));
364 }
365
366 @Test
367 public void testUpdateGapIndex_allLowPriority() {
368 when(mStackScroller.getChildCount()).thenReturn(3);
369 for (int i = 0; i < 3; i++) {
370 ExpandableNotificationRow row = mock(ExpandableNotificationRow.class,
371 RETURNS_DEEP_STUBS);
372 String key = Integer.toString(i);
373 when(row.getStatusBarNotification().getKey()).thenReturn(key);
Gus Prevascaed15c2019-01-18 14:19:51 -0500374 when(row.getEntry().isHighPriority()).thenReturn(false);
Gus Prevase2d6f042018-10-17 15:25:30 -0400375 when(mStackScroller.getChildAt(i)).thenReturn(row);
376 }
377
378 mStackScroller.updateSectionBoundaries();
379 assertEquals(-1, mStackScroller.getSectionBoundaryIndex(0));
380 }
381
382 @Test
383 public void testUpdateGapIndex_gapExists() {
384 when(mStackScroller.getChildCount()).thenReturn(6);
385 for (int i = 0; i < 6; i++) {
386 ExpandableNotificationRow row = mock(ExpandableNotificationRow.class,
387 RETURNS_DEEP_STUBS);
388 String key = Integer.toString(i);
389 when(row.getStatusBarNotification().getKey()).thenReturn(key);
Gus Prevascaed15c2019-01-18 14:19:51 -0500390 when(row.getEntry().isHighPriority()).thenReturn(i < 3);
Gus Prevase2d6f042018-10-17 15:25:30 -0400391 when(mStackScroller.getChildAt(i)).thenReturn(row);
392 }
393
394 mStackScroller.updateSectionBoundaries();
395 assertEquals(3, mStackScroller.getSectionBoundaryIndex(0));
396 }
397
398 @Test
399 public void testUpdateGapIndex_empty() {
400 when(mStackScroller.getChildCount()).thenReturn(0);
401
402 mStackScroller.updateSectionBoundaries();
403 assertEquals(-1, mStackScroller.getSectionBoundaryIndex(0));
404 }
405
406 @Test
Jason Monke59dc402018-08-16 12:05:01 -0400407 public void testOnDensityOrFontScaleChanged_reInflatesFooterViews() {
408 clearInvocations(mStackScroller);
409 mStackScroller.onDensityOrFontScaleChanged();
410 verify(mStackScroller).setFooterView(any());
411 verify(mStackScroller).setEmptyShadeView(any());
412 }
413
Gus Prevas99ba4ba2018-10-01 16:40:23 -0400414 @Test
415 @UiThreadTest
416 public void testSetIsBeingDraggedResetsExposedMenu() {
417 NotificationSwipeHelper swipeActionHelper =
418 (NotificationSwipeHelper) mStackScroller.getSwipeActionHelper();
419 swipeActionHelper.setExposedMenuView(new View(mContext));
420 mStackScroller.setIsBeingDragged(true);
421 assertNull(swipeActionHelper.getExposedMenuView());
422 }
423
424 @Test
425 @UiThreadTest
426 public void testPanelTrackingStartResetsExposedMenu() {
427 NotificationSwipeHelper swipeActionHelper =
428 (NotificationSwipeHelper) mStackScroller.getSwipeActionHelper();
429 swipeActionHelper.setExposedMenuView(new View(mContext));
430 mStackScroller.onPanelTrackingStarted();
431 assertNull(swipeActionHelper.getExposedMenuView());
432 }
433
434 @Test
435 @UiThreadTest
436 public void testDarkModeResetsExposedMenu() {
437 NotificationSwipeHelper swipeActionHelper =
438 (NotificationSwipeHelper) mStackScroller.getSwipeActionHelper();
439 swipeActionHelper.setExposedMenuView(new View(mContext));
440 mStackScroller.setDarkAmount(0.1f, 0.1f);
441 assertNull(swipeActionHelper.getExposedMenuView());
442 }
443
Will Brockmane718d582019-01-17 16:38:38 -0500444 class LogMatcher implements ArgumentMatcher<LogMaker> {
445 private int mCategory, mType;
446
447 LogMatcher(int category, int type) {
448 mCategory = category;
449 mType = type;
450 }
451 public boolean matches(LogMaker l) {
452 return (l.getCategory() == mCategory)
453 && (l.getType() == mType);
454 }
455
456 public String toString() {
457 return String.format("LogMaker(%d, %d)", mCategory, mType);
458 }
459 }
460
461 private LogMaker logMatcher(int category, int type) {
462 return argThat(new LogMatcher(category, type));
463 }
464
465 @Test
466 @UiThreadTest
467 public void testOnMenuClickedLogging() {
468 // Set up the object under test to have a valid mLongPressListener. We're testing an
469 // anonymous-class member, mMenuEventListener, so we need to modify the state of the
470 // class itself, not the Mockito spy copied from it. See notes in setup.
471 mStackScrollerInternal.setLongPressListener(
472 mock(ExpandableNotificationRow.LongPressListener.class));
473
474 ExpandableNotificationRow row = mock(ExpandableNotificationRow.class, RETURNS_DEEP_STUBS);
475 when(row.getStatusBarNotification().getLogMaker()).thenReturn(new LogMaker(
476 MetricsProto.MetricsEvent.VIEW_UNKNOWN));
477
478 mStackScroller.mMenuEventListener.onMenuClicked(row, 0, 0, mock(
479 NotificationMenuRowPlugin.MenuItem.class));
480 verify(row.getStatusBarNotification()).getLogMaker(); // This writes most of the log data
481 verify(mMetricsLogger).write(logMatcher(MetricsProto.MetricsEvent.ACTION_TOUCH_GEAR,
482 MetricsProto.MetricsEvent.TYPE_ACTION));
483 }
484
485 @Test
486 @UiThreadTest
487 public void testOnMenuShownLogging() {
488 // Set up the object under test to have a valid mHeadsUpManager. See notes in setup.
489 mStackScrollerInternal.setHeadsUpManager(mHeadsUpManager);
490
491 ExpandableNotificationRow row = mock(ExpandableNotificationRow.class, RETURNS_DEEP_STUBS);
492 when(row.getStatusBarNotification().getLogMaker()).thenReturn(new LogMaker(
493 MetricsProto.MetricsEvent.VIEW_UNKNOWN));
494
495 mStackScroller.mMenuEventListener.onMenuShown(row);
496 verify(row.getStatusBarNotification()).getLogMaker(); // This writes most of the log data
497 verify(mMetricsLogger).write(logMatcher(MetricsProto.MetricsEvent.ACTION_REVEAL_GEAR,
498 MetricsProto.MetricsEvent.TYPE_ACTION));
499 }
500
Jason Monke59dc402018-08-16 12:05:01 -0400501 private void setBarStateForTest(int state) {
Jason Monk297c04e2018-08-23 17:16:59 -0400502 // Can't inject this through the listener or we end up on the actual implementation
503 // rather than the mock because the spy just coppied the anonymous inner /shruggie.
504 mStackScroller.setStatusBarState(state);
Jason Monke59dc402018-08-16 12:05:01 -0400505 }
Selim Cinek8a9308d2017-08-24 09:31:08 -0700506}