Do not handle touches next to shelf Take #2

NSSL would return true for all touches below the last notification,
and to the right of the shelf. This is essentially empty space.
The reason for this is the SwipeHelper is always returning true,
since it is looking at the full width of the NotificationShelf. By
using getActualWidth() instead, the touches are more accurately
handled.

This fixes an issue where the shelf is vertically overlapping
the unlock icon, and taking touches from it.

Fixes: 358424256
Test: atest NotificationSwipeHelperTest
Test: manual - horizontal swipe notifications
Test: manual - vertically swipe notifications
Test: manual - all touch interactions with notifs
Test: manual - tap on shelf to expand shade
Test: manual - face auth without bypass, add notifications next
to unlock icon, use unlock
Flag: com.android.systemui.ignore_touches_next_to_notification_shelf

Change-Id: Idf22d6e72e7d618ed98e869142b5cc5dc785985e
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 651244a..b854964 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -1489,6 +1489,16 @@
 }
 
 flag {
+  name: "ignore_touches_next_to_notification_shelf"
+  namespace: "systemui"
+  description: "The shelf can vertically overlap the unlock icon. Ignore touches if so."
+  bug: "358424256"
+   metadata {
+       purpose: PURPOSE_BUGFIX
+   }
+}
+
+flag {
    name: "media_projection_request_attribution_fix"
    namespace: "systemui"
    description: "Ensure MediaProjection consent requests are properly attributed"
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
index 95db95c..789701f5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
@@ -13,6 +13,8 @@
 
 package com.android.systemui.statusbar.notification.stack;
 
+import static com.android.systemui.Flags.FLAG_IGNORE_TOUCHES_NEXT_TO_NOTIFICATION_SHELF;
+
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
@@ -36,6 +38,7 @@
 import android.animation.Animator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.os.Handler;
+import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
 import android.service.notification.StatusBarNotification;
 import android.testing.TestableLooper;
@@ -52,6 +55,7 @@
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
+import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.shared.NotificationContentAlphaOptimization;
 
@@ -85,6 +89,7 @@
     private NotificationMenuRowPlugin mMenuRow;
     private Handler mHandler;
     private ExpandableNotificationRow mNotificationRow;
+    private NotificationShelf mShelf;
     private Runnable mFalsingCheck;
     private final FeatureFlags mFeatureFlags = new FakeFeatureFlags();
 
@@ -111,6 +116,7 @@
         mEvent = mock(MotionEvent.class);
         mMenuRow = mock(NotificationMenuRowPlugin.class);
         mNotificationRow = mock(ExpandableNotificationRow.class);
+        mShelf = mock(NotificationShelf.class);
         mHandler = mock(Handler.class);
         mFalsingCheck = mock(Runnable.class);
     }
@@ -665,6 +671,54 @@
     }
 
     @Test
+    @EnableFlags(FLAG_IGNORE_TOUCHES_NEXT_TO_NOTIFICATION_SHELF)
+    public void testIsTouchInView_notificationShelf_flagEnabled() {
+        doReturn(500).when(mShelf).getWidth();
+        doReturn(FAKE_ROW_WIDTH).when(mShelf).getActualWidth();
+        doReturn(FAKE_ROW_HEIGHT).when(mShelf).getHeight();
+        doReturn(FAKE_ROW_HEIGHT).when(mShelf).getActualHeight();
+
+        Answer answer = (Answer) invocation -> {
+            int[] arr = invocation.getArgument(0);
+            arr[0] = 0;
+            arr[1] = 0;
+            return null;
+        };
+
+        doReturn(5f).when(mEvent).getRawX();
+        doReturn(10f).when(mEvent).getRawY();
+        doAnswer(answer).when(mShelf).getLocationOnScreen(any());
+        assertTrue("Touch is within the view", mSwipeHelper.isTouchInView(mEvent, mShelf));
+
+        doReturn(50f).when(mEvent).getRawX();
+        assertFalse("Touch is not within the view", mSwipeHelper.isTouchInView(mEvent, mShelf));
+    }
+
+    @Test
+    @DisableFlags(FLAG_IGNORE_TOUCHES_NEXT_TO_NOTIFICATION_SHELF)
+    public void testIsTouchInView_notificationShelf_flagDisabled() {
+        doReturn(500).when(mShelf).getWidth();
+        doReturn(FAKE_ROW_WIDTH).when(mShelf).getActualWidth();
+        doReturn(FAKE_ROW_HEIGHT).when(mShelf).getHeight();
+        doReturn(FAKE_ROW_HEIGHT).when(mShelf).getActualHeight();
+
+        Answer answer = (Answer) invocation -> {
+            int[] arr = invocation.getArgument(0);
+            arr[0] = 0;
+            arr[1] = 0;
+            return null;
+        };
+
+        doReturn(5f).when(mEvent).getRawX();
+        doReturn(10f).when(mEvent).getRawY();
+        doAnswer(answer).when(mShelf).getLocationOnScreen(any());
+        assertTrue("Touch is within the view", mSwipeHelper.isTouchInView(mEvent, mShelf));
+
+        doReturn(50f).when(mEvent).getRawX();
+        assertTrue("Touch is within the view", mSwipeHelper.isTouchInView(mEvent, mShelf));
+    }
+
+    @Test
     public void testContentAlphaRemainsUnchangedWhenNotificationIsNotDismissible() {
         doReturn(FAKE_ROW_WIDTH).when(mNotificationRow).getMeasuredWidth();
 
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 379a67e..04974b4 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
@@ -24,6 +24,7 @@
 import static com.android.server.notification.Flags.screenshareNotificationHiding;
 import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
 import static com.android.systemui.Flags.confineNotificationTouchToViewWidth;
+import static com.android.systemui.Flags.ignoreTouchesNextToNotificationShelf;
 import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.OnEmptySpaceClickListener;
 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.OnOverscrollTopChangedListener;
@@ -607,6 +608,16 @@
                             true /* requireMinHeight */,
                             false /* ignoreDecors */,
                             !confineNotificationTouchToViewWidth() /* ignoreWidth */);
+
+                    // Verify the MotionEvent x,y are actually inside the touch area of the shelf,
+                    // since the shelf may be animated down to a collapsed size on keyguard.
+                    if (ignoreTouchesNextToNotificationShelf()) {
+                        if (child instanceof NotificationShelf shelf) {
+                            if (!NotificationSwipeHelper.isTouchInView(ev, shelf)) {
+                                return null;
+                            }
+                        }
+                    }
                     if (child instanceof ExpandableNotificationRow row) {
                         ExpandableNotificationRow parent = row.getNotificationParent();
                         if (parent != null && parent.areChildrenExpanded()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
index 7e327e6..0e94ca35 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
@@ -18,6 +18,7 @@
 package com.android.systemui.statusbar.notification.stack;
 
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_ROW_SWIPE;
+import static com.android.systemui.Flags.ignoreTouchesNextToNotificationShelf;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -39,6 +40,7 @@
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
+import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.notification.SourceType;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
@@ -503,13 +505,21 @@
         final int height = (view instanceof ExpandableView)
                 ? ((ExpandableView) view).getActualHeight()
                 : view.getHeight();
+        final int width;
+        if (ignoreTouchesNextToNotificationShelf()) {
+            width = (view instanceof NotificationShelf)
+                ? ((NotificationShelf) view).getActualWidth()
+                : view.getWidth();
+        } else {
+            width = view.getWidth();
+        }
         final int rx = (int) ev.getRawX();
         final int ry = (int) ev.getRawY();
         int[] temp = new int[2];
         view.getLocationOnScreen(temp);
         final int x = temp[0];
         final int y = temp[1];
-        Rect rect = new Rect(x, y, x + view.getWidth(), y + height);
+        Rect rect = new Rect(x, y, x + width, y + height);
         boolean ret = rect.contains(rx, ry);
         return ret;
     }