Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 08943191 authored by Yohei Yukawa's avatar Yohei Yukawa
Browse files

Add subtypeId for SpellCheckerSubtype.

What this CL actually does is just copying the existing concept
"subtypeId" from InputMethodSubtype to SpellCheckerSubtype.

To recap, the underlying problem is that the system has stored only the
return value of SpellCheckerSubtype#hashCode() to track the set of
enabled subtypes, and SpellCheckerSubtype#hashCode() has been
implemented as Arrays.hashCode(new Object[] {locale, extraValue}), which
is problematic because:
  - Spell checker developers cannot change "locale" and/or 'extraValue'
    if they want to keep enabled subtypes enabled.
  - Android Framework developers cannot change the hash function even
    when new fields are added if they want to keep enabled subtypes
    enabled.
InputMethodSubtype has had the same issue, and what we did was
introducing a concept "subtypeId", which allows IME developers to
specify the return value of #hashCode().

For instance, suppose that a subtype X has already been used in
production with the following attributes:
  - locale: "tl_PH"
  - extraValues: "key1=value1,key2=value2"

With "subtypeId", you can change the attributes of subtype X without
losing the enabled state of subtype X on devices as follows.
  - locale: "fil_PH"
  - extraValues: "key1=value1,key2=value2,key3=value3"
  - subtypeId: Arrays.hashCode(new Object[] {
            "tl_PH", "key1=value1,key2=value2"})

This CL also deprecates existing public constructor of
SpellCheckerSubtype, which was probably published as a public API by
mistake.  Note that the constructor of SpellCheckerInfo class is @hide.
Also there is no public API that receives SpellCheckerSubtype object
instantiated by developers with custom data.  Making developers to be
able to instantiate SpellCheckerSubtype does not make sense right now.

Bug: 11736916
Bug: 22858221
Change-Id: I98f05c1e9421c47a93769bc4a6fe11b678bc2509
parent bd2b4aa3
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -43249,7 +43249,7 @@ package android.view.textservice {
  }
  public final class SpellCheckerSubtype implements android.os.Parcelable {
    ctor public SpellCheckerSubtype(int, java.lang.String, java.lang.String);
    ctor public deprecated SpellCheckerSubtype(int, java.lang.String, java.lang.String);
    method public boolean containsExtraValueKey(java.lang.String);
    method public int describeContents();
    method public java.lang.CharSequence getDisplayName(android.content.Context, java.lang.String, android.content.pm.ApplicationInfo);
+1 −1
Original line number Diff line number Diff line
@@ -45586,7 +45586,7 @@ package android.view.textservice {
  }
  public final class SpellCheckerSubtype implements android.os.Parcelable {
    ctor public SpellCheckerSubtype(int, java.lang.String, java.lang.String);
    ctor public deprecated SpellCheckerSubtype(int, java.lang.String, java.lang.String);
    method public boolean containsExtraValueKey(java.lang.String);
    method public int describeContents();
    method public java.lang.CharSequence getDisplayName(android.content.Context, java.lang.String, android.content.pm.ApplicationInfo);
+1 −1
Original line number Diff line number Diff line
@@ -43251,7 +43251,7 @@ package android.view.textservice {
  }
  public final class SpellCheckerSubtype implements android.os.Parcelable {
    ctor public SpellCheckerSubtype(int, java.lang.String, java.lang.String);
    ctor public deprecated SpellCheckerSubtype(int, java.lang.String, java.lang.String);
    method public boolean containsExtraValueKey(java.lang.String);
    method public int describeContents();
    method public java.lang.CharSequence getDisplayName(android.content.Context, java.lang.String, android.content.pm.ApplicationInfo);
+3 −1
Original line number Diff line number Diff line
@@ -117,7 +117,9 @@ public final class SpellCheckerInfo implements Parcelable {
                            a.getString(com.android.internal.R.styleable
                                    .SpellChecker_Subtype_subtypeLocale),
                            a.getString(com.android.internal.R.styleable
                                    .SpellChecker_Subtype_subtypeExtraValue));
                                    .SpellChecker_Subtype_subtypeExtraValue),
                            a.getInt(com.android.internal.R.styleable
                                    .SpellChecker_Subtype_subtypeId, 0));
                    mSubtypes.add(subtype);
                }
            }
+46 −7
Original line number Diff line number Diff line
@@ -36,12 +36,21 @@ 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 SpellCheckerInfo
 *
 * @attr ref android.R.styleable#SpellChecker_Subtype_label
 * @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 = "=";
    private static final int SUBTYPE_ID_NONE = 0;

    private final int mSubtypeId;
    private final int mSubtypeHashCode;
    private final int mSubtypeNameResId;
    private final String mSubtypeLocale;
@@ -49,16 +58,40 @@ public final class SpellCheckerSubtype implements Parcelable {
    private HashMap<String, String> mExtraValueHashMapCache;

    /**
     * Constructor
     * 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 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 extraValue) {
    public SpellCheckerSubtype(int nameId, String locale, String extraValue, int subtypeId) {
        mSubtypeNameResId = nameId;
        mSubtypeLocale = locale != null ? locale : "";
        mSubtypeExtraValue = extraValue != null ? extraValue : "";
        mSubtypeHashCode = hashCodeInternal(mSubtypeLocale, mSubtypeExtraValue);
        mSubtypeId = subtypeId;
        mSubtypeHashCode = mSubtypeId != SUBTYPE_ID_NONE ?
                mSubtypeId : hashCodeInternal(mSubtypeLocale, mSubtypeExtraValue);
    }

    /**
     * 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.
     */
    public SpellCheckerSubtype(int nameId, String locale, String extraValue) {
        this(nameId, locale, extraValue, SUBTYPE_ID_NONE);
    }

    SpellCheckerSubtype(Parcel source) {
@@ -68,7 +101,9 @@ public final class SpellCheckerSubtype implements Parcelable {
        mSubtypeLocale = s != null ? s : "";
        s = source.readString();
        mSubtypeExtraValue = s != null ? s : "";
        mSubtypeHashCode = hashCodeInternal(mSubtypeLocale, mSubtypeExtraValue);
        mSubtypeId = source.readInt();
        mSubtypeHashCode = mSubtypeId != SUBTYPE_ID_NONE ?
                mSubtypeId : hashCodeInternal(mSubtypeLocale, mSubtypeExtraValue);
    }

    /**
@@ -141,6 +176,9 @@ public final class SpellCheckerSubtype implements Parcelable {
    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()))
@@ -197,6 +235,7 @@ public final class SpellCheckerSubtype implements Parcelable {
        dest.writeInt(mSubtypeNameResId);
        dest.writeString(mSubtypeLocale);
        dest.writeString(mSubtypeExtraValue);
        dest.writeInt(mSubtypeId);
    }

    public static final Parcelable.Creator<SpellCheckerSubtype> CREATOR
Loading