From 3895d7f8dc2e4999947f61220b86fa148f433413 Mon Sep 17 00:00:00 2001 From: Yohei Yukawa Date: Wed, 2 Jul 2014 13:29:36 +0900 Subject: [PATCH] Additional subtype ID should be independent of OS-version This CL consolidates the initialization logic for additional subtypes so that each additional subtypes can have predictable subtype ID regardless of OS version. Previously subtype IDs for additional subtypes are calculated differently depending on the running OS version with hoping it minimizes the risk of compatibility issues. However, it is getting harder and harder to maintain slightly different logic between OSes. Thus we decided to unify the logic into that in KitKat even though it may causes some breaking changes. Note that the actual extra values that are used to instantiate InputMethodSubtype object are still determined on the fly depending on the running OS version. However these actual extra values are no longer used for the subtype ID calculation. BUG: 16000850 Change-Id: Id3c262386a7bc7ed75966b1395a50171abe550d3 --- .../compat/InputMethodSubtypeCompatUtils.java | 8 +- .../settings/AdditionalSubtypeSettings.java | 10 +- .../AndroidSpellCheckerService.java | 4 +- .../latin/utils/AdditionalSubtypeUtils.java | 144 +++++++++----- .../keyboard/KeyboardLayoutSetTestsBase.java | 4 +- .../LanguageOnSpacebarHelperTests.java | 8 +- .../utils/AdditionalSubtypeUtilsTests.java | 175 ++++++++++++++++++ .../utils/SpacebarLanguagetUtilsTests.java | 28 +-- .../latin/utils/SubtypeLocaleUtilsTests.java | 28 +-- 9 files changed, 323 insertions(+), 86 deletions(-) create mode 100644 tests/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtilsTests.java diff --git a/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatUtils.java b/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatUtils.java index 4ea7fb888..ee9125a07 100644 --- a/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatUtils.java +++ b/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatUtils.java @@ -19,6 +19,7 @@ package com.android.inputmethod.compat; import android.os.Build; import android.view.inputmethod.InputMethodSubtype; +import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.Constants; import java.lang.reflect.Constructor; @@ -64,7 +65,12 @@ public final class InputMethodSubtypeCompatUtils { } public static boolean isAsciiCapable(final InputMethodSubtype subtype) { - return (Boolean)CompatUtils.invoke(subtype, false, METHOD_isAsciiCapable) + return isAsciiCapableWithAPI(subtype) || subtype.containsExtraValueKey(Constants.Subtype.ExtraValue.ASCII_CAPABLE); } + + @UsedForTesting + public static boolean isAsciiCapableWithAPI(final InputMethodSubtype subtype) { + return (Boolean)CompatUtils.invoke(subtype, false, METHOD_isAsciiCapable); + } } diff --git a/java/src/com/android/inputmethod/latin/settings/AdditionalSubtypeSettings.java b/java/src/com/android/inputmethod/latin/settings/AdditionalSubtypeSettings.java index 31fa86774..ad411f9ee 100644 --- a/java/src/com/android/inputmethod/latin/settings/AdditionalSubtypeSettings.java +++ b/java/src/com/android/inputmethod/latin/settings/AdditionalSubtypeSettings.java @@ -150,8 +150,9 @@ public final class AdditionalSubtypeSettings extends PreferenceFragment { // TODO: Should filter out already existing combinations of locale and layout. for (final String layout : SubtypeLocaleUtils.getPredefinedKeyboardLayoutSet()) { // This is a dummy subtype with NO_LANGUAGE, only for display. - final InputMethodSubtype subtype = AdditionalSubtypeUtils.createAdditionalSubtype( - SubtypeLocaleUtils.NO_LANGUAGE, layout, null); + final InputMethodSubtype subtype = + AdditionalSubtypeUtils.createDummyAdditionalSubtype( + SubtypeLocaleUtils.NO_LANGUAGE, layout); add(new KeyboardLayoutSetItem(subtype)); } } @@ -286,8 +287,9 @@ public final class AdditionalSubtypeSettings extends PreferenceFragment { (SubtypeLocaleItem) mSubtypeLocaleSpinner.getSelectedItem(); final KeyboardLayoutSetItem layout = (KeyboardLayoutSetItem) mKeyboardLayoutSetSpinner.getSelectedItem(); - final InputMethodSubtype subtype = AdditionalSubtypeUtils.createAdditionalSubtype( - locale.first, layout.first, Constants.Subtype.ExtraValue.ASCII_CAPABLE); + final InputMethodSubtype subtype = + AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype( + locale.first, layout.first); setSubtype(subtype); notifyChanged(); if (isEditing) { diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java index b57eab31b..90c8f618f 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java @@ -374,8 +374,8 @@ public final class AndroidSpellCheckerService extends SpellCheckerService public DictAndKeyboard createDictAndKeyboard(final Locale locale) { final int script = ScriptUtils.getScriptFromSpellCheckerLocale(locale); final String keyboardLayoutName = getKeyboardLayoutNameForScript(script); - final InputMethodSubtype subtype = AdditionalSubtypeUtils.createAdditionalSubtype( - locale.toString(), keyboardLayoutName, null); + final InputMethodSubtype subtype = AdditionalSubtypeUtils.createDummyAdditionalSubtype( + locale.toString(), keyboardLayoutName); final KeyboardLayoutSet keyboardLayoutSet = createKeyboardSetForSpellChecker(subtype); final DictionaryCollection dictionaryCollection = diff --git a/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java b/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java index 3ca7c7e1c..db7f2a56c 100644 --- a/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java @@ -17,6 +17,7 @@ package com.android.inputmethod.latin.utils; import static com.android.inputmethod.latin.Constants.Subtype.KEYBOARD_MODE; +import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.ASCII_CAPABLE; import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.EMOJI_CAPABLE; import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.IS_ADDITIONAL_SUBTYPE; import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.KEYBOARD_LAYOUT_SET; @@ -56,25 +57,33 @@ public final class AdditionalSubtypeUtils { private static final int LENGTH_WITH_EXTRA_VALUE = (INDEX_OF_EXTRA_VALUE + 1); private static final String PREF_SUBTYPE_SEPARATOR = ";"; - public static InputMethodSubtype createAdditionalSubtype(final String localeString, - final String keyboardLayoutSetName, final String extraValue) { - final String layoutExtraValue = KEYBOARD_LAYOUT_SET + "=" + keyboardLayoutSetName; - final String layoutDisplayNameExtraValue; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN - && SubtypeLocaleUtils.isExceptionalLocale(localeString)) { - final String layoutDisplayName = SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName( - keyboardLayoutSetName); - layoutDisplayNameExtraValue = StringUtils.appendToCommaSplittableTextIfNotExists( - UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME + "=" + layoutDisplayName, extraValue); - } else { - layoutDisplayNameExtraValue = extraValue; - } - final String additionalSubtypeExtraValue = - StringUtils.appendToCommaSplittableTextIfNotExists( - IS_ADDITIONAL_SUBTYPE, layoutDisplayNameExtraValue); + private static InputMethodSubtype createAdditionalSubtypeInternal( + final String localeString, final String keyboardLayoutSetName, + final boolean isAsciiCapable, final boolean isEmojiCapable) { final int nameId = SubtypeLocaleUtils.getSubtypeNameId(localeString, keyboardLayoutSetName); - return buildInputMethodSubtype( - nameId, localeString, layoutExtraValue, additionalSubtypeExtraValue); + final String platformVersionDependentExtraValues = getPlatformVersionDependentExtraValue( + localeString, keyboardLayoutSetName, isAsciiCapable, isEmojiCapable); + final int platformVersionIndependentSubtypeId = + getPlatformVersionIndependentSubtypeId(localeString, keyboardLayoutSetName); + // NOTE: In KitKat and later, InputMethodSubtypeBuilder#setIsAsciiCapable is also available. + // TODO: Use InputMethodSubtypeBuilder#setIsAsciiCapable when appropriate. + return InputMethodSubtypeCompatUtils.newInputMethodSubtype(nameId, + R.drawable.ic_ime_switcher_dark, localeString, KEYBOARD_MODE, + platformVersionDependentExtraValues, + false /* isAuxiliary */, false /* overrideImplicitlyEnabledSubtype */, + platformVersionIndependentSubtypeId); + } + + public static InputMethodSubtype createDummyAdditionalSubtype( + final String localeString, final String keyboardLayoutSetName) { + return createAdditionalSubtypeInternal(localeString, keyboardLayoutSetName, + false /* isAsciiCapable */, false /* isEmojiCapable */); + } + + public static InputMethodSubtype createAsciiEmojiCapableAdditionalSubtype( + final String localeString, final String keyboardLayoutSetName) { + return createAdditionalSubtypeInternal(localeString, keyboardLayoutSetName, + true /* isAsciiCapable */, true /* isEmojiCapable */); } public static String getPrefSubtype(final InputMethodSubtype subtype) { @@ -106,10 +115,10 @@ public final class AdditionalSubtypeUtils { } final String localeString = elems[INDEX_OF_LOCALE]; final String keyboardLayoutSetName = elems[INDEX_OF_KEYBOARD_LAYOUT]; - final String extraValue = (elems.length == LENGTH_WITH_EXTRA_VALUE) - ? elems[INDEX_OF_EXTRA_VALUE] : null; - final InputMethodSubtype subtype = createAdditionalSubtype( - localeString, keyboardLayoutSetName, extraValue); + // Here we assume that all the additional subtypes have AsciiCapable and EmojiCapable. + // This is actually what the setting dialog for additional subtype is doing. + final InputMethodSubtype subtype = createAsciiEmojiCapableAdditionalSubtype( + localeString, keyboardLayoutSetName); if (subtype.getNameResId() == SubtypeLocaleUtils.UNKNOWN_KEYBOARD_LAYOUT) { // Skip unknown keyboard layout subtype. This may happen when predefined keyboard // layout has been removed. @@ -148,35 +157,80 @@ public final class AdditionalSubtypeUtils { return sb.toString(); } - private static InputMethodSubtype buildInputMethodSubtype(final int nameId, - final String localeString, final String layoutExtraValue, - final String additionalSubtypeExtraValue) { - // To preserve additional subtype settings and user's selection across OS updates, subtype - // id shouldn't be changed. New attributes, such as emojiCapable, are carefully excluded - // from the calculation of subtype id. - final String compatibleExtraValue = StringUtils.joinCommaSplittableText( - layoutExtraValue, additionalSubtypeExtraValue); - final int compatibleSubtypeId = getInputMethodSubtypeId(localeString, compatibleExtraValue); - final String extraValue; - // Color Emoji is supported from KitKat. - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - extraValue = StringUtils.appendToCommaSplittableTextIfNotExists( - EMOJI_CAPABLE, compatibleExtraValue); - } else { - extraValue = compatibleExtraValue; + /** + * Returns the extra value that is optimized for the running OS. + *

+ * Historically the extra value has been used as the last resort to annotate various kinds of + * attributes. Some of these attributes are valid only on some platform versions. Thus we cannot + * assume that the extra values stored in a persistent storage are always valid. We need to + * regenerate the extra value on the fly instead. + *

+ * @param localeString the locale string (e.g., "en_US"). + * @param keyboardLayoutSetName the keyboard layout set name (e.g., "dvorak"). + * @param isAsciiCapable true when ASCII characters are supported with this layout. + * @param isEmojiCapable true when Unicode Emoji characters are supported with this layout. + * @return extra value that is optimized for the running OS. + * @see #getPlatformVersionIndependentSubtypeId(String, String) + */ + private static String getPlatformVersionDependentExtraValue(final String localeString, + final String keyboardLayoutSetName, final boolean isAsciiCapable, + final boolean isEmojiCapable) { + final ArrayList extraValueItems = new ArrayList<>(); + extraValueItems.add(KEYBOARD_LAYOUT_SET + "=" + keyboardLayoutSetName); + if (isAsciiCapable) { + extraValueItems.add(ASCII_CAPABLE); } - return InputMethodSubtypeCompatUtils.newInputMethodSubtype(nameId, - R.drawable.ic_ime_switcher_dark, localeString, KEYBOARD_MODE, extraValue, - false, false, compatibleSubtypeId); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN && + SubtypeLocaleUtils.isExceptionalLocale(localeString)) { + extraValueItems.add(UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME + "=" + + SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(keyboardLayoutSetName)); + } + if (isEmojiCapable && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + extraValueItems.add(EMOJI_CAPABLE); + } + extraValueItems.add(IS_ADDITIONAL_SUBTYPE); + return TextUtils.join(",", extraValueItems); } - private static int getInputMethodSubtypeId(final String localeString, final String extraValue) { - // From the compatibility point of view, the calculation of subtype id has been copied from - // {@link InputMethodSubtype} of JellyBean MR2. + /** + * Returns the subtype ID that is supposed to be compatible between different version of OSes. + *

+ * From the compatibility point of view, it is important to keep subtype id predictable and + * stable between different OSes. For this purpose, the calculation code in this method is + * carefully chosen and then fixed. Treat the following code as no more or less than a + * hash function. Each component to be hashed can be different from the corresponding value + * that is used to instantiate {@link InputMethodSubtype} actually. + * For example, you don't need to update compatibilityExtraValueItems in this + * method even when we need to add some new extra values for the actual instance of + * {@link InputMethodSubtype}. + *

+ * @param localeString the locale string (e.g., "en_US"). + * @param keyboardLayoutSetName the keyboard layout set name (e.g., "dvorak"). + * @return a platform-version independent subtype ID. + * @see #getPlatformVersionDependentExtraValue(String, String, boolean, boolean) + */ + private static int getPlatformVersionIndependentSubtypeId(final String localeString, + final String keyboardLayoutSetName) { + // For compatibility reasons, we concatenate the extra values in the following order. + // - KeyboardLayoutSet + // - AsciiCapable + // - UntranslatableReplacementStringInSubtypeName + // - EmojiCapable + // - isAdditionalSubtype + final ArrayList compatibilityExtraValueItems = new ArrayList<>(); + compatibilityExtraValueItems.add(KEYBOARD_LAYOUT_SET + "=" + keyboardLayoutSetName); + compatibilityExtraValueItems.add(ASCII_CAPABLE); + if (SubtypeLocaleUtils.isExceptionalLocale(localeString)) { + compatibilityExtraValueItems.add(UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME + "=" + + SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(keyboardLayoutSetName)); + } + compatibilityExtraValueItems.add(EMOJI_CAPABLE); + compatibilityExtraValueItems.add(IS_ADDITIONAL_SUBTYPE); + final String compatibilityExtraValues = TextUtils.join(",", compatibilityExtraValueItems); return Arrays.hashCode(new Object[] { localeString, KEYBOARD_MODE, - extraValue, + compatibilityExtraValues, false /* isAuxiliary */, false /* overrideImplicitlyEnabledSubtype */ }); } diff --git a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java index ab7d1b28d..cf884bfea 100644 --- a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java +++ b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java @@ -104,8 +104,8 @@ public abstract class KeyboardLayoutSetTestsBase extends AndroidTestCase { final Locale subtypeLocale = SubtypeLocaleUtils.getSubtypeLocale(subtype); if (locale.equals(subtypeLocale)) { // Create additional subtype. - return AdditionalSubtypeUtils.createAdditionalSubtype( - locale.toString(), keyboardLayout, null /* extraValue */); + return AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype( + locale.toString(), keyboardLayout); } } throw new RuntimeException( diff --git a/tests/src/com/android/inputmethod/keyboard/internal/LanguageOnSpacebarHelperTests.java b/tests/src/com/android/inputmethod/keyboard/internal/LanguageOnSpacebarHelperTests.java index 0be1e374c..6ea27588e 100644 --- a/tests/src/com/android/inputmethod/keyboard/internal/LanguageOnSpacebarHelperTests.java +++ b/tests/src/com/android/inputmethod/keyboard/internal/LanguageOnSpacebarHelperTests.java @@ -67,10 +67,10 @@ public class LanguageOnSpacebarHelperTests extends AndroidTestCase { Locale.CANADA_FRENCH.toString(), "qwerty"); FR_CH_SWISS = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( "fr_CH", "swiss"); - FR_CH_QWERTZ = AdditionalSubtypeUtils.createAdditionalSubtype( - "fr_CH", "qwertz", null); - FR_CH_QWERTY = AdditionalSubtypeUtils.createAdditionalSubtype( - "fr_CH", "qwerty", null); + FR_CH_QWERTZ = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype( + "fr_CH", "qwertz"); + FR_CH_QWERTY = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype( + "fr_CH", "qwerty"); ZZ_QWERTY = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( SubtypeLocaleUtils.NO_LANGUAGE, "qwerty"); } diff --git a/tests/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtilsTests.java new file mode 100644 index 000000000..91c9c3775 --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtilsTests.java @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin.utils; + +import android.content.Context; +import android.os.Build; +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.SmallTest; +import android.view.inputmethod.InputMethodSubtype; + +import com.android.inputmethod.compat.InputMethodSubtypeCompatUtils; + +import java.util.Locale; + +import static com.android.inputmethod.latin.Constants.Subtype.KEYBOARD_MODE; +import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.ASCII_CAPABLE; +import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.EMOJI_CAPABLE; +import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.IS_ADDITIONAL_SUBTYPE; +import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.KEYBOARD_LAYOUT_SET; +import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue + .UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME; + +@SmallTest +public class AdditionalSubtypeUtilsTests extends AndroidTestCase { + + /** + * Predictable subtype ID for en_US dvorak layout. This is actually a hash code calculated as + * follows. + * + * final boolean isAuxiliary = false; + * final boolean overrideImplicitlyEnabledSubtype = false; + * final int SUBTYPE_ID_EN_US_DVORAK = Arrays.hashCode(new Object[] { + * "en_US", + * "keyboard", + * "KeyboardLayoutSet=dvorak" + * + ",AsciiCapable" + * + ",UntranslatableReplacementStringInSubtypeName=Dvorak" + * + ",EmojiCapable" + * + ",isAdditionalSubtype", + * isAuxiliary, + * overrideImplicitlyEnabledSubtype }); + * + */ + private static int SUBTYPE_ID_EN_US_DVORAK = 0xb3c0cc56; + private static String EXTRA_VALUE_EN_US_DVORAK_ICS = + "KeyboardLayoutSet=dvorak" + + ",AsciiCapable" + + ",isAdditionalSubtype"; + private static String EXTRA_VALUE_EN_US_DVORAK_JELLY_BEAN = + "KeyboardLayoutSet=dvorak" + + ",AsciiCapable" + + ",UntranslatableReplacementStringInSubtypeName=Dvorak" + + ",isAdditionalSubtype"; + private static String EXTRA_VALUE_EN_US_DVORAK_KITKAT = + "KeyboardLayoutSet=dvorak" + + ",AsciiCapable" + + ",UntranslatableReplacementStringInSubtypeName=Dvorak" + + ",EmojiCapable" + + ",isAdditionalSubtype"; + + /** + * Predictable subtype ID for azerty layout. This is actually a hash code calculated as follows. + * + * final boolean isAuxiliary = false; + * final boolean overrideImplicitlyEnabledSubtype = false; + * final int SUBTYPE_ID_ZZ_AZERTY = Arrays.hashCode(new Object[] { + * "zz", + * "keyboard", + * "KeyboardLayoutSet=azerty" + * + ",AsciiCapable" + * + ",EmojiCapable" + * + ",isAdditionalSubtype", + * isAuxiliary, + * overrideImplicitlyEnabledSubtype }); + * + */ + private static int SUBTYPE_ID_ZZ_AZERTY = 0x5b6be697; + private static String EXTRA_VALUE_ZZ_AZERTY_ICS = + "KeyboardLayoutSet=azerty" + + ",AsciiCapable" + + ",isAdditionalSubtype"; + private static String EXTRA_VALUE_ZZ_AZERTY_KITKAT = + "KeyboardLayoutSet=azerty" + + ",AsciiCapable" + + ",EmojiCapable" + + ",isAdditionalSubtype"; + + @Override + protected void setUp() throws Exception { + super.setUp(); + final Context context = getContext(); + SubtypeLocaleUtils.init(context); + } + + private static void assertEnUsDvorak(InputMethodSubtype subtype) { + assertEquals("en_US", subtype.getLocale()); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + assertEquals(EXTRA_VALUE_EN_US_DVORAK_KITKAT, subtype.getExtraValue()); + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + assertEquals(EXTRA_VALUE_EN_US_DVORAK_JELLY_BEAN, subtype.getExtraValue()); + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + assertEquals(EXTRA_VALUE_EN_US_DVORAK_ICS, subtype.getExtraValue()); + } + assertTrue(subtype.containsExtraValueKey(ASCII_CAPABLE)); + assertTrue(InputMethodSubtypeCompatUtils.isAsciiCapable(subtype)); + // TODO: Enable following test + // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + // assertTrue(InputMethodSubtypeCompatUtils.isAsciiCapableWithAPI(subtype)); + // } + assertTrue(subtype.containsExtraValueKey(EMOJI_CAPABLE)); + assertTrue(subtype.containsExtraValueKey(IS_ADDITIONAL_SUBTYPE)); + assertEquals("dvorak", subtype.getExtraValueOf(KEYBOARD_LAYOUT_SET)); + assertEquals("Dvorak", subtype.getExtraValueOf(UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME)); + assertEquals(KEYBOARD_MODE, subtype.getMode()); + assertEquals(SUBTYPE_ID_EN_US_DVORAK, subtype.hashCode()); + } + + private static void assertAzerty(InputMethodSubtype subtype) { + assertEquals("zz", subtype.getLocale()); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + assertEquals(EXTRA_VALUE_ZZ_AZERTY_KITKAT, subtype.getExtraValue()); + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + assertEquals(EXTRA_VALUE_ZZ_AZERTY_ICS, subtype.getExtraValue()); + } + assertTrue(subtype.containsExtraValueKey(ASCII_CAPABLE)); + assertTrue(InputMethodSubtypeCompatUtils.isAsciiCapable(subtype)); + // TODO: Enable following test + // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + // assertTrue(InputMethodSubtypeCompatUtils.isAsciiCapableWithAPI(subtype)); + // } + assertTrue(subtype.containsExtraValueKey(EMOJI_CAPABLE)); + assertTrue(subtype.containsExtraValueKey(IS_ADDITIONAL_SUBTYPE)); + assertEquals("azerty", subtype.getExtraValueOf(KEYBOARD_LAYOUT_SET)); + assertFalse(subtype.containsExtraValueKey(UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME)); + assertEquals(KEYBOARD_MODE, subtype.getMode()); + assertEquals(SUBTYPE_ID_ZZ_AZERTY, subtype.hashCode()); + } + + public void testRestorable() { + final InputMethodSubtype EN_UK_DVORAK = + AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype( + Locale.US.toString(), "dvorak"); + final InputMethodSubtype ZZ_AZERTY = + AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype( + SubtypeLocaleUtils.NO_LANGUAGE, "azerty"); + assertEnUsDvorak(EN_UK_DVORAK); + assertAzerty(ZZ_AZERTY); + + // Make sure the subtype can be stored and restored in a deterministic manner. + final InputMethodSubtype[] subtypes = { EN_UK_DVORAK, ZZ_AZERTY }; + final String prefSubtype = AdditionalSubtypeUtils.createPrefSubtypes(subtypes); + final InputMethodSubtype[] restoredSubtypes = + AdditionalSubtypeUtils.createAdditionalSubtypesArray(prefSubtype); + assertEquals(2, restoredSubtypes.length); + final InputMethodSubtype restored_EN_UK_DVORAK = restoredSubtypes[0]; + final InputMethodSubtype restored_ZZ_AZERTY = restoredSubtypes[1]; + + assertEnUsDvorak(restored_EN_UK_DVORAK); + assertAzerty(restored_ZZ_AZERTY); + } +} diff --git a/tests/src/com/android/inputmethod/latin/utils/SpacebarLanguagetUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/SpacebarLanguagetUtilsTests.java index 4156de78b..fdde34251 100644 --- a/tests/src/com/android/inputmethod/latin/utils/SpacebarLanguagetUtilsTests.java +++ b/tests/src/com/android/inputmethod/latin/utils/SpacebarLanguagetUtilsTests.java @@ -87,20 +87,20 @@ public class SpacebarLanguagetUtilsTests extends AndroidTestCase { "de_CH", "swiss"); ZZ = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( SubtypeLocaleUtils.NO_LANGUAGE, "qwerty"); - DE_QWERTY = AdditionalSubtypeUtils.createAdditionalSubtype( - Locale.GERMAN.toString(), "qwerty", null); - FR_QWERTZ = AdditionalSubtypeUtils.createAdditionalSubtype( - Locale.FRENCH.toString(), "qwertz", null); - EN_US_AZERTY = AdditionalSubtypeUtils.createAdditionalSubtype( - Locale.US.toString(), "azerty", null); - EN_UK_DVORAK = AdditionalSubtypeUtils.createAdditionalSubtype( - Locale.UK.toString(), "dvorak", null); - ES_US_COLEMAK = AdditionalSubtypeUtils.createAdditionalSubtype( - "es_US", "colemak", null); - ZZ_AZERTY = AdditionalSubtypeUtils.createAdditionalSubtype( - SubtypeLocaleUtils.NO_LANGUAGE, "azerty", null); - ZZ_PC = AdditionalSubtypeUtils.createAdditionalSubtype( - SubtypeLocaleUtils.NO_LANGUAGE, "pcqwerty", null); + DE_QWERTY = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype( + Locale.GERMAN.toString(), "qwerty"); + FR_QWERTZ = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype( + Locale.FRENCH.toString(), "qwertz"); + EN_US_AZERTY = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype( + Locale.US.toString(), "azerty"); + EN_UK_DVORAK = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype( + Locale.UK.toString(), "dvorak"); + ES_US_COLEMAK = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype( + "es_US", "colemak"); + ZZ_AZERTY = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype( + SubtypeLocaleUtils.NO_LANGUAGE, "azerty"); + ZZ_PC = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype( + SubtypeLocaleUtils.NO_LANGUAGE, "pcqwerty"); } public void testAllFullDisplayNameForSpacebar() { diff --git a/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java index 8e409ab99..ce3df7dd6 100644 --- a/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java +++ b/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java @@ -87,20 +87,20 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase { "de_CH", "swiss"); ZZ = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( SubtypeLocaleUtils.NO_LANGUAGE, "qwerty"); - DE_QWERTY = AdditionalSubtypeUtils.createAdditionalSubtype( - Locale.GERMAN.toString(), "qwerty", null); - FR_QWERTZ = AdditionalSubtypeUtils.createAdditionalSubtype( - Locale.FRENCH.toString(), "qwertz", null); - EN_US_AZERTY = AdditionalSubtypeUtils.createAdditionalSubtype( - Locale.US.toString(), "azerty", null); - EN_UK_DVORAK = AdditionalSubtypeUtils.createAdditionalSubtype( - Locale.UK.toString(), "dvorak", null); - ES_US_COLEMAK = AdditionalSubtypeUtils.createAdditionalSubtype( - "es_US", "colemak", null); - ZZ_AZERTY = AdditionalSubtypeUtils.createAdditionalSubtype( - SubtypeLocaleUtils.NO_LANGUAGE, "azerty", null); - ZZ_PC = AdditionalSubtypeUtils.createAdditionalSubtype( - SubtypeLocaleUtils.NO_LANGUAGE, "pcqwerty", null); + DE_QWERTY = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype( + Locale.GERMAN.toString(), "qwerty"); + FR_QWERTZ = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype( + Locale.FRENCH.toString(), "qwertz"); + EN_US_AZERTY = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype( + Locale.US.toString(), "azerty"); + EN_UK_DVORAK = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype( + Locale.UK.toString(), "dvorak"); + ES_US_COLEMAK = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype( + "es_US", "colemak"); + ZZ_AZERTY = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype( + SubtypeLocaleUtils.NO_LANGUAGE, "azerty"); + ZZ_PC = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype( + SubtypeLocaleUtils.NO_LANGUAGE, "pcqwerty"); } public void testAllFullDisplayName() {