|
|
|
|
@@ -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();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|