Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9c3a0b79 authored by Vaibhav Devmurari's avatar Vaibhav Devmurari
Browse files

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
parent 1b485a66
Loading
Loading
Loading
Loading
+29 −14
Original line number Diff line number Diff line
@@ -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) {
+8 −0
Original line number Diff line number Diff line
@@ -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>
+54 −23
Original line number Diff line number Diff line
@@ -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