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

Commit acb120ef authored by Vaibhav Devmurari's avatar Vaibhav Devmurari Committed by Android (Google) Code Review
Browse files

Merge "Add support for overriding keyboard layout for a keyboard" into main

parents 588b6d62 5604d48b
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -103,6 +103,12 @@ interface IInputManager {
            in InputDeviceIdentifier identifier, int userId, in InputMethodInfo imeInfo,
            in InputMethodSubtype imeSubtype);

    @EnforcePermission("SET_KEYBOARD_LAYOUT")
    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
            + "android.Manifest.permission.SET_KEYBOARD_LAYOUT)")
    void setKeyboardLayoutOverrideForInputDevice(in InputDeviceIdentifier identifier,
            String keyboardLayoutDescriptor);

    @EnforcePermission("SET_KEYBOARD_LAYOUT")
    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
            + "android.Manifest.permission.SET_KEYBOARD_LAYOUT)")
+39 −0
Original line number Diff line number Diff line
@@ -57,6 +57,8 @@ import android.view.InputMonitor;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.PointerIcon;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodSubtype;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -1255,6 +1257,43 @@ public final class InputManagerGlobal {
        }
    }

    /**
     * Sets the keyboard layout override for the specified input device. This will set the
     * keyboard layout as the default for the input device irrespective of the underlying IME
     * configuration.
     *
     * <p>
     * Prefer using {@link InputManager#setKeyboardLayoutForInputDevice(InputDeviceIdentifier, int,
     * InputMethodInfo, InputMethodSubtype, String)} for normal use cases.
     * </p><p>
     * This method is to be used only for special cases where we knowingly want to set a
     * particular keyboard layout for a keyboard, ignoring the IME configuration. e.g. Setting a
     * default layout for an Android Emulator where we know the preferred H/W keyboard layout.
     * </p><p>
     * NOTE: This may affect the typing experience if the layout isn't compatible with the IME
     * configuration.
     * </p><p>
     * NOTE: User can still change the keyboard layout configuration from the settings page.
     * </p>
     *
     * @param identifier The identifier for the input device.
     * @param keyboardLayoutDescriptor The keyboard layout descriptor to use.
     *
     * @hide
     */
    @RequiresPermission(Manifest.permission.SET_KEYBOARD_LAYOUT)
    public void setKeyboardLayoutOverrideForInputDevice(@NonNull InputDeviceIdentifier identifier,
            @NonNull String keyboardLayoutDescriptor) {
        Objects.requireNonNull(identifier, "identifier should not be null");
        Objects.requireNonNull(keyboardLayoutDescriptor,
                "keyboardLayoutDescriptor should not be null");
        try {
            mIm.setKeyboardLayoutOverrideForInputDevice(identifier, keyboardLayoutDescriptor);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * TODO(b/330517633): Cleanup the unsupported API
     */
+9 −0
Original line number Diff line number Diff line
@@ -1259,6 +1259,15 @@ public class InputManagerService extends IInputManager.Stub
                imeInfo, imeSubtype);
    }

    @EnforcePermission(Manifest.permission.SET_KEYBOARD_LAYOUT)
    @Override // Binder call
    public void setKeyboardLayoutOverrideForInputDevice(InputDeviceIdentifier identifier,
            String keyboardLayoutDescriptor) {
        super.setKeyboardLayoutOverrideForInputDevice_enforcePermission();
        mKeyboardLayoutManager.setKeyboardLayoutOverrideForInputDevice(identifier,
                keyboardLayoutDescriptor);
    }

    @EnforcePermission(Manifest.permission.SET_KEYBOARD_LAYOUT)
    @Override // Binder call
    public void setKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
+30 −5
Original line number Diff line number Diff line
@@ -113,6 +113,7 @@ class KeyboardLayoutManager implements InputManager.InputDeviceListener {
    private static final int MSG_UPDATE_EXISTING_DEVICES = 1;
    private static final int MSG_RELOAD_KEYBOARD_LAYOUTS = 2;
    private static final int MSG_UPDATE_KEYBOARD_LAYOUTS = 3;
    private static final String GLOBAL_OVERRIDE_KEY = "GLOBAL_OVERRIDE_KEY";

    private final Context mContext;
    private final NativeInputManagerService mNative;
@@ -507,27 +508,45 @@ class KeyboardLayoutManager implements InputManager.InputDeviceListener {
        return result;
    }

    @AnyThread
    public void setKeyboardLayoutOverrideForInputDevice(InputDeviceIdentifier identifier,
            String keyboardLayoutDescriptor) {
        InputDevice inputDevice = getInputDevice(identifier);
        if (inputDevice == null || inputDevice.isVirtual() || !inputDevice.isFullKeyboard()) {
            return;
        }
        KeyboardIdentifier keyboardIdentifier = new KeyboardIdentifier(inputDevice);
        setKeyboardLayoutForInputDeviceInternal(keyboardIdentifier, GLOBAL_OVERRIDE_KEY,
                keyboardLayoutDescriptor);
    }

    @AnyThread
    public void setKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
            @UserIdInt int userId, @NonNull InputMethodInfo imeInfo,
            @Nullable InputMethodSubtype imeSubtype,
            String keyboardLayoutDescriptor) {
        Objects.requireNonNull(keyboardLayoutDescriptor,
                "keyboardLayoutDescriptor must not be null");
        InputDevice inputDevice = getInputDevice(identifier);
        if (inputDevice == null || inputDevice.isVirtual() || !inputDevice.isFullKeyboard()) {
            return;
        }
        KeyboardIdentifier keyboardIdentifier = new KeyboardIdentifier(inputDevice);
        String layoutKey = new LayoutKey(keyboardIdentifier,
        final String datastoreKey = new LayoutKey(keyboardIdentifier,
                new ImeInfo(userId, imeInfo, imeSubtype)).toString();
        setKeyboardLayoutForInputDeviceInternal(keyboardIdentifier, datastoreKey,
                keyboardLayoutDescriptor);
    }

    private void setKeyboardLayoutForInputDeviceInternal(KeyboardIdentifier identifier,
            String datastoreKey, String keyboardLayoutDescriptor) {
        Objects.requireNonNull(keyboardLayoutDescriptor,
                "keyboardLayoutDescriptor must not be null");
        synchronized (mDataStore) {
            try {
                if (mDataStore.setKeyboardLayout(keyboardIdentifier.toString(), layoutKey,
                if (mDataStore.setKeyboardLayout(identifier.toString(), datastoreKey,
                        keyboardLayoutDescriptor)) {
                    if (DEBUG) {
                        Slog.d(TAG, "setKeyboardLayoutForInputDevice() " + identifier
                                + " key: " + layoutKey
                                + " key: " + datastoreKey
                                + " keyboardLayoutDescriptor: " + keyboardLayoutDescriptor);
                    }
                    mHandler.sendEmptyMessage(MSG_RELOAD_KEYBOARD_LAYOUTS);
@@ -635,6 +654,12 @@ class KeyboardLayoutManager implements InputManager.InputDeviceListener {
            if (layout != null) {
                return new KeyboardLayoutSelectionResult(layout, LAYOUT_SELECTION_CRITERIA_USER);
            }

            layout = mDataStore.getKeyboardLayout(keyboardIdentifier.toString(),
                    GLOBAL_OVERRIDE_KEY);
            if (layout != null) {
                return new KeyboardLayoutSelectionResult(layout, LAYOUT_SELECTION_CRITERIA_DEVICE);
            }
        }

        synchronized (mKeyboardLayoutCache) {
+37 −0
Original line number Diff line number Diff line
@@ -28,6 +28,8 @@ import android.hardware.input.InputManager
import android.hardware.input.InputManagerGlobal
import android.hardware.input.KeyboardLayout
import android.hardware.input.KeyboardLayoutSelectionResult
import android.hardware.input.KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_DEVICE
import android.hardware.input.KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_USER
import android.icu.util.ULocale
import android.os.Bundle
import android.os.test.TestLooper
@@ -280,6 +282,41 @@ class KeyboardLayoutManagerTests {
        )
    }

    @Test
    fun testGetSetKeyboardLayoutOverrideForInputDevice() {
        val imeSubtype = createImeSubtype()

        keyboardLayoutManager.setKeyboardLayoutOverrideForInputDevice(
            keyboardDevice.identifier,
            ENGLISH_UK_LAYOUT_DESCRIPTOR
        )
        var result =
            keyboardLayoutManager.getKeyboardLayoutForInputDevice(
                keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype
            )
        assertEquals(LAYOUT_SELECTION_CRITERIA_DEVICE, result.selectionCriteria)
        assertEquals(
            "getKeyboardLayoutForInputDevice API should return the set layout",
            ENGLISH_UK_LAYOUT_DESCRIPTOR,
            result.layoutDescriptor
        )

        // This should replace the overriding layout set above
        keyboardLayoutManager.setKeyboardLayoutForInputDevice(
            keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype,
            ENGLISH_US_LAYOUT_DESCRIPTOR
        )
        result = keyboardLayoutManager.getKeyboardLayoutForInputDevice(
            keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype
        )
        assertEquals(LAYOUT_SELECTION_CRITERIA_USER, result.selectionCriteria)
        assertEquals(
            "getKeyboardLayoutForInputDevice API should return the user set layout",
            ENGLISH_US_LAYOUT_DESCRIPTOR,
            result.layoutDescriptor
        )
    }

    @Test
    fun testGetKeyboardLayoutListForInputDevice() {
        // Check Layouts for "hi-Latn". It should return all 'Latn' keyboard layouts