Suggest.java revision 9666a228153bb2269da8983762bdd47e448f2cec
1923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project/* 2443c360d0afdbab091994244f045f4756feaf2b4Jean-Baptiste Queru * Copyright (C) 2008 The Android Open Source Project 3e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa * 48aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * Licensed under the Apache License, Version 2.0 (the "License"); 58aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * you may not use this file except in compliance with the License. 68aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * You may obtain a copy of the License at 7e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa * 88aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * 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 118aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * distributed under the License is distributed on an "AS IS" BASIS, 128aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 138aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * See the License for the specific language governing permissions and 148aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * limitations under 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; 20d7491e6e817e8954d15aa91300d209fb7e8bf384Satoshi Kataokaimport android.preference.PreferenceManager; 21923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.text.TextUtils; 22d7491e6e817e8954d15aa91300d209fb7e8bf384Satoshi Kataokaimport android.util.Log; 23923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 2415f6d4ae34664ea3d92827a2c3003198c0bac70bTadashi G. Takaokaimport com.android.inputmethod.annotations.UsedForTesting; 25043f7841985916717f4fa821fe3e423daf3ff2f5Jean Chalardimport com.android.inputmethod.keyboard.ProximityInfo; 26def4551c2a570e7f575b2e9303506d790c2f335fJean Chalardimport com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; 27366c0c5198f43279f4671a196556124f41297c0cSatoshi Kataokaimport com.android.inputmethod.latin.personalization.PersonalizationDictionary; 2860586b57cf4ca4af16cf9a9261aaba9490f128bcSatoshi Kataokaimport com.android.inputmethod.latin.personalization.PersonalizationPredictionDictionary; 2987d06afc66db68f0b30b36593095511314793517Satoshi Kataokaimport com.android.inputmethod.latin.personalization.UserHistoryPredictionDictionary; 30d7491e6e817e8954d15aa91300d209fb7e8bf384Satoshi Kataokaimport com.android.inputmethod.latin.settings.Settings; 31b03447e1af950888d901fccbd2cc3e3b4a11ef98Ken Wakasaimport com.android.inputmethod.latin.utils.AutoCorrectionUtils; 32e28eba5074664d5716b8e58b8d0a235746b261ebKen Wakasaimport com.android.inputmethod.latin.utils.BoundedTreeSet; 33e28eba5074664d5716b8e58b8d0a235746b261ebKen Wakasaimport com.android.inputmethod.latin.utils.CollectionUtils; 34e28eba5074664d5716b8e58b8d0a235746b261ebKen Wakasaimport com.android.inputmethod.latin.utils.StringUtils; 35043f7841985916717f4fa821fe3e423daf3ff2f5Jean Chalard 36fa086c90760bc2bedf0b74eacb0fed3bf7ebc2b7Tadashi G. Takaokaimport java.util.ArrayList; 379da0027b386c23b83c2f9b0121bc15fa15306e3aJean Chalardimport java.util.Comparator; 38c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaokaimport java.util.HashSet; 39cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalardimport java.util.Locale; 401b06b59e28743b713947947437ea5b312477f808Jean Chalardimport java.util.concurrent.ConcurrentHashMap; 41fa086c90760bc2bedf0b74eacb0fed3bf7ebc2b7Tadashi G. Takaoka 42923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project/** 43e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa * This class loads a dictionary and provides a list of suggestions for a given sequence of 44923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * characters. This includes corrections and completions. 45923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */ 46a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaokapublic final class Suggest { 4782411d47ba7e8133ed2390c6920945e139a738cesatok public static final String TAG = Suggest.class.getSimpleName(); 48cdbbea735f590784791f0c1fe33a514c4e864836satok 49f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka // Session id for 50bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka // {@link #getSuggestedWords(WordComposer,String,ProximityInfo,boolean,int)}. 51f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka public static final int SESSION_TYPING = 0; 52f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka public static final int SESSION_GESTURE = 1; 53f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka 545475e92b3fb33dd7d6b021ddcbe1ca593112b5c8Jean Chalard // TODO: rename this to CORRECTION_OFF 55923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project public static final int CORRECTION_NONE = 0; 565475e92b3fb33dd7d6b021ddcbe1ca593112b5c8Jean Chalard // TODO: rename this to CORRECTION_ON 574606de117b7541125f3f15bd6b50d77ed20e5132Jean Chalard public static final int CORRECTION_FULL = 1; 58979f8690967ff5409fe18f5085858ccdb8e0ccf1satok 59a5a2f3e3c77ebf2e1bb74b08c8587c15b9711ac8Jean Chalard // Close to -2**31 60a5a2f3e3c77ebf2e1bb74b08c8587c15b9711ac8Jean Chalard private static final int SUPPRESS_SUGGEST_THRESHOLD = -2000000000; 61a5a2f3e3c77ebf2e1bb74b08c8587c15b9711ac8Jean Chalard 62d7491e6e817e8954d15aa91300d209fb7e8bf384Satoshi Kataoka public static final int MAX_SUGGESTIONS = 18; 63d7491e6e817e8954d15aa91300d209fb7e8bf384Satoshi Kataoka 64369e54cc338d8115d63138663b882f56208d7ec3Tadashi G. Takaoka public interface SuggestInitializationListener { 65369e54cc338d8115d63138663b882f56208d7ec3Tadashi G. Takaoka public void onUpdateMainDictionaryAvailability(boolean isMainDictionaryAvailable); 66369e54cc338d8115d63138663b882f56208d7ec3Tadashi G. Takaoka } 67369e54cc338d8115d63138663b882f56208d7ec3Tadashi G. Takaoka 688553b5ec315660ab53dd9234e64e1e39ea09ec0fJean Chalard private static final boolean DBG = LatinImeLogger.sDBG; 6982411d47ba7e8133ed2390c6920945e139a738cesatok 706234be1fe76740c458781b633f4ac66edd8ea84fJean Chalard private final ConcurrentHashMap<String, Dictionary> mDictionaries = 715f282ea9e4a4590fcbab6e27d5fca7dacbb40a6aTadashi G. Takaoka CollectionUtils.newConcurrentHashMap(); 72d7491e6e817e8954d15aa91300d209fb7e8bf384Satoshi Kataoka private HashSet<String> mOnlyDictionarySetForDebug = null; 73d7491e6e817e8954d15aa91300d209fb7e8bf384Satoshi Kataoka private Dictionary mMainDictionary; 74d7491e6e817e8954d15aa91300d209fb7e8bf384Satoshi Kataoka private ContactsBinaryDictionary mContactsDict; 7501a4ebcd88f8a7001aac2f7f45293ceab717a30dJean Chalard @UsedForTesting 7601a4ebcd88f8a7001aac2f7f45293ceab717a30dJean Chalard private boolean mIsCurrentlyWaitingForMainDictionary = false; 77979f8690967ff5409fe18f5085858ccdb8e0ccf1satok 780028ed3627ff4f37a62a80f3b2c857e373cd5090satok private float mAutoCorrectionThreshold; 79979f8690967ff5409fe18f5085858ccdb8e0ccf1satok 808e17f6d93a3b079eab41450539b9890763fb6e3fJean Chalard // Locale used for upper- and title-casing words 81d91268ad9fb69b4733044b4e466e1d33f6c4725fJean Chalard public final Locale mLocale; 828e17f6d93a3b079eab41450539b9890763fb6e3fJean Chalard 83369e54cc338d8115d63138663b882f56208d7ec3Tadashi G. Takaoka public Suggest(final Context context, final Locale locale, 84369e54cc338d8115d63138663b882f56208d7ec3Tadashi G. Takaoka final SuggestInitializationListener listener) { 8579eefda0d3ab5e03c2d012ed8ea1898004781871Tadashi G. Takaoka initAsynchronously(context, locale, listener); 868e17f6d93a3b079eab41450539b9890763fb6e3fJean Chalard mLocale = locale; 87d7491e6e817e8954d15aa91300d209fb7e8bf384Satoshi Kataoka // initialize a debug flag for the personalization 88d7491e6e817e8954d15aa91300d209fb7e8bf384Satoshi Kataoka if (Settings.readUseOnlyPersonalizationDictionaryForDebug( 89d7491e6e817e8954d15aa91300d209fb7e8bf384Satoshi Kataoka PreferenceManager.getDefaultSharedPreferences(context))) { 90d7491e6e817e8954d15aa91300d209fb7e8bf384Satoshi Kataoka mOnlyDictionarySetForDebug = new HashSet<String>(); 91d7491e6e817e8954d15aa91300d209fb7e8bf384Satoshi Kataoka mOnlyDictionarySetForDebug.add(Dictionary.TYPE_PERSONALIZATION); 92d7491e6e817e8954d15aa91300d209fb7e8bf384Satoshi Kataoka mOnlyDictionarySetForDebug.add(Dictionary.TYPE_PERSONALIZATION_PREDICTION_IN_JAVA); 93d7491e6e817e8954d15aa91300d209fb7e8bf384Satoshi Kataoka } 94979f8690967ff5409fe18f5085858ccdb8e0ccf1satok } 95979f8690967ff5409fe18f5085858ccdb8e0ccf1satok 9615f6d4ae34664ea3d92827a2c3003198c0bac70bTadashi G. Takaoka @UsedForTesting 971562fc91f015616f900b82bb44e6f1493be92c5aJean Chalard Suggest(final AssetFileAddress[] dictionaryList, final Locale locale) { 981562fc91f015616f900b82bb44e6f1493be92c5aJean Chalard final Dictionary mainDict = DictionaryFactory.createDictionaryForTest(dictionaryList, 991562fc91f015616f900b82bb44e6f1493be92c5aJean Chalard false /* useFullEditDistance */, locale); 1008e17f6d93a3b079eab41450539b9890763fb6e3fJean Chalard mLocale = locale; 1016080f6878b10916013a8a5e1d5f58f8041452c56Kurt Partridge mMainDictionary = mainDict; 102d7491e6e817e8954d15aa91300d209fb7e8bf384Satoshi Kataoka addOrReplaceDictionaryInternal(Dictionary.TYPE_MAIN, mainDict); 10333e0b1e79e464ac48a09433bbfcbb17ded620452Tadashi G. Takaoka } 10433e0b1e79e464ac48a09433bbfcbb17ded620452Tadashi G. Takaoka 10579eefda0d3ab5e03c2d012ed8ea1898004781871Tadashi G. Takaoka private void initAsynchronously(final Context context, final Locale locale, 10679eefda0d3ab5e03c2d012ed8ea1898004781871Tadashi G. Takaoka final SuggestInitializationListener listener) { 10779eefda0d3ab5e03c2d012ed8ea1898004781871Tadashi G. Takaoka resetMainDict(context, locale, listener); 1083af9f05f2916e376f265974c820c369a6c63a780Jean Chalard } 1093af9f05f2916e376f265974c820c369a6c63a780Jean Chalard 110d7491e6e817e8954d15aa91300d209fb7e8bf384Satoshi Kataoka private void addOrReplaceDictionaryInternal(final String key, final Dictionary dict) { 111d09ad2e3683a25a4fb461dfcf3e6f4d92a5822ddSatoshi Kataoka if (mOnlyDictionarySetForDebug != null && !mOnlyDictionarySetForDebug.contains(key)) { 112d7491e6e817e8954d15aa91300d209fb7e8bf384Satoshi Kataoka Log.w(TAG, "Ignore add " + key + " dictionary for debug."); 113d7491e6e817e8954d15aa91300d209fb7e8bf384Satoshi Kataoka return; 114d7491e6e817e8954d15aa91300d209fb7e8bf384Satoshi Kataoka } 115d7491e6e817e8954d15aa91300d209fb7e8bf384Satoshi Kataoka addOrReplaceDictionary(mDictionaries, key, dict); 116d7491e6e817e8954d15aa91300d209fb7e8bf384Satoshi Kataoka } 117d7491e6e817e8954d15aa91300d209fb7e8bf384Satoshi Kataoka 1181b06b59e28743b713947947437ea5b312477f808Jean Chalard private static void addOrReplaceDictionary( 1191b06b59e28743b713947947437ea5b312477f808Jean Chalard final ConcurrentHashMap<String, Dictionary> dictionaries, 1201b06b59e28743b713947947437ea5b312477f808Jean Chalard final String key, final Dictionary dict) { 1213439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka final Dictionary oldDict = (dict == null) 1223439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka ? dictionaries.remove(key) 1233439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka : dictionaries.put(key, dict); 1243439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka if (oldDict != null && dict != oldDict) { 1253439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka oldDict.close(); 1263439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka } 1273439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka } 1283439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka 12979eefda0d3ab5e03c2d012ed8ea1898004781871Tadashi G. Takaoka public void resetMainDict(final Context context, final Locale locale, 13079eefda0d3ab5e03c2d012ed8ea1898004781871Tadashi G. Takaoka final SuggestInitializationListener listener) { 13101a4ebcd88f8a7001aac2f7f45293ceab717a30dJean Chalard mIsCurrentlyWaitingForMainDictionary = true; 1326080f6878b10916013a8a5e1d5f58f8041452c56Kurt Partridge mMainDictionary = null; 13379eefda0d3ab5e03c2d012ed8ea1898004781871Tadashi G. Takaoka if (listener != null) { 13479eefda0d3ab5e03c2d012ed8ea1898004781871Tadashi G. Takaoka listener.onUpdateMainDictionaryAvailability(hasMainDictionary()); 135369e54cc338d8115d63138663b882f56208d7ec3Tadashi G. Takaoka } 1363af9f05f2916e376f265974c820c369a6c63a780Jean Chalard new Thread("InitializeBinaryDictionary") { 137904baab25a4c6ec5d9c4bf7e562154e3f544d296satok @Override 1383af9f05f2916e376f265974c820c369a6c63a780Jean Chalard public void run() { 139f0e12a969974987f1b97929886c6ebe6a685c538Jean Chalard final DictionaryCollection newMainDict = 140f0e12a969974987f1b97929886c6ebe6a685c538Jean Chalard DictionaryFactory.createMainDictionaryFromManager(context, locale); 141d7491e6e817e8954d15aa91300d209fb7e8bf384Satoshi Kataoka addOrReplaceDictionaryInternal(Dictionary.TYPE_MAIN, newMainDict); 1426080f6878b10916013a8a5e1d5f58f8041452c56Kurt Partridge mMainDictionary = newMainDict; 14379eefda0d3ab5e03c2d012ed8ea1898004781871Tadashi G. Takaoka if (listener != null) { 14479eefda0d3ab5e03c2d012ed8ea1898004781871Tadashi G. Takaoka listener.onUpdateMainDictionaryAvailability(hasMainDictionary()); 145369e54cc338d8115d63138663b882f56208d7ec3Tadashi G. Takaoka } 14601a4ebcd88f8a7001aac2f7f45293ceab717a30dJean Chalard mIsCurrentlyWaitingForMainDictionary = false; 1473af9f05f2916e376f265974c820c369a6c63a780Jean Chalard } 1483af9f05f2916e376f265974c820c369a6c63a780Jean Chalard }.start(); 149cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard } 150cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard 151c769ef4dd17ff9561e99528624f74b9072a09fbbKen Wakasa // The main dictionary could have been loaded asynchronously. Don't cache the return value 152c769ef4dd17ff9561e99528624f74b9072a09fbbKen Wakasa // of this method. 153e8f1edefeb2375a253d742c7f95e8d91677c7073Amith Yamasani public boolean hasMainDictionary() { 1546080f6878b10916013a8a5e1d5f58f8041452c56Kurt Partridge return null != mMainDictionary && mMainDictionary.isInitialized(); 1556080f6878b10916013a8a5e1d5f58f8041452c56Kurt Partridge } 1566080f6878b10916013a8a5e1d5f58f8041452c56Kurt Partridge 15701a4ebcd88f8a7001aac2f7f45293ceab717a30dJean Chalard @UsedForTesting 15801a4ebcd88f8a7001aac2f7f45293ceab717a30dJean Chalard public boolean isCurrentlyWaitingForMainDictionary() { 15901a4ebcd88f8a7001aac2f7f45293ceab717a30dJean Chalard return mIsCurrentlyWaitingForMainDictionary; 16001a4ebcd88f8a7001aac2f7f45293ceab717a30dJean Chalard } 16101a4ebcd88f8a7001aac2f7f45293ceab717a30dJean Chalard 1626080f6878b10916013a8a5e1d5f58f8041452c56Kurt Partridge public Dictionary getMainDictionary() { 1636080f6878b10916013a8a5e1d5f58f8041452c56Kurt Partridge return mMainDictionary; 164e8f1edefeb2375a253d742c7f95e8d91677c7073Amith Yamasani } 165e8f1edefeb2375a253d742c7f95e8d91677c7073Amith Yamasani 16667fd0c240d7c37b06e05333347fd17acf59fadf8Jean Chalard public ContactsBinaryDictionary getContactsDictionary() { 16714051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard return mContactsDict; 16814051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard } 16914051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard 1701b06b59e28743b713947947437ea5b312477f808Jean Chalard public ConcurrentHashMap<String, Dictionary> getUnigramDictionaries() { 1716234be1fe76740c458781b633f4ac66edd8ea84fJean Chalard return mDictionaries; 172bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok } 173bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok 174923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project /** 175923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * Sets an optional user dictionary resource to be loaded. The user dictionary is consulted 176f4223452119f9ff8b52f026f7ef92d961736dc51Jean Chalard * before the main dictionary, if set. This refers to the system-managed user dictionary. 177923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */ 178bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka public void setUserDictionary(final UserBinaryDictionary userDictionary) { 179d7491e6e817e8954d15aa91300d209fb7e8bf384Satoshi Kataoka addOrReplaceDictionaryInternal(Dictionary.TYPE_USER, userDictionary); 180923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 1812bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer 1822bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer /** 183699094f9b6e0a4621e8b3cfab70b59c0c7c086bbJean Chalard * Sets an optional contacts dictionary resource to be loaded. It is also possible to remove 184699094f9b6e0a4621e8b3cfab70b59c0c7c086bbJean Chalard * the contacts dictionary by passing null to this method. In this case no contacts dictionary 185699094f9b6e0a4621e8b3cfab70b59c0c7c086bbJean Chalard * won't be used. 1862bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer */ 187bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka public void setContactsDictionary(final ContactsBinaryDictionary contactsDictionary) { 18814051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard mContactsDict = contactsDictionary; 189d7491e6e817e8954d15aa91300d209fb7e8bf384Satoshi Kataoka addOrReplaceDictionaryInternal(Dictionary.TYPE_CONTACTS, contactsDictionary); 1902bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer } 191e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa 19287d06afc66db68f0b30b36593095511314793517Satoshi Kataoka public void setUserHistoryPredictionDictionary( 19387d06afc66db68f0b30b36593095511314793517Satoshi Kataoka final UserHistoryPredictionDictionary userHistoryPredictionDictionary) { 194d7491e6e817e8954d15aa91300d209fb7e8bf384Satoshi Kataoka addOrReplaceDictionaryInternal(Dictionary.TYPE_USER_HISTORY, 19587d06afc66db68f0b30b36593095511314793517Satoshi Kataoka userHistoryPredictionDictionary); 196979f8690967ff5409fe18f5085858ccdb8e0ccf1satok } 197979f8690967ff5409fe18f5085858ccdb8e0ccf1satok 19860586b57cf4ca4af16cf9a9261aaba9490f128bcSatoshi Kataoka public void setPersonalizationPredictionDictionary( 19960586b57cf4ca4af16cf9a9261aaba9490f128bcSatoshi Kataoka final PersonalizationPredictionDictionary personalizationPredictionDictionary) { 200d7491e6e817e8954d15aa91300d209fb7e8bf384Satoshi Kataoka addOrReplaceDictionaryInternal(Dictionary.TYPE_PERSONALIZATION_PREDICTION_IN_JAVA, 20160586b57cf4ca4af16cf9a9261aaba9490f128bcSatoshi Kataoka personalizationPredictionDictionary); 20260586b57cf4ca4af16cf9a9261aaba9490f128bcSatoshi Kataoka } 20360586b57cf4ca4af16cf9a9261aaba9490f128bcSatoshi Kataoka 204366c0c5198f43279f4671a196556124f41297c0cSatoshi Kataoka public void setPersonalizationDictionary( 205366c0c5198f43279f4671a196556124f41297c0cSatoshi Kataoka final PersonalizationDictionary personalizationDictionary) { 206366c0c5198f43279f4671a196556124f41297c0cSatoshi Kataoka addOrReplaceDictionaryInternal(Dictionary.TYPE_PERSONALIZATION, 207366c0c5198f43279f4671a196556124f41297c0cSatoshi Kataoka personalizationDictionary); 208366c0c5198f43279f4671a196556124f41297c0cSatoshi Kataoka } 209366c0c5198f43279f4671a196556124f41297c0cSatoshi Kataoka 2100028ed3627ff4f37a62a80f3b2c857e373cd5090satok public void setAutoCorrectionThreshold(float threshold) { 2111b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka mAutoCorrectionThreshold = threshold; 212b1abda8d62d654e876c4f781a07d724922c736e4Mitsuhiro Shimoda } 213b1abda8d62d654e876c4f781a07d724922c736e4Mitsuhiro Shimoda 2149666a228153bb2269da8983762bdd47e448f2cecYuichiro Hanada public interface OnGetSuggestedWordsCallback { 2159666a228153bb2269da8983762bdd47e448f2cecYuichiro Hanada public void onGetSuggestedWords(final SuggestedWords suggestedWords); 2169666a228153bb2269da8983762bdd47e448f2cecYuichiro Hanada } 2179666a228153bb2269da8983762bdd47e448f2cecYuichiro Hanada 2189666a228153bb2269da8983762bdd47e448f2cecYuichiro Hanada public void getSuggestedWords(final WordComposer wordComposer, 219bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka final String prevWordForBigram, final ProximityInfo proximityInfo, 2202dbb5957e3c8354fa9bcb1e08c7ce81387b7fe25Jean Chalard final boolean blockOffensiveWords, final boolean isCorrectionEnabled, 2219666a228153bb2269da8983762bdd47e448f2cecYuichiro Hanada final int[] additionalFeaturesOptions, final int sessionId, 2229666a228153bb2269da8983762bdd47e448f2cecYuichiro Hanada final OnGetSuggestedWordsCallback callback) { 223979f8690967ff5409fe18f5085858ccdb8e0ccf1satok LatinImeLogger.onStartSuggestion(prevWordForBigram); 224d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka if (wordComposer.isBatchMode()) { 2259666a228153bb2269da8983762bdd47e448f2cecYuichiro Hanada getSuggestedWordsForBatchInput(wordComposer, prevWordForBigram, proximityInfo, 2269666a228153bb2269da8983762bdd47e448f2cecYuichiro Hanada blockOffensiveWords, additionalFeaturesOptions, sessionId, callback); 227d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka } else { 2289666a228153bb2269da8983762bdd47e448f2cecYuichiro Hanada getSuggestedWordsForTypingInput(wordComposer, prevWordForBigram, proximityInfo, 2299666a228153bb2269da8983762bdd47e448f2cecYuichiro Hanada blockOffensiveWords, isCorrectionEnabled, additionalFeaturesOptions, callback); 230d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka } 231d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka } 232d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka 2339666a228153bb2269da8983762bdd47e448f2cecYuichiro Hanada // Retrieves suggestions for the typing input 2349666a228153bb2269da8983762bdd47e448f2cecYuichiro Hanada // and calls the callback function with the suggestions. 2359666a228153bb2269da8983762bdd47e448f2cecYuichiro Hanada private void getSuggestedWordsForTypingInput(final WordComposer wordComposer, 236bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka final String prevWordForBigram, final ProximityInfo proximityInfo, 237fe87f5f41744a633a2ed91af9b171bda2637649eKeisuke Kuroyanagi final boolean blockOffensiveWords, final boolean isCorrectionEnabled, 2389666a228153bb2269da8983762bdd47e448f2cecYuichiro Hanada final int[] additionalFeaturesOptions, final OnGetSuggestedWordsCallback callback) { 23910abf10c1fd3782389cbec1aec7b91855a7b5154Jean Chalard final int trailingSingleQuotesCount = wordComposer.trailingSingleQuotesCount(); 2402d2e3480338b97b55f1a22bf2bfe89c52ba866e2Jean Chalard final BoundedTreeSet suggestionsSet = new BoundedTreeSet(sSuggestedWordInfoComparator, 2412d2e3480338b97b55f1a22bf2bfe89c52ba866e2Jean Chalard MAX_SUGGESTIONS); 2421b62ff1a3d61cd44ab88acdfcbdf0fc70a7e1b10Amith Yamasani 243c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard final String typedWord = wordComposer.getTypedWord(); 24410abf10c1fd3782389cbec1aec7b91855a7b5154Jean Chalard final String consideredWord = trailingSingleQuotesCount > 0 24510abf10c1fd3782389cbec1aec7b91855a7b5154Jean Chalard ? typedWord.substring(0, typedWord.length() - trailingSingleQuotesCount) 246117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard : typedWord; 247d8f0caa406a0ca1df488baeb3af05528085755b7Jean Chalard LatinImeLogger.onAddSuggestedWord(typedWord, Dictionary.TYPE_USER_TYPED); 248979f8690967ff5409fe18f5085858ccdb8e0ccf1satok 249c677b0071d51a277413079b30f2215605637aa6bJean Chalard final WordComposer wordComposerForLookup; 250c677b0071d51a277413079b30f2215605637aa6bJean Chalard if (trailingSingleQuotesCount > 0) { 251c677b0071d51a277413079b30f2215605637aa6bJean Chalard wordComposerForLookup = new WordComposer(wordComposer); 252c677b0071d51a277413079b30f2215605637aa6bJean Chalard for (int i = trailingSingleQuotesCount - 1; i >= 0; --i) { 253c677b0071d51a277413079b30f2215605637aa6bJean Chalard wordComposerForLookup.deleteLast(); 254c677b0071d51a277413079b30f2215605637aa6bJean Chalard } 255c677b0071d51a277413079b30f2215605637aa6bJean Chalard } else { 256c677b0071d51a277413079b30f2215605637aa6bJean Chalard wordComposerForLookup = wordComposer; 257c677b0071d51a277413079b30f2215605637aa6bJean Chalard } 258d8afa2fbe13adf9f512fd294056a884a0edb0573Jean Chalard 259d8afa2fbe13adf9f512fd294056a884a0edb0573Jean Chalard for (final String key : mDictionaries.keySet()) { 260d8afa2fbe13adf9f512fd294056a884a0edb0573Jean Chalard final Dictionary dictionary = mDictionaries.get(key); 2619666a228153bb2269da8983762bdd47e448f2cecYuichiro Hanada suggestionsSet.addAll(dictionary.getSuggestions(wordComposerForLookup, 2629666a228153bb2269da8983762bdd47e448f2cecYuichiro Hanada prevWordForBigram, proximityInfo, blockOffensiveWords, 263fe87f5f41744a633a2ed91af9b171bda2637649eKeisuke Kuroyanagi additionalFeaturesOptions)); 264923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 2659f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok 266bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka final String whitelistedWord; 2677b258e512dc2a8c821eb9f435e5719b8a967b441Jean Chalard if (suggestionsSet.isEmpty()) { 2687b258e512dc2a8c821eb9f435e5719b8a967b441Jean Chalard whitelistedWord = null; 2697b258e512dc2a8c821eb9f435e5719b8a967b441Jean Chalard } else if (SuggestedWordInfo.KIND_WHITELIST != suggestionsSet.first().mKind) { 2707b258e512dc2a8c821eb9f435e5719b8a967b441Jean Chalard whitelistedWord = null; 2717b258e512dc2a8c821eb9f435e5719b8a967b441Jean Chalard } else { 2727b258e512dc2a8c821eb9f435e5719b8a967b441Jean Chalard whitelistedWord = suggestionsSet.first().mWord; 2737b258e512dc2a8c821eb9f435e5719b8a967b441Jean Chalard } 2747b258e512dc2a8c821eb9f435e5719b8a967b441Jean Chalard 2758c06a468e0bffa5cedc7d782be4754da70d9a657Jean Chalard // The word can be auto-corrected if it has a whitelist entry that is not itself, 2768c06a468e0bffa5cedc7d782be4754da70d9a657Jean Chalard // or if it's a 2+ characters non-word (i.e. it's not in the dictionary). 277caed149b67be378adf49f3db16a2cfbb8dd15d84Jean Chalard final boolean allowsToBeAutoCorrected = (null != whitelistedWord 278caed149b67be378adf49f3db16a2cfbb8dd15d84Jean Chalard && !whitelistedWord.equals(consideredWord)) 279b03447e1af950888d901fccbd2cc3e3b4a11ef98Ken Wakasa || (consideredWord.length() > 1 && !AutoCorrectionUtils.isValidWord(this, 2808c06a468e0bffa5cedc7d782be4754da70d9a657Jean Chalard consideredWord, wordComposer.isFirstCharCapitalized())); 281caed149b67be378adf49f3db16a2cfbb8dd15d84Jean Chalard 28267af2a24157ead953607bdfd585fba3a7e6bf50cJean Chalard final boolean hasAutoCorrection; 283966efe48891cbdd364d94f1e72fa0435ab8f2b77Jean Chalard // TODO: using isCorrectionEnabled here is not very good. It's probably useless, because 284966efe48891cbdd364d94f1e72fa0435ab8f2b77Jean Chalard // any attempt to do auto-correction is already shielded with a test for this flag; at the 285966efe48891cbdd364d94f1e72fa0435ab8f2b77Jean Chalard // same time, it feels wrong that the SuggestedWord object includes information about 286966efe48891cbdd364d94f1e72fa0435ab8f2b77Jean Chalard // the current settings. It may also be useful to know, when the setting is off, whether 287966efe48891cbdd364d94f1e72fa0435ab8f2b77Jean Chalard // the word *would* have been auto-corrected. 2882549b4978e5b0460d0f34a5e4016374ac2198753Jean Chalard if (!isCorrectionEnabled || !allowsToBeAutoCorrected || !wordComposer.isComposingWord() 289e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard || suggestionsSet.isEmpty() || wordComposer.hasDigits() 2902549b4978e5b0460d0f34a5e4016374ac2198753Jean Chalard || wordComposer.isMostlyCaps() || wordComposer.isResumed() 2912549b4978e5b0460d0f34a5e4016374ac2198753Jean Chalard || !hasMainDictionary()) { 29290d300c770b1697af5b715e55fa87d97e22588d2Jean Chalard // If we don't have a main dictionary, we never want to auto-correct. The reason for 29390d300c770b1697af5b715e55fa87d97e22588d2Jean Chalard // this is, the user may have a contact whose name happens to match a valid word in 29490d300c770b1697af5b715e55fa87d97e22588d2Jean Chalard // their language, and it will unexpectedly auto-correct. For example, if the user 29590d300c770b1697af5b715e55fa87d97e22588d2Jean Chalard // types in English with no dictionary and has a "Will" in their contact list, "will" 29690d300c770b1697af5b715e55fa87d97e22588d2Jean Chalard // would always auto-correct to "Will" which is unwanted. Hence, no main dict => no 29790d300c770b1697af5b715e55fa87d97e22588d2Jean Chalard // auto-correct. 298ea578f6b1dbcf04ffcc9c673f72a38ed2cfecdfcJean Chalard hasAutoCorrection = false; 29994b20c90d86aa042c2f361597665045271956decJean Chalard } else { 300b03447e1af950888d901fccbd2cc3e3b4a11ef98Ken Wakasa hasAutoCorrection = AutoCorrectionUtils.suggestionExceedsAutoCorrectionThreshold( 3011343d27de30c4010c54576d6c8bbb052c7630cbeJean Chalard suggestionsSet.first(), consideredWord, mAutoCorrectionThreshold); 30294b20c90d86aa042c2f361597665045271956decJean Chalard } 3039f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok 304ed83d4b14366b9799bf94c3f3486dc14ebd15d0fJean Chalard final ArrayList<SuggestedWordInfo> suggestionsContainer = 3055f282ea9e4a4590fcbab6e27d5fca7dacbb40a6aTadashi G. Takaoka CollectionUtils.newArrayList(suggestionsSet); 3065110e2cb5115bc7d8337a63427b895eeb74c9cd5Jean Chalard final int suggestionsCount = suggestionsContainer.size(); 307f5b55cb70c9d6012e1aa2b201c4785530afab168Jean Chalard final boolean isFirstCharCapitalized = wordComposer.isFirstCharCapitalized(); 308f5b55cb70c9d6012e1aa2b201c4785530afab168Jean Chalard final boolean isAllUpperCase = wordComposer.isAllUpperCase(); 3095110e2cb5115bc7d8337a63427b895eeb74c9cd5Jean Chalard if (isFirstCharCapitalized || isAllUpperCase || 0 != trailingSingleQuotesCount) { 3105110e2cb5115bc7d8337a63427b895eeb74c9cd5Jean Chalard for (int i = 0; i < suggestionsCount; ++i) { 3115110e2cb5115bc7d8337a63427b895eeb74c9cd5Jean Chalard final SuggestedWordInfo wordInfo = suggestionsContainer.get(i); 3125110e2cb5115bc7d8337a63427b895eeb74c9cd5Jean Chalard final SuggestedWordInfo transformedWordInfo = getTransformedSuggestedWordInfo( 3135110e2cb5115bc7d8337a63427b895eeb74c9cd5Jean Chalard wordInfo, mLocale, isAllUpperCase, isFirstCharCapitalized, 3145110e2cb5115bc7d8337a63427b895eeb74c9cd5Jean Chalard trailingSingleQuotesCount); 3155110e2cb5115bc7d8337a63427b895eeb74c9cd5Jean Chalard suggestionsContainer.set(i, transformedWordInfo); 3165110e2cb5115bc7d8337a63427b895eeb74c9cd5Jean Chalard } 3175110e2cb5115bc7d8337a63427b895eeb74c9cd5Jean Chalard } 3185110e2cb5115bc7d8337a63427b895eeb74c9cd5Jean Chalard 3195110e2cb5115bc7d8337a63427b895eeb74c9cd5Jean Chalard for (int i = 0; i < suggestionsCount; ++i) { 320ed83d4b14366b9799bf94c3f3486dc14ebd15d0fJean Chalard final SuggestedWordInfo wordInfo = suggestionsContainer.get(i); 321e8ef09567077211da034a77b457fd5f87e70f6f0Jean Chalard LatinImeLogger.onAddSuggestedWord(wordInfo.mWord.toString(), 322e8ef09567077211da034a77b457fd5f87e70f6f0Jean Chalard wordInfo.mSourceDict.mDictType); 323bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok } 324bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok 325f89a75134b03bd2675c85249a184c09f83c6f80cJean Chalard if (!TextUtils.isEmpty(typedWord)) { 326bed514bd902d9736edcbfe03d37d8cced2bb03a3Jean Chalard suggestionsContainer.add(0, new SuggestedWordInfo(typedWord, 32724eec0fa680f97e64d1fa0df754acbad95ed9a76Jean Chalard SuggestedWordInfo.MAX_SCORE, SuggestedWordInfo.KIND_TYPED, 328e8ef09567077211da034a77b457fd5f87e70f6f0Jean Chalard Dictionary.DICTIONARY_USER_TYPED, 329ef1e363016623ccf409c8c270f2c1e35a67734c9Jean Chalard SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */)); 33028eeb35d149468514a65379e9d0d1672cf26981eJean Chalard } 331bed514bd902d9736edcbfe03d37d8cced2bb03a3Jean Chalard SuggestedWordInfo.removeDups(suggestionsContainer); 3329f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok 333def4551c2a570e7f575b2e9303506d790c2f335fJean Chalard final ArrayList<SuggestedWordInfo> suggestionsList; 334bed514bd902d9736edcbfe03d37d8cced2bb03a3Jean Chalard if (DBG && !suggestionsContainer.isEmpty()) { 335bed514bd902d9736edcbfe03d37d8cced2bb03a3Jean Chalard suggestionsList = getSuggestionsInfoListWithDebugInfo(typedWord, suggestionsContainer); 336ed9986824e1339855376771ad29fae4de921a029Jean Chalard } else { 337bed514bd902d9736edcbfe03d37d8cced2bb03a3Jean Chalard suggestionsList = suggestionsContainer; 3388553b5ec315660ab53dd9234e64e1e39ea09ec0fJean Chalard } 339ed9986824e1339855376771ad29fae4de921a029Jean Chalard 3409666a228153bb2269da8983762bdd47e448f2cecYuichiro Hanada callback.onGetSuggestedWords(new SuggestedWords(suggestionsList, 341b3cfde2cbb96951b1202c70b9961f340bdf495d0Jean Chalard // TODO: this first argument is lying. If this is a whitelisted word which is an 342b3cfde2cbb96951b1202c70b9961f340bdf495d0Jean Chalard // actual word, it says typedWordValid = false, which looks wrong. We should either 343b3cfde2cbb96951b1202c70b9961f340bdf495d0Jean Chalard // rename the attribute or change the value. 34402f1c1534c2060aaea7a9a020ce87f6c5ff5d8e0Jean Chalard !allowsToBeAutoCorrected /* typedWordValid */, 3452549b4978e5b0460d0f34a5e4016374ac2198753Jean Chalard hasAutoCorrection, /* willAutoCorrect */ 34603a35170751a635332c00bf6c272a0127a255cf6Jean Chalard false /* isPunctuationSuggestions */, 3470142b997bf18f5d07e83b3fd403f0b3ea4736040satok false /* isObsoleteSuggestions */, 3489666a228153bb2269da8983762bdd47e448f2cecYuichiro Hanada !wordComposer.isComposingWord() /* isPrediction */)); 349923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 350923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 3519666a228153bb2269da8983762bdd47e448f2cecYuichiro Hanada // Retrieves suggestions for the batch input 3529666a228153bb2269da8983762bdd47e448f2cecYuichiro Hanada // and calls the callback function with the suggestions. 3539666a228153bb2269da8983762bdd47e448f2cecYuichiro Hanada private void getSuggestedWordsForBatchInput(final WordComposer wordComposer, 354bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka final String prevWordForBigram, final ProximityInfo proximityInfo, 355fe87f5f41744a633a2ed91af9b171bda2637649eKeisuke Kuroyanagi final boolean blockOffensiveWords, final int[] additionalFeaturesOptions, 3569666a228153bb2269da8983762bdd47e448f2cecYuichiro Hanada final int sessionId, final OnGetSuggestedWordsCallback callback) { 357d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka final BoundedTreeSet suggestionsSet = new BoundedTreeSet(sSuggestedWordInfoComparator, 358d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka MAX_SUGGESTIONS); 359d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka 360d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka // At second character typed, search the unigrams (scores being affected by bigrams) 361d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka for (final String key : mDictionaries.keySet()) { 36246fc768e54e3d52003645494552f9e686f28f20fJean Chalard // Skip User history dictionary for lookup 36346fc768e54e3d52003645494552f9e686f28f20fJean Chalard // TODO: The user history dictionary should just override getSuggestionsWithSessionId 36446fc768e54e3d52003645494552f9e686f28f20fJean Chalard // to make sure it doesn't return anything and we should remove this test 36546fc768e54e3d52003645494552f9e686f28f20fJean Chalard if (key.equals(Dictionary.TYPE_USER_HISTORY)) { 366d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka continue; 367d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka } 368d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka final Dictionary dictionary = mDictionaries.get(key); 3692dbb5957e3c8354fa9bcb1e08c7ce81387b7fe25Jean Chalard suggestionsSet.addAll(dictionary.getSuggestionsWithSessionId(wordComposer, 370fe87f5f41744a633a2ed91af9b171bda2637649eKeisuke Kuroyanagi prevWordForBigram, proximityInfo, blockOffensiveWords, 371fe87f5f41744a633a2ed91af9b171bda2637649eKeisuke Kuroyanagi additionalFeaturesOptions, sessionId)); 372d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka } 373d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka 37487cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka for (SuggestedWordInfo wordInfo : suggestionsSet) { 375e8ef09567077211da034a77b457fd5f87e70f6f0Jean Chalard LatinImeLogger.onAddSuggestedWord(wordInfo.mWord, wordInfo.mSourceDict.mDictType); 37687cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka } 37787cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka 378d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka final ArrayList<SuggestedWordInfo> suggestionsContainer = 3795f282ea9e4a4590fcbab6e27d5fca7dacbb40a6aTadashi G. Takaoka CollectionUtils.newArrayList(suggestionsSet); 380eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang final int suggestionsCount = suggestionsContainer.size(); 3811eba97d92fb5caa4f23425837b6680ccc2a23ae8Jean Chalard final boolean isFirstCharCapitalized = wordComposer.wasShiftedNoLock(); 3821eba97d92fb5caa4f23425837b6680ccc2a23ae8Jean Chalard final boolean isAllUpperCase = wordComposer.isAllUpperCase(); 383eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang if (isFirstCharCapitalized || isAllUpperCase) { 384eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang for (int i = 0; i < suggestionsCount; ++i) { 385eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang final SuggestedWordInfo wordInfo = suggestionsContainer.get(i); 386eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang final SuggestedWordInfo transformedWordInfo = getTransformedSuggestedWordInfo( 387eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang wordInfo, mLocale, isAllUpperCase, isFirstCharCapitalized, 388eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang 0 /* trailingSingleQuotesCount */); 389eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang suggestionsContainer.set(i, transformedWordInfo); 390eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang } 391eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang } 392d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka 393d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard if (suggestionsContainer.size() > 1 && TextUtils.equals(suggestionsContainer.get(0).mWord, 394d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard wordComposer.getRejectedBatchModeSuggestion())) { 395d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard final SuggestedWordInfo rejected = suggestionsContainer.remove(0); 396d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard suggestionsContainer.add(1, rejected); 397d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard } 398d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka SuggestedWordInfo.removeDups(suggestionsContainer); 399a5a2f3e3c77ebf2e1bb74b08c8587c15b9711ac8Jean Chalard 400a5a2f3e3c77ebf2e1bb74b08c8587c15b9711ac8Jean Chalard // For some reason some suggestions with MIN_VALUE are making their way here. 401a5a2f3e3c77ebf2e1bb74b08c8587c15b9711ac8Jean Chalard // TODO: Find a more robust way to detect distractors. 402a5a2f3e3c77ebf2e1bb74b08c8587c15b9711ac8Jean Chalard for (int i = suggestionsContainer.size() - 1; i >= 0; --i) { 403a5a2f3e3c77ebf2e1bb74b08c8587c15b9711ac8Jean Chalard if (suggestionsContainer.get(i).mScore < SUPPRESS_SUGGEST_THRESHOLD) { 404a5a2f3e3c77ebf2e1bb74b08c8587c15b9711ac8Jean Chalard suggestionsContainer.remove(i); 405a5a2f3e3c77ebf2e1bb74b08c8587c15b9711ac8Jean Chalard } 406a5a2f3e3c77ebf2e1bb74b08c8587c15b9711ac8Jean Chalard } 407a5a2f3e3c77ebf2e1bb74b08c8587c15b9711ac8Jean Chalard 408eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang // In the batch input mode, the most relevant suggested word should act as a "typed word" 409eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang // (typedWordValid=true), not as an "auto correct word" (willAutoCorrect=false). 4109666a228153bb2269da8983762bdd47e448f2cecYuichiro Hanada callback.onGetSuggestedWords(new SuggestedWords(suggestionsContainer, 411d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka true /* typedWordValid */, 412eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang false /* willAutoCorrect */, 413d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka false /* isPunctuationSuggestions */, 414d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka false /* isObsoleteSuggestions */, 4159666a228153bb2269da8983762bdd47e448f2cecYuichiro Hanada false /* isPrediction */)); 416d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka } 417d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka 4180d0f182959600d83c376e6b844337ea45e5ddbbfJean Chalard private static ArrayList<SuggestedWordInfo> getSuggestionsInfoListWithDebugInfo( 4197e518d8b8358c96b94b900f0917cdc5fd8190ce1satok final String typedWord, final ArrayList<SuggestedWordInfo> suggestions) { 4207e518d8b8358c96b94b900f0917cdc5fd8190ce1satok final SuggestedWordInfo typedWordInfo = suggestions.get(0); 4217e518d8b8358c96b94b900f0917cdc5fd8190ce1satok typedWordInfo.setDebugString("+"); 4220d0f182959600d83c376e6b844337ea45e5ddbbfJean Chalard final int suggestionsSize = suggestions.size(); 4230d0f182959600d83c376e6b844337ea45e5ddbbfJean Chalard final ArrayList<SuggestedWordInfo> suggestionsList = 4245f282ea9e4a4590fcbab6e27d5fca7dacbb40a6aTadashi G. Takaoka CollectionUtils.newArrayList(suggestionsSize); 4257e518d8b8358c96b94b900f0917cdc5fd8190ce1satok suggestionsList.add(typedWordInfo); 4260d0f182959600d83c376e6b844337ea45e5ddbbfJean Chalard // Note: i here is the index in mScores[], but the index in mSuggestions is one more 4270d0f182959600d83c376e6b844337ea45e5ddbbfJean Chalard // than i because we added the typed word to mSuggestions without touching mScores. 4287e518d8b8358c96b94b900f0917cdc5fd8190ce1satok for (int i = 0; i < suggestionsSize - 1; ++i) { 4297e518d8b8358c96b94b900f0917cdc5fd8190ce1satok final SuggestedWordInfo cur = suggestions.get(i + 1); 4300028ed3627ff4f37a62a80f3b2c857e373cd5090satok final float normalizedScore = BinaryDictionary.calcNormalizedScore( 431db1939dbaa1de59eaf5693e2c89b02b323e9aac8satok typedWord, cur.toString(), cur.mScore); 4320d0f182959600d83c376e6b844337ea45e5ddbbfJean Chalard final String scoreInfoString; 4330d0f182959600d83c376e6b844337ea45e5ddbbfJean Chalard if (normalizedScore > 0) { 43494027c7201a376107a35ec78cd21db1905662601Tadashi G. Takaoka scoreInfoString = String.format( 43594027c7201a376107a35ec78cd21db1905662601Tadashi G. Takaoka Locale.ROOT, "%d (%4.2f)", cur.mScore, normalizedScore); 4360d0f182959600d83c376e6b844337ea45e5ddbbfJean Chalard } else { 4377e518d8b8358c96b94b900f0917cdc5fd8190ce1satok scoreInfoString = Integer.toString(cur.mScore); 4380d0f182959600d83c376e6b844337ea45e5ddbbfJean Chalard } 4397e518d8b8358c96b94b900f0917cdc5fd8190ce1satok cur.setDebugString(scoreInfoString); 4407e518d8b8358c96b94b900f0917cdc5fd8190ce1satok suggestionsList.add(cur); 4410d0f182959600d83c376e6b844337ea45e5ddbbfJean Chalard } 4420d0f182959600d83c376e6b844337ea45e5ddbbfJean Chalard return suggestionsList; 4430d0f182959600d83c376e6b844337ea45e5ddbbfJean Chalard } 4440d0f182959600d83c376e6b844337ea45e5ddbbfJean Chalard 445a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaoka private static final class SuggestedWordInfoComparator 446a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaoka implements Comparator<SuggestedWordInfo> { 4479da0027b386c23b83c2f9b0121bc15fa15306e3aJean Chalard // This comparator ranks the word info with the higher frequency first. That's because 4489da0027b386c23b83c2f9b0121bc15fa15306e3aJean Chalard // that's the order we want our elements in. 4499da0027b386c23b83c2f9b0121bc15fa15306e3aJean Chalard @Override 4509da0027b386c23b83c2f9b0121bc15fa15306e3aJean Chalard public int compare(final SuggestedWordInfo o1, final SuggestedWordInfo o2) { 4519da0027b386c23b83c2f9b0121bc15fa15306e3aJean Chalard if (o1.mScore > o2.mScore) return -1; 4529da0027b386c23b83c2f9b0121bc15fa15306e3aJean Chalard if (o1.mScore < o2.mScore) return 1; 4539da0027b386c23b83c2f9b0121bc15fa15306e3aJean Chalard if (o1.mCodePointCount < o2.mCodePointCount) return -1; 4549da0027b386c23b83c2f9b0121bc15fa15306e3aJean Chalard if (o1.mCodePointCount > o2.mCodePointCount) return 1; 455bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka return o1.mWord.compareTo(o2.mWord); 4569da0027b386c23b83c2f9b0121bc15fa15306e3aJean Chalard } 4579da0027b386c23b83c2f9b0121bc15fa15306e3aJean Chalard } 4589da0027b386c23b83c2f9b0121bc15fa15306e3aJean Chalard private static final SuggestedWordInfoComparator sSuggestedWordInfoComparator = 4599da0027b386c23b83c2f9b0121bc15fa15306e3aJean Chalard new SuggestedWordInfoComparator(); 4609da0027b386c23b83c2f9b0121bc15fa15306e3aJean Chalard 461ec8b27fe49bd0a149cf7dcd36d1b0d966b03a3b5Jean Chalard private static SuggestedWordInfo getTransformedSuggestedWordInfo( 462ec8b27fe49bd0a149cf7dcd36d1b0d966b03a3b5Jean Chalard final SuggestedWordInfo wordInfo, final Locale locale, final boolean isAllUpperCase, 463ec8b27fe49bd0a149cf7dcd36d1b0d966b03a3b5Jean Chalard final boolean isFirstCharCapitalized, final int trailingSingleQuotesCount) { 4649011b89f4ea0d73f1ad78b2dd0a6557b950fddd9Jean Chalard final StringBuilder sb = new StringBuilder(wordInfo.mWord.length()); 465ec8b27fe49bd0a149cf7dcd36d1b0d966b03a3b5Jean Chalard if (isAllUpperCase) { 466bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka sb.append(wordInfo.mWord.toUpperCase(locale)); 467ec8b27fe49bd0a149cf7dcd36d1b0d966b03a3b5Jean Chalard } else if (isFirstCharCapitalized) { 46899b93d17d53c2d587c45373831b327f7851ec0a8Jean Chalard sb.append(StringUtils.capitalizeFirstCodePoint(wordInfo.mWord, locale)); 469ec8b27fe49bd0a149cf7dcd36d1b0d966b03a3b5Jean Chalard } else { 470ec8b27fe49bd0a149cf7dcd36d1b0d966b03a3b5Jean Chalard sb.append(wordInfo.mWord); 471ec8b27fe49bd0a149cf7dcd36d1b0d966b03a3b5Jean Chalard } 472ec8b27fe49bd0a149cf7dcd36d1b0d966b03a3b5Jean Chalard for (int i = trailingSingleQuotesCount - 1; i >= 0; --i) { 473240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka sb.appendCodePoint(Constants.CODE_SINGLE_QUOTE); 474ec8b27fe49bd0a149cf7dcd36d1b0d966b03a3b5Jean Chalard } 475bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka return new SuggestedWordInfo(sb.toString(), wordInfo.mScore, wordInfo.mKind, 476ef1e363016623ccf409c8c270f2c1e35a67734c9Jean Chalard wordInfo.mSourceDict, wordInfo.mIndexOfTouchPointOfSecondWord); 477ec8b27fe49bd0a149cf7dcd36d1b0d966b03a3b5Jean Chalard } 478ec8b27fe49bd0a149cf7dcd36d1b0d966b03a3b5Jean Chalard 47936fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani public void close() { 4805f282ea9e4a4590fcbab6e27d5fca7dacbb40a6aTadashi G. Takaoka final HashSet<Dictionary> dictionaries = CollectionUtils.newHashSet(); 4816234be1fe76740c458781b633f4ac66edd8ea84fJean Chalard dictionaries.addAll(mDictionaries.values()); 482c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka for (final Dictionary dictionary : dictionaries) { 483c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka dictionary.close(); 48436fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani } 4856080f6878b10916013a8a5e1d5f58f8041452c56Kurt Partridge mMainDictionary = null; 48636fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani } 487923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project} 488