diff options
-rw-r--r-- | java/res/raw/setup_welcome_image.png | bin | 50389 -> 151016 bytes | |||
-rw-r--r-- | java/res/raw/setup_welcome_video.mp4 | bin | 549513 -> 378604 bytes | |||
-rw-r--r-- | java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java | 7 | ||||
-rw-r--r-- | java/src/com/android/inputmethod/latin/BinaryDictionary.java | 5 | ||||
-rw-r--r-- | java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java | 60 | ||||
-rw-r--r-- | java/src/com/android/inputmethod/latin/NgramContext.java | 5 | ||||
-rw-r--r-- | java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java | 31 | ||||
-rw-r--r-- | java/src/com/android/inputmethod/latin/settings/CustomInputStyleSettingsFragment.java | 54 | ||||
-rw-r--r-- | java/src/com/android/inputmethod/latin/setup/SetupActivity.java | 4 | ||||
-rw-r--r-- | java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java | 5 | ||||
-rw-r--r-- | tests/src/com/android/inputmethod/compat/SuggestionSpanUtilsTest.java | 181 |
11 files changed, 267 insertions, 85 deletions
diff --git a/java/res/raw/setup_welcome_image.png b/java/res/raw/setup_welcome_image.png Binary files differindex 98e731331..613636907 100644 --- a/java/res/raw/setup_welcome_image.png +++ b/java/res/raw/setup_welcome_image.png diff --git a/java/res/raw/setup_welcome_video.mp4 b/java/res/raw/setup_welcome_video.mp4 Binary files differindex 224bf250c..09be565e4 100644 --- a/java/res/raw/setup_welcome_video.mp4 +++ b/java/res/raw/setup_welcome_video.mp4 diff --git a/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java index c33c01552..27db9b8e8 100644 --- a/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java +++ b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java @@ -23,10 +23,11 @@ import android.text.Spanned; import android.text.TextUtils; import android.text.style.SuggestionSpan; +import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; -import com.android.inputmethod.latin.define.DebugFlags; import com.android.inputmethod.latin.SuggestionSpanPickedNotificationReceiver; +import com.android.inputmethod.latin.define.DebugFlags; import java.lang.reflect.Field; import java.util.ArrayList; @@ -51,12 +52,14 @@ public final class SuggestionSpanUtils { // This utility class is not publicly instantiable. } + @UsedForTesting public static CharSequence getTextWithAutoCorrectionIndicatorUnderline( final Context context, final String text) { if (TextUtils.isEmpty(text) || OBJ_FLAG_AUTO_CORRECTION == null) { return text; } final Spannable spannable = new SpannableString(text); + // TODO: Set locale if it is feasible. final SuggestionSpan suggestionSpan = new SuggestionSpan(context, null /* locale */, new String[] {} /* suggestions */, OBJ_FLAG_AUTO_CORRECTION, SuggestionSpanPickedNotificationReceiver.class); @@ -65,6 +68,7 @@ public final class SuggestionSpanUtils { return spannable; } + @UsedForTesting public static CharSequence getTextWithSuggestionSpan(final Context context, final String pickedWord, final SuggestedWords suggestedWords) { if (TextUtils.isEmpty(pickedWord) || suggestedWords.isEmpty() @@ -86,6 +90,7 @@ public final class SuggestionSpanUtils { suggestionsList.add(word.toString()); } } + // TODO: Set locale if it is feasible. final SuggestionSpan suggestionSpan = new SuggestionSpan(context, null /* locale */, suggestionsList.toArray(new String[suggestionsList.size()]), 0 /* flags */, SuggestionSpanPickedNotificationReceiver.class); diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java index 86c265f86..10dea749d 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java @@ -42,6 +42,8 @@ import java.util.HashMap; import java.util.Locale; import java.util.Map; +import javax.annotation.Nonnull; + /** * Implements a static, compacted, binary dictionary of standard words. */ @@ -495,8 +497,7 @@ public final class BinaryDictionary extends Dictionary { } // Update entries for the word occurrence with the ngramContext. - @UsedForTesting - public boolean updateEntriesForWordWithNgramContext(final NgramContext ngramContext, + public boolean updateEntriesForWordWithNgramContext(@Nonnull final NgramContext ngramContext, final String word, final boolean isValidWord, final int count, final int timestamp) { if (TextUtils.isEmpty(word)) { return false; diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java index 1bdadc30b..d24f80a45 100644 --- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java @@ -46,6 +46,8 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantReadWriteLock; +import javax.annotation.Nonnull; + /** * Abstract base class for an expandable dictionary that can be created and updated dynamically * during runtime. When updated it automatically generates a new binary dictionary to handle future @@ -292,13 +294,9 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { } } - /** - * Adds unigram information of a word to the dictionary. May overwrite an existing entry. - */ - public void addUnigramEntryWithCheckingDistracter(final String word, final int frequency, - final String shortcutTarget, final int shortcutFreq, final boolean isNotAWord, - final boolean isBlacklisted, final int timestamp, - final DistracterFilter distracterFilter) { + private void updateDictionaryWithWriteLockIfWordIsNotADistracter( + @Nonnull final Runnable updateTask, + @Nonnull final String word, @Nonnull final DistracterFilter distracterFilter) { reloadDictionaryIfRequired(); asyncPreCheckAndExecuteTaskWithWriteLock( new Callable<Boolean>() { @@ -315,12 +313,27 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { return; } runGCIfRequiredLocked(true /* mindsBlockByGC */); - addUnigramLocked(word, frequency, shortcutTarget, shortcutFreq, - isNotAWord, isBlacklisted, timestamp); + updateTask.run(); } }); } + /** + * Adds unigram information of a word to the dictionary. May overwrite an existing entry. + */ + public void addUnigramEntryWithCheckingDistracter(final String word, final int frequency, + final String shortcutTarget, final int shortcutFreq, final boolean isNotAWord, + final boolean isBlacklisted, final int timestamp, + @Nonnull final DistracterFilter distracterFilter) { + updateDictionaryWithWriteLockIfWordIsNotADistracter(new Runnable() { + @Override + public void run() { + addUnigramLocked(word, frequency, shortcutTarget, shortcutFreq, + isNotAWord, isBlacklisted, timestamp); + } + }, word, distracterFilter); + } + protected void addUnigramLocked(final String word, final int frequency, final String shortcutTarget, final int shortcutFreq, final boolean isNotAWord, final boolean isBlacklisted, final int timestamp) { @@ -354,7 +367,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { /** * Adds n-gram information of a word to the dictionary. May overwrite an existing entry. */ - public void addNgramEntry(final NgramContext ngramContext, final String word, + public void addNgramEntry(@Nonnull final NgramContext ngramContext, final String word, final int frequency, final int timestamp) { reloadDictionaryIfRequired(); asyncExecuteTaskWithWriteLock(new Runnable() { @@ -369,7 +382,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { }); } - protected void addNgramEntryLocked(final NgramContext ngramContext, final String word, + protected void addNgramEntryLocked(@Nonnull final NgramContext ngramContext, final String word, final int frequency, final int timestamp) { if (!mBinaryDictionary.addNgramEntry(ngramContext, word, frequency, timestamp)) { if (DEBUG) { @@ -383,7 +396,8 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { * Dynamically remove the n-gram entry in the dictionary. */ @UsedForTesting - public void removeNgramDynamically(final NgramContext ngramContext, final String word) { + public void removeNgramDynamically(@Nonnull final NgramContext ngramContext, + final String word) { reloadDictionaryIfRequired(); asyncExecuteTaskWithWriteLock(new Runnable() { @Override @@ -402,6 +416,26 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { }); } + /** + * Update dictionary for the word with the ngramContext if the word is not a distracter. + */ + public void updateEntriesForWordWithCheckingDistracter(@Nonnull final NgramContext ngramContext, + final String word, final boolean isValidWord, final int count, final int timestamp, + @Nonnull final DistracterFilter distracterFilter) { + updateDictionaryWithWriteLockIfWordIsNotADistracter(new Runnable() { + @Override + public void run() { + if (!mBinaryDictionary.updateEntriesForWordWithNgramContext(ngramContext, word, + isValidWord, count, timestamp)) { + if (DEBUG) { + Log.e(TAG, "Cannot update counter. word: " + word + + " context: "+ ngramContext.toString()); + } + } + } + }, word, distracterFilter); + } + public interface AddMultipleDictionaryEntriesCallback { public void onFinished(); } @@ -410,7 +444,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { * Dynamically add multiple entries to the dictionary. */ public void addMultipleDictionaryEntriesDynamically( - final ArrayList<LanguageModelParam> languageModelParams, + @Nonnull final ArrayList<LanguageModelParam> languageModelParams, final AddMultipleDictionaryEntriesCallback callback) { reloadDictionaryIfRequired(); asyncExecuteTaskWithWriteLock(new Runnable() { diff --git a/java/src/com/android/inputmethod/latin/NgramContext.java b/java/src/com/android/inputmethod/latin/NgramContext.java index 6d438584f..a02531cc4 100644 --- a/java/src/com/android/inputmethod/latin/NgramContext.java +++ b/java/src/com/android/inputmethod/latin/NgramContext.java @@ -158,11 +158,6 @@ public class NgramContext { } } - public NgramContext getTrimmedNgramContext(final int maxPrevWordCount) { - final int newSize = Math.min(maxPrevWordCount, mPrevWordsCount); - return new NgramContext(this /* prevWordsInfo */, newSize); - } - public int getPrevWordCount() { return mPrevWordsCount; } diff --git a/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java b/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java index d61684698..59761547d 100644 --- a/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java +++ b/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java @@ -65,34 +65,7 @@ public class UserHistoryDictionary extends DecayingExpandableBinaryDictionaryBas if (word.length() > Constants.DICTIONARY_MAX_WORD_LENGTH) { return; } - final int frequency = isValid ? - FREQUENCY_FOR_WORDS_IN_DICTS : FREQUENCY_FOR_WORDS_NOT_IN_DICTS; - userHistoryDictionary.addUnigramEntryWithCheckingDistracter(word, frequency, - null /* shortcutTarget */, 0 /* shortcutFreq */, false /* isNotAWord */, - false /* isBlacklisted */, timestamp, distracterFilter); - - final boolean isBeginningOfSentenceContext = ngramContext.isBeginningOfSentenceContext(); - final NgramContext ngramContextToBeSaved = - ngramContext.getTrimmedNgramContext(SUPPORTED_NGRAM - 1); - for (int i = 0; i < ngramContextToBeSaved.getPrevWordCount(); i++) { - final CharSequence prevWord = ngramContextToBeSaved.getNthPrevWord(1 /* n */); - if (prevWord == null || (prevWord.length() > Constants.DICTIONARY_MAX_WORD_LENGTH)) { - return; - } - // Do not insert a word as a bigram of itself - if (i == 0 && TextUtils.equals(word, prevWord)) { - return; - } - if (isBeginningOfSentenceContext) { - // Beginning-of-Sentence n-gram entry is added as an n-gram entry of an OOV word. - userHistoryDictionary.addNgramEntry( - ngramContextToBeSaved.getTrimmedNgramContext(i + 1), word, - FREQUENCY_FOR_WORDS_NOT_IN_DICTS, timestamp); - } else { - userHistoryDictionary.addNgramEntry( - ngramContextToBeSaved.getTrimmedNgramContext(i + 1), word, frequency, - timestamp); - } - } + userHistoryDictionary.updateEntriesForWordWithCheckingDistracter(ngramContext, word, + isValid, 1 /* count */, timestamp, distracterFilter); } } diff --git a/java/src/com/android/inputmethod/latin/settings/CustomInputStyleSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/CustomInputStyleSettingsFragment.java index 2174f5224..c633fc167 100644 --- a/java/src/com/android/inputmethod/latin/settings/CustomInputStyleSettingsFragment.java +++ b/java/src/com/android/inputmethod/latin/settings/CustomInputStyleSettingsFragment.java @@ -33,7 +33,6 @@ import android.preference.PreferenceGroup; import android.support.v4.view.ViewCompat; import android.text.TextUtils; import android.util.Log; -import android.util.Pair; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; @@ -80,25 +79,26 @@ public final class CustomInputStyleSettingsFragment extends PreferenceFragment { "is_subtype_enabler_notification_dialog_open"; private static final String KEY_SUBTYPE_FOR_SUBTYPE_ENABLER = "subtype_for_subtype_enabler"; - static final class SubtypeLocaleItem extends Pair<String, String> - implements Comparable<SubtypeLocaleItem> { - public SubtypeLocaleItem(final String localeString, final String displayName) { - super(localeString, displayName); - } + static final class SubtypeLocaleItem implements Comparable<SubtypeLocaleItem> { + public final String mLocaleString; + private final String mDisplayName; - public SubtypeLocaleItem(final String localeString) { - this(localeString, - SubtypeLocaleUtils.getSubtypeLocaleDisplayNameInSystemLocale(localeString)); + public SubtypeLocaleItem(final InputMethodSubtype subtype) { + mLocaleString = subtype.getLocale(); + mDisplayName = SubtypeLocaleUtils.getSubtypeLocaleDisplayNameInSystemLocale( + mLocaleString); } + // {@link ArrayAdapter<T>} that hosts the instance of this class needs {@link #toString()} + // to get display name. @Override public String toString() { - return second; + return mDisplayName; } @Override public int compareTo(final SubtypeLocaleItem o) { - return first.compareTo(o.first); + return mLocaleString.compareTo(o.mLocaleString); } } @@ -121,32 +121,28 @@ public final class CustomInputStyleSettingsFragment extends PreferenceFragment { SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(subtype))); } if (InputMethodSubtypeCompatUtils.isAsciiCapable(subtype)) { - items.add(createItem(context, subtype.getLocale())); + items.add(new SubtypeLocaleItem(subtype)); } } // TODO: Should filter out already existing combinations of locale and layout. addAll(items); } - - public static SubtypeLocaleItem createItem(final Context context, - final String localeString) { - if (localeString.equals(SubtypeLocaleUtils.NO_LANGUAGE)) { - final String displayName = context.getString(R.string.subtype_no_language); - return new SubtypeLocaleItem(localeString, displayName); - } - return new SubtypeLocaleItem(localeString); - } } - static final class KeyboardLayoutSetItem extends Pair<String, String> { + static final class KeyboardLayoutSetItem { + public final String mLayoutName; + private final String mDisplayName; + public KeyboardLayoutSetItem(final InputMethodSubtype subtype) { - super(SubtypeLocaleUtils.getKeyboardLayoutSetName(subtype), - SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(subtype)); + mLayoutName = SubtypeLocaleUtils.getKeyboardLayoutSetName(subtype); + mDisplayName = SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(subtype); } + // {@link ArrayAdapter<T>} that hosts the instance of this class needs {@link #toString()} + // to get display name. @Override public String toString() { - return second; + return mDisplayName; } } @@ -255,7 +251,6 @@ public final class CustomInputStyleSettingsFragment extends PreferenceFragment { @Override protected void onPrepareDialogBuilder(final AlertDialog.Builder builder) { - final Context context = builder.getContext(); builder.setCancelable(true).setOnCancelListener(this); if (isIncomplete()) { builder.setPositiveButton(R.string.add, this) @@ -264,8 +259,7 @@ public final class CustomInputStyleSettingsFragment extends PreferenceFragment { builder.setPositiveButton(R.string.save, this) .setNeutralButton(android.R.string.cancel, this) .setNegativeButton(R.string.remove, this); - final SubtypeLocaleItem localeItem = SubtypeLocaleAdapter.createItem( - context, mSubtype.getLocale()); + final SubtypeLocaleItem localeItem = new SubtypeLocaleItem(mSubtype); final KeyboardLayoutSetItem layoutItem = new KeyboardLayoutSetItem(mSubtype); setSpinnerPosition(mSubtypeLocaleSpinner, localeItem); setSpinnerPosition(mKeyboardLayoutSetSpinner, layoutItem); @@ -303,7 +297,7 @@ public final class CustomInputStyleSettingsFragment extends PreferenceFragment { (KeyboardLayoutSetItem) mKeyboardLayoutSetSpinner.getSelectedItem(); final InputMethodSubtype subtype = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype( - locale.first, layout.first); + locale.mLocaleString, layout.mLayoutName); setSubtype(subtype); notifyChanged(); if (isEditing) { @@ -469,8 +463,6 @@ public final class CustomInputStyleSettingsFragment extends PreferenceFragment { KEY_IS_SUBTYPE_ENABLER_NOTIFICATION_DIALOG_OPEN)) { mSubtypePreferenceKeyForSubtypeEnabler = savedInstanceState.getString( KEY_SUBTYPE_FOR_SUBTYPE_ENABLER); - final SubtypePreference subtypePref = (SubtypePreference)findPreference( - mSubtypePreferenceKeyForSubtypeEnabler); mSubtypeEnablerNotificationDialog = createDialog(); mSubtypeEnablerNotificationDialog.show(); } diff --git a/java/src/com/android/inputmethod/latin/setup/SetupActivity.java b/java/src/com/android/inputmethod/latin/setup/SetupActivity.java index b770ea512..7607429f8 100644 --- a/java/src/com/android/inputmethod/latin/setup/SetupActivity.java +++ b/java/src/com/android/inputmethod/latin/setup/SetupActivity.java @@ -17,12 +17,8 @@ package com.android.inputmethod.latin.setup; import android.app.Activity; -import android.content.Context; import android.content.Intent; import android.os.Bundle; -import android.provider.Settings; -import android.view.inputmethod.InputMethodInfo; -import android.view.inputmethod.InputMethodManager; public final class SetupActivity extends Activity { @Override diff --git a/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java b/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java index e455e53d3..54562f39d 100644 --- a/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java +++ b/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java @@ -46,6 +46,8 @@ import java.util.ArrayList; public final class SetupWizardActivity extends Activity implements View.OnClickListener { static final String TAG = SetupWizardActivity.class.getSimpleName(); + // For debugging purpose. + private static final boolean FORCE_TO_SHOW_WELCOME_SCREEN = false; private static final boolean ENABLE_WELCOME_VIDEO = true; private InputMethodManager mImm; @@ -304,6 +306,9 @@ public final class SetupWizardActivity extends Activity implements View.OnClickL private int determineSetupStepNumber() { mHandler.cancelPollingImeSettings(); + if (FORCE_TO_SHOW_WELCOME_SCREEN) { + return STEP_1; + } if (!UncachedInputMethodManagerUtils.isThisImeEnabled(this, mImm)) { return STEP_1; } diff --git a/tests/src/com/android/inputmethod/compat/SuggestionSpanUtilsTest.java b/tests/src/com/android/inputmethod/compat/SuggestionSpanUtilsTest.java new file mode 100644 index 000000000..a6b3af4c2 --- /dev/null +++ b/tests/src/com/android/inputmethod/compat/SuggestionSpanUtilsTest.java @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2014 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 com.android.inputmethod.compat; + +import android.annotation.TargetApi; +import android.os.Build; +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.SmallTest; +import android.text.Spanned; +import android.text.TextUtils; +import android.text.style.SuggestionSpan; + +import com.android.inputmethod.latin.SuggestedWords; +import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Locale; + +@SmallTest +public class SuggestionSpanUtilsTest extends AndroidTestCase { + + /** + * Helper method to create a dummy {@link SuggestedWordInfo}. + * + * @param kindAndFlags the kind and flags to be used to create {@link SuggestedWordInfo}. + * @param word the word to be used to create {@link SuggestedWordInfo}. + * @return a new instance of {@link SuggestedWordInfo}. + */ + private static SuggestedWordInfo createWordInfo(final String word, final int kindAndFlags) { + return new SuggestedWordInfo(word, 1 /* score */, kindAndFlags, null /* sourceDict */, + SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */, + SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */); + } + + private static void assertNotSuggestionSpan(final String expectedText, + final CharSequence actualText) { + assertTrue(TextUtils.equals(expectedText, actualText)); + if (!(actualText instanceof Spanned)) { + return; + } + final Spanned spanned = (Spanned)actualText; + final SuggestionSpan[] suggestionSpans = spanned.getSpans(0, spanned.length(), + SuggestionSpan.class); + assertEquals(0, suggestionSpans.length); + } + + private static void assertSuggestionSpan(final String expectedText, + final int reuiredSuggestionSpanFlags, final int requiredSpanFlags, + final String[] expectedSuggestions, + final CharSequence actualText) { + assertTrue(TextUtils.equals(expectedText, actualText)); + assertTrue(actualText instanceof Spanned); + final Spanned spanned = (Spanned)actualText; + final SuggestionSpan[] suggestionSpans = spanned.getSpans(0, spanned.length(), + SuggestionSpan.class); + assertEquals(1, suggestionSpans.length); + final SuggestionSpan suggestionSpan = suggestionSpans[0]; + if (reuiredSuggestionSpanFlags != 0) { + assertTrue((suggestionSpan.getFlags() & reuiredSuggestionSpanFlags) != 0); + } + if (requiredSpanFlags != 0) { + assertTrue((spanned.getSpanFlags(suggestionSpan) & requiredSpanFlags) != 0); + } + if (expectedSuggestions != null) { + final String[] actualSuggestions = suggestionSpan.getSuggestions(); + assertEquals(expectedSuggestions.length, actualSuggestions.length); + for (int i = 0; i < expectedSuggestions.length; ++i) { + assertEquals(expectedSuggestions[i], actualSuggestions[i]); + } + } + } + + @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) + public void testGetTextWithAutoCorrectionIndicatorUnderline() { + final String ORIGINAL_TEXT = "Hey!"; + final CharSequence text = SuggestionSpanUtils.getTextWithAutoCorrectionIndicatorUnderline( + getContext(), ORIGINAL_TEXT); + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) { + assertNotSuggestionSpan(ORIGINAL_TEXT, text); + return; + } + + assertSuggestionSpan(ORIGINAL_TEXT, + SuggestionSpan.FLAG_AUTO_CORRECTION /* reuiredSuggestionSpanFlags */, + Spanned.SPAN_COMPOSING | Spanned.SPAN_EXCLUSIVE_EXCLUSIVE /* requiredSpanFlags */, + new String[]{}, text); + } + + public void testGetTextWithSuggestionSpan() { + final SuggestedWordInfo predicition1 = + createWordInfo("Quality", SuggestedWordInfo.KIND_PREDICTION); + final SuggestedWordInfo predicition2 = + createWordInfo("Speed", SuggestedWordInfo.KIND_PREDICTION); + final SuggestedWordInfo predicition3 = + createWordInfo("Price", SuggestedWordInfo.KIND_PREDICTION); + + final SuggestedWordInfo typed = + createWordInfo("Hey", SuggestedWordInfo.KIND_TYPED); + + final SuggestedWordInfo[] corrections = + new SuggestedWordInfo[SuggestionSpan.SUGGESTIONS_MAX_SIZE * 2]; + for (int i = 0; i < corrections.length; ++i) { + corrections[i] = createWordInfo("correction" + i, SuggestedWordInfo.KIND_CORRECTION); + } + + // SuggestionSpan will not be attached when {@link SuggestedWords#INPUT_STYLE_PREDICTION} + // is specified. + { + final SuggestedWords predictedWords = new SuggestedWords( + new ArrayList<>(Arrays.asList(predicition1, predicition2, predicition3)), + null /* rawSuggestions */, + false /* typedWordValid */, + false /* willAutoCorrect */, + false /* isObsoleteSuggestions */, + SuggestedWords.INPUT_STYLE_PREDICTION); + final String PICKED_WORD = predicition2.mWord; + assertNotSuggestionSpan( + PICKED_WORD, + SuggestionSpanUtils.getTextWithSuggestionSpan(getContext(), PICKED_WORD, + predictedWords)); + } + + final ArrayList<SuggestedWordInfo> suggestedWordList = new ArrayList<>(); + suggestedWordList.add(typed); + suggestedWordList.add(predicition1); + suggestedWordList.add(predicition2); + suggestedWordList.add(predicition3); + suggestedWordList.addAll(Arrays.asList(corrections)); + final SuggestedWords typedAndCollectedWords = new SuggestedWords( + suggestedWordList, + null /* rawSuggestions */, + false /* typedWordValid */, + false /* willAutoCorrect */, + false /* isObsoleteSuggestions */, + SuggestedWords.INPUT_STYLE_TYPING); + + for (final SuggestedWordInfo pickedWord : suggestedWordList) { + final String PICKED_WORD = pickedWord.mWord; + + final ArrayList<String> expectedSuggestions = new ArrayList<>(); + for (SuggestedWordInfo suggestedWordInfo : suggestedWordList) { + if (expectedSuggestions.size() >= SuggestionSpan.SUGGESTIONS_MAX_SIZE) { + break; + } + if (suggestedWordInfo.isKindOf(SuggestedWordInfo.KIND_PREDICTION)) { + // Currently predictions are not filled into SuggestionSpan. + continue; + } + final String suggestedWord = suggestedWordInfo.mWord; + if (TextUtils.equals(PICKED_WORD, suggestedWord)) { + // Typed word itself is not added to SuggestionSpan. + continue; + } + expectedSuggestions.add(suggestedWord); + } + + assertSuggestionSpan( + PICKED_WORD, + 0 /* reuiredSuggestionSpanFlags */, + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE /* requiredSpanFlags */, + expectedSuggestions.toArray(new String[expectedSuggestions.size()]), + SuggestionSpanUtils.getTextWithSuggestionSpan(getContext(), PICKED_WORD, + typedAndCollectedWords)); + } + } +} |