• 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 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 
SubtypeLocale()68     private SubtypeLocale() {
69         // Intentional empty constructor for utility class.
70     }
71 
init(Context context)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 
getPredefinedKeyboardLayoutSet()114     public static String[] getPredefinedKeyboardLayoutSet() {
115         return sPredefinedKeyboardLayoutSet;
116     }
117 
isExceptionalLocale(String localeString)118     public static boolean isExceptionalLocale(String localeString) {
119         return sExceptionalLocaleToWithLayoutNameIdsMap.containsKey(localeString);
120     }
121 
getNoLanguageLayoutKey(String keyboardLayoutName)122     private static final String getNoLanguageLayoutKey(String keyboardLayoutName) {
123         return NO_LANGUAGE + "_" + keyboardLayoutName;
124     }
125 
getSubtypeNameId(String localeString, String keyboardLayoutName)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 
getSubtypeLocaleDisplayName(String localeString)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  Français
153     //  fr_CA qwerty F  Français (Canada)
154     //  de    qwertz F  Deutsch
155     //  zz    qwerty F  No language (QWERTY)    in system locale
156     //  fr    qwertz T  Français (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 
getSubtypeDisplayName(final InputMethodSubtype subtype, Resources res)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 
isNoLanguage(InputMethodSubtype subtype)188     public static boolean isNoLanguage(InputMethodSubtype subtype) {
189         final String localeString = subtype.getLocale();
190         return localeString.equals(NO_LANGUAGE);
191     }
192 
getSubtypeLocale(InputMethodSubtype subtype)193     public static Locale getSubtypeLocale(InputMethodSubtype subtype) {
194         final String localeString = subtype.getLocale();
195         return LocaleUtils.constructLocaleFromString(localeString);
196     }
197 
getKeyboardLayoutSetDisplayName(InputMethodSubtype subtype)198     public static String getKeyboardLayoutSetDisplayName(InputMethodSubtype subtype) {
199         final String layoutName = getKeyboardLayoutSetName(subtype);
200         return getKeyboardLayoutSetDisplayName(layoutName);
201     }
202 
getKeyboardLayoutSetDisplayName(String layoutName)203     public static String getKeyboardLayoutSetDisplayName(String layoutName) {
204         return sKeyboardLayoutToDisplayNameMap.get(layoutName);
205     }
206 
getKeyboardLayoutSetName(InputMethodSubtype subtype)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