Loading core/java/android/hardware/input/IInputManager.aidl +6 −0 Original line number Diff line number Diff line Loading @@ -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)") Loading core/java/android/hardware/input/InputManagerGlobal.java +39 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 */ Loading services/core/java/com/android/server/input/InputManagerService.java +9 −0 Original line number Diff line number Diff line Loading @@ -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, Loading services/core/java/com/android/server/input/KeyboardLayoutManager.java +30 −5 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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) { Loading tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt +37 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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 Loading Loading
core/java/android/hardware/input/IInputManager.aidl +6 −0 Original line number Diff line number Diff line Loading @@ -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)") Loading
core/java/android/hardware/input/InputManagerGlobal.java +39 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 */ Loading
services/core/java/com/android/server/input/InputManagerService.java +9 −0 Original line number Diff line number Diff line Loading @@ -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, Loading
services/core/java/com/android/server/input/KeyboardLayoutManager.java +30 −5 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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) { Loading
tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt +37 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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 Loading