diff options
| -rw-r--r-- | core/java/android/view/InputEventAssigner.java | 12 | ||||
| -rw-r--r-- | tests/Input/src/com/android/test/input/InputEventAssignerTest.kt | 186 |
2 files changed, 154 insertions, 44 deletions
diff --git a/core/java/android/view/InputEventAssigner.java b/core/java/android/view/InputEventAssigner.java index 7fac6c5e4af6..30d9aaa4b144 100644 --- a/core/java/android/view/InputEventAssigner.java +++ b/core/java/android/view/InputEventAssigner.java @@ -17,7 +17,8 @@ package android.view; import static android.os.IInputConstants.INVALID_INPUT_EVENT_ID; -import static android.view.InputDevice.SOURCE_TOUCHSCREEN; +import static android.view.InputDevice.SOURCE_CLASS_POINTER; +import static android.view.InputDevice.SOURCE_CLASS_POSITION; /** * Process input events and assign input event id to a specific frame. @@ -64,18 +65,19 @@ public class InputEventAssigner { public int processEvent(InputEvent event) { if (event instanceof MotionEvent) { MotionEvent motionEvent = (MotionEvent) event; - if (motionEvent.isFromSource(SOURCE_TOUCHSCREEN)) { + if (motionEvent.isFromSource(SOURCE_CLASS_POINTER) || motionEvent.isFromSource( + SOURCE_CLASS_POSITION)) { final int action = motionEvent.getActionMasked(); if (action == MotionEvent.ACTION_DOWN) { mHasUnprocessedDown = true; mDownEventId = event.getId(); } - if (mHasUnprocessedDown && action == MotionEvent.ACTION_MOVE) { - return mDownEventId; - } if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) { mHasUnprocessedDown = false; } + if (mHasUnprocessedDown) { + return mDownEventId; + } } } return event.getId(); diff --git a/tests/Input/src/com/android/test/input/InputEventAssignerTest.kt b/tests/Input/src/com/android/test/input/InputEventAssignerTest.kt index c1a86b3a2dac..015e188fc98e 100644 --- a/tests/Input/src/com/android/test/input/InputEventAssignerTest.kt +++ b/tests/Input/src/com/android/test/input/InputEventAssignerTest.kt @@ -18,12 +18,20 @@ package com.android.test.input import android.view.InputDevice.SOURCE_MOUSE import android.view.InputDevice.SOURCE_TOUCHSCREEN +import android.view.InputDevice.SOURCE_STYLUS +import android.view.InputDevice.SOURCE_TOUCHPAD + import android.view.InputEventAssigner import android.view.KeyEvent import android.view.MotionEvent import org.junit.Assert.assertEquals import org.junit.Test +sealed class StreamEvent +private data object Vsync : StreamEvent() +data class MotionEventData(val action: Int, val source: Int, val id: Int, val expectedId: Int) : + StreamEvent() + /** * Create a MotionEvent with the provided action, eventTime, and source */ @@ -49,64 +57,164 @@ private fun createKeyEvent(action: Int, eventTime: Long): KeyEvent { return KeyEvent(eventTime, eventTime, action, code, repeat) } +/** + * Check that the correct eventIds are assigned in a stream. The stream consists of motion + * events or vsync (processed frame) + * Each streamEvent should have unique ids when writing tests + * The test passes even if two events get assigned the same eventId, since the mapping is + * streamEventId -> motionEventId and streamEvents have unique ids + */ +private fun checkEventStream(vararg streamEvents: StreamEvent) { + val assigner = InputEventAssigner() + var eventTime = 10L + // Maps MotionEventData.id to MotionEvent.id + // We can't control the event id of the generated motion events but for testing it's easier + // to label the events with a custom id for readability + val eventIdMap: HashMap<Int, Int> = HashMap() + for (streamEvent in streamEvents) { + when (streamEvent) { + is MotionEventData -> { + val event = createMotionEvent(streamEvent.action, eventTime, streamEvent.source) + eventIdMap[streamEvent.id] = event.id + val eventId = assigner.processEvent(event) + assertEquals(eventIdMap[streamEvent.expectedId], eventId) + } + is Vsync -> assigner.notifyFrameProcessed() + } + eventTime += 1 + } +} + class InputEventAssignerTest { companion object { private const val TAG = "InputEventAssignerTest" } /** - * A single MOVE event should be assigned to the next available frame. + * A single event should be assigned to the next available frame. */ @Test - fun testTouchGesture() { - val assigner = InputEventAssigner() - val event = createMotionEvent(MotionEvent.ACTION_MOVE, 10, SOURCE_TOUCHSCREEN) - val eventId = assigner.processEvent(event) - assertEquals(event.id, eventId) + fun testTouchMove() { + checkEventStream( + MotionEventData(MotionEvent.ACTION_MOVE, SOURCE_TOUCHSCREEN, id = 1, expectedId = 1) + ) + } + + @Test + fun testMouseMove() { + checkEventStream( + MotionEventData(MotionEvent.ACTION_MOVE, SOURCE_MOUSE, id = 1, expectedId = 1) + ) + } + + @Test + fun testMouseScroll() { + checkEventStream( + MotionEventData(MotionEvent.ACTION_SCROLL, SOURCE_MOUSE, id = 1, expectedId = 1) + ) + } + + @Test + fun testStylusMove() { + checkEventStream( + MotionEventData(MotionEvent.ACTION_MOVE, SOURCE_STYLUS, id = 1, expectedId = 1) + ) + } + + @Test + fun testStylusHover() { + checkEventStream( + MotionEventData(MotionEvent.ACTION_HOVER_MOVE, SOURCE_STYLUS, id = 1, expectedId = 1) + ) + } + + @Test + fun testTouchpadMove() { + checkEventStream( + MotionEventData(MotionEvent.ACTION_MOVE, SOURCE_STYLUS, id = 1, expectedId = 1) + ) } /** - * DOWN event should be used until a vsync comes in. After vsync, the latest event should be - * produced. + * Test that before a VSYNC the event id generated by input event assigner for move events is + * the id of the down event. Move events coming after a VSYNC should be assigned their own event + * id */ + private fun testDownAndMove(source: Int) { + checkEventStream( + MotionEventData(MotionEvent.ACTION_DOWN, source, id = 1, expectedId = 1), + MotionEventData(MotionEvent.ACTION_MOVE, source, id = 2, expectedId = 1), + Vsync, + MotionEventData(MotionEvent.ACTION_MOVE, source, id = 4, expectedId = 4) + ) + } + @Test - fun testTouchDownWithMove() { - val assigner = InputEventAssigner() - val down = createMotionEvent(MotionEvent.ACTION_DOWN, 10, SOURCE_TOUCHSCREEN) - val move1 = createMotionEvent(MotionEvent.ACTION_MOVE, 12, SOURCE_TOUCHSCREEN) - val move2 = createMotionEvent(MotionEvent.ACTION_MOVE, 13, SOURCE_TOUCHSCREEN) - val move3 = createMotionEvent(MotionEvent.ACTION_MOVE, 14, SOURCE_TOUCHSCREEN) - val move4 = createMotionEvent(MotionEvent.ACTION_MOVE, 15, SOURCE_TOUCHSCREEN) - var eventId = assigner.processEvent(down) - assertEquals(down.id, eventId) - eventId = assigner.processEvent(move1) - assertEquals(down.id, eventId) - eventId = assigner.processEvent(move2) - // Even though we already had 2 move events, there was no choreographer callback yet. - // Therefore, we should still get the id of the down event - assertEquals(down.id, eventId) + fun testTouchDownAndMove() { + testDownAndMove(SOURCE_TOUCHSCREEN) + } - // Now send CALLBACK_INPUT to the assigner. It should provide the latest motion event - assigner.notifyFrameProcessed() - eventId = assigner.processEvent(move3) - assertEquals(move3.id, eventId) - eventId = assigner.processEvent(move4) - assertEquals(move4.id, eventId) + @Test + fun testMouseDownAndMove() { + testDownAndMove(SOURCE_MOUSE) + } + + @Test + fun testStylusDownAndMove() { + testDownAndMove(SOURCE_STYLUS) + } + + @Test + fun testTouchpadDownAndMove() { + testDownAndMove(SOURCE_TOUCHPAD) } /** - * Similar to the above test, but with SOURCE_MOUSE. Since we don't have down latency - * concept for non-touchscreens, the latest input event will be used. + * After an up event, motion events should be assigned their own event id */ @Test - fun testMouseDownWithMove() { - val assigner = InputEventAssigner() - val down = createMotionEvent(MotionEvent.ACTION_DOWN, 10, SOURCE_MOUSE) - val move1 = createMotionEvent(MotionEvent.ACTION_MOVE, 12, SOURCE_MOUSE) - var eventId = assigner.processEvent(down) - assertEquals(down.id, eventId) - eventId = assigner.processEvent(move1) - assertEquals(move1.id, eventId) + fun testMouseDownUpAndScroll() { + checkEventStream( + MotionEventData(MotionEvent.ACTION_DOWN, SOURCE_MOUSE, id = 1, expectedId = 1), + MotionEventData(MotionEvent.ACTION_UP, SOURCE_MOUSE, id = 2, expectedId = 2), + MotionEventData(MotionEvent.ACTION_SCROLL, SOURCE_MOUSE, id = 3, expectedId = 3) + ) + } + + /** + * After an up event, motion events should be assigned their own event id + */ + @Test + fun testStylusDownUpAndHover() { + checkEventStream( + MotionEventData(MotionEvent.ACTION_DOWN, SOURCE_STYLUS, id = 1, expectedId = 1), + MotionEventData(MotionEvent.ACTION_UP, SOURCE_STYLUS, id = 2, expectedId = 2), + MotionEventData(MotionEvent.ACTION_HOVER_ENTER, SOURCE_STYLUS, id = 3, expectedId = 3) + ) + } + + /** + * After a cancel event, motion events should be assigned their own event id + */ + @Test + fun testMouseDownCancelAndScroll() { + checkEventStream( + MotionEventData(MotionEvent.ACTION_DOWN, SOURCE_MOUSE, id = 1, expectedId = 1), + MotionEventData(MotionEvent.ACTION_CANCEL, SOURCE_MOUSE, id = 2, expectedId = 2), + MotionEventData(MotionEvent.ACTION_SCROLL, SOURCE_MOUSE, id = 3, expectedId = 3) + ) + } + + /** + * After a cancel event, motion events should be assigned their own event id + */ + @Test + fun testStylusDownCancelAndHover() { + checkEventStream( + MotionEventData(MotionEvent.ACTION_DOWN, SOURCE_STYLUS, id = 1, expectedId = 1), + MotionEventData(MotionEvent.ACTION_CANCEL, SOURCE_STYLUS, id = 2, expectedId = 2), + MotionEventData(MotionEvent.ACTION_HOVER_ENTER, SOURCE_STYLUS, id = 3, expectedId = 3) + ) } /** |