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

Commit 37fa9143 authored by Vaibhav Devmurari's avatar Vaibhav Devmurari
Browse files

Handle custom key gestures added by the user

DD: go/customizable_shortcuts
PRD: go/custom-kb-shortcuts

Bug: 365064144
Test: atest InputTests:CustomInputGestureManagerTests
Flag: com.android.hardware.input.enable_customizable_input_gestures
Change-Id: I3d3f18558174fb9cc5ec03b3808a357ff0aa2b5b
parent bda78423
Loading
Loading
Loading
Loading
+24 −0
Original line number Diff line number Diff line
@@ -17,10 +17,13 @@
package com.android.server.input;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.hardware.input.InputGestureData;
import android.hardware.input.InputManager;
import android.util.IndentingPrintWriter;
import android.util.SparseArray;
import android.view.KeyEvent;

import com.android.internal.annotations.GuardedBy;

@@ -40,6 +43,10 @@ import java.util.Objects;
final class InputGestureManager {
    private static final String TAG = "InputGestureManager";

    private static final int KEY_GESTURE_META_MASK =
            KeyEvent.META_CTRL_ON | KeyEvent.META_ALT_ON | KeyEvent.META_SHIFT_ON
                    | KeyEvent.META_META_ON;

    @GuardedBy("mCustomInputGestures")
    private final SparseArray<Map<InputGestureData.Trigger, InputGestureData>>
            mCustomInputGestures = new SparseArray<>();
@@ -96,6 +103,23 @@ final class InputGestureManager {
        }
    }

    @Nullable
    public InputGestureData getCustomGestureForKeyEvent(@UserIdInt int userId, KeyEvent event) {
        final int keyCode = event.getKeyCode();
        if (keyCode == KeyEvent.KEYCODE_UNKNOWN) {
            return null;
        }
        synchronized (mCustomInputGestures) {
            Map<InputGestureData.Trigger, InputGestureData> customGestures =
                    mCustomInputGestures.get(userId);
            if (customGestures == null) {
                return null;
            }
            int modifierState = event.getMetaState() & KEY_GESTURE_META_MASK;
            return customGestures.get(InputGestureData.createKeyTrigger(keyCode, modifierState));
        }
    }

    public void dump(IndentingPrintWriter ipw) {
        ipw.println("InputGestureManager:");
        ipw.increaseIndent();
+1 −0
Original line number Diff line number Diff line
@@ -3022,6 +3022,7 @@ public class InputManagerService extends IInputManager.Stub

    private void handleCurrentUserChanged(@UserIdInt int userId) {
        mCurrentUserId = userId;
        mKeyGestureController.setCurrentUserId(userId);
    }

    /**
+121 −78

File changed.

Preview size limit exceeded, changes collapsed.

+65 −1
Original line number Diff line number Diff line
@@ -22,8 +22,10 @@ import android.content.pm.PackageManager
import android.content.res.Resources
import android.hardware.input.IInputManager
import android.hardware.input.AidlKeyGestureEvent
import android.hardware.input.AppLaunchData
import android.hardware.input.IKeyGestureEventListener
import android.hardware.input.IKeyGestureHandler
import android.hardware.input.InputGestureData
import android.hardware.input.InputManager
import android.hardware.input.InputManagerGlobal
import android.hardware.input.KeyGestureEvent
@@ -232,7 +234,7 @@ class KeyGestureControllerTests {
        keyGestureController.handleKeyGesture(/* deviceId = */ 0, intArrayOf(KeyEvent.KEYCODE_HOME),
            /* modifierState = */ 0, KeyGestureEvent.KEY_GESTURE_TYPE_HOME,
            KeyGestureEvent.ACTION_GESTURE_COMPLETE, /* displayId */ 0,
            /* focusedToken = */ null, /* flags = */ 0
            /* focusedToken = */ null, /* flags = */ 0, /* appLaunchData = */null
        )

        assertEquals(
@@ -259,6 +261,7 @@ class KeyGestureControllerTests {
        val expectedKeys: IntArray,
        val expectedModifierState: Int,
        val expectedActions: IntArray,
        val expectedAppLaunchData: AppLaunchData? = null,
    ) {
        override fun toString(): String = name
    }
@@ -1055,6 +1058,62 @@ class KeyGestureControllerTests {
        testKeyGestureInternal(keyGestureController, test)
    }

    @Keep
    private fun customInputGesturesTestArguments(): Array<TestData> {
        return arrayOf(
            TestData(
                "META + ALT + Q -> Go Home",
                intArrayOf(
                    KeyEvent.KEYCODE_META_LEFT,
                    KeyEvent.KEYCODE_ALT_LEFT,
                    KeyEvent.KEYCODE_Q
                ),
                KeyGestureEvent.KEY_GESTURE_TYPE_HOME,
                intArrayOf(KeyEvent.KEYCODE_Q),
                KeyEvent.META_META_ON or KeyEvent.META_ALT_ON,
                intArrayOf(
                    KeyGestureEvent.ACTION_GESTURE_COMPLETE
                )
            ),
            TestData(
                "META + ALT + Q -> Launch app",
                intArrayOf(
                    KeyEvent.KEYCODE_CTRL_LEFT,
                    KeyEvent.KEYCODE_SHIFT_LEFT,
                    KeyEvent.KEYCODE_Q
                ),
                KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
                intArrayOf(KeyEvent.KEYCODE_Q),
                KeyEvent.META_CTRL_ON or KeyEvent.META_SHIFT_ON,
                intArrayOf(
                    KeyGestureEvent.ACTION_GESTURE_COMPLETE
                ),
                AppLaunchData.createLaunchDataForComponent("com.test", "com.test.BookmarkTest")
            ),
        )
    }

    @Test
    @Parameters(method = "customInputGesturesTestArguments")
    fun testCustomKeyGestures(test: TestData) {
        val keyGestureController = KeyGestureController(context, testLooper.looper)
        val builder = InputGestureData.Builder()
            .setKeyGestureType(test.expectedKeyGestureType)
            .setTrigger(
                InputGestureData.createKeyTrigger(
                    test.expectedKeys[0],
                    test.expectedModifierState
                )
            );
        if (test.expectedAppLaunchData != null) {
            builder.setAppLaunchData(test.expectedAppLaunchData)
        }
        val inputGestureData = builder.build();

        keyGestureController.addCustomInputGesture(0, inputGestureData.aidlData)
        testKeyGestureInternal(keyGestureController, test)
    }

    private fun testKeyGestureInternal(keyGestureController: KeyGestureController, test: TestData) {
        var handleEvents = mutableListOf<KeyGestureEvent>()
        val handler = KeyGestureHandler { event, _ ->
@@ -1093,6 +1152,11 @@ class KeyGestureControllerTests {
                test.expectedActions[i],
                event.action
            )
            assertEquals(
                "Test: $test doesn't produce correct app launch data",
                test.expectedAppLaunchData,
                event.appLaunchData
            )
        }

        keyGestureController.unregisterKeyGestureHandler(handler, 0)