Modify Script matching algorithm
First manually match script code based on provided scripts in the language tag before using UScript to derive script code from tag and matching that. Corner case: Uscript.getCode() hard codes script for "ja..." tags to Japenese, Hirangana and Han scripts even if we provide explicit "Latn" in the language tag. IME can explicitly say "Latn" in language tag to allow any KCMs with Latn support and handle composition to Japense on IME side. So, if explicitly mentioned we should respect the script code provided. Test: atest KeyboardLayoutManagerTests Bug: 272200660 Change-Id: Ic0ffd3aa57d83b7fd8c1fb66ec12581f90c1deaf
This commit is contained in:
@@ -1261,30 +1261,45 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
|
||||
|
||||
private static boolean isLayoutCompatibleWithLanguageTag(KeyboardLayout layout,
|
||||
@NonNull String languageTag) {
|
||||
final int[] scriptsFromLanguageTag = UScript.getCode(Locale.forLanguageTag(languageTag));
|
||||
if (scriptsFromLanguageTag.length == 0) {
|
||||
// If no scripts inferred from languageTag then allowing the layout
|
||||
return true;
|
||||
}
|
||||
LocaleList locales = layout.getLocales();
|
||||
if (locales.isEmpty()) {
|
||||
LocaleList layoutLocales = layout.getLocales();
|
||||
if (layoutLocales.isEmpty()) {
|
||||
// KCM file doesn't have an associated language tag. This can be from
|
||||
// a 3rd party app so need to include it as a potential layout.
|
||||
return true;
|
||||
}
|
||||
for (int i = 0; i < locales.size(); i++) {
|
||||
final Locale locale = locales.get(i);
|
||||
if (locale == null) {
|
||||
continue;
|
||||
}
|
||||
int[] scripts = UScript.getCode(locale);
|
||||
if (scripts != null && haveCommonValue(scripts, scriptsFromLanguageTag)) {
|
||||
// Match derived Script codes
|
||||
final int[] scriptsFromLanguageTag = getScriptCodes(Locale.forLanguageTag(languageTag));
|
||||
if (scriptsFromLanguageTag.length == 0) {
|
||||
// If no scripts inferred from languageTag then allowing the layout
|
||||
return true;
|
||||
}
|
||||
for (int i = 0; i < layoutLocales.size(); i++) {
|
||||
final Locale locale = layoutLocales.get(i);
|
||||
int[] scripts = getScriptCodes(locale);
|
||||
if (haveCommonValue(scripts, scriptsFromLanguageTag)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static int[] getScriptCodes(@Nullable Locale locale) {
|
||||
if (locale == null) {
|
||||
return new int[0];
|
||||
}
|
||||
if (!TextUtils.isEmpty(locale.getScript())) {
|
||||
int scriptCode = UScript.getCodeFromName(locale.getScript());
|
||||
if (scriptCode != UScript.INVALID_CODE) {
|
||||
return new int[]{scriptCode};
|
||||
}
|
||||
}
|
||||
int[] scripts = UScript.getCode(locale);
|
||||
if (scripts != null) {
|
||||
return scripts;
|
||||
}
|
||||
return new int[0];
|
||||
}
|
||||
|
||||
private static boolean haveCommonValue(int[] arr1, int[] arr2) {
|
||||
for (int a1 : arr1) {
|
||||
for (int a2 : arr2) {
|
||||
|
||||
@@ -72,10 +72,18 @@
|
||||
android:keyboardLayout="@raw/dummy_keyboard_layout"
|
||||
android:keyboardLocale="ru-Cyrl" />
|
||||
|
||||
<keyboard-layout
|
||||
android:name="keyboard_layout_english_without_script_code"
|
||||
android:label="English(No script code)"
|
||||
android:keyboardLayout="@raw/dummy_keyboard_layout"
|
||||
android:keyboardLocale="en"
|
||||
android:keyboardLayoutType="qwerty" />
|
||||
|
||||
<keyboard-layout
|
||||
android:name="keyboard_layout_vendorId:1,productId:1"
|
||||
android:label="vendorId:1,productId:1"
|
||||
android:keyboardLayout="@raw/dummy_keyboard_layout"
|
||||
androidprv:vendorId="1"
|
||||
androidprv:productId="1" />
|
||||
|
||||
</keyboard-layouts>
|
||||
|
||||
@@ -26,7 +26,6 @@ import android.content.pm.ServiceInfo
|
||||
import android.hardware.input.IInputManager
|
||||
import android.hardware.input.InputManager
|
||||
import android.hardware.input.KeyboardLayout
|
||||
import android.icu.lang.UScript
|
||||
import android.icu.util.ULocale
|
||||
import android.os.Bundle
|
||||
import android.os.test.TestLooper
|
||||
@@ -52,7 +51,6 @@ import java.io.FileNotFoundException
|
||||
import java.io.FileOutputStream
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.util.Locale
|
||||
|
||||
private fun createKeyboard(
|
||||
deviceId: Int,
|
||||
@@ -553,24 +551,17 @@ class KeyboardLayoutManagerTests {
|
||||
0,
|
||||
keyboardLayouts.size
|
||||
)
|
||||
|
||||
val englishScripts = UScript.getCode(Locale.forLanguageTag("hi-Latn"))
|
||||
for (kl in keyboardLayouts) {
|
||||
var isCompatible = false
|
||||
for (i in 0 until kl.locales.size()) {
|
||||
val locale: Locale = kl.locales.get(i) ?: continue
|
||||
val scripts = UScript.getCode(locale)
|
||||
if (scripts != null && areScriptsCompatible(scripts, englishScripts)) {
|
||||
isCompatible = true
|
||||
break
|
||||
}
|
||||
}
|
||||
assertTrue(
|
||||
"New UI: getKeyboardLayoutListForInputDevice API should only return " +
|
||||
"compatible layouts but found " + kl.descriptor,
|
||||
isCompatible
|
||||
assertTrue("New UI: getKeyboardLayoutListForInputDevice API should return a list " +
|
||||
"containing English(US) layout for hi-Latn",
|
||||
containsLayout(keyboardLayouts, ENGLISH_US_LAYOUT_DESCRIPTOR)
|
||||
)
|
||||
assertTrue("New UI: getKeyboardLayoutListForInputDevice API should return a list " +
|
||||
"containing English(No script code) layout for hi-Latn",
|
||||
containsLayout(
|
||||
keyboardLayouts,
|
||||
createLayoutDescriptor("keyboard_layout_english_without_script_code")
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
// Check Layouts for "hi" which by default uses 'Deva' script.
|
||||
keyboardLayouts =
|
||||
@@ -600,6 +591,46 @@ class KeyboardLayoutManagerTests {
|
||||
1,
|
||||
keyboardLayouts.size
|
||||
)
|
||||
|
||||
// Special case Japanese: UScript ignores provided script code for certain language tags
|
||||
// Should manually match provided script codes and then rely on Uscript to derive
|
||||
// script from language tags and match those.
|
||||
keyboardLayouts =
|
||||
keyboardLayoutManager.getKeyboardLayoutListForInputDevice(
|
||||
keyboardDevice.identifier, USER_ID, imeInfo,
|
||||
createImeSubtypeForLanguageTag("ja-Latn-JP")
|
||||
)
|
||||
assertNotEquals(
|
||||
"New UI: getKeyboardLayoutListForInputDevice API should return the list of " +
|
||||
"supported layouts with matching script code for ja-Latn-JP",
|
||||
0,
|
||||
keyboardLayouts.size
|
||||
)
|
||||
assertTrue("New UI: getKeyboardLayoutListForInputDevice API should return a list " +
|
||||
"containing English(US) layout for ja-Latn-JP",
|
||||
containsLayout(keyboardLayouts, ENGLISH_US_LAYOUT_DESCRIPTOR)
|
||||
)
|
||||
assertTrue("New UI: getKeyboardLayoutListForInputDevice API should return a list " +
|
||||
"containing English(No script code) layout for ja-Latn-JP",
|
||||
containsLayout(
|
||||
keyboardLayouts,
|
||||
createLayoutDescriptor("keyboard_layout_english_without_script_code")
|
||||
)
|
||||
)
|
||||
|
||||
// If script code not explicitly provided for Japanese should rely on Uscript to find
|
||||
// derived script code and hence no suitable layout will be found.
|
||||
keyboardLayouts =
|
||||
keyboardLayoutManager.getKeyboardLayoutListForInputDevice(
|
||||
keyboardDevice.identifier, USER_ID, imeInfo,
|
||||
createImeSubtypeForLanguageTag("ja-JP")
|
||||
)
|
||||
assertEquals(
|
||||
"New UI: getKeyboardLayoutListForInputDevice API should return empty list of " +
|
||||
"supported layouts with matching script code for ja-JP",
|
||||
0,
|
||||
keyboardLayouts.size
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -779,10 +810,10 @@ class KeyboardLayoutManagerTests {
|
||||
private fun createLayoutDescriptor(keyboardName: String): String =
|
||||
"$PACKAGE_NAME/$RECEIVER_NAME/$keyboardName"
|
||||
|
||||
private fun areScriptsCompatible(scriptList1: IntArray, scriptList2: IntArray): Boolean {
|
||||
for (s1 in scriptList1) {
|
||||
for (s2 in scriptList2) {
|
||||
if (s1 == s2) return true
|
||||
private fun containsLayout(layoutList: Array<KeyboardLayout>, layoutDesc: String): Boolean {
|
||||
for (kl in layoutList) {
|
||||
if (kl.descriptor.equals(layoutDesc)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
|
||||
Reference in New Issue
Block a user