diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java index 396a3a7834fb8..4a6c7d27516c8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java @@ -16,18 +16,26 @@ package com.android.systemui.statusbar.notification; +import static com.android.systemui.Dependency.MAIN_HANDLER_NAME; + +import android.os.Handler; +import android.os.SystemClock; import android.view.View; import androidx.collection.ArraySet; +import com.android.systemui.Dumpable; import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; +import java.io.FileDescriptor; +import java.io.PrintWriter; import java.util.ArrayList; import javax.inject.Inject; +import javax.inject.Named; import javax.inject.Singleton; /** @@ -35,14 +43,19 @@ import javax.inject.Singleton; * and reorder at the right time when they are out of view. */ @Singleton -public class VisualStabilityManager implements OnHeadsUpChangedListener { +public class VisualStabilityManager implements OnHeadsUpChangedListener, Dumpable { + + private static final long TEMPORARY_REORDERING_ALLOWED_DURATION = 1000; private final ArrayList mCallbacks = new ArrayList<>(); + private final Handler mHandler; private NotificationPresenter mPresenter; private boolean mPanelExpanded; private boolean mScreenOn; private boolean mReorderingAllowed; + private boolean mIsTemporaryReorderingAllowed; + private long mTemporaryReorderingStart; private VisibilityLocationProvider mVisibilityLocationProvider; private ArraySet mAllowedReorderViews = new ArraySet<>(); private ArraySet mLowPriorityReorderingViews = new ArraySet<>(); @@ -50,7 +63,12 @@ public class VisualStabilityManager implements OnHeadsUpChangedListener { private boolean mPulsing; @Inject - public VisualStabilityManager(NotificationEntryManager notificationEntryManager) { + public VisualStabilityManager( + NotificationEntryManager notificationEntryManager, + @Named(MAIN_HANDLER_NAME) Handler handler) { + + mHandler = handler; + notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() { @Override public void onPreEntryUpdated(NotificationEntry entry) { @@ -114,10 +132,11 @@ public class VisualStabilityManager implements OnHeadsUpChangedListener { } private void updateReorderingAllowed() { - boolean reorderingAllowed = (!mScreenOn || !mPanelExpanded) && !mPulsing; - boolean changed = reorderingAllowed && !mReorderingAllowed; + boolean reorderingAllowed = + (!mScreenOn || !mPanelExpanded || mIsTemporaryReorderingAllowed) && !mPulsing; + boolean changedToTrue = reorderingAllowed && !mReorderingAllowed; mReorderingAllowed = reorderingAllowed; - if (changed) { + if (changedToTrue) { notifyCallbacks(); } } @@ -179,6 +198,25 @@ public class VisualStabilityManager implements OnHeadsUpChangedListener { } } + /** + * Temporarily allows reordering of the entire shade for a period of 1000ms. Subsequent calls + * to this method will extend the timer. + */ + public void temporarilyAllowReordering() { + mHandler.removeCallbacks(mOnTemporaryReorderingExpired); + mHandler.postDelayed(mOnTemporaryReorderingExpired, TEMPORARY_REORDERING_ALLOWED_DURATION); + if (!mIsTemporaryReorderingAllowed) { + mTemporaryReorderingStart = SystemClock.elapsedRealtime(); + } + mIsTemporaryReorderingAllowed = true; + updateReorderingAllowed(); + } + + private final Runnable mOnTemporaryReorderingExpired = () -> { + mIsTemporaryReorderingAllowed = false; + updateReorderingAllowed(); + }; + /** * Notify the visual stability manager that a new view was added and should be allowed to * reorder next time. @@ -187,6 +225,20 @@ public class VisualStabilityManager implements OnHeadsUpChangedListener { mAddedChildren.add(view); } + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.println("VisualStabilityManager state:"); + pw.print(" mIsTemporaryReorderingAllowed="); pw.println(mIsTemporaryReorderingAllowed); + pw.print(" mTemporaryReorderingStart="); pw.println(mTemporaryReorderingStart); + + long now = SystemClock.elapsedRealtime(); + pw.print(" Temporary reordering window has been open for "); + pw.print(now - (mIsTemporaryReorderingAllowed ? mTemporaryReorderingStart : now)); + pw.println("ms"); + + pw.println(); + } + public interface Callback { /** * Called when reordering is allowed again. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java index f15d6b75020f9..fe890fb3b471d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java @@ -48,6 +48,7 @@ import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.notification.NotificationActivityStarter; +import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSaveListener; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; @@ -73,6 +74,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class); private final Context mContext; + private final VisualStabilityManager mVisualStabilityManager; private final AccessibilityManager mAccessibilityManager; // Dependencies: @@ -96,8 +98,11 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx protected String mKeyToRemoveOnGutsClosed; @Inject - public NotificationGutsManager(Context context) { + public NotificationGutsManager( + Context context, + VisualStabilityManager visualStabilityManager) { mContext = context; + mVisualStabilityManager = visualStabilityManager; mAccessibilityManager = (AccessibilityManager) mContext.getSystemService(Context.ACCESSIBILITY_SERVICE); } @@ -304,6 +309,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx notificationInfoView.bindNotification( pmUser, iNotificationManager, + mVisualStabilityManager, packageName, row.getEntry().channel, row.getUniqueChannels(), diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java index 626701c6cb349..0f6740d44d04a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java @@ -65,6 +65,7 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; +import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.logging.NotificationCounters; import java.lang.annotation.Retention; @@ -104,6 +105,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G private INotificationManager mINotificationManager; private PackageManager mPm; private MetricsLogger mMetricsLogger; + private VisualStabilityManager mVisualStabilityManager; private ChannelEditorDialogController mChannelEditorDialogController; private String mPackageName; @@ -244,6 +246,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G void bindNotification( final PackageManager pm, final INotificationManager iNotificationManager, + final VisualStabilityManager visualStabilityManager, final String pkg, final NotificationChannel notificationChannel, final Set uniqueChannelsInRow, @@ -256,7 +259,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G int importance, boolean wasShownHighPriority) throws RemoteException { - bindNotification(pm, iNotificationManager, pkg, notificationChannel, + bindNotification(pm, iNotificationManager, visualStabilityManager, pkg, notificationChannel, uniqueChannelsInRow, sbn, checkSaveListener, onSettingsClick, onAppSettingsClick, isDeviceProvisioned, isNonblockable, false /* isBlockingHelper */, @@ -266,6 +269,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G public void bindNotification( PackageManager pm, INotificationManager iNotificationManager, + VisualStabilityManager visualStabilityManager, String pkg, NotificationChannel notificationChannel, Set uniqueChannelsInRow, @@ -281,6 +285,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G throws RemoteException { mINotificationManager = iNotificationManager; mMetricsLogger = Dependency.get(MetricsLogger.class); + mVisualStabilityManager = visualStabilityManager; mChannelEditorDialogController = Dependency.get(ChannelEditorDialogController.class); mPackageName = pkg; mUniqueChannelsInRow = uniqueChannelsInRow; @@ -539,6 +544,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G new UpdateImportanceRunnable(mINotificationManager, mPackageName, mAppUid, mNumUniqueChannelsInRow == 1 ? mSingleNotificationChannel : null, mStartingChannelImportance, newImportance)); + mVisualStabilityManager.temporarilyAllowReordering(); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java index b35dcb62136c5..dd2630bc3417a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java @@ -16,17 +16,20 @@ package com.android.systemui.statusbar.notification; -import static junit.framework.Assert.assertEquals; - -import static org.mockito.Matchers.anyObject; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.os.Handler; import android.service.notification.StatusBarNotification; -import android.test.suitebuilder.annotation.SmallTest; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; -import androidx.test.runner.AndroidJUnit4; +import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.NotificationPresenter; @@ -38,11 +41,13 @@ import org.junit.Test; import org.junit.runner.RunWith; @SmallTest -@RunWith(AndroidJUnit4.class) +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper() public class VisualStabilityManagerTest extends SysuiTestCase { - private VisualStabilityManager mVisualStabilityManager = new VisualStabilityManager( - mock(NotificationEntryManager.class)); + private TestableLooper mTestableLooper; + + private VisualStabilityManager mVisualStabilityManager; private VisualStabilityManager.Callback mCallback = mock(VisualStabilityManager.Callback.class); private VisibilityLocationProvider mLocationProvider = mock(VisibilityLocationProvider.class); private ExpandableNotificationRow mRow = mock(ExpandableNotificationRow.class); @@ -50,46 +55,53 @@ public class VisualStabilityManagerTest extends SysuiTestCase { @Before public void setUp() { + mTestableLooper = TestableLooper.get(this); + mVisualStabilityManager = new VisualStabilityManager( + mock(NotificationEntryManager.class), + new Handler(mTestableLooper.getLooper())); + mVisualStabilityManager.setUpWithPresenter(mock(NotificationPresenter.class)); mVisualStabilityManager.setVisibilityLocationProvider(mLocationProvider); mEntry = new NotificationEntry(mock(StatusBarNotification.class)); mEntry.setRow(mRow); + + when(mRow.getEntry()).thenReturn(mEntry); } @Test public void testPanelExpansion() { mVisualStabilityManager.setPanelExpanded(true); mVisualStabilityManager.setScreenOn(true); - assertEquals(mVisualStabilityManager.canReorderNotification(mRow), false); + assertFalse(mVisualStabilityManager.canReorderNotification(mRow)); mVisualStabilityManager.setPanelExpanded(false); - assertEquals(mVisualStabilityManager.canReorderNotification(mRow), true); + assertTrue(mVisualStabilityManager.canReorderNotification(mRow)); } @Test public void testScreenOn() { mVisualStabilityManager.setPanelExpanded(true); mVisualStabilityManager.setScreenOn(true); - assertEquals(mVisualStabilityManager.canReorderNotification(mRow), false); + assertFalse(mVisualStabilityManager.canReorderNotification(mRow)); mVisualStabilityManager.setScreenOn(false); - assertEquals(mVisualStabilityManager.canReorderNotification(mRow), true); + assertTrue(mVisualStabilityManager.canReorderNotification(mRow)); } @Test public void testReorderingAllowedChangesScreenOn() { mVisualStabilityManager.setPanelExpanded(true); mVisualStabilityManager.setScreenOn(true); - assertEquals(mVisualStabilityManager.isReorderingAllowed(), false); + assertFalse(mVisualStabilityManager.isReorderingAllowed()); mVisualStabilityManager.setScreenOn(false); - assertEquals(mVisualStabilityManager.isReorderingAllowed(), true); + assertTrue(mVisualStabilityManager.isReorderingAllowed()); } @Test public void testReorderingAllowedChangesPanel() { mVisualStabilityManager.setPanelExpanded(true); mVisualStabilityManager.setScreenOn(true); - assertEquals(mVisualStabilityManager.isReorderingAllowed(), false); + assertFalse(mVisualStabilityManager.isReorderingAllowed()); mVisualStabilityManager.setPanelExpanded(false); - assertEquals(mVisualStabilityManager.isReorderingAllowed(), true); + assertTrue(mVisualStabilityManager.isReorderingAllowed()); } @Test @@ -126,51 +138,51 @@ public class VisualStabilityManagerTest extends SysuiTestCase { mVisualStabilityManager.setPanelExpanded(true); mVisualStabilityManager.setScreenOn(true); mVisualStabilityManager.notifyViewAddition(mRow); - assertEquals(mVisualStabilityManager.canReorderNotification(mRow), true); + assertTrue(mVisualStabilityManager.canReorderNotification(mRow)); } @Test public void testReorderingVisibleHeadsUpNotAllowed() { mVisualStabilityManager.setPanelExpanded(true); mVisualStabilityManager.setScreenOn(true); - when(mLocationProvider.isInVisibleLocation(anyObject())).thenReturn(true); + when(mLocationProvider.isInVisibleLocation(any(NotificationEntry.class))).thenReturn(true); mVisualStabilityManager.onHeadsUpStateChanged(mEntry, true); - assertEquals(mVisualStabilityManager.canReorderNotification(mRow), false); + assertFalse(mVisualStabilityManager.canReorderNotification(mRow)); } @Test public void testReorderingVisibleHeadsUpAllowed() { mVisualStabilityManager.setPanelExpanded(true); mVisualStabilityManager.setScreenOn(true); - when(mLocationProvider.isInVisibleLocation(anyObject())).thenReturn(false); + when(mLocationProvider.isInVisibleLocation(any(NotificationEntry.class))).thenReturn(false); mVisualStabilityManager.onHeadsUpStateChanged(mEntry, true); - assertEquals(mVisualStabilityManager.canReorderNotification(mRow), true); + assertTrue(mVisualStabilityManager.canReorderNotification(mRow)); } @Test public void testReorderingVisibleHeadsUpAllowedOnce() { mVisualStabilityManager.setPanelExpanded(true); mVisualStabilityManager.setScreenOn(true); - when(mLocationProvider.isInVisibleLocation(anyObject())).thenReturn(false); + when(mLocationProvider.isInVisibleLocation(any(NotificationEntry.class))).thenReturn(false); mVisualStabilityManager.onHeadsUpStateChanged(mEntry, true); mVisualStabilityManager.onReorderingFinished(); - assertEquals(mVisualStabilityManager.canReorderNotification(mRow), false); + assertFalse(mVisualStabilityManager.canReorderNotification(mRow)); } @Test public void testPulsing() { mVisualStabilityManager.setPulsing(true); - assertEquals(mVisualStabilityManager.canReorderNotification(mRow), false); + assertFalse(mVisualStabilityManager.canReorderNotification(mRow)); mVisualStabilityManager.setPulsing(false); - assertEquals(mVisualStabilityManager.canReorderNotification(mRow), true); + assertTrue(mVisualStabilityManager.canReorderNotification(mRow)); } @Test public void testReorderingAllowedChanges_Pulsing() { mVisualStabilityManager.setPulsing(true); - assertEquals(mVisualStabilityManager.isReorderingAllowed(), false); + assertFalse(mVisualStabilityManager.isReorderingAllowed()); mVisualStabilityManager.setPulsing(false); - assertEquals(mVisualStabilityManager.isReorderingAllowed(), true); + assertTrue(mVisualStabilityManager.isReorderingAllowed()); } @Test @@ -180,4 +192,49 @@ public class VisualStabilityManagerTest extends SysuiTestCase { mVisualStabilityManager.setPulsing(false); verify(mCallback).onReorderingAllowed(); } + + @Test + public void testTemporarilyAllowReorderingNotifiesCallbacks() { + // GIVEN having the panel open (which would block reordering) + mVisualStabilityManager.setScreenOn(true); + mVisualStabilityManager.setPanelExpanded(true); + mVisualStabilityManager.addReorderingAllowedCallback(mCallback); + + // WHEN we temprarily allow reordering + mVisualStabilityManager.temporarilyAllowReordering(); + + // THEN callbacks are notified that reordering is allowed + verify(mCallback).onReorderingAllowed(); + assertTrue(mVisualStabilityManager.isReorderingAllowed()); + } + + @Test + public void testTemporarilyAllowReorderingDoesntOverridePulsing() { + // GIVEN we are in a pulsing state + mVisualStabilityManager.setPulsing(true); + mVisualStabilityManager.addReorderingAllowedCallback(mCallback); + + // WHEN we temprarily allow reordering + mVisualStabilityManager.temporarilyAllowReordering(); + + // THEN reordering is still not allowed + verify(mCallback, never()).onReorderingAllowed(); + assertFalse(mVisualStabilityManager.isReorderingAllowed()); + } + + @Test + public void testTemporarilyAllowReorderingExpires() { + // GIVEN having the panel open (which would block reordering) + mVisualStabilityManager.setScreenOn(true); + mVisualStabilityManager.setPanelExpanded(true); + mVisualStabilityManager.addReorderingAllowedCallback(mCallback); + + // WHEN we temprarily allow reordering and then wait until the window expires + mVisualStabilityManager.temporarilyAllowReordering(); + assertTrue(mVisualStabilityManager.isReorderingAllowed()); + mTestableLooper.processMessages(1); + + // THEN reordering is no longer allowed + assertFalse(mVisualStabilityManager.isReorderingAllowed()); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java index 6376bd3e00e47..ef13b61162c5f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java @@ -63,6 +63,7 @@ import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.NotificationTestHelper; import com.android.systemui.statusbar.notification.NotificationActivityStarter; +import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; @@ -97,6 +98,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { @Rule public MockitoRule mockito = MockitoJUnit.rule(); @Mock private MetricsLogger mMetricsLogger; + @Mock private VisualStabilityManager mVisualStabilityManager; @Mock private NotificationPresenter mPresenter; @Mock private NotificationActivityStarter mNotificationActivityStarter; @Mock private NotificationStackScrollLayout mStackScroller; @@ -111,11 +113,12 @@ public class NotificationGutsManagerTest extends SysuiTestCase { mDependency.injectTestDependency(DeviceProvisionedController.class, mDeviceProvisionedController); mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger); + mDependency.injectTestDependency(VisualStabilityManager.class, mVisualStabilityManager); mHandler = Handler.createAsync(mTestableLooper.getLooper()); mHelper = new NotificationTestHelper(mContext); - mGutsManager = new NotificationGutsManager(mContext); + mGutsManager = new NotificationGutsManager(mContext, mVisualStabilityManager); mGutsManager.setUpWithPresenter(mPresenter, mStackScroller, mCheckSaveListener, mOnSettingsClickListener); mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter); @@ -316,6 +319,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { verify(notificationInfoView).bindNotification( any(PackageManager.class), any(INotificationManager.class), + eq(mVisualStabilityManager), eq(statusBarNotification.getPackageName()), any(NotificationChannel.class), anySet(), @@ -344,6 +348,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { verify(notificationInfoView).bindNotification( any(PackageManager.class), any(INotificationManager.class), + eq(mVisualStabilityManager), eq(statusBarNotification.getPackageName()), any(NotificationChannel.class), anySet(), @@ -374,6 +379,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { verify(notificationInfoView).bindNotification( any(PackageManager.class), any(INotificationManager.class), + eq(mVisualStabilityManager), eq(statusBarNotification.getPackageName()), any(NotificationChannel.class), anySet(), @@ -403,6 +409,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { verify(notificationInfoView).bindNotification( any(PackageManager.class), any(INotificationManager.class), + eq(mVisualStabilityManager), eq(statusBarNotification.getPackageName()), any(NotificationChannel.class), anySet(), @@ -431,6 +438,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { verify(notificationInfoView).bindNotification( any(PackageManager.class), any(INotificationManager.class), + eq(mVisualStabilityManager), eq(statusBarNotification.getPackageName()), any(NotificationChannel.class), anySet(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java index 78970d9658e20..703adf7a047f5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java @@ -72,6 +72,7 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.statusbar.notification.VisualStabilityManager; import org.junit.After; import org.junit.Before; @@ -116,6 +117,8 @@ public class NotificationInfoTest extends SysuiTestCase { private PackageManager mMockPackageManager; @Mock private NotificationBlockingHelperManager mBlockingHelperManager; + @Mock + private VisualStabilityManager mVisualStabilityManager; @Before public void setUp() throws Exception { @@ -193,11 +196,21 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testBindNotification_SetsTextApplicationName() throws Exception { when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name"); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, - null, null, null, - true, false, - IMPORTANCE_DEFAULT, true); + mNotificationInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + mVisualStabilityManager, + TEST_PACKAGE_NAME, + mNotificationChannel, + mNotificationChannelSet, + mSbn, + null, + null, + null, + true, + false, + IMPORTANCE_DEFAULT, + true); final TextView textView = mNotificationInfo.findViewById(R.id.pkgname); assertTrue(textView.getText().toString().contains("App Name")); assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility()); @@ -208,20 +221,42 @@ public class NotificationInfoTest extends SysuiTestCase { final Drawable iconDrawable = mock(Drawable.class); when(mMockPackageManager.getApplicationIcon(any(ApplicationInfo.class))) .thenReturn(iconDrawable); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, - null, null, null, true, false, - IMPORTANCE_DEFAULT, true); + mNotificationInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + mVisualStabilityManager, + TEST_PACKAGE_NAME, + mNotificationChannel, + mNotificationChannelSet, + mSbn, + null, + null, + null, + true, + false, + IMPORTANCE_DEFAULT, + true); final ImageView iconView = mNotificationInfo.findViewById(R.id.pkgicon); assertEquals(iconDrawable, iconView.getDrawable()); } @Test public void testBindNotification_noDelegate() throws Exception { - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, - null, null, null, true, false, - IMPORTANCE_DEFAULT, true); + mNotificationInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + mVisualStabilityManager, + TEST_PACKAGE_NAME, + mNotificationChannel, + mNotificationChannelSet, + mSbn, + null, + null, + null, + true, + false, + IMPORTANCE_DEFAULT, + true); final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name); assertEquals(GONE, nameView.getVisibility()); final TextView dividerView = mNotificationInfo.findViewById(R.id.pkg_divider); @@ -238,10 +273,21 @@ public class NotificationInfoTest extends SysuiTestCase { applicationInfo); when(mMockPackageManager.getApplicationLabel(any())).thenReturn("Other"); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null, - null, true, false, - IMPORTANCE_DEFAULT, true); + mNotificationInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + mVisualStabilityManager, + TEST_PACKAGE_NAME, + mNotificationChannel, + mNotificationChannelSet, + mSbn, + null, + null, + null, + true, + false, + IMPORTANCE_DEFAULT, + true); final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name); assertEquals(VISIBLE, nameView.getVisibility()); assertTrue(nameView.getText().toString().contains("Proxied")); @@ -251,10 +297,21 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testBindNotification_GroupNameHiddenIfNoGroup() throws Exception { - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null, - null, true, false, - IMPORTANCE_DEFAULT, true); + mNotificationInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + mVisualStabilityManager, + TEST_PACKAGE_NAME, + mNotificationChannel, + mNotificationChannelSet, + mSbn, + null, + null, + null, + true, + false, + IMPORTANCE_DEFAULT, + true); final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name); assertEquals(GONE, groupNameView.getVisibility()); } @@ -267,10 +324,21 @@ public class NotificationInfoTest extends SysuiTestCase { when(mMockINotificationManager.getNotificationChannelGroupForPackage( eq("test_group_id"), eq(TEST_PACKAGE_NAME), eq(TEST_UID))) .thenReturn(notificationChannelGroup); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null, - null, true, false, - IMPORTANCE_DEFAULT, true); + mNotificationInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + mVisualStabilityManager, + TEST_PACKAGE_NAME, + mNotificationChannel, + mNotificationChannelSet, + mSbn, + null, + null, + null, + true, + false, + IMPORTANCE_DEFAULT, + true); final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name); assertEquals(View.VISIBLE, groupNameView.getVisibility()); assertEquals("Test Group Name", groupNameView.getText()); @@ -278,19 +346,42 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testBindNotification_SetsTextChannelName() throws Exception { - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null, - null, true, false, - IMPORTANCE_DEFAULT, true); + mNotificationInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + mVisualStabilityManager, + TEST_PACKAGE_NAME, + mNotificationChannel, + mNotificationChannelSet, + mSbn, + null, + null, + null, + true, + false, + IMPORTANCE_DEFAULT, + true); final TextView textView = mNotificationInfo.findViewById(R.id.channel_name); assertEquals(TEST_CHANNEL_NAME, textView.getText()); } @Test public void testBindNotification_DefaultChannelDoesNotUseChannelName() throws Exception { - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mDefaultNotificationChannel, mDefaultNotificationChannelSet, - mSbn, null, null, null, true, false, IMPORTANCE_DEFAULT, true); + mNotificationInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + mVisualStabilityManager, + TEST_PACKAGE_NAME, + mDefaultNotificationChannel, + mDefaultNotificationChannelSet, + mSbn, + null, + null, + null, + true, + false, + IMPORTANCE_DEFAULT, + true); final TextView textView = mNotificationInfo.findViewById(R.id.channel_name); assertEquals(GONE, textView.getVisibility()); } @@ -301,30 +392,64 @@ public class NotificationInfoTest extends SysuiTestCase { // Package has one channel by default. when(mMockINotificationManager.getNumNotificationChannelsForPackage( eq(TEST_PACKAGE_NAME), eq(TEST_UID), anyBoolean())).thenReturn(10); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mDefaultNotificationChannel, mDefaultNotificationChannelSet, - mSbn, null, null, null, true, - false, IMPORTANCE_DEFAULT, true); + mNotificationInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + mVisualStabilityManager, + TEST_PACKAGE_NAME, + mDefaultNotificationChannel, + mDefaultNotificationChannelSet, + mSbn, + null, + null, + null, + true, + false, + IMPORTANCE_DEFAULT, + true); final TextView textView = mNotificationInfo.findViewById(R.id.channel_name); assertEquals(VISIBLE, textView.getVisibility()); } @Test public void testBindNotification_UnblockablePackageUsesChannelName() throws Exception { - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null, - null, true, true, - IMPORTANCE_DEFAULT, true); + mNotificationInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + mVisualStabilityManager, + TEST_PACKAGE_NAME, + mNotificationChannel, + mNotificationChannelSet, + mSbn, + null, + null, + null, + true, + true, + IMPORTANCE_DEFAULT, + true); final TextView textView = mNotificationInfo.findViewById(R.id.channel_name); assertEquals(VISIBLE, textView.getVisibility()); } @Test public void testBindNotification_BlockLink_BlockingHelper() throws Exception { - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, mock( - NotificationInfo.OnSettingsClickListener.class), null, true, false, - true /* isBlockingHelper */, IMPORTANCE_DEFAULT, true); + mNotificationInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + mVisualStabilityManager, + TEST_PACKAGE_NAME, + mNotificationChannel, + mNotificationChannelSet, + mSbn, + null, + mock(NotificationInfo.OnSettingsClickListener.class), + null, + true, + false, + true /* isBlockingHelper */, + IMPORTANCE_DEFAULT, + true); final View block = mNotificationInfo.findViewById(R.id.blocking_helper_turn_off_notifications); final View interruptivenessSettings = mNotificationInfo.findViewById( @@ -336,12 +461,24 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testBindNotification_SetsOnClickListenerForSettings() throws Exception { final CountDownLatch latch = new CountDownLatch(1); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, + mNotificationInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + mVisualStabilityManager, + TEST_PACKAGE_NAME, + mNotificationChannel, + mNotificationChannelSet, + mSbn, + null, (View v, NotificationChannel c, int appUid) -> { assertEquals(mNotificationChannel, c); latch.countDown(); - }, null, true, false, IMPORTANCE_DEFAULT, true); + }, + null, + true, + false, + IMPORTANCE_DEFAULT, + true); final View settingsButton = mNotificationInfo.findViewById(R.id.info); settingsButton.performClick(); @@ -351,10 +488,21 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testBindNotification_SettingsButtonInvisibleWhenNoClickListener() throws Exception { - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null, - null, true, false, - IMPORTANCE_DEFAULT, true); + mNotificationInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + mVisualStabilityManager, + TEST_PACKAGE_NAME, + mNotificationChannel, + mNotificationChannelSet, + mSbn, + null, + null, + null, + true, + false, + IMPORTANCE_DEFAULT, + true); final View settingsButton = mNotificationInfo.findViewById(R.id.info); assertTrue(settingsButton.getVisibility() != View.VISIBLE); } @@ -362,36 +510,80 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testBindNotification_SettingsButtonInvisibleWhenDeviceUnprovisioned() throws Exception { - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, + mNotificationInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + mVisualStabilityManager, + TEST_PACKAGE_NAME, + mNotificationChannel, + mNotificationChannelSet, + mSbn, + null, (View v, NotificationChannel c, int appUid) -> { assertEquals(mNotificationChannel, c); - }, null, false, false, IMPORTANCE_DEFAULT, true); + }, + null, + false, + false, + IMPORTANCE_DEFAULT, + true); final View settingsButton = mNotificationInfo.findViewById(R.id.info); assertTrue(settingsButton.getVisibility() != View.VISIBLE); } @Test public void testBindNotification_SettingsButtonReappearsAfterSecondBind() throws Exception { - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null, - null, true, false, - IMPORTANCE_DEFAULT, true); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, - (View v, NotificationChannel c, int appUid) -> { - }, null, true, false, IMPORTANCE_DEFAULT, true); + mNotificationInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + mVisualStabilityManager, + TEST_PACKAGE_NAME, + mNotificationChannel, + mNotificationChannelSet, + mSbn, + null, + null, + null, + true, + false, + IMPORTANCE_DEFAULT, + true); + mNotificationInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + mVisualStabilityManager, + TEST_PACKAGE_NAME, + mNotificationChannel, + mNotificationChannelSet, + mSbn, + null, + (View v, NotificationChannel c, int appUid) -> { }, + null, + true, + false, + IMPORTANCE_DEFAULT, + true); final View settingsButton = mNotificationInfo.findViewById(R.id.info); assertEquals(View.VISIBLE, settingsButton.getVisibility()); } @Test public void testBindNotificationLogging_notBlockingHelper() throws Exception { - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, - null, null, null, - true, false, - IMPORTANCE_DEFAULT, true); + mNotificationInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + mVisualStabilityManager, + TEST_PACKAGE_NAME, + mNotificationChannel, + mNotificationChannelSet, + mSbn, + null, + null, + null, + true, + false, + IMPORTANCE_DEFAULT, + true); verify(mMetricsLogger).write(argThat(logMaker -> logMaker.getCategory() == MetricsEvent.ACTION_NOTE_CONTROLS && logMaker.getType() == MetricsEvent.TYPE_OPEN @@ -401,12 +593,22 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testBindNotificationLogging_BlockingHelper() throws Exception { - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, - null, null, null, - false, true, + mNotificationInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + mVisualStabilityManager, + TEST_PACKAGE_NAME, + mNotificationChannel, + mNotificationChannelSet, + mSbn, + null, + null, + null, + false, true, - IMPORTANCE_DEFAULT, true); + true, + IMPORTANCE_DEFAULT, + true); verify(mMetricsLogger).write(argThat(logMaker -> logMaker.getCategory() == MetricsEvent.ACTION_NOTE_CONTROLS && logMaker.getType() == MetricsEvent.TYPE_OPEN @@ -416,12 +618,22 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testLogBlockingHelperCounter_logsForBlockingHelper() throws Exception { - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, - null, null, null, - false, true, + mNotificationInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + mVisualStabilityManager, + TEST_PACKAGE_NAME, + mNotificationChannel, + mNotificationChannelSet, + mSbn, + null, + null, + null, + false, true, - IMPORTANCE_DEFAULT, true); + true, + IMPORTANCE_DEFAULT, + true); mNotificationInfo.logBlockingHelperCounter("HowCanNotifsBeRealIfAppsArent"); verify(mMetricsLogger).count(eq("HowCanNotifsBeRealIfAppsArent"), eq(1)); } @@ -429,13 +641,23 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testOnClickListenerPassesNullChannelForBundle() throws Exception { final CountDownLatch latch = new CountDownLatch(1); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + mNotificationInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + mVisualStabilityManager, TEST_PACKAGE_NAME, mNotificationChannel, - createMultipleChannelSet(MULTIPLE_CHANNEL_COUNT), mSbn, null, + createMultipleChannelSet(MULTIPLE_CHANNEL_COUNT), + mSbn, + null, (View v, NotificationChannel c, int appUid) -> { assertEquals(null, c); latch.countDown(); - }, null, true, true, IMPORTANCE_DEFAULT, true); + }, + null, + true, + true, + IMPORTANCE_DEFAULT, + true); mNotificationInfo.findViewById(R.id.info).performClick(); // Verify that listener was triggered. @@ -446,10 +668,21 @@ public class NotificationInfoTest extends SysuiTestCase { @UiThreadTest public void testBindNotification_ChannelNameInvisibleWhenBundleFromDifferentChannels() throws Exception { - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, - createMultipleChannelSet(MULTIPLE_CHANNEL_COUNT), mSbn, null, null, - null, true, false, IMPORTANCE_DEFAULT, true); + mNotificationInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + mVisualStabilityManager, + TEST_PACKAGE_NAME, + mNotificationChannel, + createMultipleChannelSet(MULTIPLE_CHANNEL_COUNT), + mSbn, + null, + null, + null, + true, + false, + IMPORTANCE_DEFAULT, + true); final TextView channelNameView = mNotificationInfo.findViewById(R.id.channel_name); assertEquals(GONE, channelNameView.getVisibility()); @@ -458,10 +691,21 @@ public class NotificationInfoTest extends SysuiTestCase { @Test @UiThreadTest public void testStopInvisibleIfBundleFromDifferentChannels() throws Exception { - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, - createMultipleChannelSet(MULTIPLE_CHANNEL_COUNT), mSbn, null, null, - null, true, false, IMPORTANCE_DEFAULT, true); + mNotificationInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + mVisualStabilityManager, + TEST_PACKAGE_NAME, + mNotificationChannel, + createMultipleChannelSet(MULTIPLE_CHANNEL_COUNT), + mSbn, + null, + null, + null, + true, + false, + IMPORTANCE_DEFAULT, + true); assertEquals(GONE, mNotificationInfo.findViewById( R.id.interruptiveness_settings).getVisibility()); assertEquals(VISIBLE, mNotificationInfo.findViewById( @@ -470,10 +714,21 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testBindNotification_whenAppUnblockable() throws Exception { - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null, - null, true, true, - IMPORTANCE_DEFAULT, true); + mNotificationInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + mVisualStabilityManager, + TEST_PACKAGE_NAME, + mNotificationChannel, + mNotificationChannelSet, + mSbn, + null, + null, + null, + true, + true, + IMPORTANCE_DEFAULT, + true); final TextView view = mNotificationInfo.findViewById(R.id.non_configurable_text); assertEquals(View.VISIBLE, view.getVisibility()); assertEquals(mContext.getString(R.string.notification_unblockable_desc), @@ -484,10 +739,21 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testBindNotification_DoesNotUpdateNotificationChannel() throws Exception { - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null, - null, true, false, - IMPORTANCE_DEFAULT, true); + mNotificationInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + mVisualStabilityManager, + TEST_PACKAGE_NAME, + mNotificationChannel, + mNotificationChannelSet, + mSbn, + null, + null, + null, + true, + false, + IMPORTANCE_DEFAULT, + true); mTestableLooper.processAllMessages(); verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( anyString(), eq(TEST_UID), any()); @@ -496,10 +762,21 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testDoesNotUpdateNotificationChannelAfterImportanceChanged() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null, - null, true, false, - IMPORTANCE_LOW, false); + mNotificationInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + mVisualStabilityManager, + TEST_PACKAGE_NAME, + mNotificationChannel, + mNotificationChannelSet, + mSbn, + null, + null, + null, + true, + false, + IMPORTANCE_LOW, + false); mNotificationInfo.findViewById(R.id.alert).performClick(); mTestableLooper.processAllMessages(); @@ -511,10 +788,21 @@ public class NotificationInfoTest extends SysuiTestCase { public void testDoesNotUpdateNotificationChannelAfterImportanceChangedSilenced() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_DEFAULT); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null, - null, true, false, - IMPORTANCE_DEFAULT, true); + mNotificationInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + mVisualStabilityManager, + TEST_PACKAGE_NAME, + mNotificationChannel, + mNotificationChannelSet, + mSbn, + null, + null, + null, + true, + false, + IMPORTANCE_DEFAULT, + true); mNotificationInfo.findViewById(R.id.silence).performClick(); mTestableLooper.processAllMessages(); @@ -526,10 +814,21 @@ public class NotificationInfoTest extends SysuiTestCase { public void testHandleCloseControls_DoesNotUpdateNotificationChannelIfUnchanged() throws Exception { int originalImportance = mNotificationChannel.getImportance(); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null, - null, true, false, - IMPORTANCE_DEFAULT, true); + mNotificationInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + mVisualStabilityManager, + TEST_PACKAGE_NAME, + mNotificationChannel, + mNotificationChannelSet, + mSbn, + null, + null, + null, + true, + false, + IMPORTANCE_DEFAULT, + true); mNotificationInfo.handleCloseControls(true, false); mTestableLooper.processAllMessages(); @@ -542,10 +841,21 @@ public class NotificationInfoTest extends SysuiTestCase { public void testHandleCloseControls_DoesNotUpdateNotificationChannelIfUnspecified() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null, - null, true, false, - IMPORTANCE_UNSPECIFIED, true); + mNotificationInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + mVisualStabilityManager, + TEST_PACKAGE_NAME, + mNotificationChannel, + mNotificationChannelSet, + mSbn, + null, + null, + null, + true, + false, + IMPORTANCE_UNSPECIFIED, + true); mNotificationInfo.handleCloseControls(true, false); @@ -561,13 +871,22 @@ public class NotificationInfoTest extends SysuiTestCase { NotificationInfo.CheckSaveListener listener = mock(NotificationInfo.CheckSaveListener.class); mNotificationChannel.setImportance(IMPORTANCE_DEFAULT); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */, - createMultipleChannelSet(10) /* numUniqueChannelsInRow */, mSbn, + mNotificationInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + mVisualStabilityManager, + TEST_PACKAGE_NAME, + mNotificationChannel /* notificationChannel */, + createMultipleChannelSet(10) /* numUniqueChannelsInRow */, + mSbn, listener /* checkSaveListener */, - null /* onSettingsClick */, null /* onAppSettingsClick */, true /* provisioned */, - false /* isNonblockable */, true /* isForBlockingHelper */, - IMPORTANCE_DEFAULT, true); + null /* onSettingsClick */, + null /* onAppSettingsClick */, + true /* provisioned */, + false /* isNonblockable */, + true /* isForBlockingHelper */, + IMPORTANCE_DEFAULT, + true); NotificationGuts guts = spy(new NotificationGuts(mContext, null)); when(guts.getWindowToken()).thenReturn(mock(IBinder.class)); @@ -591,13 +910,21 @@ public class NotificationInfoTest extends SysuiTestCase { NotificationInfo.CheckSaveListener listener = mock(NotificationInfo.CheckSaveListener.class); mNotificationChannel.setImportance(IMPORTANCE_DEFAULT); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */, - createMultipleChannelSet(10) /* numUniqueChannelsInRow */, mSbn, + mNotificationInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + mVisualStabilityManager, + TEST_PACKAGE_NAME, + mNotificationChannel /* notificationChannel */, + createMultipleChannelSet(10) /* numUniqueChannelsInRow */, + mSbn, listener /* checkSaveListener */, - null /* onSettingsClick */, null /* onAppSettingsClick */, - false /* isNonblockable */, true /* isForBlockingHelper */, - true, IMPORTANCE_DEFAULT, true); + null /* onSettingsClick */, + null /* onAppSettingsClick */, + false /* isNonblockable */, + true /* isForBlockingHelper */, + true, IMPORTANCE_DEFAULT, + true); mNotificationInfo.handleCloseControls(true /* save */, false /* force */); @@ -611,14 +938,22 @@ public class NotificationInfoTest extends SysuiTestCase { NotificationInfo.CheckSaveListener listener = mock(NotificationInfo.CheckSaveListener.class); mNotificationChannel.setImportance(IMPORTANCE_DEFAULT); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */, - createMultipleChannelSet(10) /* numUniqueChannelsInRow */, mSbn, + mNotificationInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + mVisualStabilityManager, + TEST_PACKAGE_NAME, + mNotificationChannel /* notificationChannel */, + createMultipleChannelSet(10) /* numUniqueChannelsInRow */, + mSbn, listener /* checkSaveListener */, - null /* onSettingsClick */, null /* onAppSettingsClick */, + null /* onSettingsClick */, + null /* onAppSettingsClick */, true /* provisioned */, - false /* isNonblockable */, true /* isForBlockingHelper */, - IMPORTANCE_DEFAULT, true); + false /* isNonblockable */, + true /* isForBlockingHelper */, + IMPORTANCE_DEFAULT, + true); mNotificationInfo.findViewById(R.id.deliver_silently).performClick(); mTestableLooper.processAllMessages(); @@ -631,6 +966,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, + mVisualStabilityManager, TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet /* numChannels */, @@ -641,7 +977,8 @@ public class NotificationInfoTest extends SysuiTestCase { false /* isNonblockable */, true /* isForBlockingHelper */, true, - IMPORTANCE_DEFAULT, true); + IMPORTANCE_DEFAULT, + true); NotificationGuts guts = mock(NotificationGuts.class); doCallRealMethod().when(guts).closeControls(anyInt(), anyInt(), anyBoolean(), anyBoolean()); mNotificationInfo.setGutsParent(guts); @@ -658,6 +995,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, + mVisualStabilityManager, TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet /* numChannels */, @@ -688,10 +1026,21 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testKeepUpdatesNotificationChannel_blockingHelper() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null, - null, true, true, - IMPORTANCE_LOW, false); + mNotificationInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + mVisualStabilityManager, + TEST_PACKAGE_NAME, + mNotificationChannel, + mNotificationChannelSet, + mSbn, + null, + null, + null, + true, + true, + IMPORTANCE_LOW, + false); mNotificationInfo.findViewById(R.id.keep_showing).performClick(); mNotificationInfo.handleCloseControls(true, false); @@ -708,10 +1057,21 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testNoActionsUpdatesNotificationChannel_blockingHelper() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_DEFAULT); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null, - null, true, true, - IMPORTANCE_DEFAULT, true); + mNotificationInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + mVisualStabilityManager, + TEST_PACKAGE_NAME, + mNotificationChannel, + mNotificationChannelSet, + mSbn, + null, + null, + null, + true, + true, + IMPORTANCE_DEFAULT, + true); mNotificationInfo.handleCloseControls(true, false); @@ -727,10 +1087,21 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testSilenceCallsUpdateNotificationChannel() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_DEFAULT); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null, - null, true, false, - IMPORTANCE_DEFAULT, true); + mNotificationInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + mVisualStabilityManager, + TEST_PACKAGE_NAME, + mNotificationChannel, + mNotificationChannelSet, + mSbn, + null, + null, + null, + true, + false, + IMPORTANCE_DEFAULT, + true); mNotificationInfo.findViewById(R.id.silence).performClick(); mNotificationInfo.findViewById(R.id.done).performClick(); @@ -749,10 +1120,21 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testUnSilenceCallsUpdateNotificationChannel() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null, - null, true, false, - IMPORTANCE_LOW, false); + mNotificationInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + mVisualStabilityManager, + TEST_PACKAGE_NAME, + mNotificationChannel, + mNotificationChannelSet, + mSbn, + null, + null, + null, + true, + false, + IMPORTANCE_LOW, + false); mNotificationInfo.findViewById(R.id.alert).performClick(); mNotificationInfo.findViewById(R.id.done).performClick(); @@ -772,10 +1154,21 @@ public class NotificationInfoTest extends SysuiTestCase { public void testSilenceCallsUpdateNotificationChannel_channelImportanceUnspecified() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null, - null, true, false, - IMPORTANCE_UNSPECIFIED, true); + mNotificationInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + mVisualStabilityManager, + TEST_PACKAGE_NAME, + mNotificationChannel, + mNotificationChannelSet, + mSbn, + null, + null, + null, + true, + false, + IMPORTANCE_UNSPECIFIED, + true); mNotificationInfo.findViewById(R.id.silence).performClick(); mNotificationInfo.findViewById(R.id.done).performClick(); @@ -795,10 +1188,21 @@ public class NotificationInfoTest extends SysuiTestCase { public void testSilenceCallsUpdateNotificationChannel_channelImportanceMin() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_MIN); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null, - null, true, false, - IMPORTANCE_MIN, false); + mNotificationInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + mVisualStabilityManager, + TEST_PACKAGE_NAME, + mNotificationChannel, + mNotificationChannelSet, + mSbn, + null, + null, + null, + true, + false, + IMPORTANCE_MIN, + false); assertEquals(mContext.getString(R.string.inline_done_button), ((TextView) mNotificationInfo.findViewById(R.id.done)).getText()); @@ -821,10 +1225,21 @@ public class NotificationInfoTest extends SysuiTestCase { public void testAlertCallsUpdateNotificationChannel_channelImportanceMin() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_MIN); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null, - null, true, false, - IMPORTANCE_MIN, false); + mNotificationInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + mVisualStabilityManager, + TEST_PACKAGE_NAME, + mNotificationChannel, + mNotificationChannelSet, + mSbn, + null, + null, + null, + true, + false, + IMPORTANCE_MIN, + false); assertEquals(mContext.getString(R.string.inline_done_button), ((TextView) mNotificationInfo.findViewById(R.id.done)).getText()); @@ -843,14 +1258,51 @@ public class NotificationInfoTest extends SysuiTestCase { assertEquals(IMPORTANCE_DEFAULT, updated.getValue().getImportance()); } + @Test + public void testAdjustImportanceTemporarilyAllowsReordering() throws Exception { + mNotificationChannel.setImportance(IMPORTANCE_DEFAULT); + mNotificationInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + mVisualStabilityManager, + TEST_PACKAGE_NAME, + mNotificationChannel, + mNotificationChannelSet, + mSbn, + null, + null, + null, + true, + false, + IMPORTANCE_DEFAULT, + true); + + mNotificationInfo.findViewById(R.id.silence).performClick(); + mNotificationInfo.findViewById(R.id.done).performClick(); + mNotificationInfo.handleCloseControls(true, false); + + verify(mVisualStabilityManager).temporarilyAllowReordering(); + } + @Test public void testDoneText() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null, - null, true, false, - IMPORTANCE_LOW, false); + mNotificationInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + mVisualStabilityManager, + TEST_PACKAGE_NAME, + mNotificationChannel, + mNotificationChannelSet, + mSbn, + null, + null, + null, + true, + false, + IMPORTANCE_LOW, + false); assertEquals(mContext.getString(R.string.inline_done_button), ((TextView) mNotificationInfo.findViewById(R.id.done)).getText()); @@ -866,10 +1318,21 @@ public class NotificationInfoTest extends SysuiTestCase { public void testUnSilenceCallsUpdateNotificationChannel_channelImportanceUnspecified() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null, - null, true, false, - IMPORTANCE_LOW, false); + mNotificationInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + mVisualStabilityManager, + TEST_PACKAGE_NAME, + mNotificationChannel, + mNotificationChannelSet, + mSbn, + null, + null, + null, + true, + false, + IMPORTANCE_LOW, + false); mNotificationInfo.findViewById(R.id.alert).performClick(); mNotificationInfo.findViewById(R.id.done).performClick(); @@ -888,10 +1351,21 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testCloseControlsDoesNotUpdateIfSaveIsFalse() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null, - null, true, false, - IMPORTANCE_LOW, false); + mNotificationInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + mVisualStabilityManager, + TEST_PACKAGE_NAME, + mNotificationChannel, + mNotificationChannelSet, + mSbn, + null, + null, + null, + true, + false, + IMPORTANCE_LOW, + false); mNotificationInfo.findViewById(R.id.alert).performClick(); mNotificationInfo.findViewById(R.id.done).performClick(); @@ -905,11 +1379,23 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testCloseControlsUpdatesWhenCheckSaveListenerUsesCallback() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, + mNotificationInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + mVisualStabilityManager, + TEST_PACKAGE_NAME, + mNotificationChannel, + mNotificationChannelSet, + mSbn, (Runnable saveImportance, StatusBarNotification sbn) -> { saveImportance.run(); - }, null, null, true, false, IMPORTANCE_LOW, false + }, + null, + null, + true, + false, + IMPORTANCE_LOW, + false ); mNotificationInfo.findViewById(R.id.alert).performClick(); @@ -928,11 +1414,23 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testCloseControls_withoutHittingApply() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, + mNotificationInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + mVisualStabilityManager, + TEST_PACKAGE_NAME, + mNotificationChannel, + mNotificationChannelSet, + mSbn, (Runnable saveImportance, StatusBarNotification sbn) -> { saveImportance.run(); - }, null, null, true, false, IMPORTANCE_LOW, false + }, + null, + null, + true, + false, + IMPORTANCE_LOW, + false ); mNotificationInfo.findViewById(R.id.alert).performClick(); @@ -944,11 +1442,23 @@ public class NotificationInfoTest extends SysuiTestCase { public void testWillBeRemovedReturnsFalse() throws Exception { assertFalse(mNotificationInfo.willBeRemoved()); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, + mNotificationInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + mVisualStabilityManager, + TEST_PACKAGE_NAME, + mNotificationChannel, + mNotificationChannelSet, + mSbn, (Runnable saveImportance, StatusBarNotification sbn) -> { saveImportance.run(); - }, null, null, true, false, IMPORTANCE_LOW, false + }, + null, + null, + true, + false, + IMPORTANCE_LOW, + false ); assertFalse(mNotificationInfo.willBeRemoved());