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

Commit 98c7ca2f authored by Liana Kazanova (xWF)'s avatar Liana Kazanova (xWF)
Browse files

Revert "Refactor Single key detector to provide start, complete ..."

Revert submission 33464207-support_gemini_power_key_animation

Reason for revert: DroidMonitor: Potential culprit for http://b/417498518 - verifying through ABTD before revert submission. This is part of the standard investigation process, and does not mean your CL will be reverted.

Bug: 417498518

Reverted changes: /q/submissionid:33464207-support_gemini_power_key_animation

Change-Id: I49516b544f924e5fca774117414e0dbae1ee96d5
parent a02bd7a3
Loading
Loading
Loading
Loading
+39 −80
Original line number Diff line number Diff line
@@ -82,10 +82,6 @@ import static android.view.contentprotection.flags.Flags.createAccessibilityOver

import static com.android.hardware.input.Flags.enableNew25q2Keycodes;
import static com.android.hardware.input.Flags.hidBluetoothWakeup;
import static com.android.server.policy.SingleKeyGestureEvent.ACTION_COMPLETE;
import static com.android.server.policy.SingleKeyGestureEvent.SINGLE_KEY_GESTURE_TYPE_LONG_PRESS;
import static com.android.server.policy.SingleKeyGestureEvent.SINGLE_KEY_GESTURE_TYPE_PRESS;
import static com.android.server.policy.SingleKeyGestureEvent.SINGLE_KEY_GESTURE_TYPE_VERY_LONG_PRESS;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVERED;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVER_ABSENT;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_UNCOVERED;
@@ -109,7 +105,6 @@ import static com.android.systemui.shared.Flags.enableLppAssistInvocationHapticE
import static com.android.window.flags.Flags.delegateBackGestureToShell;

import android.accessibilityservice.AccessibilityService;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
@@ -2501,31 +2496,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
        }

        @Override
        void onKeyGesture(@NonNull SingleKeyGestureEvent event) {
            final long startTime = event.getStartTime();
            final int displayId = event.getDisplayId();
            final int pressCount = event.getPressCount();
            if (event.getAction() != ACTION_COMPLETE) {
                return;
            }
            switch (event.getType()) {
                case SINGLE_KEY_GESTURE_TYPE_PRESS:
                    if (event.getPressCount() > 1) {
                        onMultiPress(startTime, pressCount, displayId);
                    } else {
                        onPress(startTime, displayId);
                    }
                    break;
                case SINGLE_KEY_GESTURE_TYPE_LONG_PRESS:
                    onLongPress(startTime);
                    break;
                case SINGLE_KEY_GESTURE_TYPE_VERY_LONG_PRESS:
                    onVeryLongPress();
                    break;
            }
        }

        private void onPress(long downTime, int displayId) {
        void onPress(long downTime, int displayId) {
            if (mShouldEarlyShortPressOnPower) {
                return;
            }
@@ -2541,29 +2512,32 @@ public class PhoneWindowManager implements WindowManagerPolicy {
            }
        }

        private void onLongPress(long downTime) {
        @Override
        void onLongPress(long eventTime) {
            if (mSingleKeyGestureDetector.beganFromNonInteractive()
                    && !mSupportLongPressPowerWhenNonInteractive) {
                Slog.v(TAG, "Not support long press power when device is not interactive.");
                return;
            }

            powerLongPress(downTime);
            powerLongPress(eventTime);
        }

        private void onVeryLongPress() {
        @Override
        void onVeryLongPress(long eventTime) {
            mActivityManagerInternal.prepareForPossibleShutdown();
            powerVeryLongPress();
        }

        private void onMultiPress(long downTime, int count, int displayId) {
        @Override
        void onMultiPress(long downTime, int count, int displayId) {
            powerPress(downTime, count, displayId);
        }

        @Override
        void onKeyUp(int count, KeyEvent event) {
        void onKeyUp(long eventTime, int count, int displayId, int deviceId, int metaState) {
            if (mShouldEarlyShortPressOnPower && count == 1) {
                powerPress(event.getDownTime(), 1 /*pressCount*/, event.getDisplayId());
                powerPress(eventTime, 1 /*pressCount*/, displayId);
            }
        }
    }
@@ -2582,20 +2556,18 @@ public class PhoneWindowManager implements WindowManagerPolicy {
        }

        @Override
        void onKeyGesture(@NonNull SingleKeyGestureEvent event) {
            if (event.getAction() != ACTION_COMPLETE) {
                return;
        int getMaxMultiPressCount() {
            return 1;
        }
            switch (event.getType()) {
                case SINGLE_KEY_GESTURE_TYPE_PRESS:
                    if (event.getPressCount() == 1) {

        @Override
        void onPress(long downTime, int unusedDisplayId) {
            mBackKeyHandled |= backKeyPress();
        }
                    break;
                case SINGLE_KEY_GESTURE_TYPE_LONG_PRESS:

        @Override
        void onLongPress(long downTime) {
            backLongPress();
                    break;
            }
        }
    }

@@ -2618,27 +2590,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
        }

        @Override
        void onKeyGesture(@NonNull SingleKeyGestureEvent event) {
            final long startTime = event.getStartTime();
            final int pressCount = event.getPressCount();
            if (event.getAction() != ACTION_COMPLETE) {
                return;
            }
            switch (event.getType()) {
                case SINGLE_KEY_GESTURE_TYPE_PRESS:
                    if (event.getPressCount() > 1) {
                        onMultiPress(startTime, pressCount);
                    } else {
                        onPress(startTime);
                    }
                    break;
                case SINGLE_KEY_GESTURE_TYPE_LONG_PRESS:
                    onLongPress(startTime);
                    break;
            }
        }

        private void onPress(long downTime) {
        void onPress(long downTime, int unusedDisplayId) {
            if (shouldHandleStemPrimaryEarlyShortPress()) {
                return;
            }
@@ -2647,20 +2599,22 @@ public class PhoneWindowManager implements WindowManagerPolicy {
                    KeyEvent.KEYCODE_STEM_PRIMARY, downTime, () -> stemPrimaryPress(1 /*count*/));
        }

        private void onLongPress(long downTime) {
        @Override
        void onLongPress(long eventTime) {
            if (mLongPressOnStemPrimaryBehavior == LONG_PRESS_PRIMARY_LAUNCH_VOICE_ASSISTANT) {
                // Long-press to assistant gesture is not overridable by apps.
                stemPrimaryLongPress(downTime);
                stemPrimaryLongPress(eventTime);
            } else {
                // Other long-press actions should be triggered only if app doesn't handle it.
                mDeferredKeyActionExecutor.queueKeyAction(
                        KeyEvent.KEYCODE_STEM_PRIMARY,
                        downTime,
                        () -> stemPrimaryLongPress(downTime));
                        eventTime,
                        () -> stemPrimaryLongPress(eventTime));
            }
        }

        private void onMultiPress(long downTime, int count) {
        @Override
        void onMultiPress(long downTime, int count, int unusedDisplayId) {
            // Triple-press stem to toggle accessibility gesture should always be triggered
            // regardless of if app handles it.
            if (count == 3
@@ -2701,7 +2655,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
        }

        @Override
        void onKeyUp(int count, KeyEvent event) {
        void onKeyUp(long eventTime, int count, int displayId, int deviceId, int metaState) {
            if (count == 1) {
                // Save info about the most recent task on the first press of the stem key. This
                // may be used later to switch to the most recent app using double press gesture.
@@ -2718,7 +2672,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
                    // Key-up gesture should be triggered only if app doesn't handle it.
                    mDeferredKeyActionExecutor.queueKeyAction(
                            KeyEvent.KEYCODE_STEM_PRIMARY,
                            event.getDownTime(),
                            eventTime,
                            () -> {
                                Slog.d(TAG, "StemPrimaryKeyRule: executing deferred onKeyUp");
                                // Save the info of the focused task on screen. This may be used
@@ -2766,13 +2720,18 @@ public class PhoneWindowManager implements WindowManagerPolicy {
        }

        @Override
        void onKeyUp(int pressCount, KeyEvent event) {
        void onPress(long downTime, int displayId) {

        }

        @Override
        void onKeyUp(long eventTime, int pressCount, int displayId, int deviceId, int metaState) {
            if (pressCount != 1) {
                return;
            }
            // Single press on tail button triggers the open notes gesture.
            handleKeyGestureInKeyGestureController(KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES,
                    event.getDeviceId(), KEYCODE_STYLUS_BUTTON_TAIL, event.getMetaState());
                    deviceId, KEYCODE_STYLUS_BUTTON_TAIL, metaState);
        }
    }

+101 −164

File changed.

Preview size limit exceeded, changes collapsed.

+0 −585

File deleted.

Preview size limit exceeded, changes collapsed.

+19 −285
Original line number Diff line number Diff line
@@ -18,19 +18,12 @@ package com.android.server.policy;

import static android.view.KeyEvent.ACTION_DOWN;
import static android.view.KeyEvent.ACTION_UP;
import static android.view.KeyEvent.KEYCODE_A;
import static android.view.KeyEvent.KEYCODE_BACK;
import static android.view.KeyEvent.KEYCODE_POWER;

import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;

import static com.android.hardware.input.Flags.FLAG_ABORT_SLOW_MULTI_PRESS;
import static com.android.server.policy.SingleKeyGestureEvent.ACTION_CANCEL;
import static com.android.server.policy.SingleKeyGestureEvent.ACTION_COMPLETE;
import static com.android.server.policy.SingleKeyGestureEvent.ACTION_START;
import static com.android.server.policy.SingleKeyGestureEvent.SINGLE_KEY_GESTURE_TYPE_LONG_PRESS;
import static com.android.server.policy.SingleKeyGestureEvent.SINGLE_KEY_GESTURE_TYPE_PRESS;
import static com.android.server.policy.SingleKeyGestureEvent.SINGLE_KEY_GESTURE_TYPE_VERY_LONG_PRESS;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -50,9 +43,6 @@ import android.platform.test.annotations.Presubmit;
import android.platform.test.flag.junit.SetFlagsRule;
import android.view.KeyEvent;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -82,7 +72,7 @@ public class SingleKeyGestureTests {
    private CountDownLatch mVeryLongPressed = new CountDownLatch(1);
    private CountDownLatch mMultiPressed = new CountDownLatch(1);
    private BlockingQueue<KeyUpData> mKeyUpQueue = new LinkedBlockingQueue<>();
    private RandomKeyRule mRandomGestureRule = new RandomKeyRule(KEYCODE_A);

    private final Instrumentation mInstrumentation = getInstrumentation();
    private final Context mContext = mInstrumentation.getTargetContext();
    private long mWaitTimeout;
@@ -111,7 +101,6 @@ public class SingleKeyGestureTests {
    }

    private void initSingleKeyGestureRules() {
        // Similar to current POWER key rules defined in PhoneWindowManager
        mDetector.addRule(
                new SingleKeyGestureDetector.SingleKeyRule(KEYCODE_POWER) {
                    @Override
@@ -130,36 +119,15 @@ public class SingleKeyGestureTests {
                    }

                    @Override
                    void onKeyGesture(@NonNull SingleKeyGestureEvent event) {
                        final int pressCount = event.getPressCount();
                        if (event.getAction() != ACTION_COMPLETE) {
                            return;
                        }
                        switch (event.getType()) {
                            case SINGLE_KEY_GESTURE_TYPE_PRESS:
                                if (event.getPressCount() > 1) {
                                    onMultiPress(pressCount);
                                } else {
                                    onPress();
                                }
                                break;
                            case SINGLE_KEY_GESTURE_TYPE_LONG_PRESS:
                                onLongPress();
                                break;
                            case SINGLE_KEY_GESTURE_TYPE_VERY_LONG_PRESS:
                                onVeryLongPress();
                                break;
                        }
                    }

                    private void onPress() {
                    public void onPress(long downTime, int displayId) {
                        if (mDetector.beganFromNonInteractive() && !mAllowNonInteractiveForPress) {
                            return;
                        }
                        mShortPressed.countDown();
                    }

                    private void onLongPress() {
                    @Override
                    void onLongPress(long downTime) {
                        if (mDetector.beganFromNonInteractive()
                                && !mAllowNonInteractiveForLongPress) {
                            return;
@@ -167,11 +135,13 @@ public class SingleKeyGestureTests {
                        mLongPressed.countDown();
                    }

                    private void onVeryLongPress() {
                    @Override
                    void onVeryLongPress(long downTime) {
                        mVeryLongPressed.countDown();
                    }

                    private void onMultiPress(int count) {
                    @Override
                    void onMultiPress(long downTime, int count, int displayId) {
                        if (mDetector.beganFromNonInteractive() && !mAllowNonInteractiveForPress) {
                            return;
                        }
@@ -181,12 +151,12 @@ public class SingleKeyGestureTests {
                    }

                    @Override
                    void onKeyUp(int multiPressCount, KeyEvent event) {
                    void onKeyUp(long eventTime, int multiPressCount, int displayId, int deviceId,
                            int metaState) {
                        mKeyUpQueue.add(new KeyUpData(KEYCODE_POWER, multiPressCount));
                    }
                });

        // Similar to current POWER key rules defined in PhoneWindowManager
        mDetector.addRule(
                new SingleKeyGestureDetector.SingleKeyRule(KEYCODE_BACK) {
                    @Override
@@ -200,35 +170,15 @@ public class SingleKeyGestureTests {
                    }

                    @Override
                    void onKeyGesture(@NonNull SingleKeyGestureEvent event) {
                        final long eventTime = event.getEventTime();
                        final int displayId = event.getDisplayId();
                        final int pressCount = event.getPressCount();
                        if (event.getAction() != ACTION_COMPLETE) {
                            return;
                        }
                        switch (event.getType()) {
                            case SINGLE_KEY_GESTURE_TYPE_PRESS:
                                if (event.getPressCount() > 1) {
                                    onMultiPress(pressCount);
                                } else {
                                    onPress();
                                }
                                break;
                            case SINGLE_KEY_GESTURE_TYPE_LONG_PRESS:
                                onLongPress();
                                break;
                        }
                    }

                    private void onPress() {
                    public void onPress(long downTime, int displayId) {
                        if (mDetector.beganFromNonInteractive() && !mAllowNonInteractiveForPress) {
                            return;
                        }
                        mShortPressed.countDown();
                    }

                    private void onMultiPress(int count) {
                    @Override
                    void onMultiPress(long downTime, int count, int displayId) {
                        if (mDetector.beganFromNonInteractive() && !mAllowNonInteractiveForPress) {
                            return;
                        }
@@ -238,16 +188,16 @@ public class SingleKeyGestureTests {
                    }

                    @Override
                    void onKeyUp(int multiPressCount, KeyEvent event) {
                    void onKeyUp(long eventTime, int multiPressCount, int displayId, int deviceId,
                            int metaState) {
                        mKeyUpQueue.add(new KeyUpData(KEYCODE_BACK, multiPressCount));
                    }

                    private void onLongPress() {
                    @Override
                    void onLongPress(long downTime) {
                        mLongPressed.countDown();
                    }
                });

        mDetector.addRule(mRandomGestureRule);
    }

    private static class KeyUpData {
@@ -498,12 +448,9 @@ public class SingleKeyGestureTests {
        final SingleKeyGestureDetector.SingleKeyRule rule =
                new SingleKeyGestureDetector.SingleKeyRule(KEYCODE_POWER) {
                    @Override
                    void onKeyGesture(@NonNull SingleKeyGestureEvent event) {
                        if (event.getType() == SINGLE_KEY_GESTURE_TYPE_PRESS
                                && event.getPressCount() == 1) {
                    void onPress(long downTime, int displayId) {
                        mShortPressed.countDown();
                    }
                    }
                };

        mDetector.removeRule(rule);
@@ -526,217 +473,4 @@ public class SingleKeyGestureTests {
        assertEquals(mVeryLongPressed.getCount(), 1);
        assertEquals(mShortPressed.getCount(), 1);
    }

    @Test
    public void testRandomRuleLongPress() throws InterruptedException {
        // The current flow of events based on implementation is:
        // - long-press(START)
        // - very long press (START)
        // - long press(COMPLETE)
        // - very long press(CANCEL)
        pressKey(KEYCODE_A, mLongPressTime + 50);

        mRandomGestureRule.assertEventReceived(SINGLE_KEY_GESTURE_TYPE_LONG_PRESS, ACTION_START);
        mRandomGestureRule.assertEventReceived(SINGLE_KEY_GESTURE_TYPE_VERY_LONG_PRESS,
                ACTION_START);
        mRandomGestureRule.assertEventReceived(SINGLE_KEY_GESTURE_TYPE_LONG_PRESS, ACTION_COMPLETE);
        mRandomGestureRule.assertEventReceived(SINGLE_KEY_GESTURE_TYPE_VERY_LONG_PRESS,
                ACTION_CANCEL);
    }

    @Test
    public void testRandomRuleVeryLongPress() throws InterruptedException {
        // The current flow of events based on implementation is:
        // - long-press(START)
        // - very long press (START)
        // - long press(COMPLETE)
        // - very long press(COMPLETE)
        pressKey(KEYCODE_A, mVeryLongPressTime + 50);

        mRandomGestureRule.assertEventReceived(SINGLE_KEY_GESTURE_TYPE_LONG_PRESS, ACTION_START);
        mRandomGestureRule.assertEventReceived(SINGLE_KEY_GESTURE_TYPE_VERY_LONG_PRESS,
                ACTION_START);
        mRandomGestureRule.assertEventReceived(SINGLE_KEY_GESTURE_TYPE_LONG_PRESS, ACTION_COMPLETE);
        mRandomGestureRule.assertEventReceived(SINGLE_KEY_GESTURE_TYPE_VERY_LONG_PRESS,
                ACTION_COMPLETE);
    }

    @Test
    public void testRandomRuleShortPress() throws InterruptedException {
        // The current flow of events based on implementation is:
        // - long-press(START)
        // - very long press (START)
        // - long press(CANCEL)
        // - very long press(CANCEL)
        // - 1-press(START)
        // - 1-press(COMPLETE)
        pressKey(KEYCODE_A, 0);

        // Long press and very long press gestures are started on key down
        mRandomGestureRule.assertEventReceived(SINGLE_KEY_GESTURE_TYPE_LONG_PRESS, ACTION_START);
        mRandomGestureRule.assertEventReceived(SINGLE_KEY_GESTURE_TYPE_VERY_LONG_PRESS,
                ACTION_START);

        // Long press and very long press gestures are cancelled on key up (duration < thresholds)
        mRandomGestureRule.assertEventReceived(SINGLE_KEY_GESTURE_TYPE_LONG_PRESS, ACTION_CANCEL);
        mRandomGestureRule.assertEventReceived(SINGLE_KEY_GESTURE_TYPE_VERY_LONG_PRESS,
                ACTION_CANCEL);

        // On key up start single press
        mRandomGestureRule.assertEventReceived(SINGLE_KEY_GESTURE_TYPE_PRESS,
                ACTION_START, /* pressCount = */1);
        // After waiting for multi-press timeout single press gesture is completed
        mRandomGestureRule.assertEventReceived(SINGLE_KEY_GESTURE_TYPE_PRESS,
                ACTION_COMPLETE, /* pressCount = */1);
    }

    @Test
    public void testRandomRuleDoublePress() throws InterruptedException {
        // The current flow of events based on implementation is:
        // - long-press(START)
        // - very long press (START)
        // - long press(CANCEL)
        // - very long press(CANCEL)
        // - 1-press(START)
        // - 1-press(CANCEL)
        // - 2-press(START)
        // - 2-press(COMPLETE)
        pressKey(KEYCODE_A, 0);
        pressKey(KEYCODE_A, 0);

        // Long press and very long press gestures are started on key down and cancelled on key up
        mRandomGestureRule.assertEventReceived(SINGLE_KEY_GESTURE_TYPE_LONG_PRESS, ACTION_START);
        mRandomGestureRule.assertEventReceived(SINGLE_KEY_GESTURE_TYPE_VERY_LONG_PRESS,
                ACTION_START);
        mRandomGestureRule.assertEventReceived(SINGLE_KEY_GESTURE_TYPE_LONG_PRESS, ACTION_CANCEL);
        mRandomGestureRule.assertEventReceived(SINGLE_KEY_GESTURE_TYPE_VERY_LONG_PRESS,
                ACTION_CANCEL);

        // First press will start single press gesture
        mRandomGestureRule.assertEventReceived(SINGLE_KEY_GESTURE_TYPE_PRESS,
                ACTION_START, /* pressCount = */1);

        // Single press is cancelled on second key down
        mRandomGestureRule.assertEventReceived(SINGLE_KEY_GESTURE_TYPE_PRESS,
                ACTION_CANCEL, /* pressCount = */1);

        // On second key up start double press
        mRandomGestureRule.assertEventReceived(SINGLE_KEY_GESTURE_TYPE_PRESS,
                ACTION_START, /* pressCount = */2);
        // After waiting for multi-press timeout double press gesture is completed
        mRandomGestureRule.assertEventReceived(SINGLE_KEY_GESTURE_TYPE_PRESS,
                ACTION_COMPLETE, /* pressCount = */2);
    }

    @Test
    public void testRandomRuleTriplePress() throws InterruptedException {
        // The current flow of events based on implementation is:
        // - long-press(START)
        // - very long press (START)
        // - long press(CANCEL)
        // - very long press(CANCEL)
        // - 1-press(START)
        // - 1-press(CANCEL)
        // - 2-press(START)
        // - 2-press(CANCEL)
        // - 3-press(COMPLETE)
        pressKey(KEYCODE_A, 0);
        pressKey(KEYCODE_A, 0);
        pressKey(KEYCODE_A, 0);

        // Long press and very long press gestures are started on key down and cancelled on key up
        mRandomGestureRule.assertEventReceived(SINGLE_KEY_GESTURE_TYPE_LONG_PRESS, ACTION_START);
        mRandomGestureRule.assertEventReceived(SINGLE_KEY_GESTURE_TYPE_VERY_LONG_PRESS,
                ACTION_START);
        mRandomGestureRule.assertEventReceived(SINGLE_KEY_GESTURE_TYPE_LONG_PRESS, ACTION_CANCEL);
        mRandomGestureRule.assertEventReceived(SINGLE_KEY_GESTURE_TYPE_VERY_LONG_PRESS,
                ACTION_CANCEL);

        // First press will start single press gesture
        mRandomGestureRule.assertEventReceived(SINGLE_KEY_GESTURE_TYPE_PRESS,
                ACTION_START, /* pressCount = */1);

        // Single press is cancelled on second key down
        mRandomGestureRule.assertEventReceived(SINGLE_KEY_GESTURE_TYPE_PRESS,
                ACTION_CANCEL, /* pressCount = */1);

        // On second key up start double press
        mRandomGestureRule.assertEventReceived(SINGLE_KEY_GESTURE_TYPE_PRESS,
                ACTION_START, /* pressCount = */2);
        // Double press is cancelled on third key down
        mRandomGestureRule.assertEventReceived(SINGLE_KEY_GESTURE_TYPE_PRESS,
                ACTION_CANCEL, /* pressCount = */2);

        // Triple press is completed on third key down (since max press count is 3 no need to wait)
        mRandomGestureRule.assertEventReceived(SINGLE_KEY_GESTURE_TYPE_PRESS,
                ACTION_COMPLETE, /* pressCount = */3);
    }

    private class RandomKeyRule extends SingleKeyGestureDetector.SingleKeyRule {

        private final BlockingQueue<SingleKeyGestureEvent> mEvents = new LinkedBlockingQueue<>();

        private final int mKeyCode;

        RandomKeyRule(int keyCode) {
            super(keyCode);
            mKeyCode = keyCode;
        }

        @Override
        boolean supportLongPress() {
            return true;
        }

        @Override
        boolean supportVeryLongPress() {
            return true;
        }

        @Override
        int getMaxMultiPressCount() {
            return mMaxMultiPressCount;
        }

        @Override
        long getLongPressTimeoutMs() {
            return mLongPressTime;
        }

        @Override
        long getVeryLongPressTimeoutMs() {
            return mVeryLongPressTime;
        }

        @Override
        void onKeyGesture(@NonNull SingleKeyGestureEvent event) {
            if (event.getKeyCode() != mKeyCode) {
                throw new IllegalArgumentException(
                        "Rule generated a gesture for " + KeyEvent.keyCodeToString(
                                event.getKeyCode()) + " but the rule was made for "
                                + KeyEvent.keyCodeToString(mKeyCode));
            }
            mEvents.add(event);
        }

        @Nullable
        SingleKeyGestureEvent getEvent() throws InterruptedException {
            return mEvents.poll(500, TimeUnit.MILLISECONDS);
        }

        void assertEventReceived(int type, int action) throws InterruptedException {
            SingleKeyGestureEvent event = getEvent();
            assertNotNull(event);
            assertEquals("Type mismatch", type, event.getType());
            assertEquals("Action mismatch", action, event.getAction());
        }

        void assertEventReceived(int type, int action, int pressCount) throws InterruptedException {
            SingleKeyGestureEvent event = getEvent();
            assertNotNull(event);
            assertEquals("Type mismatch", type, event.getType());
            assertEquals("Action mismatch", action, event.getAction());
            assertEquals("Count mismatch", pressCount, event.getPressCount());
        }
    }
}