blob: 2ab30246662caf1acd903dc5912bc6b759aff5c1 [file] [log] [blame]
satokab751aa2010-09-14 19:17:36 +09001/*
2 * Copyright (C) 2010 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
17package android.view.inputmethod;
18
satok7265d9b2011-02-14 15:47:30 +090019import android.content.Context;
satok4f313532011-06-01 16:13:45 +090020import android.content.pm.ApplicationInfo;
satokab751aa2010-09-14 19:17:36 +090021import android.os.Parcel;
22import android.os.Parcelable;
satok4f313532011-06-01 16:13:45 +090023import android.text.TextUtils;
satok9c4cc032011-02-14 18:03:32 +090024import android.util.Slog;
satokab751aa2010-09-14 19:17:36 +090025
satok7265d9b2011-02-14 15:47:30 +090026import java.util.ArrayList;
satokab751aa2010-09-14 19:17:36 +090027import java.util.Arrays;
satok9c4cc032011-02-14 18:03:32 +090028import java.util.HashMap;
satok7265d9b2011-02-14 15:47:30 +090029import java.util.HashSet;
satok83e675f2012-03-26 12:09:44 +090030import java.util.IllegalFormatException;
satok7265d9b2011-02-14 15:47:30 +090031import java.util.List;
satoka9778d42011-06-08 19:11:14 +090032import java.util.Locale;
satokab751aa2010-09-14 19:17:36 +090033
34/**
Ken Wakasa9a744762012-01-06 10:41:41 +090035 * This class is used to specify meta information of a subtype contained in an input method editor
36 * (IME). Subtype can describe locale (e.g. en_US, fr_FR...) and mode (e.g. voice, keyboard...),
37 * and is used for IME switch and settings. The input method subtype allows the system to bring up
38 * the specified subtype of the designated IME directly.
satok44b75032011-10-14 14:48:59 +090039 *
Ken Wakasa9a744762012-01-06 10:41:41 +090040 * <p>It should be defined in an XML resource file of the input method with the
Scott Main5df06312013-10-18 16:07:47 -070041 * <code>&lt;subtype&gt;</code> element, which resides within an {@code &lt;input-method>} element.
42 * For more information, see the guide to
43 * <a href="{@docRoot}guide/topics/text/creating-input-method.html">
satok44b75032011-10-14 14:48:59 +090044 * Creating an Input Method</a>.</p>
Scott Main5df06312013-10-18 16:07:47 -070045 *
46 * @see InputMethodInfo
47 *
48 * @attr ref android.R.styleable#InputMethod_Subtype_label
49 * @attr ref android.R.styleable#InputMethod_Subtype_icon
50 * @attr ref android.R.styleable#InputMethod_Subtype_imeSubtypeLocale
51 * @attr ref android.R.styleable#InputMethod_Subtype_imeSubtypeMode
52 * @attr ref android.R.styleable#InputMethod_Subtype_imeSubtypeExtraValue
53 * @attr ref android.R.styleable#InputMethod_Subtype_isAuxiliary
54 * @attr ref android.R.styleable#InputMethod_Subtype_overridesImplicitlyEnabledSubtype
55 * @attr ref android.R.styleable#InputMethod_Subtype_subtypeId
56 * @attr ref android.R.styleable#InputMethod_Subtype_isAsciiCapable
satokab751aa2010-09-14 19:17:36 +090057 */
58public final class InputMethodSubtype implements Parcelable {
satok9c4cc032011-02-14 18:03:32 +090059 private static final String TAG = InputMethodSubtype.class.getSimpleName();
60 private static final String EXTRA_VALUE_PAIR_SEPARATOR = ",";
61 private static final String EXTRA_VALUE_KEY_VALUE_SEPARATOR = "=";
satok83e675f2012-03-26 12:09:44 +090062 // TODO: remove this
63 private static final String EXTRA_KEY_UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME =
64 "UntranslatableReplacementStringInSubtypeName";
satok9c4cc032011-02-14 18:03:32 +090065
satok9aabb952011-05-06 15:41:37 +090066 private final boolean mIsAuxiliary;
satoka86f5e42011-09-02 17:12:42 +090067 private final boolean mOverridesImplicitlyEnabledSubtype;
Satoshi Kataokadc8abf62013-03-12 14:40:04 +090068 private final boolean mIsAsciiCapable;
satok9aabb952011-05-06 15:41:37 +090069 private final int mSubtypeHashCode;
satokab751aa2010-09-14 19:17:36 +090070 private final int mSubtypeIconResId;
satok9aabb952011-05-06 15:41:37 +090071 private final int mSubtypeNameResId;
Satoshi Kataokae62e6d82012-07-02 18:45:43 +090072 private final int mSubtypeId;
satokab751aa2010-09-14 19:17:36 +090073 private final String mSubtypeLocale;
satok9ef02832010-11-04 21:17:48 +090074 private final String mSubtypeMode;
satokab751aa2010-09-14 19:17:36 +090075 private final String mSubtypeExtraValue;
satoke52eb4e2012-05-08 14:45:09 +090076 private volatile HashMap<String, String> mExtraValueHashMapCache;
satokab751aa2010-09-14 19:17:36 +090077
78 /**
Satoshi Kataokadc8abf62013-03-12 14:40:04 +090079 * InputMethodSubtypeBuilder is a builder class of InputMethodSubtype.
80 * This class is designed to be used with
81 * {@link android.view.inputmethod.InputMethodManager#setAdditionalInputMethodSubtypes}.
82 * The developer needs to be aware of what each parameter means.
83 */
84 public static class InputMethodSubtypeBuilder {
85 /**
86 * @param isAuxiliary should true when this subtype is auxiliary, false otherwise.
87 * An auxiliary subtype has the following differences with a regular subtype:
88 * - An auxiliary subtype cannot be chosen as the default IME in Settings.
89 * - The framework will never switch to this subtype through
90 * {@link android.view.inputmethod.InputMethodManager#switchToLastInputMethod}.
91 * Note that the subtype will still be available in the IME switcher.
92 * The intent is to allow for IMEs to specify they are meant to be invoked temporarily
93 * in a one-shot way, and to return to the previous IME once finished (e.g. voice input).
94 */
95 public InputMethodSubtypeBuilder setIsAuxiliary(boolean isAuxiliary) {
96 mIsAuxiliary = isAuxiliary;
97 return this;
98 }
99 private boolean mIsAuxiliary = false;
100
101 /**
102 * @param overridesImplicitlyEnabledSubtype should be true if this subtype should be
103 * enabled by default if no other subtypes in the IME are enabled explicitly. Note that a
104 * subtype with this parameter set will not be shown in the list of subtypes in each IME's
105 * subtype enabler. A canonical use of this would be for an IME to supply an "automatic"
106 * subtype that adapts to the current system language.
107 */
108 public InputMethodSubtypeBuilder setOverridesImplicitlyEnabledSubtype(
109 boolean overridesImplicitlyEnabledSubtype) {
110 mOverridesImplicitlyEnabledSubtype = overridesImplicitlyEnabledSubtype;
111 return this;
112 }
113 private boolean mOverridesImplicitlyEnabledSubtype = false;
114
115 /**
116 * @param isAsciiCapable should be true if this subtype is ASCII capable. If the subtype
117 * is ASCII capable, it should guarantee that the user can input ASCII characters with
118 * this subtype. This is important because many password fields only allow
119 * ASCII-characters.
120 */
121 public InputMethodSubtypeBuilder setIsAsciiCapable(boolean isAsciiCapable) {
122 mIsAsciiCapable = isAsciiCapable;
123 return this;
124 }
125 private boolean mIsAsciiCapable = false;
126
127 /**
128 * @param subtypeIconResId is a resource ID of the subtype icon drawable.
129 */
130 public InputMethodSubtypeBuilder setSubtypeIconResId(int subtypeIconResId) {
131 mSubtypeIconResId = subtypeIconResId;
132 return this;
133 }
134 private int mSubtypeIconResId = 0;
135
136 /**
137 * @param subtypeNameResId is the resource ID of the subtype name string.
138 * The string resource may have exactly one %s in it. If present,
139 * the %s part will be replaced with the locale's display name by
140 * the formatter. Please refer to {@link #getDisplayName} for details.
141 */
142 public InputMethodSubtypeBuilder setSubtypeNameResId(int subtypeNameResId) {
143 mSubtypeNameResId = subtypeNameResId;
144 return this;
145 }
146 private int mSubtypeNameResId = 0;
147
148 /**
149 * @param subtypeId is the unique ID for this subtype. The input method framework keeps
150 * track of enabled subtypes by ID. When the IME package gets upgraded, enabled IDs will
151 * stay enabled even if other attributes are different. If the ID is unspecified or 0,
152 * Arrays.hashCode(new Object[] {locale, mode, extraValue,
153 * isAuxiliary, overridesImplicitlyEnabledSubtype}) will be used instead.
154 */
155 public InputMethodSubtypeBuilder setSubtypeId(int subtypeId) {
156 mSubtypeId = subtypeId;
157 return this;
158 }
159 private int mSubtypeId = 0;
160
161 /**
162 * @param subtypeLocale is the locale supported by this subtype.
163 */
164 public InputMethodSubtypeBuilder setSubtypeLocale(String subtypeLocale) {
165 mSubtypeLocale = subtypeLocale == null ? "" : subtypeLocale;
166 return this;
167 }
168 private String mSubtypeLocale = "";
169
170 /**
171 * @param subtypeMode is the mode supported by this subtype.
172 */
173 public InputMethodSubtypeBuilder setSubtypeMode(String subtypeMode) {
174 mSubtypeMode = subtypeMode == null ? "" : subtypeMode;
175 return this;
176 }
177 private String mSubtypeMode = "";
178 /**
179 * @param subtypeExtraValue is the extra value of the subtype. This string is free-form,
180 * but the API supplies tools to deal with a key-value comma-separated list; see
181 * {@link #containsExtraValueKey} and {@link #getExtraValueOf}.
182 */
183 public InputMethodSubtypeBuilder setSubtypeExtraValue(String subtypeExtraValue) {
184 mSubtypeExtraValue = subtypeExtraValue == null ? "" : subtypeExtraValue;
185 return this;
186 }
187 private String mSubtypeExtraValue = "";
188
189 /**
190 * @return InputMethodSubtype using parameters in this InputMethodSubtypeBuilder.
191 */
192 public InputMethodSubtype build() {
193 return new InputMethodSubtype(this);
194 }
195 }
196
197 private static InputMethodSubtypeBuilder getBuilder(int nameId, int iconId, String locale,
198 String mode, String extraValue, boolean isAuxiliary,
199 boolean overridesImplicitlyEnabledSubtype, int id, boolean isAsciiCapable) {
200 final InputMethodSubtypeBuilder builder = new InputMethodSubtypeBuilder();
201 builder.mSubtypeNameResId = nameId;
202 builder.mSubtypeIconResId = iconId;
203 builder.mSubtypeLocale = locale;
204 builder.mSubtypeMode = mode;
205 builder.mSubtypeExtraValue = extraValue;
206 builder.mIsAuxiliary = isAuxiliary;
207 builder.mOverridesImplicitlyEnabledSubtype = overridesImplicitlyEnabledSubtype;
208 builder.mSubtypeId = id;
209 builder.mIsAsciiCapable = isAsciiCapable;
210 return builder;
211 }
212
213 /**
Satoshi Kataokae62e6d82012-07-02 18:45:43 +0900214 * Constructor with no subtype ID specified, overridesImplicitlyEnabledSubtype not specified.
Satoshi Kataokadc8abf62013-03-12 14:40:04 +0900215 * Arguments for this constructor have the same meanings as
216 * {@link InputMethodSubtype#InputMethodSubtype(int, int, String, String, String, boolean,
217 * boolean, int)} except "id" and "overridesImplicitlyEnabledSubtype".
satok3889e492011-09-02 14:59:06 +0900218 * @hide
satokab751aa2010-09-14 19:17:36 +0900219 */
satoka86f5e42011-09-02 17:12:42 +0900220 public InputMethodSubtype(int nameId, int iconId, String locale, String mode, String extraValue,
221 boolean isAuxiliary) {
Ken Wakasa8b83a722011-09-09 20:14:22 +0900222 this(nameId, iconId, locale, mode, extraValue, isAuxiliary, false);
satok9aabb952011-05-06 15:41:37 +0900223 }
224
225 /**
Satoshi Kataokae62e6d82012-07-02 18:45:43 +0900226 * Constructor with no subtype ID specified.
Satoshi Kataokadc8abf62013-03-12 14:40:04 +0900227 * @deprecated use {@link InputMethodSubtypeBuilder} instead.
228 * Arguments for this constructor have the same meanings as
229 * {@link InputMethodSubtype#InputMethodSubtype(int, int, String, String, String, boolean,
230 * boolean, int)} except "id".
satok9aabb952011-05-06 15:41:37 +0900231 */
satoka9778d42011-06-08 19:11:14 +0900232 public InputMethodSubtype(int nameId, int iconId, String locale, String mode, String extraValue,
satoka86f5e42011-09-02 17:12:42 +0900233 boolean isAuxiliary, boolean overridesImplicitlyEnabledSubtype) {
Satoshi Kataokae62e6d82012-07-02 18:45:43 +0900234 this(nameId, iconId, locale, mode, extraValue, isAuxiliary,
235 overridesImplicitlyEnabledSubtype, 0);
236 }
237
238 /**
239 * Constructor.
Satoshi Kataokadc8abf62013-03-12 14:40:04 +0900240 * @deprecated use {@link InputMethodSubtypeBuilder} instead.
241 * "isAsciiCapable" is "false" in this constructor.
Satoshi Kataokae62e6d82012-07-02 18:45:43 +0900242 * @param nameId Resource ID of the subtype name string. The string resource may have exactly
243 * one %s in it. If there is, the %s part will be replaced with the locale's display name by
244 * the formatter. Please refer to {@link #getDisplayName} for details.
245 * @param iconId Resource ID of the subtype icon drawable.
246 * @param locale The locale supported by the subtype
247 * @param mode The mode supported by the subtype
248 * @param extraValue The extra value of the subtype. This string is free-form, but the API
249 * supplies tools to deal with a key-value comma-separated list; see
250 * {@link #containsExtraValueKey} and {@link #getExtraValueOf}.
251 * @param isAuxiliary true when this subtype is auxiliary, false otherwise. An auxiliary
252 * subtype will not be shown in the list of enabled IMEs for choosing the current IME in
253 * the Settings even when this subtype is enabled. Please note that this subtype will still
254 * be shown in the list of IMEs in the IME switcher to allow the user to tentatively switch
255 * to this subtype while an IME is shown. The framework will never switch the current IME to
256 * this subtype by {@link android.view.inputmethod.InputMethodManager#switchToLastInputMethod}.
257 * The intent of having this flag is to allow for IMEs that are invoked in a one-shot way as
258 * auxiliary input mode, and return to the previous IME once it is finished (e.g. voice input).
259 * @param overridesImplicitlyEnabledSubtype true when this subtype should be enabled by default
260 * if no other subtypes in the IME are enabled explicitly. Note that a subtype with this
261 * parameter being true will not be shown in the list of subtypes in each IME's subtype enabler.
262 * Having an "automatic" subtype is an example use of this flag.
263 * @param id The unique ID for the subtype. The input method framework keeps track of enabled
264 * subtypes by ID. When the IME package gets upgraded, enabled IDs will stay enabled even if
265 * other attributes are different. If the ID is unspecified or 0,
266 * Arrays.hashCode(new Object[] {locale, mode, extraValue,
267 * isAuxiliary, overridesImplicitlyEnabledSubtype}) will be used instead.
268 */
269 public InputMethodSubtype(int nameId, int iconId, String locale, String mode, String extraValue,
270 boolean isAuxiliary, boolean overridesImplicitlyEnabledSubtype, int id) {
Satoshi Kataokadc8abf62013-03-12 14:40:04 +0900271 this(getBuilder(nameId, iconId, locale, mode, extraValue, isAuxiliary,
272 overridesImplicitlyEnabledSubtype, id, false));
273 }
274
275 /**
276 * Constructor.
277 * @param builder Builder for InputMethodSubtype
278 */
279 private InputMethodSubtype(InputMethodSubtypeBuilder builder) {
280 mSubtypeNameResId = builder.mSubtypeNameResId;
281 mSubtypeIconResId = builder.mSubtypeIconResId;
282 mSubtypeLocale = builder.mSubtypeLocale;
283 mSubtypeMode = builder.mSubtypeMode;
284 mSubtypeExtraValue = builder.mSubtypeExtraValue;
285 mIsAuxiliary = builder.mIsAuxiliary;
286 mOverridesImplicitlyEnabledSubtype = builder.mOverridesImplicitlyEnabledSubtype;
287 mSubtypeId = builder.mSubtypeId;
288 mIsAsciiCapable = builder.mIsAsciiCapable;
Satoshi Kataokae62e6d82012-07-02 18:45:43 +0900289 // If hashCode() of this subtype is 0 and you want to specify it as an id of this subtype,
290 // just specify 0 as this subtype's id. Then, this subtype's id is treated as 0.
Satoshi Kataokadc8abf62013-03-12 14:40:04 +0900291 mSubtypeHashCode = mSubtypeId != 0 ? mSubtypeId : hashCodeInternal(mSubtypeLocale,
292 mSubtypeMode, mSubtypeExtraValue, mIsAuxiliary, mOverridesImplicitlyEnabledSubtype,
293 mIsAsciiCapable);
satokab751aa2010-09-14 19:17:36 +0900294 }
295
296 InputMethodSubtype(Parcel source) {
satokaf4bf402010-11-30 14:02:49 +0900297 String s;
satokab751aa2010-09-14 19:17:36 +0900298 mSubtypeNameResId = source.readInt();
299 mSubtypeIconResId = source.readInt();
satokaf4bf402010-11-30 14:02:49 +0900300 s = source.readString();
301 mSubtypeLocale = s != null ? s : "";
302 s = source.readString();
303 mSubtypeMode = s != null ? s : "";
304 s = source.readString();
305 mSubtypeExtraValue = s != null ? s : "";
satok9aabb952011-05-06 15:41:37 +0900306 mIsAuxiliary = (source.readInt() == 1);
satoka86f5e42011-09-02 17:12:42 +0900307 mOverridesImplicitlyEnabledSubtype = (source.readInt() == 1);
Satoshi Kataokae62e6d82012-07-02 18:45:43 +0900308 mSubtypeHashCode = source.readInt();
309 mSubtypeId = source.readInt();
Satoshi Kataokadc8abf62013-03-12 14:40:04 +0900310 mIsAsciiCapable = (source.readInt() == 1);
satokab751aa2010-09-14 19:17:36 +0900311 }
312
313 /**
Ken Wakasa9a744762012-01-06 10:41:41 +0900314 * @return Resource ID of the subtype name string.
satokab751aa2010-09-14 19:17:36 +0900315 */
316 public int getNameResId() {
317 return mSubtypeNameResId;
318 }
319
320 /**
Ken Wakasa9a744762012-01-06 10:41:41 +0900321 * @return Resource ID of the subtype icon drawable.
satokab751aa2010-09-14 19:17:36 +0900322 */
323 public int getIconResId() {
324 return mSubtypeIconResId;
325 }
326
327 /**
Ken Wakasa9a744762012-01-06 10:41:41 +0900328 * @return The locale of the subtype. This method returns the "locale" string parameter passed
329 * to the constructor.
satokab751aa2010-09-14 19:17:36 +0900330 */
331 public String getLocale() {
332 return mSubtypeLocale;
333 }
334
335 /**
Ken Wakasa9a744762012-01-06 10:41:41 +0900336 * @return The mode of the subtype.
satokab751aa2010-09-14 19:17:36 +0900337 */
satok9ef02832010-11-04 21:17:48 +0900338 public String getMode() {
339 return mSubtypeMode;
satokab751aa2010-09-14 19:17:36 +0900340 }
341
342 /**
Ken Wakasa9a744762012-01-06 10:41:41 +0900343 * @return The extra value of the subtype.
satokab751aa2010-09-14 19:17:36 +0900344 */
345 public String getExtraValue() {
346 return mSubtypeExtraValue;
347 }
348
satok9aabb952011-05-06 15:41:37 +0900349 /**
Ken Wakasa9a744762012-01-06 10:41:41 +0900350 * @return true if this subtype is auxiliary, false otherwise. An auxiliary subtype will not be
351 * shown in the list of enabled IMEs for choosing the current IME in the Settings even when this
352 * subtype is enabled. Please note that this subtype will still be shown in the list of IMEs in
353 * the IME switcher to allow the user to tentatively switch to this subtype while an IME is
354 * shown. The framework will never switch the current IME to this subtype by
355 * {@link android.view.inputmethod.InputMethodManager#switchToLastInputMethod}.
356 * The intent of having this flag is to allow for IMEs that are invoked in a one-shot way as
357 * auxiliary input mode, and return to the previous IME once it is finished (e.g. voice input).
satok9aabb952011-05-06 15:41:37 +0900358 */
359 public boolean isAuxiliary() {
360 return mIsAuxiliary;
361 }
362
satok4f313532011-06-01 16:13:45 +0900363 /**
Ken Wakasa9a744762012-01-06 10:41:41 +0900364 * @return true when this subtype will be enabled by default if no other subtypes in the IME
365 * are enabled explicitly, false otherwise. Note that a subtype with this method returning true
366 * will not be shown in the list of subtypes in each IME's subtype enabler. Having an
367 * "automatic" subtype is an example use of this flag.
satoka86f5e42011-09-02 17:12:42 +0900368 */
369 public boolean overridesImplicitlyEnabledSubtype() {
370 return mOverridesImplicitlyEnabledSubtype;
371 }
372
373 /**
Satoshi Kataokadc8abf62013-03-12 14:40:04 +0900374 * @return true if this subtype is Ascii capable, false otherwise. If the subtype is ASCII
375 * capable, it should guarantee that the user can input ASCII characters with this subtype.
376 * This is important because many password fields only allow ASCII-characters.
377 */
378 public boolean isAsciiCapable() {
379 return mIsAsciiCapable;
380 }
381
382 /**
satok4f313532011-06-01 16:13:45 +0900383 * @param context Context will be used for getting Locale and PackageManager.
384 * @param packageName The package name of the IME
385 * @param appInfo The application info of the IME
386 * @return a display name for this subtype. The string resource of the label (mSubtypeNameResId)
Ken Wakasa9a744762012-01-06 10:41:41 +0900387 * may have exactly one %s in it. If there is, the %s part will be replaced with the locale's
388 * display name by the formatter. If there is not, this method returns the string specified by
389 * mSubtypeNameResId. If mSubtypeNameResId is not specified (== 0), it's up to the framework to
390 * generate an appropriate display name.
satok4f313532011-06-01 16:13:45 +0900391 */
392 public CharSequence getDisplayName(
393 Context context, String packageName, ApplicationInfo appInfo) {
satoka9778d42011-06-08 19:11:14 +0900394 final Locale locale = constructLocaleFromString(mSubtypeLocale);
395 final String localeStr = locale != null ? locale.getDisplayName() : mSubtypeLocale;
satok4f313532011-06-01 16:13:45 +0900396 if (mSubtypeNameResId == 0) {
satoka9778d42011-06-08 19:11:14 +0900397 return localeStr;
satok4f313532011-06-01 16:13:45 +0900398 }
satok35412d62011-07-04 17:37:09 +0900399 final CharSequence subtypeName = context.getPackageManager().getText(
400 packageName, mSubtypeNameResId, appInfo);
satok4f313532011-06-01 16:13:45 +0900401 if (!TextUtils.isEmpty(subtypeName)) {
satok83e675f2012-03-26 12:09:44 +0900402 final String replacementString =
403 containsExtraValueKey(EXTRA_KEY_UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME)
404 ? getExtraValueOf(EXTRA_KEY_UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME)
405 : localeStr;
406 try {
407 return String.format(
408 subtypeName.toString(), replacementString != null ? replacementString : "");
409 } catch (IllegalFormatException e) {
410 Slog.w(TAG, "Found illegal format in subtype name("+ subtypeName + "): " + e);
411 return "";
412 }
satok4f313532011-06-01 16:13:45 +0900413 } else {
satoka9778d42011-06-08 19:11:14 +0900414 return localeStr;
satok4f313532011-06-01 16:13:45 +0900415 }
416 }
417
satok9c4cc032011-02-14 18:03:32 +0900418 private HashMap<String, String> getExtraValueHashMap() {
419 if (mExtraValueHashMapCache == null) {
satoke52eb4e2012-05-08 14:45:09 +0900420 synchronized(this) {
421 if (mExtraValueHashMapCache == null) {
422 mExtraValueHashMapCache = new HashMap<String, String>();
423 final String[] pairs = mSubtypeExtraValue.split(EXTRA_VALUE_PAIR_SEPARATOR);
424 final int N = pairs.length;
425 for (int i = 0; i < N; ++i) {
426 final String[] pair = pairs[i].split(EXTRA_VALUE_KEY_VALUE_SEPARATOR);
427 if (pair.length == 1) {
428 mExtraValueHashMapCache.put(pair[0], null);
429 } else if (pair.length > 1) {
430 if (pair.length > 2) {
431 Slog.w(TAG, "ExtraValue has two or more '='s");
432 }
433 mExtraValueHashMapCache.put(pair[0], pair[1]);
434 }
satok9c4cc032011-02-14 18:03:32 +0900435 }
satok9c4cc032011-02-14 18:03:32 +0900436 }
437 }
438 }
439 return mExtraValueHashMapCache;
440 }
441
442 /**
443 * The string of ExtraValue in subtype should be defined as follows:
444 * example: key0,key1=value1,key2,key3,key4=value4
Ken Wakasa9a744762012-01-06 10:41:41 +0900445 * @param key The key of extra value
446 * @return The subtype contains specified the extra value
satok9c4cc032011-02-14 18:03:32 +0900447 */
448 public boolean containsExtraValueKey(String key) {
449 return getExtraValueHashMap().containsKey(key);
450 }
451
452 /**
453 * The string of ExtraValue in subtype should be defined as follows:
454 * example: key0,key1=value1,key2,key3,key4=value4
Ken Wakasa9a744762012-01-06 10:41:41 +0900455 * @param key The key of extra value
456 * @return The value of the specified key
satok9c4cc032011-02-14 18:03:32 +0900457 */
458 public String getExtraValueOf(String key) {
459 return getExtraValueHashMap().get(key);
460 }
461
satokab751aa2010-09-14 19:17:36 +0900462 @Override
463 public int hashCode() {
464 return mSubtypeHashCode;
465 }
466
467 @Override
468 public boolean equals(Object o) {
469 if (o instanceof InputMethodSubtype) {
470 InputMethodSubtype subtype = (InputMethodSubtype) o;
Satoshi Kataokae62e6d82012-07-02 18:45:43 +0900471 if (subtype.mSubtypeId != 0 || mSubtypeId != 0) {
472 return (subtype.hashCode() == hashCode());
473 }
satokaf4bf402010-11-30 14:02:49 +0900474 return (subtype.hashCode() == hashCode())
475 && (subtype.getNameResId() == getNameResId())
476 && (subtype.getMode().equals(getMode()))
satokab751aa2010-09-14 19:17:36 +0900477 && (subtype.getIconResId() == getIconResId())
478 && (subtype.getLocale().equals(getLocale()))
satoka9778d42011-06-08 19:11:14 +0900479 && (subtype.getExtraValue().equals(getExtraValue()))
Satoshi Kataokadc8abf62013-03-12 14:40:04 +0900480 && (subtype.isAuxiliary() == isAuxiliary())
481 && (subtype.isAsciiCapable() == isAsciiCapable());
satokab751aa2010-09-14 19:17:36 +0900482 }
483 return false;
484 }
485
satok9aabb952011-05-06 15:41:37 +0900486 @Override
satokab751aa2010-09-14 19:17:36 +0900487 public int describeContents() {
488 return 0;
489 }
490
satok9aabb952011-05-06 15:41:37 +0900491 @Override
satokab751aa2010-09-14 19:17:36 +0900492 public void writeToParcel(Parcel dest, int parcelableFlags) {
493 dest.writeInt(mSubtypeNameResId);
494 dest.writeInt(mSubtypeIconResId);
495 dest.writeString(mSubtypeLocale);
satok9ef02832010-11-04 21:17:48 +0900496 dest.writeString(mSubtypeMode);
satokab751aa2010-09-14 19:17:36 +0900497 dest.writeString(mSubtypeExtraValue);
satok9aabb952011-05-06 15:41:37 +0900498 dest.writeInt(mIsAuxiliary ? 1 : 0);
satoka86f5e42011-09-02 17:12:42 +0900499 dest.writeInt(mOverridesImplicitlyEnabledSubtype ? 1 : 0);
Satoshi Kataokae62e6d82012-07-02 18:45:43 +0900500 dest.writeInt(mSubtypeHashCode);
501 dest.writeInt(mSubtypeId);
Satoshi Kataokadc8abf62013-03-12 14:40:04 +0900502 dest.writeInt(mIsAsciiCapable ? 1 : 0);
satokab751aa2010-09-14 19:17:36 +0900503 }
504
505 public static final Parcelable.Creator<InputMethodSubtype> CREATOR
506 = new Parcelable.Creator<InputMethodSubtype>() {
satok9aabb952011-05-06 15:41:37 +0900507 @Override
satokab751aa2010-09-14 19:17:36 +0900508 public InputMethodSubtype createFromParcel(Parcel source) {
509 return new InputMethodSubtype(source);
510 }
511
satok9aabb952011-05-06 15:41:37 +0900512 @Override
satokab751aa2010-09-14 19:17:36 +0900513 public InputMethodSubtype[] newArray(int size) {
514 return new InputMethodSubtype[size];
515 }
516 };
517
satoka9778d42011-06-08 19:11:14 +0900518 private static Locale constructLocaleFromString(String localeStr) {
519 if (TextUtils.isEmpty(localeStr))
520 return null;
521 String[] localeParams = localeStr.split("_", 3);
522 // The length of localeStr is guaranteed to always return a 1 <= value <= 3
523 // because localeStr is not empty.
524 if (localeParams.length == 1) {
525 return new Locale(localeParams[0]);
526 } else if (localeParams.length == 2) {
527 return new Locale(localeParams[0], localeParams[1]);
528 } else if (localeParams.length == 3) {
529 return new Locale(localeParams[0], localeParams[1], localeParams[2]);
530 }
531 return null;
532 }
533
534 private static int hashCodeInternal(String locale, String mode, String extraValue,
Satoshi Kataokadc8abf62013-03-12 14:40:04 +0900535 boolean isAuxiliary, boolean overridesImplicitlyEnabledSubtype,
536 boolean isAsciiCapable) {
Satoshi Kataoka5fd5aa32013-11-14 12:04:51 +0900537 // CAVEAT: Must revisit how to compute needsToCalculateCompatibleHashCode when a new
538 // attribute is added in order to avoid enabled subtypes being unexpectedly disabled.
539 final boolean needsToCalculateCompatibleHashCode = !isAsciiCapable;
540 if (needsToCalculateCompatibleHashCode) {
541 return Arrays.hashCode(new Object[] {locale, mode, extraValue, isAuxiliary,
542 overridesImplicitlyEnabledSubtype});
543 }
satoka86f5e42011-09-02 17:12:42 +0900544 return Arrays.hashCode(new Object[] {locale, mode, extraValue, isAuxiliary,
Satoshi Kataokadc8abf62013-03-12 14:40:04 +0900545 overridesImplicitlyEnabledSubtype, isAsciiCapable});
satokab751aa2010-09-14 19:17:36 +0900546 }
satok7265d9b2011-02-14 15:47:30 +0900547
548 /**
549 * Sort the list of InputMethodSubtype
550 * @param context Context will be used for getting localized strings from IME
551 * @param flags Flags for the sort order
552 * @param imi InputMethodInfo of which subtypes are subject to be sorted
553 * @param subtypeList List of InputMethodSubtype which will be sorted
554 * @return Sorted list of subtypes
555 * @hide
556 */
557 public static List<InputMethodSubtype> sort(Context context, int flags, InputMethodInfo imi,
558 List<InputMethodSubtype> subtypeList) {
559 if (imi == null) return subtypeList;
560 final HashSet<InputMethodSubtype> inputSubtypesSet = new HashSet<InputMethodSubtype>(
561 subtypeList);
562 final ArrayList<InputMethodSubtype> sortedList = new ArrayList<InputMethodSubtype>();
563 int N = imi.getSubtypeCount();
564 for (int i = 0; i < N; ++i) {
565 InputMethodSubtype subtype = imi.getSubtypeAt(i);
566 if (inputSubtypesSet.contains(subtype)) {
567 sortedList.add(subtype);
568 inputSubtypesSet.remove(subtype);
569 }
570 }
571 // If subtypes in inputSubtypesSet remain, that means these subtypes are not
572 // contained in imi, so the remaining subtypes will be appended.
573 for (InputMethodSubtype subtype: inputSubtypesSet) {
574 sortedList.add(subtype);
575 }
576 return sortedList;
577 }
satokaf4bf402010-11-30 14:02:49 +0900578}