Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2daf6331 authored by Dave Mankoff's avatar Dave Mankoff Committed by Steve Elliott
Browse files

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
parent aa6aff0e
Loading
Loading
Loading
Loading
+42 −69
Original line number Diff line number Diff line
@@ -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 =
+68 −5
Original line number Diff line number Diff line
@@ -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);

+0 −59
Original line number Diff line number Diff line
@@ -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<LogMaker> {
        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);
+84 −2
Original line number Diff line number Diff line
@@ -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<OnMenuEventListener> 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<OnMenuEventListener> 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<LogMaker> {
        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);
        }
    }
}