blob: 59898479c519a5453fe0eacab617a8f4253963d3 [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
Yohei Yukawa868d19b2015-12-07 15:58:57 -080019import android.annotation.NonNull;
Yohei Yukawa92280cd2015-06-02 16:50:14 -070020import android.annotation.Nullable;
satok7265d9b2011-02-14 15:47:30 +090021import android.content.Context;
satok4f313532011-06-01 16:13:45 +090022import android.content.pm.ApplicationInfo;
Yohei Yukawae489baf2016-04-11 22:09:57 -070023import android.content.res.Configuration;
24import android.icu.text.DisplayContext;
25import android.icu.text.LocaleDisplayNames;
satokab751aa2010-09-14 19:17:36 +090026import android.os.Parcel;
27import android.os.Parcelable;
satok4f313532011-06-01 16:13:45 +090028import android.text.TextUtils;
satok9c4cc032011-02-14 18:03:32 +090029import android.util.Slog;
satokab751aa2010-09-14 19:17:36 +090030
Yohei Yukawa835ab942018-09-12 16:23:26 -070031import com.android.internal.inputmethod.SubtypeLocaleUtils;
Yohei Yukawaf487e0e2015-02-21 02:15:48 +090032
satok7265d9b2011-02-14 15:47:30 +090033import java.util.ArrayList;
satokab751aa2010-09-14 19:17:36 +090034import java.util.Arrays;
satok9c4cc032011-02-14 18:03:32 +090035import java.util.HashMap;
satok7265d9b2011-02-14 15:47:30 +090036import java.util.HashSet;
satok83e675f2012-03-26 12:09:44 +090037import java.util.IllegalFormatException;
satok7265d9b2011-02-14 15:47:30 +090038import java.util.List;
satoka9778d42011-06-08 19:11:14 +090039import java.util.Locale;
satokab751aa2010-09-14 19:17:36 +090040
41/**
Ken Wakasa9a744762012-01-06 10:41:41 +090042 * This class is used to specify meta information of a subtype contained in an input method editor
43 * (IME). Subtype can describe locale (e.g. en_US, fr_FR...) and mode (e.g. voice, keyboard...),
44 * and is used for IME switch and settings. The input method subtype allows the system to bring up
45 * the specified subtype of the designated IME directly.
satok44b75032011-10-14 14:48:59 +090046 *
Ken Wakasa9a744762012-01-06 10:41:41 +090047 * <p>It should be defined in an XML resource file of the input method with the
Neil Fuller71fbb812015-11-30 09:51:33 +000048 * <code>&lt;subtype&gt;</code> element, which resides within an {@code <input-method>} element.
Scott Main5df06312013-10-18 16:07:47 -070049 * For more information, see the guide to
50 * <a href="{@docRoot}guide/topics/text/creating-input-method.html">
satok44b75032011-10-14 14:48:59 +090051 * Creating an Input Method</a>.</p>
Scott Main5df06312013-10-18 16:07:47 -070052 *
53 * @see InputMethodInfo
54 *
55 * @attr ref android.R.styleable#InputMethod_Subtype_label
56 * @attr ref android.R.styleable#InputMethod_Subtype_icon
Yohei Yukawa868d19b2015-12-07 15:58:57 -080057 * @attr ref android.R.styleable#InputMethod_Subtype_languageTag
Scott Main5df06312013-10-18 16:07:47 -070058 * @attr ref android.R.styleable#InputMethod_Subtype_imeSubtypeLocale
59 * @attr ref android.R.styleable#InputMethod_Subtype_imeSubtypeMode
60 * @attr ref android.R.styleable#InputMethod_Subtype_imeSubtypeExtraValue
61 * @attr ref android.R.styleable#InputMethod_Subtype_isAuxiliary
62 * @attr ref android.R.styleable#InputMethod_Subtype_overridesImplicitlyEnabledSubtype
63 * @attr ref android.R.styleable#InputMethod_Subtype_subtypeId
64 * @attr ref android.R.styleable#InputMethod_Subtype_isAsciiCapable
satokab751aa2010-09-14 19:17:36 +090065 */
66public final class InputMethodSubtype implements Parcelable {
satok9c4cc032011-02-14 18:03:32 +090067 private static final String TAG = InputMethodSubtype.class.getSimpleName();
Yohei Yukawa868d19b2015-12-07 15:58:57 -080068 private static final String LANGUAGE_TAG_NONE = "";
satok9c4cc032011-02-14 18:03:32 +090069 private static final String EXTRA_VALUE_PAIR_SEPARATOR = ",";
70 private static final String EXTRA_VALUE_KEY_VALUE_SEPARATOR = "=";
satok83e675f2012-03-26 12:09:44 +090071 // TODO: remove this
72 private static final String EXTRA_KEY_UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME =
73 "UntranslatableReplacementStringInSubtypeName";
Yohei Yukawa66baf692016-04-11 02:29:35 -070074 private static final int SUBTYPE_ID_NONE = 0;
satok9c4cc032011-02-14 18:03:32 +090075
satok9aabb952011-05-06 15:41:37 +090076 private final boolean mIsAuxiliary;
satoka86f5e42011-09-02 17:12:42 +090077 private final boolean mOverridesImplicitlyEnabledSubtype;
Satoshi Kataokadc8abf62013-03-12 14:40:04 +090078 private final boolean mIsAsciiCapable;
satok9aabb952011-05-06 15:41:37 +090079 private final int mSubtypeHashCode;
satokab751aa2010-09-14 19:17:36 +090080 private final int mSubtypeIconResId;
satok9aabb952011-05-06 15:41:37 +090081 private final int mSubtypeNameResId;
Satoshi Kataokae62e6d82012-07-02 18:45:43 +090082 private final int mSubtypeId;
satokab751aa2010-09-14 19:17:36 +090083 private final String mSubtypeLocale;
Yohei Yukawa868d19b2015-12-07 15:58:57 -080084 private final String mSubtypeLanguageTag;
satok9ef02832010-11-04 21:17:48 +090085 private final String mSubtypeMode;
satokab751aa2010-09-14 19:17:36 +090086 private final String mSubtypeExtraValue;
Tony Makafcd19c2017-08-07 15:15:57 +010087 private final Object mLock = new Object();
88 private volatile Locale mCachedLocaleObj;
satoke52eb4e2012-05-08 14:45:09 +090089 private volatile HashMap<String, String> mExtraValueHashMapCache;
satokab751aa2010-09-14 19:17:36 +090090
91 /**
Satoshi Kataokadc8abf62013-03-12 14:40:04 +090092 * InputMethodSubtypeBuilder is a builder class of InputMethodSubtype.
93 * This class is designed to be used with
94 * {@link android.view.inputmethod.InputMethodManager#setAdditionalInputMethodSubtypes}.
95 * The developer needs to be aware of what each parameter means.
96 */
97 public static class InputMethodSubtypeBuilder {
98 /**
99 * @param isAuxiliary should true when this subtype is auxiliary, false otherwise.
100 * An auxiliary subtype has the following differences with a regular subtype:
101 * - An auxiliary subtype cannot be chosen as the default IME in Settings.
102 * - The framework will never switch to this subtype through
103 * {@link android.view.inputmethod.InputMethodManager#switchToLastInputMethod}.
104 * Note that the subtype will still be available in the IME switcher.
105 * The intent is to allow for IMEs to specify they are meant to be invoked temporarily
106 * in a one-shot way, and to return to the previous IME once finished (e.g. voice input).
107 */
108 public InputMethodSubtypeBuilder setIsAuxiliary(boolean isAuxiliary) {
109 mIsAuxiliary = isAuxiliary;
110 return this;
111 }
112 private boolean mIsAuxiliary = false;
113
114 /**
115 * @param overridesImplicitlyEnabledSubtype should be true if this subtype should be
116 * enabled by default if no other subtypes in the IME are enabled explicitly. Note that a
117 * subtype with this parameter set will not be shown in the list of subtypes in each IME's
118 * subtype enabler. A canonical use of this would be for an IME to supply an "automatic"
119 * subtype that adapts to the current system language.
120 */
121 public InputMethodSubtypeBuilder setOverridesImplicitlyEnabledSubtype(
122 boolean overridesImplicitlyEnabledSubtype) {
123 mOverridesImplicitlyEnabledSubtype = overridesImplicitlyEnabledSubtype;
124 return this;
125 }
126 private boolean mOverridesImplicitlyEnabledSubtype = false;
127
128 /**
129 * @param isAsciiCapable should be true if this subtype is ASCII capable. If the subtype
130 * is ASCII capable, it should guarantee that the user can input ASCII characters with
131 * this subtype. This is important because many password fields only allow
132 * ASCII-characters.
133 */
134 public InputMethodSubtypeBuilder setIsAsciiCapable(boolean isAsciiCapable) {
135 mIsAsciiCapable = isAsciiCapable;
136 return this;
137 }
138 private boolean mIsAsciiCapable = false;
139
140 /**
141 * @param subtypeIconResId is a resource ID of the subtype icon drawable.
142 */
143 public InputMethodSubtypeBuilder setSubtypeIconResId(int subtypeIconResId) {
144 mSubtypeIconResId = subtypeIconResId;
145 return this;
146 }
147 private int mSubtypeIconResId = 0;
148
149 /**
150 * @param subtypeNameResId is the resource ID of the subtype name string.
151 * The string resource may have exactly one %s in it. If present,
152 * the %s part will be replaced with the locale's display name by
153 * the formatter. Please refer to {@link #getDisplayName} for details.
154 */
155 public InputMethodSubtypeBuilder setSubtypeNameResId(int subtypeNameResId) {
156 mSubtypeNameResId = subtypeNameResId;
157 return this;
158 }
159 private int mSubtypeNameResId = 0;
160
161 /**
162 * @param subtypeId is the unique ID for this subtype. The input method framework keeps
163 * track of enabled subtypes by ID. When the IME package gets upgraded, enabled IDs will
164 * stay enabled even if other attributes are different. If the ID is unspecified or 0,
165 * Arrays.hashCode(new Object[] {locale, mode, extraValue,
Yohei Yukawa66baf692016-04-11 02:29:35 -0700166 * isAuxiliary, overridesImplicitlyEnabledSubtype, isAsciiCapable}) will be used instead.
Satoshi Kataokadc8abf62013-03-12 14:40:04 +0900167 */
168 public InputMethodSubtypeBuilder setSubtypeId(int subtypeId) {
169 mSubtypeId = subtypeId;
170 return this;
171 }
Yohei Yukawa66baf692016-04-11 02:29:35 -0700172 private int mSubtypeId = SUBTYPE_ID_NONE;
Satoshi Kataokadc8abf62013-03-12 14:40:04 +0900173
174 /**
175 * @param subtypeLocale is the locale supported by this subtype.
176 */
177 public InputMethodSubtypeBuilder setSubtypeLocale(String subtypeLocale) {
178 mSubtypeLocale = subtypeLocale == null ? "" : subtypeLocale;
179 return this;
180 }
181 private String mSubtypeLocale = "";
182
183 /**
Yohei Yukawa868d19b2015-12-07 15:58:57 -0800184 * @param languageTag is the BCP-47 Language Tag supported by this subtype.
185 */
186 public InputMethodSubtypeBuilder setLanguageTag(String languageTag) {
187 mSubtypeLanguageTag = languageTag == null ? LANGUAGE_TAG_NONE : languageTag;
188 return this;
189 }
190 private String mSubtypeLanguageTag = LANGUAGE_TAG_NONE;
191
192 /**
Satoshi Kataokadc8abf62013-03-12 14:40:04 +0900193 * @param subtypeMode is the mode supported by this subtype.
194 */
195 public InputMethodSubtypeBuilder setSubtypeMode(String subtypeMode) {
196 mSubtypeMode = subtypeMode == null ? "" : subtypeMode;
197 return this;
198 }
199 private String mSubtypeMode = "";
200 /**
201 * @param subtypeExtraValue is the extra value of the subtype. This string is free-form,
202 * but the API supplies tools to deal with a key-value comma-separated list; see
203 * {@link #containsExtraValueKey} and {@link #getExtraValueOf}.
204 */
205 public InputMethodSubtypeBuilder setSubtypeExtraValue(String subtypeExtraValue) {
206 mSubtypeExtraValue = subtypeExtraValue == null ? "" : subtypeExtraValue;
207 return this;
208 }
209 private String mSubtypeExtraValue = "";
210
211 /**
212 * @return InputMethodSubtype using parameters in this InputMethodSubtypeBuilder.
213 */
214 public InputMethodSubtype build() {
215 return new InputMethodSubtype(this);
216 }
217 }
218
219 private static InputMethodSubtypeBuilder getBuilder(int nameId, int iconId, String locale,
220 String mode, String extraValue, boolean isAuxiliary,
221 boolean overridesImplicitlyEnabledSubtype, int id, boolean isAsciiCapable) {
222 final InputMethodSubtypeBuilder builder = new InputMethodSubtypeBuilder();
223 builder.mSubtypeNameResId = nameId;
224 builder.mSubtypeIconResId = iconId;
225 builder.mSubtypeLocale = locale;
226 builder.mSubtypeMode = mode;
227 builder.mSubtypeExtraValue = extraValue;
228 builder.mIsAuxiliary = isAuxiliary;
229 builder.mOverridesImplicitlyEnabledSubtype = overridesImplicitlyEnabledSubtype;
230 builder.mSubtypeId = id;
231 builder.mIsAsciiCapable = isAsciiCapable;
232 return builder;
233 }
234
235 /**
Satoshi Kataokae62e6d82012-07-02 18:45:43 +0900236 * Constructor with no subtype ID specified.
Satoshi Kataokadc8abf62013-03-12 14:40:04 +0900237 * @deprecated use {@link InputMethodSubtypeBuilder} instead.
238 * Arguments for this constructor have the same meanings as
239 * {@link InputMethodSubtype#InputMethodSubtype(int, int, String, String, String, boolean,
240 * boolean, int)} except "id".
satok9aabb952011-05-06 15:41:37 +0900241 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -0700242 @Deprecated
satoka9778d42011-06-08 19:11:14 +0900243 public InputMethodSubtype(int nameId, int iconId, String locale, String mode, String extraValue,
satoka86f5e42011-09-02 17:12:42 +0900244 boolean isAuxiliary, boolean overridesImplicitlyEnabledSubtype) {
Satoshi Kataokae62e6d82012-07-02 18:45:43 +0900245 this(nameId, iconId, locale, mode, extraValue, isAuxiliary,
246 overridesImplicitlyEnabledSubtype, 0);
247 }
248
249 /**
250 * Constructor.
Satoshi Kataokadc8abf62013-03-12 14:40:04 +0900251 * @deprecated use {@link InputMethodSubtypeBuilder} instead.
252 * "isAsciiCapable" is "false" in this constructor.
Satoshi Kataokae62e6d82012-07-02 18:45:43 +0900253 * @param nameId Resource ID of the subtype name string. The string resource may have exactly
254 * one %s in it. If there is, the %s part will be replaced with the locale's display name by
255 * the formatter. Please refer to {@link #getDisplayName} for details.
256 * @param iconId Resource ID of the subtype icon drawable.
257 * @param locale The locale supported by the subtype
258 * @param mode The mode supported by the subtype
259 * @param extraValue The extra value of the subtype. This string is free-form, but the API
260 * supplies tools to deal with a key-value comma-separated list; see
261 * {@link #containsExtraValueKey} and {@link #getExtraValueOf}.
262 * @param isAuxiliary true when this subtype is auxiliary, false otherwise. An auxiliary
263 * subtype will not be shown in the list of enabled IMEs for choosing the current IME in
264 * the Settings even when this subtype is enabled. Please note that this subtype will still
265 * be shown in the list of IMEs in the IME switcher to allow the user to tentatively switch
266 * to this subtype while an IME is shown. The framework will never switch the current IME to
267 * this subtype by {@link android.view.inputmethod.InputMethodManager#switchToLastInputMethod}.
268 * The intent of having this flag is to allow for IMEs that are invoked in a one-shot way as
269 * auxiliary input mode, and return to the previous IME once it is finished (e.g. voice input).
270 * @param overridesImplicitlyEnabledSubtype true when this subtype should be enabled by default
271 * if no other subtypes in the IME are enabled explicitly. Note that a subtype with this
272 * parameter being true will not be shown in the list of subtypes in each IME's subtype enabler.
273 * Having an "automatic" subtype is an example use of this flag.
274 * @param id The unique ID for the subtype. The input method framework keeps track of enabled
275 * subtypes by ID. When the IME package gets upgraded, enabled IDs will stay enabled even if
276 * other attributes are different. If the ID is unspecified or 0,
277 * Arrays.hashCode(new Object[] {locale, mode, extraValue,
Yohei Yukawa66baf692016-04-11 02:29:35 -0700278 * isAuxiliary, overridesImplicitlyEnabledSubtype, isAsciiCapable}) will be used instead.
Satoshi Kataokae62e6d82012-07-02 18:45:43 +0900279 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -0700280 @Deprecated
Satoshi Kataokae62e6d82012-07-02 18:45:43 +0900281 public InputMethodSubtype(int nameId, int iconId, String locale, String mode, String extraValue,
282 boolean isAuxiliary, boolean overridesImplicitlyEnabledSubtype, int id) {
Satoshi Kataokadc8abf62013-03-12 14:40:04 +0900283 this(getBuilder(nameId, iconId, locale, mode, extraValue, isAuxiliary,
284 overridesImplicitlyEnabledSubtype, id, false));
285 }
286
287 /**
288 * Constructor.
289 * @param builder Builder for InputMethodSubtype
290 */
291 private InputMethodSubtype(InputMethodSubtypeBuilder builder) {
292 mSubtypeNameResId = builder.mSubtypeNameResId;
293 mSubtypeIconResId = builder.mSubtypeIconResId;
294 mSubtypeLocale = builder.mSubtypeLocale;
Yohei Yukawa868d19b2015-12-07 15:58:57 -0800295 mSubtypeLanguageTag = builder.mSubtypeLanguageTag;
Satoshi Kataokadc8abf62013-03-12 14:40:04 +0900296 mSubtypeMode = builder.mSubtypeMode;
297 mSubtypeExtraValue = builder.mSubtypeExtraValue;
298 mIsAuxiliary = builder.mIsAuxiliary;
299 mOverridesImplicitlyEnabledSubtype = builder.mOverridesImplicitlyEnabledSubtype;
300 mSubtypeId = builder.mSubtypeId;
301 mIsAsciiCapable = builder.mIsAsciiCapable;
Satoshi Kataokae62e6d82012-07-02 18:45:43 +0900302 // If hashCode() of this subtype is 0 and you want to specify it as an id of this subtype,
303 // just specify 0 as this subtype's id. Then, this subtype's id is treated as 0.
Yohei Yukawa66baf692016-04-11 02:29:35 -0700304 if (mSubtypeId != SUBTYPE_ID_NONE) {
305 mSubtypeHashCode = mSubtypeId;
306 } else {
307 mSubtypeHashCode = hashCodeInternal(mSubtypeLocale, mSubtypeMode, mSubtypeExtraValue,
308 mIsAuxiliary, mOverridesImplicitlyEnabledSubtype, mIsAsciiCapable);
309 }
satokab751aa2010-09-14 19:17:36 +0900310 }
311
312 InputMethodSubtype(Parcel source) {
satokaf4bf402010-11-30 14:02:49 +0900313 String s;
satokab751aa2010-09-14 19:17:36 +0900314 mSubtypeNameResId = source.readInt();
315 mSubtypeIconResId = source.readInt();
satokaf4bf402010-11-30 14:02:49 +0900316 s = source.readString();
317 mSubtypeLocale = s != null ? s : "";
318 s = source.readString();
Yohei Yukawa868d19b2015-12-07 15:58:57 -0800319 mSubtypeLanguageTag = s != null ? s : LANGUAGE_TAG_NONE;
320 s = source.readString();
satokaf4bf402010-11-30 14:02:49 +0900321 mSubtypeMode = s != null ? s : "";
322 s = source.readString();
323 mSubtypeExtraValue = s != null ? s : "";
satok9aabb952011-05-06 15:41:37 +0900324 mIsAuxiliary = (source.readInt() == 1);
satoka86f5e42011-09-02 17:12:42 +0900325 mOverridesImplicitlyEnabledSubtype = (source.readInt() == 1);
Satoshi Kataokae62e6d82012-07-02 18:45:43 +0900326 mSubtypeHashCode = source.readInt();
327 mSubtypeId = source.readInt();
Satoshi Kataokadc8abf62013-03-12 14:40:04 +0900328 mIsAsciiCapable = (source.readInt() == 1);
satokab751aa2010-09-14 19:17:36 +0900329 }
330
331 /**
Ken Wakasa9a744762012-01-06 10:41:41 +0900332 * @return Resource ID of the subtype name string.
satokab751aa2010-09-14 19:17:36 +0900333 */
334 public int getNameResId() {
335 return mSubtypeNameResId;
336 }
337
338 /**
Ken Wakasa9a744762012-01-06 10:41:41 +0900339 * @return Resource ID of the subtype icon drawable.
satokab751aa2010-09-14 19:17:36 +0900340 */
341 public int getIconResId() {
342 return mSubtypeIconResId;
343 }
344
345 /**
Ken Wakasa9a744762012-01-06 10:41:41 +0900346 * @return The locale of the subtype. This method returns the "locale" string parameter passed
347 * to the constructor.
Yohei Yukawa868d19b2015-12-07 15:58:57 -0800348 *
349 * @deprecated Use {@link #getLanguageTag()} instead.
satokab751aa2010-09-14 19:17:36 +0900350 */
Yohei Yukawa868d19b2015-12-07 15:58:57 -0800351 @Deprecated
352 @NonNull
satokab751aa2010-09-14 19:17:36 +0900353 public String getLocale() {
354 return mSubtypeLocale;
355 }
356
357 /**
Yohei Yukawa868d19b2015-12-07 15:58:57 -0800358 * @return the BCP-47 Language Tag of the subtype. Returns an empty string when no Language Tag
359 * is specified.
Yohei Yukawa92280cd2015-06-02 16:50:14 -0700360 *
Yohei Yukawa868d19b2015-12-07 15:58:57 -0800361 * @see Locale#forLanguageTag(String)
362 */
363 @NonNull
364 public String getLanguageTag() {
365 return mSubtypeLanguageTag;
366 }
367
368 /**
369 * @return {@link Locale} constructed from {@link #getLanguageTag()}. If the Language Tag is not
370 * specified, then try to construct from {@link #getLocale()}
371 *
372 * <p>TODO: Consider to make this a public API, or move this to support lib.</p>
Yohei Yukawa92280cd2015-06-02 16:50:14 -0700373 * @hide
374 */
375 @Nullable
376 public Locale getLocaleObject() {
Tony Makafcd19c2017-08-07 15:15:57 +0100377 if (mCachedLocaleObj != null) {
378 return mCachedLocaleObj;
Yohei Yukawa868d19b2015-12-07 15:58:57 -0800379 }
Tony Makafcd19c2017-08-07 15:15:57 +0100380 synchronized (mLock) {
381 if (mCachedLocaleObj != null) {
382 return mCachedLocaleObj;
383 }
384 if (!TextUtils.isEmpty(mSubtypeLanguageTag)) {
385 mCachedLocaleObj = Locale.forLanguageTag(mSubtypeLanguageTag);
386 } else {
Yohei Yukawa835ab942018-09-12 16:23:26 -0700387 mCachedLocaleObj = SubtypeLocaleUtils.constructLocaleFromString(mSubtypeLocale);
Tony Makafcd19c2017-08-07 15:15:57 +0100388 }
389 return mCachedLocaleObj;
390 }
Yohei Yukawa92280cd2015-06-02 16:50:14 -0700391 }
392
393 /**
Ken Wakasa9a744762012-01-06 10:41:41 +0900394 * @return The mode of the subtype.
satokab751aa2010-09-14 19:17:36 +0900395 */
satok9ef02832010-11-04 21:17:48 +0900396 public String getMode() {
397 return mSubtypeMode;
satokab751aa2010-09-14 19:17:36 +0900398 }
399
400 /**
Ken Wakasa9a744762012-01-06 10:41:41 +0900401 * @return The extra value of the subtype.
satokab751aa2010-09-14 19:17:36 +0900402 */
403 public String getExtraValue() {
404 return mSubtypeExtraValue;
405 }
406
satok9aabb952011-05-06 15:41:37 +0900407 /**
Ken Wakasa9a744762012-01-06 10:41:41 +0900408 * @return true if this subtype is auxiliary, false otherwise. An auxiliary subtype will not be
409 * shown in the list of enabled IMEs for choosing the current IME in the Settings even when this
410 * subtype is enabled. Please note that this subtype will still be shown in the list of IMEs in
411 * the IME switcher to allow the user to tentatively switch to this subtype while an IME is
412 * shown. The framework will never switch the current IME to this subtype by
413 * {@link android.view.inputmethod.InputMethodManager#switchToLastInputMethod}.
414 * The intent of having this flag is to allow for IMEs that are invoked in a one-shot way as
415 * auxiliary input mode, and return to the previous IME once it is finished (e.g. voice input).
satok9aabb952011-05-06 15:41:37 +0900416 */
417 public boolean isAuxiliary() {
418 return mIsAuxiliary;
419 }
420
satok4f313532011-06-01 16:13:45 +0900421 /**
Ken Wakasa9a744762012-01-06 10:41:41 +0900422 * @return true when this subtype will be enabled by default if no other subtypes in the IME
423 * are enabled explicitly, false otherwise. Note that a subtype with this method returning true
424 * will not be shown in the list of subtypes in each IME's subtype enabler. Having an
425 * "automatic" subtype is an example use of this flag.
satoka86f5e42011-09-02 17:12:42 +0900426 */
427 public boolean overridesImplicitlyEnabledSubtype() {
428 return mOverridesImplicitlyEnabledSubtype;
429 }
430
431 /**
Satoshi Kataokadc8abf62013-03-12 14:40:04 +0900432 * @return true if this subtype is Ascii capable, false otherwise. If the subtype is ASCII
433 * capable, it should guarantee that the user can input ASCII characters with this subtype.
434 * This is important because many password fields only allow ASCII-characters.
435 */
436 public boolean isAsciiCapable() {
437 return mIsAsciiCapable;
438 }
439
440 /**
Yohei Yukawae489baf2016-04-11 22:09:57 -0700441 * Returns a display name for this subtype.
442 *
443 * <p>If {@code subtypeNameResId} is specified (!= 0) text generated from that resource will
444 * be returned. The localized string resource of the label should be capitalized for inclusion
445 * in UI lists. The string resource may contain at most one {@code %s}. If present, the
446 * {@code %s} will be replaced with the display name of the subtype locale in the user's locale.
447 *
448 * <p>If {@code subtypeNameResId} is not specified (== 0) the framework returns the display name
449 * of the subtype locale, as capitalized for use in UI lists, in the user's locale.
450 *
451 * @param context {@link Context} will be used for getting {@link Locale} and
452 * {@link android.content.pm.PackageManager}.
453 * @param packageName The package name of the input method.
454 * @param appInfo The {@link ApplicationInfo} of the input method.
455 * @return a display name for this subtype.
satok4f313532011-06-01 16:13:45 +0900456 */
Yohei Yukawae489baf2016-04-11 22:09:57 -0700457 @NonNull
satok4f313532011-06-01 16:13:45 +0900458 public CharSequence getDisplayName(
459 Context context, String packageName, ApplicationInfo appInfo) {
satok4f313532011-06-01 16:13:45 +0900460 if (mSubtypeNameResId == 0) {
Yohei Yukawae489baf2016-04-11 22:09:57 -0700461 return getLocaleDisplayName(getLocaleFromContext(context), getLocaleObject(),
462 DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU);
satok4f313532011-06-01 16:13:45 +0900463 }
Yohei Yukawae489baf2016-04-11 22:09:57 -0700464
satok35412d62011-07-04 17:37:09 +0900465 final CharSequence subtypeName = context.getPackageManager().getText(
466 packageName, mSubtypeNameResId, appInfo);
Yohei Yukawae489baf2016-04-11 22:09:57 -0700467 if (TextUtils.isEmpty(subtypeName)) {
468 return "";
satok4f313532011-06-01 16:13:45 +0900469 }
Yohei Yukawae489baf2016-04-11 22:09:57 -0700470 final String subtypeNameString = subtypeName.toString();
471 String replacementString;
472 if (containsExtraValueKey(EXTRA_KEY_UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME)) {
473 replacementString = getExtraValueOf(
474 EXTRA_KEY_UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME);
475 } else {
476 final DisplayContext displayContext;
477 if (TextUtils.equals(subtypeNameString, "%s")) {
478 displayContext = DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU;
479 } else if (subtypeNameString.startsWith("%s")) {
480 displayContext = DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE;
481 } else {
482 displayContext = DisplayContext.CAPITALIZATION_FOR_MIDDLE_OF_SENTENCE;
483 }
484 replacementString = getLocaleDisplayName(getLocaleFromContext(context),
485 getLocaleObject(), displayContext);
486 }
487 if (replacementString == null) {
488 replacementString = "";
489 }
490 try {
491 return String.format(subtypeNameString, replacementString);
492 } catch (IllegalFormatException e) {
493 Slog.w(TAG, "Found illegal format in subtype name("+ subtypeName + "): " + e);
494 return "";
495 }
496 }
497
498 @Nullable
499 private static Locale getLocaleFromContext(@Nullable final Context context) {
500 if (context == null) {
501 return null;
502 }
503 if (context.getResources() == null) {
504 return null;
505 }
506 final Configuration configuration = context.getResources().getConfiguration();
507 if (configuration == null) {
508 return null;
509 }
510 return configuration.getLocales().get(0);
511 }
512
513 /**
514 * @param displayLocale {@link Locale} to be used to display {@code localeToDisplay}
515 * @param localeToDisplay {@link Locale} to be displayed in {@code displayLocale}
516 * @param displayContext context parameter to be used to display {@code localeToDisplay} in
517 * {@code displayLocale}
518 * @return Returns the name of the {@code localeToDisplay} in the user's current locale.
519 */
520 @NonNull
521 private static String getLocaleDisplayName(
522 @Nullable Locale displayLocale, @Nullable Locale localeToDisplay,
523 final DisplayContext displayContext) {
524 if (localeToDisplay == null) {
525 return "";
526 }
527 final Locale nonNullDisplayLocale =
528 displayLocale != null ? displayLocale : Locale.getDefault();
529 return LocaleDisplayNames
530 .getInstance(nonNullDisplayLocale, displayContext)
531 .localeDisplayName(localeToDisplay);
satok4f313532011-06-01 16:13:45 +0900532 }
533
satok9c4cc032011-02-14 18:03:32 +0900534 private HashMap<String, String> getExtraValueHashMap() {
Tadashi G. Takaokada5790d2017-03-24 17:22:33 +0900535 synchronized (this) {
536 HashMap<String, String> extraValueMap = mExtraValueHashMapCache;
537 if (extraValueMap != null) {
538 return extraValueMap;
539 }
540 extraValueMap = new HashMap<>();
541 final String[] pairs = mSubtypeExtraValue.split(EXTRA_VALUE_PAIR_SEPARATOR);
542 for (int i = 0; i < pairs.length; ++i) {
543 final String[] pair = pairs[i].split(EXTRA_VALUE_KEY_VALUE_SEPARATOR);
544 if (pair.length == 1) {
545 extraValueMap.put(pair[0], null);
546 } else if (pair.length > 1) {
547 if (pair.length > 2) {
548 Slog.w(TAG, "ExtraValue has two or more '='s");
satok9c4cc032011-02-14 18:03:32 +0900549 }
Tadashi G. Takaokada5790d2017-03-24 17:22:33 +0900550 extraValueMap.put(pair[0], pair[1]);
satok9c4cc032011-02-14 18:03:32 +0900551 }
552 }
Tadashi G. Takaokada5790d2017-03-24 17:22:33 +0900553 mExtraValueHashMapCache = extraValueMap;
554 return extraValueMap;
satok9c4cc032011-02-14 18:03:32 +0900555 }
satok9c4cc032011-02-14 18:03:32 +0900556 }
557
558 /**
559 * The string of ExtraValue in subtype should be defined as follows:
560 * example: key0,key1=value1,key2,key3,key4=value4
Ken Wakasa9a744762012-01-06 10:41:41 +0900561 * @param key The key of extra value
562 * @return The subtype contains specified the extra value
satok9c4cc032011-02-14 18:03:32 +0900563 */
564 public boolean containsExtraValueKey(String key) {
565 return getExtraValueHashMap().containsKey(key);
566 }
567
568 /**
569 * The string of ExtraValue in subtype should be defined as follows:
570 * example: key0,key1=value1,key2,key3,key4=value4
Ken Wakasa9a744762012-01-06 10:41:41 +0900571 * @param key The key of extra value
572 * @return The value of the specified key
satok9c4cc032011-02-14 18:03:32 +0900573 */
574 public String getExtraValueOf(String key) {
575 return getExtraValueHashMap().get(key);
576 }
577
satokab751aa2010-09-14 19:17:36 +0900578 @Override
579 public int hashCode() {
580 return mSubtypeHashCode;
581 }
582
Yohei Yukawa66baf692016-04-11 02:29:35 -0700583 /**
584 * @hide
585 * @return {@code true} if a valid subtype ID exists.
586 */
587 public final boolean hasSubtypeId() {
588 return mSubtypeId != SUBTYPE_ID_NONE;
589 }
590
591 /**
592 * @hide
593 * @return subtype ID. {@code 0} means that not subtype ID is specified.
594 */
595 public final int getSubtypeId() {
596 return mSubtypeId;
597 }
598
satokab751aa2010-09-14 19:17:36 +0900599 @Override
600 public boolean equals(Object o) {
601 if (o instanceof InputMethodSubtype) {
602 InputMethodSubtype subtype = (InputMethodSubtype) o;
Satoshi Kataokae62e6d82012-07-02 18:45:43 +0900603 if (subtype.mSubtypeId != 0 || mSubtypeId != 0) {
604 return (subtype.hashCode() == hashCode());
605 }
satokaf4bf402010-11-30 14:02:49 +0900606 return (subtype.hashCode() == hashCode())
Yohei Yukawa868d19b2015-12-07 15:58:57 -0800607 && (subtype.getLocale().equals(getLocale()))
608 && (subtype.getLanguageTag().equals(getLanguageTag()))
609 && (subtype.getMode().equals(getMode()))
610 && (subtype.getExtraValue().equals(getExtraValue()))
611 && (subtype.isAuxiliary() == isAuxiliary())
612 && (subtype.overridesImplicitlyEnabledSubtype()
613 == overridesImplicitlyEnabledSubtype())
614 && (subtype.isAsciiCapable() == isAsciiCapable());
satokab751aa2010-09-14 19:17:36 +0900615 }
616 return false;
617 }
618
satok9aabb952011-05-06 15:41:37 +0900619 @Override
satokab751aa2010-09-14 19:17:36 +0900620 public int describeContents() {
621 return 0;
622 }
623
satok9aabb952011-05-06 15:41:37 +0900624 @Override
satokab751aa2010-09-14 19:17:36 +0900625 public void writeToParcel(Parcel dest, int parcelableFlags) {
626 dest.writeInt(mSubtypeNameResId);
627 dest.writeInt(mSubtypeIconResId);
628 dest.writeString(mSubtypeLocale);
Yohei Yukawa868d19b2015-12-07 15:58:57 -0800629 dest.writeString(mSubtypeLanguageTag);
satok9ef02832010-11-04 21:17:48 +0900630 dest.writeString(mSubtypeMode);
satokab751aa2010-09-14 19:17:36 +0900631 dest.writeString(mSubtypeExtraValue);
satok9aabb952011-05-06 15:41:37 +0900632 dest.writeInt(mIsAuxiliary ? 1 : 0);
satoka86f5e42011-09-02 17:12:42 +0900633 dest.writeInt(mOverridesImplicitlyEnabledSubtype ? 1 : 0);
Satoshi Kataokae62e6d82012-07-02 18:45:43 +0900634 dest.writeInt(mSubtypeHashCode);
635 dest.writeInt(mSubtypeId);
Satoshi Kataokadc8abf62013-03-12 14:40:04 +0900636 dest.writeInt(mIsAsciiCapable ? 1 : 0);
satokab751aa2010-09-14 19:17:36 +0900637 }
638
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -0700639 public static final @android.annotation.NonNull Parcelable.Creator<InputMethodSubtype> CREATOR
satokab751aa2010-09-14 19:17:36 +0900640 = new Parcelable.Creator<InputMethodSubtype>() {
satok9aabb952011-05-06 15:41:37 +0900641 @Override
satokab751aa2010-09-14 19:17:36 +0900642 public InputMethodSubtype createFromParcel(Parcel source) {
643 return new InputMethodSubtype(source);
644 }
645
satok9aabb952011-05-06 15:41:37 +0900646 @Override
satokab751aa2010-09-14 19:17:36 +0900647 public InputMethodSubtype[] newArray(int size) {
648 return new InputMethodSubtype[size];
649 }
650 };
651
satoka9778d42011-06-08 19:11:14 +0900652 private static int hashCodeInternal(String locale, String mode, String extraValue,
Satoshi Kataokadc8abf62013-03-12 14:40:04 +0900653 boolean isAuxiliary, boolean overridesImplicitlyEnabledSubtype,
654 boolean isAsciiCapable) {
Satoshi Kataoka5fd5aa32013-11-14 12:04:51 +0900655 // CAVEAT: Must revisit how to compute needsToCalculateCompatibleHashCode when a new
656 // attribute is added in order to avoid enabled subtypes being unexpectedly disabled.
657 final boolean needsToCalculateCompatibleHashCode = !isAsciiCapable;
658 if (needsToCalculateCompatibleHashCode) {
659 return Arrays.hashCode(new Object[] {locale, mode, extraValue, isAuxiliary,
660 overridesImplicitlyEnabledSubtype});
661 }
satoka86f5e42011-09-02 17:12:42 +0900662 return Arrays.hashCode(new Object[] {locale, mode, extraValue, isAuxiliary,
Satoshi Kataokadc8abf62013-03-12 14:40:04 +0900663 overridesImplicitlyEnabledSubtype, isAsciiCapable});
satokab751aa2010-09-14 19:17:36 +0900664 }
satok7265d9b2011-02-14 15:47:30 +0900665
666 /**
667 * Sort the list of InputMethodSubtype
668 * @param context Context will be used for getting localized strings from IME
669 * @param flags Flags for the sort order
670 * @param imi InputMethodInfo of which subtypes are subject to be sorted
671 * @param subtypeList List of InputMethodSubtype which will be sorted
672 * @return Sorted list of subtypes
673 * @hide
674 */
675 public static List<InputMethodSubtype> sort(Context context, int flags, InputMethodInfo imi,
676 List<InputMethodSubtype> subtypeList) {
677 if (imi == null) return subtypeList;
678 final HashSet<InputMethodSubtype> inputSubtypesSet = new HashSet<InputMethodSubtype>(
679 subtypeList);
680 final ArrayList<InputMethodSubtype> sortedList = new ArrayList<InputMethodSubtype>();
681 int N = imi.getSubtypeCount();
682 for (int i = 0; i < N; ++i) {
683 InputMethodSubtype subtype = imi.getSubtypeAt(i);
684 if (inputSubtypesSet.contains(subtype)) {
685 sortedList.add(subtype);
686 inputSubtypesSet.remove(subtype);
687 }
688 }
689 // If subtypes in inputSubtypesSet remain, that means these subtypes are not
690 // contained in imi, so the remaining subtypes will be appended.
691 for (InputMethodSubtype subtype: inputSubtypesSet) {
692 sortedList.add(subtype);
693 }
694 return sortedList;
695 }
Tony Makafcd19c2017-08-07 15:15:57 +0100696}