• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 final 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 boolean sInitialized = false;
45     private static String[] sPredefinedKeyboardLayoutSet;
46     // Keyboard layout to its display name map.
47     private static final HashMap<String, String> sKeyboardLayoutToDisplayNameMap =
48             CollectionUtils.newHashMap();
49     // Keyboard layout to subtype name resource id map.
50     private static final HashMap<String, Integer> sKeyboardLayoutToNameIdsMap =
51             CollectionUtils.newHashMap();
52     // Exceptional locale to subtype name resource id map.
53     private static final HashMap<String, Integer> sExceptionalLocaleToWithLayoutNameIdsMap =
54             CollectionUtils.newHashMap();
55     private static final String SUBTYPE_NAME_RESOURCE_GENERIC_PREFIX =
56             "string/subtype_generic_";
57     private static final String SUBTYPE_NAME_RESOURCE_WITH_LAYOUT_PREFIX =
58             "string/subtype_with_layout_";
59     private static final String SUBTYPE_NAME_RESOURCE_NO_LANGUAGE_PREFIX =
60             "string/subtype_no_language_";
61     // Exceptional locales to display name map.
62     private static final HashMap<String, String> sExceptionalDisplayNamesMap =
63             CollectionUtils.newHashMap();
64     // Keyboard layout set name for the subtypes that don't have a keyboardLayoutSet extra value.
65     // This is for compatibility to keep the same subtype ids as pre-JellyBean.
66     private static final HashMap<String,String> sLocaleAndExtraValueToKeyboardLayoutSetMap =
67             CollectionUtils.newHashMap();
68 
SubtypeLocale()69     private SubtypeLocale() {
70         // Intentional empty constructor for utility class.
71     }
72 
73     // Note that this initialization method can be called multiple times.
init(Context context)74     public static synchronized void init(Context context) {
75         if (sInitialized) return;
76 
77         final Resources res = context.getResources();
78 
79         final String[] predefinedLayoutSet = res.getStringArray(R.array.predefined_layouts);
80         sPredefinedKeyboardLayoutSet = predefinedLayoutSet;
81         final String[] layoutDisplayNames = res.getStringArray(
82                 R.array.predefined_layout_display_names);
83         for (int i = 0; i < predefinedLayoutSet.length; i++) {
84             final String layoutName = predefinedLayoutSet[i];
85             sKeyboardLayoutToDisplayNameMap.put(layoutName, layoutDisplayNames[i]);
86             final String resourceName = SUBTYPE_NAME_RESOURCE_GENERIC_PREFIX + layoutName;
87             final int resId = res.getIdentifier(resourceName, null, RESOURCE_PACKAGE_NAME);
88             sKeyboardLayoutToNameIdsMap.put(layoutName, resId);
89             // Register subtype name resource id of "No language" with key "zz_<layout>"
90             final String noLanguageResName = SUBTYPE_NAME_RESOURCE_NO_LANGUAGE_PREFIX + layoutName;
91             final int noLanguageResId = res.getIdentifier(
92                     noLanguageResName, null, RESOURCE_PACKAGE_NAME);
93             final String key = getNoLanguageLayoutKey(layoutName);
94             sKeyboardLayoutToNameIdsMap.put(key, noLanguageResId);
95         }
96 
97         final String[] exceptionalLocales = res.getStringArray(
98                 R.array.subtype_locale_exception_keys);
99         final String[] exceptionalDisplayNames = res.getStringArray(
100                 R.array.subtype_locale_exception_values);
101         for (int i = 0; i < exceptionalLocales.length; i++) {
102             final String localeString = exceptionalLocales[i];
103             sExceptionalDisplayNamesMap.put(localeString, exceptionalDisplayNames[i]);
104             final String resourceName = SUBTYPE_NAME_RESOURCE_WITH_LAYOUT_PREFIX + localeString;
105             final int resId = res.getIdentifier(resourceName, null, RESOURCE_PACKAGE_NAME);
106             sExceptionalLocaleToWithLayoutNameIdsMap.put(localeString, resId);
107         }
108 
109         final String[] keyboardLayoutSetMap = res.getStringArray(
110                 R.array.locale_and_extra_value_to_keyboard_layout_set_map);
111         for (int i = 0; i < keyboardLayoutSetMap.length; i += 2) {
112             final String key = keyboardLayoutSetMap[i];
113             final String keyboardLayoutSet = keyboardLayoutSetMap[i + 1];
114             sLocaleAndExtraValueToKeyboardLayoutSetMap.put(key, keyboardLayoutSet);
115         }
116 
117         sInitialized = true;
118     }
119 
getPredefinedKeyboardLayoutSet()120     public static String[] getPredefinedKeyboardLayoutSet() {
121         return sPredefinedKeyboardLayoutSet;
122     }
123 
isExceptionalLocale(String localeString)124     public static boolean isExceptionalLocale(String localeString) {
125         return sExceptionalLocaleToWithLayoutNameIdsMap.containsKey(localeString);
126     }
127 
getNoLanguageLayoutKey(String keyboardLayoutName)128     private static final String getNoLanguageLayoutKey(String keyboardLayoutName) {
129         return NO_LANGUAGE + "_" + keyboardLayoutName;
130     }
131 
getSubtypeNameId(String localeString, String keyboardLayoutName)132     public static int getSubtypeNameId(String localeString, String keyboardLayoutName) {
133         if (Build.VERSION.SDK_INT >= /* JELLY_BEAN */ 15 && isExceptionalLocale(localeString)) {
134             return sExceptionalLocaleToWithLayoutNameIdsMap.get(localeString);
135         }
136         final String key = localeString.equals(NO_LANGUAGE)
137                 ? getNoLanguageLayoutKey(keyboardLayoutName)
138                 : keyboardLayoutName;
139         final Integer nameId = sKeyboardLayoutToNameIdsMap.get(key);
140         return nameId == null ? UNKNOWN_KEYBOARD_LAYOUT : nameId;
141     }
142 
getSubtypeLocaleDisplayName(String localeString)143     public static String getSubtypeLocaleDisplayName(String localeString) {
144         final String exceptionalValue = sExceptionalDisplayNamesMap.get(localeString);
145         if (exceptionalValue != null) {
146             return exceptionalValue;
147         }
148         final Locale locale = LocaleUtils.constructLocaleFromString(localeString);
149         return StringUtils.toTitleCase(locale.getDisplayName(locale), locale);
150     }
151 
152     // InputMethodSubtype's display name in its locale.
153     //        isAdditionalSubtype (T=true, F=false)
154     // locale layout |  display name
155     // ------ ------ - ----------------------
156     //  en_US qwerty F  English (US)            exception
157     //  en_GB qwerty F  English (UK)            exception
158     //  fr    azerty F  Français
159     //  fr_CA qwerty F  Français (Canada)
160     //  de    qwertz F  Deutsch
161     //  zz    qwerty F  No language (QWERTY)    in system locale
162     //  fr    qwertz T  Français (QWERTZ)
163     //  de    qwerty T  Deutsch (QWERTY)
164     //  en_US azerty T  English (US) (AZERTY)
165     //  zz    azerty T  No language (AZERTY)    in system locale
166 
getSubtypeDisplayName(final InputMethodSubtype subtype, Resources res)167     public static String getSubtypeDisplayName(final InputMethodSubtype subtype, Resources res) {
168         final String replacementString = (Build.VERSION.SDK_INT >= /* JELLY_BEAN */ 15
169                 && subtype.containsExtraValueKey(UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME))
170                 ? subtype.getExtraValueOf(UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME)
171                 : getSubtypeLocaleDisplayName(subtype.getLocale());
172         final int nameResId = subtype.getNameResId();
173         final RunInLocale<String> getSubtypeName = new RunInLocale<String>() {
174             @Override
175             protected String job(Resources res) {
176                 try {
177                     return res.getString(nameResId, replacementString);
178                 } catch (Resources.NotFoundException e) {
179                     // TODO: Remove this catch when InputMethodManager.getCurrentInputMethodSubtype
180                     // is fixed.
181                     Log.w(TAG, "Unknown subtype: mode=" + subtype.getMode()
182                             + " locale=" + subtype.getLocale()
183                             + " extra=" + subtype.getExtraValue()
184                             + "\n" + Utils.getStackTrace());
185                     return "";
186                 }
187             }
188         };
189         final Locale locale = isNoLanguage(subtype)
190                 ? res.getConfiguration().locale : getSubtypeLocale(subtype);
191         return getSubtypeName.runInLocale(res, locale);
192     }
193 
isNoLanguage(InputMethodSubtype subtype)194     public static boolean isNoLanguage(InputMethodSubtype subtype) {
195         final String localeString = subtype.getLocale();
196         return localeString.equals(NO_LANGUAGE);
197     }
198 
getSubtypeLocale(InputMethodSubtype subtype)199     public static Locale getSubtypeLocale(InputMethodSubtype subtype) {
200         final String localeString = subtype.getLocale();
201         return LocaleUtils.constructLocaleFromString(localeString);
202     }
203 
getKeyboardLayoutSetDisplayName(InputMethodSubtype subtype)204     public static String getKeyboardLayoutSetDisplayName(InputMethodSubtype subtype) {
205         final String layoutName = getKeyboardLayoutSetName(subtype);
206         return getKeyboardLayoutSetDisplayName(layoutName);
207     }
208 
getKeyboardLayoutSetDisplayName(String layoutName)209     public static String getKeyboardLayoutSetDisplayName(String layoutName) {
210         return sKeyboardLayoutToDisplayNameMap.get(layoutName);
211     }
212 
getKeyboardLayoutSetName(InputMethodSubtype subtype)213     public static String getKeyboardLayoutSetName(InputMethodSubtype subtype) {
214         String keyboardLayoutSet = subtype.getExtraValueOf(KEYBOARD_LAYOUT_SET);
215         if (keyboardLayoutSet == null) {
216             // This subtype doesn't have a keyboardLayoutSet extra value, so lookup its keyboard
217             // layout set in sLocaleAndExtraValueToKeyboardLayoutSetMap to keep it compatible with
218             // pre-JellyBean.
219             final String key = subtype.getLocale() + ":" + subtype.getExtraValue();
220             keyboardLayoutSet = sLocaleAndExtraValueToKeyboardLayoutSetMap.get(key);
221         }
222         // TODO: Remove this null check when InputMethodManager.getCurrentInputMethodSubtype is
223         // fixed.
224         if (keyboardLayoutSet == null) {
225             android.util.Log.w(TAG, "KeyboardLayoutSet not found, use QWERTY: " +
226                     "locale=" + subtype.getLocale() + " extraValue=" + subtype.getExtraValue());
227             return QWERTY;
228         }
229         return keyboardLayoutSet;
230     }
231 }
232