diff --git a/services/core/java/com/android/server/input/KeyboardLayoutManager.java b/services/core/java/com/android/server/input/KeyboardLayoutManager.java index 4d4a87e186645..72c7dadac2714 100644 --- a/services/core/java/com/android/server/input/KeyboardLayoutManager.java +++ b/services/core/java/com/android/server/input/KeyboardLayoutManager.java @@ -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) { diff --git a/services/tests/servicestests/res/xml/keyboard_layouts.xml b/services/tests/servicestests/res/xml/keyboard_layouts.xml index b5a05fcaff173..5f3fcd6eaed07 100644 --- a/services/tests/servicestests/res/xml/keyboard_layouts.xml +++ b/services/tests/servicestests/res/xml/keyboard_layouts.xml @@ -72,10 +72,18 @@ android:keyboardLayout="@raw/dummy_keyboard_layout" android:keyboardLocale="ru-Cyrl" /> + + + diff --git a/services/tests/servicestests/src/com/android/server/input/KeyboardLayoutManagerTests.kt b/services/tests/servicestests/src/com/android/server/input/KeyboardLayoutManagerTests.kt index b660926f13946..7729fa29667b8 100644 --- a/services/tests/servicestests/src/com/android/server/input/KeyboardLayoutManagerTests.kt +++ b/services/tests/servicestests/src/com/android/server/input/KeyboardLayoutManagerTests.kt @@ -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, layoutDesc: String): Boolean { + for (kl in layoutList) { + if (kl.descriptor.equals(layoutDesc)) { + return true } } return false