Permalink
Browse files

Introduce SuggestionSpan#getLocaleObject().

This is a series of CLs to promote Java Locale object as a primary data
object to represent locale information in Android APIs.

With this CL, developers can get Locale information from SuggestionSpan
without semantically losing the original data.  Possible use cases would
be:
 - Emoji subtag (CLDR Ticket #9063)
 - Stable and reliable 3-letter country codes handling
 - Custom attributes based on private extensions.

Note that this CL does not change the current behavior of
SuggestionSpan#getLocale() as is, even if it is created with a malformed
Locale object:

  new SuggestionSpan(new Locale(" a ", " b c ", " e"), new String[0], 0)
          .getLocale() -> " a  b c  e".

Bug: 22858221
Change-Id: I96dfd4f819a236ee2e6dbd3e12903d1214223cb0
  • Loading branch information...
1 parent a1dcb87 commit 60fbc8e3bbe55e8be655418cc8111354e17f6ea7 @yukawa yukawa committed Jan 14, 2016
Showing with 54 additions and 15 deletions.
  1. +2 −1 api/current.txt
  2. +2 −1 api/system-current.txt
  3. +2 −1 api/test-current.txt
  4. +48 −12 core/java/android/text/style/SuggestionSpan.java
View
@@ -38206,7 +38206,8 @@ package android.text.style {
ctor public SuggestionSpan(android.os.Parcel);
method public int describeContents();
method public int getFlags();
- method public java.lang.String getLocale();
+ method public deprecated java.lang.String getLocale();
+ method public java.util.Locale getLocaleObject();
method public int getSpanTypeId();
method public java.lang.String[] getSuggestions();
method public void setFlags(int);
@@ -40557,7 +40557,8 @@ package android.text.style {
ctor public SuggestionSpan(android.os.Parcel);
method public int describeContents();
method public int getFlags();
- method public java.lang.String getLocale();
+ method public deprecated java.lang.String getLocale();
+ method public java.util.Locale getLocaleObject();
method public int getSpanTypeId();
method public java.lang.String[] getSuggestions();
method public void setFlags(int);
@@ -38222,7 +38222,8 @@ package android.text.style {
ctor public SuggestionSpan(android.os.Parcel);
method public int describeContents();
method public int getFlags();
- method public java.lang.String getLocale();
+ method public deprecated java.lang.String getLocale();
+ method public java.util.Locale getLocaleObject();
method public int getSpanTypeId();
method public java.lang.String[] getSuggestions();
method public void setFlags(int);
@@ -16,6 +16,8 @@
package android.text.style;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.content.Intent;
import android.content.res.TypedArray;
@@ -84,7 +86,15 @@
private int mFlags;
private final String[] mSuggestions;
- private final String mLocaleString;
+ /**
+ * Kept for compatibility for apps that rely on invalid locale strings e.g.
+ * {@code new Locale(" an ", " i n v a l i d ", "data")}, which cannot be handled by
+ * {@link #mLanguageTag}.
+ */
+ @NonNull
+ private final String mLocaleStringForCompatibility;
+ @NonNull
+ private final String mLanguageTag;
private final String mNotificationTargetClassName;
private final String mNotificationTargetPackageName;
private final int mHashCode;
@@ -130,14 +140,18 @@ public SuggestionSpan(Context context, Locale locale, String[] suggestions, int
final int N = Math.min(SUGGESTIONS_MAX_SIZE, suggestions.length);
mSuggestions = Arrays.copyOf(suggestions, N);
mFlags = flags;
+ final Locale sourceLocale;
if (locale != null) {
- mLocaleString = locale.toString();
+ sourceLocale = locale;
} else if (context != null) {
- mLocaleString = context.getResources().getConfiguration().locale.toString();
+ // TODO: Consider to context.getResources().getResolvedLocale() instead.
+ sourceLocale = context.getResources().getConfiguration().locale;
} else {
Log.e("SuggestionSpan", "No locale or context specified in SuggestionSpan constructor");
- mLocaleString = "";
+ sourceLocale = null;
}
+ mLocaleStringForCompatibility = sourceLocale == null ? "" : sourceLocale.toString();
+ mLanguageTag = sourceLocale == null ? "" : sourceLocale.toLanguageTag();
if (context != null) {
mNotificationTargetPackageName = context.getPackageName();
@@ -150,7 +164,8 @@ public SuggestionSpan(Context context, Locale locale, String[] suggestions, int
} else {
mNotificationTargetClassName = "";
}
- mHashCode = hashCodeInternal(mSuggestions, mLocaleString, mNotificationTargetClassName);
+ mHashCode = hashCodeInternal(mSuggestions, mLanguageTag, mLocaleStringForCompatibility,
+ mNotificationTargetClassName);
initStyle(context);
}
@@ -194,7 +209,8 @@ private void initStyle(Context context) {
public SuggestionSpan(Parcel src) {
mSuggestions = src.readStringArray();
mFlags = src.readInt();
- mLocaleString = src.readString();
+ mLocaleStringForCompatibility = src.readString();
+ mLanguageTag = src.readString();
mNotificationTargetClassName = src.readString();
mNotificationTargetPackageName = src.readString();
mHashCode = src.readInt();
@@ -214,10 +230,29 @@ public SuggestionSpan(Parcel src) {
}
/**
- * @return the locale of the suggestions
+ * @deprecated use {@link #getLocaleObject()} instead.
+ * @return the locale of the suggestions. An empty string is returned if no locale is specified.
*/
+ @NonNull
+ @Deprecated
public String getLocale() {
- return mLocaleString;
+ return mLocaleStringForCompatibility;
+ }
+
+ /**
+ * Returns a well-formed BCP 47 language tag representation of the suggestions, as a
+ * {@link Locale} object.
+ *
+ * <p><b>Caveat</b>: The returned object is guaranteed to be a a well-formed BCP 47 language tag
+ * representation. For example, this method can return an empty locale rather than returning a
+ * malformed data when this object is initialized with an malformed {@link Locale} object, e.g.
+ * {@code new Locale(" a ", " b c d ", " "}.</p>
+ *
+ * @return the locale of the suggestions. {@code null} is returned if no locale is specified.
+ */
+ @Nullable
+ public Locale getLocaleObject() {
+ return mLanguageTag.isEmpty() ? null : Locale.forLanguageTag(mLanguageTag);
}
/**
@@ -255,7 +290,8 @@ public void writeToParcel(Parcel dest, int flags) {
public void writeToParcelInternal(Parcel dest, int flags) {
dest.writeStringArray(mSuggestions);
dest.writeInt(mFlags);
- dest.writeString(mLocaleString);
+ dest.writeString(mLocaleStringForCompatibility);
+ dest.writeString(mLanguageTag);
dest.writeString(mNotificationTargetClassName);
dest.writeString(mNotificationTargetPackageName);
dest.writeInt(mHashCode);
@@ -290,10 +326,10 @@ public int hashCode() {
return mHashCode;
}
- private static int hashCodeInternal(String[] suggestions, String locale,
- String notificationTargetClassName) {
+ private static int hashCodeInternal(String[] suggestions, @NonNull String languageTag,
+ @NonNull String localeStringForCompatibility, String notificationTargetClassName) {
return Arrays.hashCode(new Object[] {Long.valueOf(SystemClock.uptimeMillis()), suggestions,
- locale, notificationTargetClassName});
+ languageTag, localeStringForCompatibility, notificationTargetClassName});
}
public static final Parcelable.Creator<SuggestionSpan> CREATOR =

0 comments on commit 60fbc8e

Please sign in to comment.