Skip to content
This repository has been archived by the owner on Nov 8, 2023. It is now read-only.

Commit

Permalink
Introduce SuggestionSpan#getLocaleObject().
Browse files Browse the repository at this point in the history
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
yukawa committed Jan 14, 2016
1 parent a1dcb87 commit 60fbc8e
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 15 deletions.
3 changes: 2 additions & 1 deletion api/current.txt
Expand Up @@ -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);
Expand Down
3 changes: 2 additions & 1 deletion api/system-current.txt
Expand Up @@ -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);
Expand Down
3 changes: 2 additions & 1 deletion api/test-current.txt
Expand Up @@ -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);
Expand Down
60 changes: 48 additions & 12 deletions core/java/android/text/style/SuggestionSpan.java
Expand Up @@ -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;
Expand Down Expand Up @@ -84,7 +86,15 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan {

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;
Expand Down Expand Up @@ -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();
Expand All @@ -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);
}
Expand Down Expand Up @@ -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();
Expand All @@ -214,10 +230,29 @@ public String[] getSuggestions() {
}

/**
* @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);
}

/**
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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 =
Expand Down

0 comments on commit 60fbc8e

Please sign in to comment.