/* * 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.text.style; import android.content.Context; import android.os.Parcel; import android.os.Parcelable; import android.os.SystemClock; import android.text.ParcelableSpan; import android.text.TextUtils; import android.widget.TextView; import java.util.Arrays; import java.util.Locale; /** * Holds suggestion candidates for the text enclosed in this span. * * When such a span is edited in an EditText, double tapping on the text enclosed in this span will * display a popup dialog listing suggestion replacement for that text. The user can then replace * the original text by one of the suggestions. * * These spans should typically be created by the input method to privide correction and alternates * for the text. * * @see TextView#setSuggestionsEnabled(boolean) */ public class SuggestionSpan implements ParcelableSpan { /** * Flag for indicating that the input is verbatim. TextView refers to this flag to determine * how it displays a word with SuggestionSpan. */ public static final int FLAG_VERBATIM = 0x0001; public static final String ACTION_SUGGESTION_PICKED = "android.text.style.SUGGESTION_PICKED"; public static final String SUGGESTION_SPAN_PICKED_AFTER = "after"; public static final String SUGGESTION_SPAN_PICKED_BEFORE = "before"; public static final String SUGGESTION_SPAN_PICKED_HASHCODE = "hashcode"; public static final int SUGGESTIONS_MAX_SIZE = 5; /* * TODO: Needs to check the validity and add a feature that TextView will change * the current IME to the other IME which is specified in SuggestionSpan. * An IME needs to set the span by specifying the target IME and Subtype of SuggestionSpan. * And the current IME might want to specify any IME as the target IME including other IMEs. */ private final int mFlags; private final String[] mSuggestions; private final String mLocaleString; private final String mNotificationTargetClassName; private final int mHashCode; /* * TODO: If switching IME is required, needs to add parameters for ids of InputMethodInfo * and InputMethodSubtype. */ /** * @param context Context for the application * @param suggestions Suggestions for the string under the span * @param flags Additional flags indicating how this span is handled in TextView */ public SuggestionSpan(Context context, String[] suggestions, int flags) { this(context, null, suggestions, flags, null); } /** * @param locale Locale of the suggestions * @param suggestions Suggestions for the string under the span * @param flags Additional flags indicating how this span is handled in TextView */ public SuggestionSpan(Locale locale, String[] suggestions, int flags) { this(null, locale, suggestions, flags, null); } /** * @param context Context for the application * @param locale locale Locale of the suggestions * @param suggestions Suggestions for the string under the span * @param flags Additional flags indicating how this span is handled in TextView * @param notificationTargetClass if not null, this class will get notified when the user * selects one of the suggestions. */ public SuggestionSpan(Context context, Locale locale, String[] suggestions, int flags, Class notificationTargetClass) { final int N = Math.min(SUGGESTIONS_MAX_SIZE, suggestions.length); mSuggestions = Arrays.copyOf(suggestions, N); mFlags = flags; if (context != null && locale == null) { mLocaleString = context.getResources().getConfiguration().locale.toString(); } else { mLocaleString = locale.toString(); } if (notificationTargetClass != null) { mNotificationTargetClassName = notificationTargetClass.getCanonicalName(); } else { mNotificationTargetClassName = ""; } mHashCode = hashCodeInternal( mFlags, mSuggestions, mLocaleString, mNotificationTargetClassName); } public SuggestionSpan(Parcel src) { mSuggestions = src.readStringArray(); mFlags = src.readInt(); mLocaleString = src.readString(); mNotificationTargetClassName = src.readString(); mHashCode = src.readInt(); } /** * @return an array of suggestion texts for this span */ public String[] getSuggestions() { return mSuggestions; } /** * @return the locale of the suggestions */ public String getLocale() { return mLocaleString; } /** * @return The name of the class to notify. The class of the original IME package will receive * a notification when the user selects one of the suggestions. The notification will include * the original string, the suggested replacement string as well as the hashCode of this span. * The class will get notified by an intent that has those information. * This is an internal API because only the framework should know the class name. * * @hide */ public String getNotificationTargetClassName() { return mNotificationTargetClassName; } public int getFlags() { return mFlags; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeStringArray(mSuggestions); dest.writeInt(mFlags); dest.writeString(mLocaleString); dest.writeString(mNotificationTargetClassName); dest.writeInt(mHashCode); } @Override public int getSpanTypeId() { return TextUtils.SUGGESTION_SPAN; } @Override public boolean equals(Object o) { if (o instanceof SuggestionSpan) { return ((SuggestionSpan)o).hashCode() == mHashCode; } return false; } @Override public int hashCode() { return mHashCode; } private static int hashCodeInternal(int flags, String[] suggestions,String locale, String notificationTargetClassName) { return Arrays.hashCode(new Object[] {SystemClock.uptimeMillis(), flags, suggestions, locale, notificationTargetClassName}); } public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { @Override public SuggestionSpan createFromParcel(Parcel source) { return new SuggestionSpan(source); } @Override public SuggestionSpan[] newArray(int size) { return new SuggestionSpan[size]; } }; }