diff options
Diffstat (limited to 'java/src/com/android/inputmethod/latin/utils')
-rw-r--r-- | java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java | 138 | ||||
-rw-r--r-- | java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java | 333 |
2 files changed, 471 insertions, 0 deletions
diff --git a/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java b/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java new file mode 100644 index 000000000..215faa0c7 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2012 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 static com.android.inputmethod.latin.Constants.Subtype.KEYBOARD_MODE; +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; + +import android.os.Build; +import android.text.TextUtils; +import android.view.inputmethod.InputMethodSubtype; + +import com.android.inputmethod.latin.R; + +import java.util.ArrayList; + +public final class AdditionalSubtypeUtils { + private static final InputMethodSubtype[] EMPTY_SUBTYPE_ARRAY = new InputMethodSubtype[0]; + + private AdditionalSubtypeUtils() { + // This utility class is not publicly instantiable. + } + + public static boolean isAdditionalSubtype(final InputMethodSubtype subtype) { + return subtype.containsExtraValueKey(IS_ADDITIONAL_SUBTYPE); + } + + private static final String LOCALE_AND_LAYOUT_SEPARATOR = ":"; + 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); + final int nameId = SubtypeLocaleUtils.getSubtypeNameId(localeString, keyboardLayoutSetName); + return new InputMethodSubtype(nameId, R.drawable.ic_subtype_keyboard, + localeString, KEYBOARD_MODE, + layoutExtraValue + "," + additionalSubtypeExtraValue, false, false); + } + + public static String getPrefSubtype(final InputMethodSubtype subtype) { + final String localeString = subtype.getLocale(); + final String keyboardLayoutSetName = SubtypeLocaleUtils.getKeyboardLayoutSetName(subtype); + final String layoutExtraValue = KEYBOARD_LAYOUT_SET + "=" + keyboardLayoutSetName; + final String extraValue = StringUtils.removeFromCommaSplittableTextIfExists( + layoutExtraValue, StringUtils.removeFromCommaSplittableTextIfExists( + IS_ADDITIONAL_SUBTYPE, subtype.getExtraValue())); + final String basePrefSubtype = localeString + LOCALE_AND_LAYOUT_SEPARATOR + + keyboardLayoutSetName; + return extraValue.isEmpty() ? basePrefSubtype + : basePrefSubtype + LOCALE_AND_LAYOUT_SEPARATOR + extraValue; + } + + public static InputMethodSubtype createAdditionalSubtype(final String prefSubtype) { + final String elems[] = prefSubtype.split(LOCALE_AND_LAYOUT_SEPARATOR); + if (elems.length < 2 || elems.length > 3) { + throw new RuntimeException("Unknown additional subtype specified: " + prefSubtype); + } + final String localeString = elems[0]; + final String keyboardLayoutSetName = elems[1]; + final String extraValue = (elems.length == 3) ? elems[2] : null; + return createAdditionalSubtype(localeString, keyboardLayoutSetName, extraValue); + } + + public static InputMethodSubtype[] createAdditionalSubtypesArray(final String prefSubtypes) { + if (TextUtils.isEmpty(prefSubtypes)) { + return EMPTY_SUBTYPE_ARRAY; + } + final String[] prefSubtypeArray = prefSubtypes.split(PREF_SUBTYPE_SEPARATOR); + final ArrayList<InputMethodSubtype> subtypesList = + CollectionUtils.newArrayList(prefSubtypeArray.length); + for (final String prefSubtype : prefSubtypeArray) { + final InputMethodSubtype subtype = createAdditionalSubtype(prefSubtype); + if (subtype.getNameResId() == SubtypeLocaleUtils.UNKNOWN_KEYBOARD_LAYOUT) { + // Skip unknown keyboard layout subtype. This may happen when predefined keyboard + // layout has been removed. + continue; + } + subtypesList.add(subtype); + } + return subtypesList.toArray(new InputMethodSubtype[subtypesList.size()]); + } + + public static String createPrefSubtypes(final InputMethodSubtype[] subtypes) { + if (subtypes == null || subtypes.length == 0) { + return ""; + } + final StringBuilder sb = new StringBuilder(); + for (final InputMethodSubtype subtype : subtypes) { + if (sb.length() > 0) { + sb.append(PREF_SUBTYPE_SEPARATOR); + } + sb.append(getPrefSubtype(subtype)); + } + return sb.toString(); + } + + public static String createPrefSubtypes(final String[] prefSubtypes) { + if (prefSubtypes == null || prefSubtypes.length == 0) { + return ""; + } + final StringBuilder sb = new StringBuilder(); + for (final String prefSubtype : prefSubtypes) { + if (sb.length() > 0) { + sb.append(PREF_SUBTYPE_SEPARATOR); + } + sb.append(prefSubtype); + } + return sb.toString(); + } +} diff --git a/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java b/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java new file mode 100644 index 000000000..4cfa2897d --- /dev/null +++ b/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java @@ -0,0 +1,333 @@ +/* + * Copyright (C) 2011 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 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; + +import android.content.Context; +import android.content.res.Resources; +import android.os.Build; +import android.util.Log; +import android.view.inputmethod.InputMethodSubtype; + +import com.android.inputmethod.latin.DictionaryFactory; +import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.utils.LocaleUtils.RunInLocale; + +import java.util.HashMap; +import java.util.Locale; + +public final class SubtypeLocaleUtils { + static final String TAG = SubtypeLocaleUtils.class.getSimpleName(); + // This class must be located in the same package as LatinIME.java. + private static final String RESOURCE_PACKAGE_NAME = + DictionaryFactory.class.getPackage().getName(); + + // Special language code to represent "no language". + public static final String NO_LANGUAGE = "zz"; + public static final String QWERTY = "qwerty"; + public static final int UNKNOWN_KEYBOARD_LAYOUT = R.string.subtype_generic; + + private static boolean sInitialized = false; + private static Resources sResources; + private static String[] sPredefinedKeyboardLayoutSet; + // Keyboard layout to its display name map. + private static final HashMap<String, String> sKeyboardLayoutToDisplayNameMap = + CollectionUtils.newHashMap(); + // Keyboard layout to subtype name resource id map. + private static final HashMap<String, Integer> sKeyboardLayoutToNameIdsMap = + CollectionUtils.newHashMap(); + // Exceptional locale to subtype name resource id map. + private static final HashMap<String, Integer> sExceptionalLocaleToNameIdsMap = + CollectionUtils.newHashMap(); + // Exceptional locale to subtype name with layout resource id map. + private static final HashMap<String, Integer> sExceptionalLocaleToWithLayoutNameIdsMap = + CollectionUtils.newHashMap(); + private static final String SUBTYPE_NAME_RESOURCE_PREFIX = + "string/subtype_"; + private static final String SUBTYPE_NAME_RESOURCE_GENERIC_PREFIX = + "string/subtype_generic_"; + private static final String SUBTYPE_NAME_RESOURCE_WITH_LAYOUT_PREFIX = + "string/subtype_with_layout_"; + private static final String SUBTYPE_NAME_RESOURCE_NO_LANGUAGE_PREFIX = + "string/subtype_no_language_"; + // Keyboard layout set name for the subtypes that don't have a keyboardLayoutSet extra value. + // This is for compatibility to keep the same subtype ids as pre-JellyBean. + private static final HashMap<String, String> sLocaleAndExtraValueToKeyboardLayoutSetMap = + CollectionUtils.newHashMap(); + + private SubtypeLocaleUtils() { + // Intentional empty constructor for utility class. + } + + // Note that this initialization method can be called multiple times. + public static synchronized void init(final Context context) { + if (sInitialized) return; + + final Resources res = context.getResources(); + sResources = res; + + final String[] predefinedLayoutSet = res.getStringArray(R.array.predefined_layouts); + sPredefinedKeyboardLayoutSet = predefinedLayoutSet; + final String[] layoutDisplayNames = res.getStringArray( + R.array.predefined_layout_display_names); + for (int i = 0; i < predefinedLayoutSet.length; i++) { + final String layoutName = predefinedLayoutSet[i]; + sKeyboardLayoutToDisplayNameMap.put(layoutName, layoutDisplayNames[i]); + final String resourceName = SUBTYPE_NAME_RESOURCE_GENERIC_PREFIX + layoutName; + final int resId = res.getIdentifier(resourceName, null, RESOURCE_PACKAGE_NAME); + sKeyboardLayoutToNameIdsMap.put(layoutName, resId); + // Register subtype name resource id of "No language" with key "zz_<layout>" + final String noLanguageResName = SUBTYPE_NAME_RESOURCE_NO_LANGUAGE_PREFIX + layoutName; + final int noLanguageResId = res.getIdentifier( + noLanguageResName, null, RESOURCE_PACKAGE_NAME); + final String key = getNoLanguageLayoutKey(layoutName); + sKeyboardLayoutToNameIdsMap.put(key, noLanguageResId); + } + + final String[] exceptionalLocales = res.getStringArray( + R.array.subtype_locale_exception_keys); + for (int i = 0; i < exceptionalLocales.length; i++) { + final String localeString = exceptionalLocales[i]; + final String resourceName = SUBTYPE_NAME_RESOURCE_PREFIX + localeString; + final int resId = res.getIdentifier(resourceName, null, RESOURCE_PACKAGE_NAME); + sExceptionalLocaleToNameIdsMap.put(localeString, resId); + final String resourceNameWithLayout = + SUBTYPE_NAME_RESOURCE_WITH_LAYOUT_PREFIX + localeString; + final int resIdWithLayout = res.getIdentifier( + resourceNameWithLayout, null, RESOURCE_PACKAGE_NAME); + sExceptionalLocaleToWithLayoutNameIdsMap.put(localeString, resIdWithLayout); + } + + final String[] keyboardLayoutSetMap = res.getStringArray( + R.array.locale_and_extra_value_to_keyboard_layout_set_map); + for (int i = 0; i + 1 < keyboardLayoutSetMap.length; i += 2) { + final String key = keyboardLayoutSetMap[i]; + final String keyboardLayoutSet = keyboardLayoutSetMap[i + 1]; + sLocaleAndExtraValueToKeyboardLayoutSetMap.put(key, keyboardLayoutSet); + } + + sInitialized = true; + } + + public static String[] getPredefinedKeyboardLayoutSet() { + return sPredefinedKeyboardLayoutSet; + } + + public static boolean isExceptionalLocale(final String localeString) { + return sExceptionalLocaleToNameIdsMap.containsKey(localeString); + } + + private static final String getNoLanguageLayoutKey(final String keyboardLayoutName) { + return NO_LANGUAGE + "_" + keyboardLayoutName; + } + + public static int getSubtypeNameId(final String localeString, final String keyboardLayoutName) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN + && isExceptionalLocale(localeString)) { + return sExceptionalLocaleToWithLayoutNameIdsMap.get(localeString); + } + final String key = NO_LANGUAGE.equals(localeString) + ? getNoLanguageLayoutKey(keyboardLayoutName) + : keyboardLayoutName; + final Integer nameId = sKeyboardLayoutToNameIdsMap.get(key); + return nameId == null ? UNKNOWN_KEYBOARD_LAYOUT : nameId; + } + + private static Locale getDisplayLocaleOfSubtypeLocale(final String localeString) { + if (NO_LANGUAGE.equals(localeString)) { + return sResources.getConfiguration().locale; + } + return LocaleUtils.constructLocaleFromString(localeString); + } + + public static String getSubtypeLocaleDisplayNameInSystemLocale(final String localeString) { + final Locale displayLocale = sResources.getConfiguration().locale; + return getSubtypeLocaleDisplayNameInternal(localeString, displayLocale); + } + + public static String getSubtypeLocaleDisplayName(final String localeString) { + final Locale displayLocale = getDisplayLocaleOfSubtypeLocale(localeString); + return getSubtypeLocaleDisplayNameInternal(localeString, displayLocale); + } + + private static String getSubtypeLocaleDisplayNameInternal(final String localeString, + final Locale displayLocale) { + final Integer exceptionalNameResId = sExceptionalLocaleToNameIdsMap.get(localeString); + final String displayName; + if (exceptionalNameResId != null) { + final RunInLocale<String> getExceptionalName = new RunInLocale<String>() { + @Override + protected String job(final Resources res) { + return res.getString(exceptionalNameResId); + } + }; + displayName = getExceptionalName.runInLocale(sResources, displayLocale); + } else if (NO_LANGUAGE.equals(localeString)) { + // No language subtype should be displayed in system locale. + return sResources.getString(R.string.subtype_no_language); + } else { + final Locale locale = LocaleUtils.constructLocaleFromString(localeString); + displayName = locale.getDisplayName(displayLocale); + } + return StringUtils.capitalizeFirstCodePoint(displayName, displayLocale); + } + + // InputMethodSubtype's display name in its locale. + // isAdditionalSubtype (T=true, F=false) + // locale layout | display name + // ------ ------- - ---------------------- + // en_US qwerty F English (US) exception + // en_GB qwerty F English (UK) exception + // es_US spanish F Español (EE.UU.) exception + // fr azerty F Français + // fr_CA qwerty F Français (Canada) + // de qwertz F Deutsch + // zz qwerty F No language (QWERTY) in system locale + // fr qwertz T Français (QWERTZ) + // de qwerty T Deutsch (QWERTY) + // en_US azerty T English (US) (AZERTY) exception + // zz azerty T No language (AZERTY) in system locale + + private static String getReplacementString(final InputMethodSubtype subtype, + final Locale displayLocale) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN + && subtype.containsExtraValueKey(UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME)) { + return subtype.getExtraValueOf(UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME); + } else { + return getSubtypeLocaleDisplayNameInternal(subtype.getLocale(), displayLocale); + } + } + + public static String getSubtypeDisplayNameInSystemLocale(final InputMethodSubtype subtype) { + final Locale displayLocale = sResources.getConfiguration().locale; + return getSubtypeDisplayNameInternal(subtype, displayLocale); + } + + public static String getSubtypeNameForLogging(final InputMethodSubtype subtype) { + if (subtype == null) { + return "<null subtype>"; + } + return getSubtypeLocale(subtype) + "/" + getKeyboardLayoutSetName(subtype); + } + + private static String getSubtypeDisplayNameInternal(final InputMethodSubtype subtype, + final Locale displayLocale) { + final String replacementString = getReplacementString(subtype, displayLocale); + final int nameResId = subtype.getNameResId(); + final RunInLocale<String> getSubtypeName = new RunInLocale<String>() { + @Override + protected String job(final Resources res) { + try { + return res.getString(nameResId, replacementString); + } catch (Resources.NotFoundException e) { + // TODO: Remove this catch when InputMethodManager.getCurrentInputMethodSubtype + // is fixed. + Log.w(TAG, "Unknown subtype: mode=" + subtype.getMode() + + " nameResId=" + subtype.getNameResId() + + " locale=" + subtype.getLocale() + + " extra=" + subtype.getExtraValue() + + "\n" + DebugLogUtils.getStackTrace()); + return ""; + } + } + }; + return StringUtils.capitalizeFirstCodePoint( + getSubtypeName.runInLocale(sResources, displayLocale), displayLocale); + } + + public static boolean isNoLanguage(final InputMethodSubtype subtype) { + final String localeString = subtype.getLocale(); + return NO_LANGUAGE.equals(localeString); + } + + public static Locale getSubtypeLocale(final InputMethodSubtype subtype) { + final String localeString = subtype.getLocale(); + return LocaleUtils.constructLocaleFromString(localeString); + } + + public static String getKeyboardLayoutSetDisplayName(final InputMethodSubtype subtype) { + final String layoutName = getKeyboardLayoutSetName(subtype); + return getKeyboardLayoutSetDisplayName(layoutName); + } + + public static String getKeyboardLayoutSetDisplayName(final String layoutName) { + return sKeyboardLayoutToDisplayNameMap.get(layoutName); + } + + public static String getKeyboardLayoutSetName(final InputMethodSubtype subtype) { + String keyboardLayoutSet = subtype.getExtraValueOf(KEYBOARD_LAYOUT_SET); + if (keyboardLayoutSet == null) { + // This subtype doesn't have a keyboardLayoutSet extra value, so lookup its keyboard + // layout set in sLocaleAndExtraValueToKeyboardLayoutSetMap to keep it compatible with + // pre-JellyBean. + final String key = subtype.getLocale() + ":" + subtype.getExtraValue(); + keyboardLayoutSet = sLocaleAndExtraValueToKeyboardLayoutSetMap.get(key); + } + // TODO: Remove this null check when InputMethodManager.getCurrentInputMethodSubtype is + // fixed. + if (keyboardLayoutSet == null) { + android.util.Log.w(TAG, "KeyboardLayoutSet not found, use QWERTY: " + + "locale=" + subtype.getLocale() + " extraValue=" + subtype.getExtraValue()); + return QWERTY; + } + return keyboardLayoutSet; + } + + // InputMethodSubtype's display name for spacebar text in its locale. + // isAdditionalSubtype (T=true, F=false) + // locale layout | Short Middle Full + // ------ ------- - ---- --------- ---------------------- + // en_US qwerty F En English English (US) exception + // en_GB qwerty F En English English (UK) exception + // es_US spanish F Es Español Español (EE.UU.) exception + // fr azerty F Fr Français Français + // fr_CA qwerty F Fr Français Français (Canada) + // de qwertz F De Deutsch Deutsch + // zz qwerty F QWERTY QWERTY + // fr qwertz T Fr Français Français + // de qwerty T De Deutsch Deutsch + // en_US azerty T En English English (US) + // zz azerty T AZERTY AZERTY + + // Get InputMethodSubtype's full display name in its locale. + public static String getFullDisplayName(final InputMethodSubtype subtype) { + if (isNoLanguage(subtype)) { + return getKeyboardLayoutSetDisplayName(subtype); + } + return getSubtypeLocaleDisplayName(subtype.getLocale()); + } + + // Get InputMethodSubtype's middle display name in its locale. + public static String getMiddleDisplayName(final InputMethodSubtype subtype) { + if (isNoLanguage(subtype)) { + return getKeyboardLayoutSetDisplayName(subtype); + } + final Locale locale = getSubtypeLocale(subtype); + return getSubtypeLocaleDisplayName(locale.getLanguage()); + } + + // Get InputMethodSubtype's short display name in its locale. + public static String getShortDisplayName(final InputMethodSubtype subtype) { + if (isNoLanguage(subtype)) { + return ""; + } + final Locale locale = getSubtypeLocale(subtype); + return StringUtils.capitalizeFirstCodePoint(locale.getLanguage(), locale); + } +} |