Loading core/java/android/view/MotionEvent.java +101 −10 Original line number Diff line number Diff line Loading @@ -1471,6 +1471,10 @@ public final class MotionEvent extends InputEvent implements Parcelable { @UnsupportedAppUsage private static final int HISTORY_CURRENT = -0x80000000; // This is essentially the same as native AMOTION_EVENT_INVALID_CURSOR_POSITION as they're all // NaN and we use isnan() everywhere to check validity. private static final float INVALID_CURSOR_POSITION = Float.NaN; private static final int MAX_RECYCLED = 10; private static final Object gRecyclerLock = new Object(); private static int gRecyclerUsed; Loading Loading @@ -1590,6 +1594,12 @@ public final class MotionEvent extends InputEvent implements Parcelable { @CriticalNative private static native float nativeGetYPrecision(long nativePtr); @CriticalNative private static native float nativeGetXCursorPosition(long nativePtr); @CriticalNative private static native float nativeGetYCursorPosition(long nativePtr); @CriticalNative private static native void nativeSetCursorPosition(long nativePtr, float x, float y); @CriticalNative private static native long nativeGetDownTimeNanos(long nativePtr); @CriticalNative private static native void nativeSetDownTimeNanos(long nativePtr, long downTime); Loading Loading @@ -1674,12 +1684,11 @@ public final class MotionEvent extends InputEvent implements Parcelable { float xPrecision, float yPrecision, int deviceId, int edgeFlags, int source, int displayId, int flags) { MotionEvent ev = obtain(); ev.mNativePtr = nativeInitialize(ev.mNativePtr, deviceId, source, displayId, action, flags, edgeFlags, metaState, buttonState, CLASSIFICATION_NONE, 0, 0, xPrecision, yPrecision, final boolean success = ev.initialize(deviceId, source, displayId, action, flags, edgeFlags, metaState, buttonState, CLASSIFICATION_NONE, 0, 0, xPrecision, yPrecision, downTime * NS_PER_MS, eventTime * NS_PER_MS, pointerCount, pointerProperties, pointerCoords); if (ev.mNativePtr == 0) { if (!success) { Log.e(TAG, "Could not initialize MotionEvent"); ev.recycle(); return null; Loading Loading @@ -1859,8 +1868,7 @@ public final class MotionEvent extends InputEvent implements Parcelable { pc[0].pressure = pressure; pc[0].size = size; ev.mNativePtr = nativeInitialize(ev.mNativePtr, deviceId, source, displayId, ev.initialize(deviceId, source, displayId, action, 0, edgeFlags, metaState, 0 /*buttonState*/, CLASSIFICATION_NONE, 0, 0, xPrecision, yPrecision, downTime * NS_PER_MS, eventTime * NS_PER_MS, Loading Loading @@ -1958,6 +1966,22 @@ public final class MotionEvent extends InputEvent implements Parcelable { return ev; } private boolean initialize(int deviceId, int source, int displayId, int action, int flags, int edgeFlags, int metaState, int buttonState, @Classification int classification, float xOffset, float yOffset, float xPrecision, float yPrecision, long downTimeNanos, long eventTimeNanos, int pointerCount, PointerProperties[] pointerIds, PointerCoords[] pointerCoords) { mNativePtr = nativeInitialize(mNativePtr, deviceId, source, displayId, action, flags, edgeFlags, metaState, buttonState, classification, xOffset, yOffset, xPrecision, yPrecision, downTimeNanos, eventTimeNanos, pointerCount, pointerIds, pointerCoords); if (mNativePtr == 0) { return false; } updateCursorPosition(); return true; } /** @hide */ @Override @UnsupportedAppUsage Loading Loading @@ -2015,7 +2039,11 @@ public final class MotionEvent extends InputEvent implements Parcelable { /** {@inheritDoc} */ @Override public final void setSource(int source) { if (source == getSource()) { return; } nativeSetSource(mNativePtr, source); updateCursorPosition(); } /** @hide */ Loading Loading @@ -2669,6 +2697,39 @@ public final class MotionEvent extends InputEvent implements Parcelable { return nativeGetYPrecision(mNativePtr); } /** * Returns the x coordinate of mouse cursor position when this event is * reported. This value is only valid if {@link #getSource()} returns * {@link InputDevice#SOURCE_MOUSE}. * * @hide */ public float getXCursorPosition() { return nativeGetXCursorPosition(mNativePtr); } /** * Returns the y coordinate of mouse cursor position when this event is * reported. This value is only valid if {@link #getSource()} returns * {@link InputDevice#SOURCE_MOUSE}. * * @hide */ public float getYCursorPosition() { return nativeGetYCursorPosition(mNativePtr); } /** * Sets cursor position to given coordinates. The coordinate in parameters should be after * offsetting. In other words, the effect of this function is {@link #getXCursorPosition()} and * {@link #getYCursorPosition()} will return the same value passed in the parameters. * * @hide */ private void setCursorPosition(float x, float y) { nativeSetCursorPosition(mNativePtr, x, y); } /** * Returns the number of historical points in this event. These are * movements that have occurred between this event and the previous event. Loading Loading @@ -3305,8 +3366,7 @@ public final class MotionEvent extends InputEvent implements Parcelable { pc[i].x = clamp(pc[i].x, left, right); pc[i].y = clamp(pc[i].y, top, bottom); } ev.mNativePtr = nativeInitialize(ev.mNativePtr, nativeGetDeviceId(mNativePtr), nativeGetSource(mNativePtr), ev.initialize(nativeGetDeviceId(mNativePtr), nativeGetSource(mNativePtr), nativeGetDisplayId(mNativePtr), nativeGetAction(mNativePtr), nativeGetFlags(mNativePtr), nativeGetEdgeFlags(mNativePtr), nativeGetMetaState(mNativePtr), Loading Loading @@ -3399,8 +3459,7 @@ public final class MotionEvent extends InputEvent implements Parcelable { final long eventTimeNanos = nativeGetEventTimeNanos(mNativePtr, historyPos); if (h == 0) { ev.mNativePtr = nativeInitialize(ev.mNativePtr, nativeGetDeviceId(mNativePtr), nativeGetSource(mNativePtr), ev.initialize(nativeGetDeviceId(mNativePtr), nativeGetSource(mNativePtr), nativeGetDisplayId(mNativePtr), newAction, nativeGetFlags(mNativePtr), nativeGetEdgeFlags(mNativePtr), nativeGetMetaState(mNativePtr), Loading @@ -3417,6 +3476,38 @@ public final class MotionEvent extends InputEvent implements Parcelable { } } /** * Calculate new cursor position for events from mouse. This is used to split, clamp and inject * events. * * <p>If the source is mouse, it sets cursor position to the centroid of all pointers because * InputReader maps multiple fingers on a touchpad to locations around cursor position in screen * coordinates so that the mouse cursor is at the centroid of all pointers. * * <p>If the source is not mouse it sets cursor position to NaN. */ private void updateCursorPosition() { if (getSource() != InputDevice.SOURCE_MOUSE) { setCursorPosition(INVALID_CURSOR_POSITION, INVALID_CURSOR_POSITION); return; } float x = 0; float y = 0; final int pointerCount = getPointerCount(); for (int i = 0; i < pointerCount; ++i) { x += getX(i); y += getY(i); } // If pointer count is 0, divisions below yield NaN, which is an acceptable result for this // corner case. x /= pointerCount; y /= pointerCount; setCursorPosition(x, y); } @Override public String toString() { StringBuilder msg = new StringBuilder(); Loading core/jni/android_view_MotionEvent.cpp +24 −0 Original line number Diff line number Diff line Loading @@ -701,6 +701,21 @@ static jfloat android_view_MotionEvent_nativeGetYPrecision(jlong nativePtr) { return event->getYPrecision(); } static jfloat android_view_MotionEvent_nativeGetXCursorPosition(jlong nativePtr) { MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr); return event->getXCursorPosition(); } static jfloat android_view_MotionEvent_nativeGetYCursorPosition(jlong nativePtr) { MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr); return event->getYCursorPosition(); } static void android_view_MotionEvent_nativeSetCursorPosition(jlong nativePtr, jfloat x, jfloat y) { MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr); event->setCursorPosition(x, y); } static jlong android_view_MotionEvent_nativeGetDownTimeNanos(jlong nativePtr) { MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr); return event->getDownTime(); Loading Loading @@ -871,6 +886,15 @@ static const JNINativeMethod gMotionEventMethods[] = { { "nativeGetYPrecision", "(J)F", (void*)android_view_MotionEvent_nativeGetYPrecision }, { "nativeGetXCursorPosition", "(J)F", (void*)android_view_MotionEvent_nativeGetXCursorPosition }, { "nativeGetYCursorPosition", "(J)F", (void*)android_view_MotionEvent_nativeGetYCursorPosition }, { "nativeSetCursorPosition", "(JFF)V", (void*)android_view_MotionEvent_nativeSetCursorPosition }, { "nativeGetDownTimeNanos", "(J)J", (void*)android_view_MotionEvent_nativeGetDownTimeNanos }, Loading core/tests/coretests/src/android/view/MotionEventTest.java +61 −0 Original line number Diff line number Diff line Loading @@ -17,8 +17,11 @@ package android.view; import static android.view.MotionEvent.ACTION_DOWN; import static android.view.MotionEvent.ACTION_POINTER_DOWN; import static android.view.MotionEvent.TOOL_TYPE_FINGER; import static junit.framework.Assert.assertTrue; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; Loading Loading @@ -77,4 +80,62 @@ public class MotionEventTest { 0, 0, 0, 0, 0, 0, InputDevice.SOURCE_TOUCHSCREEN, displayId, 0); assertNull(motionEvent); } @Test public void testCalculatesCursorPositionForTouchscreenEvents() { final MotionEvent event = MotionEvent.obtain(0 /* downTime */, 0 /* eventTime */, ACTION_DOWN, 30 /* x */, 50 /* y */, 0 /* metaState */); event.setSource(InputDevice.SOURCE_TOUCHSCREEN); assertTrue(Float.isNaN(event.getXCursorPosition())); assertTrue(Float.isNaN(event.getYCursorPosition())); } @Test public void testCalculatesCursorPositionForSimpleMouseEvents() { final MotionEvent event = MotionEvent.obtain(0 /* downTime */, 0 /* eventTime */, ACTION_DOWN, 30 /* x */, 50 /* y */, 0 /* metaState */); event.setSource(InputDevice.SOURCE_MOUSE); assertEquals(30, event.getXCursorPosition(), 0.1); assertEquals(50, event.getYCursorPosition(), 0.1); } @Test public void testCalculatesCursorPositionForSimpleMouseEventsWithOffset() { final MotionEvent event = MotionEvent.obtain(0 /* downTime */, 0 /* eventTime */, ACTION_DOWN, 30 /* x */, 50 /* y */, 0 /* metaState */); event.offsetLocation(10 /* deltaX */, 20 /* deltaY */); event.setSource(InputDevice.SOURCE_MOUSE); assertEquals(40, event.getXCursorPosition(), 0.1); assertEquals(70, event.getYCursorPosition(), 0.1); } @Test public void testCalculatesCursorPositionForMultiTouchMouseEvents() { final int pointerCount = 2; final PointerProperties[] properties = new PointerProperties[pointerCount]; final PointerCoords[] coords = new PointerCoords[pointerCount]; for (int i = 0; i < pointerCount; ++i) { properties[i] = new PointerProperties(); properties[i].id = i; properties[i].toolType = MotionEvent.TOOL_TYPE_FINGER; coords[i] = new PointerCoords(); coords[i].x = 20 + i * 20; coords[i].y = 60 - i * 20; } final MotionEvent event = MotionEvent.obtain(0 /* downTime */, 0 /* eventTime */, ACTION_POINTER_DOWN, pointerCount, properties, coords, 0 /* metaState */, 0 /* buttonState */, 1 /* xPrecision */, 1 /* yPrecision */, 0 /* deviceId */, 0 /* edgeFlags */, InputDevice.SOURCE_MOUSE, 0 /* flags */); assertEquals(30, event.getXCursorPosition(), 0.1); assertEquals(50, event.getYCursorPosition(), 0.1); } } Loading
core/java/android/view/MotionEvent.java +101 −10 Original line number Diff line number Diff line Loading @@ -1471,6 +1471,10 @@ public final class MotionEvent extends InputEvent implements Parcelable { @UnsupportedAppUsage private static final int HISTORY_CURRENT = -0x80000000; // This is essentially the same as native AMOTION_EVENT_INVALID_CURSOR_POSITION as they're all // NaN and we use isnan() everywhere to check validity. private static final float INVALID_CURSOR_POSITION = Float.NaN; private static final int MAX_RECYCLED = 10; private static final Object gRecyclerLock = new Object(); private static int gRecyclerUsed; Loading Loading @@ -1590,6 +1594,12 @@ public final class MotionEvent extends InputEvent implements Parcelable { @CriticalNative private static native float nativeGetYPrecision(long nativePtr); @CriticalNative private static native float nativeGetXCursorPosition(long nativePtr); @CriticalNative private static native float nativeGetYCursorPosition(long nativePtr); @CriticalNative private static native void nativeSetCursorPosition(long nativePtr, float x, float y); @CriticalNative private static native long nativeGetDownTimeNanos(long nativePtr); @CriticalNative private static native void nativeSetDownTimeNanos(long nativePtr, long downTime); Loading Loading @@ -1674,12 +1684,11 @@ public final class MotionEvent extends InputEvent implements Parcelable { float xPrecision, float yPrecision, int deviceId, int edgeFlags, int source, int displayId, int flags) { MotionEvent ev = obtain(); ev.mNativePtr = nativeInitialize(ev.mNativePtr, deviceId, source, displayId, action, flags, edgeFlags, metaState, buttonState, CLASSIFICATION_NONE, 0, 0, xPrecision, yPrecision, final boolean success = ev.initialize(deviceId, source, displayId, action, flags, edgeFlags, metaState, buttonState, CLASSIFICATION_NONE, 0, 0, xPrecision, yPrecision, downTime * NS_PER_MS, eventTime * NS_PER_MS, pointerCount, pointerProperties, pointerCoords); if (ev.mNativePtr == 0) { if (!success) { Log.e(TAG, "Could not initialize MotionEvent"); ev.recycle(); return null; Loading Loading @@ -1859,8 +1868,7 @@ public final class MotionEvent extends InputEvent implements Parcelable { pc[0].pressure = pressure; pc[0].size = size; ev.mNativePtr = nativeInitialize(ev.mNativePtr, deviceId, source, displayId, ev.initialize(deviceId, source, displayId, action, 0, edgeFlags, metaState, 0 /*buttonState*/, CLASSIFICATION_NONE, 0, 0, xPrecision, yPrecision, downTime * NS_PER_MS, eventTime * NS_PER_MS, Loading Loading @@ -1958,6 +1966,22 @@ public final class MotionEvent extends InputEvent implements Parcelable { return ev; } private boolean initialize(int deviceId, int source, int displayId, int action, int flags, int edgeFlags, int metaState, int buttonState, @Classification int classification, float xOffset, float yOffset, float xPrecision, float yPrecision, long downTimeNanos, long eventTimeNanos, int pointerCount, PointerProperties[] pointerIds, PointerCoords[] pointerCoords) { mNativePtr = nativeInitialize(mNativePtr, deviceId, source, displayId, action, flags, edgeFlags, metaState, buttonState, classification, xOffset, yOffset, xPrecision, yPrecision, downTimeNanos, eventTimeNanos, pointerCount, pointerIds, pointerCoords); if (mNativePtr == 0) { return false; } updateCursorPosition(); return true; } /** @hide */ @Override @UnsupportedAppUsage Loading Loading @@ -2015,7 +2039,11 @@ public final class MotionEvent extends InputEvent implements Parcelable { /** {@inheritDoc} */ @Override public final void setSource(int source) { if (source == getSource()) { return; } nativeSetSource(mNativePtr, source); updateCursorPosition(); } /** @hide */ Loading Loading @@ -2669,6 +2697,39 @@ public final class MotionEvent extends InputEvent implements Parcelable { return nativeGetYPrecision(mNativePtr); } /** * Returns the x coordinate of mouse cursor position when this event is * reported. This value is only valid if {@link #getSource()} returns * {@link InputDevice#SOURCE_MOUSE}. * * @hide */ public float getXCursorPosition() { return nativeGetXCursorPosition(mNativePtr); } /** * Returns the y coordinate of mouse cursor position when this event is * reported. This value is only valid if {@link #getSource()} returns * {@link InputDevice#SOURCE_MOUSE}. * * @hide */ public float getYCursorPosition() { return nativeGetYCursorPosition(mNativePtr); } /** * Sets cursor position to given coordinates. The coordinate in parameters should be after * offsetting. In other words, the effect of this function is {@link #getXCursorPosition()} and * {@link #getYCursorPosition()} will return the same value passed in the parameters. * * @hide */ private void setCursorPosition(float x, float y) { nativeSetCursorPosition(mNativePtr, x, y); } /** * Returns the number of historical points in this event. These are * movements that have occurred between this event and the previous event. Loading Loading @@ -3305,8 +3366,7 @@ public final class MotionEvent extends InputEvent implements Parcelable { pc[i].x = clamp(pc[i].x, left, right); pc[i].y = clamp(pc[i].y, top, bottom); } ev.mNativePtr = nativeInitialize(ev.mNativePtr, nativeGetDeviceId(mNativePtr), nativeGetSource(mNativePtr), ev.initialize(nativeGetDeviceId(mNativePtr), nativeGetSource(mNativePtr), nativeGetDisplayId(mNativePtr), nativeGetAction(mNativePtr), nativeGetFlags(mNativePtr), nativeGetEdgeFlags(mNativePtr), nativeGetMetaState(mNativePtr), Loading Loading @@ -3399,8 +3459,7 @@ public final class MotionEvent extends InputEvent implements Parcelable { final long eventTimeNanos = nativeGetEventTimeNanos(mNativePtr, historyPos); if (h == 0) { ev.mNativePtr = nativeInitialize(ev.mNativePtr, nativeGetDeviceId(mNativePtr), nativeGetSource(mNativePtr), ev.initialize(nativeGetDeviceId(mNativePtr), nativeGetSource(mNativePtr), nativeGetDisplayId(mNativePtr), newAction, nativeGetFlags(mNativePtr), nativeGetEdgeFlags(mNativePtr), nativeGetMetaState(mNativePtr), Loading @@ -3417,6 +3476,38 @@ public final class MotionEvent extends InputEvent implements Parcelable { } } /** * Calculate new cursor position for events from mouse. This is used to split, clamp and inject * events. * * <p>If the source is mouse, it sets cursor position to the centroid of all pointers because * InputReader maps multiple fingers on a touchpad to locations around cursor position in screen * coordinates so that the mouse cursor is at the centroid of all pointers. * * <p>If the source is not mouse it sets cursor position to NaN. */ private void updateCursorPosition() { if (getSource() != InputDevice.SOURCE_MOUSE) { setCursorPosition(INVALID_CURSOR_POSITION, INVALID_CURSOR_POSITION); return; } float x = 0; float y = 0; final int pointerCount = getPointerCount(); for (int i = 0; i < pointerCount; ++i) { x += getX(i); y += getY(i); } // If pointer count is 0, divisions below yield NaN, which is an acceptable result for this // corner case. x /= pointerCount; y /= pointerCount; setCursorPosition(x, y); } @Override public String toString() { StringBuilder msg = new StringBuilder(); Loading
core/jni/android_view_MotionEvent.cpp +24 −0 Original line number Diff line number Diff line Loading @@ -701,6 +701,21 @@ static jfloat android_view_MotionEvent_nativeGetYPrecision(jlong nativePtr) { return event->getYPrecision(); } static jfloat android_view_MotionEvent_nativeGetXCursorPosition(jlong nativePtr) { MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr); return event->getXCursorPosition(); } static jfloat android_view_MotionEvent_nativeGetYCursorPosition(jlong nativePtr) { MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr); return event->getYCursorPosition(); } static void android_view_MotionEvent_nativeSetCursorPosition(jlong nativePtr, jfloat x, jfloat y) { MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr); event->setCursorPosition(x, y); } static jlong android_view_MotionEvent_nativeGetDownTimeNanos(jlong nativePtr) { MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr); return event->getDownTime(); Loading Loading @@ -871,6 +886,15 @@ static const JNINativeMethod gMotionEventMethods[] = { { "nativeGetYPrecision", "(J)F", (void*)android_view_MotionEvent_nativeGetYPrecision }, { "nativeGetXCursorPosition", "(J)F", (void*)android_view_MotionEvent_nativeGetXCursorPosition }, { "nativeGetYCursorPosition", "(J)F", (void*)android_view_MotionEvent_nativeGetYCursorPosition }, { "nativeSetCursorPosition", "(JFF)V", (void*)android_view_MotionEvent_nativeSetCursorPosition }, { "nativeGetDownTimeNanos", "(J)J", (void*)android_view_MotionEvent_nativeGetDownTimeNanos }, Loading
core/tests/coretests/src/android/view/MotionEventTest.java +61 −0 Original line number Diff line number Diff line Loading @@ -17,8 +17,11 @@ package android.view; import static android.view.MotionEvent.ACTION_DOWN; import static android.view.MotionEvent.ACTION_POINTER_DOWN; import static android.view.MotionEvent.TOOL_TYPE_FINGER; import static junit.framework.Assert.assertTrue; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; Loading Loading @@ -77,4 +80,62 @@ public class MotionEventTest { 0, 0, 0, 0, 0, 0, InputDevice.SOURCE_TOUCHSCREEN, displayId, 0); assertNull(motionEvent); } @Test public void testCalculatesCursorPositionForTouchscreenEvents() { final MotionEvent event = MotionEvent.obtain(0 /* downTime */, 0 /* eventTime */, ACTION_DOWN, 30 /* x */, 50 /* y */, 0 /* metaState */); event.setSource(InputDevice.SOURCE_TOUCHSCREEN); assertTrue(Float.isNaN(event.getXCursorPosition())); assertTrue(Float.isNaN(event.getYCursorPosition())); } @Test public void testCalculatesCursorPositionForSimpleMouseEvents() { final MotionEvent event = MotionEvent.obtain(0 /* downTime */, 0 /* eventTime */, ACTION_DOWN, 30 /* x */, 50 /* y */, 0 /* metaState */); event.setSource(InputDevice.SOURCE_MOUSE); assertEquals(30, event.getXCursorPosition(), 0.1); assertEquals(50, event.getYCursorPosition(), 0.1); } @Test public void testCalculatesCursorPositionForSimpleMouseEventsWithOffset() { final MotionEvent event = MotionEvent.obtain(0 /* downTime */, 0 /* eventTime */, ACTION_DOWN, 30 /* x */, 50 /* y */, 0 /* metaState */); event.offsetLocation(10 /* deltaX */, 20 /* deltaY */); event.setSource(InputDevice.SOURCE_MOUSE); assertEquals(40, event.getXCursorPosition(), 0.1); assertEquals(70, event.getYCursorPosition(), 0.1); } @Test public void testCalculatesCursorPositionForMultiTouchMouseEvents() { final int pointerCount = 2; final PointerProperties[] properties = new PointerProperties[pointerCount]; final PointerCoords[] coords = new PointerCoords[pointerCount]; for (int i = 0; i < pointerCount; ++i) { properties[i] = new PointerProperties(); properties[i].id = i; properties[i].toolType = MotionEvent.TOOL_TYPE_FINGER; coords[i] = new PointerCoords(); coords[i].x = 20 + i * 20; coords[i].y = 60 - i * 20; } final MotionEvent event = MotionEvent.obtain(0 /* downTime */, 0 /* eventTime */, ACTION_POINTER_DOWN, pointerCount, properties, coords, 0 /* metaState */, 0 /* buttonState */, 1 /* xPrecision */, 1 /* yPrecision */, 0 /* deviceId */, 0 /* edgeFlags */, InputDevice.SOURCE_MOUSE, 0 /* flags */); assertEquals(30, event.getXCursorPosition(), 0.1); assertEquals(50, event.getYCursorPosition(), 0.1); } }