Suggest.java revision 0cf422fbb763e2672fb2f9e8e1e8af91d2e87cb3
1923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project/* 2443c360d0afdbab091994244f045f4756feaf2b4Jean-Baptiste Queru * Copyright (C) 2008 The Android Open Source Project 3e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa * 4923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * use this file except in compliance with the License. You may obtain a copy of 6923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * the License at 7e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa * 8923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * http://www.apache.org/licenses/LICENSE-2.0 9e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa * 10923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * Unless required by applicable law or agreed to in writing, software 11923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * License for the specific language governing permissions and limitations under 14923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * the License. 15923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */ 16923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 17923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectpackage com.android.inputmethod.latin; 18923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 19923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.content.Context; 20923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.text.TextUtils; 21923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.util.Log; 22923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 23c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalardimport com.android.inputmethod.keyboard.Keyboard; 24043f7841985916717f4fa821fe3e423daf3ff2f5Jean Chalardimport com.android.inputmethod.keyboard.ProximityInfo; 25043f7841985916717f4fa821fe3e423daf3ff2f5Jean Chalard 2633e0b1e79e464ac48a09433bbfcbb17ded620452Tadashi G. Takaokaimport java.io.File; 27fa086c90760bc2bedf0b74eacb0fed3bf7ebc2b7Tadashi G. Takaokaimport java.util.ArrayList; 28fa086c90760bc2bedf0b74eacb0fed3bf7ebc2b7Tadashi G. Takaokaimport java.util.Arrays; 29c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaokaimport java.util.HashMap; 30c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaokaimport java.util.HashSet; 31cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalardimport java.util.Locale; 32c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaokaimport java.util.Map; 33c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaokaimport java.util.Set; 34fa086c90760bc2bedf0b74eacb0fed3bf7ebc2b7Tadashi G. Takaoka 35923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project/** 36e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa * This class loads a dictionary and provides a list of suggestions for a given sequence of 37923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * characters. This includes corrections and completions. 38923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */ 39923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectpublic class Suggest implements Dictionary.WordCallback { 40923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 4182411d47ba7e8133ed2390c6920945e139a738cesatok public static final String TAG = Suggest.class.getSimpleName(); 42cdbbea735f590784791f0c1fe33a514c4e864836satok 43979f8690967ff5409fe18f5085858ccdb8e0ccf1satok public static final int APPROX_MAX_WORD_LENGTH = 32; 44979f8690967ff5409fe18f5085858ccdb8e0ccf1satok 45923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project public static final int CORRECTION_NONE = 0; 464606de117b7541125f3f15bd6b50d77ed20e5132Jean Chalard public static final int CORRECTION_FULL = 1; 474606de117b7541125f3f15bd6b50d77ed20e5132Jean Chalard public static final int CORRECTION_FULL_BIGRAM = 2; 48979f8690967ff5409fe18f5085858ccdb8e0ccf1satok 49979f8690967ff5409fe18f5085858ccdb8e0ccf1satok /** 50979f8690967ff5409fe18f5085858ccdb8e0ccf1satok * Words that appear in both bigram and unigram data gets multiplier ranging from 51e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka * BIGRAM_MULTIPLIER_MIN to BIGRAM_MULTIPLIER_MAX depending on the score from 52979f8690967ff5409fe18f5085858ccdb8e0ccf1satok * bigram data. 53979f8690967ff5409fe18f5085858ccdb8e0ccf1satok */ 54979f8690967ff5409fe18f5085858ccdb8e0ccf1satok public static final double BIGRAM_MULTIPLIER_MIN = 1.2; 55979f8690967ff5409fe18f5085858ccdb8e0ccf1satok public static final double BIGRAM_MULTIPLIER_MAX = 1.5; 56979f8690967ff5409fe18f5085858ccdb8e0ccf1satok 57979f8690967ff5409fe18f5085858ccdb8e0ccf1satok /** 58979f8690967ff5409fe18f5085858ccdb8e0ccf1satok * Maximum possible bigram frequency. Will depend on how many bits are being used in data 590c8d5ca023d54b7c9ef6c20eb7988288132bacb5Jean Chalard * structure. Maximum bigram frequency will get the BIGRAM_MULTIPLIER_MAX as the multiplier. 60979f8690967ff5409fe18f5085858ccdb8e0ccf1satok */ 61979f8690967ff5409fe18f5085858ccdb8e0ccf1satok public static final int MAXIMUM_BIGRAM_FREQUENCY = 127; 62979f8690967ff5409fe18f5085858ccdb8e0ccf1satok 63f4223452119f9ff8b52f026f7ef92d961736dc51Jean Chalard // It seems the following values are only used for logging. 64979f8690967ff5409fe18f5085858ccdb8e0ccf1satok public static final int DIC_USER_TYPED = 0; 65979f8690967ff5409fe18f5085858ccdb8e0ccf1satok public static final int DIC_MAIN = 1; 66979f8690967ff5409fe18f5085858ccdb8e0ccf1satok public static final int DIC_USER = 2; 67f4223452119f9ff8b52f026f7ef92d961736dc51Jean Chalard public static final int DIC_USER_UNIGRAM = 3; 68979f8690967ff5409fe18f5085858ccdb8e0ccf1satok public static final int DIC_CONTACTS = 4; 69f4223452119f9ff8b52f026f7ef92d961736dc51Jean Chalard public static final int DIC_USER_BIGRAM = 5; 70fee149abe0358ff0efcebff3d0b60d8be83af437Jean Chalard public static final int DIC_WHITELIST = 6; 71979f8690967ff5409fe18f5085858ccdb8e0ccf1satok // If you add a type of dictionary, increment DIC_TYPE_LAST_ID 72f4223452119f9ff8b52f026f7ef92d961736dc51Jean Chalard // TODO: this value seems unused. Remove it? 73fee149abe0358ff0efcebff3d0b60d8be83af437Jean Chalard public static final int DIC_TYPE_LAST_ID = 6; 74bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok public static final String DICT_KEY_MAIN = "main"; 75bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok public static final String DICT_KEY_CONTACTS = "contacts"; 76f4223452119f9ff8b52f026f7ef92d961736dc51Jean Chalard // User dictionary, the system-managed one. 77bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok public static final String DICT_KEY_USER = "user"; 78f4223452119f9ff8b52f026f7ef92d961736dc51Jean Chalard // User unigram dictionary, internal to LatinIME 79f4223452119f9ff8b52f026f7ef92d961736dc51Jean Chalard public static final String DICT_KEY_USER_UNIGRAM = "user_unigram"; 80f4223452119f9ff8b52f026f7ef92d961736dc51Jean Chalard // User bigram dictionary, internal to LatinIME 81bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok public static final String DICT_KEY_USER_BIGRAM = "user_bigram"; 82bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok public static final String DICT_KEY_WHITELIST ="whitelist"; 83bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok 848553b5ec315660ab53dd9234e64e1e39ea09ec0fJean Chalard private static final boolean DBG = LatinImeLogger.sDBG; 8582411d47ba7e8133ed2390c6920945e139a738cesatok 864250eb27f54f8fedc388fe4825b0646a88778744Jean Chalard private Dictionary mMainDict; 8714051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard private ContactsDictionary mContactsDict; 88bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok private WhitelistDictionary mWhiteListDictionary; 89c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka private final Map<String, Dictionary> mUnigramDictionaries = new HashMap<String, Dictionary>(); 90c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka private final Map<String, Dictionary> mBigramDictionaries = new HashMap<String, Dictionary>(); 91979f8690967ff5409fe18f5085858ccdb8e0ccf1satok 9286e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka private int mPrefMaxSuggestions = 18; 9334386e698876c0f29b2d5b54b44db1e32b562c47Amith Yamasani 94979f8690967ff5409fe18f5085858ccdb8e0ccf1satok private static final int PREF_MAX_BIGRAMS = 60; 95979f8690967ff5409fe18f5085858ccdb8e0ccf1satok 961b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka private double mAutoCorrectionThreshold; 97e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka private int[] mScores = new int[mPrefMaxSuggestions]; 98e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka private int[] mBigramScores = new int[PREF_MAX_BIGRAMS]; 99979f8690967ff5409fe18f5085858ccdb8e0ccf1satok 100bf0f4d9c8b789035255d6b89752c77801929002eAmith Yamasani private ArrayList<CharSequence> mSuggestions = new ArrayList<CharSequence>(); 101979f8690967ff5409fe18f5085858ccdb8e0ccf1satok ArrayList<CharSequence> mBigramSuggestions = new ArrayList<CharSequence>(); 102c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard private CharSequence mConsideredWord; 1030b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa 1040b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa // TODO: Remove these member variables by passing more context to addWord() callback method 1050b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa private boolean mIsFirstCharCapitalized; 1060b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa private boolean mIsAllUpperCase; 107117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard private int mTrailingSingleQuotesCount; 108923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 109cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka private static final int MINIMUM_SAFETY_NET_CHAR_LENGTH = 4; 110cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka 1113af9f05f2916e376f265974c820c369a6c63a780Jean Chalard public Suggest(final Context context, final int dictionaryResId, final Locale locale) { 1123af9f05f2916e376f265974c820c369a6c63a780Jean Chalard initAsynchronously(context, dictionaryResId, locale); 113979f8690967ff5409fe18f5085858ccdb8e0ccf1satok } 114979f8690967ff5409fe18f5085858ccdb8e0ccf1satok 1153458d61807a03ed7fb8571488ee0fcbff39e07f8Jean Chalard /* package for test */ Suggest(final Context context, final File dictionary, 1163458d61807a03ed7fb8571488ee0fcbff39e07f8Jean Chalard final long startOffset, final long length, final Flag[] flagArray, 1173458d61807a03ed7fb8571488ee0fcbff39e07f8Jean Chalard final Locale locale) { 1189c735b803809add04d865a039259686b220a0e93Tadashi G. Takaoka initSynchronously(context, DictionaryFactory.createDictionaryForTest(context, dictionary, 1193458d61807a03ed7fb8571488ee0fcbff39e07f8Jean Chalard startOffset, length, flagArray), locale); 1209f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok } 1219f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok 1223458d61807a03ed7fb8571488ee0fcbff39e07f8Jean Chalard private void initWhitelistAndAutocorrectAndPool(final Context context, final Locale locale) { 1233458d61807a03ed7fb8571488ee0fcbff39e07f8Jean Chalard mWhiteListDictionary = new WhitelistDictionary(context, locale); 1243439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_WHITELIST, mWhiteListDictionary); 1254e01afc520da212b73804164d4d5a1c62239b02aJean Chalard StringBuilderPool.ensureCapacity(mPrefMaxSuggestions, getApproxMaxWordLength()); 12633e0b1e79e464ac48a09433bbfcbb17ded620452Tadashi G. Takaoka } 12733e0b1e79e464ac48a09433bbfcbb17ded620452Tadashi G. Takaoka 1283af9f05f2916e376f265974c820c369a6c63a780Jean Chalard private void initAsynchronously(final Context context, final int dictionaryResId, 1293af9f05f2916e376f265974c820c369a6c63a780Jean Chalard final Locale locale) { 1303af9f05f2916e376f265974c820c369a6c63a780Jean Chalard resetMainDict(context, dictionaryResId, locale); 1313af9f05f2916e376f265974c820c369a6c63a780Jean Chalard 1323af9f05f2916e376f265974c820c369a6c63a780Jean Chalard // TODO: read the whitelist and init the pool asynchronously too. 1334e01afc520da212b73804164d4d5a1c62239b02aJean Chalard // initPool should be done asynchronously now that the pool is thread-safe. 1343458d61807a03ed7fb8571488ee0fcbff39e07f8Jean Chalard initWhitelistAndAutocorrectAndPool(context, locale); 1353af9f05f2916e376f265974c820c369a6c63a780Jean Chalard } 1363af9f05f2916e376f265974c820c369a6c63a780Jean Chalard 1373458d61807a03ed7fb8571488ee0fcbff39e07f8Jean Chalard private void initSynchronously(final Context context, final Dictionary mainDict, 1383458d61807a03ed7fb8571488ee0fcbff39e07f8Jean Chalard final Locale locale) { 1393af9f05f2916e376f265974c820c369a6c63a780Jean Chalard mMainDict = mainDict; 1403af9f05f2916e376f265974c820c369a6c63a780Jean Chalard addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_MAIN, mainDict); 1413af9f05f2916e376f265974c820c369a6c63a780Jean Chalard addOrReplaceDictionary(mBigramDictionaries, DICT_KEY_MAIN, mainDict); 1423458d61807a03ed7fb8571488ee0fcbff39e07f8Jean Chalard initWhitelistAndAutocorrectAndPool(context, locale); 1433af9f05f2916e376f265974c820c369a6c63a780Jean Chalard } 1443af9f05f2916e376f265974c820c369a6c63a780Jean Chalard 1458fbf29e2d54027a17993cd0d4ad486e3454b56f6Tadashi G. Takaoka private static void addOrReplaceDictionary(Map<String, Dictionary> dictionaries, String key, 1463439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka Dictionary dict) { 1473439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka final Dictionary oldDict = (dict == null) 1483439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka ? dictionaries.remove(key) 1493439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka : dictionaries.put(key, dict); 1503439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka if (oldDict != null && dict != oldDict) { 1513439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka oldDict.close(); 1523439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka } 1533439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka } 1543439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka 1553af9f05f2916e376f265974c820c369a6c63a780Jean Chalard public void resetMainDict(final Context context, final int dictionaryResId, 1563af9f05f2916e376f265974c820c369a6c63a780Jean Chalard final Locale locale) { 1573af9f05f2916e376f265974c820c369a6c63a780Jean Chalard mMainDict = null; 1583af9f05f2916e376f265974c820c369a6c63a780Jean Chalard new Thread("InitializeBinaryDictionary") { 159904baab25a4c6ec5d9c4bf7e562154e3f544d296satok @Override 1603af9f05f2916e376f265974c820c369a6c63a780Jean Chalard public void run() { 1613af9f05f2916e376f265974c820c369a6c63a780Jean Chalard final Dictionary newMainDict = DictionaryFactory.createDictionaryFromManager( 1623af9f05f2916e376f265974c820c369a6c63a780Jean Chalard context, locale, dictionaryResId); 1633af9f05f2916e376f265974c820c369a6c63a780Jean Chalard mMainDict = newMainDict; 1643af9f05f2916e376f265974c820c369a6c63a780Jean Chalard addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_MAIN, newMainDict); 1653af9f05f2916e376f265974c820c369a6c63a780Jean Chalard addOrReplaceDictionary(mBigramDictionaries, DICT_KEY_MAIN, newMainDict); 1663af9f05f2916e376f265974c820c369a6c63a780Jean Chalard } 1673af9f05f2916e376f265974c820c369a6c63a780Jean Chalard }.start(); 168cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard } 169cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard 170c769ef4dd17ff9561e99528624f74b9072a09fbbKen Wakasa // The main dictionary could have been loaded asynchronously. Don't cache the return value 171c769ef4dd17ff9561e99528624f74b9072a09fbbKen Wakasa // of this method. 172e8f1edefeb2375a253d742c7f95e8d91677c7073Amith Yamasani public boolean hasMainDictionary() { 1734250eb27f54f8fedc388fe4825b0646a88778744Jean Chalard return mMainDict != null; 174e8f1edefeb2375a253d742c7f95e8d91677c7073Amith Yamasani } 175e8f1edefeb2375a253d742c7f95e8d91677c7073Amith Yamasani 17614051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard public ContactsDictionary getContactsDictionary() { 17714051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard return mContactsDict; 17814051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard } 17914051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard 180bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok public Map<String, Dictionary> getUnigramDictionaries() { 181bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok return mUnigramDictionaries; 182bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok } 183bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok 184979f8690967ff5409fe18f5085858ccdb8e0ccf1satok public int getApproxMaxWordLength() { 185979f8690967ff5409fe18f5085858ccdb8e0ccf1satok return APPROX_MAX_WORD_LENGTH; 186979f8690967ff5409fe18f5085858ccdb8e0ccf1satok } 187979f8690967ff5409fe18f5085858ccdb8e0ccf1satok 188923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project /** 189923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * Sets an optional user dictionary resource to be loaded. The user dictionary is consulted 190f4223452119f9ff8b52f026f7ef92d961736dc51Jean Chalard * before the main dictionary, if set. This refers to the system-managed user dictionary. 191923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */ 192923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project public void setUserDictionary(Dictionary userDictionary) { 1933439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_USER, userDictionary); 194923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 1952bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer 1962bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer /** 197699094f9b6e0a4621e8b3cfab70b59c0c7c086bbJean Chalard * Sets an optional contacts dictionary resource to be loaded. It is also possible to remove 198699094f9b6e0a4621e8b3cfab70b59c0c7c086bbJean Chalard * the contacts dictionary by passing null to this method. In this case no contacts dictionary 199699094f9b6e0a4621e8b3cfab70b59c0c7c086bbJean Chalard * won't be used. 2002bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer */ 20114051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard public void setContactsDictionary(ContactsDictionary contactsDictionary) { 20214051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard mContactsDict = contactsDictionary; 2033439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_CONTACTS, contactsDictionary); 2043439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka addOrReplaceDictionary(mBigramDictionaries, DICT_KEY_CONTACTS, contactsDictionary); 2052bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer } 206e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa 207f4223452119f9ff8b52f026f7ef92d961736dc51Jean Chalard public void setUserUnigramDictionary(Dictionary userUnigramDictionary) { 208f4223452119f9ff8b52f026f7ef92d961736dc51Jean Chalard addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_USER_UNIGRAM, userUnigramDictionary); 20934386e698876c0f29b2d5b54b44db1e32b562c47Amith Yamasani } 210923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 211979f8690967ff5409fe18f5085858ccdb8e0ccf1satok public void setUserBigramDictionary(Dictionary userBigramDictionary) { 2123439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka addOrReplaceDictionary(mBigramDictionaries, DICT_KEY_USER_BIGRAM, userBigramDictionary); 213979f8690967ff5409fe18f5085858ccdb8e0ccf1satok } 214979f8690967ff5409fe18f5085858ccdb8e0ccf1satok 2151b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka public void setAutoCorrectionThreshold(double threshold) { 2161b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka mAutoCorrectionThreshold = threshold; 217b1abda8d62d654e876c4f781a07d724922c736e4Mitsuhiro Shimoda } 218b1abda8d62d654e876c4f781a07d724922c736e4Mitsuhiro Shimoda 219923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project /** 220923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * Number of suggestions to generate from the input key sequence. This has 221923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * to be a number between 1 and 100 (inclusive). 222923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * @param maxSuggestions 223923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * @throws IllegalArgumentException if the number is out of range 224923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */ 225923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project public void setMaxSuggestions(int maxSuggestions) { 226923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project if (maxSuggestions < 1 || maxSuggestions > 100) { 227923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project throw new IllegalArgumentException("maxSuggestions must be between 1 and 100"); 228923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 229923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project mPrefMaxSuggestions = maxSuggestions; 230e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka mScores = new int[mPrefMaxSuggestions]; 231e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka mBigramScores = new int[PREF_MAX_BIGRAMS]; 232979f8690967ff5409fe18f5085858ccdb8e0ccf1satok collectGarbage(mSuggestions, mPrefMaxSuggestions); 2334e01afc520da212b73804164d4d5a1c62239b02aJean Chalard StringBuilderPool.ensureCapacity(mPrefMaxSuggestions, getApproxMaxWordLength()); 234923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 23563fa90a7910d9f43f27a0bf9a6702f8fb44ce3e7Amith Yamasani 236bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok private CharSequence capitalizeWord(boolean all, boolean first, CharSequence word) { 237bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok if (TextUtils.isEmpty(word) || !(all || first)) return word; 238bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok final int wordLength = word.length(); 2394e01afc520da212b73804164d4d5a1c62239b02aJean Chalard final StringBuilder sb = StringBuilderPool.getStringBuilder(getApproxMaxWordLength()); 24035f20916e5348d7fa485ba8eb0a5cf2e67f4f354Tadashi G. Takaoka // TODO: Must pay attention to locale when changing case. 241bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok if (all) { 242bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok sb.append(word.toString().toUpperCase()); 243bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok } else if (first) { 244bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok sb.append(Character.toUpperCase(word.charAt(0))); 245bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok if (wordLength > 1) { 246bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok sb.append(word.subSequence(1, wordLength)); 247bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok } 248bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok } 249bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok return sb; 250bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok } 251bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok 25289bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard protected void addBigramToSuggestions(CharSequence bigram) { 253a6e912cf9849f5c979303042ce83820a8dc560d0Jean Chalard // TODO: Try to be a little more shrewd with resource allocation. 254a6e912cf9849f5c979303042ce83820a8dc560d0Jean Chalard // At the moment we copy this object because the StringBuilders are pooled (see 255a6e912cf9849f5c979303042ce83820a8dc560d0Jean Chalard // StringBuilderPool.java) and when we are finished using mSuggestions and 256a6e912cf9849f5c979303042ce83820a8dc560d0Jean Chalard // mBigramSuggestions we will take everything from both and insert them back in the 257a6e912cf9849f5c979303042ce83820a8dc560d0Jean Chalard // pool, so we can't allow the same object to be in both lists at the same time. 258a6e912cf9849f5c979303042ce83820a8dc560d0Jean Chalard final StringBuilder sb = StringBuilderPool.getStringBuilder(getApproxMaxWordLength()); 259a6e912cf9849f5c979303042ce83820a8dc560d0Jean Chalard sb.append(bigram); 260a6e912cf9849f5c979303042ce83820a8dc560d0Jean Chalard mSuggestions.add(sb); 26189bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard } 26289bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard 263de165aed2ab9bfa13b5227cfe29d0770092db468Jean Chalard private static final WordComposer sEmptyWordComposer = new WordComposer(); 264a333ff19ef330c93287cfa0f6568d0cdcd431b04Jean Chalard public SuggestedWords.Builder getBigramPredictionWordBuilder(CharSequence prevWordForBigram) { 265de165aed2ab9bfa13b5227cfe29d0770092db468Jean Chalard LatinImeLogger.onStartSuggestion(prevWordForBigram); 266f08f30176b2020b36fa6ee856d9a0b84ee5b1fbaJean Chalard mIsFirstCharCapitalized = false; 267f08f30176b2020b36fa6ee856d9a0b84ee5b1fbaJean Chalard mIsAllUpperCase = false; 268f08f30176b2020b36fa6ee856d9a0b84ee5b1fbaJean Chalard mTrailingSingleQuotesCount = 0; 269de165aed2ab9bfa13b5227cfe29d0770092db468Jean Chalard collectGarbage(mSuggestions, mPrefMaxSuggestions); 270de165aed2ab9bfa13b5227cfe29d0770092db468Jean Chalard Arrays.fill(mScores, 0); 271de165aed2ab9bfa13b5227cfe29d0770092db468Jean Chalard 272de165aed2ab9bfa13b5227cfe29d0770092db468Jean Chalard // Treating USER_TYPED as UNIGRAM suggestion for logging now. 273dbd140504d0f1c6b02998d0899efc853e78a3966Jean Chalard LatinImeLogger.onAddSuggestedWord("", Suggest.DIC_USER_TYPED, Dictionary.UNIGRAM); 2740b96bc4b2b9c523c0228ed53e4576518cd7ab3ebJean Chalard mConsideredWord = ""; 275de165aed2ab9bfa13b5227cfe29d0770092db468Jean Chalard 276a333ff19ef330c93287cfa0f6568d0cdcd431b04Jean Chalard Arrays.fill(mBigramScores, 0); 277a333ff19ef330c93287cfa0f6568d0cdcd431b04Jean Chalard collectGarbage(mBigramSuggestions, PREF_MAX_BIGRAMS); 278a333ff19ef330c93287cfa0f6568d0cdcd431b04Jean Chalard 2790cf422fbb763e2672fb2f9e8e1e8af91d2e87cb3Jean Chalard CharSequence lowerPrevWord = prevWordForBigram.toString().toLowerCase(); 2800cf422fbb763e2672fb2f9e8e1e8af91d2e87cb3Jean Chalard if (mMainDict != null && mMainDict.isValidWord(lowerPrevWord)) { 2810cf422fbb763e2672fb2f9e8e1e8af91d2e87cb3Jean Chalard prevWordForBigram = lowerPrevWord; 2820cf422fbb763e2672fb2f9e8e1e8af91d2e87cb3Jean Chalard } 2830cf422fbb763e2672fb2f9e8e1e8af91d2e87cb3Jean Chalard for (final Dictionary dictionary : mBigramDictionaries.values()) { 2840cf422fbb763e2672fb2f9e8e1e8af91d2e87cb3Jean Chalard dictionary.getBigrams(sEmptyWordComposer, prevWordForBigram, this); 2850cf422fbb763e2672fb2f9e8e1e8af91d2e87cb3Jean Chalard } 2860cf422fbb763e2672fb2f9e8e1e8af91d2e87cb3Jean Chalard // Nothing entered: return all bigrams for the previous word 2870cf422fbb763e2672fb2f9e8e1e8af91d2e87cb3Jean Chalard int insertCount = Math.min(mBigramSuggestions.size(), mPrefMaxSuggestions); 2880cf422fbb763e2672fb2f9e8e1e8af91d2e87cb3Jean Chalard for (int i = 0; i < insertCount; ++i) { 2890cf422fbb763e2672fb2f9e8e1e8af91d2e87cb3Jean Chalard addBigramToSuggestions(mBigramSuggestions.get(i)); 290de165aed2ab9bfa13b5227cfe29d0770092db468Jean Chalard } 291de165aed2ab9bfa13b5227cfe29d0770092db468Jean Chalard 292de165aed2ab9bfa13b5227cfe29d0770092db468Jean Chalard StringUtils.removeDupes(mSuggestions); 293de165aed2ab9bfa13b5227cfe29d0770092db468Jean Chalard 294de165aed2ab9bfa13b5227cfe29d0770092db468Jean Chalard return new SuggestedWords.Builder().addWords(mSuggestions, null) 295dbd140504d0f1c6b02998d0899efc853e78a3966Jean Chalard .setAllowsToBeAutoCorrected(false) 2960b96bc4b2b9c523c0228ed53e4576518cd7ab3ebJean Chalard .setHasAutoCorrection(false); 297de165aed2ab9bfa13b5227cfe29d0770092db468Jean Chalard } 298de165aed2ab9bfa13b5227cfe29d0770092db468Jean Chalard 2997e181fe1010c8eac7814cc67a0c4b3864a10b151Tadashi G. Takaoka // TODO: cleanup dictionaries looking up and suggestions building with SuggestedWords.Builder 300904baab25a4c6ec5d9c4bf7e562154e3f544d296satok public SuggestedWords.Builder getSuggestedWordBuilder( 301043f7841985916717f4fa821fe3e423daf3ff2f5Jean Chalard final WordComposer wordComposer, CharSequence prevWordForBigram, 3024606de117b7541125f3f15bd6b50d77ed20e5132Jean Chalard final ProximityInfo proximityInfo, final int correctionMode) { 303979f8690967ff5409fe18f5085858ccdb8e0ccf1satok LatinImeLogger.onStartSuggestion(prevWordForBigram); 3040b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa mIsFirstCharCapitalized = wordComposer.isFirstCharCapitalized(); 3050b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa mIsAllUpperCase = wordComposer.isAllUpperCase(); 306117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard mTrailingSingleQuotesCount = wordComposer.trailingSingleQuotesCount(); 307979f8690967ff5409fe18f5085858ccdb8e0ccf1satok collectGarbage(mSuggestions, mPrefMaxSuggestions); 308e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka Arrays.fill(mScores, 0); 3091b62ff1a3d61cd44ab88acdfcbdf0fc70a7e1b10Amith Yamasani 310c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard final String typedWord = wordComposer.getTypedWord(); 311117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard final String consideredWord = mTrailingSingleQuotesCount > 0 312117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard ? typedWord.substring(0, typedWord.length() - mTrailingSingleQuotesCount) 313117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard : typedWord; 31492146f29269a799935c00c530d05829d7f17cc9eJean Chalard // Treating USER_TYPED as UNIGRAM suggestion for logging now. 31592146f29269a799935c00c530d05829d7f17cc9eJean Chalard LatinImeLogger.onAddSuggestedWord(typedWord, Suggest.DIC_USER_TYPED, 31692146f29269a799935c00c530d05829d7f17cc9eJean Chalard Dictionary.UNIGRAM); 317c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard mConsideredWord = consideredWord; 318979f8690967ff5409fe18f5085858ccdb8e0ccf1satok 319df9fce5df1bacdffb39c7926bdda4b205f186998Jean Chalard // TODO: Change this scheme - a boolean is not enough. A whitelisted word may be "valid" 320df9fce5df1bacdffb39c7926bdda4b205f186998Jean Chalard // but still autocorrected from - in the case the whitelist only capitalizes the word. 321df9fce5df1bacdffb39c7926bdda4b205f186998Jean Chalard // The whitelist should be case-insensitive, so it's not possible to be consistent with 322df9fce5df1bacdffb39c7926bdda4b205f186998Jean Chalard // a boolean flag. Right now this is handled with a slight hack in 323df9fce5df1bacdffb39c7926bdda4b205f186998Jean Chalard // WhitelistDictionary#shouldForciblyAutoCorrectFrom. 324df9fce5df1bacdffb39c7926bdda4b205f186998Jean Chalard final boolean allowsToBeAutoCorrected = AutoCorrection.allowsToBeAutoCorrected( 325df9fce5df1bacdffb39c7926bdda4b205f186998Jean Chalard getUnigramDictionaries(), consideredWord, wordComposer.isFirstCharCapitalized()); 326df9fce5df1bacdffb39c7926bdda4b205f186998Jean Chalard 3274606de117b7541125f3f15bd6b50d77ed20e5132Jean Chalard if (wordComposer.size() <= 1 && (correctionMode == CORRECTION_FULL_BIGRAM)) { 328979f8690967ff5409fe18f5085858ccdb8e0ccf1satok // At first character typed, search only the bigrams 329e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka Arrays.fill(mBigramScores, 0); 330979f8690967ff5409fe18f5085858ccdb8e0ccf1satok collectGarbage(mBigramSuggestions, PREF_MAX_BIGRAMS); 331979f8690967ff5409fe18f5085858ccdb8e0ccf1satok 332979f8690967ff5409fe18f5085858ccdb8e0ccf1satok if (!TextUtils.isEmpty(prevWordForBigram)) { 333979f8690967ff5409fe18f5085858ccdb8e0ccf1satok CharSequence lowerPrevWord = prevWordForBigram.toString().toLowerCase(); 334e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa if (mMainDict != null && mMainDict.isValidWord(lowerPrevWord)) { 335979f8690967ff5409fe18f5085858ccdb8e0ccf1satok prevWordForBigram = lowerPrevWord; 336979f8690967ff5409fe18f5085858ccdb8e0ccf1satok } 337c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka for (final Dictionary dictionary : mBigramDictionaries.values()) { 338c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka dictionary.getBigrams(wordComposer, prevWordForBigram, this); 339979f8690967ff5409fe18f5085858ccdb8e0ccf1satok } 340c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard if (TextUtils.isEmpty(consideredWord)) { 34189bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard // Nothing entered: return all bigrams for the previous word 34289bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard int insertCount = Math.min(mBigramSuggestions.size(), mPrefMaxSuggestions); 34389bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard for (int i = 0; i < insertCount; ++i) { 34489bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard addBigramToSuggestions(mBigramSuggestions.get(i)); 34589bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard } 34689bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard } else { 34789bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard // Word entered: return only bigrams that match the first char of the typed word 348c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard final char currentChar = consideredWord.charAt(0); 34935f20916e5348d7fa485ba8eb0a5cf2e67f4f354Tadashi G. Takaoka // TODO: Must pay attention to locale when changing case. 35089bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard final char currentCharUpper = Character.toUpperCase(currentChar); 35189bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard int count = 0; 35289bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard final int bigramSuggestionSize = mBigramSuggestions.size(); 35389bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard for (int i = 0; i < bigramSuggestionSize; i++) { 35489bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard final CharSequence bigramSuggestion = mBigramSuggestions.get(i); 35589bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard final char bigramSuggestionFirstChar = bigramSuggestion.charAt(0); 35689bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard if (bigramSuggestionFirstChar == currentChar 35789bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard || bigramSuggestionFirstChar == currentCharUpper) { 35889bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard addBigramToSuggestions(bigramSuggestion); 35989bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard if (++count > mPrefMaxSuggestions) break; 36089bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard } 361979f8690967ff5409fe18f5085858ccdb8e0ccf1satok } 362979f8690967ff5409fe18f5085858ccdb8e0ccf1satok } 363979f8690967ff5409fe18f5085858ccdb8e0ccf1satok } 364979f8690967ff5409fe18f5085858ccdb8e0ccf1satok 365979f8690967ff5409fe18f5085858ccdb8e0ccf1satok } else if (wordComposer.size() > 1) { 366979f8690967ff5409fe18f5085858ccdb8e0ccf1satok // At second character typed, search the unigrams (scores being affected by bigrams) 367c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka for (final String key : mUnigramDictionaries.keySet()) { 368f4223452119f9ff8b52f026f7ef92d961736dc51Jean Chalard // Skip UserUnigramDictionary and WhitelistDictionary to lookup 369f4223452119f9ff8b52f026f7ef92d961736dc51Jean Chalard if (key.equals(DICT_KEY_USER_UNIGRAM) || key.equals(DICT_KEY_WHITELIST)) 370c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka continue; 371c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka final Dictionary dictionary = mUnigramDictionaries.get(key); 372117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard if (mTrailingSingleQuotesCount > 0) { 373c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard final WordComposer tmpWordComposer = new WordComposer(wordComposer); 374117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard for (int i = mTrailingSingleQuotesCount - 1; i >= 0; --i) { 375117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard tmpWordComposer.deleteLast(); 376117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard } 377c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard dictionary.getWords(tmpWordComposer, this, proximityInfo); 378c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard } else { 379c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard dictionary.getWords(wordComposer, this, proximityInfo); 380c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard } 381923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 382923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 3839f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok 384bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok CharSequence whitelistedWord = capitalizeWord(mIsAllUpperCase, mIsFirstCharCapitalized, 3858abd15b59f87f2738b0d27b7c24a126b1450a17cJean Chalard mWhiteListDictionary.getWhitelistedWord(consideredWord)); 386bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok 38767af2a24157ead953607bdfd585fba3a7e6bf50cJean Chalard final boolean hasAutoCorrection; 38894b20c90d86aa042c2f361597665045271956decJean Chalard if (CORRECTION_FULL == correctionMode 38994b20c90d86aa042c2f361597665045271956decJean Chalard || CORRECTION_FULL_BIGRAM == correctionMode) { 39094b20c90d86aa042c2f361597665045271956decJean Chalard final CharSequence autoCorrection = 39194b20c90d86aa042c2f361597665045271956decJean Chalard AutoCorrection.computeAutoCorrectionWord(mUnigramDictionaries, wordComposer, 39294b20c90d86aa042c2f361597665045271956decJean Chalard mSuggestions, mScores, consideredWord, mAutoCorrectionThreshold, 39394b20c90d86aa042c2f361597665045271956decJean Chalard whitelistedWord); 39467af2a24157ead953607bdfd585fba3a7e6bf50cJean Chalard hasAutoCorrection = (null != autoCorrection); 39594b20c90d86aa042c2f361597665045271956decJean Chalard } else { 39667af2a24157ead953607bdfd585fba3a7e6bf50cJean Chalard hasAutoCorrection = false; 39794b20c90d86aa042c2f361597665045271956decJean Chalard } 3989f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok 399bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok if (whitelistedWord != null) { 400117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard if (mTrailingSingleQuotesCount > 0) { 401117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard final StringBuilder sb = new StringBuilder(whitelistedWord); 402117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard for (int i = mTrailingSingleQuotesCount - 1; i >= 0; --i) { 403117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard sb.appendCodePoint(Keyboard.CODE_SINGLE_QUOTE); 404117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard } 405117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard mSuggestions.add(0, sb.toString()); 406117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard } else { 407117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard mSuggestions.add(0, whitelistedWord); 408117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard } 409bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok } 410bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok 4118abd15b59f87f2738b0d27b7c24a126b1450a17cJean Chalard mSuggestions.add(0, typedWord); 412cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka StringUtils.removeDupes(mSuggestions); 4139f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok 4148553b5ec315660ab53dd9234e64e1e39ea09ec0fJean Chalard if (DBG) { 415719f92fc77d10a55fe78daa5bce9617d8a0af335Jean Chalard final CharSequence autoCorrectionSuggestion = mSuggestions.get(0); 416719f92fc77d10a55fe78daa5bce9617d8a0af335Jean Chalard final int autoCorrectionSuggestionScore = mScores[0]; 417719f92fc77d10a55fe78daa5bce9617d8a0af335Jean Chalard double normalizedScore = BinaryDictionary.calcNormalizedScore( 4188abd15b59f87f2738b0d27b7c24a126b1450a17cJean Chalard typedWord, autoCorrectionSuggestion.toString(), 419719f92fc77d10a55fe78daa5bce9617d8a0af335Jean Chalard autoCorrectionSuggestionScore); 420e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka ArrayList<SuggestedWords.SuggestedWordInfo> scoreInfoList = 4218553b5ec315660ab53dd9234e64e1e39ea09ec0fJean Chalard new ArrayList<SuggestedWords.SuggestedWordInfo>(); 422e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka scoreInfoList.add(new SuggestedWords.SuggestedWordInfo("+", false)); 423e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka for (int i = 0; i < mScores.length; ++i) { 4248553b5ec315660ab53dd9234e64e1e39ea09ec0fJean Chalard if (normalizedScore > 0) { 425071f47140cec02197de5e163f45c77990b39457dTadashi G. Takaoka final String scoreThreshold = String.format("%d (%4.2f)", mScores[i], 426071f47140cec02197de5e163f45c77990b39457dTadashi G. Takaoka normalizedScore); 427e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka scoreInfoList.add( 428e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka new SuggestedWords.SuggestedWordInfo(scoreThreshold, false)); 4298553b5ec315660ab53dd9234e64e1e39ea09ec0fJean Chalard normalizedScore = 0.0; 4308553b5ec315660ab53dd9234e64e1e39ea09ec0fJean Chalard } else { 431e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka final String score = Integer.toString(mScores[i]); 432e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka scoreInfoList.add(new SuggestedWords.SuggestedWordInfo(score, false)); 4338553b5ec315660ab53dd9234e64e1e39ea09ec0fJean Chalard } 4348553b5ec315660ab53dd9234e64e1e39ea09ec0fJean Chalard } 435e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka for (int i = mScores.length; i < mSuggestions.size(); ++i) { 436e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka scoreInfoList.add(new SuggestedWords.SuggestedWordInfo("--", false)); 4378553b5ec315660ab53dd9234e64e1e39ea09ec0fJean Chalard } 438df9fce5df1bacdffb39c7926bdda4b205f186998Jean Chalard return new SuggestedWords.Builder().addWords(mSuggestions, scoreInfoList) 43967af2a24157ead953607bdfd585fba3a7e6bf50cJean Chalard .setAllowsToBeAutoCorrected(allowsToBeAutoCorrected) 44067af2a24157ead953607bdfd585fba3a7e6bf50cJean Chalard .setHasAutoCorrection(hasAutoCorrection); 4418553b5ec315660ab53dd9234e64e1e39ea09ec0fJean Chalard } 442df9fce5df1bacdffb39c7926bdda4b205f186998Jean Chalard return new SuggestedWords.Builder().addWords(mSuggestions, null) 44367af2a24157ead953607bdfd585fba3a7e6bf50cJean Chalard .setAllowsToBeAutoCorrected(allowsToBeAutoCorrected) 44467af2a24157ead953607bdfd585fba3a7e6bf50cJean Chalard .setHasAutoCorrection(hasAutoCorrection); 445923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 446923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 4475a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka @Override 448e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka public boolean addWord(final char[] word, final int offset, final int length, int score, 4496e082cb30dbe1a8cc314b474dc1377b85fdb25c2Jean Chalard final int dicTypeId, final int dataType) { 4506e082cb30dbe1a8cc314b474dc1377b85fdb25c2Jean Chalard int dataTypeForLog = dataType; 451e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka final ArrayList<CharSequence> suggestions; 452e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka final int[] sortedScores; 453e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka final int prefMaxSuggestions; 4546e082cb30dbe1a8cc314b474dc1377b85fdb25c2Jean Chalard if (dataType == Dictionary.BIGRAM) { 455979f8690967ff5409fe18f5085858ccdb8e0ccf1satok suggestions = mBigramSuggestions; 456e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka sortedScores = mBigramScores; 457979f8690967ff5409fe18f5085858ccdb8e0ccf1satok prefMaxSuggestions = PREF_MAX_BIGRAMS; 458979f8690967ff5409fe18f5085858ccdb8e0ccf1satok } else { 459979f8690967ff5409fe18f5085858ccdb8e0ccf1satok suggestions = mSuggestions; 460e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka sortedScores = mScores; 461979f8690967ff5409fe18f5085858ccdb8e0ccf1satok prefMaxSuggestions = mPrefMaxSuggestions; 462979f8690967ff5409fe18f5085858ccdb8e0ccf1satok } 463979f8690967ff5409fe18f5085858ccdb8e0ccf1satok 464923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project int pos = 0; 465979f8690967ff5409fe18f5085858ccdb8e0ccf1satok 466923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project // Check if it's the same word, only caps are different 467cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka if (StringUtils.equalsIgnoreCase(mConsideredWord, word, offset, length)) { 468d631651b1291aef52bdd6ea7caaf9b95c9704506Jean Chalard // TODO: remove this surrounding if clause and move this logic to 469d631651b1291aef52bdd6ea7caaf9b95c9704506Jean Chalard // getSuggestedWordBuilder. 470d631651b1291aef52bdd6ea7caaf9b95c9704506Jean Chalard if (suggestions.size() > 0) { 471e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka final String currentHighestWord = suggestions.get(0).toString(); 472d631651b1291aef52bdd6ea7caaf9b95c9704506Jean Chalard // If the current highest word is also equal to typed word, we need to compare 473d631651b1291aef52bdd6ea7caaf9b95c9704506Jean Chalard // frequency to determine the insertion position. This does not ensure strictly 474d631651b1291aef52bdd6ea7caaf9b95c9704506Jean Chalard // correct ordering, but ensures the top score is on top which is enough for 475d631651b1291aef52bdd6ea7caaf9b95c9704506Jean Chalard // removing duplicates correctly. 476cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka if (StringUtils.equalsIgnoreCase(currentHighestWord, word, offset, length) 477e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka && score <= sortedScores[0]) { 478d631651b1291aef52bdd6ea7caaf9b95c9704506Jean Chalard pos = 1; 479d631651b1291aef52bdd6ea7caaf9b95c9704506Jean Chalard } 480d631651b1291aef52bdd6ea7caaf9b95c9704506Jean Chalard } 481923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } else { 4826e082cb30dbe1a8cc314b474dc1377b85fdb25c2Jean Chalard if (dataType == Dictionary.UNIGRAM) { 483979f8690967ff5409fe18f5085858ccdb8e0ccf1satok // Check if the word was already added before (by bigram data) 484979f8690967ff5409fe18f5085858ccdb8e0ccf1satok int bigramSuggestion = searchBigramSuggestion(word,offset,length); 485979f8690967ff5409fe18f5085858ccdb8e0ccf1satok if(bigramSuggestion >= 0) { 4866e082cb30dbe1a8cc314b474dc1377b85fdb25c2Jean Chalard dataTypeForLog = Dictionary.BIGRAM; 487979f8690967ff5409fe18f5085858ccdb8e0ccf1satok // turn freq from bigram into multiplier specified above 488e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka double multiplier = (((double) mBigramScores[bigramSuggestion]) 489979f8690967ff5409fe18f5085858ccdb8e0ccf1satok / MAXIMUM_BIGRAM_FREQUENCY) 490979f8690967ff5409fe18f5085858ccdb8e0ccf1satok * (BIGRAM_MULTIPLIER_MAX - BIGRAM_MULTIPLIER_MIN) 491979f8690967ff5409fe18f5085858ccdb8e0ccf1satok + BIGRAM_MULTIPLIER_MIN; 492979f8690967ff5409fe18f5085858ccdb8e0ccf1satok /* Log.d(TAG,"bigram num: " + bigramSuggestion 493979f8690967ff5409fe18f5085858ccdb8e0ccf1satok + " wordB: " + mBigramSuggestions.get(bigramSuggestion).toString() 494e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka + " currentScore: " + score + " bigramScore: " 495e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka + mBigramScores[bigramSuggestion] 496979f8690967ff5409fe18f5085858ccdb8e0ccf1satok + " multiplier: " + multiplier); */ 497e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka score = (int)Math.round((score * multiplier)); 498979f8690967ff5409fe18f5085858ccdb8e0ccf1satok } 499979f8690967ff5409fe18f5085858ccdb8e0ccf1satok } 500979f8690967ff5409fe18f5085858ccdb8e0ccf1satok 501e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka // Check the last one's score and bail 502e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka if (sortedScores[prefMaxSuggestions - 1] >= score) return true; 503923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project while (pos < prefMaxSuggestions) { 504e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka if (sortedScores[pos] < score 505e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka || (sortedScores[pos] == score && length < suggestions.get(pos).length())) { 506923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project break; 507923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 508923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project pos++; 509923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 510923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 511923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project if (pos >= prefMaxSuggestions) { 512923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project return true; 513923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 514979f8690967ff5409fe18f5085858ccdb8e0ccf1satok 515e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka System.arraycopy(sortedScores, pos, sortedScores, pos + 1, prefMaxSuggestions - pos - 1); 516e7a2512aa3666e1b891dc7dfc5a0cb28fd66bea9Tadashi G. Takaoka sortedScores[pos] = score; 5174e01afc520da212b73804164d4d5a1c62239b02aJean Chalard final StringBuilder sb = StringBuilderPool.getStringBuilder(getApproxMaxWordLength()); 51835f20916e5348d7fa485ba8eb0a5cf2e67f4f354Tadashi G. Takaoka // TODO: Must pay attention to locale when changing case. 5190b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa if (mIsAllUpperCase) { 5200b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa sb.append(new String(word, offset, length).toUpperCase()); 5210b4ae1f578e768eec4ada90aeb81d11acb10eb2eKen Wakasa } else if (mIsFirstCharCapitalized) { 522359f168161074acb11fcd7a443c91fe3cc010e1dAmith Yamasani sb.append(Character.toUpperCase(word[offset])); 523359f168161074acb11fcd7a443c91fe3cc010e1dAmith Yamasani if (length > 1) { 524359f168161074acb11fcd7a443c91fe3cc010e1dAmith Yamasani sb.append(word, offset + 1, length - 1); 525359f168161074acb11fcd7a443c91fe3cc010e1dAmith Yamasani } 526359f168161074acb11fcd7a443c91fe3cc010e1dAmith Yamasani } else { 527359f168161074acb11fcd7a443c91fe3cc010e1dAmith Yamasani sb.append(word, offset, length); 528359f168161074acb11fcd7a443c91fe3cc010e1dAmith Yamasani } 529117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard for (int i = mTrailingSingleQuotesCount - 1; i >= 0; --i) { 530c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard sb.appendCodePoint(Keyboard.CODE_SINGLE_QUOTE); 531c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard } 532979f8690967ff5409fe18f5085858ccdb8e0ccf1satok suggestions.add(pos, sb); 533979f8690967ff5409fe18f5085858ccdb8e0ccf1satok if (suggestions.size() > prefMaxSuggestions) { 5344e01afc520da212b73804164d4d5a1c62239b02aJean Chalard final CharSequence garbage = suggestions.remove(prefMaxSuggestions); 535923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project if (garbage instanceof StringBuilder) { 5364e01afc520da212b73804164d4d5a1c62239b02aJean Chalard StringBuilderPool.recycle((StringBuilder)garbage); 537923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 538979f8690967ff5409fe18f5085858ccdb8e0ccf1satok } else { 539979f8690967ff5409fe18f5085858ccdb8e0ccf1satok LatinImeLogger.onAddSuggestedWord(sb.toString(), dicTypeId, dataTypeForLog); 540923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 541923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project return true; 542923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 543923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 544979f8690967ff5409fe18f5085858ccdb8e0ccf1satok private int searchBigramSuggestion(final char[] word, final int offset, final int length) { 545979f8690967ff5409fe18f5085858ccdb8e0ccf1satok // TODO This is almost O(n^2). Might need fix. 546979f8690967ff5409fe18f5085858ccdb8e0ccf1satok // search whether the word appeared in bigram data 547979f8690967ff5409fe18f5085858ccdb8e0ccf1satok int bigramSuggestSize = mBigramSuggestions.size(); 548979f8690967ff5409fe18f5085858ccdb8e0ccf1satok for(int i = 0; i < bigramSuggestSize; i++) { 549979f8690967ff5409fe18f5085858ccdb8e0ccf1satok if(mBigramSuggestions.get(i).length() == length) { 550979f8690967ff5409fe18f5085858ccdb8e0ccf1satok boolean chk = true; 551979f8690967ff5409fe18f5085858ccdb8e0ccf1satok for(int j = 0; j < length; j++) { 552979f8690967ff5409fe18f5085858ccdb8e0ccf1satok if(mBigramSuggestions.get(i).charAt(j) != word[offset+j]) { 553979f8690967ff5409fe18f5085858ccdb8e0ccf1satok chk = false; 554979f8690967ff5409fe18f5085858ccdb8e0ccf1satok break; 555979f8690967ff5409fe18f5085858ccdb8e0ccf1satok } 556979f8690967ff5409fe18f5085858ccdb8e0ccf1satok } 557979f8690967ff5409fe18f5085858ccdb8e0ccf1satok if(chk) return i; 558979f8690967ff5409fe18f5085858ccdb8e0ccf1satok } 559979f8690967ff5409fe18f5085858ccdb8e0ccf1satok } 560979f8690967ff5409fe18f5085858ccdb8e0ccf1satok 561979f8690967ff5409fe18f5085858ccdb8e0ccf1satok return -1; 562979f8690967ff5409fe18f5085858ccdb8e0ccf1satok } 563979f8690967ff5409fe18f5085858ccdb8e0ccf1satok 5648fbf29e2d54027a17993cd0d4ad486e3454b56f6Tadashi G. Takaoka private static void collectGarbage(ArrayList<CharSequence> suggestions, 5658fbf29e2d54027a17993cd0d4ad486e3454b56f6Tadashi G. Takaoka int prefMaxSuggestions) { 5664e01afc520da212b73804164d4d5a1c62239b02aJean Chalard int poolSize = StringBuilderPool.getSize(); 567979f8690967ff5409fe18f5085858ccdb8e0ccf1satok int garbageSize = suggestions.size(); 568979f8690967ff5409fe18f5085858ccdb8e0ccf1satok while (poolSize < prefMaxSuggestions && garbageSize > 0) { 5694e01afc520da212b73804164d4d5a1c62239b02aJean Chalard final CharSequence garbage = suggestions.get(garbageSize - 1); 5704e01afc520da212b73804164d4d5a1c62239b02aJean Chalard if (garbage instanceof StringBuilder) { 5714e01afc520da212b73804164d4d5a1c62239b02aJean Chalard StringBuilderPool.recycle((StringBuilder)garbage); 572923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project poolSize++; 573923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 574923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project garbageSize--; 575923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 576979f8690967ff5409fe18f5085858ccdb8e0ccf1satok if (poolSize == prefMaxSuggestions + 1) { 577923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project Log.w("Suggest", "String pool got too big: " + poolSize); 578923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 579979f8690967ff5409fe18f5085858ccdb8e0ccf1satok suggestions.clear(); 580923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 58136fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani 58236fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani public void close() { 583c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka final Set<Dictionary> dictionaries = new HashSet<Dictionary>(); 584c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka dictionaries.addAll(mUnigramDictionaries.values()); 585c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka dictionaries.addAll(mBigramDictionaries.values()); 586c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka for (final Dictionary dictionary : dictionaries) { 587c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka dictionary.close(); 58836fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani } 589c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka mMainDict = null; 59036fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani } 591cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka 592cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka // TODO: Resolve the inconsistencies between the native auto correction algorithms and 593cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka // this safety net 594cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka public static boolean shouldBlockAutoCorrectionBySafetyNet( 59513fb8fb775f77610105115d7cb30f6177ec6d771Jean Chalard final SuggestedWords.Builder suggestedWordsBuilder, final Suggest suggest, 59613fb8fb775f77610105115d7cb30f6177ec6d771Jean Chalard final double autoCorrectionThreshold) { 597cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka // Safety net for auto correction. 598cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka // Actually if we hit this safety net, it's actually a bug. 599cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka if (suggestedWordsBuilder.size() <= 1 || suggestedWordsBuilder.isTypedWordValid()) { 600cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka return false; 601cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka } 602cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka // If user selected aggressive auto correction mode, there is no need to use the safety 603cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka // net. 60413fb8fb775f77610105115d7cb30f6177ec6d771Jean Chalard if (0 == autoCorrectionThreshold) { 605cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka return false; 606cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka } 607cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka final CharSequence typedWord = suggestedWordsBuilder.getWord(0); 608cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka // If the length of typed word is less than MINIMUM_SAFETY_NET_CHAR_LENGTH, 609cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka // we should not use net because relatively edit distance can be big. 610cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka if (typedWord.length() < Suggest.MINIMUM_SAFETY_NET_CHAR_LENGTH) { 611cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka return false; 612cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka } 613cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka final CharSequence suggestionWord = suggestedWordsBuilder.getWord(1); 614cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka final int typedWordLength = typedWord.length(); 615cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka final int maxEditDistanceOfNativeDictionary = 616cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka (typedWordLength < 5 ? 2 : typedWordLength / 2) + 1; 617cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka final int distance = BinaryDictionary.editDistance( 618cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka typedWord.toString(), suggestionWord.toString()); 619cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka if (DBG) { 620cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka Log.d(TAG, "Autocorrected edit distance = " + distance 621cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka + ", " + maxEditDistanceOfNativeDictionary); 622cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka } 623cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka if (distance > maxEditDistanceOfNativeDictionary) { 624cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka if (DBG) { 625cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka Log.e(TAG, "Safety net: before = " + typedWord + ", after = " + suggestionWord); 626cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka Log.e(TAG, "(Error) The edit distance of this correction exceeds limit. " 627cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka + "Turning off auto-correction."); 628cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka } 629cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka return true; 630cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka } else { 631cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka return false; 632cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka } 633cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka } 634923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project} 635