diff options
19 files changed, 348 insertions, 189 deletions
diff --git a/common/src/com/android/inputmethod/latin/common/Constants.java b/common/src/com/android/inputmethod/latin/common/Constants.java index a860d3560..29cdb8ee6 100644 --- a/common/src/com/android/inputmethod/latin/common/Constants.java +++ b/common/src/com/android/inputmethod/latin/common/Constants.java @@ -163,7 +163,6 @@ public final class Constants { // TODO: replace the following constants with state in InputTransaction? public static final int NOT_A_COORDINATE = -1; public static final int SUGGESTION_STRIP_COORDINATE = -2; - public static final int SPELL_CHECKER_COORDINATE = -3; public static final int EXTERNAL_KEYBOARD_COORDINATE = -4; // A hint on how many characters to cache from the TextView. A good value of this is given by @@ -175,10 +174,12 @@ public final class Constants { public static final int MAX_CHARACTERS_FOR_RECAPITALIZATION = 1024 * 100; // Must be equal to MAX_WORD_LENGTH in native/jni/src/defines.h + // TODO: create a overlay and update the value appropriately for the new decoder. public static final int DICTIONARY_MAX_WORD_LENGTH = 48; // (MAX_PREV_WORD_COUNT_FOR_N_GRAM + 1)-gram is supported in Java side. Needs to modify // MAX_PREV_WORD_COUNT_FOR_N_GRAM in native/jni/src/defines.h for suggestions. + // TODO: create a overlay and update the value appropriately for the new decoder. public static final int MAX_PREV_WORD_COUNT_FOR_N_GRAM = 3; // Key events coming any faster than this are long-presses. @@ -214,8 +215,6 @@ public final class Constants { public static final int CODE_DASH = '-'; public static final int CODE_SINGLE_QUOTE = '\''; public static final int CODE_DOUBLE_QUOTE = '"'; - public static final int CODE_QUESTION_MARK = '?'; - public static final int CODE_EXCLAMATION_MARK = '!'; public static final int CODE_SLASH = '/'; public static final int CODE_BACKSLASH = '\\'; public static final int CODE_VERTICAL_BAR = '|'; @@ -330,6 +329,10 @@ public final class Constants { */ public static final int DEFAULT_GESTURE_POINTS_CAPACITY = 128; + public static final int MAX_IME_DECODER_RESULTS = 20; + public static final int DECODER_SCORE_SCALAR = 1000000; + public static final int DECODER_MAX_SCORE = 1000000000; + private Constants() { // This utility class is not publicly instantiable. } diff --git a/common/src/com/android/inputmethod/latin/common/UnicodeSurrogate.java b/common/src/com/android/inputmethod/latin/common/UnicodeSurrogate.java new file mode 100644 index 000000000..10974634d --- /dev/null +++ b/common/src/com/android/inputmethod/latin/common/UnicodeSurrogate.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2015 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.latin.common; + +/** + * Emojis are supplementary characters expressed as a low+high pair. For instance, + * the emoji U+1F625 is encoded as "\uD83D\uDE25" in UTF-16, where '\uD83D' is in + * the range of [0xd800, 0xdbff] and '\uDE25' is in the range of [0xdc00, 0xdfff]. + * {@see http://docs.oracle.com/javase/6/docs/api/java/lang/Character.html#unicode} + */ +public final class UnicodeSurrogate { + private static final char LOW_SURROGATE_MIN = '\uD800'; + private static final char LOW_SURROGATE_MAX = '\uDBFF'; + private static final char HIGH_SURROGATE_MIN = '\uDC00'; + private static final char HIGH_SURROGATE_MAX = '\uDFFF'; + + public static boolean isLowSurrogate(final char c) { + return c >= LOW_SURROGATE_MIN && c <= LOW_SURROGATE_MAX; + } + + public static boolean isHighSurrogate(final char c) { + return c >= HIGH_SURROGATE_MIN && c <= HIGH_SURROGATE_MAX; + } +} diff --git a/java/src/com/android/inputmethod/keyboard/emoji/EmojiPalettesView.java b/java/src/com/android/inputmethod/keyboard/emoji/EmojiPalettesView.java index 9ab465207..f4c4f1aab 100644 --- a/java/src/com/android/inputmethod/keyboard/emoji/EmojiPalettesView.java +++ b/java/src/com/android/inputmethod/keyboard/emoji/EmojiPalettesView.java @@ -21,6 +21,7 @@ import static com.android.inputmethod.latin.common.Constants.NOT_A_COORDINATE; import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; +import android.graphics.Color; import android.preference.PreferenceManager; import android.support.v4.view.ViewPager; import android.util.AttributeSet; @@ -73,6 +74,7 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange private final int mCategoryPageIndicatorBackground; private EmojiPalettesAdapter mEmojiPalettesAdapter; private final EmojiLayoutParams mEmojiLayoutParams; + private final DeleteKeyOnTouchListener mDeleteKeyOnTouchListener; private ImageButton mDeleteKey; private TextView mAlphabetKeyLeft; @@ -127,6 +129,7 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange mCategoryPageIndicatorBackground = emojiPalettesViewAttr.getColor( R.styleable.EmojiPalettesView_categoryPageIndicatorBackground, 0); emojiPalettesViewAttr.recycle(); + mDeleteKeyOnTouchListener = new DeleteKeyOnTouchListener(); } @Override @@ -197,7 +200,7 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange mDeleteKey = (ImageButton)findViewById(R.id.emoji_keyboard_delete); mDeleteKey.setBackgroundResource(mFunctionalKeyBackgroundId); mDeleteKey.setTag(Constants.CODE_DELETE); - mDeleteKey.setOnTouchListener(this); + mDeleteKey.setOnTouchListener(mDeleteKeyOnTouchListener); // {@link #mAlphabetKeyLeft}, {@link #mAlphabetKeyRight, and spaceKey depend on // {@link View.OnClickListener} as well as {@link View.OnTouchListener}. @@ -366,7 +369,8 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange } public void startEmojiPalettes(final String switchToAlphaLabel, - final KeyVisualAttributes keyVisualAttr, final KeyboardIconsSet iconSet) { + final KeyVisualAttributes keyVisualAttr, + final KeyboardIconsSet iconSet) { final int deleteIconResId = iconSet.getIconResourceId(KeyboardIconsSet.NAME_DELETE_KEY); if (deleteIconResId != 0) { mDeleteKey.setImageResource(deleteIconResId); @@ -392,6 +396,7 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange public void setKeyboardActionListener(final KeyboardActionListener listener) { mKeyboardActionListener = listener; + mDeleteKeyOnTouchListener.setKeyboardActionListener(listener); } private void updateEmojiCategoryPageIdView() { @@ -427,4 +432,52 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange mTabHost.setCurrentTab(newTabId); } } + + private static class DeleteKeyOnTouchListener implements OnTouchListener { + private KeyboardActionListener mKeyboardActionListener = + KeyboardActionListener.EMPTY_LISTENER; + + public void setKeyboardActionListener(final KeyboardActionListener listener) { + mKeyboardActionListener = listener; + } + + @Override + public boolean onTouch(final View v, final MotionEvent event) { + switch (event.getActionMasked()) { + case MotionEvent.ACTION_DOWN: + onTouchDown(v); + return true; + case MotionEvent.ACTION_MOVE: + final float x = event.getX(); + final float y = event.getY(); + if (x < 0.0f || v.getWidth() < x || y < 0.0f || v.getHeight() < y) { + // Stop generating key events once the finger moves away from the view area. + onTouchCanceled(v); + } + return true; + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + onTouchUp(v); + return true; + } + return false; + } + + private void onTouchDown(final View v) { + mKeyboardActionListener.onPressKey(Constants.CODE_DELETE, + 0 /* repeatCount */, true /* isSinglePointer */); + v.setPressed(true /* pressed */); + } + + private void onTouchUp(final View v) { + mKeyboardActionListener.onCodeInput(Constants.CODE_DELETE, + NOT_A_COORDINATE, NOT_A_COORDINATE, false /* isKeyRepeat */); + mKeyboardActionListener.onReleaseKey(Constants.CODE_DELETE, false /* withSliding */); + v.setPressed(false /* pressed */); + } + + private void onTouchCanceled(final View v) { + v.setBackgroundColor(Color.TRANSPARENT); + } + } }
\ No newline at end of file diff --git a/java/src/com/android/inputmethod/latin/Dictionary.java b/java/src/com/android/inputmethod/latin/Dictionary.java index 7d7ed77e7..16dcb3208 100644 --- a/java/src/com/android/inputmethod/latin/Dictionary.java +++ b/java/src/com/android/inputmethod/latin/Dictionary.java @@ -49,8 +49,7 @@ public abstract class Dictionary { // Spawned by resuming suggestions. Comes from a span that was in the TextView. public static final String TYPE_RESUMED = "resumed"; - public static final PhonyDictionary DICTIONARY_RESUMED = - new PhonyDictionary(TYPE_RESUMED); + public static final PhonyDictionary DICTIONARY_RESUMED = new PhonyDictionary(TYPE_RESUMED); // The following types of dictionary have actual functional instances. We don't need final // phony dictionary instances for them. @@ -60,10 +59,6 @@ public abstract class Dictionary { public static final String TYPE_USER = "user"; // User history dictionary internal to LatinIME. public static final String TYPE_USER_HISTORY = "history"; - // Personalization dictionary. - public static final String TYPE_PERSONALIZATION = "personalization"; - // Contextual dictionary. - public static final String TYPE_CONTEXTUAL = "contextual"; public final String mDictType; // The locale for this dictionary. May be null if unknown (phony dictionary for example). public final Locale mLocale; @@ -76,9 +71,7 @@ public abstract class Dictionary { TYPE_USER_TYPED, TYPE_USER, TYPE_CONTACTS, - TYPE_USER_HISTORY, - TYPE_PERSONALIZATION, - TYPE_CONTEXTUAL)); + TYPE_USER_HISTORY)); public Dictionary(final String dictType, final Locale locale) { mDictType = dictType; diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java index 401b52847..d174b40dd 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java +++ b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java @@ -21,6 +21,7 @@ import android.util.Pair; import android.view.inputmethod.InputMethodSubtype; import com.android.inputmethod.annotations.UsedForTesting; +import com.android.inputmethod.keyboard.KeyboardLayout; import com.android.inputmethod.latin.settings.SettingsValuesForSuggestion; import com.android.inputmethod.latin.utils.SuggestionResults; @@ -42,6 +43,35 @@ import javax.annotation.Nullable; * DictionaryFacilitator as a client for interacting with dictionaries. */ public interface DictionaryFacilitator { + + public static final String[] ALL_DICTIONARY_TYPES = new String[] { + Dictionary.TYPE_MAIN, + Dictionary.TYPE_USER_HISTORY, + Dictionary.TYPE_USER, + Dictionary.TYPE_CONTACTS}; + + public static final String[] DYNAMIC_DICTIONARY_TYPES = new String[] { + Dictionary.TYPE_USER_HISTORY, + Dictionary.TYPE_USER, + Dictionary.TYPE_CONTACTS}; + + /** + * {@link Dictionary#TYPE_USER} is deprecated, except for the spelling service. + */ + public static final String[] DICTIONARY_TYPES_FOR_SPELLING = new String[] { + Dictionary.TYPE_MAIN, + Dictionary.TYPE_USER_HISTORY, + Dictionary.TYPE_USER, + Dictionary.TYPE_CONTACTS}; + + /** + * {@link Dictionary#TYPE_USER} is deprecated, except for the spelling service. + */ + public static final String[] DICTIONARY_TYPES_FOR_SUGGESTIONS = new String[] { + Dictionary.TYPE_MAIN, + Dictionary.TYPE_USER_HISTORY, + Dictionary.TYPE_CONTACTS}; + /** * Returns whether this facilitator is exactly for this list of locales. * @@ -115,8 +145,6 @@ public interface DictionaryFacilitator { boolean hasAtLeastOneUninitializedMainDictionary(); - boolean hasPersonalizationDictionary(); - void waitForLoadingMainDictionaries(final long timeout, final TimeUnit unit) throws InterruptedException; @@ -133,9 +161,12 @@ public interface DictionaryFacilitator { // TODO: Revise the way to fusion suggestion results. SuggestionResults getSuggestionResults(final WordComposer composer, final NgramContext ngramContext, final long proximityInfoHandle, - final SettingsValuesForSuggestion settingsValuesForSuggestion, final int sessionId); + final SettingsValuesForSuggestion settingsValuesForSuggestion, final int sessionId, + final int inputStyle, final KeyboardLayout keyboardLayout); + + boolean isValidSpellingWord(final String word); - boolean isValidWord(final String word, final boolean ignoreCase); + boolean isValidSuggestionWord(final String word); int getFrequency(final String word); diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitatorImpl.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitatorImpl.java index 3c390db75..96603ef20 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryFacilitatorImpl.java +++ b/java/src/com/android/inputmethod/latin/DictionaryFacilitatorImpl.java @@ -23,6 +23,7 @@ import android.util.Pair; import android.view.inputmethod.InputMethodSubtype; import com.android.inputmethod.annotations.UsedForTesting; +import com.android.inputmethod.keyboard.KeyboardLayout; import com.android.inputmethod.latin.NgramContext.WordInfo; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.common.Constants; @@ -38,7 +39,6 @@ import java.io.File; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -78,14 +78,6 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator { private final Object mLock = new Object(); private final DistracterFilter mDistracterFilter; - private static final String[] DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS = - new String[] { - Dictionary.TYPE_MAIN, - Dictionary.TYPE_USER_HISTORY, - Dictionary.TYPE_USER, - Dictionary.TYPE_CONTACTS, - }; - public static final Map<String, Class<? extends ExpandableBinaryDictionary>> DICT_TYPE_TO_CLASS = new HashMap<>(); @@ -99,10 +91,6 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator { private static final Class<?>[] DICT_FACTORY_METHOD_ARG_TYPES = new Class[] { Context.class, Locale.class, File.class, String.class, String.class }; - private static final String[] SUB_DICT_TYPES = - Arrays.copyOfRange(DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS, 1 /* start */, - DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS.length); - /** * Returns whether this facilitator is exactly for this list of locales. * @@ -392,8 +380,6 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator { } if (usePersonalizedDicts) { subDictTypesToUse.add(Dictionary.TYPE_USER_HISTORY); - subDictTypesToUse.add(Dictionary.TYPE_PERSONALIZATION); - subDictTypesToUse.add(Dictionary.TYPE_CONTEXTUAL); } // Gather all dictionaries. We'll remove them from the list to clean up later. @@ -405,7 +391,7 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator { if (null == currentDictionaryGroupForLocale) { continue; } - for (final String dictType : SUB_DICT_TYPES) { + for (final String dictType : DYNAMIC_DICTIONARY_TYPES) { if (currentDictionaryGroupForLocale.hasDict(dictType, account)) { dictTypeForLocale.add(dictType); } @@ -564,7 +550,7 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator { mDictionaryGroups = new DictionaryGroup[] { mMostProbableDictionaryGroup }; } for (final DictionaryGroup dictionaryGroup : dictionaryGroups) { - for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) { + for (final String dictType : ALL_DICTIONARY_TYPES) { dictionaryGroup.closeDict(dictType); } } @@ -600,16 +586,6 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator { return false; } - public boolean hasPersonalizationDictionary() { - final DictionaryGroup[] dictionaryGroups = mDictionaryGroups; - for (final DictionaryGroup dictionaryGroup : dictionaryGroups) { - if (dictionaryGroup.hasDict(Dictionary.TYPE_PERSONALIZATION, null /* account */)) { - return true; - } - } - return false; - } - public void waitForLoadingMainDictionaries(final long timeout, final TimeUnit unit) throws InterruptedException { mLatchForWaitingLoadingMainDictionaries.await(timeout, unit); @@ -659,8 +635,7 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator { final String lowerCasedWord = word.toLowerCase(dictionaryGroup.mLocale); final String secondWord; if (wasAutoCapitalized) { - if (isValidWord(word, false /* ignoreCase */) - && !isValidWord(lowerCasedWord, false /* ignoreCase */)) { + if (isValidSuggestionWord(word) && !isValidSuggestionWord(lowerCasedWord)) { // If the word was auto-capitalized and exists only as a capitalized word in the // dictionary, then we must not downcase it before registering it. For example, // the name of the contacts in start-of-sentence position would come here with the @@ -708,21 +683,21 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator { public void removeWordFromPersonalizedDicts(final String word) { removeWord(Dictionary.TYPE_USER_HISTORY, word); - removeWord(Dictionary.TYPE_PERSONALIZATION, word); - removeWord(Dictionary.TYPE_CONTEXTUAL, word); } // TODO: Revise the way to fusion suggestion results. - public SuggestionResults getSuggestionResults(final WordComposer composer, - final NgramContext ngramContext, final long proximityInfoHandle, - final SettingsValuesForSuggestion settingsValuesForSuggestion, final int sessionId) { + @Override + public SuggestionResults getSuggestionResults(WordComposer composer, + NgramContext ngramContext, long proximityInfoHandle, + SettingsValuesForSuggestion settingsValuesForSuggestion, int sessionId, + int inputStyle, KeyboardLayout keyboardLayout) { final DictionaryGroup[] dictionaryGroups = mDictionaryGroups; final SuggestionResults suggestionResults = new SuggestionResults( SuggestedWords.MAX_SUGGESTIONS, ngramContext.isBeginningOfSentenceContext()); final float[] weightOfLangModelVsSpatialModel = new float[] { Dictionary.NOT_A_WEIGHT_OF_LANG_MODEL_VS_SPATIAL_MODEL }; for (final DictionaryGroup dictionaryGroup : dictionaryGroups) { - for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) { + for (final String dictType : DICTIONARY_TYPES_FOR_SUGGESTIONS) { final Dictionary dictionary = dictionaryGroup.getDict(dictType); if (null == dictionary) continue; final float weightForLocale = composer.isBatchMode() @@ -742,7 +717,15 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator { return suggestionResults; } - public boolean isValidWord(final String word, final boolean ignoreCase) { + public boolean isValidSpellingWord(final String word) { + return isValidWord(word, DICTIONARY_TYPES_FOR_SPELLING); + } + + public boolean isValidSuggestionWord(final String word) { + return isValidWord(word, DICTIONARY_TYPES_FOR_SUGGESTIONS); + } + + private boolean isValidWord(final String word, final String[] dictionariesToCheck) { if (TextUtils.isEmpty(word)) { return false; } @@ -751,15 +734,13 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator { if (dictionaryGroup.mLocale == null) { continue; } - final String lowerCasedWord = word.toLowerCase(dictionaryGroup.mLocale); - for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) { + for (final String dictType : dictionariesToCheck) { final Dictionary dictionary = dictionaryGroup.getDict(dictType); // Ideally the passed map would come out of a {@link java.util.concurrent.Future} and // would be immutable once it's finished initializing, but concretely a null test is // probably good enough for the time being. if (null == dictionary) continue; - if (dictionary.isValidWord(word) - || (ignoreCase && dictionary.isValidWord(lowerCasedWord))) { + if (dictionary.isValidWord(word)) { return true; } } @@ -775,7 +756,7 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator { int maxFreq = Dictionary.NOT_A_PROBABILITY; final DictionaryGroup[] dictionaryGroups = mDictionaryGroups; for (final DictionaryGroup dictionaryGroup : dictionaryGroups) { - for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) { + for (final String dictType : ALL_DICTIONARY_TYPES) { final Dictionary dictionary = dictionaryGroup.getDict(dictType); if (dictionary == null) continue; final int tempFreq; @@ -814,10 +795,6 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator { clearSubDictionary(Dictionary.TYPE_USER_HISTORY); } - public void clearContextualDictionary() { - clearSubDictionary(Dictionary.TYPE_CONTEXTUAL); - } - public void dumpDictionaryForDebug(final String dictName) { final DictionaryGroup[] dictionaryGroups = mDictionaryGroups; for (final DictionaryGroup dictionaryGroup : dictionaryGroups) { @@ -835,7 +812,7 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator { final ArrayList<Pair<String, DictionaryStats>> statsOfEnabledSubDicts = new ArrayList<>(); final DictionaryGroup[] dictionaryGroups = mDictionaryGroups; for (final DictionaryGroup dictionaryGroup : dictionaryGroups) { - for (final String dictType : SUB_DICT_TYPES) { + for (final String dictType : DYNAMIC_DICTIONARY_TYPES) { final ExpandableBinaryDictionary dictionary = dictionaryGroup.getSubDict(dictType); if (dictionary == null) continue; statsOfEnabledSubDicts.add(new Pair<>(dictType, dictionary.getDictionaryStats())); diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitatorLruCache.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitatorLruCache.java index 13bd15101..666813da5 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryFacilitatorLruCache.java +++ b/java/src/com/android/inputmethod/latin/DictionaryFacilitatorLruCache.java @@ -128,7 +128,7 @@ public class DictionaryFacilitatorLruCache { public DictionaryFacilitator get(final Locale locale) { DictionaryFacilitator dictionaryFacilitator = mLruCache.get(locale); if (dictionaryFacilitator != null) { - // dictionary falicitator for the locale is in the cache. + // dictionary facilitator for the locale is in the cache. return dictionaryFacilitator; } synchronized (mLock) { diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 822700808..9a1df4995 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -1520,7 +1520,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen return; } mInputLogic.getSuggestedWords(mSettings.getCurrent(), keyboard.getProximityInfo(), - mKeyboardSwitcher.getKeyboardShiftMode(), inputStyle, sequenceNumber, callback); + mKeyboardSwitcher.getKeyboardShiftMode(), inputStyle, sequenceNumber, callback, + keyboard.getKeyboardLayout()); } @Override diff --git a/java/src/com/android/inputmethod/latin/NgramContext.java b/java/src/com/android/inputmethod/latin/NgramContext.java index b47731229..2d66fb000 100644 --- a/java/src/com/android/inputmethod/latin/NgramContext.java +++ b/java/src/com/android/inputmethod/latin/NgramContext.java @@ -22,6 +22,7 @@ import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.common.Constants; import com.android.inputmethod.latin.common.StringUtils; +import java.util.ArrayList; import java.util.Arrays; import javax.annotation.Nonnull; @@ -38,6 +39,10 @@ public class NgramContext { public static final NgramContext BEGINNING_OF_SENTENCE = new NgramContext(WordInfo.BEGINNING_OF_SENTENCE_WORD_INFO); + public static final String BEGINNING_OF_SENTENCE_TAG = "<S>"; + + public static final String CONTEXT_SEPARATOR = " "; + /** * Word information used to represent previous words information. */ @@ -114,6 +119,31 @@ public class NgramContext { return new NgramContext(prevWordsInfo); } + + /** + * Extracts the previous words context. + * + * @return a String with the previous words separated by white space. + */ + public String extractPrevWordsContext() { + final ArrayList<String> terms = new ArrayList<>(); + for (int i = mPrevWordsInfo.length - 1; i >= 0; --i) { + if (mPrevWordsInfo[i] != null && mPrevWordsInfo[i].isValid()) { + final NgramContext.WordInfo wordInfo = mPrevWordsInfo[i]; + if (wordInfo.mIsBeginningOfSentence) { + terms.add(BEGINNING_OF_SENTENCE_TAG); + } else { + final String term = wordInfo.mWord.toString(); + if (!term.isEmpty()) { + terms.add(term); + } + } + } + } + return terms.size() == 0 ? BEGINNING_OF_SENTENCE_TAG + : TextUtils.join(CONTEXT_SEPARATOR, terms); + } + public boolean isValid() { return mPrevWordsCount > 0 && mPrevWordsInfo[0].isValid(); } diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java index 834f747d9..660a051b9 100644 --- a/java/src/com/android/inputmethod/latin/RichInputConnection.java +++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java @@ -16,14 +16,12 @@ package com.android.inputmethod.latin; -import android.graphics.Color; import android.inputmethodservice.InputMethodService; import android.os.Build; import android.os.Bundle; import android.text.SpannableStringBuilder; -import android.text.Spanned; import android.text.TextUtils; -import android.text.style.BackgroundColorSpan; +import android.text.style.CharacterStyle; import android.util.Log; import android.view.KeyEvent; import android.view.inputmethod.CompletionInfo; @@ -35,6 +33,7 @@ import android.view.inputmethod.InputMethodManager; import com.android.inputmethod.compat.InputConnectionCompatUtils; import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.UnicodeSurrogate; import com.android.inputmethod.latin.common.StringUtils; import com.android.inputmethod.latin.inputlogic.PrivateCommandPerformer; import com.android.inputmethod.latin.settings.SpacingAndPunctuations; @@ -91,16 +90,10 @@ public final class RichInputConnection implements PrivateCommandPerformer { private final StringBuilder mComposingText = new StringBuilder(); /** - * This variable is a temporary object used in - * {@link #commitTextWithBackgroundColor(CharSequence,int,int,int)} to avoid object creation. + * This variable is a temporary object used in {@link #commitText(CharSequence,int)} + * to avoid object creation. */ private SpannableStringBuilder mTempObjectForCommitText = new SpannableStringBuilder(); - /** - * This variable is used to track whether the last committed text had the background color or - * not. - * TODO: Omit this flag if possible. - */ - private boolean mLastCommittedTextHasBackgroundColor = false; private final InputMethodService mParent; InputConnection mIC; @@ -239,39 +232,18 @@ public final class RichInputConnection implements PrivateCommandPerformer { // it works, but it's wrong and should be fixed. mCommittedTextBeforeComposingText.append(mComposingText); mComposingText.setLength(0); - // TODO: Clear this flag in setComposingRegion() and setComposingText() as well if needed. - mLastCommittedTextHasBackgroundColor = false; if (null != mIC) { mIC.finishComposingText(); } } /** - * Synonym of {@code commitTextWithBackgroundColor(text, newCursorPosition, Color.TRANSPARENT}. + * Calls {@link InputConnection#commitText(CharSequence, int)}. + * * @param text The text to commit. This may include styles. - * See {@link InputConnection#commitText(CharSequence, int)}. * @param newCursorPosition The new cursor position around the text. - * See {@link InputConnection#commitText(CharSequence, int)}. */ public void commitText(final CharSequence text, final int newCursorPosition) { - commitTextWithBackgroundColor(text, newCursorPosition, Color.TRANSPARENT, text.length()); - } - - /** - * Calls {@link InputConnection#commitText(CharSequence, int)} with the given background color. - * @param text The text to commit. This may include styles. - * See {@link InputConnection#commitText(CharSequence, int)}. - * @param newCursorPosition The new cursor position around the text. - * See {@link InputConnection#commitText(CharSequence, int)}. - * @param color The background color to be attached. Set {@link Color#TRANSPARENT} to disable - * the background color. Note that this method specifies {@link BackgroundColorSpan} with - * {@link Spanned#SPAN_COMPOSING} flag, meaning that the background color persists until - * {@link #finishComposingText()} is called. - * @param coloredTextLength the length of text, in Java chars, which should be rendered with - * the given background color. - */ - public void commitTextWithBackgroundColor(final CharSequence text, final int newCursorPosition, - final int color, final int coloredTextLength) { if (DEBUG_BATCH_NESTING) checkBatchEdit(); if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug(); mCommittedTextBeforeComposingText.append(text); @@ -281,44 +253,32 @@ public final class RichInputConnection implements PrivateCommandPerformer { mExpectedSelStart += text.length() - mComposingText.length(); mExpectedSelEnd = mExpectedSelStart; mComposingText.setLength(0); - mLastCommittedTextHasBackgroundColor = false; if (null != mIC) { - if (color == Color.TRANSPARENT) { - mIC.commitText(text, newCursorPosition); - } else { - mTempObjectForCommitText.clear(); - mTempObjectForCommitText.append(text); - final BackgroundColorSpan backgroundColorSpan = new BackgroundColorSpan(color); - final int spanLength = Math.min(coloredTextLength, text.length()); - mTempObjectForCommitText.setSpan(backgroundColorSpan, 0, spanLength, - Spanned.SPAN_COMPOSING | Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - mIC.commitText(mTempObjectForCommitText, newCursorPosition); - mLastCommittedTextHasBackgroundColor = true; + mTempObjectForCommitText.clear(); + mTempObjectForCommitText.append(text); + final CharacterStyle[] spans = mTempObjectForCommitText.getSpans( + 0, text.length(), CharacterStyle.class); + for (final CharacterStyle span : spans) { + final int spanStart = mTempObjectForCommitText.getSpanStart(span); + final int spanEnd = mTempObjectForCommitText.getSpanEnd(span); + final int spanFlags = mTempObjectForCommitText.getSpanFlags(span); + // We have to adjust the end of the span to include an additional character. + // This is to avoid splitting a unicode surrogate pair. + // See com.android.inputmethod.latin.common.Constants.UnicodeSurrogate + // See https://b.corp.google.com/issues/19255233 + if (0 < spanEnd && spanEnd < mTempObjectForCommitText.length()) { + final char spanEndChar = mTempObjectForCommitText.charAt(spanEnd - 1); + final char nextChar = mTempObjectForCommitText.charAt(spanEnd); + if (UnicodeSurrogate.isLowSurrogate(spanEndChar) + && UnicodeSurrogate.isHighSurrogate(nextChar)) { + mTempObjectForCommitText.setSpan(span, spanStart, spanEnd + 1, spanFlags); + } + } } + mIC.commitText(mTempObjectForCommitText, newCursorPosition); } } - /** - * Removes the background color from the highlighted text if necessary. Should be called while - * there is no on-going composing text. - * - * <p>CAVEAT: This method internally calls {@link InputConnection#finishComposingText()}. - * Be careful of any unexpected side effects.</p> - */ - public void removeBackgroundColorFromHighlightedTextIfNecessary() { - // TODO: We haven't yet full tested if we really need to check this flag or not. Omit this - // flag if everything works fine without this condition. - if (!mLastCommittedTextHasBackgroundColor) { - return; - } - if (mComposingText.length() > 0) { - Log.e(TAG, "clearSpansWithComposingFlags should be called when composing text is " + - "empty. mComposingText=" + mComposingText); - return; - } - finishComposingText(); - } - public CharSequence getSelectedText(final int flags) { return (null == mIC) ? null : mIC.getSelectedText(flags); } @@ -946,8 +906,6 @@ public final class RichInputConnection implements PrivateCommandPerformer { } } - private boolean mCursorAnchorInfoMonitorEnabled = false; - /** * Requests the editor to call back {@link InputMethodManager#updateCursorAnchorInfo}. * @param enableMonitor {@code true} to request the editor to call back the method whenever the @@ -962,23 +920,10 @@ public final class RichInputConnection implements PrivateCommandPerformer { public boolean requestCursorUpdates(final boolean enableMonitor, final boolean requestImmediateCallback) { mIC = mParent.getCurrentInputConnection(); - final boolean scheduled; - if (null != mIC) { - scheduled = InputConnectionCompatUtils.requestCursorUpdates(mIC, enableMonitor, - requestImmediateCallback); - } else { - scheduled = false; + if (mIC == null) { + return false; } - mCursorAnchorInfoMonitorEnabled = (scheduled && enableMonitor); - return scheduled; - } - - /** - * @return {@code true} if the application reported that the monitor mode of - * {@link InputMethodService#onUpdateCursorAnchorInfo(android.view.inputmethod.CursorAnchorInfo)} - * is currently enabled. - */ - public boolean isCursorAnchorInfoMonitorEnabled() { - return mCursorAnchorInfoMonitorEnabled; + return InputConnectionCompatUtils.requestCursorUpdates( + mIC, enableMonitor, requestImmediateCallback); } } diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index 0bf0f687a..ddb2b5358 100644 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -18,6 +18,7 @@ package com.android.inputmethod.latin; import android.text.TextUtils; +import com.android.inputmethod.keyboard.KeyboardLayout; import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.common.Constants; @@ -97,14 +98,16 @@ public final class Suggest { final NgramContext ngramContext, final ProximityInfo proximityInfo, final SettingsValuesForSuggestion settingsValuesForSuggestion, final boolean isCorrectionEnabled, final int inputStyle, final int sequenceNumber, - final OnGetSuggestedWordsCallback callback) { + final OnGetSuggestedWordsCallback callback, + final KeyboardLayout keyboardLayout) { if (wordComposer.isBatchMode()) { getSuggestedWordsForBatchInput(wordComposer, ngramContext, proximityInfo, - settingsValuesForSuggestion, inputStyle, sequenceNumber, callback); + settingsValuesForSuggestion, inputStyle, sequenceNumber, callback, + keyboardLayout); } else { getSuggestedWordsForNonBatchInput(wordComposer, ngramContext, proximityInfo, settingsValuesForSuggestion, inputStyle, isCorrectionEnabled, - sequenceNumber, callback); + sequenceNumber, callback, keyboardLayout); } } @@ -163,7 +166,8 @@ public final class Suggest { final NgramContext ngramContext, final ProximityInfo proximityInfo, final SettingsValuesForSuggestion settingsValuesForSuggestion, final int inputStyleIfNotPrediction, final boolean isCorrectionEnabled, - final int sequenceNumber, final OnGetSuggestedWordsCallback callback) { + final int sequenceNumber, final OnGetSuggestedWordsCallback callback, + final KeyboardLayout keyboardLayout) { final String typedWordString = wordComposer.getTypedWord(); final int trailingSingleQuotesCount = StringUtils.getTrailingSingleQuotesCount(typedWordString); @@ -173,7 +177,8 @@ public final class Suggest { final SuggestionResults suggestionResults = mDictionaryFacilitator.getSuggestionResults( wordComposer, ngramContext, proximityInfo.getNativeProximityInfo(), - settingsValuesForSuggestion, SESSION_ID_TYPING); + settingsValuesForSuggestion, SESSION_ID_TYPING, inputStyleIfNotPrediction, + keyboardLayout); final Locale mostProbableLocale = mDictionaryFacilitator.getMostProbableLocale(); final ArrayList<SuggestedWordInfo> suggestionsContainer = getTransformedSuggestedWordInfoList(wordComposer, suggestionResults, @@ -270,7 +275,9 @@ public final class Suggest { hasAutoCorrection = false; } else { final SuggestedWordInfo firstSuggestion = suggestionResults.first(); - if (!AutoCorrectionUtils.suggestionExceedsThreshold( + if (suggestionResults.mAutocorrectRecommendation) { + hasAutoCorrection = true; + } else if (!AutoCorrectionUtils.suggestionExceedsThreshold( firstSuggestion, consideredWord, mAutoCorrectionThreshold)) { // Score is too low for autocorrect hasAutoCorrection = false; @@ -339,10 +346,11 @@ public final class Suggest { final NgramContext ngramContext, final ProximityInfo proximityInfo, final SettingsValuesForSuggestion settingsValuesForSuggestion, final int inputStyle, final int sequenceNumber, - final OnGetSuggestedWordsCallback callback) { + final OnGetSuggestedWordsCallback callback, + final KeyboardLayout keyboardLayout) { final SuggestionResults suggestionResults = mDictionaryFacilitator.getSuggestionResults( wordComposer, ngramContext, proximityInfo.getNativeProximityInfo(), - settingsValuesForSuggestion, SESSION_ID_GESTURE); + settingsValuesForSuggestion, SESSION_ID_GESTURE, inputStyle, keyboardLayout); // For transforming words that don't come from a dictionary, because it's our best bet final Locale defaultLocale = mDictionaryFacilitator.getMostProbableLocale(); final ArrayList<SuggestedWordInfo> suggestionsContainer = diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java index e80e3628f..e6f2f1ec9 100644 --- a/java/src/com/android/inputmethod/latin/WordComposer.java +++ b/java/src/com/android/inputmethod/latin/WordComposer.java @@ -16,6 +16,7 @@ package com.android.inputmethod.latin; +import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.event.CombinerChain; import com.android.inputmethod.event.Event; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; @@ -464,4 +465,14 @@ public final class WordComposer { public String getRejectedBatchModeSuggestion() { return mRejectedBatchModeSuggestion; } + + @UsedForTesting + void addInputPointerForTest(int index, int keyX, int keyY) { + mInputPointers.addPointerAt(index, keyX, keyY, 0, 0); + } + + @UsedForTesting + void setTypedWordCacheForTests(String typedWordCacheForTests) { + mTypedWordCache = typedWordCacheForTests; + } } diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java index 18927ce85..9154cc35a 100644 --- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java +++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java @@ -32,6 +32,7 @@ import android.view.inputmethod.EditorInfo; import com.android.inputmethod.compat.SuggestionSpanUtils; import com.android.inputmethod.event.Event; import com.android.inputmethod.event.InputTransaction; +import com.android.inputmethod.keyboard.KeyboardLayout; import com.android.inputmethod.keyboard.KeyboardSwitcher; import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.latin.Dictionary; @@ -474,9 +475,7 @@ public final class InputLogic { handler.cancelUpdateSuggestionStrip(); ++mAutoCommitSequenceNumber; mConnection.beginBatchEdit(); - if (!mWordComposer.isComposingWord()) { - mConnection.removeBackgroundColorFromHighlightedTextIfNecessary(); - } else { + if (mWordComposer.isComposingWord()) { if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) { // If we are in the middle of a recorrection, we need to commit the recorrection // first so that we can insert the batch input at the current cursor position. @@ -767,10 +766,6 @@ public final class InputLogic { final InputTransaction inputTransaction, // TODO: remove this argument final LatinIME.UIHandler handler) { - if (!mWordComposer.isComposingWord()) { - mConnection.removeBackgroundColorFromHighlightedTextIfNecessary(); - } - final int codePoint = event.mCodePoint; mSpaceState = SpaceState.NONE; if (inputTransaction.mSettingsValues.isWordSeparator(codePoint) @@ -2115,7 +2110,8 @@ public final class InputLogic { public void getSuggestedWords(final SettingsValues settingsValues, final ProximityInfo proximityInfo, final int keyboardShiftMode, final int inputStyle, - final int sequenceNumber, final OnGetSuggestedWordsCallback callback) { + final int sequenceNumber, final OnGetSuggestedWordsCallback callback, + final KeyboardLayout keyboardLayout) { mWordComposer.adviseCapitalizedModeBeforeFetchingSuggestions( getActualCapsMode(settingsValues, keyboardShiftMode)); mSuggest.getSuggestedWords(mWordComposer, @@ -2129,7 +2125,7 @@ public final class InputLogic { new SettingsValuesForSuggestion(settingsValues.mBlockPotentiallyOffensive, settingsValues.mPhraseGestureEnabled), settingsValues.mAutoCorrectionEnabledPerUserSettings, - inputStyle, sequenceNumber, callback); + inputStyle, sequenceNumber, callback, keyboardLayout); } /** diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java index 8744020b1..477e5702f 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java @@ -27,6 +27,7 @@ import android.view.textservice.SuggestionsInfo; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardId; +import com.android.inputmethod.keyboard.KeyboardLayout; import com.android.inputmethod.keyboard.KeyboardLayoutSet; import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.latin.DictionaryFacilitator; @@ -34,6 +35,7 @@ import com.android.inputmethod.latin.DictionaryFacilitatorLruCache; import com.android.inputmethod.latin.NgramContext; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.RichInputMethodSubtype; +import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.settings.SettingsValuesForSuggestion; import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils; import com.android.inputmethod.latin.utils.ScriptUtils; @@ -152,14 +154,15 @@ public final class AndroidSpellCheckerService extends SpellCheckerService try { DictionaryFacilitator dictionaryFacilitatorForLocale = mDictionaryFacilitatorCache.get(locale); - return dictionaryFacilitatorForLocale.isValidWord(word, false /* igroreCase */); + return dictionaryFacilitatorForLocale.isValidSpellingWord(word); } finally { mSemaphore.release(); } } public SuggestionResults getSuggestionResults(final Locale locale, final WordComposer composer, - final NgramContext ngramContext, final ProximityInfo proximityInfo) { + final NgramContext ngramContext, final ProximityInfo proximityInfo, + final KeyboardLayout keyboardLayout) { Integer sessionId = null; mSemaphore.acquireUninterruptibly(); try { @@ -168,7 +171,7 @@ public final class AndroidSpellCheckerService extends SpellCheckerService mDictionaryFacilitatorCache.get(locale); return dictionaryFacilitatorForLocale.getSuggestionResults(composer, ngramContext, proximityInfo.getNativeProximityInfo(), mSettingsValuesForSuggestion, - sessionId); + sessionId, SuggestedWords.INPUT_STYLE_TYPING, keyboardLayout); } finally { if (sessionId != null) { mSessionIdPool.add(sessionId); diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java index 832bfd066..0b5e12f03 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java @@ -29,6 +29,7 @@ import android.view.textservice.TextInfo; import com.android.inputmethod.compat.SuggestionsInfoCompatUtils; import com.android.inputmethod.keyboard.Keyboard; +import com.android.inputmethod.keyboard.KeyboardLayout; import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.latin.NgramContext; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; @@ -271,18 +272,21 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session { final int[] codePoints = StringUtils.toCodePointArray(text); final int[] coordinates; final ProximityInfo proximityInfo; + final KeyboardLayout keyboardLayout; if (null == keyboard) { coordinates = CoordinateUtils.newCoordinateArray(codePoints.length, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE); proximityInfo = null; + keyboardLayout = null; } else { coordinates = keyboard.getCoordinates(codePoints); proximityInfo = keyboard.getProximityInfo(); + keyboardLayout = keyboard.getKeyboardLayout(); } composer.setComposingWord(codePoints, coordinates); // TODO: Don't gather suggestions if the limit is <= 0 unless necessary final SuggestionResults suggestionResults = mService.getSuggestionResults( - mLocale, composer, ngramContext, proximityInfo); + mLocale, composer, ngramContext, proximityInfo, keyboardLayout); final Result result = getResult(capitalizeType, mLocale, suggestionsLimit, mService.getRecommendedThreshold(), text, suggestionResults); isInDict = isInDictForAnyCapitalization(text, capitalizeType); diff --git a/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatchesAndSuggestions.java b/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatchesAndSuggestions.java index 56a04a856..09bb03f65 100644 --- a/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatchesAndSuggestions.java +++ b/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatchesAndSuggestions.java @@ -38,6 +38,7 @@ import com.android.inputmethod.latin.DictionaryFacilitator; import com.android.inputmethod.latin.DictionaryFacilitatorLruCache; import com.android.inputmethod.latin.NgramContext; import com.android.inputmethod.latin.RichInputMethodSubtype; +import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.WordComposer; import com.android.inputmethod.latin.common.StringUtils; @@ -195,8 +196,7 @@ public class DistracterFilterCheckingExactMatchesAndSuggestions implements Distr mDistractersCache.put(cacheKey, Boolean.TRUE); return true; } - final boolean Word = dictionaryFacilitator.isValidWord(testedWord, false /* ignoreCase */); - if (Word) { + if (dictionaryFacilitator.isValidSuggestionWord(testedWord)) { // Valid word is not a distracter. if (DEBUG) { Log.d(TAG, "isDistracter: false (valid word)"); @@ -252,7 +252,9 @@ public class DistracterFilterCheckingExactMatchesAndSuggestions implements Distr suggestionResults = dictionaryFacilitator.getSuggestionResults(composer, NgramContext.EMPTY_PREV_WORDS_INFO, keyboard.getProximityInfo().getNativeProximityInfo(), - settingsValuesForSuggestion, 0 /* sessionId */); + settingsValuesForSuggestion, 0 /* sessionId */, + SuggestedWords.INPUT_STYLE_TYPING, + keyboard.getKeyboardLayout()); } if (suggestionResults.isEmpty()) { return false; @@ -288,14 +290,14 @@ public class DistracterFilterCheckingExactMatchesAndSuggestions implements Distr final Locale locale) { final DictionaryFacilitator dictionaryFacilitator = mDictionaryFacilitatorLruCache.get(locale); - if (dictionaryFacilitator.isValidWord(testedWord, false /* ignoreCase */)) { + if (dictionaryFacilitator.isValidSuggestionWord(testedWord)) { return false; } - final String lowerCaseTargetWord = testedWord.toLowerCase(locale); - if (testedWord.equals(lowerCaseTargetWord)) { + final String lowerCaseWord = testedWord.toLowerCase(locale); + if (testedWord.equals(lowerCaseWord)) { return false; } - if (dictionaryFacilitator.isValidWord(lowerCaseTargetWord, false /* ignoreCase */)) { + if (dictionaryFacilitator.isValidSuggestionWord(lowerCaseWord)) { return true; } if (StringUtils.getCapitalizationType(testedWord) == StringUtils.CAPITALIZE_FIRST @@ -314,10 +316,10 @@ public class DistracterFilterCheckingExactMatchesAndSuggestions implements Distr return HandlingType.getHandlingType(false /* shouldBeLowerCased */, false /* isOov */); } final boolean shouldBeLowerCased = shouldBeLowerCased(ngramContext, testedWord, locale); - final String caseModifiedWord = - shouldBeLowerCased ? testedWord.toLowerCase(locale) : testedWord; - final boolean isOov = !mDictionaryFacilitatorLruCache.get(locale).isValidWord( - caseModifiedWord, false /* ignoreCase */); + final String caseModifiedWord = shouldBeLowerCased + ? testedWord.toLowerCase(locale) : testedWord; + final boolean isOov = !mDictionaryFacilitatorLruCache.get(locale).isValidSuggestionWord( + caseModifiedWord); return HandlingType.getHandlingType(shouldBeLowerCased, isOov); } } diff --git a/java/src/com/android/inputmethod/latin/utils/SuggestionResults.java b/java/src/com/android/inputmethod/latin/utils/SuggestionResults.java index b319aeb8a..10e3994b6 100644 --- a/java/src/com/android/inputmethod/latin/utils/SuggestionResults.java +++ b/java/src/com/android/inputmethod/latin/utils/SuggestionResults.java @@ -33,14 +33,21 @@ public final class SuggestionResults extends TreeSet<SuggestedWordInfo> { // TODO: Instead of a boolean , we may want to include the context of this suggestion results, // such as {@link NgramContext}. public final boolean mIsBeginningOfSentence; + public final boolean mAutocorrectRecommendation; private final int mCapacity; public SuggestionResults(final int capacity, final boolean isBeginningOfSentence) { - this(sSuggestedWordInfoComparator, capacity, isBeginningOfSentence); + this(sSuggestedWordInfoComparator, capacity, isBeginningOfSentence, false); } - private SuggestionResults(final Comparator<SuggestedWordInfo> comparator, - final int capacity, final boolean isBeginningOfSentence) { + public SuggestionResults(final int capacity, final boolean isBeginningOfSentence, + final boolean autocorrectRecommendation) { + this(sSuggestedWordInfoComparator, capacity, isBeginningOfSentence, + autocorrectRecommendation); + } + + private SuggestionResults(final Comparator<SuggestedWordInfo> comparator, final int capacity, + final boolean isBeginningOfSentence, final boolean autocorrectRecommendation) { super(comparator); mCapacity = capacity; if (ProductionFlags.INCLUDE_RAW_SUGGESTIONS) { @@ -49,6 +56,7 @@ public final class SuggestionResults extends TreeSet<SuggestedWordInfo> { mRawSuggestions = null; } mIsBeginningOfSentence = isBeginningOfSentence; + mAutocorrectRecommendation = autocorrectRecommendation; } @Override diff --git a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutTest.java b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutTest.java index 9aced5cea..733bf96e2 100644 --- a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutTest.java +++ b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutTest.java @@ -45,8 +45,10 @@ public class KeyboardLayoutTest { assertEquals(0, keyboardLayout.getKeyXCoordinates().length); assertEquals(0, keyboardLayout.getKeyYCoordinates().length); - Key key1 = new Key("label1", 101, 102, "101", "101hint", 103, 104, 105, 106, 1100, 1101, 2, 2); - Key key2 = new Key("label2", 201, 202, "201", "201hint", 203, 204, 205, 206, 2100, 2201, 2, 2); + Key key1 = new Key("label1", 101, 102, "101", "101hint", 103, 104, 105, 106, 1100, 1101, + 10, 10); + Key key2 = new Key("label2", 201, 103, "201", "201hint", 203, 204, 205, 206, 2100, 2101, + 10, 10); ArrayList<Key> sortedKeys = new ArrayList<>(2); sortedKeys.add(key1); @@ -57,5 +59,23 @@ public class KeyboardLayoutTest { assertEquals(2, keyboardLayout.getKeyHeights().length); assertEquals(2, keyboardLayout.getKeyXCoordinates().length); assertEquals(2, keyboardLayout.getKeyYCoordinates().length); + + assertEquals(102, keyboardLayout.getKeyCodes()[0]); + // xCo + horizontalGap/2 + assertEquals(105 + 5, keyboardLayout.getKeyXCoordinates()[0]); + assertEquals(106, keyboardLayout.getKeyYCoordinates()[0]); + // width - horizontalGap + assertEquals(1100 - 10, keyboardLayout.getKeyWidths()[0]); + // height - verticalGap + assertEquals(1101 - 10, keyboardLayout.getKeyHeights()[0]); + + assertEquals(103, keyboardLayout.getKeyCodes()[1]); + // xCo + horizontalGap/2 + assertEquals(205 + 5, keyboardLayout.getKeyXCoordinates()[1]); + assertEquals(206, keyboardLayout.getKeyYCoordinates()[1]); + // width - horizontalGap + assertEquals(2100 - 10, keyboardLayout.getKeyWidths()[1]); + // height - verticalGap + assertEquals(2101 - 10, keyboardLayout.getKeyHeights()[1]); } } diff --git a/tests/src/com/android/inputmethod/latin/common/UnicodeSurrogateTests.java b/tests/src/com/android/inputmethod/latin/common/UnicodeSurrogateTests.java new file mode 100644 index 000000000..59bb08292 --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/common/UnicodeSurrogateTests.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2015 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.latin.common; + +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.SmallTest; + +@SmallTest +public class UnicodeSurrogateTests extends AndroidTestCase { + + public void testIsLowSurrogate() { + assertFalse(UnicodeSurrogate.isLowSurrogate('\uD7FF')); + assertTrue(UnicodeSurrogate.isLowSurrogate('\uD83D')); + assertFalse(UnicodeSurrogate.isLowSurrogate('\uDC00')); + } + + public void testIsHighSurrogate() { + assertFalse(UnicodeSurrogate.isHighSurrogate('\uDBFF')); + assertTrue(UnicodeSurrogate.isHighSurrogate('\uDE25')); + assertFalse(UnicodeSurrogate.isHighSurrogate('\uE000')); + } +} |