Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7a0c3b2b authored by Ryuichiro Chiba's avatar Ryuichiro Chiba
Browse files

Inject swipe events with fixed frequency

The current implementation of `input swipe` injects too many motion
events during the swipe simulation. This behavior is different from the
real situation and other tools such as UI Automator.
This CL adds a sleep between input events so that they are emitted with
fixed frequency (120Hz).

Bug: 373998306
Test: atest InputShellCommandTest
Test: adb shell input swipe 200 800 200 200
Test: adb shell input swipe 200 800 200 200 100
Flag: EXEMPT minor change with unit test
Change-Id: I8b0d1a387e910280da8676201797723c47567a3a
parent 0ef1067a
Loading
Loading
Loading
Loading
+23 −1
Original line number Diff line number Diff line
@@ -89,6 +89,9 @@ public class InputShellCommand extends ShellCommand {
    private static final int DEFAULT_FLAGS = 0;
    private static final boolean INJECT_ASYNC = true;
    private static final boolean INJECT_SYNC = false;
    private static final long SECOND_IN_MILLISECONDS = 1000;

    public static final int SWIPE_EVENT_HZ_DEFAULT = 120;

    /** Modifier key to meta state */
    private static final Map<Integer, Integer> MODIFIER;
@@ -519,11 +522,30 @@ public class InputShellCommand extends ShellCommand {
        }
        long now = SystemClock.uptimeMillis();
        final long endTime = down + duration;
        final float swipeEventPeriodMillis =
                (float) SECOND_IN_MILLISECONDS / SWIPE_EVENT_HZ_DEFAULT;
        int injected = 1;
        while (now < endTime) {
            final long elapsedTime = now - down;
            // Ensure that we inject at most at the frequency of SWIPE_EVENT_HZ_DEFAULT
            // by waiting an additional delta between the actual time and expected time.
            long elapsedTime = now - down;
            final long errorMillis =
                    (long) Math.floor(injected * swipeEventPeriodMillis - elapsedTime);
            if (errorMillis > 0) {
                // Make sure not to exceed the duration and inject an extra event.
                if (errorMillis > endTime - now) {
                    sleep(endTime - now);
                    break;
                }
                sleep(errorMillis);
            }

            now = SystemClock.uptimeMillis();
            elapsedTime = now - down;
            final float alpha = (float) elapsedTime / duration;
            injectMotionEvent(inputSource, MotionEvent.ACTION_MOVE, down, now,
                    lerp(x1, x2, alpha), lerp(y1, y2, alpha), 1.0f, displayId);
            injected++;
            now = SystemClock.uptimeMillis();
        }
        injectMotionEvent(inputSource, MotionEvent.ACTION_UP, down, now, x2, y2, 0.0f,
+15 −0
Original line number Diff line number Diff line
@@ -133,6 +133,21 @@ public class InputShellCommandTest {
        assertThat(mInputEventInjector.mInjectedEvents).isEmpty();
    }

    @Test
    public void testSwipeCommandEventFrequency() {
        int[] durations = {100, 300, 500};
        for (int durationMillis: durations) {
            mInputEventInjector.mInjectedEvents.clear();
            runCommand(String.format("swipe 200 800 200 200 %d", durationMillis));

            // Add 2 events for ACTION_DOWN and ACTION_UP.
            final int maxEventNum =
                    (int) Math.ceil(InputShellCommand.SWIPE_EVENT_HZ_DEFAULT
                            * (float) durationMillis / 1000) + 2;
            assertThat(mInputEventInjector.mInjectedEvents.size()).isAtMost(maxEventNum);
        }
    }

    private InputEvent getSingleInjectedInputEvent() {
        assertThat(mInputEventInjector.mInjectedEvents).hasSize(1);
        return mInputEventInjector.mInjectedEvents.get(0);