11fef530ec7626fa16777f52b48191e61db8f46d4satok/* 21fef530ec7626fa16777f52b48191e61db8f46d4satok * Copyright (C) 2011 The Android Open Source Project 31fef530ec7626fa16777f52b48191e61db8f46d4satok * 41fef530ec7626fa16777f52b48191e61db8f46d4satok * Licensed under the Apache License, Version 2.0 (the "License"); 51fef530ec7626fa16777f52b48191e61db8f46d4satok * you may not use this file except in compliance with the License. 61fef530ec7626fa16777f52b48191e61db8f46d4satok * You may obtain a copy of the License at 71fef530ec7626fa16777f52b48191e61db8f46d4satok * 81fef530ec7626fa16777f52b48191e61db8f46d4satok * http://www.apache.org/licenses/LICENSE-2.0 91fef530ec7626fa16777f52b48191e61db8f46d4satok * 101fef530ec7626fa16777f52b48191e61db8f46d4satok * Unless required by applicable law or agreed to in writing, software 111fef530ec7626fa16777f52b48191e61db8f46d4satok * distributed under the License is distributed on an "AS IS" BASIS, 121fef530ec7626fa16777f52b48191e61db8f46d4satok * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 131fef530ec7626fa16777f52b48191e61db8f46d4satok * See the License for the specific language governing permissions and 141fef530ec7626fa16777f52b48191e61db8f46d4satok * limitations under the License. 151fef530ec7626fa16777f52b48191e61db8f46d4satok */ 161fef530ec7626fa16777f52b48191e61db8f46d4satok 171fef530ec7626fa16777f52b48191e61db8f46d4satokpackage com.android.inputmethod.compat; 181fef530ec7626fa16777f52b48191e61db8f46d4satok 1968ef7b77ede812051668465b1d65119735cf643dsatokimport com.android.inputmethod.latin.LatinImeLogger; 201fef530ec7626fa16777f52b48191e61db8f46d4satokimport com.android.inputmethod.latin.SuggestedWords; 21ea9098ca69599905fdd5fd5eba55dc63d2073357satokimport com.android.inputmethod.latin.SuggestionSpanPickedNotificationReceiver; 221fef530ec7626fa16777f52b48191e61db8f46d4satok 231fef530ec7626fa16777f52b48191e61db8f46d4satokimport android.content.Context; 241fef530ec7626fa16777f52b48191e61db8f46d4satokimport android.text.Spannable; 251fef530ec7626fa16777f52b48191e61db8f46d4satokimport android.text.SpannableString; 261fef530ec7626fa16777f52b48191e61db8f46d4satokimport android.text.Spanned; 27064e21bb1d634fb986598804302ac77172e51367satokimport android.text.TextUtils; 28ec780e2868962bf17f0dfd35d36895f543bde40asatokimport android.util.Log; 291fef530ec7626fa16777f52b48191e61db8f46d4satok 301fef530ec7626fa16777f52b48191e61db8f46d4satokimport java.lang.reflect.Constructor; 3168ef7b77ede812051668465b1d65119735cf643dsatokimport java.lang.reflect.Field; 32064e21bb1d634fb986598804302ac77172e51367satokimport java.util.ArrayList; 33ea9098ca69599905fdd5fd5eba55dc63d2073357satokimport java.util.Locale; 341fef530ec7626fa16777f52b48191e61db8f46d4satok 351fef530ec7626fa16777f52b48191e61db8f46d4satokpublic class SuggestionSpanUtils { 36ec780e2868962bf17f0dfd35d36895f543bde40asatok private static final String TAG = SuggestionSpanUtils.class.getSimpleName(); 3723726dbdca0478297a4eed5dcce70b1bace34293satok // TODO: Use reflection to get field values 38ea9098ca69599905fdd5fd5eba55dc63d2073357satok public static final String ACTION_SUGGESTION_PICKED = 39ea9098ca69599905fdd5fd5eba55dc63d2073357satok "android.text.style.SUGGESTION_PICKED"; 40ea9098ca69599905fdd5fd5eba55dc63d2073357satok public static final String SUGGESTION_SPAN_PICKED_AFTER = "after"; 41ea9098ca69599905fdd5fd5eba55dc63d2073357satok public static final String SUGGESTION_SPAN_PICKED_BEFORE = "before"; 42ea9098ca69599905fdd5fd5eba55dc63d2073357satok public static final String SUGGESTION_SPAN_PICKED_HASHCODE = "hashcode"; 4323726dbdca0478297a4eed5dcce70b1bace34293satok public static final boolean SUGGESTION_SPAN_IS_SUPPORTED; 44ea9098ca69599905fdd5fd5eba55dc63d2073357satok 45ea9098ca69599905fdd5fd5eba55dc63d2073357satok private static final Class<?> CLASS_SuggestionSpan = CompatUtils 46ea9098ca69599905fdd5fd5eba55dc63d2073357satok .getClass("android.text.style.SuggestionSpan"); 471fef530ec7626fa16777f52b48191e61db8f46d4satok private static final Class<?>[] INPUT_TYPE_SuggestionSpan = new Class<?>[] { 48ea9098ca69599905fdd5fd5eba55dc63d2073357satok Context.class, Locale.class, String[].class, int.class, Class.class }; 49ea9098ca69599905fdd5fd5eba55dc63d2073357satok private static final Constructor<?> CONSTRUCTOR_SuggestionSpan = CompatUtils 50ea9098ca69599905fdd5fd5eba55dc63d2073357satok .getConstructor(CLASS_SuggestionSpan, INPUT_TYPE_SuggestionSpan); 5168ef7b77ede812051668465b1d65119735cf643dsatok public static final Field FIELD_FLAG_AUTO_CORRECTION 5268ef7b77ede812051668465b1d65119735cf643dsatok = CompatUtils.getField(CLASS_SuggestionSpan, "FLAG_AUTO_CORRECTION"); 5368ef7b77ede812051668465b1d65119735cf643dsatok public static final Field FIELD_SUGGESTION_MAX_SIZE 54e5db3871b1269703e5ef360090fb35f50ae57ed4satok = CompatUtils.getField(CLASS_SuggestionSpan, "SUGGESTIONS_MAX_SIZE"); 5568ef7b77ede812051668465b1d65119735cf643dsatok public static final Integer OBJ_FLAG_AUTO_CORRECTION = (Integer) CompatUtils 5668ef7b77ede812051668465b1d65119735cf643dsatok .getFieldValue(null, null, FIELD_FLAG_AUTO_CORRECTION);; 5768ef7b77ede812051668465b1d65119735cf643dsatok public static final Integer OBJ_SUGGESTION_MAX_SIZE = (Integer) CompatUtils 5868ef7b77ede812051668465b1d65119735cf643dsatok .getFieldValue(null, null, FIELD_SUGGESTION_MAX_SIZE);; 5968ef7b77ede812051668465b1d65119735cf643dsatok 600d4757bb0b65bb9e56d7168d9e04d784c5917873satok static { 61ea9098ca69599905fdd5fd5eba55dc63d2073357satok SUGGESTION_SPAN_IS_SUPPORTED = 62ea9098ca69599905fdd5fd5eba55dc63d2073357satok CLASS_SuggestionSpan != null && CONSTRUCTOR_SuggestionSpan != null; 6368ef7b77ede812051668465b1d65119735cf643dsatok if (LatinImeLogger.sDBG) { 6468ef7b77ede812051668465b1d65119735cf643dsatok if (SUGGESTION_SPAN_IS_SUPPORTED 6568ef7b77ede812051668465b1d65119735cf643dsatok && (OBJ_FLAG_AUTO_CORRECTION == null || OBJ_SUGGESTION_MAX_SIZE == null)) { 66e5db3871b1269703e5ef360090fb35f50ae57ed4satok throw new RuntimeException("Field is accidentially null."); 6768ef7b77ede812051668465b1d65119735cf643dsatok } 6868ef7b77ede812051668465b1d65119735cf643dsatok } 690d4757bb0b65bb9e56d7168d9e04d784c5917873satok } 701fef530ec7626fa16777f52b48191e61db8f46d4satok 71ec780e2868962bf17f0dfd35d36895f543bde40asatok public static CharSequence getTextWithAutoCorrectionIndicatorUnderline( 72ec780e2868962bf17f0dfd35d36895f543bde40asatok Context context, CharSequence text) { 7368ef7b77ede812051668465b1d65119735cf643dsatok if (TextUtils.isEmpty(text) || CONSTRUCTOR_SuggestionSpan == null 74e5db3871b1269703e5ef360090fb35f50ae57ed4satok || OBJ_FLAG_AUTO_CORRECTION == null || OBJ_SUGGESTION_MAX_SIZE == null) { 75ec780e2868962bf17f0dfd35d36895f543bde40asatok return text; 76ec780e2868962bf17f0dfd35d36895f543bde40asatok } 77ec780e2868962bf17f0dfd35d36895f543bde40asatok final Spannable spannable = text instanceof Spannable 78ec780e2868962bf17f0dfd35d36895f543bde40asatok ? (Spannable) text : new SpannableString(text); 79ec780e2868962bf17f0dfd35d36895f543bde40asatok final Object[] args = 8068ef7b77ede812051668465b1d65119735cf643dsatok { context, null, new String[] {}, (int)OBJ_FLAG_AUTO_CORRECTION, 81ec780e2868962bf17f0dfd35d36895f543bde40asatok (Class<?>) SuggestionSpanPickedNotificationReceiver.class }; 82ec780e2868962bf17f0dfd35d36895f543bde40asatok final Object ss = CompatUtils.newInstance(CONSTRUCTOR_SuggestionSpan, args); 83ec780e2868962bf17f0dfd35d36895f543bde40asatok if (ss == null) { 84ec780e2868962bf17f0dfd35d36895f543bde40asatok Log.w(TAG, "Suggestion span was not created."); 85ec780e2868962bf17f0dfd35d36895f543bde40asatok return text; 86ec780e2868962bf17f0dfd35d36895f543bde40asatok } 8753af4ae6346d7647a4e12250f5c0f94386a83af3satok spannable.setSpan(ss, 0, text.length(), 8853af4ae6346d7647a4e12250f5c0f94386a83af3satok Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_COMPOSING); 89ec780e2868962bf17f0dfd35d36895f543bde40asatok return spannable; 90ec780e2868962bf17f0dfd35d36895f543bde40asatok } 91ec780e2868962bf17f0dfd35d36895f543bde40asatok 92ea9098ca69599905fdd5fd5eba55dc63d2073357satok public static CharSequence getTextWithSuggestionSpan(Context context, 9323726dbdca0478297a4eed5dcce70b1bace34293satok CharSequence pickedWord, SuggestedWords suggestedWords) { 9423726dbdca0478297a4eed5dcce70b1bace34293satok if (TextUtils.isEmpty(pickedWord) || CONSTRUCTOR_SuggestionSpan == null 95add3e053797d7d2355c61160ab746f7dfeb92aefsatok || suggestedWords == null || suggestedWords.size() == 0 9668ef7b77ede812051668465b1d65119735cf643dsatok || suggestedWords.getInfo(0).isObsoleteSuggestedWord() 9768ef7b77ede812051668465b1d65119735cf643dsatok || OBJ_SUGGESTION_MAX_SIZE == null) { 9823726dbdca0478297a4eed5dcce70b1bace34293satok return pickedWord; 991fef530ec7626fa16777f52b48191e61db8f46d4satok } 1001fef530ec7626fa16777f52b48191e61db8f46d4satok 1011fef530ec7626fa16777f52b48191e61db8f46d4satok final Spannable spannable; 10223726dbdca0478297a4eed5dcce70b1bace34293satok if (pickedWord instanceof Spannable) { 10323726dbdca0478297a4eed5dcce70b1bace34293satok spannable = (Spannable) pickedWord; 1041fef530ec7626fa16777f52b48191e61db8f46d4satok } else { 10523726dbdca0478297a4eed5dcce70b1bace34293satok spannable = new SpannableString(pickedWord); 1061fef530ec7626fa16777f52b48191e61db8f46d4satok } 107064e21bb1d634fb986598804302ac77172e51367satok final ArrayList<String> suggestionsList = new ArrayList<String>(); 108064e21bb1d634fb986598804302ac77172e51367satok for (int i = 0; i < suggestedWords.size(); ++i) { 10968ef7b77ede812051668465b1d65119735cf643dsatok if (suggestionsList.size() >= OBJ_SUGGESTION_MAX_SIZE) { 110064e21bb1d634fb986598804302ac77172e51367satok break; 111064e21bb1d634fb986598804302ac77172e51367satok } 112064e21bb1d634fb986598804302ac77172e51367satok final CharSequence word = suggestedWords.getWord(i); 11323726dbdca0478297a4eed5dcce70b1bace34293satok if (!TextUtils.equals(pickedWord, word)) { 114064e21bb1d634fb986598804302ac77172e51367satok suggestionsList.add(word.toString()); 115064e21bb1d634fb986598804302ac77172e51367satok } 1161fef530ec7626fa16777f52b48191e61db8f46d4satok } 117064e21bb1d634fb986598804302ac77172e51367satok 118ea9098ca69599905fdd5fd5eba55dc63d2073357satok final Object[] args = 119064e21bb1d634fb986598804302ac77172e51367satok { context, null, suggestionsList.toArray(new String[suggestionsList.size()]), 0, 120ea9098ca69599905fdd5fd5eba55dc63d2073357satok (Class<?>) SuggestionSpanPickedNotificationReceiver.class }; 1211fef530ec7626fa16777f52b48191e61db8f46d4satok final Object ss = CompatUtils.newInstance(CONSTRUCTOR_SuggestionSpan, args); 1221fef530ec7626fa16777f52b48191e61db8f46d4satok if (ss == null) { 12323726dbdca0478297a4eed5dcce70b1bace34293satok return pickedWord; 1241fef530ec7626fa16777f52b48191e61db8f46d4satok } 12523726dbdca0478297a4eed5dcce70b1bace34293satok spannable.setSpan(ss, 0, pickedWord.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 1261fef530ec7626fa16777f52b48191e61db8f46d4satok return spannable; 1271fef530ec7626fa16777f52b48191e61db8f46d4satok } 1281fef530ec7626fa16777f52b48191e61db8f46d4satok} 129