1/* 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. 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 17package com.android.systemui.statusbar; 18 19import static android.app.NotificationManager.IMPORTANCE_DEFAULT; 20 21import static org.junit.Assert.assertEquals; 22import static org.junit.Assert.assertFalse; 23import static org.junit.Assert.assertTrue; 24import static org.mockito.ArgumentMatchers.any; 25import static org.mockito.ArgumentMatchers.anyInt; 26import static org.mockito.ArgumentMatchers.eq; 27import static org.mockito.Mockito.mock; 28import static org.mockito.Mockito.reset; 29import static org.mockito.Mockito.spy; 30import static org.mockito.Mockito.times; 31import static org.mockito.Mockito.verify; 32import static org.mockito.Mockito.when; 33 34import android.app.AppOpsManager; 35import android.app.NotificationChannel; 36import android.support.test.filters.SmallTest; 37import android.testing.AndroidTestingRunner; 38import android.testing.TestableLooper.RunWithLooper; 39import android.util.ArraySet; 40import android.view.NotificationHeaderView; 41import android.view.View; 42 43import com.android.systemui.SysuiTestCase; 44import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; 45import com.android.systemui.statusbar.notification.AboveShelfChangedListener; 46import com.android.systemui.statusbar.stack.NotificationChildrenContainer; 47 48import org.junit.Assert; 49import org.junit.Before; 50import org.junit.Rule; 51import org.junit.Test; 52import org.junit.runner.RunWith; 53import org.mockito.Mock; 54import org.mockito.junit.MockitoJUnit; 55import org.mockito.junit.MockitoRule; 56 57import java.util.List; 58 59@SmallTest 60@RunWith(AndroidTestingRunner.class) 61@RunWithLooper(setAsMainLooper = true) 62public class ExpandableNotificationRowTest extends SysuiTestCase { 63 64 private ExpandableNotificationRow mGroupRow; 65 66 private NotificationTestHelper mNotificationTestHelper; 67 boolean mHeadsUpAnimatingAway = false; 68 69 @Rule public MockitoRule mockito = MockitoJUnit.rule(); 70 @Mock private NotificationBlockingHelperManager mBlockingHelperManager; 71 72 @Before 73 public void setUp() throws Exception { 74 mNotificationTestHelper = new NotificationTestHelper(mContext); 75 mGroupRow = mNotificationTestHelper.createGroup(); 76 mGroupRow.setHeadsUpAnimatingAwayListener( 77 animatingAway -> mHeadsUpAnimatingAway = animatingAway); 78 mDependency.injectTestDependency( 79 NotificationBlockingHelperManager.class, 80 mBlockingHelperManager); 81 } 82 83 @Test 84 public void testGroupSummaryNotShowingIconWhenPublic() { 85 mGroupRow.setSensitive(true, true); 86 mGroupRow.setHideSensitiveForIntrinsicHeight(true); 87 assertTrue(mGroupRow.isSummaryWithChildren()); 88 assertFalse(mGroupRow.isShowingIcon()); 89 } 90 91 @Test 92 public void testNotificationHeaderVisibleWhenAnimating() { 93 mGroupRow.setSensitive(true, true); 94 mGroupRow.setHideSensitive(true, false, 0, 0); 95 mGroupRow.setHideSensitive(false, true, 0, 0); 96 assertTrue(mGroupRow.getChildrenContainer().getVisibleHeader().getVisibility() 97 == View.VISIBLE); 98 } 99 100 @Test 101 public void testUserLockedResetEvenWhenNoChildren() { 102 mGroupRow.setUserLocked(true); 103 mGroupRow.removeAllChildren(); 104 mGroupRow.setUserLocked(false); 105 assertFalse("The childrencontainer should not be userlocked but is, the state " 106 + "seems out of sync.", mGroupRow.getChildrenContainer().isUserLocked()); 107 } 108 109 @Test 110 public void testReinflatedOnDensityChange() { 111 mGroupRow.setUserLocked(true); 112 mGroupRow.removeAllChildren(); 113 mGroupRow.setUserLocked(false); 114 NotificationChildrenContainer mockContainer = mock(NotificationChildrenContainer.class); 115 mGroupRow.setChildrenContainer(mockContainer); 116 mGroupRow.onDensityOrFontScaleChanged(); 117 verify(mockContainer).reInflateViews(any(), any()); 118 } 119 120 @Test 121 public void testIconColorShouldBeUpdatedWhenSensitive() throws Exception { 122 ExpandableNotificationRow row = spy(mNotificationTestHelper.createRow()); 123 row.setSensitive(true, true); 124 row.setHideSensitive(true, false, 0, 0); 125 verify(row).updateShelfIconColor(); 126 } 127 128 @Test 129 public void testIconColorShouldBeUpdatedWhenSettingDark() throws Exception { 130 ExpandableNotificationRow row = spy(mNotificationTestHelper.createRow()); 131 row.setDark(true, false, 0); 132 verify(row).updateShelfIconColor(); 133 } 134 135 @Test 136 public void testAboveShelfChangedListenerCalled() throws Exception { 137 ExpandableNotificationRow row = mNotificationTestHelper.createRow(); 138 AboveShelfChangedListener listener = mock(AboveShelfChangedListener.class); 139 row.setAboveShelfChangedListener(listener); 140 row.setHeadsUp(true); 141 verify(listener).onAboveShelfStateChanged(true); 142 } 143 144 @Test 145 public void testAboveShelfChangedListenerCalledPinned() throws Exception { 146 ExpandableNotificationRow row = mNotificationTestHelper.createRow(); 147 AboveShelfChangedListener listener = mock(AboveShelfChangedListener.class); 148 row.setAboveShelfChangedListener(listener); 149 row.setPinned(true); 150 verify(listener).onAboveShelfStateChanged(true); 151 } 152 153 @Test 154 public void testAboveShelfChangedListenerCalledHeadsUpGoingAway() throws Exception { 155 ExpandableNotificationRow row = mNotificationTestHelper.createRow(); 156 AboveShelfChangedListener listener = mock(AboveShelfChangedListener.class); 157 row.setAboveShelfChangedListener(listener); 158 row.setHeadsUpAnimatingAway(true); 159 verify(listener).onAboveShelfStateChanged(true); 160 } 161 @Test 162 public void testAboveShelfChangedListenerCalledWhenGoingBelow() throws Exception { 163 ExpandableNotificationRow row = mNotificationTestHelper.createRow(); 164 row.setHeadsUp(true); 165 AboveShelfChangedListener listener = mock(AboveShelfChangedListener.class); 166 row.setAboveShelfChangedListener(listener); 167 row.setAboveShelf(false); 168 verify(listener).onAboveShelfStateChanged(false); 169 } 170 171 @Test 172 public void testClickSound() throws Exception { 173 assertTrue("Should play sounds by default.", mGroupRow.isSoundEffectsEnabled()); 174 mGroupRow.setDark(true /* dark */, false /* fade */, 0 /* delay */); 175 mGroupRow.setSecureStateProvider(()-> false); 176 assertFalse("Shouldn't play sounds when dark and trusted.", 177 mGroupRow.isSoundEffectsEnabled()); 178 mGroupRow.setSecureStateProvider(()-> true); 179 assertTrue("Should always play sounds when not trusted.", 180 mGroupRow.isSoundEffectsEnabled()); 181 } 182 183 @Test 184 public void testSetDismissed_longPressListenerRemoved() { 185 ExpandableNotificationRow.LongPressListener listener = 186 mock(ExpandableNotificationRow.LongPressListener.class); 187 mGroupRow.setLongPressListener(listener); 188 mGroupRow.doLongClickCallback(0,0); 189 verify(listener, times(1)).onLongPress(eq(mGroupRow), eq(0), eq(0), 190 any(NotificationMenuRowPlugin.MenuItem.class)); 191 reset(listener); 192 193 mGroupRow.setDismissed(true); 194 mGroupRow.doLongClickCallback(0,0); 195 verify(listener, times(0)).onLongPress(eq(mGroupRow), eq(0), eq(0), 196 any(NotificationMenuRowPlugin.MenuItem.class)); 197 } 198 199 @Test 200 public void testShowAppOps_noHeader() { 201 // public notification is custom layout - no header 202 mGroupRow.setSensitive(true, true); 203 mGroupRow.setAppOpsOnClickListener(null); 204 mGroupRow.showAppOpsIcons(null); 205 } 206 207 @Test 208 public void testShowAppOpsIcons_header() { 209 NotificationHeaderView mockHeader = mock(NotificationHeaderView.class); 210 211 NotificationContentView publicLayout = mock(NotificationContentView.class); 212 mGroupRow.setPublicLayout(publicLayout); 213 NotificationContentView privateLayout = mock(NotificationContentView.class); 214 mGroupRow.setPrivateLayout(privateLayout); 215 NotificationChildrenContainer mockContainer = mock(NotificationChildrenContainer.class); 216 when(mockContainer.getNotificationChildCount()).thenReturn(1); 217 when(mockContainer.getHeaderView()).thenReturn(mockHeader); 218 mGroupRow.setChildrenContainer(mockContainer); 219 220 ArraySet<Integer> ops = new ArraySet<>(); 221 ops.add(AppOpsManager.OP_ANSWER_PHONE_CALLS); 222 mGroupRow.showAppOpsIcons(ops); 223 224 verify(mockHeader, times(1)).showAppOpsIcons(ops); 225 verify(privateLayout, times(1)).showAppOpsIcons(ops); 226 verify(publicLayout, times(1)).showAppOpsIcons(ops); 227 228 } 229 230 @Test 231 public void testAppOpsOnClick() { 232 ExpandableNotificationRow.OnAppOpsClickListener l = mock( 233 ExpandableNotificationRow.OnAppOpsClickListener.class); 234 View view = mock(View.class); 235 236 mGroupRow.setAppOpsOnClickListener(l); 237 238 mGroupRow.getAppOpsOnClickListener().onClick(view); 239 verify(l, times(1)).onClick(any(), anyInt(), anyInt(), any()); 240 } 241 242 @Test 243 public void testHeadsUpAnimatingAwayListener() { 244 mGroupRow.setHeadsUpAnimatingAway(true); 245 Assert.assertEquals(true, mHeadsUpAnimatingAway); 246 mGroupRow.setHeadsUpAnimatingAway(false); 247 Assert.assertEquals(false, mHeadsUpAnimatingAway); 248 } 249 250 @Test 251 public void testPerformDismissWithBlockingHelper_falseWhenBlockingHelperIsntShown() { 252 when(mBlockingHelperManager.perhapsShowBlockingHelper( 253 eq(mGroupRow), any(NotificationMenuRowPlugin.class))).thenReturn(false); 254 255 assertFalse( 256 mGroupRow.performDismissWithBlockingHelper(false /* fromAccessibility */)); 257 } 258 259 @Test 260 public void testPerformDismissWithBlockingHelper_doesntPerformOnGroupSummary() { 261 ExpandableNotificationRow childRow = mGroupRow.getChildrenContainer().getViewAtPosition(0); 262 when(mBlockingHelperManager.perhapsShowBlockingHelper(eq(childRow), any(NotificationMenuRowPlugin.class))) 263 .thenReturn(true); 264 265 assertTrue( 266 childRow.performDismissWithBlockingHelper(false /* fromAccessibility */)); 267 268 verify(mBlockingHelperManager, times(1)) 269 .perhapsShowBlockingHelper(eq(childRow), any(NotificationMenuRowPlugin.class)); 270 verify(mBlockingHelperManager, times(0)) 271 .perhapsShowBlockingHelper(eq(mGroupRow), any(NotificationMenuRowPlugin.class)); 272 } 273 274 @Test 275 public void testIsBlockingHelperShowing_isCorrectlyUpdated() { 276 mGroupRow.setBlockingHelperShowing(true); 277 assertTrue(mGroupRow.isBlockingHelperShowing()); 278 279 mGroupRow.setBlockingHelperShowing(false); 280 assertFalse(mGroupRow.isBlockingHelperShowing()); 281 } 282 283 @Test 284 public void testGetNumUniqueChildren_defaultChannel() { 285 assertEquals(1, mGroupRow.getNumUniqueChannels()); 286 } 287 288 @Test 289 public void testGetNumUniqueChildren_multiChannel() { 290 List<ExpandableNotificationRow> childRows = 291 mGroupRow.getChildrenContainer().getNotificationChildren(); 292 // Give each child a unique channel id/name. 293 int i = 0; 294 for (ExpandableNotificationRow childRow : childRows) { 295 childRow.getEntry().channel = 296 new NotificationChannel("id" + i, "dinnertime" + i, IMPORTANCE_DEFAULT); 297 i++; 298 } 299 300 assertEquals(3, mGroupRow.getNumUniqueChannels()); 301 } 302 303 @Test 304 public void testIconScrollXAfterTranslationAndReset() throws Exception { 305 mGroupRow.setTranslation(50); 306 assertEquals(50, -mGroupRow.getEntry().expandedIcon.getScrollX()); 307 308 mGroupRow.resetTranslation(); 309 assertEquals(0, mGroupRow.getEntry().expandedIcon.getScrollX()); 310 } 311} 312