ViewGroup: Send events to the child at the event's dispatch location
A mouse event should always be dispatched to the cursor position, regardless of the location of the pointers in the MotionEvent. When there are touchpad gestures like a two finger swipe or a pinch, we promote compatibility with apps by adding fake pointers that emulate the same behavior on a touchscreen. For example, two finger swipes turn into a one finger drag, and two finger pinch emulates a pinch on the screen using two pointers. The proper way for views to handle touchpad gestures is to look for the new motion classifications added for touchpad gestures, and dispatch events at the cursor position for these gestures. However, our API is incomplete at the moment, becuase apps cannot query the cursor position. We can at least fix this in the UiToolkit by dispatching mouse events to the cursor location. Bug: 279444161 Test: manual Test: atest FrameworksCoreTests:ViewGroupTest Change-Id: I58a9c06b7651ebe97cd03447b083cc9887f863b1
This commit is contained in:
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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<View> 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<View> 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<View> 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<View> 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;
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user