Loading services/core/java/com/android/server/input/KeyboardLayoutManager.java +29 −14 Original line number Diff line number Diff line Loading @@ -1261,28 +1261,43 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener { private static boolean isLayoutCompatibleWithLanguageTag(KeyboardLayout layout, @NonNull String languageTag) { final int[] scriptsFromLanguageTag = UScript.getCode(Locale.forLanguageTag(languageTag)); 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; } // 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; } LocaleList locales = layout.getLocales(); if (locales.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. for (int i = 0; i < layoutLocales.size(); i++) { final Locale locale = layoutLocales.get(i); int[] scripts = getScriptCodes(locale); if (haveCommonValue(scripts, scriptsFromLanguageTag)) { return true; } for (int i = 0; i < locales.size(); i++) { final Locale locale = locales.get(i); } return false; } private static int[] getScriptCodes(@Nullable Locale locale) { if (locale == null) { continue; return new int[0]; } int[] scripts = UScript.getCode(locale); if (scripts != null && haveCommonValue(scripts, scriptsFromLanguageTag)) { return true; if (!TextUtils.isEmpty(locale.getScript())) { int scriptCode = UScript.getCodeFromName(locale.getScript()); if (scriptCode != UScript.INVALID_CODE) { return new int[]{scriptCode}; } } return false; int[] scripts = UScript.getCode(locale); if (scripts != null) { return scripts; } return new int[0]; } private static boolean haveCommonValue(int[] arr1, int[] arr2) { Loading services/tests/servicestests/res/xml/keyboard_layouts.xml +8 −0 Original line number Diff line number Diff line Loading @@ -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> services/tests/servicestests/src/com/android/server/input/KeyboardLayoutManagerTests.kt +54 −23 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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, Loading Loading @@ -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 = Loading Loading @@ -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 ) } } Loading Loading @@ -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 Loading Loading
services/core/java/com/android/server/input/KeyboardLayoutManager.java +29 −14 Original line number Diff line number Diff line Loading @@ -1261,28 +1261,43 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener { private static boolean isLayoutCompatibleWithLanguageTag(KeyboardLayout layout, @NonNull String languageTag) { final int[] scriptsFromLanguageTag = UScript.getCode(Locale.forLanguageTag(languageTag)); 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; } // 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; } LocaleList locales = layout.getLocales(); if (locales.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. for (int i = 0; i < layoutLocales.size(); i++) { final Locale locale = layoutLocales.get(i); int[] scripts = getScriptCodes(locale); if (haveCommonValue(scripts, scriptsFromLanguageTag)) { return true; } for (int i = 0; i < locales.size(); i++) { final Locale locale = locales.get(i); } return false; } private static int[] getScriptCodes(@Nullable Locale locale) { if (locale == null) { continue; return new int[0]; } int[] scripts = UScript.getCode(locale); if (scripts != null && haveCommonValue(scripts, scriptsFromLanguageTag)) { return true; if (!TextUtils.isEmpty(locale.getScript())) { int scriptCode = UScript.getCodeFromName(locale.getScript()); if (scriptCode != UScript.INVALID_CODE) { return new int[]{scriptCode}; } } return false; int[] scripts = UScript.getCode(locale); if (scripts != null) { return scripts; } return new int[0]; } private static boolean haveCommonValue(int[] arr1, int[] arr2) { Loading
services/tests/servicestests/res/xml/keyboard_layouts.xml +8 −0 Original line number Diff line number Diff line Loading @@ -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>
services/tests/servicestests/src/com/android/server/input/KeyboardLayoutManagerTests.kt +54 −23 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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, Loading Loading @@ -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 = Loading Loading @@ -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 ) } } Loading Loading @@ -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 Loading