Home | History | Annotate | Download | only in latin
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.inputmethod.latin;
     18 
     19 import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.KEYBOARD_LAYOUT_SET;
     20 import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME;
     21 
     22 import android.content.Context;
     23 import android.content.res.Resources;
     24 import android.os.Build;
     25 import android.util.Log;
     26 import android.view.inputmethod.InputMethodSubtype;
     27 
     28 import com.android.inputmethod.latin.LocaleUtils.RunInLocale;
     29 
     30 import java.util.HashMap;
     31 import java.util.Locale;
     32 
     33 public class SubtypeLocale {
     34     static final String TAG = SubtypeLocale.class.getSimpleName();
     35     // This class must be located in the same package as LatinIME.java.
     36     private static final String RESOURCE_PACKAGE_NAME =
     37             DictionaryFactory.class.getPackage().getName();
     38 
     39     // Special language code to represent "no language".
     40     public static final String NO_LANGUAGE = "zz";
     41     public static final String QWERTY = "qwerty";
     42     public static final int UNKNOWN_KEYBOARD_LAYOUT = R.string.subtype_generic;
     43 
     44     private static String[] sPredefinedKeyboardLayoutSet;
     45     // Keyboard layout to its display name map.
     46     private static final HashMap<String, String> sKeyboardLayoutToDisplayNameMap =
     47             new HashMap<String, String>();
     48     // Keyboard layout to subtype name resource id map.
     49     private static final HashMap<String, Integer> sKeyboardLayoutToNameIdsMap =
     50             new HashMap<String, Integer>();
     51     // Exceptional locale to subtype name resource id map.
     52     private static final HashMap<String, Integer> sExceptionalLocaleToWithLayoutNameIdsMap =
     53             new HashMap<String, Integer>();
     54     private static final String SUBTYPE_NAME_RESOURCE_GENERIC_PREFIX =
     55             "string/subtype_generic_";
     56     private static final String SUBTYPE_NAME_RESOURCE_WITH_LAYOUT_PREFIX =
     57             "string/subtype_with_layout_";
     58     private static final String SUBTYPE_NAME_RESOURCE_NO_LANGUAGE_PREFIX =
     59             "string/subtype_no_language_";
     60     // Exceptional locales to display name map.
     61     private static final HashMap<String, String> sExceptionalDisplayNamesMap =
     62             new HashMap<String, String>();
     63     // Keyboard layout set name for the subtypes that don't have a keyboardLayoutSet extra value.
     64     // This is for compatibility to keep the same subtype ids as pre-JellyBean.
     65     private static final HashMap<String,String> sLocaleAndExtraValueToKeyboardLayoutSetMap =
     66             new HashMap<String,String>();
     67 
     68     private SubtypeLocale() {
     69         // Intentional empty constructor for utility class.
     70     }
     71 
     72     public static void init(Context context) {
     73         final Resources res = context.getResources();
     74 
     75         final String[] predefinedLayoutSet = res.getStringArray(R.array.predefined_layouts);
     76         sPredefinedKeyboardLayoutSet = predefinedLayoutSet;
     77         final String[] layoutDisplayNames = res.getStringArray(
     78                 R.array.predefined_layout_display_names);
     79         for (int i = 0; i < predefinedLayoutSet.length; i++) {
     80             final String layoutName = predefinedLayoutSet[i];
     81             sKeyboardLayoutToDisplayNameMap.put(layoutName, layoutDisplayNames[i]);
     82             final String resourceName = SUBTYPE_NAME_RESOURCE_GENERIC_PREFIX + layoutName;
     83             final int resId = res.getIdentifier(resourceName, null, RESOURCE_PACKAGE_NAME);
     84             sKeyboardLayoutToNameIdsMap.put(layoutName, resId);
     85             // Register subtype name resource id of "No language" with key "zz_<layout>"
     86             final String noLanguageResName = SUBTYPE_NAME_RESOURCE_NO_LANGUAGE_PREFIX + layoutName;
     87             final int noLanguageResId = res.getIdentifier(
     88                     noLanguageResName, null, RESOURCE_PACKAGE_NAME);
     89             final String key = getNoLanguageLayoutKey(layoutName);
     90             sKeyboardLayoutToNameIdsMap.put(key, noLanguageResId);
     91         }
     92 
     93         final String[] exceptionalLocales = res.getStringArray(
     94                 R.array.subtype_locale_exception_keys);
     95         final String[] exceptionalDisplayNames = res.getStringArray(
     96                 R.array.subtype_locale_exception_values);
     97         for (int i = 0; i < exceptionalLocales.length; i++) {
     98             final String localeString = exceptionalLocales[i];
     99             sExceptionalDisplayNamesMap.put(localeString, exceptionalDisplayNames[i]);
    100             final String resourceName = SUBTYPE_NAME_RESOURCE_WITH_LAYOUT_PREFIX + localeString;
    101             final int resId = res.getIdentifier(resourceName, null, RESOURCE_PACKAGE_NAME);
    102             sExceptionalLocaleToWithLayoutNameIdsMap.put(localeString, resId);
    103         }
    104 
    105         final String[] keyboardLayoutSetMap = res.getStringArray(
    106                 R.array.locale_and_extra_value_to_keyboard_layout_set_map);
    107         for (int i = 0; i < keyboardLayoutSetMap.length; i += 2) {
    108             final String key = keyboardLayoutSetMap[i];
    109             final String keyboardLayoutSet = keyboardLayoutSetMap[i + 1];
    110             sLocaleAndExtraValueToKeyboardLayoutSetMap.put(key, keyboardLayoutSet);
    111         }
    112     }
    113 
    114     public static String[] getPredefinedKeyboardLayoutSet() {
    115         return sPredefinedKeyboardLayoutSet;
    116     }
    117 
    118     public static boolean isExceptionalLocale(String localeString) {
    119         return sExceptionalLocaleToWithLayoutNameIdsMap.containsKey(localeString);
    120     }
    121 
    122     private static final String getNoLanguageLayoutKey(String keyboardLayoutName) {
    123         return NO_LANGUAGE + "_" + keyboardLayoutName;
    124     }
    125 
    126     public static int getSubtypeNameId(String localeString, String keyboardLayoutName) {
    127         if (Build.VERSION.SDK_INT >= /* JELLY_BEAN */ 15 && isExceptionalLocale(localeString)) {
    128             return sExceptionalLocaleToWithLayoutNameIdsMap.get(localeString);
    129         }
    130         final String key = localeString.equals(NO_LANGUAGE)
    131                 ? getNoLanguageLayoutKey(keyboardLayoutName)
    132                 : keyboardLayoutName;
    133         final Integer nameId = sKeyboardLayoutToNameIdsMap.get(key);
    134         return nameId == null ? UNKNOWN_KEYBOARD_LAYOUT : nameId;
    135     }
    136 
    137     public static String getSubtypeLocaleDisplayName(String localeString) {
    138         final String exceptionalValue = sExceptionalDisplayNamesMap.get(localeString);
    139         if (exceptionalValue != null) {
    140             return exceptionalValue;
    141         }
    142         final Locale locale = LocaleUtils.constructLocaleFromString(localeString);
    143         return StringUtils.toTitleCase(locale.getDisplayName(locale), locale);
    144     }
    145 
    146     // InputMethodSubtype's display name in its locale.
    147     //        isAdditionalSubtype (T=true, F=false)
    148     // locale layout |  display name
    149     // ------ ------ - ----------------------
    150     //  en_US qwerty F  English (US)            exception
    151     //  en_GB qwerty F  English (UK)            exception
    152     //  fr    azerty F  Franais
    153     //  fr_CA qwerty F  Franais (Canada)
    154     //  de    qwertz F  Deutsch
    155     //  zz    qwerty F  No language (QWERTY)    in system locale
    156     //  fr    qwertz T  Franais (QWERTZ)
    157     //  de    qwerty T  Deutsch (QWERTY)
    158     //  en_US azerty T  English (US) (AZERTY)
    159     //  zz    azerty T  No language (AZERTY)    in system locale
    160 
    161     public static String getSubtypeDisplayName(final InputMethodSubtype subtype, Resources res) {
    162         final String replacementString = (Build.VERSION.SDK_INT >= /* JELLY_BEAN */ 15
    163                 && subtype.containsExtraValueKey(UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME))
    164                 ? subtype.getExtraValueOf(UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME)
    165                 : getSubtypeLocaleDisplayName(subtype.getLocale());
    166         final int nameResId = subtype.getNameResId();
    167         final RunInLocale<String> getSubtypeName = new RunInLocale<String>() {
    168             @Override
    169             protected String job(Resources res) {
    170                 try {
    171                     return res.getString(nameResId, replacementString);
    172                 } catch (Resources.NotFoundException e) {
    173                     // TODO: Remove this catch when InputMethodManager.getCurrentInputMethodSubtype
    174                     // is fixed.
    175                     Log.w(TAG, "Unknown subtype: mode=" + subtype.getMode()
    176                             + " locale=" + subtype.getLocale()
    177                             + " extra=" + subtype.getExtraValue()
    178                             + "\n" + Utils.getStackTrace());
    179                     return "";
    180                 }
    181             }
    182         };
    183         final Locale locale = isNoLanguage(subtype)
    184                 ? res.getConfiguration().locale : getSubtypeLocale(subtype);
    185         return getSubtypeName.runInLocale(res, locale);
    186     }
    187 
    188     public static boolean isNoLanguage(InputMethodSubtype subtype) {
    189         final String localeString = subtype.getLocale();
    190         return localeString.equals(NO_LANGUAGE);
    191     }
    192 
    193     public static Locale getSubtypeLocale(InputMethodSubtype subtype) {
    194         final String localeString = subtype.getLocale();
    195         return LocaleUtils.constructLocaleFromString(localeString);
    196     }
    197 
    198     public static String getKeyboardLayoutSetDisplayName(InputMethodSubtype subtype) {
    199         final String layoutName = getKeyboardLayoutSetName(subtype);
    200         return getKeyboardLayoutSetDisplayName(layoutName);
    201     }
    202 
    203     public static String getKeyboardLayoutSetDisplayName(String layoutName) {
    204         return sKeyboardLayoutToDisplayNameMap.get(layoutName);
    205     }
    206 
    207     public static String getKeyboardLayoutSetName(InputMethodSubtype subtype) {
    208         String keyboardLayoutSet = subtype.getExtraValueOf(KEYBOARD_LAYOUT_SET);
    209         if (keyboardLayoutSet == null) {
    210             // This subtype doesn't have a keyboardLayoutSet extra value, so lookup its keyboard
    211             // layout set in sLocaleAndExtraValueToKeyboardLayoutSetMap to keep it compatible with
    212             // pre-JellyBean.
    213             final String key = subtype.getLocale() + ":" + subtype.getExtraValue();
    214             keyboardLayoutSet = sLocaleAndExtraValueToKeyboardLayoutSetMap.get(key);
    215         }
    216         // TODO: Remove this null check when InputMethodManager.getCurrentInputMethodSubtype is
    217         // fixed.
    218         if (keyboardLayoutSet == null) {
    219             android.util.Log.w(TAG, "KeyboardLayoutSet not found, use QWERTY: " +
    220                     "locale=" + subtype.getLocale() + " extraValue=" + subtype.getExtraValue());
    221             return QWERTY;
    222         }
    223         return keyboardLayoutSet;
    224     }
    225 }
    226