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 cf3783e053843..bf27550b331be 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 @@ -85,8 +85,8 @@ import com.android.systemui.statusbar.EmptyShadeView; import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.NotificationShelfController; import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.notification.LaunchAnimationParameters; import com.android.systemui.statusbar.notification.FakeShadowView; +import com.android.systemui.statusbar.notification.LaunchAnimationParameters; import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorController; import com.android.systemui.statusbar.notification.NotificationUtils; import com.android.systemui.statusbar.notification.ShadeViewRefactor; @@ -3615,7 +3615,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable @ShadeViewRefactor(RefactorComponent.INPUT) protected boolean isInsideQsHeader(MotionEvent ev) { mQsHeader.getBoundsOnScreen(mQsHeaderBound); - return mQsHeaderBound.contains((int) ev.getX(), (int) ev.getY()); + return mQsHeaderBound.contains((int) ev.getRawX(), (int) ev.getRawY()); } @ShadeViewRefactor(RefactorComponent.INPUT) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index 29fdfdb65be26..78ae938b4a98b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -2816,12 +2816,12 @@ public class NotificationPanelViewController extends PanelViewController { return false; } View header = mKeyguardShowing || mQs == null ? mKeyguardStatusBar : mQs.getHeader(); - + int frameTop = mKeyguardShowing || mQs == null ? 0 : mQsFrame.getTop(); mQsInterceptRegion.set( /* left= */ (int) mQsFrame.getX(), - /* top= */ header.getTop(), + /* top= */ header.getTop() + frameTop, /* right= */ (int) mQsFrame.getX() + mQsFrame.getWidth(), - /* bottom= */ header.getBottom()); + /* bottom= */ header.getBottom() + frameTop); // Also allow QS to intercept if the touch is near the notch. mStatusBarTouchableRegionManager.updateRegionForNotch(mQsInterceptRegion); final boolean onHeader = mQsInterceptRegion.contains((int) x, (int) y); 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 077f6bfc5f315..e8608fa76c062 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 @@ -34,15 +34,20 @@ import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.graphics.Rect; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.util.MathUtils; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; import androidx.test.annotation.UiThreadTest; import androidx.test.filters.SmallTest; @@ -572,9 +577,70 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { assertEquals(0, mStackScroller.getSpeedBumpIndex()); } + @Test + public void testInsideQSHeader_noOffset() { + ViewGroup qsHeader = mock(ViewGroup.class); + Rect boundsOnScreen = new Rect(0, 0, 1000, 1000); + mockBoundsOnScreen(qsHeader, boundsOnScreen); + + mStackScroller.setQsHeader(qsHeader); + mStackScroller.setLeftTopRightBottom(0, 0, 2000, 2000); + + MotionEvent event1 = transformEventForView(createMotionEvent(100f, 100f), mStackScroller); + assertTrue(mStackScroller.isInsideQsHeader(event1)); + + MotionEvent event2 = transformEventForView(createMotionEvent(1100f, 100f), mStackScroller); + assertFalse(mStackScroller.isInsideQsHeader(event2)); + } + + @Test + public void testInsideQSHeader_Offset() { + ViewGroup qsHeader = mock(ViewGroup.class); + Rect boundsOnScreen = new Rect(100, 100, 1000, 1000); + mockBoundsOnScreen(qsHeader, boundsOnScreen); + + mStackScroller.setQsHeader(qsHeader); + mStackScroller.setLeftTopRightBottom(200, 200, 2000, 2000); + + MotionEvent event1 = transformEventForView(createMotionEvent(50f, 50f), mStackScroller); + assertFalse(mStackScroller.isInsideQsHeader(event1)); + + MotionEvent event2 = transformEventForView(createMotionEvent(150f, 150f), mStackScroller); + assertTrue(mStackScroller.isInsideQsHeader(event2)); + + MotionEvent event3 = transformEventForView(createMotionEvent(250f, 250f), mStackScroller); + assertTrue(mStackScroller.isInsideQsHeader(event2)); + } + private void setBarStateForTest(int state) { // Can't inject this through the listener or we end up on the actual implementation // rather than the mock because the spy just coppied the anonymous inner /shruggie. mStackScroller.setStatusBarState(state); } + + private static void mockBoundsOnScreen(View view, Rect bounds) { + doAnswer(invocation -> { + Rect out = invocation.getArgument(0); + out.set(bounds); + return null; + }).when(view).getBoundsOnScreen(any()); + } + + private static MotionEvent transformEventForView(MotionEvent event, View view) { + // From `ViewGroup#dispatchTransformedTouchEvent` + MotionEvent transformed = event.copy(); + transformed.offsetLocation(-view.getTop(), -view.getLeft()); + return transformed; + } + + private static MotionEvent createMotionEvent(float x, float y) { + return MotionEvent.obtain( + /* downTime= */0, + /* eventTime= */0, + MotionEvent.ACTION_DOWN, + x, + y, + /* metaState= */0 + ); + } }