diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java index 39029896331cf..1af8ca2efe11b 100644 --- a/core/java/android/view/MotionEvent.java +++ b/core/java/android/view/MotionEvent.java @@ -4166,6 +4166,40 @@ public final class MotionEvent extends InputEvent implements Parcelable { nativeWriteToParcel(mNativePtr, out); } + /** + * Get the x coordinate of the location where the pointer should be dispatched. + * + * This is required because a mouse event, such as from a touchpad, may contain multiple + * pointers that should all be dispatched to the cursor position. + * @hide + */ + public float getXDispatchLocation(int pointerIndex) { + if (isFromSource(InputDevice.SOURCE_MOUSE)) { + final float xCursorPosition = getXCursorPosition(); + if (xCursorPosition != INVALID_CURSOR_POSITION) { + return xCursorPosition; + } + } + return getX(pointerIndex); + } + + /** + * Get the y coordinate of the location where the pointer should be dispatched. + * + * This is required because a mouse event, such as from a touchpad, may contain multiple + * pointers that should all be dispatched to the cursor position. + * @hide + */ + public float getYDispatchLocation(int pointerIndex) { + if (isFromSource(InputDevice.SOURCE_MOUSE)) { + final float yCursorPosition = getYCursorPosition(); + if (yCursorPosition != INVALID_CURSOR_POSITION) { + return yCursorPosition; + } + } + return getY(pointerIndex); + } + /** * Transfer object for pointer coordinates. * diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index f5e4da86bfea8..d4578475e9c3d 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -2040,8 +2040,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager @Override public PointerIcon onResolvePointerIcon(MotionEvent event, int pointerIndex) { - final float x = event.getX(pointerIndex); - final float y = event.getY(pointerIndex); + final float x = event.getXDispatchLocation(pointerIndex); + final float y = event.getYDispatchLocation(pointerIndex); if (isOnScrollbarThumb(x, y) || isDraggingScrollBar()) { return PointerIcon.getSystemIcon(mContext, PointerIcon.TYPE_ARROW); } @@ -2125,8 +2125,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager HoverTarget firstOldHoverTarget = mFirstHoverTarget; mFirstHoverTarget = null; if (!interceptHover && action != MotionEvent.ACTION_HOVER_EXIT) { - final float x = event.getX(); - final float y = event.getY(); + final float x = event.getXDispatchLocation(0); + final float y = event.getYDispatchLocation(0); final int childrenCount = mChildrenCount; if (childrenCount != 0) { final ArrayList preorderedList = buildOrderedChildList(); @@ -2347,8 +2347,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager // Check what the child under the pointer says about the tooltip. final int childrenCount = mChildrenCount; if (childrenCount != 0) { - final float x = event.getX(); - final float y = event.getY(); + final float x = event.getXDispatchLocation(0); + final float y = event.getYDispatchLocation(0); final ArrayList preorderedList = buildOrderedChildList(); final boolean customOrder = preorderedList == null @@ -2443,8 +2443,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager @Override protected boolean pointInHoveredChild(MotionEvent event) { if (mFirstHoverTarget != null) { - return isTransformedTouchPointInView(event.getX(), event.getY(), - mFirstHoverTarget.child, null); + return isTransformedTouchPointInView(event.getXDispatchLocation(0), + event.getYDispatchLocation(0), mFirstHoverTarget.child, null); } return false; } @@ -2513,8 +2513,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager public boolean onInterceptHoverEvent(MotionEvent event) { if (event.isFromSource(InputDevice.SOURCE_MOUSE)) { final int action = event.getAction(); - final float x = event.getX(); - final float y = event.getY(); + final float x = event.getXDispatchLocation(0); + final float y = event.getYDispatchLocation(0); if ((action == MotionEvent.ACTION_HOVER_MOVE || action == MotionEvent.ACTION_HOVER_ENTER) && isOnScrollbar(x, y)) { return true; @@ -2535,8 +2535,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager // Send the event to the child under the pointer. final int childrenCount = mChildrenCount; if (childrenCount != 0) { - final float x = event.getX(); - final float y = event.getY(); + final float x = event.getXDispatchLocation(0); + final float y = event.getXDispatchLocation(0); final ArrayList preorderedList = buildOrderedChildList(); final boolean customOrder = preorderedList == null @@ -2700,10 +2700,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager final int childrenCount = mChildrenCount; if (newTouchTarget == null && childrenCount != 0) { - final float x = - isMouseEvent ? ev.getXCursorPosition() : ev.getX(actionIndex); - final float y = - isMouseEvent ? ev.getYCursorPosition() : ev.getY(actionIndex); + final float x = ev.getXDispatchLocation(actionIndex); + final float y = ev.getYDispatchLocation(actionIndex); // Find a child that can receive the event. // Scan children from front to back. final ArrayList preorderedList = buildTouchDispatchChildList(); @@ -2757,8 +2755,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } else { mLastTouchDownIndex = childIndex; } - mLastTouchDownX = ev.getX(); - mLastTouchDownY = ev.getY(); + mLastTouchDownX = x; + mLastTouchDownY = y; newTouchTarget = addTouchTarget(child, idBitsToAssign); alreadyDispatchedToNewTouchTarget = true; break; @@ -3287,7 +3285,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager if (ev.isFromSource(InputDevice.SOURCE_MOUSE) && ev.getAction() == MotionEvent.ACTION_DOWN && ev.isButtonPressed(MotionEvent.BUTTON_PRIMARY) - && isOnScrollbarThumb(ev.getX(), ev.getY())) { + && isOnScrollbarThumb(ev.getXDispatchLocation(0), ev.getYDispatchLocation(0))) { return true; } return false; diff --git a/core/tests/coretests/src/android/view/ViewGroupTest.java b/core/tests/coretests/src/android/view/ViewGroupTest.java index 506cc2d3ff971..b37c8fd8c34e4 100644 --- a/core/tests/coretests/src/android/view/ViewGroupTest.java +++ b/core/tests/coretests/src/android/view/ViewGroupTest.java @@ -20,6 +20,7 @@ import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentat import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; @@ -87,6 +88,9 @@ public class ViewGroupTest { viewGroup.dispatchTouchEvent(event); verify(viewB).dispatchTouchEvent(event); + viewGroup.onResolvePointerIcon(event, 0 /* pointerIndex */); + verify(viewB).onResolvePointerIcon(event, 0); + event = MotionEvent.obtain(0 /* downTime */, 0 /* eventTime */, MotionEvent.ACTION_POINTER_DOWN | (1 << MotionEvent.ACTION_POINTER_INDEX_SHIFT), 2 /* pointerCount */, properties, coords, 0 /* metaState */, 0 /* buttonState */, @@ -95,7 +99,11 @@ public class ViewGroupTest { viewGroup.dispatchTouchEvent(event); verify(viewB).dispatchTouchEvent(event); + viewGroup.onResolvePointerIcon(event, 1 /* pointerIndex */); + verify(viewB).onResolvePointerIcon(event, 1); + verify(viewA, never()).dispatchTouchEvent(any()); + verify(viewA, never()).onResolvePointerIcon(any(), anyInt()); } /**