SuggestionSpan.java revision e6d368218918f911b1954296dab25bf84147b4c6
1/* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.text.style; 18 19import android.content.Context; 20import android.content.res.TypedArray; 21import android.graphics.Color; 22import android.os.Parcel; 23import android.os.Parcelable; 24import android.os.SystemClock; 25import android.text.ParcelableSpan; 26import android.text.TextPaint; 27import android.text.TextUtils; 28import android.widget.TextView; 29 30import java.util.Arrays; 31import java.util.Locale; 32 33/** 34 * Holds suggestion candidates for the text enclosed in this span. 35 * 36 * When such a span is edited in an EditText, double tapping on the text enclosed in this span will 37 * display a popup dialog listing suggestion replacement for that text. The user can then replace 38 * the original text by one of the suggestions. 39 * 40 * These spans should typically be created by the input method to provide correction and alternates 41 * for the text. 42 * 43 * @see TextView#isSuggestionsEnabled() 44 */ 45public class SuggestionSpan extends CharacterStyle implements ParcelableSpan { 46 47 /** 48 * Sets this flag if the suggestions should be easily accessible with few interactions. 49 * This flag should be set for every suggestions that the user is likely to use. 50 */ 51 public static final int FLAG_EASY_CORRECT = 0x0001; 52 53 /** 54 * Sets this flag if the suggestions apply to a misspelled word/text. This type of suggestion is 55 * rendered differently to highlight the error. 56 */ 57 public static final int FLAG_MISSPELLED = 0x0002; 58 59 public static final String ACTION_SUGGESTION_PICKED = "android.text.style.SUGGESTION_PICKED"; 60 public static final String SUGGESTION_SPAN_PICKED_AFTER = "after"; 61 public static final String SUGGESTION_SPAN_PICKED_BEFORE = "before"; 62 public static final String SUGGESTION_SPAN_PICKED_HASHCODE = "hashcode"; 63 64 public static final int SUGGESTIONS_MAX_SIZE = 5; 65 66 /* 67 * TODO: Needs to check the validity and add a feature that TextView will change 68 * the current IME to the other IME which is specified in SuggestionSpan. 69 * An IME needs to set the span by specifying the target IME and Subtype of SuggestionSpan. 70 * And the current IME might want to specify any IME as the target IME including other IMEs. 71 */ 72 73 private int mFlags; 74 private final String[] mSuggestions; 75 private final String mLocaleString; 76 private final String mNotificationTargetClassName; 77 private final int mHashCode; 78 79 private float mUnderlineThickness; 80 private int mUnderlineColor; 81 82 /* 83 * TODO: If switching IME is required, needs to add parameters for ids of InputMethodInfo 84 * and InputMethodSubtype. 85 */ 86 87 /** 88 * @param context Context for the application 89 * @param suggestions Suggestions for the string under the span 90 * @param flags Additional flags indicating how this span is handled in TextView 91 */ 92 public SuggestionSpan(Context context, String[] suggestions, int flags) { 93 this(context, null, suggestions, flags, null); 94 } 95 96 /** 97 * @param locale Locale of the suggestions 98 * @param suggestions Suggestions for the string under the span 99 * @param flags Additional flags indicating how this span is handled in TextView 100 */ 101 public SuggestionSpan(Locale locale, String[] suggestions, int flags) { 102 this(null, locale, suggestions, flags, null); 103 } 104 105 /** 106 * @param context Context for the application 107 * @param locale locale Locale of the suggestions 108 * @param suggestions Suggestions for the string under the span 109 * @param flags Additional flags indicating how this span is handled in TextView 110 * @param notificationTargetClass if not null, this class will get notified when the user 111 * selects one of the suggestions. 112 */ 113 public SuggestionSpan(Context context, Locale locale, String[] suggestions, int flags, 114 Class<?> notificationTargetClass) { 115 final int N = Math.min(SUGGESTIONS_MAX_SIZE, suggestions.length); 116 mSuggestions = Arrays.copyOf(suggestions, N); 117 mFlags = flags; 118 if (context != null && locale == null) { 119 mLocaleString = context.getResources().getConfiguration().locale.toString(); 120 } else { 121 mLocaleString = locale.toString(); 122 } 123 124 if (notificationTargetClass != null) { 125 mNotificationTargetClassName = notificationTargetClass.getCanonicalName(); 126 } else { 127 mNotificationTargetClassName = ""; 128 } 129 mHashCode = hashCodeInternal(mSuggestions, mLocaleString, mNotificationTargetClassName); 130 131 initStyle(context); 132 } 133 134 private void initStyle(Context context) { 135 int defStyle = 0; 136 if ((getFlags() & FLAG_MISSPELLED) != 0) { 137 defStyle = com.android.internal.R.attr.textAppearanceMisspelledSuggestion; 138 } else if ((getFlags() & FLAG_EASY_CORRECT) != 0) { 139 defStyle = com.android.internal.R.attr.textAppearanceEasyCorrectSuggestion; 140 } else { 141 // No style is applied. 142 mUnderlineThickness = 0; 143 mUnderlineColor = 0; 144 return; 145 } 146 147 TypedArray typedArray = context.obtainStyledAttributes(null, 148 com.android.internal.R.styleable.SuggestionSpan, 149 defStyle, 0); 150 151 mUnderlineThickness = typedArray.getDimension( 152 com.android.internal.R.styleable.SuggestionSpan_textUnderlineThickness, 0); 153 mUnderlineColor = typedArray.getColor( 154 com.android.internal.R.styleable.SuggestionSpan_textUnderlineColor, Color.BLACK); 155 } 156 157 public SuggestionSpan(Parcel src) { 158 mSuggestions = src.readStringArray(); 159 mFlags = src.readInt(); 160 mLocaleString = src.readString(); 161 mNotificationTargetClassName = src.readString(); 162 mHashCode = src.readInt(); 163 mUnderlineColor = src.readInt(); 164 mUnderlineThickness = src.readFloat(); 165 } 166 167 /** 168 * @return an array of suggestion texts for this span 169 */ 170 public String[] getSuggestions() { 171 return mSuggestions; 172 } 173 174 /** 175 * @return the locale of the suggestions 176 */ 177 public String getLocale() { 178 return mLocaleString; 179 } 180 181 /** 182 * @return The name of the class to notify. The class of the original IME package will receive 183 * a notification when the user selects one of the suggestions. The notification will include 184 * the original string, the suggested replacement string as well as the hashCode of this span. 185 * The class will get notified by an intent that has those information. 186 * This is an internal API because only the framework should know the class name. 187 * 188 * @hide 189 */ 190 public String getNotificationTargetClassName() { 191 return mNotificationTargetClassName; 192 } 193 194 public int getFlags() { 195 return mFlags; 196 } 197 198 public void setFlags(int flags) { 199 mFlags = flags; 200 } 201 202 @Override 203 public int describeContents() { 204 return 0; 205 } 206 207 @Override 208 public void writeToParcel(Parcel dest, int flags) { 209 dest.writeStringArray(mSuggestions); 210 dest.writeInt(mFlags); 211 dest.writeString(mLocaleString); 212 dest.writeString(mNotificationTargetClassName); 213 dest.writeInt(mHashCode); 214 dest.writeInt(mUnderlineColor); 215 dest.writeFloat(mUnderlineThickness); 216 } 217 218 @Override 219 public int getSpanTypeId() { 220 return TextUtils.SUGGESTION_SPAN; 221 } 222 223 @Override 224 public boolean equals(Object o) { 225 if (o instanceof SuggestionSpan) { 226 return ((SuggestionSpan)o).hashCode() == mHashCode; 227 } 228 return false; 229 } 230 231 @Override 232 public int hashCode() { 233 return mHashCode; 234 } 235 236 private static int hashCodeInternal(String[] suggestions, String locale, 237 String notificationTargetClassName) { 238 return Arrays.hashCode(new Object[] {Long.valueOf(SystemClock.uptimeMillis()), suggestions, 239 locale, notificationTargetClassName}); 240 } 241 242 public static final Parcelable.Creator<SuggestionSpan> CREATOR = 243 new Parcelable.Creator<SuggestionSpan>() { 244 @Override 245 public SuggestionSpan createFromParcel(Parcel source) { 246 return new SuggestionSpan(source); 247 } 248 249 @Override 250 public SuggestionSpan[] newArray(int size) { 251 return new SuggestionSpan[size]; 252 } 253 }; 254 255 @Override 256 public void updateDrawState(TextPaint tp) { 257 tp.setUnderlineText(mUnderlineColor, mUnderlineThickness); 258 } 259} 260