Handle layout selection before onInputDeviceAdded()

With native callback passing language tag and layout type in
getKeyboardLayoutOverlay() we can setup keyboard layout before
input device is fully added.

Test: atest KeyboardLayoutManagerTests
Test: atest VirtualKeyboardLayoutTest
Bug: 271905768
Change-Id: I9b94d833f55ee8021deeca871553397861c11c51
This commit is contained in:
Vaibhav Devmurari
2023-07-11 15:01:34 +00:00
parent 034711b6a6
commit dfb9d38c32
5 changed files with 188 additions and 150 deletions

View File

@@ -3526,6 +3526,8 @@ package android.view {
method @RequiresPermission("android.permission.DISABLE_INPUT_DEVICE") public void disable();
method @RequiresPermission("android.permission.DISABLE_INPUT_DEVICE") public void enable();
method @NonNull public android.hardware.input.InputDeviceIdentifier getIdentifier();
method @Nullable public String getKeyboardLanguageTag();
method @Nullable public String getKeyboardLayoutType();
}
public abstract class InputEvent implements android.os.Parcelable {

View File

@@ -958,6 +958,7 @@ public final class InputDevice implements Parcelable {
* @hide
*/
@Nullable
@TestApi
public String getKeyboardLanguageTag() {
return mKeyboardLanguageTag;
}
@@ -968,6 +969,7 @@ public final class InputDevice implements Parcelable {
* @hide
*/
@Nullable
@TestApi
public String getKeyboardLayoutType() {
return mKeyboardLayoutType;
}

View File

@@ -2725,11 +2725,12 @@ public class InputManagerService extends IInputManager.Stub
// Native callback.
@SuppressWarnings("unused")
private String[] getKeyboardLayoutOverlay(InputDeviceIdentifier identifier) {
private String[] getKeyboardLayoutOverlay(InputDeviceIdentifier identifier, String languageTag,
String layoutType) {
if (!mSystemReady) {
return null;
}
return mKeyboardLayoutManager.getKeyboardLayoutOverlay(identifier);
return mKeyboardLayoutManager.getKeyboardLayoutOverlay(identifier, languageTag, layoutType);
}
@EnforcePermission(Manifest.permission.REMAP_MODIFIER_KEYS)

View File

@@ -25,6 +25,7 @@ import android.annotation.AnyThread;
import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.UserIdInt;
import android.app.Notification;
import android.app.NotificationManager;
@@ -109,7 +110,6 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
private static final int MSG_SWITCH_KEYBOARD_LAYOUT = 2;
private static final int MSG_RELOAD_KEYBOARD_LAYOUTS = 3;
private static final int MSG_UPDATE_KEYBOARD_LAYOUTS = 4;
private static final int MSG_CURRENT_IME_INFO_CHANGED = 5;
private final Context mContext;
private final NativeInputManagerService mNative;
@@ -186,6 +186,7 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
if (inputDevice == null || inputDevice.isVirtual() || !inputDevice.isFullKeyboard()) {
return;
}
final KeyboardIdentifier keyboardIdentifier = new KeyboardIdentifier(inputDevice);
KeyboardConfiguration config = mConfiguredKeyboards.get(deviceId);
if (config == null) {
config = new KeyboardConfiguration();
@@ -202,8 +203,6 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
setCurrentKeyboardLayoutForInputDevice(inputDevice.getIdentifier(), layout);
}
}
config.setCurrentLayout(
new KeyboardLayoutInfo(layout, LAYOUT_SELECTION_CRITERIA_USER));
if (layout == null) {
// In old settings show notification always until user manually selects a
// layout in the settings.
@@ -211,16 +210,14 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
}
}
} else {
final InputDeviceIdentifier identifier = inputDevice.getIdentifier();
final String key = getLayoutDescriptor(identifier);
Set<String> selectedLayouts = new HashSet<>();
List<ImeInfo> imeInfoList = getImeInfoListForLayoutMapping();
List<KeyboardLayoutInfo> layoutInfoList = new ArrayList<>();
boolean hasMissingLayout = false;
for (ImeInfo imeInfo : imeInfoList) {
// Check if the layout has been previously configured
KeyboardLayoutInfo layoutInfo = getKeyboardLayoutForInputDeviceInternal(identifier,
imeInfo);
KeyboardLayoutInfo layoutInfo = getKeyboardLayoutForInputDeviceInternal(
keyboardIdentifier, imeInfo);
boolean noLayoutFound = layoutInfo == null || layoutInfo.mDescriptor == null;
if (!noLayoutFound) {
selectedLayouts.add(layoutInfo.mDescriptor);
@@ -231,8 +228,8 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
if (DEBUG) {
Slog.d(TAG,
"Layouts selected for input device: " + identifier + " -> selectedLayouts: "
+ selectedLayouts);
"Layouts selected for input device: " + keyboardIdentifier
+ " -> selectedLayouts: " + selectedLayouts);
}
// If even one layout not configured properly, we need to ask user to configure
@@ -243,18 +240,9 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
config.setConfiguredLayouts(selectedLayouts);
// Update current layout: If there is a change then need to reload.
synchronized (mImeInfoLock) {
KeyboardLayoutInfo layoutInfo = getKeyboardLayoutForInputDeviceInternal(
inputDevice.getIdentifier(), mCurrentImeInfo);
if (!Objects.equals(layoutInfo, config.getCurrentLayout())) {
config.setCurrentLayout(layoutInfo);
mHandler.sendEmptyMessage(MSG_RELOAD_KEYBOARD_LAYOUTS);
}
}
synchronized (mDataStore) {
try {
final String key = keyboardIdentifier.toString();
boolean isFirstConfiguration = !mDataStore.hasInputDeviceEntry(key);
if (mDataStore.setSelectedKeyboardLayouts(key, selectedLayouts)) {
// Need to show the notification only if layout selection changed
@@ -563,35 +551,6 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
return LocaleList.forLanguageTags(languageTags.replace('|', ','));
}
private String getLayoutDescriptor(@NonNull InputDeviceIdentifier identifier) {
Objects.requireNonNull(identifier, "identifier must not be null");
Objects.requireNonNull(identifier.getDescriptor(), "descriptor must not be null");
if (identifier.getVendorId() == 0 && identifier.getProductId() == 0) {
return identifier.getDescriptor();
}
// If vendor id and product id is available, use it as keys. This allows us to have the
// same setup for all keyboards with same product and vendor id. i.e. User can swap 2
// identical keyboards and still get the same setup.
StringBuilder key = new StringBuilder();
key.append("vendor:").append(identifier.getVendorId()).append(",product:").append(
identifier.getProductId());
if (useNewSettingsUi()) {
InputDevice inputDevice = getInputDevice(identifier);
Objects.requireNonNull(inputDevice, "Input device must not be null");
// Some keyboards can have same product ID and vendor ID but different Keyboard info
// like language tag and layout type.
if (!TextUtils.isEmpty(inputDevice.getKeyboardLanguageTag())) {
key.append(",languageTag:").append(inputDevice.getKeyboardLanguageTag());
}
if (!TextUtils.isEmpty(inputDevice.getKeyboardLayoutType())) {
key.append(",layoutType:").append(inputDevice.getKeyboardLayoutType());
}
}
return key.toString();
}
@AnyThread
@Nullable
public String getCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier) {
@@ -599,7 +558,7 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
Slog.e(TAG, "getCurrentKeyboardLayoutForInputDevice API not supported");
return null;
}
String key = getLayoutDescriptor(identifier);
String key = new KeyboardIdentifier(identifier).toString();
synchronized (mDataStore) {
String layout;
// try loading it using the layout descriptor if we have it
@@ -626,7 +585,7 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
Objects.requireNonNull(keyboardLayoutDescriptor,
"keyboardLayoutDescriptor must not be null");
String key = getLayoutDescriptor(identifier);
String key = new KeyboardIdentifier(identifier).toString();
synchronized (mDataStore) {
try {
if (mDataStore.setCurrentKeyboardLayout(key, keyboardLayoutDescriptor)) {
@@ -649,7 +608,7 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
Slog.e(TAG, "getEnabledKeyboardLayoutsForInputDevice API not supported");
return new String[0];
}
String key = getLayoutDescriptor(identifier);
String key = new KeyboardIdentifier(identifier).toString();
synchronized (mDataStore) {
String[] layouts = mDataStore.getKeyboardLayouts(key);
if ((layouts == null || layouts.length == 0)
@@ -670,7 +629,7 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
Objects.requireNonNull(keyboardLayoutDescriptor,
"keyboardLayoutDescriptor must not be null");
String key = getLayoutDescriptor(identifier);
String key = new KeyboardIdentifier(identifier).toString();
synchronized (mDataStore) {
try {
String oldLayout = mDataStore.getCurrentKeyboardLayout(key);
@@ -698,7 +657,7 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
Objects.requireNonNull(keyboardLayoutDescriptor,
"keyboardLayoutDescriptor must not be null");
String key = getLayoutDescriptor(identifier);
String key = new KeyboardIdentifier(identifier).toString();
synchronized (mDataStore) {
try {
String oldLayout = mDataStore.getCurrentKeyboardLayout(key);
@@ -737,7 +696,7 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
final boolean changed;
final String keyboardLayoutDescriptor;
String key = getLayoutDescriptor(device.getIdentifier());
String key = new KeyboardIdentifier(device.getIdentifier()).toString();
synchronized (mDataStore) {
try {
changed = mDataStore.switchKeyboardLayout(key, direction);
@@ -769,11 +728,13 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
@Nullable
@AnyThread
public String[] getKeyboardLayoutOverlay(InputDeviceIdentifier identifier) {
public String[] getKeyboardLayoutOverlay(InputDeviceIdentifier identifier, String languageTag,
String layoutType) {
String keyboardLayoutDescriptor;
if (useNewSettingsUi()) {
synchronized (mImeInfoLock) {
KeyboardLayoutInfo layoutInfo = getKeyboardLayoutForInputDeviceInternal(identifier,
KeyboardLayoutInfo layoutInfo = getKeyboardLayoutForInputDeviceInternal(
new KeyboardIdentifier(identifier, languageTag, layoutType),
mCurrentImeInfo);
keyboardLayoutDescriptor = layoutInfo == null ? null : layoutInfo.mDescriptor;
}
@@ -811,12 +772,16 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
Slog.e(TAG, "getKeyboardLayoutForInputDevice() API not supported");
return null;
}
InputMethodSubtypeHandle subtypeHandle = InputMethodSubtypeHandle.of(imeInfo, imeSubtype);
KeyboardLayoutInfo layoutInfo = getKeyboardLayoutForInputDeviceInternal(identifier,
new ImeInfo(userId, subtypeHandle, imeSubtype));
InputDevice inputDevice = getInputDevice(identifier);
if (inputDevice == null || inputDevice.isVirtual() || !inputDevice.isFullKeyboard()) {
return null;
}
KeyboardIdentifier keyboardIdentifier = new KeyboardIdentifier(inputDevice);
KeyboardLayoutInfo layoutInfo = getKeyboardLayoutForInputDeviceInternal(
keyboardIdentifier, new ImeInfo(userId, imeInfo, imeSubtype));
if (DEBUG) {
Slog.d(TAG, "getKeyboardLayoutForInputDevice() " + identifier.toString() + ", userId : "
+ userId + ", subtypeHandle = " + subtypeHandle + " -> " + layoutInfo);
+ userId + ", subtype = " + imeSubtype + " -> " + layoutInfo);
}
return layoutInfo != null ? layoutInfo.mDescriptor : null;
}
@@ -832,16 +797,20 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
}
Objects.requireNonNull(keyboardLayoutDescriptor,
"keyboardLayoutDescriptor must not be null");
String key = createLayoutKey(identifier,
new ImeInfo(userId, InputMethodSubtypeHandle.of(imeInfo, imeSubtype), imeSubtype));
InputDevice inputDevice = getInputDevice(identifier);
if (inputDevice == null || inputDevice.isVirtual() || !inputDevice.isFullKeyboard()) {
return;
}
KeyboardIdentifier keyboardIdentifier = new KeyboardIdentifier(inputDevice);
String layoutKey = new LayoutKey(keyboardIdentifier,
new ImeInfo(userId, imeInfo, imeSubtype)).toString();
synchronized (mDataStore) {
try {
// Key for storing into data store = <device descriptor>,<userId>,<subtypeHandle>
if (mDataStore.setKeyboardLayout(getLayoutDescriptor(identifier), key,
if (mDataStore.setKeyboardLayout(keyboardIdentifier.toString(), layoutKey,
keyboardLayoutDescriptor)) {
if (DEBUG) {
Slog.d(TAG, "setKeyboardLayoutForInputDevice() " + identifier
+ " key: " + key
+ " key: " + layoutKey
+ " keyboardLayoutDescriptor: " + keyboardLayoutDescriptor);
}
mHandler.sendEmptyMessage(MSG_RELOAD_KEYBOARD_LAYOUTS);
@@ -860,18 +829,23 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
Slog.e(TAG, "getKeyboardLayoutListForInputDevice() API not supported");
return new KeyboardLayout[0];
}
return getKeyboardLayoutListForInputDeviceInternal(identifier, new ImeInfo(userId,
InputMethodSubtypeHandle.of(imeInfo, imeSubtype), imeSubtype));
InputDevice inputDevice = getInputDevice(identifier);
if (inputDevice == null || inputDevice.isVirtual() || !inputDevice.isFullKeyboard()) {
return new KeyboardLayout[0];
}
return getKeyboardLayoutListForInputDeviceInternal(new KeyboardIdentifier(inputDevice),
new ImeInfo(userId, imeInfo, imeSubtype));
}
private KeyboardLayout[] getKeyboardLayoutListForInputDeviceInternal(
InputDeviceIdentifier identifier, @Nullable ImeInfo imeInfo) {
String key = createLayoutKey(identifier, imeInfo);
KeyboardIdentifier keyboardIdentifier, @Nullable ImeInfo imeInfo) {
String layoutKey = new LayoutKey(keyboardIdentifier, imeInfo).toString();
// Fetch user selected layout and always include it in layout list.
String userSelectedLayout;
synchronized (mDataStore) {
userSelectedLayout = mDataStore.getKeyboardLayout(getLayoutDescriptor(identifier), key);
userSelectedLayout = mDataStore.getKeyboardLayout(keyboardIdentifier.toString(),
layoutKey);
}
final ArrayList<KeyboardLayout> potentialLayouts = new ArrayList<>();
@@ -894,8 +868,8 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
// devices that have special layouts we assume there's a reason that the generic
// layouts don't work for them, so we don't want to return them since it's likely
// to result in a poor user experience.
if (layout.getVendorId() == identifier.getVendorId()
&& layout.getProductId() == identifier.getProductId()) {
if (layout.getVendorId() == keyboardIdentifier.mIdentifier.getVendorId()
&& layout.getProductId() == keyboardIdentifier.mIdentifier.getProductId()) {
if (!mDeviceSpecificLayoutAvailable) {
mDeviceSpecificLayoutAvailable = true;
potentialLayouts.clear();
@@ -934,7 +908,7 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
if (mCurrentImeInfo == null || !subtypeHandle.equals(mCurrentImeInfo.mImeSubtypeHandle)
|| mCurrentImeInfo.mUserId != userId) {
mCurrentImeInfo = new ImeInfo(userId, subtypeHandle, subtype);
mHandler.sendEmptyMessage(MSG_CURRENT_IME_INFO_CHANGED);
mHandler.sendEmptyMessage(MSG_RELOAD_KEYBOARD_LAYOUTS);
if (DEBUG) {
Slog.d(TAG, "InputMethodSubtype changed: userId=" + userId
+ " subtypeHandle=" + subtypeHandle);
@@ -943,34 +917,12 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
}
}
@MainThread
private void onCurrentImeInfoChanged() {
synchronized (mImeInfoLock) {
for (int i = 0; i < mConfiguredKeyboards.size(); i++) {
InputDevice inputDevice = Objects.requireNonNull(
getInputDevice(mConfiguredKeyboards.keyAt(i)));
KeyboardLayoutInfo layoutInfo = getKeyboardLayoutForInputDeviceInternal(
inputDevice.getIdentifier(), mCurrentImeInfo);
KeyboardConfiguration config = mConfiguredKeyboards.valueAt(i);
if (!Objects.equals(layoutInfo, config.getCurrentLayout())) {
config.setCurrentLayout(layoutInfo);
mHandler.sendEmptyMessage(MSG_RELOAD_KEYBOARD_LAYOUTS);
return;
}
}
}
}
@Nullable
private KeyboardLayoutInfo getKeyboardLayoutForInputDeviceInternal(
InputDeviceIdentifier identifier, @Nullable ImeInfo imeInfo) {
InputDevice inputDevice = getInputDevice(identifier);
if (inputDevice == null || inputDevice.isVirtual() || !inputDevice.isFullKeyboard()) {
return null;
}
String key = createLayoutKey(identifier, imeInfo);
KeyboardIdentifier keyboardIdentifier, @Nullable ImeInfo imeInfo) {
String layoutKey = new LayoutKey(keyboardIdentifier, imeInfo).toString();
synchronized (mDataStore) {
String layout = mDataStore.getKeyboardLayout(getLayoutDescriptor(identifier), key);
String layout = mDataStore.getKeyboardLayout(keyboardIdentifier.toString(), layoutKey);
if (layout != null) {
return new KeyboardLayoutInfo(layout, LAYOUT_SELECTION_CRITERIA_USER);
}
@@ -978,16 +930,17 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
synchronized (mKeyboardLayoutCache) {
// Check Auto-selected layout cache to see if layout had been previously selected
if (mKeyboardLayoutCache.containsKey(key)) {
return mKeyboardLayoutCache.get(key);
if (mKeyboardLayoutCache.containsKey(layoutKey)) {
return mKeyboardLayoutCache.get(layoutKey);
} else {
// NOTE: This list is already filtered based on IME Script code
KeyboardLayout[] layoutList = getKeyboardLayoutListForInputDeviceInternal(
identifier, imeInfo);
keyboardIdentifier, imeInfo);
// Call auto-matching algorithm to find the best matching layout
KeyboardLayoutInfo layoutInfo =
getDefaultKeyboardLayoutBasedOnImeInfo(inputDevice, imeInfo, layoutList);
mKeyboardLayoutCache.put(key, layoutInfo);
getDefaultKeyboardLayoutBasedOnImeInfo(keyboardIdentifier, imeInfo,
layoutList);
mKeyboardLayoutCache.put(layoutKey, layoutInfo);
return layoutInfo;
}
}
@@ -995,17 +948,18 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
@Nullable
private static KeyboardLayoutInfo getDefaultKeyboardLayoutBasedOnImeInfo(
InputDevice inputDevice, @Nullable ImeInfo imeInfo, KeyboardLayout[] layoutList) {
KeyboardIdentifier keyboardIdentifier, @Nullable ImeInfo imeInfo,
KeyboardLayout[] layoutList) {
Arrays.sort(layoutList);
// Check <VendorID, ProductID> matching for explicitly declared custom KCM files.
for (KeyboardLayout layout : layoutList) {
if (layout.getVendorId() == inputDevice.getVendorId()
&& layout.getProductId() == inputDevice.getProductId()) {
if (layout.getVendorId() == keyboardIdentifier.mIdentifier.getVendorId()
&& layout.getProductId() == keyboardIdentifier.mIdentifier.getProductId()) {
if (DEBUG) {
Slog.d(TAG,
"getDefaultKeyboardLayoutBasedOnImeInfo() : Layout found based on "
+ "vendor and product Ids. " + inputDevice.getIdentifier()
+ "vendor and product Ids. " + keyboardIdentifier
+ " : " + layout.getDescriptor());
}
return new KeyboardLayoutInfo(layout.getDescriptor(),
@@ -1014,17 +968,17 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
}
// Check layout type, language tag information from InputDevice for matching
String inputLanguageTag = inputDevice.getKeyboardLanguageTag();
String inputLanguageTag = keyboardIdentifier.mLanguageTag;
if (inputLanguageTag != null) {
String layoutDesc = getMatchingLayoutForProvidedLanguageTagAndLayoutType(layoutList,
inputLanguageTag, inputDevice.getKeyboardLayoutType());
inputLanguageTag, keyboardIdentifier.mLayoutType);
if (layoutDesc != null) {
if (DEBUG) {
Slog.d(TAG,
"getDefaultKeyboardLayoutBasedOnImeInfo() : Layout found based on "
+ "HW information (Language tag and Layout type). "
+ inputDevice.getIdentifier() + " : " + layoutDesc);
+ keyboardIdentifier + " : " + layoutDesc);
}
return new KeyboardLayoutInfo(layoutDesc, LAYOUT_SELECTION_CRITERIA_DEVICE);
}
@@ -1045,7 +999,7 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
if (DEBUG) {
Slog.d(TAG,
"getDefaultKeyboardLayoutBasedOnImeInfo() : Layout found based on "
+ "IME locale matching. " + inputDevice.getIdentifier() + " : "
+ "IME locale matching. " + keyboardIdentifier + " : "
+ layoutDesc);
}
if (layoutDesc != null) {
@@ -1105,7 +1059,7 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
}
}
return layoutMatchingLanguageAndCountry != null
? layoutMatchingLanguageAndCountry : layoutMatchingLanguage;
? layoutMatchingLanguageAndCountry : layoutMatchingLanguage;
}
private void reloadKeyboardLayouts() {
@@ -1297,9 +1251,6 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
case MSG_UPDATE_KEYBOARD_LAYOUTS:
updateKeyboardLayouts();
return true;
case MSG_CURRENT_IME_INFO_CHANGED:
onCurrentImeInfoChanged();
return true;
default:
return false;
}
@@ -1322,6 +1273,7 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
identifier.getDescriptor()) : null;
}
@SuppressLint("MissingPermission")
private List<ImeInfo> getImeInfoListForLayoutMapping() {
List<ImeInfo> imeInfoList = new ArrayList<>();
UserManager userManager = Objects.requireNonNull(
@@ -1337,31 +1289,20 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
int userId = userHandle.getIdentifier();
for (InputMethodInfo imeInfo :
inputMethodManagerInternal.getEnabledInputMethodListAsUser(
userId)) {
userId)) {
for (InputMethodSubtype imeSubtype :
inputMethodManager.getEnabledInputMethodSubtypeList(
imeInfo, true /* allowsImplicitlyEnabledSubtypes */)) {
if (!imeSubtype.isSuitableForPhysicalKeyboardLayoutMapping()) {
continue;
}
imeInfoList.add(
new ImeInfo(userId, InputMethodSubtypeHandle.of(imeInfo, imeSubtype),
imeSubtype));
imeInfoList.add(new ImeInfo(userId, imeInfo, imeSubtype));
}
}
}
return imeInfoList;
}
private String createLayoutKey(InputDeviceIdentifier identifier, @Nullable ImeInfo imeInfo) {
if (imeInfo == null) {
return getLayoutDescriptor(identifier);
}
Objects.requireNonNull(imeInfo.mImeSubtypeHandle, "subtypeHandle must not be null");
return "layoutDescriptor:" + getLayoutDescriptor(identifier) + ",userId:" + imeInfo.mUserId
+ ",subtypeHandle:" + imeInfo.mImeSubtypeHandle.toStringHandle();
}
private static boolean isLayoutCompatibleWithLanguageTag(KeyboardLayout layout,
@NonNull String languageTag) {
LocaleList layoutLocales = layout.getLocales();
@@ -1451,6 +1392,11 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
mImeSubtypeHandle = imeSubtypeHandle;
mImeSubtype = imeSubtype;
}
ImeInfo(@UserIdInt int userId, @NonNull InputMethodInfo imeInfo,
@Nullable InputMethodSubtype imeSubtype) {
this(userId, InputMethodSubtypeHandle.of(imeInfo, imeSubtype), imeSubtype);
}
}
private static class KeyboardConfiguration {
@@ -1459,10 +1405,6 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
@Nullable
private Set<String> mConfiguredLayouts;
// If null, it means no layout is selected for the device.
@Nullable
private KeyboardLayoutInfo mCurrentLayout;
private boolean hasConfiguredLayouts() {
return mConfiguredLayouts != null && !mConfiguredLayouts.isEmpty();
}
@@ -1475,15 +1417,6 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
private void setConfiguredLayouts(Set<String> configuredLayouts) {
mConfiguredLayouts = configuredLayouts;
}
@Nullable
private KeyboardLayoutInfo getCurrentLayout() {
return mCurrentLayout;
}
private void setCurrentLayout(KeyboardLayoutInfo currentLayout) {
mCurrentLayout = currentLayout;
}
}
private static class KeyboardLayoutInfo {
@@ -1517,4 +1450,88 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
void visitKeyboardLayout(Resources resources,
int keyboardLayoutResId, KeyboardLayout layout);
}
private static class KeyboardIdentifier {
@NonNull
private final InputDeviceIdentifier mIdentifier;
@Nullable
private final String mLanguageTag;
@Nullable
private final String mLayoutType;
// NOTE: Use this only for old settings UI where we don't use language tag and layout
// type to determine the KCM file.
private KeyboardIdentifier(@NonNull InputDeviceIdentifier inputDeviceIdentifier) {
this(inputDeviceIdentifier, null, null);
}
private KeyboardIdentifier(@NonNull InputDevice inputDevice) {
this(inputDevice.getIdentifier(), inputDevice.getKeyboardLanguageTag(),
inputDevice.getKeyboardLayoutType());
}
private KeyboardIdentifier(@NonNull InputDeviceIdentifier identifier,
@Nullable String languageTag, @Nullable String layoutType) {
Objects.requireNonNull(identifier, "identifier must not be null");
Objects.requireNonNull(identifier.getDescriptor(), "descriptor must not be null");
mIdentifier = identifier;
mLanguageTag = languageTag;
mLayoutType = layoutType;
}
@Override
public int hashCode() {
return Objects.hashCode(toString());
}
@Override
public String toString() {
if (mIdentifier.getVendorId() == 0 && mIdentifier.getProductId() == 0) {
return mIdentifier.getDescriptor();
}
// If vendor id and product id is available, use it as keys. This allows us to have the
// same setup for all keyboards with same product and vendor id. i.e. User can swap 2
// identical keyboards and still get the same setup.
StringBuilder key = new StringBuilder();
key.append("vendor:").append(mIdentifier.getVendorId()).append(",product:").append(
mIdentifier.getProductId());
// Some keyboards can have same product ID and vendor ID but different Keyboard info
// like language tag and layout type.
if (!TextUtils.isEmpty(mLanguageTag)) {
key.append(",languageTag:").append(mLanguageTag);
}
if (!TextUtils.isEmpty(mLayoutType)) {
key.append(",layoutType:").append(mLayoutType);
}
return key.toString();
}
}
private static class LayoutKey {
private final KeyboardIdentifier mKeyboardIdentifier;
@Nullable
private final ImeInfo mImeInfo;
private LayoutKey(KeyboardIdentifier keyboardIdentifier, @Nullable ImeInfo imeInfo) {
mKeyboardIdentifier = keyboardIdentifier;
mImeInfo = imeInfo;
}
@Override
public int hashCode() {
return Objects.hashCode(toString());
}
@Override
public String toString() {
if (mImeInfo == null) {
return mKeyboardIdentifier.toString();
}
Objects.requireNonNull(mImeInfo.mImeSubtypeHandle, "subtypeHandle must not be null");
return "layoutDescriptor:" + mKeyboardIdentifier + ",userId:" + mImeInfo.mUserId
+ ",subtypeHandle:" + mImeInfo.mImeSubtypeHandle.toStringHandle();
}
}
}

View File

@@ -313,7 +313,8 @@ public:
std::shared_ptr<PointerControllerInterface> obtainPointerController(int32_t deviceId) override;
void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) override;
std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
const InputDeviceIdentifier& identifier) override;
const InputDeviceIdentifier& identifier,
const std::optional<KeyboardLayoutInfo> keyboardLayoutInfo) override;
std::string getDeviceAlias(const InputDeviceIdentifier& identifier) override;
TouchAffineTransformation getTouchAffineTransformation(const std::string& inputDeviceDescriptor,
ui::Rotation surfaceRotation) override;
@@ -779,17 +780,32 @@ void NativeInputManager::notifyInputDevicesChanged(const std::vector<InputDevice
}
std::shared_ptr<KeyCharacterMap> NativeInputManager::getKeyboardLayoutOverlay(
const InputDeviceIdentifier& identifier) {
const InputDeviceIdentifier& identifier,
const std::optional<KeyboardLayoutInfo> keyboardLayoutInfo) {
ATRACE_CALL();
JNIEnv* env = jniEnv();
std::shared_ptr<KeyCharacterMap> result;
ScopedLocalRef<jstring> descriptor(env, env->NewStringUTF(identifier.descriptor.c_str()));
ScopedLocalRef<jstring> languageTag(env,
keyboardLayoutInfo
? env->NewStringUTF(
keyboardLayoutInfo->languageTag.c_str())
: nullptr);
ScopedLocalRef<jstring> layoutType(env,
keyboardLayoutInfo
? env->NewStringUTF(
keyboardLayoutInfo->layoutType.c_str())
: nullptr);
ScopedLocalRef<jobject> identifierObj(env, env->NewObject(gInputDeviceIdentifierInfo.clazz,
gInputDeviceIdentifierInfo.constructor, descriptor.get(),
identifier.vendor, identifier.product));
ScopedLocalRef<jobjectArray> arrayObj(env, jobjectArray(env->CallObjectMethod(mServiceObj,
gServiceClassInfo.getKeyboardLayoutOverlay, identifierObj.get())));
ScopedLocalRef<jobjectArray>
arrayObj(env,
jobjectArray(env->CallObjectMethod(mServiceObj,
gServiceClassInfo.getKeyboardLayoutOverlay,
identifierObj.get(), languageTag.get(),
layoutType.get())));
if (arrayObj.get()) {
ScopedLocalRef<jstring> filenameObj(env,
jstring(env->GetObjectArrayElement(arrayObj.get(), 0)));
@@ -2803,9 +2819,9 @@ int register_android_server_InputManager(JNIEnv* env) {
GET_METHOD_ID(gServiceClassInfo.getPointerIcon, clazz,
"getPointerIcon", "(I)Landroid/view/PointerIcon;");
GET_METHOD_ID(gServiceClassInfo.getKeyboardLayoutOverlay, clazz,
"getKeyboardLayoutOverlay",
"(Landroid/hardware/input/InputDeviceIdentifier;)[Ljava/lang/String;");
GET_METHOD_ID(gServiceClassInfo.getKeyboardLayoutOverlay, clazz, "getKeyboardLayoutOverlay",
"(Landroid/hardware/input/InputDeviceIdentifier;Ljava/lang/String;Ljava/lang/"
"String;)[Ljava/lang/String;");
GET_METHOD_ID(gServiceClassInfo.getDeviceAlias, clazz,
"getDeviceAlias", "(Ljava/lang/String;)Ljava/lang/String;");