aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatUtils.java8
-rw-r--r--java/src/com/android/inputmethod/compat/LocaleSpanCompatUtils.java59
-rw-r--r--java/src/com/android/inputmethod/latin/settings/AdditionalSubtypeSettings.java10
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java4
-rw-r--r--java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java144
-rw-r--r--java/src/com/android/inputmethod/latin/utils/StringUtils.java25
-rw-r--r--tests/src/com/android/inputmethod/compat/LocaleSpanCompatUtilsTests.java38
-rw-r--r--tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java4
-rw-r--r--tests/src/com/android/inputmethod/keyboard/internal/LanguageOnSpacebarHelperTests.java8
-rw-r--r--tests/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtilsTests.java175
-rw-r--r--tests/src/com/android/inputmethod/latin/utils/SpacebarLanguagetUtilsTests.java28
-rw-r--r--tests/src/com/android/inputmethod/latin/utils/StringAndJsonUtilsTests.java42
-rw-r--r--tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java28
13 files changed, 420 insertions, 153 deletions
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/compat/LocaleSpanCompatUtils.java b/java/src/com/android/inputmethod/compat/LocaleSpanCompatUtils.java
new file mode 100644
index 000000000..967645878
--- /dev/null
+++ b/java/src/com/android/inputmethod/compat/LocaleSpanCompatUtils.java
@@ -0,0 +1,59 @@
+/*
+ * 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.compat;
+
+import com.android.inputmethod.annotations.UsedForTesting;
+import com.android.inputmethod.compat.CompatUtils;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.Locale;
+
+@UsedForTesting
+public final class LocaleSpanCompatUtils {
+ // Note that LocaleSpan(Locale locale) has been introduced in API level 17
+ // (Build.VERSION_CODE.JELLY_BEAN_MR1).
+ private static Class<?> getLocalSpanClass() {
+ try {
+ return Class.forName("android.text.style.LocaleSpan");
+ } catch (ClassNotFoundException e) {
+ return null;
+ }
+ }
+ private static final Constructor<?> LOCALE_SPAN_CONSTRUCTOR;
+ private static final Method LOCALE_SPAN_GET_LOCALE;
+ static {
+ final Class<?> localeSpanClass = getLocalSpanClass();
+ LOCALE_SPAN_CONSTRUCTOR = CompatUtils.getConstructor(localeSpanClass, Locale.class);
+ LOCALE_SPAN_GET_LOCALE = CompatUtils.getMethod(localeSpanClass, "getLocale");
+ }
+
+ @UsedForTesting
+ public static boolean isLocaleSpanAvailable() {
+ return (LOCALE_SPAN_CONSTRUCTOR != null && LOCALE_SPAN_GET_LOCALE != null);
+ }
+
+ @UsedForTesting
+ public static Object newLocaleSpan(final Locale locale) {
+ return CompatUtils.newInstance(LOCALE_SPAN_CONSTRUCTOR, locale);
+ }
+
+ @UsedForTesting
+ public static Locale getLocaleFromLocaleSpan(final Object localeSpan) {
+ return (Locale) CompatUtils.invoke(localeSpan, null, LOCALE_SPAN_GET_LOCALE);
+ }
+}
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.
+ * <p>
+ * 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.
+ * </p>
+ * @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<String> 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.
+ * <p>
+ * 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 <code>compatibilityExtraValueItems</code> in this
+ * method even when we need to add some new extra values for the actual instance of
+ * {@link InputMethodSubtype}.
+ * </p>
+ * @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<String> 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/java/src/com/android/inputmethod/latin/utils/StringUtils.java b/java/src/com/android/inputmethod/latin/utils/StringUtils.java
index e4237a7f2..ceb038371 100644
--- a/java/src/com/android/inputmethod/latin/utils/StringUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/StringUtils.java
@@ -75,31 +75,6 @@ public final class StringUtils {
return containsInArray(text, extraValues.split(SEPARATOR_FOR_COMMA_SPLITTABLE_TEXT));
}
- public static String joinCommaSplittableText(final String head, final String tail) {
- if (TextUtils.isEmpty(head) && TextUtils.isEmpty(tail)) {
- return EMPTY_STRING;
- }
- // Here either head or tail is not null.
- if (TextUtils.isEmpty(head)) {
- return tail;
- }
- if (TextUtils.isEmpty(tail)) {
- return head;
- }
- return head + SEPARATOR_FOR_COMMA_SPLITTABLE_TEXT + tail;
- }
-
- public static String appendToCommaSplittableTextIfNotExists(final String text,
- final String extraValues) {
- if (TextUtils.isEmpty(extraValues)) {
- return text;
- }
- if (containsInCommaSplittableText(text, extraValues)) {
- return extraValues;
- }
- return extraValues + SEPARATOR_FOR_COMMA_SPLITTABLE_TEXT + text;
- }
-
public static String removeFromCommaSplittableTextIfExists(final String text,
final String extraValues) {
if (TextUtils.isEmpty(extraValues)) {
diff --git a/tests/src/com/android/inputmethod/compat/LocaleSpanCompatUtilsTests.java b/tests/src/com/android/inputmethod/compat/LocaleSpanCompatUtilsTests.java
new file mode 100644
index 000000000..a920373d4
--- /dev/null
+++ b/tests/src/com/android/inputmethod/compat/LocaleSpanCompatUtilsTests.java
@@ -0,0 +1,38 @@
+/*
+ * 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.compat;
+
+import android.os.Build;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import java.util.Locale;
+
+@SmallTest
+public class LocaleSpanCompatUtilsTests extends AndroidTestCase {
+ public void testInstantiatable() {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ // LocaleSpan isn't yet available.
+ return;
+ }
+ assertTrue(LocaleSpanCompatUtils.isLocaleSpanAvailable());
+ final Object japaneseLocaleSpan = LocaleSpanCompatUtils.newLocaleSpan(Locale.JAPANESE);
+ assertNotNull(japaneseLocaleSpan);
+ assertEquals(Locale.JAPANESE,
+ LocaleSpanCompatUtils.getLocaleFromLocaleSpan(japaneseLocaleSpan));
+ }
+}
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.
+ * <code>
+ * 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 });
+ * </code>
+ */
+ 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.
+ * <code>
+ * 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 });
+ * </code>
+ */
+ 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/StringAndJsonUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/StringAndJsonUtilsTests.java
index cd9a98356..4448a6baf 100644
--- a/tests/src/com/android/inputmethod/latin/utils/StringAndJsonUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/utils/StringAndJsonUtilsTests.java
@@ -56,48 +56,6 @@ public class StringAndJsonUtilsTests extends AndroidTestCase {
assertTrue("in 2 elements", StringUtils.containsInCommaSplittableText("key", "key1,key"));
}
- public void testJoinCommaSplittableText() {
- assertEquals("2 nulls", "",
- StringUtils.joinCommaSplittableText(null, null));
- assertEquals("null and empty", "",
- StringUtils.joinCommaSplittableText(null, ""));
- assertEquals("empty and null", "",
- StringUtils.joinCommaSplittableText("", null));
- assertEquals("2 empties", "",
- StringUtils.joinCommaSplittableText("", ""));
- assertEquals("text and null", "text",
- StringUtils.joinCommaSplittableText("text", null));
- assertEquals("text and empty", "text",
- StringUtils.joinCommaSplittableText("text", ""));
- assertEquals("null and text", "text",
- StringUtils.joinCommaSplittableText(null, "text"));
- assertEquals("empty and text", "text",
- StringUtils.joinCommaSplittableText("", "text"));
- assertEquals("2 texts", "text1,text2",
- StringUtils.joinCommaSplittableText("text1", "text2"));
- }
-
- public void testAppendToCommaSplittableTextIfNotExists() {
- assertEquals("null", "key",
- StringUtils.appendToCommaSplittableTextIfNotExists("key", null));
- assertEquals("empty", "key",
- StringUtils.appendToCommaSplittableTextIfNotExists("key", ""));
-
- assertEquals("not in 1 element", "key1,key",
- StringUtils.appendToCommaSplittableTextIfNotExists("key", "key1"));
- assertEquals("not in 2 elements", "key1,key2,key",
- StringUtils.appendToCommaSplittableTextIfNotExists("key", "key1,key2"));
-
- assertEquals("in 1 element", "key",
- StringUtils.appendToCommaSplittableTextIfNotExists("key", "key"));
- assertEquals("in 2 elements at position 1", "key,key2",
- StringUtils.appendToCommaSplittableTextIfNotExists("key", "key,key2"));
- assertEquals("in 2 elements at position 2", "key1,key",
- StringUtils.appendToCommaSplittableTextIfNotExists("key", "key1,key"));
- assertEquals("in 3 elements at position 2", "key1,key,key3",
- StringUtils.appendToCommaSplittableTextIfNotExists("key", "key1,key,key3"));
- }
-
public void testRemoveFromCommaSplittableTextIfExists() {
assertEquals("null", "", StringUtils.removeFromCommaSplittableTextIfExists("key", null));
assertEquals("empty", "", StringUtils.removeFromCommaSplittableTextIfExists("key", ""));
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() {