From 2daf63315c1fb9753657b557630d8cdecf389983 Mon Sep 17 00:00:00 2001 From: Dave Mankoff Date: Mon, 6 Apr 2020 20:20:26 -0400 Subject: [PATCH] Move OnMenuEventListener out of NSSL Move the OnMenuEventListener out of the NotificationStackScrollLayout and into NotificationStackScrollLayoutController. The NotificationStackScrollLayout still contains an anonymous implementation of OnMenuEventListener until the SwipeHelper can similarly be moved. That will come soon. Bug: 147245740 Test: atest SystemUITests && manual Change-Id: I82609c5d553e91d8352d5740aa27bf9982b02a70 --- .../stack/NotificationStackScrollLayout.java | 111 +++++++----------- ...tificationStackScrollLayoutController.java | 73 +++++++++++- .../NotificationStackScrollLayoutTest.java | 59 ---------- ...tificationStackScrollerControllerTest.java | 86 +++++++++++++- 4 files changed, 194 insertions(+), 135 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index f17050a9faf8e..e061472b39393 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -185,6 +185,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable * gap is drawn between them). In this case we don't want to round their corners. */ private static final int DISTANCE_BETWEEN_ADJACENT_SECTIONS_PX = 1; + private OnMenuEventListener mMenuEventListener; private KeyguardBypassEnabledProvider mKeyguardBypassEnabledProvider; private final DynamicPrivacyController mDynamicPrivacyController; private final SysuiStatusBarStateController mStatusbarStateController; @@ -316,7 +317,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable * motion. */ private int mMaxScrollAfterExpand; - private ExpandableNotificationRow.LongPressListener mLongPressListener; boolean mCheckForLeavebehind; /** @@ -608,8 +608,27 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable minHeight, maxHeight); mExpandHelper.setEventSource(this); mExpandHelper.setScrollAdapter(mScrollAdapter); + + // TODO: move swipe helper into controller. + // The anonymous proxy through to mMenuEventListener is temporary until more can be moved + // into the controller. mSwipeHelper = new NotificationSwipeHelper(SwipeHelper.X, mNotificationCallback, - getContext(), mMenuEventListener, mFalsingManager); + getContext(), new OnMenuEventListener() { + @Override + public void onMenuClicked(View row, int x, int y, MenuItem menu) { + mMenuEventListener.onMenuClicked(row, x, y, menu); + } + + @Override + public void onMenuReset(View row) { + mMenuEventListener.onMenuReset(row); + } + + @Override + public void onMenuShown(View row) { + mMenuEventListener.onMenuShown(row); + } + }, mFalsingManager); mStackScrollAlgorithm = createStackScrollAlgorithm(context); mShouldDrawNotificationBackground = res.getBoolean(R.bool.config_drawNotificationBackground); @@ -3753,11 +3772,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable return y < getHeight() - getEmptyBottomMargin(); } - @ShadeViewRefactor(RefactorComponent.INPUT) - public void setLongPressListener(ExpandableNotificationRow.LongPressListener listener) { - mLongPressListener = listener; - } - private float getTouchSlop(MotionEvent event) { // Adjust the touch slop if another gesture may be being performed. return event.getClassification() == MotionEvent.CLASSIFICATION_AMBIGUOUS_GESTURE @@ -5812,6 +5826,27 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mCurrentUserId = userId; } + void onMenuShown(View row) { + mSwipeHelper.onMenuShown(row); + } + + void onMenuReset(View row) { + View translatingParentView = mSwipeHelper.getTranslatingParentView(); + if (translatingParentView != null && row == translatingParentView) { + mSwipeHelper.clearExposedMenuView(); + mSwipeHelper.clearTranslatingParentView(); + if (row instanceof ExpandableNotificationRow) { + mHeadsUpManager.setMenuShown( + ((ExpandableNotificationRow) row).getEntry(), false); + + } + } + } + + void setMenuEventListener(OnMenuEventListener menuEventListener) { + mMenuEventListener = menuEventListener; + } + /** * A listener that is notified when the empty space below the notifications is clicked on */ @@ -6167,69 +6202,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } } - @VisibleForTesting - @ShadeViewRefactor(RefactorComponent.INPUT) - protected final OnMenuEventListener mMenuEventListener = new OnMenuEventListener() { - @Override - public void onMenuClicked(View view, int x, int y, MenuItem item) { - if (mLongPressListener == null) { - return; - } - if (view instanceof ExpandableNotificationRow) { - ExpandableNotificationRow row = (ExpandableNotificationRow) view; - mMetricsLogger.write(row.getEntry().getSbn().getLogMaker() - .setCategory(MetricsEvent.ACTION_TOUCH_GEAR) - .setType(MetricsEvent.TYPE_ACTION) - ); - } - mLongPressListener.onLongPress(view, x, y, item); - } - @Override - public void onMenuReset(View row) { - View translatingParentView = mSwipeHelper.getTranslatingParentView(); - if (translatingParentView != null && row == translatingParentView) { - mSwipeHelper.clearExposedMenuView(); - mSwipeHelper.clearTranslatingParentView(); - if (row instanceof ExpandableNotificationRow) { - mHeadsUpManager.setMenuShown( - ((ExpandableNotificationRow) row).getEntry(), false); - - } - } - } - - @Override - public void onMenuShown(View row) { - if (row instanceof ExpandableNotificationRow) { - ExpandableNotificationRow notificationRow = (ExpandableNotificationRow) row; - mMetricsLogger.write(notificationRow.getEntry().getSbn().getLogMaker() - .setCategory(MetricsEvent.ACTION_REVEAL_GEAR) - .setType(MetricsEvent.TYPE_ACTION)); - mHeadsUpManager.setMenuShown(notificationRow.getEntry(), true); - mSwipeHelper.onMenuShown(row); - mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */, - false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */, - false /* resetMenu */); - - // Check to see if we want to go directly to the notfication guts - NotificationMenuRowPlugin provider = notificationRow.getProvider(); - if (provider.shouldShowGutsOnSnapOpen()) { - MenuItem item = provider.menuItemToExposeOnSnap(); - if (item != null) { - Point origin = provider.getRevealAnimationOrigin(); - mNotificationGutsManager.openGuts(row, origin.x, origin.y, item); - } else { - Log.e(TAG, "Provider has shouldShowGutsOnSnapOpen, but provided no " - + "menu item in menuItemtoExposeOnSnap. Skipping."); - } - - // Close the menu row since we went directly to the guts - resetExposedMenuView(false, true); - } - } - } - }; @ShadeViewRefactor(RefactorComponent.INPUT) private final NotificationSwipeHelper.NotificationCallback mNotificationCallback = diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index b396ce18fba67..ca78b2a1fc685 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java @@ -18,8 +18,10 @@ package com.android.systemui.statusbar.notification.stack; import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME; +import android.graphics.Point; import android.graphics.PointF; import android.provider.Settings; +import android.util.Log; import android.view.Display; import android.view.View; import android.view.ViewGroup; @@ -27,8 +29,12 @@ import android.view.WindowInsets; import android.widget.FrameLayout; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.media.KeyguardMediaController; +import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; +import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener; import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.NotificationLockscreenUserManager; @@ -72,6 +78,8 @@ import kotlin.Unit; */ @StatusBarComponent.StatusBarScope public class NotificationStackScrollLayoutController { + private static final String TAG = "StackScrollerController"; + private final boolean mAllowLongPress; private final NotificationGutsManager mNotificationGutsManager; private final HeadsUpManagerPhone mHeadsUpManager; @@ -80,6 +88,7 @@ public class NotificationStackScrollLayoutController { private final DynamicPrivacyController mDynamicPrivacyController; private final ConfigurationController mConfigurationController; private final ZenModeController mZenModeController; + private final MetricsLogger mMetricsLogger; private final KeyguardMediaController mKeyguardMediaController; private final SysuiStatusBarStateController mStatusBarStateController; private final KeyguardBypassController mKeyguardBypassController; @@ -175,6 +184,60 @@ public class NotificationStackScrollLayoutController { } }; + private final OnMenuEventListener mMenuEventListener = new OnMenuEventListener() { + @Override + public void onMenuClicked( + View view, int x, int y, NotificationMenuRowPlugin.MenuItem item) { + if (!mAllowLongPress) { + return; + } + if (view instanceof ExpandableNotificationRow) { + ExpandableNotificationRow row = (ExpandableNotificationRow) view; + mMetricsLogger.write(row.getEntry().getSbn().getLogMaker() + .setCategory(MetricsEvent.ACTION_TOUCH_GEAR) + .setType(MetricsEvent.TYPE_ACTION) + ); + } + mNotificationGutsManager.openGuts(view, x, y, item); + } + + @Override + public void onMenuReset(View row) { + mView.onMenuReset(row); + } + + @Override + public void onMenuShown(View row) { + if (row instanceof ExpandableNotificationRow) { + ExpandableNotificationRow notificationRow = (ExpandableNotificationRow) row; + mMetricsLogger.write(notificationRow.getEntry().getSbn().getLogMaker() + .setCategory(MetricsEvent.ACTION_REVEAL_GEAR) + .setType(MetricsEvent.TYPE_ACTION)); + mHeadsUpManager.setMenuShown(notificationRow.getEntry(), true); + mView.onMenuShown(row); + mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */, + false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */, + false /* resetMenu */); + + // Check to see if we want to go directly to the notification guts + NotificationMenuRowPlugin provider = notificationRow.getProvider(); + if (provider.shouldShowGutsOnSnapOpen()) { + NotificationMenuRowPlugin.MenuItem item = provider.menuItemToExposeOnSnap(); + if (item != null) { + Point origin = provider.getRevealAnimationOrigin(); + mNotificationGutsManager.openGuts(row, origin.x, origin.y, item); + } else { + Log.e(TAG, "Provider has shouldShowGutsOnSnapOpen, but provided no " + + "menu item in menuItemtoExposeOnSnap. Skipping."); + } + + // Close the menu row since we went directly to the guts + mView.resetExposedMenuView(false, true); + } + } + } + }; + @Inject public NotificationStackScrollLayoutController( @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress, @@ -189,7 +252,8 @@ public class NotificationStackScrollLayoutController { KeyguardBypassController keyguardBypassController, ZenModeController zenModeController, SysuiColorExtractor colorExtractor, - NotificationLockscreenUserManager lockscreenUserManager) { + NotificationLockscreenUserManager lockscreenUserManager, + MetricsLogger metricsLogger) { mAllowLongPress = allowLongPress; mNotificationGutsManager = notificationGutsManager; mHeadsUpManager = headsUpManager; @@ -203,6 +267,7 @@ public class NotificationStackScrollLayoutController { mZenModeController = zenModeController; mColorExtractor = colorExtractor; mLockscreenUserManager = lockscreenUserManager; + mMetricsLogger = metricsLogger; } public void attach(NotificationStackScrollLayout view) { @@ -210,16 +275,14 @@ public class NotificationStackScrollLayoutController { mView.setController(this); mView.initView(mView.getContext(), mKeyguardBypassController::getBypassEnabled); - if (mAllowLongPress) { - mView.setLongPressListener(mNotificationGutsManager::openGuts); - } - mHeadsUpManager.addListener(mNotificationRoundnessManager); // TODO: why is this here? mDynamicPrivacyController.addListener(mDynamicPrivacyControllerListener); mLockscreenUserManager.addUserChangedListener(mLockscreenUserChangeListener); mView.setCurrentUserid(mLockscreenUserManager.getCurrentUserId()); + mView.setMenuEventListener(mMenuEventListener); + mNotificationRoundnessManager.setOnRoundingChangedCallback(mView::invalidate); mView.addOnExpandedHeightChangedListener(mNotificationRoundnessManager::setExpanded); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java index afb1e9414e55c..bca7b312ff157 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java @@ -24,9 +24,7 @@ import static org.junit.Assert.assertFalse; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.RETURNS_DEEP_STUBS; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doNothing; @@ -47,7 +45,6 @@ import androidx.test.annotation.UiThreadTest; import androidx.test.filters.SmallTest; import com.android.internal.logging.MetricsLogger; -import com.android.internal.logging.nano.MetricsProto; import com.android.internal.logging.testing.UiEventLoggerFake; import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.ExpandHelper; @@ -99,7 +96,6 @@ import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; -import org.mockito.ArgumentMatcher; import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; @@ -451,61 +447,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { assertNull(swipeActionHelper.getExposedMenuView()); } - class LogMatcher implements ArgumentMatcher { - private int mCategory, mType; - - LogMatcher(int category, int type) { - mCategory = category; - mType = type; - } - public boolean matches(LogMaker l) { - return (l.getCategory() == mCategory) - && (l.getType() == mType); - } - - public String toString() { - return String.format("LogMaker(%d, %d)", mCategory, mType); - } - } - - private LogMaker logMatcher(int category, int type) { - return argThat(new LogMatcher(category, type)); - } - - @Test - @UiThreadTest - public void testOnMenuClickedLogging() { - // Set up the object under test to have a valid mLongPressListener. We're testing an - // anonymous-class member, mMenuEventListener, so we need to modify the state of the - // class itself, not the Mockito spy copied from it. See notes in setup. - mStackScrollerInternal.setLongPressListener( - mock(ExpandableNotificationRow.LongPressListener.class)); - - ExpandableNotificationRow row = mock(ExpandableNotificationRow.class, RETURNS_DEEP_STUBS); - when(row.getEntry().getSbn().getLogMaker()).thenReturn(new LogMaker( - MetricsProto.MetricsEvent.VIEW_UNKNOWN)); - - mStackScroller.mMenuEventListener.onMenuClicked(row, 0, 0, mock( - NotificationMenuRowPlugin.MenuItem.class)); - verify(row.getEntry().getSbn()).getLogMaker(); // This writes most of the log data - verify(mMetricsLogger).write(logMatcher(MetricsProto.MetricsEvent.ACTION_TOUCH_GEAR, - MetricsProto.MetricsEvent.TYPE_ACTION)); - } - - @Test - @UiThreadTest - public void testOnMenuShownLogging() { ; - - ExpandableNotificationRow row = mock(ExpandableNotificationRow.class, RETURNS_DEEP_STUBS); - when(row.getEntry().getSbn().getLogMaker()).thenReturn(new LogMaker( - MetricsProto.MetricsEvent.VIEW_UNKNOWN)); - - mStackScroller.mMenuEventListener.onMenuShown(row); - verify(row.getEntry().getSbn()).getLogMaker(); // This writes most of the log data - verify(mMetricsLogger).write(logMatcher(MetricsProto.MetricsEvent.ACTION_REVEAL_GEAR, - MetricsProto.MetricsEvent.TYPE_ACTION)); - } - @Test public void testClearNotifications_All() { mStackScroller.clearNotifications(NotificationStackScrollLayout.ROWS_ALL, true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java index 0b2cb0e398415..83d6ac904bfe7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java @@ -18,23 +18,32 @@ package com.android.systemui.statusbar.notification.stack; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.Mockito.RETURNS_DEEP_STUBS; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.metrics.LogMaker; import android.testing.AndroidTestingRunner; import androidx.test.filters.SmallTest; +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto; import com.android.systemui.SysuiTestCase; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.media.KeyguardMediaController; +import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; +import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.DynamicPrivacyController; +import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.phone.KeyguardBypassController; @@ -46,6 +55,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.mockito.ArgumentMatcher; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -82,8 +92,10 @@ public class NotificationStackScrollerControllerTest extends SysuiTestCase { private SysuiColorExtractor mColorExtractor; @Mock private NotificationLockscreenUserManager mNotificationLockscreenUserManager; + @Mock + private MetricsLogger mMetricsLogger; - NotificationStackScrollLayoutController mController; + private NotificationStackScrollLayoutController mController; @Before public void setUp() { @@ -102,7 +114,9 @@ public class NotificationStackScrollerControllerTest extends SysuiTestCase { mKeyguardBypassController, mZenModeController, mColorExtractor, - mNotificationLockscreenUserManager); + mNotificationLockscreenUserManager, + mMetricsLogger + ); when(mNotificationStackScrollLayout.isAttachedToWindow()).thenReturn(true); } @@ -205,4 +219,72 @@ public class NotificationStackScrollerControllerTest extends SysuiTestCase { stateListener.onStatePostChange(); verify(mNotificationStackScrollLayout).updateSensitiveness(false, true); } + + + @Test + public void testOnMenuShownLogging() { ; + + ExpandableNotificationRow row = mock(ExpandableNotificationRow.class, RETURNS_DEEP_STUBS); + when(row.getEntry().getSbn().getLogMaker()).thenReturn(new LogMaker( + MetricsProto.MetricsEvent.VIEW_UNKNOWN)); + + + ArgumentCaptor onMenuEventListenerArgumentCaptor = + ArgumentCaptor.forClass(OnMenuEventListener.class); + + mController.attach(mNotificationStackScrollLayout); + verify(mNotificationStackScrollLayout).setMenuEventListener( + onMenuEventListenerArgumentCaptor.capture()); + + OnMenuEventListener onMenuEventListener = onMenuEventListenerArgumentCaptor.getValue(); + + onMenuEventListener.onMenuShown(row); + verify(row.getEntry().getSbn()).getLogMaker(); // This writes most of the log data + verify(mMetricsLogger).write(logMatcher(MetricsProto.MetricsEvent.ACTION_REVEAL_GEAR, + MetricsProto.MetricsEvent.TYPE_ACTION)); + } + + @Test + public void testOnMenuClickedLogging() { + ExpandableNotificationRow row = mock(ExpandableNotificationRow.class, RETURNS_DEEP_STUBS); + when(row.getEntry().getSbn().getLogMaker()).thenReturn(new LogMaker( + MetricsProto.MetricsEvent.VIEW_UNKNOWN)); + + + ArgumentCaptor onMenuEventListenerArgumentCaptor = + ArgumentCaptor.forClass(OnMenuEventListener.class); + + mController.attach(mNotificationStackScrollLayout); + verify(mNotificationStackScrollLayout).setMenuEventListener( + onMenuEventListenerArgumentCaptor.capture()); + + OnMenuEventListener onMenuEventListener = onMenuEventListenerArgumentCaptor.getValue(); + + onMenuEventListener.onMenuClicked(row, 0, 0, mock( + NotificationMenuRowPlugin.MenuItem.class)); + verify(row.getEntry().getSbn()).getLogMaker(); // This writes most of the log data + verify(mMetricsLogger).write(logMatcher(MetricsProto.MetricsEvent.ACTION_TOUCH_GEAR, + MetricsProto.MetricsEvent.TYPE_ACTION)); + } + + private LogMaker logMatcher(int category, int type) { + return argThat(new LogMatcher(category, type)); + } + + static class LogMatcher implements ArgumentMatcher { + private int mCategory, mType; + + LogMatcher(int category, int type) { + mCategory = category; + mType = type; + } + public boolean matches(LogMaker l) { + return (l.getCategory() == mCategory) + && (l.getType() == mType); + } + + public String toString() { + return String.format("LogMaker(%d, %d)", mCategory, mType); + } + } }