/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.view.textservice;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.Slog;

import com.android.internal.inputmethod.InputMethodUtils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;

This class is used to specify meta information of a subtype contained in a spell checker. Subtype can describe locale (e.g. en_US, fr_FR...) used for settings.
See Also:
  • SpellCheckerInfo
@attrref android.R.styleable#SpellChecker_Subtype_label
@attrref android.R.styleable#SpellChecker_Subtype_languageTag
@attrref android.R.styleable#SpellChecker_Subtype_subtypeLocale
@attrref android.R.styleable#SpellChecker_Subtype_subtypeExtraValue
@attrref android.R.styleable#SpellChecker_Subtype_subtypeId
/** * This class is used to specify meta information of a subtype contained in a spell checker. * Subtype can describe locale (e.g. en_US, fr_FR...) used for settings. * * @see SpellCheckerInfo * * @attr ref android.R.styleable#SpellChecker_Subtype_label * @attr ref android.R.styleable#SpellChecker_Subtype_languageTag * @attr ref android.R.styleable#SpellChecker_Subtype_subtypeLocale * @attr ref android.R.styleable#SpellChecker_Subtype_subtypeExtraValue * @attr ref android.R.styleable#SpellChecker_Subtype_subtypeId */
public final class SpellCheckerSubtype implements Parcelable { private static final String TAG = SpellCheckerSubtype.class.getSimpleName(); private static final String EXTRA_VALUE_PAIR_SEPARATOR = ","; private static final String EXTRA_VALUE_KEY_VALUE_SEPARATOR = "=";
@hide
/** * @hide */
public static final int SUBTYPE_ID_NONE = 0; private static final String SUBTYPE_LANGUAGE_TAG_NONE = ""; private final int mSubtypeId; private final int mSubtypeHashCode; private final int mSubtypeNameResId; private final String mSubtypeLocale; private final String mSubtypeLanguageTag; private final String mSubtypeExtraValue; private HashMap<String, String> mExtraValueHashMapCache;
Constructor.

There is no public API that requires developers to instantiate custom SpellCheckerSubtype object. Hence so far there is no need to make this constructor available in public API.

Params:
  • nameId – The name of the subtype
  • locale – The locale supported by the subtype
  • languageTag – The BCP-47 Language Tag associated with this subtype.
  • extraValue – The extra value of the subtype
  • subtypeId – The subtype ID that is supposed to be stable during package update.
@hide
/** * Constructor. * * <p>There is no public API that requires developers to instantiate custom * {@link SpellCheckerSubtype} object. Hence so far there is no need to make this constructor * available in public API.</p> * * @param nameId The name of the subtype * @param locale The locale supported by the subtype * @param languageTag The BCP-47 Language Tag associated with this subtype. * @param extraValue The extra value of the subtype * @param subtypeId The subtype ID that is supposed to be stable during package update. * * @hide */
public SpellCheckerSubtype(int nameId, String locale, String languageTag, String extraValue, int subtypeId) { mSubtypeNameResId = nameId; mSubtypeLocale = locale != null ? locale : ""; mSubtypeLanguageTag = languageTag != null ? languageTag : SUBTYPE_LANGUAGE_TAG_NONE; mSubtypeExtraValue = extraValue != null ? extraValue : ""; mSubtypeId = subtypeId; mSubtypeHashCode = mSubtypeId != SUBTYPE_ID_NONE ? mSubtypeId : hashCodeInternal(mSubtypeLocale, mSubtypeExtraValue); }
Constructor.
Params:
  • nameId – The name of the subtype
  • locale – The locale supported by the subtype
  • extraValue – The extra value of the subtype
Deprecated:There is no public API that requires developers to directly instantiate custom SpellCheckerSubtype objects right now. Hence only the system is expected to be able to instantiate SpellCheckerSubtype object.
/** * Constructor. * @param nameId The name of the subtype * @param locale The locale supported by the subtype * @param extraValue The extra value of the subtype * * @deprecated There is no public API that requires developers to directly instantiate custom * {@link SpellCheckerSubtype} objects right now. Hence only the system is expected to be able * to instantiate {@link SpellCheckerSubtype} object. */
@Deprecated public SpellCheckerSubtype(int nameId, String locale, String extraValue) { this(nameId, locale, SUBTYPE_LANGUAGE_TAG_NONE, extraValue, SUBTYPE_ID_NONE); } SpellCheckerSubtype(Parcel source) { String s; mSubtypeNameResId = source.readInt(); s = source.readString(); mSubtypeLocale = s != null ? s : ""; s = source.readString(); mSubtypeLanguageTag = s != null ? s : ""; s = source.readString(); mSubtypeExtraValue = s != null ? s : ""; mSubtypeId = source.readInt(); mSubtypeHashCode = mSubtypeId != SUBTYPE_ID_NONE ? mSubtypeId : hashCodeInternal(mSubtypeLocale, mSubtypeExtraValue); }
Returns:the name of the subtype
/** * @return the name of the subtype */
public int getNameResId() { return mSubtypeNameResId; }
Returns:the locale of the subtype
Deprecated:Use getLanguageTag() instead.
/** * @return the locale of the subtype * * @deprecated Use {@link #getLanguageTag()} instead. */
@Deprecated @NonNull public String getLocale() { return mSubtypeLocale; }
See Also:
Returns:the BCP-47 Language Tag of the subtype. Returns an empty string when no Language Tag is specified.
/** * @return the BCP-47 Language Tag of the subtype. Returns an empty string when no Language Tag * is specified. * * @see Locale#forLanguageTag(String) */
@NonNull public String getLanguageTag() { return mSubtypeLanguageTag; }
Returns:the extra value of the subtype
/** * @return the extra value of the subtype */
public String getExtraValue() { return mSubtypeExtraValue; } private HashMap<String, String> getExtraValueHashMap() { if (mExtraValueHashMapCache == null) { mExtraValueHashMapCache = new HashMap<String, String>(); final String[] pairs = mSubtypeExtraValue.split(EXTRA_VALUE_PAIR_SEPARATOR); final int N = pairs.length; for (int i = 0; i < N; ++i) { final String[] pair = pairs[i].split(EXTRA_VALUE_KEY_VALUE_SEPARATOR); if (pair.length == 1) { mExtraValueHashMapCache.put(pair[0], null); } else if (pair.length > 1) { if (pair.length > 2) { Slog.w(TAG, "ExtraValue has two or more '='s"); } mExtraValueHashMapCache.put(pair[0], pair[1]); } } } return mExtraValueHashMapCache; }
The string of ExtraValue in subtype should be defined as follows: example: key0,key1=value1,key2,key3,key4=value4
Params:
  • key – the key of extra value
Returns:the subtype contains specified the extra value
/** * The string of ExtraValue in subtype should be defined as follows: * example: key0,key1=value1,key2,key3,key4=value4 * @param key the key of extra value * @return the subtype contains specified the extra value */
public boolean containsExtraValueKey(String key) { return getExtraValueHashMap().containsKey(key); }
The string of ExtraValue in subtype should be defined as follows: example: key0,key1=value1,key2,key3,key4=value4
Params:
  • key – the key of extra value
Returns:the value of the specified key
/** * The string of ExtraValue in subtype should be defined as follows: * example: key0,key1=value1,key2,key3,key4=value4 * @param key the key of extra value * @return the value of the specified key */
public String getExtraValueOf(String key) { return getExtraValueHashMap().get(key); } @Override public int hashCode() { return mSubtypeHashCode; } @Override public boolean equals(Object o) { if (o instanceof SpellCheckerSubtype) { SpellCheckerSubtype subtype = (SpellCheckerSubtype) o; if (subtype.mSubtypeId != SUBTYPE_ID_NONE || mSubtypeId != SUBTYPE_ID_NONE) { return (subtype.hashCode() == hashCode()); } return (subtype.hashCode() == hashCode()) && (subtype.getNameResId() == getNameResId()) && (subtype.getLocale().equals(getLocale())) && (subtype.getLanguageTag().equals(getLanguageTag())) && (subtype.getExtraValue().equals(getExtraValue())); } return false; }
Returns:Locale constructed from getLanguageTag(). If the Language Tag is not specified, then try to construct from getLocale()

TODO: Consider to make this a public API, or move this to support lib.

@hide
/** * @return {@link Locale} constructed from {@link #getLanguageTag()}. If the Language Tag is not * specified, then try to construct from {@link #getLocale()} * * <p>TODO: Consider to make this a public API, or move this to support lib.</p> * @hide */
@Nullable public Locale getLocaleObject() { if (!TextUtils.isEmpty(mSubtypeLanguageTag)) { return Locale.forLanguageTag(mSubtypeLanguageTag); } return InputMethodUtils.constructLocaleFromString(mSubtypeLocale); }
Params:
  • context – Context will be used for getting Locale and PackageManager.
  • packageName – The package name of the spell checker
  • appInfo – The application info of the spell checker
Returns:a display name for this subtype. The string resource of the label (mSubtypeNameResId) can have only one %s in it. If there is, the %s part will be replaced with the locale's display name by the formatter. If there is not, this method simply returns the string specified by mSubtypeNameResId. If mSubtypeNameResId is not specified (== 0), it's up to the framework to generate an appropriate display name.
/** * @param context Context will be used for getting Locale and PackageManager. * @param packageName The package name of the spell checker * @param appInfo The application info of the spell checker * @return a display name for this subtype. The string resource of the label (mSubtypeNameResId) * can have only one %s in it. If there is, the %s part will be replaced with the locale's * display name by the formatter. If there is not, this method simply returns the string * specified by mSubtypeNameResId. If mSubtypeNameResId is not specified (== 0), it's up to the * framework to generate an appropriate display name. */
public CharSequence getDisplayName( Context context, String packageName, ApplicationInfo appInfo) { final Locale locale = getLocaleObject(); final String localeStr = locale != null ? locale.getDisplayName() : mSubtypeLocale; if (mSubtypeNameResId == 0) { return localeStr; } final CharSequence subtypeName = context.getPackageManager().getText( packageName, mSubtypeNameResId, appInfo); if (!TextUtils.isEmpty(subtypeName)) { return String.format(subtypeName.toString(), localeStr); } else { return localeStr; } } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int parcelableFlags) { dest.writeInt(mSubtypeNameResId); dest.writeString(mSubtypeLocale); dest.writeString(mSubtypeLanguageTag); dest.writeString(mSubtypeExtraValue); dest.writeInt(mSubtypeId); } public static final Parcelable.Creator<SpellCheckerSubtype> CREATOR = new Parcelable.Creator<SpellCheckerSubtype>() { @Override public SpellCheckerSubtype createFromParcel(Parcel source) { return new SpellCheckerSubtype(source); } @Override public SpellCheckerSubtype[] newArray(int size) { return new SpellCheckerSubtype[size]; } }; private static int hashCodeInternal(String locale, String extraValue) { return Arrays.hashCode(new Object[] {locale, extraValue}); }
Sort the list of subtypes
Params:
  • context – Context will be used for getting localized strings
  • flags – Flags for the sort order
  • sci – SpellCheckerInfo of which subtypes are subject to be sorted
  • subtypeList – List which will be sorted
Returns:Sorted list of subtypes
@hide
/** * Sort the list of subtypes * @param context Context will be used for getting localized strings * @param flags Flags for the sort order * @param sci SpellCheckerInfo of which subtypes are subject to be sorted * @param subtypeList List which will be sorted * @return Sorted list of subtypes * @hide */
public static List<SpellCheckerSubtype> sort(Context context, int flags, SpellCheckerInfo sci, List<SpellCheckerSubtype> subtypeList) { if (sci == null) return subtypeList; final HashSet<SpellCheckerSubtype> subtypesSet = new HashSet<SpellCheckerSubtype>( subtypeList); final ArrayList<SpellCheckerSubtype> sortedList = new ArrayList<SpellCheckerSubtype>(); int N = sci.getSubtypeCount(); for (int i = 0; i < N; ++i) { SpellCheckerSubtype subtype = sci.getSubtypeAt(i); if (subtypesSet.contains(subtype)) { sortedList.add(subtype); subtypesSet.remove(subtype); } } // If subtypes in subtypesSet remain, that means these subtypes are not // contained in sci, so the remaining subtypes will be appended. for (SpellCheckerSubtype subtype: subtypesSet) { sortedList.add(subtype); } return sortedList; } }