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 17 package com.android.systemui.statusbar.notification.row; 18 19 import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL; 20 import static com.android.systemui.statusbar.notification.row.NotificationTestHelper.PKG; 21 import static com.android.systemui.statusbar.notification.row.NotificationTestHelper.USER_HANDLE; 22 23 import static com.google.common.truth.Truth.assertThat; 24 25 import static org.junit.Assert.assertEquals; 26 import static org.junit.Assert.assertFalse; 27 import static org.junit.Assert.assertTrue; 28 import static org.mockito.ArgumentMatchers.any; 29 import static org.mockito.ArgumentMatchers.anyBoolean; 30 import static org.mockito.ArgumentMatchers.anyInt; 31 import static org.mockito.ArgumentMatchers.eq; 32 import static org.mockito.Mockito.mock; 33 import static org.mockito.Mockito.never; 34 import static org.mockito.Mockito.reset; 35 import static org.mockito.Mockito.spy; 36 import static org.mockito.Mockito.times; 37 import static org.mockito.Mockito.verify; 38 import static org.mockito.Mockito.verifyNoMoreInteractions; 39 import static org.mockito.Mockito.when; 40 41 import android.app.Notification; 42 import android.content.Context; 43 import android.graphics.Color; 44 import android.graphics.drawable.AnimatedVectorDrawable; 45 import android.graphics.drawable.AnimationDrawable; 46 import android.graphics.drawable.Drawable; 47 import android.os.UserHandle; 48 import android.platform.test.annotations.EnableFlags; 49 import android.testing.TestableLooper; 50 import android.testing.TestableLooper.RunWithLooper; 51 import android.util.DisplayMetrics; 52 import android.view.View; 53 import android.widget.ImageView; 54 55 import androidx.test.ext.junit.runners.AndroidJUnit4; 56 import androidx.test.filters.SmallTest; 57 58 import com.android.internal.R; 59 import com.android.internal.widget.CachingIconView; 60 import com.android.systemui.SysuiTestCase; 61 import com.android.systemui.SysuiTestableContext; 62 import com.android.systemui.flags.FakeFeatureFlagsClassic; 63 import com.android.systemui.flags.Flags; 64 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; 65 import com.android.systemui.plugins.statusbar.StatusBarStateController; 66 import com.android.systemui.statusbar.notification.AboveShelfChangedListener; 67 import com.android.systemui.statusbar.notification.FeedbackIcon; 68 import com.android.systemui.statusbar.notification.SourceType; 69 import com.android.systemui.statusbar.notification.collection.NotificationEntry; 70 import com.android.systemui.statusbar.notification.row.ExpandableView.OnHeightChangedListener; 71 import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper; 72 import com.android.systemui.statusbar.notification.shared.NotificationContentAlphaOptimization; 73 import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer; 74 import com.android.systemui.statusbar.phone.KeyguardBypassController; 75 76 import org.junit.Assert; 77 import org.junit.Before; 78 import org.junit.Rule; 79 import org.junit.Test; 80 import org.junit.runner.RunWith; 81 import org.mockito.Mockito; 82 import org.mockito.junit.MockitoJUnit; 83 import org.mockito.junit.MockitoRule; 84 85 import java.util.Arrays; 86 import java.util.List; 87 import java.util.function.Consumer; 88 89 @SmallTest 90 @RunWith(AndroidJUnit4.class) 91 @RunWithLooper 92 public class ExpandableNotificationRowTest extends SysuiTestCase { 93 94 private final FakeFeatureFlagsClassic mFeatureFlags = new FakeFeatureFlagsClassic(); 95 private NotificationTestHelper mNotificationTestHelper; 96 @Rule public MockitoRule mockito = MockitoJUnit.rule(); 97 98 @Before setUp()99 public void setUp() throws Exception { 100 allowTestableLooperAsMainThread(); 101 mFeatureFlags.set(Flags.ENABLE_NOTIFICATIONS_SIMULATE_SLOW_MEASURE, false); 102 mNotificationTestHelper = new NotificationTestHelper( 103 mContext, 104 mDependency, 105 TestableLooper.get(this), 106 mFeatureFlags); 107 mNotificationTestHelper.setDefaultInflationFlags(FLAG_CONTENT_VIEW_ALL); 108 } 109 110 @Test testCanShowHeadsUp_notOnKeyguard_true()111 public void testCanShowHeadsUp_notOnKeyguard_true() throws Exception { 112 ExpandableNotificationRow row = mNotificationTestHelper.createRow(); 113 114 row.setOnKeyguard(false); 115 116 assertTrue(row.canShowHeadsUp()); 117 } 118 119 @Test testCanShowHeadsUp_dozing_true()120 public void testCanShowHeadsUp_dozing_true() throws Exception { 121 ExpandableNotificationRow row = mNotificationTestHelper.createRow(); 122 123 StatusBarStateController statusBarStateControllerMock = 124 mNotificationTestHelper.getStatusBarStateController(); 125 when(statusBarStateControllerMock.isDozing()).thenReturn(true); 126 127 assertTrue(row.canShowHeadsUp()); 128 } 129 130 @Test testCanShowHeadsUp_bypassEnabled_true()131 public void testCanShowHeadsUp_bypassEnabled_true() throws Exception { 132 ExpandableNotificationRow row = mNotificationTestHelper.createRow(); 133 134 KeyguardBypassController keyguardBypassControllerMock = 135 mNotificationTestHelper.getKeyguardBypassController(); 136 when(keyguardBypassControllerMock.getBypassEnabled()).thenReturn(true); 137 138 assertTrue(row.canShowHeadsUp()); 139 } 140 141 @Test testCanShowHeadsUp_stickyAndNotDemoted_true()142 public void testCanShowHeadsUp_stickyAndNotDemoted_true() throws Exception { 143 ExpandableNotificationRow row = mNotificationTestHelper.createStickyRow(); 144 145 assertTrue(row.canShowHeadsUp()); 146 } 147 148 @Test testCanShowHeadsUp_false()149 public void testCanShowHeadsUp_false() throws Exception { 150 ExpandableNotificationRow row = mNotificationTestHelper.createRow(); 151 152 row.setOnKeyguard(true); 153 154 StatusBarStateController statusBarStateControllerMock = 155 mNotificationTestHelper.getStatusBarStateController(); 156 when(statusBarStateControllerMock.isDozing()).thenReturn(false); 157 158 KeyguardBypassController keyguardBypassControllerMock = 159 mNotificationTestHelper.getKeyguardBypassController(); 160 when(keyguardBypassControllerMock.getBypassEnabled()).thenReturn(false); 161 162 assertFalse(row.canShowHeadsUp()); 163 } 164 165 @Test testUpdateBackgroundColors_isRecursive()166 public void testUpdateBackgroundColors_isRecursive() throws Exception { 167 ExpandableNotificationRow group = mNotificationTestHelper.createGroup(); 168 group.setTintColor(Color.RED); 169 group.getChildNotificationAt(0).setTintColor(Color.GREEN); 170 group.getChildNotificationAt(1).setTintColor(Color.BLUE); 171 172 assertThat(group.getCurrentBackgroundTint()).isEqualTo(Color.RED); 173 assertThat(group.getChildNotificationAt(0).getCurrentBackgroundTint()) 174 .isEqualTo(Color.GREEN); 175 assertThat(group.getChildNotificationAt(1).getCurrentBackgroundTint()) 176 .isEqualTo(Color.BLUE); 177 178 group.updateBackgroundColors(); 179 180 int resetTint = group.getCurrentBackgroundTint(); 181 assertThat(resetTint).isNotEqualTo(Color.RED); 182 assertThat(group.getChildNotificationAt(0).getCurrentBackgroundTint()) 183 .isEqualTo(resetTint); 184 assertThat(group.getChildNotificationAt(1).getCurrentBackgroundTint()) 185 .isEqualTo(resetTint); 186 } 187 188 @Test testSetSensitiveOnNotifRowNotifiesOfHeightChange()189 public void testSetSensitiveOnNotifRowNotifiesOfHeightChange() throws Exception { 190 // GIVEN a sensitive notification row that's currently redacted 191 ExpandableNotificationRow row = mNotificationTestHelper.createRow(); 192 measureAndLayout(row); 193 row.setHideSensitiveForIntrinsicHeight(true); 194 row.setSensitive(true, true); 195 assertThat(row.getShowingLayout()).isSameInstanceAs(row.getPublicLayout()); 196 assertThat(row.getIntrinsicHeight()).isGreaterThan(0); 197 198 // GIVEN that the row has a height change listener 199 OnHeightChangedListener listener = mock(OnHeightChangedListener.class); 200 row.setOnHeightChangedListener(listener); 201 202 // WHEN the row is set to no longer be sensitive 203 row.setSensitive(false, true); 204 205 // VERIFY that the height change listener is invoked 206 assertThat(row.getShowingLayout()).isSameInstanceAs(row.getPrivateLayout()); 207 assertThat(row.getIntrinsicHeight()).isGreaterThan(0); 208 verify(listener).onHeightChanged(eq(row), eq(true)); 209 } 210 211 @Test testSetSensitiveOnGroupRowNotifiesOfHeightChange()212 public void testSetSensitiveOnGroupRowNotifiesOfHeightChange() throws Exception { 213 // GIVEN a sensitive group row that's currently redacted 214 ExpandableNotificationRow group = mNotificationTestHelper.createGroup(); 215 measureAndLayout(group); 216 group.setHideSensitiveForIntrinsicHeight(true); 217 group.setSensitive(true, true); 218 assertThat(group.getShowingLayout()).isSameInstanceAs(group.getPublicLayout()); 219 assertThat(group.getIntrinsicHeight()).isGreaterThan(0); 220 221 // GIVEN that the row has a height change listener 222 OnHeightChangedListener listener = mock(OnHeightChangedListener.class); 223 group.setOnHeightChangedListener(listener); 224 225 // WHEN the row is set to no longer be sensitive 226 group.setSensitive(false, true); 227 228 // VERIFY that the height change listener is invoked 229 assertThat(group.getShowingLayout()).isSameInstanceAs(group.getPrivateLayout()); 230 assertThat(group.getIntrinsicHeight()).isGreaterThan(0); 231 verify(listener).onHeightChanged(eq(group), eq(true)); 232 } 233 234 @Test testSetSensitiveOnPublicRowDoesNotNotifyOfHeightChange()235 public void testSetSensitiveOnPublicRowDoesNotNotifyOfHeightChange() throws Exception { 236 // create a notification row whose public version is identical 237 Notification publicNotif = mNotificationTestHelper.createNotification(); 238 publicNotif.publicVersion = mNotificationTestHelper.createNotification(); 239 ExpandableNotificationRow publicRow = mNotificationTestHelper.createRow(publicNotif); 240 241 // GIVEN a sensitive public row that's currently redacted 242 measureAndLayout(publicRow); 243 publicRow.setHideSensitiveForIntrinsicHeight(true); 244 publicRow.setSensitive(true, true); 245 assertThat(publicRow.getShowingLayout()).isSameInstanceAs(publicRow.getPublicLayout()); 246 assertThat(publicRow.getIntrinsicHeight()).isGreaterThan(0); 247 248 // GIVEN that the row has a height change listener 249 OnHeightChangedListener listener = mock(OnHeightChangedListener.class); 250 publicRow.setOnHeightChangedListener(listener); 251 252 // WHEN the row is set to no longer be sensitive 253 publicRow.setSensitive(false, true); 254 255 // VERIFY that the height change listener is not invoked, because the height didn't change 256 assertThat(publicRow.getShowingLayout()).isSameInstanceAs(publicRow.getPrivateLayout()); 257 assertThat(publicRow.getIntrinsicHeight()).isGreaterThan(0); 258 assertThat(publicRow.getPrivateLayout().getMinHeight()) 259 .isEqualTo(publicRow.getPublicLayout().getMinHeight()); 260 verify(listener, never()).onHeightChanged(eq(publicRow), anyBoolean()); 261 } 262 measureAndLayout(ExpandableNotificationRow row)263 private void measureAndLayout(ExpandableNotificationRow row) { 264 DisplayMetrics dm = new DisplayMetrics(); 265 getContext().getDisplay().getRealMetrics(dm); 266 int width = (int) Math.ceil(400f * dm.density); 267 int height = (int) Math.ceil(600f * dm.density); 268 269 row.measure( 270 View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY), 271 View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.UNSPECIFIED) 272 ); 273 row.layout(0, 0, row.getMeasuredWidth(), row.getMeasuredHeight()); 274 } 275 276 @Test testGroupSummaryNotShowingIconWhenPublic()277 public void testGroupSummaryNotShowingIconWhenPublic() throws Exception { 278 ExpandableNotificationRow group = mNotificationTestHelper.createGroup(); 279 280 group.setSensitive(true, true); 281 group.setHideSensitiveForIntrinsicHeight(true); 282 assertTrue(group.isSummaryWithChildren()); 283 assertFalse(group.isShowingIcon()); 284 } 285 286 @Test testNotificationHeaderVisibleWhenAnimating()287 public void testNotificationHeaderVisibleWhenAnimating() throws Exception { 288 ExpandableNotificationRow group = mNotificationTestHelper.createGroup(); 289 290 group.setSensitive(true, true); 291 group.setHideSensitive(true, false, 0, 0); 292 group.setHideSensitive(false, true, 0, 0); 293 assertEquals(View.VISIBLE, group.getChildrenContainer().getVisibleWrapper() 294 .getNotificationHeader().getVisibility()); 295 } 296 297 @Test testUserLockedResetEvenWhenNoChildren()298 public void testUserLockedResetEvenWhenNoChildren() throws Exception { 299 ExpandableNotificationRow group = mNotificationTestHelper.createGroup(); 300 301 group.setUserLocked(true); 302 group.setUserLocked(false); 303 assertFalse("The childrencontainer should not be userlocked but is, the state " 304 + "seems out of sync.", group.getChildrenContainer().isUserLocked()); 305 } 306 307 @Test 308 @EnableFlags(NotificationContentAlphaOptimization.FLAG_NAME) setHideSensitive_shouldNotDisturbAnimation()309 public void setHideSensitive_shouldNotDisturbAnimation() throws Exception { 310 //Given: A row that is during alpha animation 311 ExpandableNotificationRow row = mNotificationTestHelper.createRow(); 312 313 assertEquals(row.getPrivateLayout(), row.getContentView()); 314 row.setContentAlpha(0.5f); 315 316 //When: Set its hideSensitive without changing the content view to show 317 row.setHideSensitive( 318 /* hideSensitive= */ false, 319 /* animated= */ false, 320 /* delay= */ 0L, 321 /* duration= */ 0L 322 ); 323 assertEquals(row.getPrivateLayout(), row.getContentView()); 324 325 //Then: The alpha value should not be reset 326 assertEquals(0.5f, row.getPrivateLayout().getAlpha(), 0); 327 } 328 329 @Test 330 @EnableFlags(NotificationContentAlphaOptimization.FLAG_NAME) setHideSensitive_changeContent_shouldNotDisturbAnimation()331 public void setHideSensitive_changeContent_shouldNotDisturbAnimation() throws Exception { 332 333 // Given: A sensitive row that has public version but is not hiding sensitive, 334 // and is during an animation that sets its alpha value to be 0.5f 335 Notification publicNotif = mNotificationTestHelper.createNotification(); 336 publicNotif.publicVersion = mNotificationTestHelper.createNotification(); 337 ExpandableNotificationRow row = mNotificationTestHelper.createRow(publicNotif); 338 row.setSensitive(true, false); 339 row.setContentAlpha(0.5f); 340 341 assertEquals(0.5f, row.getPrivateLayout().getAlpha(), 0); 342 assertEquals(View.VISIBLE, row.getPrivateLayout().getVisibility()); 343 344 // When: Change its hideSensitive and changes the content view to show the public version 345 row.setHideSensitive( 346 /* hideSensitive= */ true, 347 /* animated= */ false, 348 /* delay= */ 0L, 349 /* duration= */ 0L 350 ); 351 352 // Then: The alpha value of private layout should be reset to 1, private layout be 353 // INVISIBLE; 354 // The alpha value of public layout should be 0.5 to preserve the animation state, public 355 // layout should be VISIBLE 356 assertEquals(View.INVISIBLE, row.getPrivateLayout().getVisibility()); 357 assertEquals(1f, row.getPrivateLayout().getAlpha(), 0); 358 assertEquals(View.VISIBLE, row.getPublicLayout().getVisibility()); 359 assertEquals(0.5f, row.getPublicLayout().getAlpha(), 0); 360 } 361 362 @Test testReinflatedOnDensityChange()363 public void testReinflatedOnDensityChange() throws Exception { 364 ExpandableNotificationRow row = mNotificationTestHelper.createRow(); 365 NotificationChildrenContainer mockContainer = mock(NotificationChildrenContainer.class); 366 row.setChildrenContainer(mockContainer); 367 368 row.onDensityOrFontScaleChanged(); 369 370 verify(mockContainer).reInflateViews(any(), any()); 371 } 372 373 @Test testIconColorShouldBeUpdatedWhenSensitive()374 public void testIconColorShouldBeUpdatedWhenSensitive() throws Exception { 375 ExpandableNotificationRow row = spy(mNotificationTestHelper.createRow( 376 FLAG_CONTENT_VIEW_ALL)); 377 row.setSensitive(true, true); 378 row.setHideSensitive(true, false, 0, 0); 379 verify(row).updateShelfIconColor(); 380 } 381 382 @Test testAboveShelfChangedListenerCalled()383 public void testAboveShelfChangedListenerCalled() throws Exception { 384 ExpandableNotificationRow row = mNotificationTestHelper.createRow(); 385 AboveShelfChangedListener listener = mock(AboveShelfChangedListener.class); 386 row.setAboveShelfChangedListener(listener); 387 row.setHeadsUp(true); 388 verify(listener).onAboveShelfStateChanged(true); 389 } 390 391 @Test testAboveShelfChangedListenerCalledPinned()392 public void testAboveShelfChangedListenerCalledPinned() throws Exception { 393 ExpandableNotificationRow row = mNotificationTestHelper.createRow(); 394 AboveShelfChangedListener listener = mock(AboveShelfChangedListener.class); 395 row.setAboveShelfChangedListener(listener); 396 row.setPinned(true); 397 verify(listener).onAboveShelfStateChanged(true); 398 } 399 400 @Test testAboveShelfChangedListenerCalledHeadsUpAnimatingAway()401 public void testAboveShelfChangedListenerCalledHeadsUpAnimatingAway() throws Exception { 402 ExpandableNotificationRow row = mNotificationTestHelper.createRow(); 403 AboveShelfChangedListener listener = mock(AboveShelfChangedListener.class); 404 row.setAboveShelfChangedListener(listener); 405 row.setHeadsUpAnimatingAway(true); 406 verify(listener).onAboveShelfStateChanged(true); 407 } 408 @Test testAboveShelfChangedListenerCalledWhenGoingBelow()409 public void testAboveShelfChangedListenerCalledWhenGoingBelow() throws Exception { 410 ExpandableNotificationRow row = mNotificationTestHelper.createRow(); 411 AboveShelfChangedListener listener = mock(AboveShelfChangedListener.class); 412 row.setAboveShelfChangedListener(listener); 413 Mockito.reset(listener); 414 row.setHeadsUp(true); 415 row.setAboveShelf(false); 416 verify(listener).onAboveShelfStateChanged(false); 417 } 418 419 @Test testClickSound()420 public void testClickSound() throws Exception { 421 ExpandableNotificationRow group = mNotificationTestHelper.createGroup(); 422 423 assertTrue("Should play sounds by default.", group.isSoundEffectsEnabled()); 424 StatusBarStateController mock = mNotificationTestHelper.getStatusBarStateController(); 425 when(mock.isDozing()).thenReturn(true); 426 group.setSecureStateProvider(()-> false); 427 assertFalse("Shouldn't play sounds when dark and trusted.", 428 group.isSoundEffectsEnabled()); 429 group.setSecureStateProvider(()-> true); 430 assertTrue("Should always play sounds when not trusted.", 431 group.isSoundEffectsEnabled()); 432 } 433 434 @Test testSetDismissed_longPressListenerRemoved()435 public void testSetDismissed_longPressListenerRemoved() throws Exception { 436 ExpandableNotificationRow group = mNotificationTestHelper.createGroup(); 437 438 ExpandableNotificationRow.LongPressListener listener = 439 mock(ExpandableNotificationRow.LongPressListener.class); 440 group.setLongPressListener(listener); 441 group.doLongClickCallback(0, 0); 442 verify(listener, times(1)).onLongPress(eq(group), eq(0), eq(0), 443 any(NotificationMenuRowPlugin.MenuItem.class)); 444 reset(listener); 445 446 group.dismiss(true); 447 group.doLongClickCallback(0, 0); 448 verify(listener, times(0)).onLongPress(eq(group), eq(0), eq(0), 449 any(NotificationMenuRowPlugin.MenuItem.class)); 450 } 451 452 @Test testFeedback_noHeader()453 public void testFeedback_noHeader() throws Exception { 454 ExpandableNotificationRow groupRow = mNotificationTestHelper.createGroup(); 455 456 // public notification is custom layout - no header 457 groupRow.setSensitive(true, true); 458 groupRow.setOnFeedbackClickListener(null); 459 groupRow.setFeedbackIcon(null); 460 } 461 462 @Test testFeedback_header()463 public void testFeedback_header() throws Exception { 464 ExpandableNotificationRow group = mNotificationTestHelper.createGroup(); 465 466 NotificationContentView publicLayout = mock(NotificationContentView.class); 467 group.setPublicLayout(publicLayout); 468 NotificationContentView privateLayout = mock(NotificationContentView.class); 469 group.setPrivateLayout(privateLayout); 470 NotificationChildrenContainer mockContainer = mock(NotificationChildrenContainer.class); 471 when(mockContainer.getNotificationChildCount()).thenReturn(1); 472 group.setChildrenContainer(mockContainer); 473 474 final boolean show = true; 475 final FeedbackIcon icon = new FeedbackIcon( 476 R.drawable.ic_feedback_alerted, R.string.notification_feedback_indicator_alerted); 477 group.setFeedbackIcon(icon); 478 479 verify(mockContainer, times(1)).setFeedbackIcon(icon); 480 verify(privateLayout, times(1)).setFeedbackIcon(icon); 481 verify(publicLayout, times(1)).setFeedbackIcon(icon); 482 } 483 484 @Test testFeedbackOnClick()485 public void testFeedbackOnClick() throws Exception { 486 ExpandableNotificationRow group = mNotificationTestHelper.createGroup(); 487 488 ExpandableNotificationRow.CoordinateOnClickListener l = mock( 489 ExpandableNotificationRow.CoordinateOnClickListener.class); 490 View view = mock(View.class); 491 492 group.setOnFeedbackClickListener(l); 493 494 group.getFeedbackOnClickListener().onClick(view); 495 verify(l, times(1)).onClick(any(), anyInt(), anyInt(), any()); 496 } 497 498 @Test testHeadsUpAnimatingAwayListener()499 public void testHeadsUpAnimatingAwayListener() throws Exception { 500 ExpandableNotificationRow group = mNotificationTestHelper.createGroup(); 501 Consumer<Boolean> headsUpListener = mock(Consumer.class); 502 AboveShelfChangedListener aboveShelfChangedListener = mock(AboveShelfChangedListener.class); 503 group.setHeadsUpAnimatingAwayListener(headsUpListener); 504 group.setAboveShelfChangedListener(aboveShelfChangedListener); 505 506 group.setHeadsUpAnimatingAway(true); 507 verify(headsUpListener).accept(true); 508 verify(aboveShelfChangedListener).onAboveShelfStateChanged(true); 509 510 group.setHeadsUpAnimatingAway(false); 511 verify(headsUpListener).accept(false); 512 verify(aboveShelfChangedListener).onAboveShelfStateChanged(false); 513 } 514 515 @Test testIconScrollXAfterTranslationAndReset()516 public void testIconScrollXAfterTranslationAndReset() throws Exception { 517 ExpandableNotificationRow group = mNotificationTestHelper.createGroup(); 518 519 group.setDismissUsingRowTranslationX(false); 520 group.setTranslation(50); 521 assertEquals(50, -group.getEntry().getIcons().getShelfIcon().getScrollX()); 522 523 group.resetTranslation(); 524 assertEquals(0, group.getEntry().getIcons().getShelfIcon().getScrollX()); 525 } 526 527 @Test testIsExpanded_userExpanded()528 public void testIsExpanded_userExpanded() throws Exception { 529 ExpandableNotificationRow group = mNotificationTestHelper.createGroup(); 530 531 group.setExpandable(true); 532 Assert.assertFalse(group.isExpanded()); 533 group.setUserExpanded(true); 534 Assert.assertTrue(group.isExpanded()); 535 } 536 537 @Test testGetIsNonblockable()538 public void testGetIsNonblockable() throws Exception { 539 ExpandableNotificationRow row = 540 mNotificationTestHelper.createRow(mNotificationTestHelper.createNotification()); 541 row.setEntry(null); 542 543 assertTrue(row.getIsNonblockable()); 544 545 NotificationEntry entry = mock(NotificationEntry.class); 546 547 Mockito.doReturn(false, true).when(entry).isBlockable(); 548 row.setEntry(entry); 549 assertTrue(row.getIsNonblockable()); 550 assertFalse(row.getIsNonblockable()); 551 } 552 553 @Test testCanDismiss()554 public void testCanDismiss() throws Exception { 555 ExpandableNotificationRow row = 556 mNotificationTestHelper.createRow(mNotificationTestHelper.createNotification()); 557 when(mNotificationTestHelper.getDismissibilityProvider().isDismissable(row.getEntry())) 558 .thenReturn(true); 559 row.performDismiss(false); 560 verify(mNotificationTestHelper.getOnUserInteractionCallback()) 561 .registerFutureDismissal(any(), anyInt()); 562 } 563 564 @Test testCannotDismiss()565 public void testCannotDismiss() throws Exception { 566 ExpandableNotificationRow row = 567 mNotificationTestHelper.createRow(mNotificationTestHelper.createNotification()); 568 when(mNotificationTestHelper.getDismissibilityProvider().isDismissable(row.getEntry())) 569 .thenReturn(false); 570 row.performDismiss(false); 571 verify(mNotificationTestHelper.getOnUserInteractionCallback(), never()) 572 .registerFutureDismissal(any(), anyInt()); 573 } 574 575 @Test testAddChildNotification()576 public void testAddChildNotification() throws Exception { 577 ExpandableNotificationRow group = mNotificationTestHelper.createGroup(0); 578 ExpandableNotificationRow child = mNotificationTestHelper.createRow(); 579 580 group.addChildNotification(child); 581 582 Assert.assertEquals(child, group.getChildNotificationAt(0)); 583 Assert.assertEquals(group, child.getNotificationParent()); 584 Assert.assertTrue(child.isChildInGroup()); 585 } 586 587 @Test testAddChildNotification_childSkipped()588 public void testAddChildNotification_childSkipped() throws Exception { 589 ExpandableNotificationRow group = mNotificationTestHelper.createGroup(0); 590 ExpandableNotificationRow child = mNotificationTestHelper.createRow(); 591 child.setKeepInParentForDismissAnimation(true); 592 593 group.addChildNotification(child); 594 595 Assert.assertTrue(group.getAttachedChildren().isEmpty()); 596 Assert.assertNotEquals(group, child.getNotificationParent()); 597 verify(mNotificationTestHelper.getMockLogger()).logSkipAttachingKeepInParentChild( 598 /*child=*/ child.getEntry(), 599 /*newParent=*/ group.getEntry() 600 ); 601 } 602 603 @Test testRemoveChildNotification()604 public void testRemoveChildNotification() throws Exception { 605 ExpandableNotificationRow group = mNotificationTestHelper.createGroup(1); 606 ExpandableNotificationRow child = group.getAttachedChildren().get(0); 607 child.setKeepInParentForDismissAnimation(true); 608 609 group.removeChildNotification(child); 610 611 Assert.assertNull(child.getParent()); 612 Assert.assertNull(child.getNotificationParent()); 613 Assert.assertFalse(child.keepInParentForDismissAnimation()); 614 verifyNoMoreInteractions(mNotificationTestHelper.getMockLogger()); 615 } 616 617 @Test testRemoveChildrenWithKeepInParent_removesChildWithKeepInParent()618 public void testRemoveChildrenWithKeepInParent_removesChildWithKeepInParent() throws Exception { 619 ExpandableNotificationRow group = mNotificationTestHelper.createGroup(1); 620 ExpandableNotificationRow child = group.getAttachedChildren().get(0); 621 child.setKeepInParentForDismissAnimation(true); 622 623 group.removeChildrenWithKeepInParent(); 624 625 Assert.assertNull(child.getParent()); 626 Assert.assertNull(child.getNotificationParent()); 627 Assert.assertFalse(child.keepInParentForDismissAnimation()); 628 verify(mNotificationTestHelper.getMockLogger()).logKeepInParentChildDetached( 629 /*child=*/ child.getEntry(), 630 /*oldParent=*/ group.getEntry() 631 ); 632 } 633 634 @Test testRemoveChildrenWithKeepInParent_skipsChildrenWithoutKeepInParent()635 public void testRemoveChildrenWithKeepInParent_skipsChildrenWithoutKeepInParent() 636 throws Exception { 637 ExpandableNotificationRow group = mNotificationTestHelper.createGroup(1); 638 ExpandableNotificationRow child = group.getAttachedChildren().get(0); 639 640 group.removeChildrenWithKeepInParent(); 641 642 Assert.assertEquals(group, child.getNotificationParent()); 643 Assert.assertFalse(child.keepInParentForDismissAnimation()); 644 verify(mNotificationTestHelper.getMockLogger(), never()).logKeepInParentChildDetached( 645 /*child=*/ any(), 646 /*oldParent=*/ any() 647 ); 648 } 649 650 @Test applyRoundnessAndInvalidate_should_be_immediately_applied_on_childrenContainer()651 public void applyRoundnessAndInvalidate_should_be_immediately_applied_on_childrenContainer() 652 throws Exception { 653 ExpandableNotificationRow group = mNotificationTestHelper.createGroup(); 654 Assert.assertEquals(0f, group.getBottomRoundness(), 0.001f); 655 Assert.assertEquals(0f, group.getChildrenContainer().getBottomRoundness(), 0.001f); 656 657 group.requestBottomRoundness(1f, SourceType.from(""), false); 658 659 Assert.assertEquals(1f, group.getBottomRoundness(), 0.001f); 660 Assert.assertEquals(1f, group.getChildrenContainer().getBottomRoundness(), 0.001f); 661 } 662 663 @Test testSetContentAnimationRunning_Run()664 public void testSetContentAnimationRunning_Run() throws Exception { 665 // Create views for the notification row. 666 ExpandableNotificationRow row = mNotificationTestHelper.createRow(); 667 NotificationContentView publicLayout = mock(NotificationContentView.class); 668 row.setPublicLayout(publicLayout); 669 NotificationContentView privateLayout = mock(NotificationContentView.class); 670 row.setPrivateLayout(privateLayout); 671 672 row.setAnimationRunning(true); 673 verify(publicLayout, times(1)).setContentAnimationRunning(true); 674 verify(privateLayout, times(1)).setContentAnimationRunning(true); 675 } 676 677 @Test testSetContentAnimationRunning_Stop()678 public void testSetContentAnimationRunning_Stop() throws Exception { 679 // Create views for the notification row. 680 ExpandableNotificationRow row = mNotificationTestHelper.createRow(); 681 NotificationContentView publicLayout = mock(NotificationContentView.class); 682 row.setPublicLayout(publicLayout); 683 NotificationContentView privateLayout = mock(NotificationContentView.class); 684 row.setPrivateLayout(privateLayout); 685 686 row.setAnimationRunning(false); 687 verify(publicLayout, times(1)).setContentAnimationRunning(false); 688 verify(privateLayout, times(1)).setContentAnimationRunning(false); 689 } 690 691 @Test testSetContentAnimationRunningInGroupChild_Run()692 public void testSetContentAnimationRunningInGroupChild_Run() throws Exception { 693 // Creates parent views on groupRow. 694 ExpandableNotificationRow groupRow = mNotificationTestHelper.createGroup(); 695 NotificationContentView publicParentLayout = mock(NotificationContentView.class); 696 groupRow.setPublicLayout(publicParentLayout); 697 NotificationContentView privateParentLayout = mock(NotificationContentView.class); 698 groupRow.setPrivateLayout(privateParentLayout); 699 700 // Create child views on row. 701 ExpandableNotificationRow row = mNotificationTestHelper.createRow(); 702 NotificationContentView publicChildLayout = mock(NotificationContentView.class); 703 row.setPublicLayout(publicChildLayout); 704 NotificationContentView privateChildLayout = mock(NotificationContentView.class); 705 row.setPrivateLayout(privateChildLayout); 706 when(row.isGroupExpanded()).thenReturn(true); 707 setMockChildrenContainer(groupRow, row); 708 709 groupRow.setAnimationRunning(true); 710 verify(publicParentLayout, times(1)).setContentAnimationRunning(true); 711 verify(privateParentLayout, times(1)).setContentAnimationRunning(true); 712 // The child layouts should be started too. 713 verify(publicChildLayout, times(1)).setContentAnimationRunning(true); 714 verify(privateChildLayout, times(1)).setContentAnimationRunning(true); 715 } 716 717 718 @Test testSetIconAnimationRunningGroup_Run()719 public void testSetIconAnimationRunningGroup_Run() throws Exception { 720 // Create views for a group row. 721 ExpandableNotificationRow group = mNotificationTestHelper.createGroup(); 722 ExpandableNotificationRow child = mNotificationTestHelper.createRow(); 723 NotificationContentView publicParentLayout = mock(NotificationContentView.class); 724 group.setPublicLayout(publicParentLayout); 725 NotificationContentView privateParentLayout = mock(NotificationContentView.class); 726 group.setPrivateLayout(privateParentLayout); 727 when(group.isGroupExpanded()).thenReturn(true); 728 729 // Add the child to the group. 730 NotificationContentView publicChildLayout = mock(NotificationContentView.class); 731 child.setPublicLayout(publicChildLayout); 732 NotificationContentView privateChildLayout = mock(NotificationContentView.class); 733 child.setPrivateLayout(privateChildLayout); 734 when(child.isGroupExpanded()).thenReturn(true); 735 736 NotificationChildrenContainer mockContainer = 737 setMockChildrenContainer(group, child); 738 739 // Mock the children view wrappers, and give them each an icon. 740 NotificationViewWrapper mockViewWrapper = mock(NotificationViewWrapper.class); 741 when(mockContainer.getNotificationViewWrapper()).thenReturn(mockViewWrapper); 742 CachingIconView mockIcon = mock(CachingIconView.class); 743 when(mockViewWrapper.getIcon()).thenReturn(mockIcon); 744 745 NotificationViewWrapper mockLowPriorityViewWrapper = mock(NotificationViewWrapper.class); 746 when(mockContainer.getMinimizedGroupHeaderWrapper()).thenReturn(mockLowPriorityViewWrapper); 747 CachingIconView mockLowPriorityIcon = mock(CachingIconView.class); 748 when(mockLowPriorityViewWrapper.getIcon()).thenReturn(mockLowPriorityIcon); 749 750 // Give the icon image views drawables, so we can make sure they animate. 751 // We use both AnimationDrawables and AnimatedVectorDrawables to ensure both work. 752 AnimationDrawable drawable = mock(AnimationDrawable.class); 753 AnimatedVectorDrawable vectorDrawable = mock(AnimatedVectorDrawable.class); 754 setDrawableIconsInImageView(mockIcon, drawable, vectorDrawable); 755 756 AnimationDrawable lowPriDrawable = mock(AnimationDrawable.class); 757 AnimatedVectorDrawable lowPriVectorDrawable = mock(AnimatedVectorDrawable.class); 758 setDrawableIconsInImageView(mockLowPriorityIcon, lowPriDrawable, lowPriVectorDrawable); 759 760 group.setAnimationRunning(true); 761 verify(drawable, times(1)).start(); 762 verify(vectorDrawable, times(1)).start(); 763 verify(lowPriDrawable, times(1)).start(); 764 verify(lowPriVectorDrawable, times(1)).start(); 765 } 766 767 @Test isExpanded_hideSensitive_sensitiveNotExpanded()768 public void isExpanded_hideSensitive_sensitiveNotExpanded() 769 throws Exception { 770 // GIVEN 771 final ExpandableNotificationRow row = mNotificationTestHelper.createRow(); 772 row.setUserExpanded(true); 773 row.setOnKeyguard(false); 774 row.setSensitive(/* sensitive= */true, /* hideSensitive= */false); 775 row.setHideSensitiveForIntrinsicHeight(/* hideSensitive= */true); 776 777 // THEN 778 assertThat(row.isExpanded()).isFalse(); 779 } 780 781 @Test isExpanded_hideSensitive_nonSensitiveExpanded()782 public void isExpanded_hideSensitive_nonSensitiveExpanded() 783 throws Exception { 784 // GIVEN 785 final ExpandableNotificationRow row = mNotificationTestHelper.createRow(); 786 row.setUserExpanded(true); 787 row.setOnKeyguard(false); 788 row.setSensitive(/* sensitive= */true, /* hideSensitive= */false); 789 row.setHideSensitiveForIntrinsicHeight(/* hideSensitive= */false); 790 791 // THEN 792 assertThat(row.isExpanded()).isTrue(); 793 } 794 795 @Test onDisappearAnimationFinished_shouldSetFalse_headsUpAnimatingAway()796 public void onDisappearAnimationFinished_shouldSetFalse_headsUpAnimatingAway() 797 throws Exception { 798 final ExpandableNotificationRow row = mNotificationTestHelper.createRow(); 799 800 // Initial state: suppose heads up animation in progress 801 row.setHeadsUpAnimatingAway(true); 802 assertThat(row.isHeadsUpAnimatingAway()).isTrue(); 803 804 // on disappear animation ends 805 row.onAppearAnimationFinished(/* wasAppearing = */ false); 806 assertThat(row.isHeadsUpAnimatingAway()).isFalse(); 807 } 808 809 @Test onHUNAppear_cancelAppearDrawing_shouldResetAnimationState()810 public void onHUNAppear_cancelAppearDrawing_shouldResetAnimationState() throws Exception { 811 final ExpandableNotificationRow row = mNotificationTestHelper.createRow(); 812 813 row.performAddAnimation(/* delay */ 0, /* duration */ 1000, /* isHeadsUpAppear */ true); 814 815 waitForIdleSync(); 816 assertThat(row.isDrawingAppearAnimation()).isTrue(); 817 818 row.cancelAppearDrawing(); 819 820 waitForIdleSync(); 821 assertThat(row.isDrawingAppearAnimation()).isFalse(); 822 } 823 824 @Test onHUNDisappear_cancelAppearDrawing_shouldResetAnimationState()825 public void onHUNDisappear_cancelAppearDrawing_shouldResetAnimationState() throws Exception { 826 final ExpandableNotificationRow row = mNotificationTestHelper.createRow(); 827 828 row.performAddAnimation(/* delay */ 0, /* duration */ 1000, /* isHeadsUpAppear */ false); 829 830 waitForIdleSync(); 831 assertThat(row.isDrawingAppearAnimation()).isTrue(); 832 833 row.cancelAppearDrawing(); 834 835 waitForIdleSync(); 836 assertThat(row.isDrawingAppearAnimation()).isFalse(); 837 } 838 839 @Test imageResolver_sameNotificationUser_usesContext()840 public void imageResolver_sameNotificationUser_usesContext() throws Exception { 841 ExpandableNotificationRow row = mNotificationTestHelper.createRow(PKG, 842 USER_HANDLE.getUid(1234), USER_HANDLE); 843 844 assertThat(row.getImageResolver().getContext()).isSameInstanceAs(mContext); 845 } 846 847 @Test imageResolver_differentNotificationUser_createsUserContext()848 public void imageResolver_differentNotificationUser_createsUserContext() throws Exception { 849 UserHandle user = new UserHandle(33); 850 Context userContext = new SysuiTestableContext(mContext); 851 mContext.prepareCreateContextAsUser(user, userContext); 852 853 ExpandableNotificationRow row = mNotificationTestHelper.createRow(PKG, 854 user.getUid(1234), user); 855 856 assertThat(row.getImageResolver().getContext()).isSameInstanceAs(userContext); 857 } 858 setDrawableIconsInImageView(CachingIconView icon, Drawable iconDrawable, Drawable rightIconDrawable)859 private void setDrawableIconsInImageView(CachingIconView icon, Drawable iconDrawable, 860 Drawable rightIconDrawable) { 861 ImageView iconView = mock(ImageView.class); 862 when(icon.findViewById(com.android.internal.R.id.icon)).thenReturn(iconView); 863 when(iconView.getDrawable()).thenReturn(iconDrawable); 864 865 ImageView rightIconView = mock(ImageView.class); 866 when(icon.findViewById(com.android.internal.R.id.right_icon)).thenReturn(rightIconView); 867 when(rightIconView.getDrawable()).thenReturn(rightIconDrawable); 868 } 869 setMockChildrenContainer( ExpandableNotificationRow parentRow, ExpandableNotificationRow childRow)870 private NotificationChildrenContainer setMockChildrenContainer( 871 ExpandableNotificationRow parentRow, ExpandableNotificationRow childRow) { 872 List<ExpandableNotificationRow> rowList = Arrays.asList(childRow); 873 NotificationChildrenContainer mockContainer = mock(NotificationChildrenContainer.class); 874 when(mockContainer.getNotificationChildCount()).thenReturn(1); 875 when(mockContainer.getAttachedChildren()).thenReturn(rowList); 876 parentRow.setChildrenContainer(mockContainer); 877 return mockContainer; 878 } 879 } 880